📄 db.c
字号:
/* Register DB's pgin/pgout functions. */ if ((ret = dbenv->memp_register( dbenv, DB_FTYPE_SET, __db_pgin, __db_pgout)) != 0) return (ret); /* Create the DB_MPOOLFILE structure. */ if ((ret = dbenv->memp_fcreate(dbenv, &dbp->mpf, 0)) != 0) return (ret); mpf = dbp->mpf; /* Set the database's cache priority if we've been given one. */ if (dbp->priority != 0 && (ret = mpf->set_priority(mpf, dbp->priority)) != 0) return (ret); /* * Open a backing file in the memory pool. * * If we need to pre- or post-process a file's pages on I/O, set the * file type. If it's a hash file, always call the pgin and pgout * routines. This means that hash files can never be mapped into * process memory. If it's a btree file and requires swapping, we * need to page the file in and out. This has to be right -- we can't * mmap files that are being paged in and out. */ switch (dbp->type) { case DB_BTREE: case DB_RECNO: ftype = F_ISSET(dbp, DB_AM_SWAP | DB_AM_ENCRYPT | DB_AM_CHKSUM) ? DB_FTYPE_SET : DB_FTYPE_NOTSET; (void)mpf->set_ftype(mpf, ftype); (void)mpf->set_clear_len(mpf, (CRYPTO_ON(dbenv) ? dbp->pgsize : DB_PAGE_DB_LEN)); break; case DB_HASH: (void)mpf->set_ftype(mpf, DB_FTYPE_SET); (void)mpf->set_clear_len(mpf, (CRYPTO_ON(dbenv) ? dbp->pgsize : DB_PAGE_DB_LEN)); break; case DB_QUEUE: ftype = F_ISSET(dbp, DB_AM_SWAP | DB_AM_ENCRYPT | DB_AM_CHKSUM) ? DB_FTYPE_SET : DB_FTYPE_NOTSET; (void)mpf->set_ftype(mpf, ftype); (void)mpf->set_clear_len(mpf, (CRYPTO_ON(dbenv) ? dbp->pgsize : DB_PAGE_QUEUE_LEN)); break; case DB_UNKNOWN: /* * If we're running in the verifier, our database might * be corrupt and we might not know its type--but we may * still want to be able to verify and salvage. * * If we can't identify the type, it's not going to be safe * to call __db_pgin--we pretty much have to give up all * hope of salvaging cross-endianness. Proceed anyway; * at worst, the database will just appear more corrupt * than it actually is, but at best, we may be able * to salvage some data even with no metadata page. */ if (F_ISSET(dbp, DB_AM_VERIFYING)) { (void)mpf->set_ftype(mpf, DB_FTYPE_NOTSET); (void)mpf->set_clear_len(mpf, DB_PAGE_DB_LEN); break; } /* FALLTHROUGH */ default: return ( __db_unknown_type(dbenv, "__db_dbenv_setup", dbp->type)); } (void)mpf->set_fileid(mpf, dbp->fileid); (void)mpf->set_lsn_offset(mpf, 0); pginfo.db_pagesize = dbp->pgsize; pginfo.flags = F_ISSET(dbp, (DB_AM_CHKSUM | DB_AM_ENCRYPT | DB_AM_SWAP)); pginfo.type = dbp->type; pgcookie.data = &pginfo; pgcookie.size = sizeof(DB_PGINFO); (void)mpf->set_pgcookie(mpf, &pgcookie); if ((ret = mpf->open(mpf, name, LF_ISSET(DB_RDONLY | DB_NOMMAP | DB_ODDFILESIZE | DB_TRUNCATE) | (F_ISSET(dbenv, DB_ENV_DIRECT_DB) ? DB_DIRECT : 0), 0, dbp->pgsize)) != 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, name, 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 (ret = __dbreg_new_id(dbp, txn)) != 0) return (ret); /* * Insert ourselves into the DB_ENV's dblist. We allocate a * unique ID to each {fileid, meta page number} pair, and to * each temporary file (since they all have a zero fileid). * This ID gives us something to use to tell which DB handles * go with which databases in all the cursor adjustment * routines, where we don't want to do a lot of ugly and * expensive memcmps. */ MUTEX_THREAD_LOCK(dbenv, dbenv->dblist_mutexp); for (maxid = 0, ldbp = LIST_FIRST(&dbenv->dblist); ldbp != NULL; ldbp = LIST_NEXT(dbp, dblistlinks)) { if (name != NULL && memcmp(ldbp->fileid, dbp->fileid, DB_FILE_ID_LEN) == 0 && ldbp->meta_pgno == dbp->meta_pgno) break; if (ldbp->adj_fileid > maxid) maxid = ldbp->adj_fileid; } /* * If ldbp is NULL, we didn't find a match, or we weren't * really looking because name is NULL. Assign the dbp an * adj_fileid one higher than the largest we found, and * insert it at the head of the master dbp list. * * If ldbp is not NULL, it is a match for our dbp. Give dbp * the same ID that ldbp has, and add it after ldbp so they're * together in the list. */ if (ldbp == NULL) { dbp->adj_fileid = maxid + 1; LIST_INSERT_HEAD(&dbenv->dblist, dbp, dblistlinks); } else { dbp->adj_fileid = ldbp->adj_fileid; LIST_INSERT_AFTER(ldbp, dbp, dblistlinks); } MUTEX_THREAD_UNLOCK(dbenv, dbenv->dblist_mutexp); return (0);}/* * __db_close -- * DB destructor. * * PUBLIC: int __db_close __P((DB *, u_int32_t)); */int__db_close(dbp, flags) DB *dbp; u_int32_t flags;{ DB_ENV *dbenv; dbenv = dbp->dbenv; PANIC_CHECK(dbenv); /* Validate arguments, but as a DB handle destructor, we can't fail. */ if (flags != 0 && flags != DB_NOSYNC) (void)__db_ferr(dbenv, "DB->close", 0); return (__db_close_i(dbp, NULL, flags));}/* * __db_close_i -- * Internal DB destructor. * * PUBLIC: int __db_close_i __P((DB *, DB_TXN *, u_int32_t)); */int__db_close_i(dbp, txn, flags) DB *dbp; DB_TXN *txn; u_int32_t flags;{ DB_ENV *dbenv; int ret, t_ret; dbenv = dbp->dbenv; ret = 0; /* * Validate arguments, but as a DB handle destructor, we can't fail. * * Check for consistent transaction usage -- ignore errors. Only * internal callers specify transactions, so it's a serious problem * if we get error messages. */ if (txn != NULL) (void)__db_check_txn(dbp, txn, DB_LOCK_INVALIDID, 0); /* Refresh the structure and close any local environment. */ if ((t_ret = __db_refresh(dbp, txn, flags)) != 0 && ret == 0) ret = t_ret; /* * Call the access specific close function. * * !!! * Because of where these functions are called in the DB handle close * process, these routines can't do anything that would dirty pages or * otherwise affect closing down the database. Specifically, we can't * abort and recover any of the information they control. */ if ((t_ret = __ham_db_close(dbp)) != 0 && ret == 0) ret = t_ret; if ((t_ret = __bam_db_close(dbp)) != 0 && ret == 0) ret = t_ret; if ((t_ret = __qam_db_close(dbp)) != 0 && ret == 0) ret = t_ret; --dbenv->db_ref; if (F_ISSET(dbenv, DB_ENV_DBLOCAL) && dbenv->db_ref == 0 && (t_ret = dbenv->close(dbenv, 0)) != 0 && ret == 0) ret = t_ret; /* Free the database handle. */ memset(dbp, CLEAR_BYTE, sizeof(*dbp)); __os_free(dbenv, dbp); return (ret);}/* * __db_refresh -- * Refresh the DB structure, releasing any allocated resources. * This does most of the work of closing files now because refresh * is what is used during abort processing (since we can't destroy * the actual handle) and during abort processing, we may have a * fully opened handle. * * PUBLIC: int __db_refresh __P((DB *, DB_TXN *, u_int32_t)); */int__db_refresh(dbp, txn, flags) DB *dbp; DB_TXN *txn; u_int32_t flags;{ DB *sdbp; DBC *dbc; DB_ENV *dbenv; DB_LOCKREQ lreq; DB_MPOOL *dbmp; int ret, t_ret; ret = 0; dbenv = dbp->dbenv; /* If never opened, or not currently open, it's easy. */ if (!F_ISSET(dbp, DB_AM_OPEN_CALLED)) goto never_opened; /* * If we have any secondary indices, disassociate them from us. * We don't bother with the mutex here; it only protects some * of the ops that will make us core-dump mid-close anyway, and * if you're trying to do something with a secondary *while* you're * closing the primary, you deserve what you get. The disassociation * is mostly done just so we can close primaries and secondaries in * any order--but within one thread of control. */ for (sdbp = LIST_FIRST(&dbp->s_secondaries); sdbp != NULL; sdbp = LIST_NEXT(sdbp, s_links)) { LIST_REMOVE(sdbp, s_links); if ((t_ret = __db_disassociate(sdbp)) != 0 && ret == 0) ret = t_ret; } /* * Sync the underlying access method. Do before closing the cursors * because DB->sync allocates cursors in order to write Recno backing * source text files. */ if (!LF_ISSET(DB_NOSYNC) && !F_ISSET(dbp, DB_AM_DISCARD) && (t_ret = dbp->sync(dbp, 0)) != 0 && ret == 0) ret = t_ret; /* * Go through the active cursors and call the cursor recycle routine, * which resolves pending operations and moves the cursors onto the * free list. Then, walk the free list and call the cursor destroy * routine. Note that any failure on a close is considered "really * bad" and we just break out of the loop and force forward. */ while ((dbc = TAILQ_FIRST(&dbp->active_queue)) != NULL) if ((t_ret = dbc->c_close(dbc)) != 0) { if (ret == 0) ret = t_ret; break; } while ((dbc = TAILQ_FIRST(&dbp->free_queue)) != NULL) if ((t_ret = __db_c_destroy(dbc)) != 0) { if (ret == 0) ret = t_ret; break; } /* * Close any outstanding join cursors. Join cursors destroy * themselves on close and have no separate destroy routine. */ while ((dbc = TAILQ_FIRST(&dbp->join_queue)) != NULL) if ((t_ret = dbc->c_close(dbc)) != 0) { if (ret == 0) ret = t_ret; break; } /* * Sync the memory pool, even though we've already called DB->sync, * because closing cursors can dirty pages by deleting items they * referenced. */ if (!LF_ISSET(DB_NOSYNC) && !F_ISSET(dbp, DB_AM_DISCARD) && (t_ret = dbp->mpf->sync(dbp->mpf)) != 0 && ret == 0) ret = t_ret; /* Close any handle we've been holding since the open. */ if (dbp->saved_open_fhp != NULL && F_ISSET(dbp->saved_open_fhp, DB_FH_VALID) && (t_ret = __os_closehandle(dbenv, dbp->saved_open_fhp)) != 0 && ret == 0) ret = t_ret;never_opened: /* * We are not releasing the handle lock here because we're about * to release all locks held by dbp->lid below. There are two * ways that we can get in here with a handle_lock, but not a * dbp->lid. The first is when our lid has been hijacked by a * subdb. The second is when we are a Queue database in the midst * of a rename. If the queue file hasn't actually been opened, we * hijack the main dbp's locker id to do the open so we can get the * extent files. In both cases, we needn't free the handle lock * because it will be freed when the hijacked locker-id is freed. */ DB_ASSERT(!LOCK_ISSET(dbp->handle_lock) || dbp->lid != DB_LOCK_INVALIDID || dbp->type == DB_QUEUE || F_ISSET(dbp, DB_AM_SUBDB)); if (dbp->lid != DB_LOCK_INVALIDID) { /* We may have pending trade operations on this dbp. */ if (txn != NULL) __txn_remlock(dbenv, txn, &dbp->handle_lock, dbp->lid); /* We may be holding the handle lock; release it. */ lreq.op = DB_LOCK_PUT_ALL; if ((t_ret = __lock_vec(dbenv, dbp->lid, 0, &lreq, 1, NULL)) != 0 && ret == 0) ret = t_ret; if ((t_ret = dbenv->lock_id_free(dbenv, dbp->lid)) != 0 && ret == 0) ret = t_ret; dbp->lid = DB_LOCK_INVALIDID; LOCK_INIT(dbp->handle_lock); } /* Discard the locker ID allocated as the fileid. */ if (F_ISSET(dbp, DB_AM_INMEM) && LOCKING_ON(dbenv) && (t_ret = dbenv->lock_id_free( dbenv, *(u_int32_t *)dbp->fileid)) != 0 && ret == 0) ret = t_ret; dbp->type = DB_UNKNOWN; /* Discard the thread mutex. */ if (dbp->mutexp != NULL) { dbmp = dbenv->mp_handle; __db_mutex_free(dbenv, dbmp->reginfo, dbp->mutexp); dbp->mutexp = NULL; } /* Discard any memory used to store returned data. */ if (dbp->my_rskey.data != NULL) __os_free(dbp->dbenv, dbp->my_rskey.data); if (dbp->my_rkey.data != NULL) __os_free(dbp->dbenv, dbp->my_rkey.data); if (dbp->my_rdata.data != NULL) __os_free(dbp->dbenv, dbp->my_rdata.data); /* For safety's sake; we may refresh twice. */ memset(&dbp->my_rskey, 0, sizeof(DBT)); memset(&dbp->my_rkey, 0, sizeof(DBT)); memset(&dbp->my_rdata, 0, sizeof(DBT)); /* * Remove this DB handle from the DB_ENV's dblist, if it's been added. */ MUTEX_THREAD_LOCK(dbenv, dbenv->dblist_mutexp); if (dbp->dblistlinks.le_prev != NULL) LIST_REMOVE(dbp, dblistlinks); MUTEX_THREAD_UNLOCK(dbenv, dbenv->dblist_mutexp); dbp->dblistlinks.le_prev = NULL; /* Close the memory pool file handle. */ if (dbp->mpf != NULL) { if ((t_ret = dbp->mpf->close(dbp->mpf, F_ISSET(dbp, DB_AM_DISCARD) ? DB_MPOOL_DISCARD : 0)) != 0 && ret == 0) ret = t_ret; dbp->mpf = NULL; } if (LOGGING_ON(dbp->dbenv)) { /* * Discard the log file id, if any. We want to log the close * if and only if this is not a recovery dbp. */ if (F_ISSET(dbp, DB_AM_RECOVER))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -