📄 bt_cursor.c
字号:
* * Lock the primary database page, regardless of whether we're deleting * an item on a primary database page or an off-page duplicates page. */ ACQUIRE(dbc, DB_LOCK_WRITE, cp->pgno, cp_c->lock, cp_c->pgno, cp_c->page, ret); if (ret != 0) goto err;delete: /* * If the delete occurred in a btree, delete the on-page physical item * referenced by the cursor. */ if (dbc_c->dbtype == DB_BTREE && (ret = __bam_c_physdel(dbc_c)) != 0) goto err; /* * If we're not working in an off-page duplicate tree, then we're * done. */ if (!F_ISSET(dbc_c, DBC_OPD) || root_pgno == PGNO_INVALID) goto done; /* * We may have just deleted the last element in the off-page duplicate * tree, and closed the last cursor in the tree. For an off-page btree * there are no other cursors in the tree by definition, if the tree is * empty. For an off-page recno we know we have closed the last cursor * in the tree because the __ram_ca_delete call above returned 0 only * in that case. So, if the off-page duplicate tree is empty at this * point, we want to remove it. */ if ((ret = mpf->get(mpf, &root_pgno, 0, &h)) != 0) goto err; if (NUM_ENT(h) == 0) { if ((ret = __db_free(dbc, h)) != 0) goto err; } else { if ((ret = mpf->put(mpf, h, 0)) != 0) goto err; goto done; } /* * When removing the tree, we have to do one of two things. If this is * case #2, that is, the primary tree is a btree, delete the key that's * associated with the tree from the btree leaf page. We know we are * the only reference to it and we already have the correct lock. We * detect this case because the cursor that was passed to us references * an off-page duplicate cursor. * * If this is case #3, that is, the primary tree isn't a btree, pass * the information back to our caller, it's their job to do cleanup on * the primary page. */ if (dbc_opd != NULL) { if ((ret = mpf->get(mpf, &cp->pgno, 0, &cp->page)) != 0) goto err; if ((ret = __bam_c_physdel(dbc)) != 0) goto err; } else *rmroot = 1;err:done: /* * Discard the page references and locks, and confirm that the stack * has been emptied. */ if (dbc_opd != NULL) { DISCARD_CUR(dbc_opd, t_ret); if (t_ret != 0 && ret == 0) ret = t_ret; } DISCARD_CUR(dbc, t_ret); if (t_ret != 0 && ret == 0) ret = t_ret; /* Downgrade any CDB lock we acquired. */ if (cdb_lock) (void)__lock_downgrade( dbp->dbenv, &dbc->mylock, DB_LOCK_IWRITE, 0); return (ret);}/* * __bam_c_destroy -- * Close a single cursor -- internal version. */static int__bam_c_destroy(dbc) DBC *dbc;{ /* Discard the structures. */ __os_free(dbc->dbp->dbenv, dbc->internal); return (0);}/* * __bam_c_count -- * Return a count of on and off-page duplicates. * * PUBLIC: int __bam_c_count __P((DBC *, db_recno_t *)); */int__bam_c_count(dbc, recnop) DBC *dbc; db_recno_t *recnop;{ BTREE_CURSOR *cp; DB *dbp; DB_MPOOLFILE *mpf; db_indx_t indx, top; db_recno_t recno; int ret; dbp = dbc->dbp; mpf = dbp->mpf; cp = (BTREE_CURSOR *)dbc->internal; /* * Called with the top-level cursor that may reference an off-page * duplicates page. If it's a set of on-page duplicates, get the * page and count. Otherwise, get the root page of the off-page * duplicate tree, and use the count. We don't have to acquire any * new locks, we have to have a read lock to even get here. */ if (cp->opd == NULL) { if ((ret = mpf->get(mpf, &cp->pgno, 0, &cp->page)) != 0) return (ret); /* * Move back to the beginning of the set of duplicates and * then count forward. */ for (indx = cp->indx;; indx -= P_INDX) if (indx == 0 || !IS_DUPLICATE(dbc, indx, indx - P_INDX)) break; for (recno = 1, top = NUM_ENT(cp->page) - P_INDX; indx < top; ++recno, indx += P_INDX) if (!IS_DUPLICATE(dbc, indx, indx + P_INDX)) break; *recnop = recno; } else { if ((ret = mpf->get(mpf, &cp->opd->internal->root, 0, &cp->page)) != 0) return (ret); *recnop = RE_NREC(cp->page); } ret = mpf->put(mpf, cp->page, 0); cp->page = NULL; return (ret);}/* * __bam_c_del -- * Delete using a cursor. */static int__bam_c_del(dbc) DBC *dbc;{ BTREE_CURSOR *cp; DB *dbp; DB_MPOOLFILE *mpf; int ret, t_ret; dbp = dbc->dbp; mpf = dbp->mpf; cp = (BTREE_CURSOR *)dbc->internal; ret = 0; /* If the item was already deleted, return failure. */ if (F_ISSET(cp, C_DELETED)) return (DB_KEYEMPTY); /* * This code is always called with a page lock but no page. */ DB_ASSERT(cp->page == NULL); /* * We don't physically delete the record until the cursor moves, so * we have to have a long-lived write lock on the page instead of a * a long-lived read lock. Note, we have to have a read lock to even * get here. * * If we're maintaining record numbers, we lock the entire tree, else * we lock the single page. */ if (F_ISSET(cp, C_RECNUM)) { if ((ret = __bam_c_getstack(dbc)) != 0) goto err; cp->page = cp->csp->page; } else { ACQUIRE_CUR(dbc, DB_LOCK_WRITE, cp->pgno, ret); if (ret != 0) goto err; } /* Log the change. */ if (DBC_LOGGING(dbc)) { if ((ret = __bam_cdel_log(dbp, dbc->txn, &LSN(cp->page), 0, PGNO(cp->page), &LSN(cp->page), cp->indx)) != 0) goto err; } else LSN_NOT_LOGGED(LSN(cp->page)); /* Set the intent-to-delete flag on the page. */ if (TYPE(cp->page) == P_LBTREE) B_DSET(GET_BKEYDATA(dbp, cp->page, cp->indx + O_INDX)->type); else B_DSET(GET_BKEYDATA(dbp, cp->page, cp->indx)->type); /* Mark the page dirty. */ ret = mpf->set(mpf, cp->page, DB_MPOOL_DIRTY);err: /* * If we've been successful so far and the tree has record numbers, * adjust the record counts. Either way, release acquired page(s). */ if (F_ISSET(cp, C_RECNUM)) { if (ret == 0) ret = __bam_adjust(dbc, -1); (void)__bam_stkrel(dbc, 0); } else if (cp->page != NULL && (t_ret = mpf->put(mpf, cp->page, 0)) != 0 && ret == 0) ret = t_ret; cp->page = NULL; /* Update the cursors last, after all chance of failure is past. */ if (ret == 0) (void)__bam_ca_delete(dbp, cp->pgno, cp->indx, 1); return (ret);}/* * __bam_c_dup -- * Duplicate a btree cursor, such that the new one holds appropriate * locks for the position of the original. * * PUBLIC: int __bam_c_dup __P((DBC *, DBC *)); */int__bam_c_dup(orig_dbc, new_dbc) DBC *orig_dbc, *new_dbc;{ BTREE_CURSOR *orig, *new; int ret; orig = (BTREE_CURSOR *)orig_dbc->internal; new = (BTREE_CURSOR *)new_dbc->internal; /* * If we're holding a lock we need to acquire a copy of it, unless * we're in a transaction. We don't need to copy any lock we're * holding inside a transaction because all the locks are retained * until the transaction commits or aborts. */ if (LOCK_ISSET(orig->lock) && orig_dbc->txn == NULL) { if ((ret = __db_lget(new_dbc, 0, new->pgno, new->lock_mode, 0, &new->lock)) != 0) return (ret); } new->ovflsize = orig->ovflsize; new->recno = orig->recno; new->flags = orig->flags; return (0);}/* * __bam_c_get -- * Get using a cursor (btree). */static int__bam_c_get(dbc, key, data, flags, pgnop) DBC *dbc; DBT *key, *data; u_int32_t flags; db_pgno_t *pgnop;{ BTREE_CURSOR *cp; DB *dbp; DB_MPOOLFILE *mpf; db_pgno_t orig_pgno; db_indx_t orig_indx; int exact, newopd, ret; dbp = dbc->dbp; mpf = dbp->mpf; cp = (BTREE_CURSOR *)dbc->internal; orig_pgno = cp->pgno; orig_indx = cp->indx; newopd = 0; switch (flags) { case DB_CURRENT: /* It's not possible to return a deleted record. */ if (F_ISSET(cp, C_DELETED)) { ret = DB_KEYEMPTY; goto err; } /* * 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) goto err; break; case DB_FIRST: newopd = 1; if ((ret = __bam_c_first(dbc)) != 0) goto err; break; case DB_GET_BOTH: case DB_GET_BOTH_RANGE: /* * There are two ways to get here based on DBcursor->c_get * with the DB_GET_BOTH/DB_GET_BOTH_RANGE flags set: * * 1. Searching a sorted off-page duplicate tree: do a tree * search. * * 2. Searching btree: do a tree search. If it returns a * reference to off-page duplicate tree, return immediately * and let our caller deal with it. If the search doesn't * return a reference to off-page duplicate tree, continue * with an on-page search. */ if (F_ISSET(dbc, DBC_OPD)) { if ((ret = __bam_c_search( dbc, PGNO_INVALID, data, flags, &exact)) != 0) goto err; if (flags == DB_GET_BOTH) { if (!exact) { ret = DB_NOTFOUND; goto err; } break; } /* * We didn't require an exact match, so the search may * may have returned an entry past the end of the page, * or we may be referencing a deleted record. If so, * move to the next entry. */ if ((cp->indx == NUM_ENT(cp->page) || IS_CUR_DELETED(dbc)) && (ret = __bam_c_next(dbc, 1, 0)) != 0) goto err; } else { if ((ret = __bam_c_search( dbc, PGNO_INVALID, key, flags, &exact)) != 0) return (ret); if (!exact) { ret = DB_NOTFOUND; goto err; } if (pgnop != NULL && __bam_isopd(dbc, pgnop)) { newopd = 1; break; } if ((ret = __bam_getboth_finddatum(dbc, data, flags)) != 0) goto err; } break; case DB_GET_BOTHC: if ((ret = __bam_getbothc(dbc, data)) != 0) goto err; break; case DB_LAST: newopd = 1; if ((ret = __bam_c_last(dbc)) != 0) goto err; break; case DB_NEXT: newopd = 1; if (cp->pgno == PGNO_INVALID) { if ((ret = __bam_c_first(dbc)) != 0) goto err; } else if ((ret = __bam_c_next(dbc, 1, 0)) != 0) goto err; break; case DB_NEXT_DUP: if ((ret = __bam_c_next(dbc, 1, 0)) != 0) goto err; if (!IS_CUR_DUPLICATE(dbc, orig_pgno, orig_indx)) { ret = DB_NOTFOUND; goto err; } break; case DB_NEXT_NODUP: newopd = 1; if (cp->pgno == PGNO_INVALID) { if ((ret = __bam_c_first(dbc)) != 0) goto err; } else do { if ((ret = __bam_c_next(dbc, 1, 0)) != 0) goto err; } while (IS_CUR_DUPLICATE(dbc, orig_pgno, orig_indx)); break; case DB_PREV: newopd = 1; if (cp->pgno == PGNO_INVALID) { if ((ret = __bam_c_last(dbc)) != 0) goto err; } else if ((ret = __bam_c_prev(dbc)) != 0) goto err; break; case DB_PREV_NODUP: newopd = 1; if (cp->pgno == PGNO_INVALID) { if ((ret = __bam_c_last(dbc)) != 0) goto err; } else do { if ((ret = __bam_c_prev(dbc)) != 0) goto err; } while (IS_CUR_DUPLICATE(dbc, orig_pgno, orig_indx)); break; case DB_SET: case DB_SET_RECNO: newopd = 1; if ((ret = __bam_c_search(dbc, PGNO_INVALID, key, flags, &exact)) != 0) goto err; break; case DB_SET_RANGE: newopd = 1; if ((ret = __bam_c_search(dbc, PGNO_INVALID, key, flags, &exact)) != 0) goto err; /* * As we didn't require an exact match, the search function * may have returned an entry past the end of the page. Or, * we may be referencing a deleted record. If so, move to * the next entry. */ if (cp->indx == NUM_ENT(cp->page) || IS_CUR_DELETED(dbc)) if ((ret = __bam_c_next(dbc, 0, 0)) != 0) goto err; break; default: ret = __db_unknown_flag(dbp->dbenv, "__bam_c_get", flags); goto err; } /* * We may have moved to an off-page duplicate tree. Return that * information to our caller. */ if (newopd && pgnop != NULL) (void)__bam_isopd(dbc, pgnop); /* * Don't return the key, it was passed to us (this is true even if the * application defines a compare function returning equality for more * than one key value, since in that case which actual value we store * in the database is undefined -- and particularly true in the case of * duplicates where we only store one key value). */ if (flags == DB_GET_BOTH || flags == DB_GET_BOTH_RANGE || flags == DB_SET) F_SET(key, DB_DBT_ISSET);err: /* * Regardless of whether we were successful or not, if the cursor * moved, clear the delete flag, DBcursor->c_get never references * a deleted key, if it moved at all. */ if (F_ISSET(cp, C_DELETED) && (cp->pgno != orig_pgno || cp->indx != orig_indx)) F_CLR(cp, C_DELETED); return (ret);}static int__bam_get_prev(dbc) DBC *dbc;{ BTREE_CURSOR *cp; DBT key, data; db_pgno_t pgno; int ret;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -