📄 db_cam.c
字号:
dbc_n->flags |= dbc_orig->flags & ~DBC_OWN_LID; int_n->indx = int_orig->indx; int_n->pgno = int_orig->pgno; int_n->root = int_orig->root; int_n->lock_mode = int_orig->lock_mode; switch (dbc_orig->dbtype) { case DB_QUEUE: if ((ret = __qam_c_dup(dbc_orig, dbc_n)) != 0) goto err; break; case DB_BTREE: case DB_RECNO: if ((ret = __bam_c_dup(dbc_orig, dbc_n)) != 0) goto err; break; case DB_HASH: if ((ret = __ham_c_dup(dbc_orig, dbc_n)) != 0) goto err; break; default: ret = __db_unknown_type(dbp->dbenv, "__db_c_idup", dbc_orig->dbtype); goto err; } } /* Now take care of duping the CDB information. */ CDB_LOCKING_COPY(dbp, dbc_orig, dbc_n); /* Copy the dirty read flag to the new cursor. */ F_SET(dbc_n, F_ISSET(dbc_orig, DBC_DIRTY_READ)); *dbcp = dbc_n; return (0);err: (void)dbc_n->c_close(dbc_n); return (ret);}/* * __db_c_newopd -- * Create a new off-page duplicate cursor. * * PUBLIC: int __db_c_newopd __P((DBC *, db_pgno_t, DBC *, DBC **)); */int__db_c_newopd(dbc_parent, root, oldopd, dbcp) DBC *dbc_parent; db_pgno_t root; DBC *oldopd; DBC **dbcp;{ DB *dbp; DBC *opd; DBTYPE dbtype; int ret; dbp = dbc_parent->dbp; dbtype = (dbp->dup_compare == NULL) ? DB_RECNO : DB_BTREE; /* * On failure, we want to default to returning the old off-page dup * cursor, if any; our caller can't be left with a dangling pointer * to a freed cursor. On error the only allowable behavior is to * close the cursor (and the old OPD cursor it in turn points to), so * this should be safe. */ *dbcp = oldopd; if ((ret = __db_icursor(dbp, dbc_parent->txn, dbtype, root, 1, dbc_parent->locker, &opd)) != 0) return (ret); /* !!! * If the parent is a DBC_WRITER, this won't copy anything. That's * not actually a problem--we only need lock information in an * off-page dup cursor in order to upgrade at cursor close time * if we've done a delete, but WRITERs don't need to upgrade. */ CDB_LOCKING_COPY(dbp, dbc_parent, opd); *dbcp = opd; /* * Check to see if we already have an off-page dup cursor that we've * passed in. If we do, close it. It'd be nice to use it again * if it's a cursor belonging to the right tree, but if we're doing * a cursor-relative operation this might not be safe, so for now * we'll take the easy way out and always close and reopen. * * Note that under no circumstances do we want to close the old * cursor without returning a valid new one; we don't want to * leave the main cursor in our caller with a non-NULL pointer * to a freed off-page dup cursor. */ if (oldopd != NULL && (ret = oldopd->c_close(oldopd)) != 0) return (ret); return (0);}/* * __db_c_get -- * Get using a cursor. * * PUBLIC: int __db_c_get __P((DBC *, DBT *, DBT *, u_int32_t)); */int__db_c_get(dbc_arg, key, data, flags) DBC *dbc_arg; DBT *key, *data; u_int32_t flags;{ DB *dbp; DBC *dbc, *dbc_n, *opd; DBC_INTERNAL *cp, *cp_n; DB_MPOOLFILE *mpf; db_pgno_t pgno; u_int32_t multi, tmp_dirty, tmp_flags, tmp_rmw; u_int8_t type; int ret, t_ret; /* * Cursor Cleanup Note: * All of the cursors passed to the underlying access methods by this * routine are duplicated cursors. On return, any referenced pages * will be discarded, and, if the cursor is not intended to be used * again, the close function will be called. So, pages/locks that * the cursor references do not need to be resolved by the underlying * functions. */ dbp = dbc_arg->dbp; mpf = dbp->mpf; dbc_n = NULL; opd = NULL; PANIC_CHECK(dbp->dbenv); /* Check for invalid flags. */ if ((ret = __db_cgetchk(dbp, key, data, flags, IS_INITIALIZED(dbc_arg))) != 0) return (ret); /* Clear OR'd in additional bits so we can check for flag equality. */ tmp_rmw = LF_ISSET(DB_RMW); LF_CLR(DB_RMW); tmp_dirty = LF_ISSET(DB_DIRTY_READ); LF_CLR(DB_DIRTY_READ); multi = LF_ISSET(DB_MULTIPLE|DB_MULTIPLE_KEY); LF_CLR(DB_MULTIPLE|DB_MULTIPLE_KEY); DEBUG_LREAD(dbc_arg, dbc_arg->txn, "db_c_get", flags == DB_SET || flags == DB_SET_RANGE ? key : NULL, NULL, flags); /* * Return a cursor's record number. It has nothing to do with the * cursor get code except that it was put into the interface. */ if (flags == DB_GET_RECNO) { if (tmp_rmw) F_SET(dbc_arg, DBC_RMW); if (tmp_dirty) F_SET(dbc_arg, DBC_DIRTY_READ); ret = __bam_c_rget(dbc_arg, data); if (tmp_rmw) F_CLR(dbc_arg, DBC_RMW); if (tmp_dirty) F_CLR(dbc_arg, DBC_DIRTY_READ); return (ret); } if (flags == DB_CONSUME || flags == DB_CONSUME_WAIT) CDB_LOCKING_INIT(dbp, dbc_arg); /* * If we have an off-page duplicates cursor, and the operation applies * to it, perform the operation. Duplicate the cursor and call the * underlying function. * * Off-page duplicate trees are locked in the primary tree, that is, * we acquire a write lock in the primary tree and no locks in the * off-page dup tree. If the DB_RMW flag was specified and the get * operation is done in an off-page duplicate tree, call the primary * cursor's upgrade routine first. */ cp = dbc_arg->internal; if (cp->opd != NULL && (flags == DB_CURRENT || flags == DB_GET_BOTHC || flags == DB_NEXT || flags == DB_NEXT_DUP || flags == DB_PREV)) { if (tmp_rmw && (ret = dbc_arg->c_am_writelock(dbc_arg)) != 0) return (ret); if ((ret = __db_c_idup(cp->opd, &opd, DB_POSITIONI)) != 0) return (ret); switch (ret = opd->c_am_get(opd, key, data, flags, NULL)) { case 0: goto done; case DB_NOTFOUND: /* * Translate DB_NOTFOUND failures for the DB_NEXT and * DB_PREV operations into a subsequent operation on * the parent cursor. */ if (flags == DB_NEXT || flags == DB_PREV) { if ((ret = opd->c_close(opd)) != 0) goto err; opd = NULL; break; } goto err; default: goto err; } } /* * Perform an operation on the main cursor. Duplicate the cursor, * upgrade the lock as required, and call the underlying function. */ switch (flags) { case DB_CURRENT: case DB_GET_BOTHC: case DB_NEXT: case DB_NEXT_DUP: case DB_NEXT_NODUP: case DB_PREV: case DB_PREV_NODUP: tmp_flags = DB_POSITIONI; break; default: tmp_flags = 0; break; } if (tmp_dirty) F_SET(dbc_arg, DBC_DIRTY_READ); /* * If this cursor is going to be closed immediately, we don't * need to take precautions to clean it up on error. */ if (F_ISSET(dbc_arg, DBC_TRANSIENT)) dbc_n = dbc_arg; else { ret = __db_c_idup(dbc_arg, &dbc_n, tmp_flags); if (tmp_dirty) F_CLR(dbc_arg, DBC_DIRTY_READ); if (ret != 0) goto err; COPY_RET_MEM(dbc_arg, dbc_n); } if (tmp_rmw) F_SET(dbc_n, DBC_RMW); switch (multi) { case DB_MULTIPLE: F_SET(dbc_n, DBC_MULTIPLE); break; case DB_MULTIPLE_KEY: F_SET(dbc_n, DBC_MULTIPLE_KEY); break; case DB_MULTIPLE | DB_MULTIPLE_KEY: F_SET(dbc_n, DBC_MULTIPLE|DBC_MULTIPLE_KEY); break; case 0: break; } pgno = PGNO_INVALID; ret = dbc_n->c_am_get(dbc_n, key, data, flags, &pgno); if (tmp_rmw) F_CLR(dbc_n, DBC_RMW); if (tmp_dirty) F_CLR(dbc_arg, DBC_DIRTY_READ); F_CLR(dbc_n, DBC_MULTIPLE|DBC_MULTIPLE_KEY); if (ret != 0) goto err; cp_n = dbc_n->internal; /* * We may be referencing a new off-page duplicates tree. Acquire * a new cursor and call the underlying function. */ if (pgno != PGNO_INVALID) { if ((ret = __db_c_newopd(dbc_arg, pgno, cp_n->opd, &cp_n->opd)) != 0) goto err; switch (flags) { case DB_FIRST: case DB_NEXT: case DB_NEXT_NODUP: case DB_SET: case DB_SET_RECNO: case DB_SET_RANGE: tmp_flags = DB_FIRST; break; case DB_LAST: case DB_PREV: case DB_PREV_NODUP: tmp_flags = DB_LAST; break; case DB_GET_BOTH: case DB_GET_BOTHC: case DB_GET_BOTH_RANGE: tmp_flags = flags; break; default: ret = __db_unknown_flag(dbp->dbenv, "__db_c_get", flags); goto err; } if ((ret = cp_n->opd->c_am_get( cp_n->opd, key, data, tmp_flags, NULL)) != 0) goto err; }done: /* * Return a key/data item. The only exception is that we don't return * a key if the user already gave us one, that is, if the DB_SET flag * was set. The DB_SET flag is necessary. In a Btree, the user's key * doesn't have to be the same as the key stored the tree, depending on * the magic performed by the comparison function. As we may not have * done any key-oriented operation here, the page reference may not be * valid. Fill it in as necessary. We don't have to worry about any * locks, the cursor must already be holding appropriate locks. * * XXX * If not a Btree and DB_SET_RANGE is set, we shouldn't return a key * either, should we? */ cp_n = dbc_n == NULL ? dbc_arg->internal : dbc_n->internal; if (!F_ISSET(key, DB_DBT_ISSET)) { if (cp_n->page == NULL && (ret = mpf->get(mpf, &cp_n->pgno, 0, &cp_n->page)) != 0) goto err; if ((ret = __db_ret(dbp, cp_n->page, cp_n->indx, key, &dbc_arg->rkey->data, &dbc_arg->rkey->ulen)) != 0) goto err; } if (multi != 0) { /* * Even if fetching from the OPD cursor we need a duplicate * primary cursor if we are going after multiple keys. */ if (dbc_n == NULL) { /* * Non-"_KEY" DB_MULTIPLE doesn't move the main cursor, * so it's safe to just use dbc_arg, unless dbc_arg * has an open OPD cursor whose state might need to * be preserved. */ if ((!(multi & DB_MULTIPLE_KEY) && dbc_arg->internal->opd == NULL) || F_ISSET(dbc_arg, DBC_TRANSIENT)) dbc_n = dbc_arg; else { if ((ret = __db_c_idup(dbc_arg, &dbc_n, DB_POSITIONI)) != 0) goto err; if ((ret = dbc_n->c_am_get(dbc_n, key, data, DB_CURRENT, &pgno)) != 0) goto err; } cp_n = dbc_n->internal; } /* * If opd is set then we dupped the opd that we came in with. * When we return we may have a new opd if we went to another * key. */ if (opd != NULL) { DB_ASSERT(cp_n->opd == NULL); cp_n->opd = opd; opd = NULL; } /* * Bulk get doesn't use __db_retcopy, so data.size won't * get set up unless there is an error. Assume success * here. This is the only call to c_am_bulk, and it avoids * setting it exactly the same everywhere. If we have an * ENOMEM error, it'll get overwritten with the needed value. */ data->size = data->ulen; ret = dbc_n->c_am_bulk(dbc_n, data, flags | multi); } else if (!F_ISSET(data, DB_DBT_ISSET)) { dbc = opd != NULL ? opd : cp_n->opd != NULL ? cp_n->opd : dbc_n; type = TYPE(dbc->internal->page); ret = __db_ret(dbp, dbc->internal->page, dbc->internal->indx + (type == P_LBTREE || type == P_HASH ? O_INDX : 0), data, &dbc_arg->rdata->data, &dbc_arg->rdata->ulen); }err: /* Don't pass DB_DBT_ISSET back to application level, error or no. */ F_CLR(key, DB_DBT_ISSET); F_CLR(data, DB_DBT_ISSET); /* Cleanup and cursor resolution. */ if (opd != NULL) { if ((t_ret = __db_c_cleanup( dbc_arg->internal->opd, opd, ret)) != 0 && ret == 0) ret = t_ret; } if ((t_ret = __db_c_cleanup(dbc_arg, dbc_n, ret)) != 0 && ret == 0) ret = t_ret; if (flags == DB_CONSUME || flags == DB_CONSUME_WAIT) CDB_LOCKING_DONE(dbp, dbc_arg); return (ret);}/* * __db_c_put -- * Put using a cursor. * * PUBLIC: int __db_c_put __P((DBC *, DBT *, DBT *, u_int32_t)); */int__db_c_put(dbc_arg, key, data, flags) DBC *dbc_arg; DBT *key, *data; u_int32_t flags;{ DB *dbp, *sdbp; DBC *dbc_n, *oldopd, *opd, *sdbc, *pdbc; DBT olddata, oldpkey, oldskey, newdata, pkey, save_skey, skey, temp; db_pgno_t pgno; int cmp, have_oldrec, ispartial, nodel, re_pad, ret, rmw, t_ret; u_int32_t re_len, size, tmp_flags; /* * Cursor Cleanup Note: * All of the cursors passed to the underlying access methods by this * routine are duplicated cursors. On return, any referenced pages * will be discarded, and, if the cursor is not intended to be used * again, the close function will be called. So, pages/locks that * the cursor references do not need to be resolved by the underlying * functions. */ dbp = dbc_arg->dbp; sdbp = NULL; pdbc = dbc_n = NULL; memset(&newdata, 0, sizeof(DBT));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -