📄 db_open.c
字号:
/*- * See the file LICENSE for redistribution information. * * Copyright (c) 1996-2002 * Sleepycat Software. All rights reserved. */#include "db_config.h"#ifndef lintstatic const char revid[] = "$Id: db_open.c,v 11.215 2002/08/15 15:27:52 bostic Exp $";#endif /* not lint */#ifndef NO_SYSTEM_INCLUDES#include <sys/types.h>#include <stddef.h>#include <stdlib.h>#include <string.h>#endif#include "db_int.h"#include "dbinc/db_page.h"#include "dbinc/db_shash.h"#include "dbinc/db_swap.h"#include "dbinc/btree.h"#include "dbinc/crypto.h"#include "dbinc/hmac.h"#include "dbinc/fop.h"#include "dbinc/hash.h"#include "dbinc/lock.h"#include "dbinc/log.h"#include "dbinc/qam.h"#include "dbinc/txn.h"static int __db_openchk __P((DB *, DB_TXN *, const char *, const char *, DBTYPE, u_int32_t));/* * __db_open -- * Main library interface to the DB access methods. * * PUBLIC: int __db_open __P((DB *, DB_TXN *, * PUBLIC: const char *, const char *, DBTYPE, u_int32_t, int)); */int__db_open(dbp, txn, name, subdb, type, flags, mode) DB *dbp; DB_TXN *txn; const char *name, *subdb; DBTYPE type; u_int32_t flags; int mode;{ DB_ENV *dbenv; int remove_master, remove_me, ret, t_ret, txn_local; dbenv = dbp->dbenv; remove_me = remove_master = txn_local = 0; PANIC_CHECK(dbenv); if ((ret = __db_openchk(dbp, txn, name, subdb, type, flags)) != 0) return (ret); /* * Create local transaction as necessary, check for consistent * transaction usage. */ if (IS_AUTO_COMMIT(dbenv, txn, flags)) { if ((ret = __db_txn_auto(dbp, &txn)) != 0) return (ret); txn_local = 1; } else if (txn != NULL && !TXN_ON(dbenv)) return (__db_not_txn_env(dbenv)); /* * If the environment was configured with threads, the DB handle * must also be free-threaded, so we force the DB_THREAD flag on. * (See SR #2033 for why this is a requirement--recovery needs * to be able to grab a dbp using __db_fileid_to_dbp, and it has * no way of knowing which dbp goes with which thread, so whichever * one it finds has to be usable in any of them.) */ if (F_ISSET(dbenv, DB_ENV_THREAD)) LF_SET(DB_THREAD); /* Convert any DB->open flags. */ if (LF_ISSET(DB_RDONLY)) F_SET(dbp, DB_AM_RDONLY); if (LF_ISSET(DB_DIRTY_READ)) F_SET(dbp, DB_AM_DIRTY); /* Fill in the type. */ dbp->type = type; /* * If we're opening a subdatabase, we have to open (and potentially * create) the main database, and then get (and potentially store) * our base page number in that database. Then, we can finally open * the subdatabase. */ if ((ret = __db_dbopen( dbp, txn, name, subdb, flags, mode, PGNO_BASE_MD)) != 0) goto err; /* * You can open the database that describes the subdatabases in the * rest of the file read-only. The content of each key's data is * unspecified and applications should never be adding new records * or updating existing records. However, during recovery, we need * to open these databases R/W so we can redo/undo changes in them. * Likewise, we need to open master databases read/write during * rename and remove so we can be sure they're fully sync'ed, so * we provide an override flag for the purpose. */ if (subdb == NULL && !IS_RECOVERING(dbenv) && !LF_ISSET(DB_RDONLY) && !LF_ISSET(DB_RDWRMASTER) && F_ISSET(dbp, DB_AM_SUBDB)) { __db_err(dbenv, "files containing multiple databases may only be opened read-only"); ret = EINVAL; goto err; }err: /* If we were successful, don't discard the file on close. */ if (ret == 0) /* If we were successful, don't discard the file on close. */ F_CLR(dbp, DB_AM_DISCARD | DB_AM_CREATED | DB_AM_CREATED_MSTR); else { /* * If we are not transactional, we need to remove the * databases/subdatabases. If we are transactional, then * the abort of the child transaction should take care of * cleaning them up. */ remove_me = txn == NULL && F_ISSET(dbp, DB_AM_CREATED); remove_master = txn == NULL && F_ISSET(dbp, DB_AM_CREATED_MSTR); /* * If we had an error, it may have happened before or after * we actually logged the open. If it happened before, then * abort won't know anything about it and won't close or * refresh the dbp, so we need to do it explicitly. */ (void)__db_refresh(dbp, txn, DB_NOSYNC); } /* Remove anyone we created. */ if (remove_master || (subdb == NULL && remove_me)) /* Remove file. */ (void)dbenv->dbremove(dbenv, txn, name, NULL, 0); else if (remove_me) /* Remove subdatabase. */ (void)dbenv->dbremove(dbenv, txn, name, subdb, 0); /* 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_dbopen -- * Open a database. This routine gets called in three different ways. * 1. It can be called to open a file/database. In this case, subdb will * be NULL and meta_pgno will be PGNO_BASE_MD. * 2. It can be called to open a subdatabase during normal operation. In * this case, name and subname will both be non-NULL and meta_pgno will * be PGNO_BAS_MD (also PGNO_INVALID). * 3. It can be called during recovery to open a subdatabase in which case * name will be non-NULL, subname mqy be NULL and meta-pgno will be * a valid pgno (i.e., not PGNO_BASE_MD). * * PUBLIC: int __db_dbopen __P((DB *, DB_TXN *, * PUBLIC: const char *, const char *, u_int32_t, int, db_pgno_t)); */int__db_dbopen(dbp, txn, name, subdb, flags, mode, meta_pgno) DB *dbp; DB_TXN *txn; const char *name, *subdb; u_int32_t flags; int mode; db_pgno_t meta_pgno;{ DB_ENV *dbenv; int ret; u_int32_t id; dbenv = dbp->dbenv; id = TXN_INVALID; if (txn != NULL) F_SET(dbp, DB_AM_TXN); DB_TEST_RECOVERY(dbp, DB_TEST_PREOPEN, ret, name); /* * If name is NULL, it's always a create, so make sure that we * have a type specified. It would be nice if this checking * were done in __db_open where most of the interface checking * is done, but this interface (__db_dbopen) is used by the * recovery and limbo system, so we need to safeguard this * interface as well. */ if (name == NULL) { F_SET(dbp, DB_AM_INMEM); if (dbp->type == DB_UNKNOWN) { __db_err(dbenv, "DBTYPE of unknown without existing file"); return (EINVAL); } if (dbp->pgsize == 0) dbp->pgsize = DB_DEF_IOSIZE; /* * If the file is a temporary file and we're doing locking, * then we have to create a unique file ID. We can't use our * normal dev/inode pair (or whatever this OS uses in place of * dev/inode pairs) because no backing file will be created * until the mpool cache is filled forcing the buffers to disk. * Grab a random locker ID to use as a file ID. The created * ID must never match a potential real file ID -- we know it * won't because real file IDs contain a time stamp after the * dev/inode pair, and we're simply storing a 4-byte value. * * !!! * Store the locker in the file id structure -- we can get it * from there as necessary, and it saves having two copies. */ if (LOCKING_ON(dbenv) && (ret = dbenv->lock_id(dbenv, (u_int32_t *)dbp->fileid)) != 0) return (ret); } else if (subdb == NULL && meta_pgno == PGNO_BASE_MD) { /* Open/create the underlying file. Acquire locks. */ if ((ret = __fop_file_setup(dbp, txn, name, mode, flags, &id)) != 0) return (ret); } else { if ((ret = __fop_subdb_setup(dbp, txn, name, subdb, mode, flags)) != 0) return (ret); meta_pgno = dbp->meta_pgno; } /* * If we created the file, set the truncate flag for the mpool. This * isn't for anything we've done, it's protection against stupid user * tricks: if the user deleted a file behind Berkeley DB's back, we * may still have pages in the mpool that match the file's "unique" ID. * * Note that if we're opening a subdatabase, we don't want to set * the TRUNCATE flag even if we just created the file--we already * opened and updated the master using access method interfaces, * so we don't want to get rid of any pages that are in the mpool. * If we created the file when we opened the master, we already hit * this check in a non-subdb context then. */ if (subdb == NULL && F_ISSET(dbp, DB_AM_CREATED)) LF_SET(DB_TRUNCATE); /* Set up the underlying environment. */ if ((ret = __db_dbenv_setup(dbp, txn, name, id, flags)) != 0) return (ret); /* * Set the open flag. We use it to mean that the dbp has gone * through mpf setup, including dbreg_register. Also, below, * the underlying access method open functions may want to do * things like acquire cursors, so the open flag has to be set * before calling them. */ F_SET(dbp, DB_AM_OPEN_CALLED); /* * For unnamed files, we need to actually create the file now * that the mpool is open. */ if (name == NULL && (ret = __db_new_file(dbp, txn, NULL, NULL)) != 0) return (ret); switch (dbp->type) { case DB_BTREE: ret = __bam_open(dbp, txn, name, meta_pgno, flags); break; case DB_HASH: ret = __ham_open(dbp, txn, name, meta_pgno, flags); break; case DB_RECNO: ret = __ram_open(dbp, txn, name, meta_pgno, flags); break; case DB_QUEUE: ret = __qam_open(dbp, txn, name, meta_pgno, mode, flags); break; case DB_UNKNOWN: return (__db_unknown_type(dbenv, "__db_dbopen", dbp->type)); } if (ret != 0) goto err; DB_TEST_RECOVERY(dbp, DB_TEST_POSTOPEN, ret, name); /* * Unnamed files don't need handle locks, so we only have to check * for a handle lock downgrade or lockevent in the case of named * files. */ if (!F_ISSET(dbp, DB_AM_RECOVER) && name != NULL && LOCK_ISSET(dbp->handle_lock)) { if (txn != NULL) { ret = __txn_lockevent(dbenv, txn, dbp, &dbp->handle_lock, dbp->lid); } else if (LOCKING_ON(dbenv)) /* Trade write handle lock for read handle lock. */ ret = __lock_downgrade(dbenv, &dbp->handle_lock, DB_LOCK_READ, 0); }DB_TEST_RECOVERY_LABELerr: return (ret);}/* * __db_new_file -- * Create a new database file. * * PUBLIC: int __db_new_file __P((DB *, DB_TXN *, DB_FH *, const char *)); */int__db_new_file(dbp, txn, fhp, name) DB *dbp; DB_TXN *txn; DB_FH *fhp; const char *name;{ int ret; switch (dbp->type) { case DB_BTREE: case DB_RECNO: ret = __bam_new_file(dbp, txn, fhp, name); break; case DB_HASH: ret = __ham_new_file(dbp, txn, fhp, name); break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -