📄 bt_cursor.c
字号:
space = *spacep + size; } break; } memcpy(dp, (u_int8_t *)pg + HOFFSET(pg), size); need_pg = 0; space -= size; np += size; } if (first == 0 && keyoff != NULL) { *offp-- = keyoff[0]; *offp-- = keyoff[-1]; } size = bk->len; *offp-- = (int32_t)(inp[indx] - HOFFSET(pg) + dp - dbuf + SSZA(BKEYDATA, data)); } *offp-- = size; first = 0; if (no_dup) break;contin: indx++; if (opd->dbtype == DB_RECNO) cp->recno++; } while (indx < NUM_ENT(pg)); if (no_dup) break; cp->indx = indx; } while (ret == 0); /* Return the updated information. */ *spacep = space; *offpp = offp; *dpp = np; /* * If we ran out of space back up the pointer. * If we did not return any dups or reached the end, close the opd. */ if (ret == ENOMEM) { if (opd->dbtype == DB_RECNO) { if (--cp->recno == 0) goto close_opd; } else if (indx != 0) cp->indx--; else { t_ret = __bam_c_prev(opd); if (t_ret == DB_NOTFOUND) goto close_opd; if (t_ret != 0) ret = t_ret; } } else if (keyoff == NULL && ret == DB_NOTFOUND) { cp->indx--; if (opd->dbtype == DB_RECNO) --cp->recno; } else if (indx == 0 || ret == DB_NOTFOUND) {close_opd: opd->c_close(opd); ((BTREE_CURSOR *)dbc->internal)->opd = NULL; } if (ret == DB_NOTFOUND) ret = 0; return (ret);}/* * __bam_getbothc -- * Search for a matching data item on a join. */static int__bam_getbothc(dbc, data) DBC *dbc; DBT *data;{ BTREE_CURSOR *cp; DB *dbp; DB_MPOOLFILE *mpf; int cmp, exact, ret; dbp = dbc->dbp; mpf = dbp->mpf; cp = (BTREE_CURSOR *)dbc->internal; /* * Acquire the current page. We have at least a read-lock * already. The caller may have set DB_RMW asking for a * write lock, but upgrading to a write lock has no better * chance of succeeding now instead of later, so don't try. */ if ((ret = mpf->get(mpf, &cp->pgno, 0, &cp->page)) != 0) return (ret); /* * An off-page duplicate cursor. Search the remaining duplicates * for one which matches (do a normal btree search, then verify * that the retrieved record is greater than the original one). */ if (F_ISSET(dbc, DBC_OPD)) { /* * Check to make sure the desired item comes strictly after * the current position; if it doesn't, return DB_NOTFOUND. */ if ((ret = __bam_cmp(dbp, data, cp->page, cp->indx, dbp->dup_compare == NULL ? __bam_defcmp : dbp->dup_compare, &cmp)) != 0) return (ret); if (cmp <= 0) return (DB_NOTFOUND); /* Discard the current page, we're going to do a full search. */ if ((ret = mpf->put(mpf, cp->page, 0)) != 0) return (ret); cp->page = NULL; return (__bam_c_search(dbc, PGNO_INVALID, data, DB_GET_BOTH, &exact)); } /* * We're doing a DBC->c_get(DB_GET_BOTHC) and we're already searching * a set of on-page duplicates (either sorted or unsorted). Continue * a linear search from after the current position. * * (Note that we could have just finished a "set" of one duplicate, * i.e. not a duplicate at all, but the following check will always * return DB_NOTFOUND in this case, which is the desired behavior.) */ if (cp->indx + P_INDX >= NUM_ENT(cp->page) || !IS_DUPLICATE(dbc, cp->indx, cp->indx + P_INDX)) return (DB_NOTFOUND); cp->indx += P_INDX; return (__bam_getboth_finddatum(dbc, data, DB_GET_BOTH));}/* * __bam_getboth_finddatum -- * Find a matching on-page data item. */static int__bam_getboth_finddatum(dbc, data, flags) DBC *dbc; DBT *data; u_int32_t flags;{ BTREE_CURSOR *cp; DB *dbp; db_indx_t base, lim, top; int cmp, ret; dbp = dbc->dbp; cp = (BTREE_CURSOR *)dbc->internal; /* * Called (sometimes indirectly) from DBC->get to search on-page data * item(s) for a matching value. If the original flag was DB_GET_BOTH * or DB_GET_BOTH_RANGE, the cursor is set to the first undeleted data * item for the key. If the original flag was DB_GET_BOTHC, the cursor * argument is set to the first data item we can potentially return. * In both cases, there may or may not be additional duplicate data * items to search. * * If the duplicates are not sorted, do a linear search. */ if (dbp->dup_compare == NULL) { for (;; cp->indx += P_INDX) { if (!IS_CUR_DELETED(dbc) && (ret = __bam_cmp(dbp, data, cp->page, cp->indx + O_INDX, __bam_defcmp, &cmp)) != 0) return (ret); if (cmp == 0) return (0); if (cp->indx + P_INDX >= NUM_ENT(cp->page) || !IS_DUPLICATE(dbc, cp->indx, cp->indx + P_INDX)) break; } return (DB_NOTFOUND); } /* * If the duplicates are sorted, do a binary search. The reason for * this is that large pages and small key/data pairs result in large * numbers of on-page duplicates before they get pushed off-page. * * Find the top and bottom of the duplicate set. Binary search * requires at least two items, don't loop if there's only one. */ for (base = top = cp->indx; top < NUM_ENT(cp->page); top += P_INDX) if (!IS_DUPLICATE(dbc, cp->indx, top)) break; if (base == (top - P_INDX)) { if ((ret = __bam_cmp(dbp, data, cp->page, cp->indx + O_INDX, dbp->dup_compare, &cmp)) != 0) return (ret); return (cmp == 0 || (cmp < 0 && flags == DB_GET_BOTH_RANGE) ? 0 : DB_NOTFOUND); } for (lim = (top - base) / (db_indx_t)P_INDX; lim != 0; lim >>= 1) { cp->indx = base + ((lim >> 1) * P_INDX); if ((ret = __bam_cmp(dbp, data, cp->page, cp->indx + O_INDX, dbp->dup_compare, &cmp)) != 0) return (ret); if (cmp == 0) { /* * XXX * No duplicate duplicates in sorted duplicate sets, * so there can be only one. */ if (!IS_CUR_DELETED(dbc)) return (0); break; } if (cmp > 0) { base = cp->indx + P_INDX; --lim; } } /* No match found; if we're looking for an exact match, we're done. */ if (flags == DB_GET_BOTH) return (DB_NOTFOUND); /* * Base is the smallest index greater than the data item, may be zero * or a last + O_INDX index, and may be deleted. Find an undeleted * item. */ cp->indx = base; while (cp->indx < top && IS_CUR_DELETED(dbc)) cp->indx += P_INDX; return (cp->indx < top ? 0 : DB_NOTFOUND);}/* * __bam_c_put -- * Put using a cursor. */static int__bam_c_put(dbc, key, data, flags, pgnop) DBC *dbc; DBT *key, *data; u_int32_t flags; db_pgno_t *pgnop;{ BTREE_CURSOR *cp; DB *dbp; DBT dbt; DB_MPOOLFILE *mpf; db_pgno_t root_pgno; u_int32_t iiop; int cmp, exact, ret, stack; void *arg; dbp = dbc->dbp; mpf = dbp->mpf; cp = (BTREE_CURSOR *)dbc->internal; root_pgno = cp->root;split: ret = stack = 0; switch (flags) { case DB_AFTER: case DB_BEFORE: case DB_CURRENT: iiop = flags; /* * If the Btree has record numbers (and we're not replacing an * existing record), we need a complete stack so that we can * adjust the record counts. The check for flags == DB_CURRENT * is superfluous but left in for clarity. (If C_RECNUM is set * we know that flags must be DB_CURRENT, as DB_AFTER/DB_BEFORE * are illegal in a Btree unless it's configured for duplicates * and you cannot configure a Btree for both record renumbering * and duplicates.) */ if (flags == DB_CURRENT && F_ISSET(cp, C_RECNUM) && F_ISSET(cp, C_DELETED)) { if ((ret = __bam_c_getstack(dbc)) != 0) goto err; /* * Initialize the cursor from the stack. Don't take * the page number or page index, they should already * be set. */ cp->page = cp->csp->page; cp->lock = cp->csp->lock; cp->lock_mode = cp->csp->lock_mode; stack = 1; break; } /* Acquire the current page with a write lock. */ ACQUIRE_WRITE_LOCK(dbc, ret); if (ret != 0) goto err; if ((ret = mpf->get(mpf, &cp->pgno, 0, &cp->page)) != 0) goto err; break; case DB_KEYFIRST: case DB_KEYLAST: case DB_NODUPDATA: /* * Searching off-page, sorted duplicate tree: do a tree search * for the correct item; __bam_c_search returns the smallest * slot greater than the key, use it. * * See comment below regarding where we can start the search. */ if (F_ISSET(dbc, DBC_OPD)) { if ((ret = __bam_c_search(dbc, F_ISSET(cp, C_RECNUM) ? cp->root : root_pgno, data, flags, &exact)) != 0) goto err; stack = 1; /* Disallow "sorted" duplicate duplicates. */ if (exact) { if (IS_DELETED(dbp, cp->page, cp->indx)) { iiop = DB_CURRENT; break; } ret = __db_duperr(dbp, flags); goto err; } iiop = DB_BEFORE; break; } /* * Searching a btree. * * If we've done a split, we can start the search from the * parent of the split page, which __bam_split returned * for us in root_pgno, unless we're in a Btree with record * numbering. In that case, we'll need the true root page * in order to adjust the record count. */ if ((ret = __bam_c_search(dbc, F_ISSET(cp, C_RECNUM) ? cp->root : root_pgno, key, flags == DB_KEYFIRST || dbp->dup_compare != NULL ? DB_KEYFIRST : DB_KEYLAST, &exact)) != 0) goto err; stack = 1; /* * If we don't have an exact match, __bam_c_search returned * the smallest slot greater than the key, use it. */ if (!exact) { iiop = DB_KEYFIRST; break; } /* * If duplicates aren't supported, replace the current item. * (If implementing the DB->put function, our caller already * checked the DB_NOOVERWRITE flag.) */ if (!F_ISSET(dbp, DB_AM_DUP)) { iiop = DB_CURRENT; break; } /* * If we find a matching entry, it may be an off-page duplicate * tree. Return the page number to our caller, we need a new * cursor. */ if (pgnop != NULL && __bam_isopd(dbc, pgnop)) goto done; /* If the duplicates aren't sorted, move to the right slot. */ if (dbp->dup_compare == NULL) { if (flags == DB_KEYFIRST) iiop = DB_BEFORE; else for (;; cp->indx += P_INDX) if (cp->indx + P_INDX >= NUM_ENT(cp->page) || !IS_DUPLICATE(dbc, cp->indx, cp->indx + P_INDX)) { iiop = DB_AFTER; break; } break; } /* * We know that we're looking at the first of a set of sorted * on-page duplicates. Walk the list to find the right slot. */ for (;; cp->indx += P_INDX) { if ((ret = __bam_cmp(dbp, data, cp->page, cp->indx + O_INDX, dbp->dup_compare, &cmp)) != 0) goto err; if (cmp < 0) { iiop = DB_BEFORE; break; } /* Disallow "sorted" duplicate duplicates. */ if (cmp == 0) { if (IS_DELETED(dbp, cp->page, cp->indx)) { iiop = DB_CURRENT; break; } ret = __db_duperr(dbp, flags); goto err; } if (cp->indx + P_INDX >= NUM_ENT(cp->page) || P_INP(dbp, ((PAGE *)cp->page))[cp->indx] != P_INP(dbp, ((PAGE *)cp->page))[cp->indx + P_INDX]) { iiop = DB_AFTER; break; } } break; default: ret = __db_unknown_flag(dbp->dbenv, "__bam_c_put", flags); goto err; } switch (ret = __bam_iitem(dbc, key, data, iiop, 0)) { case 0: break; case DB_NEEDSPLIT: /* * To split, we need a key for the page. Either use the key * argument or get a copy of the key from the page. */ if (flags == DB_AFTER || flags == DB_BEFORE || flags == DB_CURRENT) { memset(&dbt, 0, sizeof(DBT)); if ((ret = __db_ret(dbp, cp->page, 0, &dbt, &dbc->rkey->data, &dbc->rkey->ulen)) != 0) goto err; arg = &dbt; } else arg = F_ISSET(dbc, DBC_OPD) ? data : key; /* * Discard any locks and pinned pages (the locks are discarded * even if we're running with transactions, as they lock pages * that we're sorry we ever acquired). If stack is set and the * cursor entries are valid, they point to the same entries as * the stack, don't free them twice. */ if (stack) ret = __bam_stkrel(dbc, STK_CLRDBC | STK_NOLOCK); else DISCARD_CUR(dbc, ret); if (ret != 0) goto err; /* Split the tree. */ if ((ret = __bam_split(dbc, arg, &root_pgno)) != 0) return (ret); goto split; default: goto err; }err:done: /* * Discard any pages pinned in the tree and their locks, except for * the leaf page. Note, the leaf page participated in any stack we * acquired, and so we have to adjust the stack as necessary. If * there was only a single page on the stack, we don't have to free * further stack pages. */ if (stack && BT_STK_POP(cp) != NULL) (void)__bam_stkrel(dbc, 0); /* * Regardless of whether we were successful or not, clear the delete * flag. If we're successful, we either moved the cursor or the item * is no longer deleted. If we're not successful, then we're just a * copy, no need to have the flag set. */ F_CLR(cp, C_DELETED); return (ret);}/* * __bam_c_rget -- * Return the record number for a cursor. * * PUBLIC: int __bam_c_rget __P((DBC *, DBT *)); */int__bam_c_rget(dbc, data) DBC *dbc;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -