mirror of
https://github.com/MariaDB/server.git
synced 2025-01-19 13:32:33 +01:00
362 lines
10 KiB
C
362 lines
10 KiB
C
/*-
|
|
* See the file LICENSE for redistribution information.
|
|
*
|
|
* Copyright (c) 1996-2002
|
|
* Sleepycat Software. All rights reserved.
|
|
*/
|
|
/*
|
|
* Copyright (c) 1995, 1996
|
|
* The President and Fellows of Harvard University. 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 lint
|
|
static const char revid[] = "$Id: dbreg_rec.c,v 11.108 2002/08/14 20:04:25 bostic Exp $";
|
|
#endif /* not lint */
|
|
|
|
#ifndef NO_SYSTEM_INCLUDES
|
|
#include <sys/types.h>
|
|
|
|
#include <string.h>
|
|
#endif
|
|
|
|
#include "db_int.h"
|
|
#include "dbinc/db_page.h"
|
|
#include "dbinc/db_am.h"
|
|
#include "dbinc/log.h"
|
|
#include "dbinc/txn.h"
|
|
|
|
static int __dbreg_open_file __P((DB_ENV *,
|
|
DB_TXN *, __dbreg_register_args *, void *));
|
|
|
|
/*
|
|
* PUBLIC: int __dbreg_register_recover
|
|
* PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *));
|
|
*/
|
|
int
|
|
__dbreg_register_recover(dbenv, dbtp, lsnp, op, info)
|
|
DB_ENV *dbenv;
|
|
DBT *dbtp;
|
|
DB_LSN *lsnp;
|
|
db_recops op;
|
|
void *info;
|
|
{
|
|
DB_ENTRY *dbe;
|
|
DB_LOG *dblp;
|
|
DB *dbp;
|
|
__dbreg_register_args *argp;
|
|
int do_close, do_open, do_rem, ret, t_ret;
|
|
|
|
dblp = dbenv->lg_handle;
|
|
dbp = NULL;
|
|
|
|
#ifdef DEBUG_RECOVER
|
|
REC_PRINT(__dbreg_register_print);
|
|
#endif
|
|
do_open = do_close = 0;
|
|
if ((ret = __dbreg_register_read(dbenv, dbtp->data, &argp)) != 0)
|
|
goto out;
|
|
|
|
switch (argp->opcode) {
|
|
case LOG_OPEN:
|
|
if ((DB_REDO(op) ||
|
|
op == DB_TXN_OPENFILES || op == DB_TXN_POPENFILES))
|
|
do_open = 1;
|
|
else
|
|
do_close = 1;
|
|
break;
|
|
|
|
case LOG_CLOSE:
|
|
if (DB_UNDO(op))
|
|
do_open = 1;
|
|
else
|
|
do_close = 1;
|
|
break;
|
|
case LOG_RCLOSE:
|
|
/*
|
|
* LOG_RCLOSE was generated by recover because a file
|
|
* was left open. The POPENFILES pass, which is run
|
|
* to open files to abort prepared transactions,
|
|
* may not include the open for this file so we
|
|
* open it here. Note that a normal CLOSE is
|
|
* not legal before the prepared transaction is
|
|
* committed or aborted.
|
|
*/
|
|
if (DB_UNDO(op) || op == DB_TXN_POPENFILES)
|
|
do_open = 1;
|
|
else
|
|
do_close = 1;
|
|
break;
|
|
|
|
case LOG_CHECKPOINT:
|
|
if (DB_UNDO(op) ||
|
|
op == DB_TXN_OPENFILES || op == DB_TXN_POPENFILES)
|
|
do_open = 1;
|
|
break;
|
|
}
|
|
|
|
if (do_open) {
|
|
/*
|
|
* We must open the db even if the meta page is not
|
|
* yet written as we may be creating subdatabase.
|
|
*/
|
|
if (op == DB_TXN_OPENFILES && argp->opcode != LOG_CHECKPOINT)
|
|
F_SET(dblp, DBLOG_FORCE_OPEN);
|
|
|
|
/*
|
|
* During an abort or an open pass to recover prepared txns,
|
|
* we need to make sure that we use the same locker id on the
|
|
* open. We pass the txnid along to ensure this.
|
|
*/
|
|
ret = __dbreg_open_file(dbenv,
|
|
op == DB_TXN_ABORT || op == DB_TXN_POPENFILES ?
|
|
argp->txnid : NULL, argp, info);
|
|
if (ret == ENOENT || ret == EINVAL) {
|
|
/*
|
|
* If this is an OPEN while rolling forward, it's
|
|
* possible that the file was recreated since last
|
|
* time we got here. In that case, we've got deleted
|
|
* set and probably shouldn't, so we need to check
|
|
* for that case and possibly retry.
|
|
*/
|
|
if (op == DB_TXN_FORWARD_ROLL &&
|
|
argp->txnid != 0 &&
|
|
dblp->dbentry[argp->fileid].deleted) {
|
|
dblp->dbentry[argp->fileid].deleted = 0;
|
|
ret =
|
|
__dbreg_open_file(dbenv, NULL, argp, info);
|
|
}
|
|
ret = 0;
|
|
}
|
|
F_CLR(dblp, DBLOG_FORCE_OPEN);
|
|
}
|
|
|
|
if (do_close) {
|
|
/*
|
|
* If we are undoing an open, or redoing a close,
|
|
* then we need to close the file.
|
|
*
|
|
* If the file is deleted, then we can just ignore this close.
|
|
* Otherwise, we should usually have a valid dbp we should
|
|
* close or whose reference count should be decremented.
|
|
* However, if we shut down without closing a file, we may, in
|
|
* fact, not have the file open, and that's OK.
|
|
*/
|
|
do_rem = 0;
|
|
MUTEX_THREAD_LOCK(dbenv, dblp->mutexp);
|
|
if (argp->fileid < dblp->dbentry_cnt) {
|
|
/*
|
|
* Typically, closes should match an open which means
|
|
* that if this is a close, there should be a valid
|
|
* entry in the dbentry table when we get here,
|
|
* however there is an exception. If this is an
|
|
* OPENFILES pass, then we may have started from
|
|
* a log file other than the first, and the
|
|
* corresponding open appears in an earlier file.
|
|
* We can ignore that case, but all others are errors.
|
|
*/
|
|
dbe = &dblp->dbentry[argp->fileid];
|
|
if (dbe->dbp == NULL && !dbe->deleted) {
|
|
/* No valid entry here. */
|
|
if ((argp->opcode != LOG_CLOSE &&
|
|
argp->opcode != LOG_RCLOSE) ||
|
|
(op != DB_TXN_OPENFILES &&
|
|
op !=DB_TXN_POPENFILES)) {
|
|
__db_err(dbenv,
|
|
"Improper file close at %lu/%lu",
|
|
(u_long)lsnp->file,
|
|
(u_long)lsnp->offset);
|
|
ret = EINVAL;
|
|
}
|
|
MUTEX_THREAD_UNLOCK(dbenv, dblp->mutexp);
|
|
goto done;
|
|
}
|
|
|
|
/* We have either an open entry or a deleted entry. */
|
|
if ((dbp = dbe->dbp) != NULL) {
|
|
MUTEX_THREAD_UNLOCK(dbenv, dblp->mutexp);
|
|
(void)__dbreg_revoke_id(dbp, 0);
|
|
|
|
/*
|
|
* If we're a replication client, it's
|
|
* possible to get here with a dbp that
|
|
* the user opened, but which we later
|
|
* assigned a fileid to. Be sure that
|
|
* we only close dbps that we opened in
|
|
* the recovery code; they should have
|
|
* DB_AM_RECOVER set.
|
|
*
|
|
* The only exception is if we're aborting
|
|
* in a normal environment; then we might
|
|
* get here with a non-AM_RECOVER database.
|
|
*/
|
|
if (F_ISSET(dbp, DB_AM_RECOVER) ||
|
|
op == DB_TXN_ABORT)
|
|
do_rem = 1;
|
|
} else if (dbe->deleted) {
|
|
MUTEX_THREAD_UNLOCK(dbenv, dblp->mutexp);
|
|
__dbreg_rem_dbentry(dblp, argp->fileid);
|
|
}
|
|
} else
|
|
MUTEX_THREAD_UNLOCK(dbenv, dblp->mutexp);
|
|
if (do_rem) {
|
|
/*
|
|
* If we are undoing a create we'd better discard
|
|
* any buffers from the memory pool.
|
|
*/
|
|
if (dbp != NULL && dbp->mpf != NULL && argp->id != 0) {
|
|
if ((ret = dbp->mpf->close(dbp->mpf,
|
|
DB_MPOOL_DISCARD)) != 0)
|
|
goto out;
|
|
dbp->mpf = NULL;
|
|
}
|
|
|
|
/*
|
|
* During recovery, all files are closed. On an abort,
|
|
* we only close the file if we opened it during the
|
|
* abort (DB_AM_RECOVER set), otherwise we simply do
|
|
* a __db_refresh. For the close case, if remove or
|
|
* rename has closed the file, don't request a sync,
|
|
* because the NULL mpf would be a problem.
|
|
*/
|
|
if (dbp != NULL) {
|
|
if (op == DB_TXN_ABORT &&
|
|
!F_ISSET(dbp, DB_AM_RECOVER))
|
|
t_ret =
|
|
__db_refresh(dbp, NULL, DB_NOSYNC);
|
|
else
|
|
t_ret = dbp->close(dbp, DB_NOSYNC);
|
|
if (t_ret != 0 && ret == 0)
|
|
ret = t_ret;
|
|
}
|
|
}
|
|
}
|
|
done: if (ret == 0)
|
|
*lsnp = argp->prev_lsn;
|
|
out: if (argp != NULL)
|
|
__os_free(dbenv, argp);
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* __dbreg_open_file --
|
|
* Called during log_register recovery. Make sure that we have an
|
|
* entry in the dbentry table for this ndx. Returns 0 on success,
|
|
* non-zero on error.
|
|
*/
|
|
static int
|
|
__dbreg_open_file(dbenv, txn, argp, info)
|
|
DB_ENV *dbenv;
|
|
DB_TXN *txn;
|
|
__dbreg_register_args *argp;
|
|
void *info;
|
|
{
|
|
DB_ENTRY *dbe;
|
|
DB_LOG *lp;
|
|
DB *dbp;
|
|
u_int32_t id;
|
|
|
|
lp = (DB_LOG *)dbenv->lg_handle;
|
|
/*
|
|
* We never re-open temporary files. Temp files are only
|
|
* useful during aborts in which case the dbp was entered
|
|
* when the file was registered. During recovery, we treat
|
|
* temp files as properly deleted files, allowing the open to
|
|
* fail and not reporting any errors when recovery fails to
|
|
* get a valid dbp from __dbreg_id_to_db.
|
|
*/
|
|
if (argp->name.size == 0) {
|
|
(void)__dbreg_add_dbentry(dbenv, lp, NULL, argp->fileid);
|
|
return (ENOENT);
|
|
}
|
|
|
|
/*
|
|
* When we're opening, we have to check that the name we are opening
|
|
* is what we expect. If it's not, then we close the old file and
|
|
* open the new one.
|
|
*/
|
|
MUTEX_THREAD_LOCK(dbenv, lp->mutexp);
|
|
if (argp->fileid < lp->dbentry_cnt)
|
|
dbe = &lp->dbentry[argp->fileid];
|
|
else
|
|
dbe = NULL;
|
|
|
|
if (dbe != NULL) {
|
|
if (dbe->deleted) {
|
|
MUTEX_THREAD_UNLOCK(dbenv, lp->mutexp);
|
|
return (ENOENT);
|
|
}
|
|
if ((dbp = dbe->dbp) != NULL) {
|
|
if (dbp->meta_pgno != argp->meta_pgno ||
|
|
memcmp(dbp->fileid,
|
|
argp->uid.data, DB_FILE_ID_LEN) != 0) {
|
|
MUTEX_THREAD_UNLOCK(dbenv, lp->mutexp);
|
|
(void)__dbreg_revoke_id(dbp, 0);
|
|
if (F_ISSET(dbp, DB_AM_RECOVER))
|
|
dbp->close(dbp, DB_NOSYNC);
|
|
goto reopen;
|
|
}
|
|
|
|
/*
|
|
* We should only get here if we already have the
|
|
* dbp from an openfiles pass, in which case, what's
|
|
* here had better be the same dbp.
|
|
*/
|
|
DB_ASSERT(dbe->dbp == dbp);
|
|
MUTEX_THREAD_UNLOCK(dbenv, lp->mutexp);
|
|
|
|
/*
|
|
* This is a successful open. We need to record that
|
|
* in the txnlist so that we know how to handle the
|
|
* subtransaction that created the file system object.
|
|
*/
|
|
if (argp->id != TXN_INVALID &&
|
|
__db_txnlist_update(dbenv, info,
|
|
argp->id, TXN_EXPECTED, NULL) == TXN_NOTFOUND)
|
|
(void)__db_txnlist_add(dbenv,
|
|
info, argp->id, TXN_EXPECTED, NULL);
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
MUTEX_THREAD_UNLOCK(dbenv, lp->mutexp);
|
|
|
|
/*
|
|
* We are about to pass a recovery txn pointer into the main library.
|
|
* We need to make sure that any accessed fields are set appropriately.
|
|
*/
|
|
reopen: if (txn != NULL) {
|
|
id = txn->txnid;
|
|
memset(txn, 0, sizeof(DB_TXN));
|
|
txn->txnid = id;
|
|
txn->mgrp = dbenv->tx_handle;
|
|
}
|
|
|
|
return (__dbreg_do_open(dbenv, txn, lp, argp->uid.data, argp->name.data,
|
|
argp->ftype, argp->fileid, argp->meta_pgno, info, argp->id));
|
|
}
|