📄 hash_page.c
字号:
/* Put the item element on the page. */ if (type == H_OFFPAGE) { off = HOFFSET(p) - dbt->size; HOFFSET(p) = inp[n] = off; memcpy(P_ENTRY(dbp, p, n), dbt->data, dbt->size); } else { off = HOFFSET(p) - HKEYDATA_SIZE(dbt->size); HOFFSET(p) = inp[n] = off; PUT_HKEYDATA(P_ENTRY(dbp, p, n), dbt->data, dbt->size, type); } /* Adjust page info. */ NUM_ENT(p) += 1;}/* * PUBLIC: void __ham_reputpair __P((DB *, PAGE *, * PUBLIC: u_int32_t, const DBT *, const DBT *)); * * This is a special case to restore a key/data pair to its original * location during recovery. We are guaranteed that the pair fits * on the page and is not the last pair on the page (because if it's * the last pair, the normal insert works). */void__ham_reputpair(dbp, p, ndx, key, data) DB *dbp; PAGE *p; u_int32_t ndx; const DBT *key, *data;{ db_indx_t i, *inp, movebytes, newbytes; size_t psize; u_int8_t *from; psize = dbp->pgsize; inp = P_INP(dbp, p); /* First shuffle the existing items up on the page. */ movebytes = (db_indx_t)( (ndx == 0 ? psize : inp[H_DATAINDEX(ndx - 2)]) - HOFFSET(p)); newbytes = key->size + data->size; from = (u_int8_t *)p + HOFFSET(p); memmove(from - newbytes, from, movebytes); /* * Adjust the indices and move them up 2 spaces. Note that we * have to check the exit condition inside the loop just in case * we are dealing with index 0 (db_indx_t's are unsigned). */ for (i = NUM_ENT(p) - 1; ; i-- ) { inp[i + 2] = inp[i] - newbytes; if (i == H_KEYINDEX(ndx)) break; } /* Put the key and data on the page. */ inp[H_KEYINDEX(ndx)] = (db_indx_t)( (ndx == 0 ? psize : inp[H_DATAINDEX(ndx - 2)]) - key->size); inp[H_DATAINDEX(ndx)] = inp[H_KEYINDEX(ndx)] - data->size; memcpy(P_ENTRY(dbp, p, H_KEYINDEX(ndx)), key->data, key->size); memcpy(P_ENTRY(dbp, p, H_DATAINDEX(ndx)), data->data, data->size); /* Adjust page info. */ HOFFSET(p) -= newbytes; NUM_ENT(p) += 2;}/* * PUBLIC: int __ham_del_pair __P((DBC *, int)); */int__ham_del_pair(dbc, reclaim_page) DBC *dbc; int reclaim_page;{ DB *dbp; DBT data_dbt, key_dbt; DB_LSN new_lsn, *n_lsn, tmp_lsn; DB_MPOOLFILE *mpf; HASH_CURSOR *hcp; PAGE *n_pagep, *nn_pagep, *p, *p_pagep; db_ham_mode op; db_indx_t ndx; db_pgno_t chg_pgno, pgno, tmp_pgno; int ret, t_ret; u_int32_t order; dbp = dbc->dbp; mpf = dbp->mpf; hcp = (HASH_CURSOR *)dbc->internal; n_pagep = p_pagep = nn_pagep = NULL; ndx = hcp->indx; if (hcp->page == NULL && (ret = mpf->get(mpf, &hcp->pgno, DB_MPOOL_CREATE, &hcp->page)) != 0) return (ret); p = hcp->page; /* * We optimize for the normal case which is when neither the key nor * the data are large. In this case, we write a single log record * and do the delete. If either is large, we'll call __big_delete * to remove the big item and then update the page to remove the * entry referring to the big item. */ ret = 0; if (HPAGE_PTYPE(H_PAIRKEY(dbp, p, ndx)) == H_OFFPAGE) { memcpy(&pgno, HOFFPAGE_PGNO(P_ENTRY(dbp, p, H_KEYINDEX(ndx))), sizeof(db_pgno_t)); ret = __db_doff(dbc, pgno); } if (ret == 0) switch (HPAGE_PTYPE(H_PAIRDATA(dbp, p, ndx))) { case H_OFFPAGE: memcpy(&pgno, HOFFPAGE_PGNO(P_ENTRY(dbp, p, H_DATAINDEX(ndx))), sizeof(db_pgno_t)); ret = __db_doff(dbc, pgno); break; case H_OFFDUP: case H_DUPLICATE: /* * If we delete a pair that is/was a duplicate, then * we had better clear the flag so that we update the * cursor appropriately. */ F_CLR(hcp, H_ISDUP); break; } if (ret) return (ret); /* Now log the delete off this page. */ if (DBC_LOGGING(dbc)) { key_dbt.data = P_ENTRY(dbp, p, H_KEYINDEX(ndx)); key_dbt.size = LEN_HITEM(dbp, p, dbp->pgsize, H_KEYINDEX(ndx)); data_dbt.data = P_ENTRY(dbp, p, H_DATAINDEX(ndx)); data_dbt.size = LEN_HITEM(dbp, p, dbp->pgsize, H_DATAINDEX(ndx)); if ((ret = __ham_insdel_log(dbp, dbc->txn, &new_lsn, 0, DELPAIR, PGNO(p), (u_int32_t)ndx, &LSN(p), &key_dbt, &data_dbt)) != 0) return (ret); } else LSN_NOT_LOGGED(new_lsn); /* Move lsn onto page. */ LSN(p) = new_lsn; /* Do the delete. */ __ham_dpair(dbp, p, ndx); /* * Mark item deleted so that we don't try to return it, and * so that we update the cursor correctly on the next call * to next. */ F_SET(hcp, H_DELETED); F_CLR(hcp, H_OK); /* * Update cursors that are on the page where the delete happend. */ if ((ret = __ham_c_update(dbc, 0, 0, 0)) != 0) return (ret); /* * If we are locking, we will not maintain this, because it is * a hot spot. * * XXX * Perhaps we can retain incremental numbers and apply them later. */ if (!STD_LOCKING(dbc)) { --hcp->hdr->nelem; if ((ret = __ham_dirty_meta(dbc)) != 0) return (ret); } /* * If we need to reclaim the page, then check if the page is empty. * There are two cases. If it's empty and it's not the first page * in the bucket (i.e., the bucket page) then we can simply remove * it. If it is the first chain in the bucket, then we need to copy * the second page into it and remove the second page. * If its the only page in the bucket we leave it alone. */ if (!reclaim_page || NUM_ENT(p) != 0 || (PREV_PGNO(p) == PGNO_INVALID && NEXT_PGNO(p) == PGNO_INVALID)) return (mpf->set(mpf, p, DB_MPOOL_DIRTY)); if (PREV_PGNO(p) == PGNO_INVALID) { /* * First page in chain is empty and we know that there * are more pages in the chain. */ if ((ret = mpf->get(mpf, &NEXT_PGNO(p), 0, &n_pagep)) != 0) return (ret); if (NEXT_PGNO(n_pagep) != PGNO_INVALID && (ret = mpf->get(mpf, &NEXT_PGNO(n_pagep), 0, &nn_pagep)) != 0) goto err; if (DBC_LOGGING(dbc)) { key_dbt.data = n_pagep; key_dbt.size = dbp->pgsize; if ((ret = __ham_copypage_log(dbp, dbc->txn, &new_lsn, 0, PGNO(p), &LSN(p), PGNO(n_pagep), &LSN(n_pagep), NEXT_PGNO(n_pagep), nn_pagep == NULL ? NULL : &LSN(nn_pagep), &key_dbt)) != 0) goto err; } else LSN_NOT_LOGGED(new_lsn); /* Move lsn onto page. */ LSN(p) = new_lsn; /* Structure assignment. */ LSN(n_pagep) = new_lsn; if (NEXT_PGNO(n_pagep) != PGNO_INVALID) LSN(nn_pagep) = new_lsn; if (nn_pagep != NULL) { PREV_PGNO(nn_pagep) = PGNO(p); if ((ret = mpf->put(mpf, nn_pagep, DB_MPOOL_DIRTY)) != 0) { nn_pagep = NULL; goto err; } } tmp_pgno = PGNO(p); tmp_lsn = LSN(p); memcpy(p, n_pagep, dbp->pgsize); PGNO(p) = tmp_pgno; LSN(p) = tmp_lsn; PREV_PGNO(p) = PGNO_INVALID; /* * Update cursors to reflect the fact that records * on the second page have moved to the first page. */ if ((ret = __ham_c_delpg(dbc, PGNO(n_pagep), PGNO(p), 0, DB_HAM_DELFIRSTPG, &order)) != 0) goto err; /* * Update the cursor to reflect its new position. */ hcp->indx = 0; hcp->pgno = PGNO(p); hcp->order += order; if ((ret = mpf->set(mpf, p, DB_MPOOL_DIRTY)) != 0) goto err; if ((ret = __db_free(dbc, n_pagep)) != 0) { n_pagep = NULL; goto err; } } else { if ((ret = mpf->get(mpf, &PREV_PGNO(p), 0, &p_pagep)) != 0) goto err; if (NEXT_PGNO(p) != PGNO_INVALID) { if ((ret = mpf->get(mpf, &NEXT_PGNO(p), 0, &n_pagep)) != 0) goto err; n_lsn = &LSN(n_pagep); } else { n_pagep = NULL; n_lsn = NULL; } NEXT_PGNO(p_pagep) = NEXT_PGNO(p); if (n_pagep != NULL) PREV_PGNO(n_pagep) = PGNO(p_pagep); if (DBC_LOGGING(dbc)) { if ((ret = __ham_newpage_log(dbp, dbc->txn, &new_lsn, 0, DELOVFL, PREV_PGNO(p), &LSN(p_pagep), PGNO(p), &LSN(p), NEXT_PGNO(p), n_lsn)) != 0) goto err; } else LSN_NOT_LOGGED(new_lsn); /* Move lsn onto page. */ LSN(p_pagep) = new_lsn; /* Structure assignment. */ if (n_pagep) LSN(n_pagep) = new_lsn; LSN(p) = new_lsn; if (NEXT_PGNO(p) == PGNO_INVALID) { /* * There is no next page; put the cursor on the * previous page as if we'd deleted the last item * on that page, with index after the last valid * entry. * * The deleted flag was set up above. */ hcp->pgno = PGNO(p_pagep); hcp->indx = NUM_ENT(p_pagep); op = DB_HAM_DELLASTPG; } else { /* * There is a next page, so put the cursor at * the beginning of it. */ hcp->pgno = NEXT_PGNO(p); hcp->indx = 0; op = DB_HAM_DELMIDPG; } /* * Since we are about to delete the cursor page and we have * just moved the cursor, we need to make sure that the * old page pointer isn't left hanging around in the cursor. */ hcp->page = NULL; chg_pgno = PGNO(p); ret = __db_free(dbc, p); if ((t_ret = mpf->put(mpf, p_pagep, DB_MPOOL_DIRTY)) != 0 && ret == 0) ret = t_ret; if (n_pagep != NULL && (t_ret = mpf->put(mpf, n_pagep, DB_MPOOL_DIRTY)) != 0 && ret == 0) ret = t_ret; if (ret != 0) return (ret); if ((ret = __ham_c_delpg(dbc, chg_pgno, hcp->pgno, hcp->indx, op, &order)) != 0) return (ret); hcp->order += order; } return (ret);err: /* Clean up any pages. */ if (n_pagep != NULL) (void)mpf->put(mpf, n_pagep, 0); if (nn_pagep != NULL) (void)mpf->put(mpf, nn_pagep, 0); if (p_pagep != NULL) (void)mpf->put(mpf, p_pagep, 0); return (ret);}/* * __ham_replpair -- * Given the key data indicated by the cursor, replace part/all of it * according to the fields in the dbt. * * PUBLIC: int __ham_replpair __P((DBC *, DBT *, u_int32_t)); */int__ham_replpair(dbc, dbt, make_dup) DBC *dbc; DBT *dbt; u_int32_t make_dup;{ DB *dbp; DBT old_dbt, tdata, tmp; DB_ENV *dbenv; DB_LSN new_lsn; HASH_CURSOR *hcp; int32_t change; /* XXX: Possible overflow. */ u_int32_t dup_flag, len, memsize; int beyond_eor, is_big, ret, type; u_int8_t *beg, *dest, *end, *hk, *src; void *memp; /* * Big item replacements are handled in generic code. * Items that fit on the current page fall into 4 classes. * 1. On-page element, same size * 2. On-page element, new is bigger (fits) * 3. On-page element, new is bigger (does not fit) * 4. On-page element, old is bigger * Numbers 1, 2, and 4 are essentially the same (and should * be the common case). We handle case 3 as a delete and * add. */ dbp = dbc->dbp; dbenv = dbp->dbenv; hcp = (HASH_CURSOR *)dbc->internal; /* * We need to compute the number of bytes that we are adding or * removing from the entry. Normally, we can simply substract * the number of bytes we are replacing (dbt->dlen) from the * number of bytes we are inserting (dbt->size). However, if * we are doing a partial put off the end of a record, then this * formula doesn't work, because we are essentially adding * new bytes. */ change = dbt->size - dbt->dlen; hk = H_PAIRDATA(dbp, hcp->page, hcp->indx); is_big = HPAGE_PTYPE(hk) == H_OFFPAGE; if (is_big) memcpy(&len, HOFFPAGE_TLEN(hk), sizeof(u_int32_t)); else len = LEN_HKEYDATA(dbp, hcp->page, dbp->pgsize, H_DATAINDEX(hcp->indx)); beyond_eor = dbt->doff + dbt->dlen > len; if (beyond_eor) change += dbt->doff + dbt->dlen - len; if (change > (int32_t)P_FREESPACE(dbp, hcp->page) || beyond_eor || is_big) { /* * Case 3 -- two subcases. * A. This is not really a partial operation, but an overwrite. * Simple del and add works. * B. This is a partial and we need to construct the data that * we are really inserting (yuck). * In both cases, we need to grab the key off the page (in * some cases we could do this outside of this routine; for * cleanliness we do it here. If you happen to be on a big * key, this could be a performance hit). */ memset(&tmp, 0, sizeof(tmp)); if ((ret = __db_ret(dbp, hcp->page, H_KEYINDEX(hcp->indx), &tmp, &dbc->rkey->data, &dbc->rkey->ulen)) != 0) return (ret); /* Preserve duplicate info. */ dup_flag = F_ISSET(hcp, H_ISDUP); if (dbt->doff == 0 && dbt->dlen == len) { ret = __ham_del_pair(dbc, 0); if (ret == 0) ret = __ham_add_el(dbc, &tmp, dbt, dup_flag ? H_DUPLICATE : H_KEYDATA); } else { /* Case B */ type = HPAGE_PTYPE(hk) != H_OFFPAGE ? HPAGE_PTYPE(hk) : H_KEYDATA; memset(&tdata, 0, sizeof(tdata)); memp = NULL; memsize = 0; if ((ret = __db_ret(dbp, hcp->page, H_DATAINDEX(hcp->indx), &tdata, &memp, &memsize)) != 0) goto err; /* Now we can delete the item. */ if ((ret = __ham_del_pair(dbc, 0)) != 0) { __os_free(dbenv, memp); goto err; } /* Now shift old data around to make room for new. */ if (change > 0) { if ((ret = __os_realloc(dbenv, tdata.size + change, &tdata.data)) != 0) return (ret); memp = tdata.data; memsize = tdata.size + change; memset((u_int8_t *)tdata.data + tdata.size, 0, change); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -