📄 db_am.c
字号:
/*- * See the file LICENSE for redistribution information. * * Copyright (c) 1998-2004 * Sleepycat Software. All rights reserved. * * $Id: db_am.c,v 11.120 2004/10/07 17:33:32 sue Exp $ */#include "db_config.h"#ifndef NO_SYSTEM_INCLUDES#include <sys/types.h>#include <string.h>#endif#include "db_int.h"#include "dbinc/db_page.h"#include "dbinc/db_shash.h"#include "dbinc/btree.h"#include "dbinc/hash.h"#include "dbinc/lock.h"#include "dbinc/log.h"#include "dbinc/mp.h"#include "dbinc/qam.h"static int __db_append_primary __P((DBC *, DBT *, DBT *));static int __db_secondary_get __P((DB *, DB_TXN *, DBT *, DBT *, u_int32_t));/* * __db_cursor_int -- * Internal routine to create a cursor. * * PUBLIC: int __db_cursor_int * PUBLIC: __P((DB *, DB_TXN *, DBTYPE, db_pgno_t, int, u_int32_t, DBC **)); */int__db_cursor_int(dbp, txn, dbtype, root, is_opd, lockerid, dbcp) DB *dbp; DB_TXN *txn; DBTYPE dbtype; db_pgno_t root; int is_opd; u_int32_t lockerid; DBC **dbcp;{ DBC *dbc; DBC_INTERNAL *cp; DB_ENV *dbenv; int allocated, ret; dbenv = dbp->dbenv; allocated = 0; /* * If dbcp is non-NULL it is assumed to point to an area to initialize * as a cursor. * * Take one from the free list if it's available. Take only the * right type. With off page dups we may have different kinds * of cursors on the queue for a single database. */ MUTEX_THREAD_LOCK(dbenv, dbp->mutexp); for (dbc = TAILQ_FIRST(&dbp->free_queue); dbc != NULL; dbc = TAILQ_NEXT(dbc, links)) if (dbtype == dbc->dbtype) { TAILQ_REMOVE(&dbp->free_queue, dbc, links); F_CLR(dbc, ~DBC_OWN_LID); break; } MUTEX_THREAD_UNLOCK(dbenv, dbp->mutexp); if (dbc == NULL) { if ((ret = __os_calloc(dbenv, 1, sizeof(DBC), &dbc)) != 0) return (ret); allocated = 1; dbc->flags = 0; dbc->dbp = dbp; /* Set up locking information. */ if (LOCKING_ON(dbenv)) { /* * If we are not threaded, we share a locker ID among * all cursors opened in the environment handle, * allocating one if this is the first cursor. * * This relies on the fact that non-threaded DB handles * always have non-threaded environment handles, since * we set DB_THREAD on DB handles created with threaded * environment handles. */ if (!DB_IS_THREADED(dbp)) { if (dbp->dbenv->env_lid == DB_LOCK_INVALIDID && (ret = __lock_id(dbenv,&dbp->dbenv->env_lid)) != 0) goto err; dbc->lid = dbp->dbenv->env_lid; } else { if ((ret = __lock_id(dbenv, &dbc->lid)) != 0) goto err; F_SET(dbc, DBC_OWN_LID); } /* * In CDB, secondary indices should share a lock file * ID with the primary; otherwise we're susceptible * to deadlocks. We also use __db_cursor_int rather * than __db_cursor to create secondary update cursors * in c_put and c_del; these won't acquire a new lock. * * !!! * Since this is in the one-time cursor allocation * code, we need to be sure to destroy, not just * close, all cursors in the secondary when we * associate. */ if (CDB_LOCKING(dbenv) && F_ISSET(dbp, DB_AM_SECONDARY)) memcpy(dbc->lock.fileid, dbp->s_primary->fileid, DB_FILE_ID_LEN); else memcpy(dbc->lock.fileid, dbp->fileid, DB_FILE_ID_LEN); if (CDB_LOCKING(dbenv)) { if (F_ISSET(dbenv, DB_ENV_CDB_ALLDB)) { /* * If we are doing a single lock per * environment, set up the global * lock object just like we do to * single thread creates. */ DB_ASSERT(sizeof(db_pgno_t) == sizeof(u_int32_t)); dbc->lock_dbt.size = sizeof(u_int32_t); dbc->lock_dbt.data = &dbc->lock.pgno; dbc->lock.pgno = 0; } else { dbc->lock_dbt.size = DB_FILE_ID_LEN; dbc->lock_dbt.data = dbc->lock.fileid; } } else { dbc->lock.type = DB_PAGE_LOCK; dbc->lock_dbt.size = sizeof(dbc->lock); dbc->lock_dbt.data = &dbc->lock; } } /* Init the DBC internal structure. */ switch (dbtype) { case DB_BTREE: case DB_RECNO: if ((ret = __bam_c_init(dbc, dbtype)) != 0) goto err; break; case DB_HASH: if ((ret = __ham_c_init(dbc)) != 0) goto err; break; case DB_QUEUE: if ((ret = __qam_c_init(dbc)) != 0) goto err; break; case DB_UNKNOWN: default: ret = __db_unknown_type(dbenv, "DB->cursor", dbtype); goto err; } cp = dbc->internal; } /* Refresh the DBC structure. */ dbc->dbtype = dbtype; RESET_RET_MEM(dbc); if ((dbc->txn = txn) == NULL) { /* * There are certain cases in which we want to create a * new cursor with a particular locker ID that is known * to be the same as (and thus not conflict with) an * open cursor. * * The most obvious case is cursor duplication; when we * call DBC->c_dup or __db_c_idup, we want to use the original * cursor's locker ID. * * Another case is when updating secondary indices. Standard * CDB locking would mean that we might block ourself: we need * to open an update cursor in the secondary while an update * cursor in the primary is open, and when the secondary and * primary are subdatabases or we're using env-wide locking, * this is disastrous. * * In these cases, our caller will pass a nonzero locker ID * into this function. Use this locker ID instead of dbc->lid * as the locker ID for our new cursor. */ if (lockerid != DB_LOCK_INVALIDID) dbc->locker = lockerid; else dbc->locker = dbc->lid; } else dbc->locker = txn->txnid; /* * These fields change when we are used as a secondary index, so * if the DB is a secondary, make sure they're set properly just * in case we opened some cursors before we were associated. * * __db_c_get is used by all access methods, so this should be safe. */ if (F_ISSET(dbp, DB_AM_SECONDARY)) dbc->c_get = __db_c_secondary_get_pp; if (is_opd) F_SET(dbc, DBC_OPD); if (F_ISSET(dbp, DB_AM_RECOVER)) F_SET(dbc, DBC_RECOVER); if (F_ISSET(dbp, DB_AM_COMPENSATE)) F_SET(dbc, DBC_COMPENSATE); /* Refresh the DBC internal structure. */ cp = dbc->internal; cp->opd = NULL; cp->indx = 0; cp->page = NULL; cp->pgno = PGNO_INVALID; cp->root = root; switch (dbtype) { case DB_BTREE: case DB_RECNO: if ((ret = __bam_c_refresh(dbc)) != 0) goto err; break; case DB_HASH: case DB_QUEUE: break; case DB_UNKNOWN: default: ret = __db_unknown_type(dbenv, "DB->cursor", dbp->type); goto err; } /* * The transaction keeps track of how many cursors were opened within * it to catch application errors where the cursor isn't closed when * the transaction is resolved. */ if (txn != NULL) ++txn->cursors; MUTEX_THREAD_LOCK(dbenv, dbp->mutexp); TAILQ_INSERT_TAIL(&dbp->active_queue, dbc, links); F_SET(dbc, DBC_ACTIVE); MUTEX_THREAD_UNLOCK(dbenv, dbp->mutexp); *dbcp = dbc; return (0);err: if (allocated) __os_free(dbenv, dbc); 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; dbenv = dbp->dbenv; if ((ret = __db_cursor(dbp, txn, &dbc, DB_WRITELOCK)) != 0) return (ret); 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; case DB_BTREE: case DB_HASH: case DB_UNKNOWN: default: /* The interface should prevent this. */ DB_ASSERT( dbp->type == DB_QUEUE || dbp->type == DB_RECNO); ret = __db_ferr(dbenv, "DB->put", 0); 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 done; 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 = __db_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 = __db_c_put(dbc, key, data, flags == 0 ? DB_KEYLAST : flags);err:done: /* Close the cursor. */ if ((t_ret = __db_c_close(dbc)) != 0 && ret == 0) ret = t_ret; return (ret);}/* * __db_del -- * Delete the items referenced by a key. * * PUBLIC: int __db_del __P((DB *, DB_TXN *, DBT *, u_int32_t)); */int__db_del(dbp, txn, key, flags) DB *dbp; DB_TXN *txn; DBT *key; u_int32_t flags;{ DBC *dbc; DBT data, lkey; u_int32_t f_init, f_next; int ret, t_ret; /* Allocate a cursor. */ if ((ret = __db_cursor(dbp, txn, &dbc, DB_WRITELOCK)) != 0) goto err; DEBUG_LWRITE(dbc, txn, "DB->del", key, NULL, flags); COMPQUIET(flags, 0); /* * 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -