📄 db.c
字号:
/*- * See the file LICENSE for redistribution information. * * Copyright (c) 1996-2002 * 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. */#include "db_config.h"#ifndef lintstatic const char revid[] = "$Id: db.c,v 11.246 2002/08/20 14:40:00 margo 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/hash.h"#include "dbinc/lock.h"#include "dbinc/log.h"#include "dbinc/log.h"#include "dbinc/mp.h"#include "dbinc/qam.h"#include "dbinc/txn.h"static int __db_disassociate __P((DB *));#if CONFIG_TESTstatic void __db_makecopy __P((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; /* 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->type = DB_BTREE; 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)); /* * 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_dbopen(dbp, txn, name, NULL, 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 * chksumming 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)) __db_close_i(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; 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 = mdbp->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 = dbc->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 = dbc->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 = mdbp->mpf->get(mdbp->mpf, &sdbp->meta_pgno, 0, &p)) != 0) 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 = mdbp->cursor(mdbp, txn, &ndbc, 0)) != 0) goto err; DB_ASSERT(newname != NULL); 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 = ndbc->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 = ndbc->c_put(ndbc, &key, &data, DB_KEYFIRST)) != 0) goto err; if ((ret = dbc->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)ndbc->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; } /* * We need to check against the master lorder here because * we only want to check this if we are creating. In the * case where we don't create we just want to inherit. */ if (F_ISSET(mdbp, DB_AM_SWAP) != F_ISSET(sdbp, DB_AM_SWAP)) { ret = EINVAL; __db_err(mdbp->dbenv, "Different lorder specified on existent file"); 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 = dbc->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 = mdbp->mpf->put(mdbp->mpf, p, DB_MPOOL_DIRTY)) != 0) ret = t_ret; /* * Since we cannot close this file until after * transaction commit, we need to sync the dirty * pages, because we'll read these directly from * disk to open. */ if ((t_ret = mdbp->sync(mdbp, 0)) != 0 && ret == 0) ret = t_ret; } else (void)mdbp->mpf->put(mdbp->mpf, p, 0); } /* Discard the cursor(s) and data. */ if (data.data != NULL) __os_ufree(dbenv, data.data); if (dbc != NULL && (t_ret = dbc->c_close(dbc)) != 0 && ret == 0) ret = t_ret; if (ndbc != NULL && (t_ret = ndbc->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, name, id, flags) DB *dbp; DB_TXN *txn; const char *name; u_int32_t id; u_int32_t flags;{ DB *ldbp; DBT pgcookie; DB_ENV *dbenv; DB_MPOOL *dbmp; DB_MPOOLFILE *mpf; DB_PGINFO pginfo; u_int32_t maxid; int ftype, 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 = dbenv->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); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -