📄 db.c
字号:
(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(ldbp, dblistlinks)) { if (fname != 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 fname 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_dbenv_mpool -- * Set up the underlying environment cache during a db_open. * * PUBLIC: int __db_dbenv_mpool __P((DB *, const char *, u_int32_t)); */int__db_dbenv_mpool(dbp, fname, flags) DB *dbp; const char *fname; u_int32_t flags;{ DB_ENV *dbenv; DBT pgcookie; DB_MPOOLFILE *mpf; DB_PGINFO pginfo; u_int32_t clear_len; int ftype, ret; COMPQUIET(mpf, NULL); dbenv = dbp->dbenv; /* * 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; clear_len = CRYPTO_ON(dbenv) ? dbp->pgsize : DB_PAGE_DB_LEN; break; case DB_HASH: ftype = DB_FTYPE_SET; clear_len = 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; clear_len = 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)) { ftype = DB_FTYPE_NOTSET; clear_len = DB_PAGE_DB_LEN; break; } /* FALLTHROUGH */ default: return (__db_unknown_type(dbenv, "DB->open", dbp->type)); } mpf = dbp->mpf; (void)__memp_set_clear_len(mpf, clear_len); (void)__memp_set_fileid(mpf, dbp->fileid); (void)__memp_set_ftype(mpf, ftype); (void)__memp_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)__memp_set_pgcookie(mpf, &pgcookie); if ((ret = __memp_fopen(mpf, NULL, fname, LF_ISSET(DB_RDONLY | DB_NOMMAP | DB_ODDFILESIZE | DB_TRUNCATE) | (F_ISSET(dbenv, DB_ENV_DIRECT_DB) ? DB_DIRECT : 0) | (F_ISSET(dbp, DB_AM_NOT_DURABLE) ? DB_TXN_NOT_DURABLE : 0), 0, dbp->pgsize)) != 0) return (ret); return (0);}/* * __db_close -- * DB->close method. * * PUBLIC: int __db_close __P((DB *, DB_TXN *, u_int32_t)); */int__db_close(dbp, txn, flags) DB *dbp; DB_TXN *txn; u_int32_t flags;{ DB_ENV *dbenv; int db_ref, deferred_close, ret, t_ret; dbenv = dbp->dbenv; deferred_close = 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 underlying resources. */ ret = __db_refresh(dbp, txn, flags, &deferred_close); /* * If we've deferred the close because the logging of the close failed, * return our failure right away without destroying the handle. */ if (deferred_close) return (ret); /* !!! * This code has an apparent race between the moment we read and * decrement dbenv->db_ref and the moment we check whether it's 0. * However, if the environment is DBLOCAL, the user shouldn't have a * reference to the dbenv handle anyway; the only way we can get * multiple dbps sharing a local dbenv is if we open them internally * during something like a subdatabase open. If any such thing is * going on while the user is closing the original dbp with a local * dbenv, someone's already badly screwed up, so there's no reason * to bother engineering around this possibility. */ MUTEX_THREAD_LOCK(dbenv, dbenv->dblist_mutexp); db_ref = --dbenv->db_ref; MUTEX_THREAD_UNLOCK(dbenv, dbenv->dblist_mutexp); if (F_ISSET(dbenv, DB_ENV_DBLOCAL) && 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 *)); */int__db_refresh(dbp, txn, flags, deferred_closep) DB *dbp; DB_TXN *txn; u_int32_t flags; int *deferred_closep;{ DB *sdbp; DBC *dbc; DB_ENV *dbenv; DB_LOCKREQ lreq; DB_MPOOL *dbmp; int resync, 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. * * Sync is slow on some systems, notably Solaris filesystems where the * entire buffer cache is searched. If we're in recovery, don't flush * the file, it's not necessary. */ if (!LF_ISSET(DB_NOSYNC) && !F_ISSET(dbp, DB_AM_DISCARD | DB_AM_RECOVER) && (t_ret = __db_sync(dbp)) != 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. */ resync = TAILQ_FIRST(&dbp->active_queue) == NULL ? 0 : 1; while ((dbc = TAILQ_FIRST(&dbp->active_queue)) != NULL) if ((t_ret = __db_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. We don't have to set * the resync flag here, because join cursors aren't write cursors. */ while ((dbc = TAILQ_FIRST(&dbp->join_queue)) != NULL) if ((t_ret = __db_join_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. * * Sync is slow on some systems, notably Solaris filesystems where the * entire buffer cache is searched. If we're in recovery, don't flush * the file, it's not necessary. */ if (resync && !LF_ISSET(DB_NOSYNC) && !F_ISSET(dbp, DB_AM_DISCARD | DB_AM_RECOVER) && (t_ret = __memp_fsync(dbp->mpf)) != 0 && ret == 0) ret = t_ret; /* * At this point, we haven't done anything to render the DB * handle unusable, at least by a transaction abort. Take the * opportunity now to log the file close. If this log fails * and we're in a transaction, we have to bail out of the attempted * close; we'll need a dbp in order to successfully abort the * transaction, and we can't conjure a new one up because we haven't * gotten out the dbreg_register record that represents the close. * In this case, we put off actually closing the dbp until we've * performed the abort. */ 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)) t_ret = __dbreg_revoke_id(dbp, 0, DB_LOGFILEID_INVALID); else { if ((t_ret = __dbreg_close_id(dbp, txn, DBREG_CLOSE)) != 0 && txn != NULL) { /* * We're in a txn and the attempt to log the * close failed; let the txn subsystem know * that we need to destroy this dbp once we're * done with the abort, then bail from the * close. * * Note that if the attempt to put off the * close -also- fails--which it won't unless * we're out of heap memory--we're really * screwed. Panic. */ if ((ret = __txn_closeevent(dbenv, txn, dbp)) != 0) return (__db_panic(dbenv, ret)); if (deferred_closep != NULL) *deferred_closep = 1; return (t_ret); } } if (ret == 0) ret = t_ret; /* Discard the log FNAME. */ if ((t_ret = __dbreg_teardown(dbp)) != 0 && ret == 0) ret = t_ret; } /* Close any handle we've been holding since the open. */ if (dbp->saved_open_fhp != NULL && (t_ret = __os_closehandle(dbenv, dbp->saved_open_fhp)) != 0 && ret == 0) ret = t_ret;never_opened: /* * Remove this DB handle from the DB_ENV's dblist, if it's been added. * * Close our reference to the underlying cache while locked, we don't * want to race with a thread searching for our underlying cache link * while opening a DB handle. */ MUTEX_THREAD_LOCK(dbenv, dbenv->dblist_mutexp); if (dbp->dblistlinks.le_prev != NULL) { LIST_REMOVE(dbp, dblistlinks); dbp->dblistlinks.le_prev = NULL; } /* Close the memory pool file handle. */ if (dbp->mpf != NULL) { if ((t_ret = __memp_fclose(dbp->mpf, F_ISSET(dbp, DB_AM_DISCARD) ? DB_MPOOL_DISCARD : 0)) != 0 && ret == 0) ret = t_ret; dbp->mpf = NULL; } MUTEX_THREAD_UNLOCK(dbenv, dbenv->dblist_mutexp); /* * Call the access specific close function. * * We do this here rather than in __db_close as we need to do this when * aborting an open so that file descriptors are closed and abort of * renames can succeed on platforms that lock open files (such as * Windows). In particular, we need to ensure that all the extents * associated with a queue are closed so that queue renames can be * aborted. * * It is also important that we do this before releasing the handle * lock, because dbremove and dbrename assume that once they have the * handle lock, it is safe to modify the underlying file(s). * * !!! * 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 = __bam_db_close(dbp)) != 0 && ret == 0) ret = t_ret; if ((t_ret = __ham_db_close(dbp)) != 0 && ret == 0) ret = t_ret; if ((t_ret = __qam_db_close(dbp, dbp->flags)) != 0 && ret == 0) ret = t_ret; /* * !!! * At this point, the access-method specific information has been * freed. From now on, we can use the dbp, but not touch any * access-method specific data. */ 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; lreq.obj = NULL; if ((t_ret = __lock_vec(dbenv, dbp->lid, 0, &lreq, 1, NULL)) != 0 && ret == 0) ret = t_ret; if ((t_ret = __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 = __lock_id_free(dbenv, *(u_int32_t *)dbp->fileid)) != 0 && ret == 0) ret = t_ret; dbp->type = DB_UNKNOWN;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -