aboutsummaryrefslogtreecommitdiff
path: root/db2/txn/txn_rec.c
diff options
context:
space:
mode:
Diffstat (limited to 'db2/txn/txn_rec.c')
-rw-r--r--db2/txn/txn_rec.c196
1 files changed, 178 insertions, 18 deletions
diff --git a/db2/txn/txn_rec.c b/db2/txn/txn_rec.c
index e53dc5f3b7..f21a0f92c8 100644
--- a/db2/txn/txn_rec.c
+++ b/db2/txn/txn_rec.c
@@ -40,7 +40,7 @@
#include "config.h"
#ifndef lint
-static const char sccsid[] = "@(#)txn_rec.c 10.11 (Sleepycat) 5/3/98";
+static const char sccsid[] = "@(#)txn_rec.c 10.15 (Sleepycat) 1/3/99";
#endif /* not lint */
#ifndef NO_SYSTEM_INCLUDES
@@ -54,10 +54,18 @@ static const char sccsid[] = "@(#)txn_rec.c 10.11 (Sleepycat) 5/3/98";
#include "shqueue.h"
#include "txn.h"
#include "db_am.h"
+#include "log.h"
+#include "common_ext.h"
+static int __txn_restore_txn __P((DB_ENV *, DB_LSN *, __txn_xa_regop_args *));
+
+#define IS_XA_TXN(R) (R->xid.size != 0)
+
/*
* PUBLIC: int __txn_regop_recover
- * PUBLIC: __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
+ * PUBLIC: __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
+ *
+ * These records are only ever written for commits.
*/
int
__txn_regop_recover(logp, dbtp, lsnp, redo, info)
@@ -79,24 +87,80 @@ __txn_regop_recover(logp, dbtp, lsnp, redo, info)
if ((ret = __txn_regop_read(dbtp->data, &argp)) != 0)
return (ret);
- switch (argp->opcode) {
- case TXN_COMMIT:
- if (__db_txnlist_find(info,
- argp->txnid->txnid) == DB_NOTFOUND)
- __db_txnlist_add(info, argp->txnid->txnid);
- break;
- case TXN_PREPARE: /* Nothing to do. */
- /* Call __db_txnlist_find so that we update the maxid. */
- (void)__db_txnlist_find(info, argp->txnid->txnid);
- break;
- default:
+ if (argp->opcode != TXN_COMMIT)
+ ret = EINVAL;
+ else
+ if (__db_txnlist_find(info, argp->txnid->txnid) == DB_NOTFOUND)
+ ret = __db_txnlist_add(info, argp->txnid->txnid);
+
+ if (ret == 0)
+ *lsnp = argp->prev_lsn;
+ __os_free(argp, 0);
+
+ return (ret);
+}
+
+/*
+ * PUBLIC: int __txn_xa_regop_recover
+ * PUBLIC: __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
+ *
+ * These records are only ever written for prepares.
+ */
+int
+__txn_xa_regop_recover(logp, dbtp, lsnp, redo, info)
+ DB_LOG *logp;
+ DBT *dbtp;
+ DB_LSN *lsnp;
+ int redo;
+ void *info;
+{
+ __txn_xa_regop_args *argp;
+ int ret;
+
+#ifdef DEBUG_RECOVER
+ (void)__txn_xa_regop_print(logp, dbtp, lsnp, redo, info);
+#endif
+ COMPQUIET(redo, 0);
+ COMPQUIET(logp, NULL);
+
+ if ((ret = __txn_xa_regop_read(dbtp->data, &argp)) != 0)
+ return (ret);
+
+ if (argp->opcode != TXN_PREPARE)
ret = EINVAL;
- break;
+ else {
+ /*
+ * Whether we are in XA or not, we need to call
+ * __db_txnlist_find so that we update the maxid.
+ * If this is an XA transaction, then we treat
+ * prepares like commits so that we roll forward to
+ * a point where we can handle commit/abort calls
+ * from the TMS. If this isn't XA, then a prepare
+ * is treated like a No-op; we only care about the
+ * commit.
+ */
+ ret = __db_txnlist_find(info, argp->txnid->txnid);
+ if (IS_XA_TXN(argp) && ret == DB_NOTFOUND) {
+ /*
+ * This is an XA prepared, but not yet committed
+ * transaction. We need to add it to the
+ * transaction list, so that it gets rolled
+ * forward. We also have to add it to the region's
+ * internal state so it can be properly aborted
+ * or recovered.
+ */
+ ret = __db_txnlist_add(info, argp->txnid->txnid);
+ if (ret == 0)
+ ret = __txn_restore_txn(logp->dbenv,
+ lsnp, argp);
+ }
}
- *lsnp = argp->prev_lsn;
- __db_free(argp);
- return (0);
+ if (ret == 0)
+ *lsnp = argp->prev_lsn;
+ __os_free(argp, 0);
+
+ return (ret);
}
/*
@@ -130,7 +194,103 @@ __txn_ckp_recover(logp, dbtp, lsnp, redo, info)
if (argp->ckp_lsn.file == lsnp->file &&
argp->ckp_lsn.offset == lsnp->offset)
__db_txnlist_gen(info, redo ? -1 : 1);
+
*lsnp = argp->last_ckp;
- __db_free(argp);
+ __os_free(argp, 0);
return (DB_TXN_CKP);
}
+
+/*
+ * __txn_child_recover
+ * Recover a commit record for a child transaction.
+ *
+ * PUBLIC: int __txn_child_recover
+ * PUBLIC: __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
+ */
+int
+__txn_child_recover(logp, dbtp, lsnp, redo, info)
+ DB_LOG *logp;
+ DBT *dbtp;
+ DB_LSN *lsnp;
+ int redo;
+ void *info;
+{
+ __txn_child_args *argp;
+ int ret;
+
+#ifdef DEBUG_RECOVER
+ (void)__txn_child_print(logp, dbtp, lsnp, redo, info);
+#endif
+ COMPQUIET(redo, 0);
+ COMPQUIET(logp, NULL);
+
+ if ((ret = __txn_child_read(dbtp->data, &argp)) != 0)
+ return (ret);
+
+ /*
+ * We count the child as committed only if its parent committed.
+ * So, if we are not yet in the transaction list, but our parent
+ * is, then we should go ahead and commit.
+ */
+ if (argp->opcode != TXN_COMMIT)
+ ret = EINVAL;
+ else
+ if (__db_txnlist_find(info, argp->parent) == 0 &&
+ __db_txnlist_find(info, argp->txnid->txnid) == DB_NOTFOUND)
+ ret = __db_txnlist_add(info, argp->txnid->txnid);
+
+ if (ret == 0)
+ *lsnp = argp->prev_lsn;
+ __os_free(argp, 0);
+
+ return (ret);
+}
+
+/*
+ * __txn_restore_txn --
+ * Using only during XA recovery. If we find any transactions that are
+ * prepared, but not yet committed, then we need to restore the transaction's
+ * state into the shared region, because the TM is going to issue a txn_abort
+ * or txn_commit and we need to respond correctly.
+ *
+ * lsnp is the LSN of the returned LSN
+ * argp is the perpare record (in an appropriate structure)
+ */
+static int
+__txn_restore_txn(dbenv, lsnp, argp)
+ DB_ENV *dbenv;
+ DB_LSN *lsnp;
+ __txn_xa_regop_args *argp;
+{
+ DB_TXNMGR *mgr;
+ TXN_DETAIL *td;
+ int ret;
+
+ if (argp->xid.size == 0)
+ return(0);
+
+ mgr = dbenv->tx_info;
+ LOCK_TXNREGION(mgr);
+
+ /* Allocate a new transaction detail structure. */
+ if ((ret = __db_shalloc(mgr->mem, sizeof(TXN_DETAIL), 0, &td)) != 0)
+ return (ret);
+
+ /* Place transaction on active transaction list. */
+ SH_TAILQ_INSERT_HEAD(&mgr->region->active_txn, td, links, __txn_detail);
+
+ td->txnid = argp->txnid->txnid;
+ td->begin_lsn = argp->begin_lsn;
+ td->last_lsn = *lsnp;
+ td->last_lock = 0;
+ td->parent = 0;
+ td->status = TXN_PREPARED;
+ td->xa_status = TXN_XA_PREPARED;
+ memcpy(td->xid, argp->xid.data, argp->xid.size);
+ td->bqual = argp->bqual;
+ td->gtrid = argp->gtrid;
+ td->format = argp->formatID;
+
+ UNLOCK_TXNREGION(mgr);
+ return (0);
+}