📄 db.c
字号:
/*- * See the file LICENSE for redistribution information. * * Copyright (c) 1996-2004 * Sleepycat Software. All rights reserved. *//* * Copyright (c) 1990, 1993, 1994, 1995, 1996 * Keith Bostic. All rights reserved. *//* * Copyright (c) 1990, 1993, 1994, 1995 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: db.c,v 11.300 2004/10/26 17:38:41 bostic 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/db_swap.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"#include "dbinc/txn.h"static int __db_disassociate __P((DB *));#ifdef CONFIG_TESTstatic void __db_makecopy __P((DB_ENV *, const char *, const char *));static int __db_testdocopy __P((DB_ENV *, const char *));static int __qam_testdocopy __P((DB *, const char *));#endif/* * DB.C -- * This file contains the utility functions for the DBP layer. *//* * __db_master_open -- * Open up a handle on a master database. * * PUBLIC: int __db_master_open __P((DB *, * PUBLIC: DB_TXN *, const char *, u_int32_t, int, DB **)); */int__db_master_open(subdbp, txn, name, flags, mode, dbpp) DB *subdbp; DB_TXN *txn; const char *name; u_int32_t flags; int mode; DB **dbpp;{ DB *dbp; int ret; *dbpp = NULL; /* Open up a handle on the main database. */ if ((ret = db_create(&dbp, subdbp->dbenv, 0)) != 0) return (ret); /* * It's always a btree. * Run in the transaction we've created. * Set the pagesize in case we're creating a new database. * Flag that we're creating a database with subdatabases. */ dbp->pgsize = subdbp->pgsize; F_SET(dbp, DB_AM_SUBDB); F_SET(dbp, F_ISSET(subdbp, DB_AM_RECOVER | DB_AM_SWAP | DB_AM_ENCRYPT | DB_AM_CHKSUM | DB_AM_NOT_DURABLE)); /* * If there was a subdb specified, then we only want to apply * DB_EXCL to the subdb, not the actual file. We only got here * because there was a subdb specified. */ LF_CLR(DB_EXCL); LF_SET(DB_RDWRMASTER); if ((ret = __db_open(dbp, txn, name, NULL, DB_BTREE, flags, mode, PGNO_BASE_MD)) != 0) goto err; /* * Verify that pagesize is the same on both. The items in dbp were now * initialized from the meta page. The items in dbp were set in * __db_dbopen when we either read or created the master file. Other * items such as checksum and encryption are checked when we read the * meta-page. So we do not check those here. However, if the * meta-page caused checksumming to be turned on and it wasn't already, * set it here. */ if (F_ISSET(dbp, DB_AM_CHKSUM)) F_SET(subdbp, DB_AM_CHKSUM); if (subdbp->pgsize != 0 && dbp->pgsize != subdbp->pgsize) { ret = EINVAL; __db_err(dbp->dbenv, "Different pagesize specified on existent file"); goto err; }err: if (ret != 0 && !F_ISSET(dbp, DB_AM_DISCARD)) (void)__db_close(dbp, txn, 0); else *dbpp = dbp; return (ret);}/* * __db_master_update -- * Add/Open/Remove a subdatabase from a master database. * * PUBLIC: int __db_master_update __P((DB *, DB *, DB_TXN *, const char *, * PUBLIC: DBTYPE, mu_action, const char *, u_int32_t)); */int__db_master_update(mdbp, sdbp, txn, subdb, type, action, newname, flags) DB *mdbp, *sdbp; DB_TXN *txn; const char *subdb; DBTYPE type; mu_action action; const char *newname; u_int32_t flags;{ DB_ENV *dbenv; DBC *dbc, *ndbc; DBT key, data, ndata; PAGE *p, *r; db_pgno_t t_pgno; int modify, ret, t_ret; dbenv = mdbp->dbenv; dbc = ndbc = NULL; p = NULL; memset(&key, 0, sizeof(key)); memset(&data, 0, sizeof(data)); /* Might we modify the master database? If so, we'll need to lock. */ modify = (action != MU_OPEN || LF_ISSET(DB_CREATE)) ? 1 : 0; /* * Open up a cursor. If this is CDB and we're creating the database, * make it an update cursor. */ if ((ret = __db_cursor(mdbp, txn, &dbc, (CDB_LOCKING(dbenv) && modify) ? DB_WRITECURSOR : 0)) != 0) goto err; /* * Point the cursor at the record. * * If we're removing or potentially creating an entry, lock the page * with DB_RMW. * * We do multiple cursor operations with the cursor in some cases and * subsequently access the data DBT information. Set DB_DBT_MALLOC so * we don't risk modification of the data between our uses of it. * * !!! * We don't include the name's nul termination in the database. */ key.data = (void *)subdb; key.size = (u_int32_t)strlen(subdb); F_SET(&data, DB_DBT_MALLOC); ret = __db_c_get(dbc, &key, &data, DB_SET | ((STD_LOCKING(dbc) && modify) ? DB_RMW : 0)); /* * What we do next--whether or not we found a record for the * specified subdatabase--depends on what the specified action is. * Handle ret appropriately as the first statement of each case. */ switch (action) { case MU_REMOVE: /* * We should have found something if we're removing it. Note * that in the common case where the DB we're asking to remove * doesn't exist, we won't get this far; __db_subdb_remove * will already have returned an error from __db_open. */ if (ret != 0) goto err; /* * Delete the subdatabase entry first; if this fails, * we don't want to touch the actual subdb pages. */ if ((ret = __db_c_del(dbc, 0)) != 0) goto err; /* * We're handling actual data, not on-page meta-data, * so it hasn't been converted to/from opposite * endian architectures. Do it explicitly, now. */ memcpy(&sdbp->meta_pgno, data.data, sizeof(db_pgno_t)); DB_NTOHL(&sdbp->meta_pgno); if ((ret = __memp_fget(mdbp->mpf, &sdbp->meta_pgno, 0, &p)) != 0) goto err; /* Free the root on the master db. */ if (TYPE(p) == P_BTREEMETA) { if ((ret = __memp_fget(mdbp->mpf, &((BTMETA *)p)->root, 0, &r)) != 0) goto err; /* Free and put the page. */ if ((ret = __db_free(dbc, r)) != 0) { r = NULL; goto err; } } /* Free and put the page. */ if ((ret = __db_free(dbc, p)) != 0) { p = NULL; goto err; } p = NULL; break; case MU_RENAME: /* We should have found something if we're renaming it. */ if (ret != 0) goto err; /* * Before we rename, we need to make sure we're not * overwriting another subdatabase, or else this operation * won't be undoable. Open a second cursor and check * for the existence of newname; it shouldn't appear under * us since we hold the metadata lock. */ if ((ret = __db_cursor(mdbp, txn, &ndbc, 0)) != 0) goto err; key.data = (void *)newname; key.size = (u_int32_t)strlen(newname); /* * We don't actually care what the meta page of the potentially- * overwritten DB is; we just care about existence. */ memset(&ndata, 0, sizeof(ndata)); F_SET(&ndata, DB_DBT_USERMEM | DB_DBT_PARTIAL); if ((ret = __db_c_get(ndbc, &key, &ndata, DB_SET)) == 0) { /* A subdb called newname exists. Bail. */ ret = EEXIST; __db_err(dbenv, "rename: database %s exists", newname); goto err; } else if (ret != DB_NOTFOUND) goto err; /* * Now do the put first; we don't want to lose our * sole reference to the subdb. Use the second cursor * so that the first one continues to point to the old record. */ if ((ret = __db_c_put(ndbc, &key, &data, DB_KEYFIRST)) != 0) goto err; if ((ret = __db_c_del(dbc, 0)) != 0) { /* * If the delete fails, try to delete the record * we just put, in case we're not txn-protected. */ (void)__db_c_del(ndbc, 0); goto err; } break; case MU_OPEN: /* * Get the subdatabase information. If it already exists, * copy out the page number and we're done. */ switch (ret) { case 0: if (LF_ISSET(DB_CREATE) && LF_ISSET(DB_EXCL)) { ret = EEXIST; goto err; } memcpy(&sdbp->meta_pgno, data.data, sizeof(db_pgno_t)); DB_NTOHL(&sdbp->meta_pgno); goto done; case DB_NOTFOUND: if (LF_ISSET(DB_CREATE)) break; /* * No db_err, it is reasonable to remove a * nonexistent db. */ ret = ENOENT; goto err; default: goto err; } /* Create a subdatabase. */ if ((ret = __db_new(dbc, type == DB_HASH ? P_HASHMETA : P_BTREEMETA, &p)) != 0) goto err; sdbp->meta_pgno = PGNO(p); /* * XXX * We're handling actual data, not on-page meta-data, so it * hasn't been converted to/from opposite endian architectures. * Do it explicitly, now. */ t_pgno = PGNO(p); DB_HTONL(&t_pgno); memset(&ndata, 0, sizeof(ndata)); ndata.data = &t_pgno; ndata.size = sizeof(db_pgno_t); if ((ret = __db_c_put(dbc, &key, &ndata, DB_KEYLAST)) != 0) goto err; F_SET(sdbp, DB_AM_CREATED); break; }err:done: /* * If we allocated a page: if we're successful, mark the page dirty * and return it to the cache, otherwise, discard/free it. */ if (p != NULL) { if (ret == 0) { if ((t_ret = __memp_fput(mdbp->mpf, p, DB_MPOOL_DIRTY)) != 0) ret = t_ret; } else (void)__memp_fput(mdbp->mpf, p, 0); } /* Discard the cursor(s) and data. */ if (data.data != NULL) __os_ufree(dbenv, data.data); if (dbc != NULL && (t_ret = __db_c_close(dbc)) != 0 && ret == 0) ret = t_ret; if (ndbc != NULL && (t_ret = __db_c_close(ndbc)) != 0 && ret == 0) ret = t_ret; return (ret);}/* * __db_dbenv_setup -- * Set up the underlying environment during a db_open. * * PUBLIC: int __db_dbenv_setup __P((DB *, * PUBLIC: DB_TXN *, const char *, u_int32_t, u_int32_t)); */int__db_dbenv_setup(dbp, txn, fname, id, flags) DB *dbp; DB_TXN *txn; const char *fname; u_int32_t id, flags;{ DB *ldbp; DB_ENV *dbenv; DB_MPOOL *dbmp; u_int32_t maxid; int ret; dbenv = dbp->dbenv; /* If we don't yet have an environment, it's time to create it. */ if (!F_ISSET(dbenv, DB_ENV_OPEN_CALLED)) { /* Make sure we have at least DB_MINCACHE pages in our cache. */ if (dbenv->mp_gbytes == 0 && dbenv->mp_bytes < dbp->pgsize * DB_MINPAGECACHE && (ret = __memp_set_cachesize( dbenv, 0, dbp->pgsize * DB_MINPAGECACHE, 0)) != 0) return (ret); if ((ret = __dbenv_open(dbenv, NULL, DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE | LF_ISSET(DB_THREAD), 0)) != 0) return (ret); } /* Join the underlying cache. */ if ((ret = __db_dbenv_mpool(dbp, fname, flags)) != 0) return (ret); /* * We may need a per-thread mutex. Allocate it from the mpool * region, there's supposed to be extra space there for that purpose. */ if (LF_ISSET(DB_THREAD)) { dbmp = dbenv->mp_handle; if ((ret = __db_mutex_setup(dbenv, dbmp->reginfo, &dbp->mutexp, MUTEX_ALLOC | MUTEX_THREAD)) != 0) return (ret); } /* * Set up a bookkeeping entry for this database in the log region, * if such a region exists. Note that even if we're in recovery * or a replication client, where we won't log registries, we'll * still need an FNAME struct, so LOGGING_ON is the correct macro. */ if (LOGGING_ON(dbenv) && (ret = __dbreg_setup(dbp, fname, id)) != 0) return (ret); /* * If we're actively logging and our caller isn't a recovery function * that already did so, assign this dbp a log fileid. */ if (DBENV_LOGGING(dbenv) && !F_ISSET(dbp, DB_AM_RECOVER) &&#if !defined(DEBUG_ROP) !F_ISSET(dbp, DB_AM_RDONLY) &&#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -