📄 db_am.c
字号:
/*- * See the file LICENSE for redistribution information. * * Copyright (c) 1998-2002 * Sleepycat Software. All rights reserved. */#include "db_config.h"#ifndef lintstatic const char revid[] = "$Id: db_am.c,v 1.1.1.1 2004/08/19 23:53:56 gopalan Exp $";#endif /* not lint */#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));static int __db_secondary_close __P((DB *, u_int32_t));#ifdef DEBUGstatic int __db_cprint_item __P((DBC *));#endif/* * __db_cursor -- * Allocate and return a cursor. * * PUBLIC: int __db_cursor __P((DB *, DB_TXN *, DBC **, u_int32_t)); */int__db_cursor(dbp, txn, dbcp, flags) DB *dbp; DB_TXN *txn; DBC **dbcp; u_int32_t flags;{ DB_ENV *dbenv; DBC *dbc; db_lockmode_t mode; u_int32_t op; int ret; dbenv = dbp->dbenv; PANIC_CHECK(dbenv); DB_ILLEGAL_BEFORE_OPEN(dbp, "DB->cursor"); /* Validate arguments. */ if ((ret = __db_cursorchk(dbp, flags)) != 0) return (ret); /* * Check for consistent transaction usage. For now, assume that * this cursor might be used for read operations only (in which * case it may not require a txn). We'll check more stringently * in c_del and c_put. (Note that this all means that the * read-op txn tests have to be a subset of the write-op ones.) */ if ((ret = __db_check_txn(dbp, txn, DB_LOCK_INVALIDID, 1)) != 0) return (ret); if ((ret = __db_icursor(dbp, txn, dbp->type, PGNO_INVALID, 0, DB_LOCK_INVALIDID, dbcp)) != 0) return (ret); dbc = *dbcp; /* * If this is CDB, do all the locking in the interface, which is * right here. */ if (CDB_LOCKING(dbenv)) { op = LF_ISSET(DB_OPFLAGS_MASK); mode = (op == DB_WRITELOCK) ? DB_LOCK_WRITE : ((op == DB_WRITECURSOR) ? DB_LOCK_IWRITE : DB_LOCK_READ); if ((ret = dbenv->lock_get(dbenv, dbc->locker, 0, &dbc->lock_dbt, mode, &dbc->mylock)) != 0) { (void)__db_c_close(dbc); return (ret); } if (op == DB_WRITECURSOR) F_SET(dbc, DBC_WRITECURSOR); if (op == DB_WRITELOCK) F_SET(dbc, DBC_WRITER); } if (LF_ISSET(DB_DIRTY_READ) || (txn != NULL && F_ISSET(txn, TXN_DIRTY_READ))) F_SET(dbc, DBC_DIRTY_READ); return (0);}/* * __db_icursor -- * Internal version of __db_cursor. If dbcp is * non-NULL it is assumed to point to an area to * initialize as a cursor. * * PUBLIC: int __db_icursor * PUBLIC: __P((DB *, DB_TXN *, DBTYPE, db_pgno_t, int, u_int32_t, DBC **)); */int__db_icursor(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, *adbc; DBC_INTERNAL *cp; DB_ENV *dbenv; int allocated, ret; dbenv = dbp->dbenv; allocated = 0; /* * 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(dbp->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, then there is no need to * create new locker ids. We know that no one else * is running concurrently using this DB, so we can * take a peek at any cursors on the active queue. */ if (!DB_IS_THREADED(dbp) && (adbc = TAILQ_FIRST(&dbp->active_queue)) != NULL) dbc->lid = adbc->lid; else { if ((ret = dbenv->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_icursor rather * than sdbp->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(dbp->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; default: ret = __db_unknown_type(dbp->dbenv, "__db_icursor", 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; txn->cursors++; } /* * 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; 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; default: ret = __db_unknown_type(dbp->dbenv, "__db_icursor", dbp->type); goto err; } 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(dbp->dbenv, dbc); return (ret);}#ifdef DEBUG/* * __db_cprint -- * Display the cursor active and free queues. * * PUBLIC: int __db_cprint __P((DB *)); */int__db_cprint(dbp) DB *dbp;{ DBC *dbc; int ret, t_ret; ret = 0; MUTEX_THREAD_LOCK(dbp->dbenv, dbp->mutexp); fprintf(stderr, "Active queue:\n"); for (dbc = TAILQ_FIRST(&dbp->active_queue); dbc != NULL; dbc = TAILQ_NEXT(dbc, links)) if ((t_ret = __db_cprint_item(dbc)) != 0 && ret == 0) ret = t_ret; fprintf(stderr, "Free queue:\n"); for (dbc = TAILQ_FIRST(&dbp->free_queue); dbc != NULL; dbc = TAILQ_NEXT(dbc, links)) if ((t_ret = __db_cprint_item(dbc)) != 0 && ret == 0) ret = t_ret; MUTEX_THREAD_UNLOCK(dbp->dbenv, dbp->mutexp); return (ret);}staticint __db_cprint_item(dbc) DBC *dbc;{ static const FN fn[] = { { DBC_ACTIVE, "active" }, { DBC_COMPENSATE, "compensate" }, { DBC_OPD, "off-page-dup" }, { DBC_RECOVER, "recover" }, { DBC_RMW, "read-modify-write" }, { DBC_TRANSIENT, "transient" }, { DBC_WRITECURSOR, "write cursor" }, { DBC_WRITEDUP, "internally dup'ed write cursor" }, { DBC_WRITER, "short-term write cursor" }, { 0, NULL } }; DB *dbp; DBC_INTERNAL *cp; const char *s; dbp = dbc->dbp; cp = dbc->internal; s = __db_dbtype_to_string(dbc->dbtype); if (strcmp(s, "UNKNOWN TYPE") == 0) { DB_ASSERT(0); return (1); } fprintf(stderr, "%s/%#0lx: opd: %#0lx\n", s, P_TO_ULONG(dbc), P_TO_ULONG(cp->opd)); fprintf(stderr, "\ttxn: %#0lx lid: %lu locker: %lu\n", P_TO_ULONG(dbc->txn), (u_long)dbc->lid, (u_long)dbc->locker); fprintf(stderr, "\troot: %lu page/index: %lu/%lu", (u_long)cp->root, (u_long)cp->pgno, (u_long)cp->indx); __db_prflags(dbc->flags, fn, stderr); fprintf(stderr, "\n"); switch (dbp->type) { case DB_BTREE: __bam_cprint(dbc); break; case DB_HASH: __ham_cprint(dbc); break; default: break; } return (0);}#endif /* DEBUG *//* * db_fd -- * Return a file descriptor for flock'ing. * * PUBLIC: int __db_fd __P((DB *, int *)); */int__db_fd(dbp, fdp) DB *dbp; fd_t *fdp;{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -