📄 hash_page.c
字号:
} else LSN_NOT_LOGGED(new_lsn); /* Move lsn onto page. */ LSN(hcp->page) = new_lsn; /* Structure assignment. */ __ham_putitem(dbp, hcp->page, pkey, key_type); __ham_putitem(dbp, hcp->page, pdata, data_type); /* * For splits, we are going to update item_info's page number * field, so that we can easily return to the same page the * next time we come in here. For other operations, this shouldn't * matter, since odds are this is the last thing that happens before * we return to the user program. */ hcp->pgno = PGNO(hcp->page); /* * XXX * Maybe keep incremental numbers here. */ if (!STD_LOCKING(dbc)) { hcp->hdr->nelem++; if ((ret = __ham_dirty_meta(dbc)) != 0) return (ret); } if (do_expand || (hcp->hdr->ffactor != 0 && (u_int32_t)H_NUMPAIRS(hcp->page) > hcp->hdr->ffactor)) F_SET(hcp, H_EXPAND); return (0);}/* * Special __putitem call used in splitting -- copies one entry to * another. Works for all types of hash entries (H_OFFPAGE, H_KEYDATA, * H_DUPLICATE, H_OFFDUP). Since we log splits at a high level, we * do not need to do any logging here. * * PUBLIC: void __ham_copy_item __P((DB *, PAGE *, u_int32_t, PAGE *)); */void__ham_copy_item(dbp, src_page, src_ndx, dest_page) DB *dbp; PAGE *src_page; u_int32_t src_ndx; PAGE *dest_page;{ u_int32_t len; size_t pgsize; void *src, *dest; db_indx_t *inp; pgsize = dbp->pgsize; inp = P_INP(dbp, dest_page); /* * Copy the key and data entries onto this new page. */ src = P_ENTRY(dbp, src_page, src_ndx); /* Set up space on dest. */ len = (u_int32_t)LEN_HITEM(dbp, src_page, pgsize, src_ndx); HOFFSET(dest_page) -= len; inp[NUM_ENT(dest_page)] = HOFFSET(dest_page); dest = P_ENTRY(dbp, dest_page, NUM_ENT(dest_page)); NUM_ENT(dest_page)++; memcpy(dest, src, len);}/* * * Returns: * pointer on success * NULL on error * * PUBLIC: int __ham_add_ovflpage __P((DBC *, PAGE *, int, PAGE **)); */int__ham_add_ovflpage(dbc, pagep, release, pp) DBC *dbc; PAGE *pagep; int release; PAGE **pp;{ DB *dbp; DB_LSN new_lsn; DB_MPOOLFILE *mpf; PAGE *new_pagep; int ret; dbp = dbc->dbp; mpf = dbp->mpf; if ((ret = __db_new(dbc, P_HASH, &new_pagep)) != 0) return (ret); if (DBC_LOGGING(dbc)) { if ((ret = __ham_newpage_log(dbp, dbc->txn, &new_lsn, 0, PUTOVFL, PGNO(pagep), &LSN(pagep), PGNO(new_pagep), &LSN(new_pagep), PGNO_INVALID, NULL)) != 0) return (ret); } else LSN_NOT_LOGGED(new_lsn); /* Move lsn onto page. */ LSN(pagep) = LSN(new_pagep) = new_lsn; NEXT_PGNO(pagep) = PGNO(new_pagep); PREV_PGNO(new_pagep) = PGNO(pagep); if (release) ret = mpf->put(mpf, pagep, DB_MPOOL_DIRTY); *pp = new_pagep; return (ret);}/* * PUBLIC: int __ham_get_cpage __P((DBC *, db_lockmode_t)); */int__ham_get_cpage(dbc, mode) DBC *dbc; db_lockmode_t mode;{ DB *dbp; DB_LOCK tmp_lock; DB_MPOOLFILE *mpf; HASH_CURSOR *hcp; int ret; dbp = dbc->dbp; mpf = dbp->mpf; hcp = (HASH_CURSOR *)dbc->internal; ret = 0; /* * There are four cases with respect to buckets and locks. * 1. If there is no lock held, then if we are locking, we should * get the lock. * 2. If there is a lock held, it's for the current bucket, and it's * for the right mode, we don't need to do anything. * 3. If there is a lock held for the current bucket but it's not * strong enough, we need to upgrade. * 4. If there is a lock, but it's for a different bucket, then we need * to release the existing lock and get a new lock. */ LOCK_INIT(tmp_lock); if (STD_LOCKING(dbc)) { if (hcp->lbucket != hcp->bucket && /* Case 4 */ (ret = __TLPUT(dbc, hcp->lock)) != 0) return (ret); if ((LOCK_ISSET(hcp->lock) && (hcp->lock_mode == DB_LOCK_READ && mode == DB_LOCK_WRITE))) { /* Case 3. */ tmp_lock = hcp->lock; LOCK_INIT(hcp->lock); } /* Acquire the lock. */ if (!LOCK_ISSET(hcp->lock)) /* Cases 1, 3, and 4. */ if ((ret = __ham_lock_bucket(dbc, mode)) != 0) return (ret); if (ret == 0) { hcp->lock_mode = mode; hcp->lbucket = hcp->bucket; if (LOCK_ISSET(tmp_lock)) /* Case 3: release the original lock. */ ret = dbp->dbenv->lock_put(dbp->dbenv, &tmp_lock); } else if (LOCK_ISSET(tmp_lock)) hcp->lock = tmp_lock; } if (ret == 0 && hcp->page == NULL) { if (hcp->pgno == PGNO_INVALID) hcp->pgno = BUCKET_TO_PAGE(hcp, hcp->bucket); if ((ret = mpf->get(mpf, &hcp->pgno, DB_MPOOL_CREATE, &hcp->page)) != 0) return (ret); } return (0);}/* * Get a new page at the cursor, putting the last page if necessary. * If the flag is set to H_ISDUP, then we are talking about the * duplicate page, not the main page. * * PUBLIC: int __ham_next_cpage __P((DBC *, db_pgno_t, int)); */int__ham_next_cpage(dbc, pgno, dirty) DBC *dbc; db_pgno_t pgno; int dirty;{ DB *dbp; DB_MPOOLFILE *mpf; HASH_CURSOR *hcp; PAGE *p; int ret; dbp = dbc->dbp; mpf = dbp->mpf; hcp = (HASH_CURSOR *)dbc->internal; if (hcp->page != NULL && (ret = mpf->put(mpf, hcp->page, dirty ? DB_MPOOL_DIRTY : 0)) != 0) return (ret); hcp->page = NULL; if ((ret = mpf->get(mpf, &pgno, DB_MPOOL_CREATE, &p)) != 0) return (ret); hcp->page = p; hcp->pgno = pgno; hcp->indx = 0; return (0);}/* * __ham_lock_bucket -- * Get the lock on a particular bucket. * * PUBLIC: int __ham_lock_bucket __P((DBC *, db_lockmode_t)); */int__ham_lock_bucket(dbc, mode) DBC *dbc; db_lockmode_t mode;{ HASH_CURSOR *hcp; db_pgno_t pgno; int gotmeta, ret; hcp = (HASH_CURSOR *)dbc->internal; gotmeta = hcp->hdr == NULL ? 1 : 0; if (gotmeta) if ((ret = __ham_get_meta(dbc)) != 0) return (ret); pgno = BUCKET_TO_PAGE(hcp, hcp->bucket); if (gotmeta) if ((ret = __ham_release_meta(dbc)) != 0) return (ret); ret = __db_lget(dbc, 0, pgno, mode, 0, &hcp->lock); hcp->lock_mode = mode; return (ret);}/* * __ham_dpair -- * Delete a pair on a page, paying no attention to what the pair * represents. The caller is responsible for freeing up duplicates * or offpage entries that might be referenced by this pair. * * Recovery assumes that this may be called without the metadata * page pinned. * * PUBLIC: void __ham_dpair __P((DB *, PAGE *, u_int32_t)); */void__ham_dpair(dbp, p, indx) DB *dbp; PAGE *p; u_int32_t indx;{ db_indx_t delta, n, *inp; u_int8_t *dest, *src; inp = P_INP(dbp, p); /* * Compute "delta", the amount we have to shift all of the * offsets. To find the delta, we just need to calculate * the size of the pair of elements we are removing. */ delta = H_PAIRSIZE(dbp, p, dbp->pgsize, indx); /* * The hard case: we want to remove something other than * the last item on the page. We need to shift data and * offsets down. */ if ((db_indx_t)indx != NUM_ENT(p) - 2) { /* * Move the data: src is the first occupied byte on * the page. (Length is delta.) */ src = (u_int8_t *)p + HOFFSET(p); /* * Destination is delta bytes beyond src. This might * be an overlapping copy, so we have to use memmove. */ dest = src + delta; memmove(dest, src, inp[H_DATAINDEX(indx)] - HOFFSET(p)); } /* Adjust page metadata. */ HOFFSET(p) = HOFFSET(p) + delta; NUM_ENT(p) = NUM_ENT(p) - 2; /* Adjust the offsets. */ for (n = (db_indx_t)indx; n < (db_indx_t)(NUM_ENT(p)); n++) inp[n] = inp[n + 2] + delta;}/* * __ham_c_delpg -- * * Adjust the cursors after we've emptied a page in a bucket, taking * care that when we move cursors pointing to deleted items, their * orders don't collide with the orders of cursors on the page we move * them to (since after this function is called, cursors with the same * index on the two pages will be otherwise indistinguishable--they'll * all have pgno new_pgno). There are three cases: * * 1) The emptied page is the first page in the bucket. In this * case, we've copied all the items from the second page into the * first page, so the first page is new_pgno and the second page is * old_pgno. new_pgno is empty, but can have deleted cursors * pointing at indx 0, so we need to be careful of the orders * there. This is DB_HAM_DELFIRSTPG. * * 2) The page is somewhere in the middle of a bucket. Our caller * can just delete such a page, so it's old_pgno. old_pgno is * empty, but may have deleted cursors pointing at indx 0, so we * need to be careful of indx 0 when we move those cursors to * new_pgno. This is DB_HAM_DELMIDPG. * * 3) The page is the last in a bucket. Again the empty page is * old_pgno, and again it should only have cursors that are deleted * and at indx == 0. This time, though, there's no next page to * move them to, so we set them to indx == num_ent on the previous * page--and indx == num_ent is the index whose cursors we need to * be careful of. This is DB_HAM_DELLASTPG. */static int__ham_c_delpg(dbc, old_pgno, new_pgno, num_ent, op, orderp) DBC *dbc; db_pgno_t old_pgno, new_pgno; u_int32_t num_ent; db_ham_mode op; u_int32_t *orderp;{ DB *dbp, *ldbp; DB_ENV *dbenv; DB_LSN lsn; DB_TXN *my_txn; DBC *cp; HASH_CURSOR *hcp; int found, ret; db_indx_t indx; u_int32_t order; /* Which is the worrisome index? */ indx = (op == DB_HAM_DELLASTPG) ? num_ent : 0; dbp = dbc->dbp; dbenv = dbp->dbenv; my_txn = IS_SUBTRANSACTION(dbc->txn) ? dbc->txn : NULL; found = 0; MUTEX_THREAD_LOCK(dbenv, dbenv->dblist_mutexp); /* * Find the highest order of any cursor our movement * may collide with. */ order = 1; for (ldbp = __dblist_get(dbenv, dbp->adj_fileid); ldbp != NULL && ldbp->adj_fileid == dbp->adj_fileid; ldbp = LIST_NEXT(ldbp, dblistlinks)) { MUTEX_THREAD_LOCK(dbenv, dbp->mutexp); for (cp = TAILQ_FIRST(&ldbp->active_queue); cp != NULL; cp = TAILQ_NEXT(cp, links)) { if (cp == dbc || cp->dbtype != DB_HASH) continue; hcp = (HASH_CURSOR *)cp->internal; if (hcp->pgno == new_pgno) { if (hcp->indx == indx && F_ISSET(hcp, H_DELETED) && hcp->order >= order) order = hcp->order + 1; DB_ASSERT(op != DB_HAM_DELFIRSTPG || hcp->indx == NDX_INVALID || (hcp->indx == 0 && F_ISSET(hcp, H_DELETED))); } } MUTEX_THREAD_UNLOCK(dbenv, dbp->mutexp); } for (ldbp = __dblist_get(dbenv, dbp->adj_fileid); ldbp != NULL && ldbp->adj_fileid == dbp->adj_fileid; ldbp = LIST_NEXT(ldbp, dblistlinks)) { MUTEX_THREAD_LOCK(dbenv, dbp->mutexp); for (cp = TAILQ_FIRST(&ldbp->active_queue); cp != NULL; cp = TAILQ_NEXT(cp, links)) { if (cp == dbc || cp->dbtype != DB_HASH) continue; hcp = (HASH_CURSOR *)cp->internal; if (hcp->pgno == old_pgno) { switch (op) { case DB_HAM_DELFIRSTPG: /* * We're moving all items, * regardless of index. */ hcp->pgno = new_pgno; /* * But we have to be careful of * the order values. */ if (hcp->indx == indx) hcp->order += order; break; case DB_HAM_DELMIDPG: hcp->pgno = new_pgno; DB_ASSERT(hcp->indx == 0 && F_ISSET(hcp, H_DELETED)); hcp->order += order; break; case DB_HAM_DELLASTPG: hcp->pgno = new_pgno; DB_ASSERT(hcp->indx == 0 && F_ISSET(hcp, H_DELETED)); hcp->indx = indx; hcp->order += order; break; default: DB_ASSERT(0); return (__db_panic(dbenv, EINVAL)); } if (my_txn != NULL && cp->txn != my_txn) found = 1; } } MUTEX_THREAD_UNLOCK(dbenv, dbp->mutexp); } MUTEX_THREAD_UNLOCK(dbenv, dbenv->dblist_mutexp); if (found != 0 && DBC_LOGGING(dbc)) { if ((ret = __ham_chgpg_log(dbp, my_txn, &lsn, 0, op, old_pgno, new_pgno, indx, order)) != 0) return (ret); } *orderp = order; return (0);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -