about summary refs log tree commit diff
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);
+}