📄 db_cam.c
字号:
skip_s_update: /* * 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 put operation is done in an off-page * duplicate tree, call the primary cursor's upgrade routine first. */ if (dbc_arg->internal->opd != NULL && (flags == DB_AFTER || flags == DB_BEFORE || flags == DB_CURRENT)) { /* * A special case for hash off-page duplicates. Hash doesn't * support (and is documented not to support) put operations * relative to a cursor which references an already deleted * item. For consistency, apply the same criteria to off-page * duplicates as well. */ if (dbc_arg->dbtype == DB_HASH && F_ISSET( ((BTREE_CURSOR *)(dbc_arg->internal->opd->internal)), C_DELETED)) { ret = DB_NOTFOUND; goto err; } if ((ret = dbc_arg->c_am_writelock(dbc_arg)) != 0) return (ret); if ((ret = __db_c_dup(dbc_arg, &dbc_n, DB_POSITIONI)) != 0) goto err; opd = dbc_n->internal->opd; if ((ret = opd->c_am_put( opd, key, data, flags, NULL)) != 0) goto err; goto done; } /* * Perform an operation on the main cursor. Duplicate the cursor, * and call the underlying function. * * XXX: MARGO * tmp_flags = flags == DB_AFTER || flags == DB_BEFORE || flags == DB_CURRENT ? DB_POSITIONI : 0; */ tmp_flags = DB_POSITIONI; /* * 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 if ((ret = __db_c_idup(dbc_arg, &dbc_n, tmp_flags)) != 0) goto err; pgno = PGNO_INVALID; if ((ret = dbc_n->c_am_put(dbc_n, key, data, flags, &pgno)) != 0) goto err; /* * We may be referencing a new off-page duplicates tree. Acquire * a new cursor and call the underlying function. */ if (pgno != PGNO_INVALID) { oldopd = dbc_n->internal->opd; if ((ret = __db_c_newopd(dbc_arg, pgno, oldopd, &opd)) != 0) { dbc_n->internal->opd = opd; goto err; } dbc_n->internal->opd = opd; if ((ret = opd->c_am_put( opd, key, data, flags, NULL)) != 0) goto err; }done:err: /* Cleanup and cursor resolution. */ if ((t_ret = __db_c_cleanup(dbc_arg, dbc_n, ret)) != 0 && ret == 0) ret = t_ret; /* If newdata was used, free its buffer. */ if (newdata.data != NULL) __os_free(dbp->dbenv, newdata.data); CDB_LOCKING_DONE(dbp, dbc_arg); if (sdbp != NULL && (t_ret = __db_s_done(sdbp)) != 0) return (t_ret); return (ret);}/* * __db_duperr() * Error message: we don't currently support sorted duplicate duplicates. * PUBLIC: int __db_duperr __P((DB *, u_int32_t)); */int__db_duperr(dbp, flags) DB *dbp; u_int32_t flags;{ /* * If we run into this error while updating a secondary index, * don't yell--there's no clean way to pass DB_NODUPDATA in along * with DB_UPDATE_SECONDARY, but we may run into this problem * in a normal, non-error course of events. * * !!! * If and when we ever permit duplicate duplicates in sorted-dup * databases, we need to either change the secondary index code * to check for dup dups, or we need to maintain the implicit * "DB_NODUPDATA" behavior for databases with DB_AM_SECONDARY set. */ if (flags != DB_NODUPDATA && !F_ISSET(dbp, DB_AM_SECONDARY)) __db_err(dbp->dbenv, "Duplicate data items are not supported with sorted data"); return (DB_KEYEXIST);}/* * __db_c_cleanup -- * Clean up duplicate cursors. */static int__db_c_cleanup(dbc, dbc_n, failed) DBC *dbc, *dbc_n; int failed;{ DB *dbp; DBC *opd; DBC_INTERNAL *internal; DB_MPOOLFILE *mpf; int ret, t_ret; dbp = dbc->dbp; mpf = dbp->mpf; internal = dbc->internal; ret = 0; /* Discard any pages we're holding. */ if (internal->page != NULL) { if ((t_ret = mpf->put(mpf, internal->page, 0)) != 0 && ret == 0) ret = t_ret; internal->page = NULL; } opd = internal->opd; if (opd != NULL && opd->internal->page != NULL) { if ((t_ret = mpf->put(mpf, opd->internal->page, 0)) != 0 && ret == 0) ret = t_ret; opd->internal->page = NULL; } /* * If dbc_n is NULL, there's no internal cursor swapping to be done * and no dbc_n to close--we probably did the entire operation on an * offpage duplicate cursor. Just return. * * If dbc and dbc_n are the same, we're either inside a DB->{put/get} * operation, and as an optimization we performed the operation on * the main cursor rather than on a duplicated one, or we're in a * bulk get that can't have moved the cursor (DB_MULTIPLE with the * initial c_get operation on an off-page dup cursor). Just * return--either we know we didn't move the cursor, or we're going * to close it before we return to application code, so we're sure * not to visibly violate the "cursor stays put on error" rule. */ if (dbc_n == NULL || dbc == dbc_n) return (ret); if (dbc_n->internal->page != NULL) { if ((t_ret = mpf->put(mpf, dbc_n->internal->page, 0)) != 0 && ret == 0) ret = t_ret; dbc_n->internal->page = NULL; } opd = dbc_n->internal->opd; if (opd != NULL && opd->internal->page != NULL) { if ((t_ret = mpf->put(mpf, opd->internal->page, 0)) != 0 && ret == 0) ret = t_ret; opd->internal->page = NULL; } /* * If we didn't fail before entering this routine or just now when * freeing pages, swap the interesting contents of the old and new * cursors. */ if (!failed && ret == 0) { dbc->internal = dbc_n->internal; dbc_n->internal = internal; } /* * Close the cursor we don't care about anymore. The close can fail, * but we only expect DB_LOCK_DEADLOCK failures. This violates our * "the cursor is unchanged on error" semantics, but since all you can * do with a DB_LOCK_DEADLOCK failure is close the cursor, I believe * that's OK. * * XXX * There's no way to recover from failure to close the old cursor. * All we can do is move to the new position and return an error. * * XXX * We might want to consider adding a flag to the cursor, so that any * subsequent operations other than close just return an error? */ if ((t_ret = dbc_n->c_close(dbc_n)) != 0 && ret == 0) ret = t_ret; return (ret);}/* * __db_c_secondary_get -- * This wrapper function for DBC->c_pget() is the DBC->c_get() function * for a secondary index cursor. * * PUBLIC: int __db_c_secondary_get __P((DBC *, DBT *, DBT *, u_int32_t)); */int__db_c_secondary_get(dbc, skey, data, flags) DBC *dbc; DBT *skey, *data; u_int32_t flags;{ DB_ASSERT(F_ISSET(dbc->dbp, DB_AM_SECONDARY)); return (dbc->c_pget(dbc, skey, NULL, data, flags));}/* * __db_c_pget -- * Get a primary key/data pair through a secondary index. * * PUBLIC: int __db_c_pget __P((DBC *, DBT *, DBT *, DBT *, u_int32_t)); */int__db_c_pget(dbc, skey, pkey, data, flags) DBC *dbc; DBT *skey, *pkey, *data; u_int32_t flags;{ DB *pdbp, *sdbp; DBC *pdbc; DBT *save_rdata, nullpkey; int pkeymalloc, ret, save_pkey_flags, t_ret; sdbp = dbc->dbp; pdbp = sdbp->s_primary; pkeymalloc = t_ret = 0; PANIC_CHECK(sdbp->dbenv); if ((ret = __db_cpgetchk(sdbp, skey, pkey, data, flags, IS_INITIALIZED(dbc))) != 0) return (ret); /* * The challenging part of this function is getting the behavior * right for all the various permutations of DBT flags. The * next several blocks handle the various cases we need to * deal with specially. */ /* * We may be called with a NULL pkey argument, if we've been * wrapped by a 2-DBT get call. If so, we need to use our * own DBT. */ if (pkey == NULL) { memset(&nullpkey, 0, sizeof(DBT)); pkey = &nullpkey; } /* * DB_GET_RECNO is a special case, because we're interested not in * the primary key/data pair, but rather in the primary's record * number. */ if ((flags & DB_OPFLAGS_MASK) == DB_GET_RECNO) return (__db_c_pget_recno(dbc, pkey, data, flags)); /* * If the DBTs we've been passed don't have any of the * user-specified memory management flags set, we want to make sure * we return values using the DBTs dbc->rskey, dbc->rkey, and * dbc->rdata, respectively. * * There are two tricky aspects to this: first, we need to pass * skey and pkey *in* to the initial c_get on the secondary key, * since either or both may be looked at by it (depending on the * get flag). Second, we must not use a normal DB->get call * on the secondary, even though that's what we want to accomplish, * because the DB handle may be free-threaded. Instead, * we open a cursor, then take steps to ensure that we actually use * the rkey/rdata from the *secondary* cursor. * * We accomplish all this by passing in the DBTs we started out * with to the c_get, but having swapped the contents of rskey and * rkey, respectively, into rkey and rdata; __db_ret will treat * them like the normal key/data pair in a c_get call, and will * realloc them as need be (this is "step 1"). Then, for "step 2", * we swap back rskey/rkey/rdata to normal, and do a get on the primary * with the secondary dbc appointed as the owner of the returned-data * memory. * * Note that in step 2, we copy the flags field in case we need to * pass down a DB_DBT_PARTIAL or other flag that is compatible with * letting DB do the memory management. */ /* Step 1. */ save_rdata = dbc->rdata; dbc->rdata = dbc->rkey; dbc->rkey = dbc->rskey; /* * It is correct, though slightly sick, to attempt a partial get * of a primary key. However, if we do so here, we'll never find the * primary record; clear the DB_DBT_PARTIAL field of pkey just * for the duration of the next call. */ save_pkey_flags = pkey->flags; F_CLR(pkey, DB_DBT_PARTIAL); /* * Now we can go ahead with the meat of this call. First, get the * primary key from the secondary index. (What exactly we get depends * on the flags, but the underlying cursor get will take care of the * dirty work.) */ if ((ret = dbc->c_real_get(dbc, skey, pkey, flags)) != 0) { /* Restore rskey/rkey/rdata and return. */ pkey->flags = save_pkey_flags; dbc->rskey = dbc->rkey; dbc->rkey = dbc->rdata; dbc->rdata = save_rdata; goto err; } /* Restore pkey's flags in case we stomped the PARTIAL flag. */ pkey->flags = save_pkey_flags; /* * Restore the cursor's rskey, rkey, and rdata DBTs. If DB * is handling the memory management, we now have newly * reallocated buffers and ulens in rkey and rdata which we want * to put in rskey and rkey. save_rdata contains the old value * of dbc->rdata. */ dbc->rskey = dbc->rkey; dbc->rkey = dbc->rdata; dbc->rdata = save_rdata; /* * Now we're ready for "step 2". If either or both of pkey and * data do not have memory management flags set--that is, if DB is * managing their memory--we need to swap around the rkey/rdata * structures so that we don't wind up trying to use memory managed * by the primary database cursor, which we'll close before we return. * * !!! * If you're carefully following the bouncing ball, you'll note * that in the DB-managed case, the buffer hanging off of pkey is * the same as dbc->rkey->data. This is just fine; we may well * realloc and stomp on it when we return, if we're going a * DB_GET_BOTH and need to return a different partial or key * (depending on the comparison function), but this is safe. * * !!! * We need to use __db_icursor here rather than simply calling * pdbp->cursor, because otherwise, if we're in CDB, we'll * allocate a new locker ID and leave ourselves open to deadlocks. * (Even though we're only acquiring read locks, we'll still block * if there are any waiters.) */ if ((ret = __db_icursor(pdbp, dbc->txn, pdbp->type, PGNO_INVALID, 0, dbc->locker, &pdbc)) != 0) goto err; /* * We're about to use pkey a second time. If DB_DBT_MALLOC * is set on it, we'll leak the memory we allocated the first time. * Thus, set DB_DBT_REALLOC instead so that we reuse that memory * instead of leaking it. * * !!! * This assumes that the user must always specify a compatible * realloc function if a malloc function is specified. I think * this is a reasonable requirement. */ if (F_ISSET(pkey, DB_DBT_MALLOC)) { F_CLR(pkey, DB_DBT_MALLOC); F_SET(pkey, DB_DBT_REALLOC); pkeymalloc = 1; } /* * Do the actual get. Set DBC_TRANSIENT since we don't care * about preserving the position on error, and it's faster. * SET_RET_MEM so that the secondary DBC owns any returned-data * memory. */ F_SET(pdbc, DBC_TRANSIENT); SET_RET_MEM(pdbc, dbc); ret = pdbc->c_get(pdbc, pkey, data, DB_SET); /* * If the item wasn't found in the primary, this is a bug; * our secondary has somehow gotten corrupted, and contains * elements that don't correspond to anything in the primary. * Complain. */ if (ret == DB_NOTFOUND) ret = __db_secondary_corrupt(pdbp); /* Now close the primary cursor. */ t_ret = pdbc->c_close(pdbc);err: if (pkeymalloc) { /* * If pkey had a MALLOC flag, we need to restore it; * otherwise, if the user frees the buffer but reuses * the DBT without NULL'ing its data field or changing * the flags, we may drop core. */ F_CLR(pkey, DB_DBT_REALLOC); F_SET(pkey, DB_DBT_MALLOC); } return (t_ret == 0 ? ret : t_ret);}/* * __db_c_pget_recno -- * Perform a DB_GET_RECNO c_pget on a secondary index. Returns * the secondary's record number in the pkey field and the primary's * in the data field. */static int__db_c_pget_recno(sdbc, pkey, data, flags) DBC *sdbc; DBT *pkey, *data; u_int32_t flags;{ DB *pdbp, *sdbp; DB_ENV *dbenv; DBC *pdbc; DBT discardme, primary_key; db_recno_t oob; u_int32_t rmw;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -