📄 db_am.c
字号:
DB_FH *fhp; int ret; PANIC_CHECK(dbp->dbenv); DB_ILLEGAL_BEFORE_OPEN(dbp, "DB->fd"); /* * XXX * Truly spectacular layering violation. */ if ((ret = __mp_xxx_fh(dbp->mpf, &fhp)) != 0) return (ret); if (F_ISSET(fhp, DB_FH_VALID)) { *fdp = fhp->fd; return (0); } else {#ifdef KBDB *fdp = NULL;#else *fdp = -1;#endif __db_err(dbp->dbenv, "DB does not have a valid file handle"); return (ENOENT); }}/* * __db_get -- * Return a key/data pair. * * PUBLIC: int __db_get __P((DB *, DB_TXN *, DBT *, DBT *, u_int32_t)); */int__db_get(dbp, txn, key, data, flags) DB *dbp; DB_TXN *txn; DBT *key, *data; u_int32_t flags;{ DBC *dbc; int mode, ret, t_ret; PANIC_CHECK(dbp->dbenv); DB_ILLEGAL_BEFORE_OPEN(dbp, "DB->get"); if ((ret = __db_getchk(dbp, key, data, flags)) != 0) return (ret); /* Check for consistent transaction usage. */ if ((ret = __db_check_txn(dbp, txn, DB_LOCK_INVALIDID, 1)) != 0) return (ret); mode = 0; if (LF_ISSET(DB_DIRTY_READ)) { mode = DB_DIRTY_READ; LF_CLR(DB_DIRTY_READ); } else if (flags == DB_CONSUME || flags == DB_CONSUME_WAIT) mode = DB_WRITELOCK; if ((ret = dbp->cursor(dbp, txn, &dbc, mode)) != 0) return (ret); DEBUG_LREAD(dbc, txn, "__db_get", key, NULL, flags); /* * The DBC_TRANSIENT flag indicates that we're just doing a * single operation with this cursor, and that in case of * error we don't need to restore it to its old position--we're * going to close it right away. Thus, we can perform the get * without duplicating the cursor, saving some cycles in this * common case. * * SET_RET_MEM indicates that if key and/or data have no DBT * flags set and DB manages the returned-data memory, that memory * will belong to this handle, not to the underlying cursor. */ F_SET(dbc, DBC_TRANSIENT); SET_RET_MEM(dbc, dbp); if (LF_ISSET(~(DB_RMW | DB_MULTIPLE)) == 0) LF_SET(DB_SET); ret = dbc->c_get(dbc, key, data, flags); if ((t_ret = __db_c_close(dbc)) != 0 && ret == 0) ret = t_ret; return (ret);}/* * __db_put -- * Store a key/data pair. * * PUBLIC: int __db_put __P((DB *, DB_TXN *, DBT *, DBT *, u_int32_t)); */int__db_put(dbp, txn, key, data, flags) DB *dbp; DB_TXN *txn; DBT *key, *data; u_int32_t flags;{ DBC *dbc; DBT tdata; DB_ENV *dbenv; int ret, t_ret, txn_local; dbc = NULL; dbenv = dbp->dbenv; txn_local = 0; PANIC_CHECK(dbenv); DB_ILLEGAL_BEFORE_OPEN(dbp, "DB->put"); /* Validate arguments. */ if ((ret = __db_putchk(dbp, key, data, flags, F_ISSET(dbp, DB_AM_DUP) || F_ISSET(key, DB_DBT_DUPOK))) != 0) return (ret); /* Create local transaction as necessary. */ if (IS_AUTO_COMMIT(dbenv, txn, flags)) { if ((ret = __db_txn_auto(dbp, &txn)) != 0) return (ret); txn_local = 1; LF_CLR(DB_AUTO_COMMIT); } /* Check for consistent transaction usage. */ if ((ret = __db_check_txn(dbp, txn, DB_LOCK_INVALIDID, 0)) != 0) goto err; if ((ret = dbp->cursor(dbp, txn, &dbc, DB_WRITELOCK)) != 0) goto err; DEBUG_LWRITE(dbc, txn, "db_put", key, data, flags); SET_RET_MEM(dbc, dbp); /* * See the comment in __db_get(). * * Note that the c_get in the DB_NOOVERWRITE case is safe to * do with this flag set; if it errors in any way other than * DB_NOTFOUND, we're going to close the cursor without doing * anything else, and if it returns DB_NOTFOUND then it's safe * to do a c_put(DB_KEYLAST) even if an access method moved the * cursor, since that's not position-dependent. */ F_SET(dbc, DBC_TRANSIENT); switch (flags) { case DB_APPEND: /* * If there is an append callback, the value stored in * data->data may be replaced and then freed. To avoid * passing a freed pointer back to the user, just operate * on a copy of the data DBT. */ tdata = *data; /* * Append isn't a normal put operation; call the appropriate * access method's append function. */ switch (dbp->type) { case DB_QUEUE: if ((ret = __qam_append(dbc, key, &tdata)) != 0) goto err; break; case DB_RECNO: if ((ret = __ram_append(dbc, key, &tdata)) != 0) goto err; break; default: /* The interface should prevent this. */ DB_ASSERT(0); ret = __db_ferr(dbenv, "__db_put", flags); goto err; } /* * Secondary indices: since we've returned zero from * an append function, we've just put a record, and done * so outside __db_c_put. We know we're not a secondary-- * the interface prevents puts on them--but we may be a * primary. If so, update our secondary indices * appropriately. */ DB_ASSERT(!F_ISSET(dbp, DB_AM_SECONDARY)); if (LIST_FIRST(&dbp->s_secondaries) != NULL) ret = __db_append_primary(dbc, key, &tdata); /* * The append callback, if one exists, may have allocated * a new tdata.data buffer. If so, free it. */ FREE_IF_NEEDED(dbp, &tdata); /* No need for a cursor put; we're done. */ goto err; case DB_NOOVERWRITE: flags = 0; /* * Set DB_DBT_USERMEM, this might be a threaded application and * the flags checking will catch us. We don't want the actual * data, so request a partial of length 0. */ memset(&tdata, 0, sizeof(tdata)); F_SET(&tdata, DB_DBT_USERMEM | DB_DBT_PARTIAL); /* * If we're doing page-level locking, set the read-modify-write * flag, we're going to overwrite immediately. */ if ((ret = dbc->c_get(dbc, key, &tdata, DB_SET | (STD_LOCKING(dbc) ? DB_RMW : 0))) == 0) ret = DB_KEYEXIST; else if (ret == DB_NOTFOUND || ret == DB_KEYEMPTY) ret = 0; break; default: /* Fall through to normal cursor put. */ break; } if (ret == 0) ret = dbc->c_put(dbc, key, data, flags == 0 ? DB_KEYLAST : flags);err: /* Close the cursor. */ if (dbc != NULL && (t_ret = __db_c_close(dbc)) != 0 && ret == 0) ret = t_ret; /* Commit for DB_AUTO_COMMIT. */ if (txn_local) { if (ret == 0) ret = txn->commit(txn, 0); else if ((t_ret = txn->abort(txn)) != 0) ret = __db_panic(dbenv, t_ret); } return (ret);}/* * __db_delete -- * Delete the items referenced by a key. * * PUBLIC: int __db_delete __P((DB *, DB_TXN *, DBT *, u_int32_t)); */int__db_delete(dbp, txn, key, flags) DB *dbp; DB_TXN *txn; DBT *key; u_int32_t flags;{ DBC *dbc; DBT data, lkey; DB_ENV *dbenv; u_int32_t f_init, f_next; int ret, t_ret, txn_local; dbc = NULL; dbenv = dbp->dbenv; txn_local = 0; PANIC_CHECK(dbenv); DB_ILLEGAL_BEFORE_OPEN(dbp, "DB->del"); /* Check for invalid flags. */ if ((ret = __db_delchk(dbp, key, flags)) != 0) return (ret); /* Create local transaction as necessary. */ if (IS_AUTO_COMMIT(dbenv, txn, flags)) { if ((ret = __db_txn_auto(dbp, &txn)) != 0) return (ret); txn_local = 1; LF_CLR(DB_AUTO_COMMIT); } /* Check for consistent transaction usage. */ if ((ret = __db_check_txn(dbp, txn, DB_LOCK_INVALIDID, 0)) != 0) goto err; /* Allocate a cursor. */ if ((ret = dbp->cursor(dbp, txn, &dbc, DB_WRITELOCK)) != 0) goto err; DEBUG_LWRITE(dbc, txn, "db_delete", key, NULL, flags); /* * Walk a cursor through the key/data pairs, deleting as we go. Set * the DB_DBT_USERMEM flag, as this might be a threaded application * and the flags checking will catch us. We don't actually want the * keys or data, so request a partial of length 0. */ memset(&lkey, 0, sizeof(lkey)); F_SET(&lkey, DB_DBT_USERMEM | DB_DBT_PARTIAL); memset(&data, 0, sizeof(data)); F_SET(&data, DB_DBT_USERMEM | DB_DBT_PARTIAL); /* * If locking (and we haven't already acquired CDB locks), set the * read-modify-write flag. */ f_init = DB_SET; f_next = DB_NEXT_DUP; if (STD_LOCKING(dbc)) { f_init |= DB_RMW; f_next |= DB_RMW; } /* Walk through the set of key/data pairs, deleting as we go. */ if ((ret = dbc->c_get(dbc, key, &data, f_init)) != 0) goto err; /* * Hash permits an optimization in DB->del: since on-page * duplicates are stored in a single HKEYDATA structure, it's * possible to delete an entire set of them at once, and as * the HKEYDATA has to be rebuilt and re-put each time it * changes, this is much faster than deleting the duplicates * one by one. Thus, if we're not pointing at an off-page * duplicate set, and we're not using secondary indices (in * which case we'd have to examine the items one by one anyway), * let hash do this "quick delete". * * !!! * Note that this is the only application-executed delete call in * Berkeley DB that does not go through the __db_c_del function. * If anything other than the delete itself (like a secondary index * update) has to happen there in a particular situation, the * conditions here should be modified not to call __ham_quick_delete. * The ordinary AM-independent alternative will work just fine with * a hash; it'll just be slower. */ if (dbp->type == DB_HASH) { if (LIST_FIRST(&dbp->s_secondaries) == NULL && !F_ISSET(dbp, DB_AM_SECONDARY) && dbc->internal->opd == NULL) { ret = __ham_quick_delete(dbc); goto err; } } for (;;) { if ((ret = dbc->c_del(dbc, 0)) != 0) goto err; if ((ret = dbc->c_get(dbc, &lkey, &data, f_next)) != 0) { if (ret == DB_NOTFOUND) { ret = 0; break; } goto err; } }err: /* Discard the cursor. */ if (dbc != NULL && (t_ret = dbc->c_close(dbc)) != 0 && ret == 0) ret = t_ret; /* Commit for DB_AUTO_COMMIT. */ if (txn_local) { if (ret == 0) ret = txn->commit(txn, 0); else if ((t_ret = txn->abort(txn)) != 0) ret = __db_panic(dbenv, t_ret); } return (ret);}/* * __db_sync -- * Flush the database cache. * * PUBLIC: int __db_sync __P((DB *, u_int32_t)); */int__db_sync(dbp, flags) DB *dbp; u_int32_t flags;{ int ret, t_ret; PANIC_CHECK(dbp->dbenv); DB_ILLEGAL_BEFORE_OPEN(dbp, "DB->sync"); if ((ret = __db_syncchk(dbp, flags)) != 0) return (ret); /* Read-only trees never need to be sync'd. */ if (F_ISSET(dbp, DB_AM_RDONLY)) return (0); /* If it's a Recno tree, write the backing source text file. */ if (dbp->type == DB_RECNO) ret = __ram_writeback(dbp); /* If the tree was never backed by a database file, we're done. */ if (F_ISSET(dbp, DB_AM_INMEM)) return (0); /* Flush any dirty pages from the cache to the backing file. */ if ((t_ret = dbp->mpf->sync(dbp->mpf)) != 0 && ret == 0) ret = t_ret; return (ret);}/* * __db_associate -- * Associate another database as a secondary index to this one. * * PUBLIC: int __db_associate __P((DB *, DB_TXN *, DB *, * PUBLIC: int (*)(DB *, const DBT *, const DBT *, DBT *), u_int32_t)); */int__db_associate(dbp, txn, sdbp, callback, flags) DB *dbp, *sdbp; DB_TXN *txn; int (*callback) __P((DB *, const DBT *, const DBT *, DBT *));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -