xref: /illumos-gate/usr/src/cmd/sendmail/db/txn/txn_rec.c (revision 55fea89d)
17c478bd9Sstevel@tonic-gate /*-
27c478bd9Sstevel@tonic-gate  * See the file LICENSE for redistribution information.
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * Copyright (c) 1996, 1997, 1998
57c478bd9Sstevel@tonic-gate  *	Sleepycat Software.  All rights reserved.
67c478bd9Sstevel@tonic-gate  */
77c478bd9Sstevel@tonic-gate /*
87c478bd9Sstevel@tonic-gate  * Copyright (c) 1996
97c478bd9Sstevel@tonic-gate  *	The President and Fellows of Harvard University.  All rights reserved.
107c478bd9Sstevel@tonic-gate  *
117c478bd9Sstevel@tonic-gate  * Redistribution and use in source and binary forms, with or without
127c478bd9Sstevel@tonic-gate  * modification, are permitted provided that the following conditions
137c478bd9Sstevel@tonic-gate  * are met:
147c478bd9Sstevel@tonic-gate  * 1. Redistributions of source code must retain the above copyright
157c478bd9Sstevel@tonic-gate  *    notice, this list of conditions and the following disclaimer.
167c478bd9Sstevel@tonic-gate  * 2. Redistributions in binary form must reproduce the above copyright
177c478bd9Sstevel@tonic-gate  *    notice, this list of conditions and the following disclaimer in the
187c478bd9Sstevel@tonic-gate  *    documentation and/or other materials provided with the distribution.
197c478bd9Sstevel@tonic-gate  * 3. All advertising materials mentioning features or use of this software
207c478bd9Sstevel@tonic-gate  *    must display the following acknowledgement:
217c478bd9Sstevel@tonic-gate  *	This product includes software developed by the University of
227c478bd9Sstevel@tonic-gate  *	California, Berkeley and its contributors.
237c478bd9Sstevel@tonic-gate  * 4. Neither the name of the University nor the names of its contributors
247c478bd9Sstevel@tonic-gate  *    may be used to endorse or promote products derived from this software
257c478bd9Sstevel@tonic-gate  *    without specific prior written permission.
267c478bd9Sstevel@tonic-gate  *
277c478bd9Sstevel@tonic-gate  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
287c478bd9Sstevel@tonic-gate  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
297c478bd9Sstevel@tonic-gate  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
307c478bd9Sstevel@tonic-gate  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
317c478bd9Sstevel@tonic-gate  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
327c478bd9Sstevel@tonic-gate  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
337c478bd9Sstevel@tonic-gate  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
347c478bd9Sstevel@tonic-gate  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
357c478bd9Sstevel@tonic-gate  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
367c478bd9Sstevel@tonic-gate  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
377c478bd9Sstevel@tonic-gate  * SUCH DAMAGE.
387c478bd9Sstevel@tonic-gate  */
397c478bd9Sstevel@tonic-gate 
407c478bd9Sstevel@tonic-gate #include "config.h"
417c478bd9Sstevel@tonic-gate 
427c478bd9Sstevel@tonic-gate #ifndef lint
437c478bd9Sstevel@tonic-gate static const char sccsid[] = "@(#)txn_rec.c	10.15 (Sleepycat) 1/3/99";
447c478bd9Sstevel@tonic-gate #endif /* not lint */
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate #ifndef NO_SYSTEM_INCLUDES
477c478bd9Sstevel@tonic-gate #include <sys/types.h>
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate #include <errno.h>
507c478bd9Sstevel@tonic-gate #endif
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate #include "db_int.h"
537c478bd9Sstevel@tonic-gate #include "db_page.h"
547c478bd9Sstevel@tonic-gate #include "shqueue.h"
557c478bd9Sstevel@tonic-gate #include "txn.h"
567c478bd9Sstevel@tonic-gate #include "db_am.h"
577c478bd9Sstevel@tonic-gate #include "log.h"
587c478bd9Sstevel@tonic-gate #include "common_ext.h"
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate static int __txn_restore_txn __P((DB_ENV *, DB_LSN *, __txn_xa_regop_args *));
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate #define	IS_XA_TXN(R) (R->xid.size != 0)
63*55fea89dSDan Cross 
647c478bd9Sstevel@tonic-gate /*
657c478bd9Sstevel@tonic-gate  * PUBLIC: int __txn_regop_recover
667c478bd9Sstevel@tonic-gate  * PUBLIC:    __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
677c478bd9Sstevel@tonic-gate  *
687c478bd9Sstevel@tonic-gate  * These records are only ever written for commits.
697c478bd9Sstevel@tonic-gate  */
707c478bd9Sstevel@tonic-gate int
__txn_regop_recover(logp,dbtp,lsnp,redo,info)717c478bd9Sstevel@tonic-gate __txn_regop_recover(logp, dbtp, lsnp, redo, info)
727c478bd9Sstevel@tonic-gate 	DB_LOG *logp;
737c478bd9Sstevel@tonic-gate 	DBT *dbtp;
747c478bd9Sstevel@tonic-gate 	DB_LSN *lsnp;
757c478bd9Sstevel@tonic-gate 	int redo;
767c478bd9Sstevel@tonic-gate 	void *info;
777c478bd9Sstevel@tonic-gate {
787c478bd9Sstevel@tonic-gate 	__txn_regop_args *argp;
797c478bd9Sstevel@tonic-gate 	int ret;
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate #ifdef DEBUG_RECOVER
827c478bd9Sstevel@tonic-gate 	(void)__txn_regop_print(logp, dbtp, lsnp, redo, info);
837c478bd9Sstevel@tonic-gate #endif
847c478bd9Sstevel@tonic-gate 	COMPQUIET(redo, 0);
857c478bd9Sstevel@tonic-gate 	COMPQUIET(logp, NULL);
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate 	if ((ret = __txn_regop_read(dbtp->data, &argp)) != 0)
887c478bd9Sstevel@tonic-gate 		return (ret);
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate 	if (argp->opcode != TXN_COMMIT)
917c478bd9Sstevel@tonic-gate 		ret = EINVAL;
927c478bd9Sstevel@tonic-gate 	else
937c478bd9Sstevel@tonic-gate 		if (__db_txnlist_find(info, argp->txnid->txnid) == DB_NOTFOUND)
947c478bd9Sstevel@tonic-gate 			ret = __db_txnlist_add(info, argp->txnid->txnid);
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate 	if (ret == 0)
977c478bd9Sstevel@tonic-gate 		*lsnp = argp->prev_lsn;
987c478bd9Sstevel@tonic-gate 	__os_free(argp, 0);
997c478bd9Sstevel@tonic-gate 
1007c478bd9Sstevel@tonic-gate 	return (ret);
1017c478bd9Sstevel@tonic-gate }
1027c478bd9Sstevel@tonic-gate 
1037c478bd9Sstevel@tonic-gate /*
1047c478bd9Sstevel@tonic-gate  * PUBLIC: int __txn_xa_regop_recover
1057c478bd9Sstevel@tonic-gate  * PUBLIC:    __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
1067c478bd9Sstevel@tonic-gate  *
1077c478bd9Sstevel@tonic-gate  * These records are only ever written for prepares.
1087c478bd9Sstevel@tonic-gate  */
1097c478bd9Sstevel@tonic-gate int
__txn_xa_regop_recover(logp,dbtp,lsnp,redo,info)1107c478bd9Sstevel@tonic-gate __txn_xa_regop_recover(logp, dbtp, lsnp, redo, info)
1117c478bd9Sstevel@tonic-gate 	DB_LOG *logp;
1127c478bd9Sstevel@tonic-gate 	DBT *dbtp;
1137c478bd9Sstevel@tonic-gate 	DB_LSN *lsnp;
1147c478bd9Sstevel@tonic-gate 	int redo;
1157c478bd9Sstevel@tonic-gate 	void *info;
1167c478bd9Sstevel@tonic-gate {
1177c478bd9Sstevel@tonic-gate 	__txn_xa_regop_args *argp;
1187c478bd9Sstevel@tonic-gate 	int ret;
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate #ifdef DEBUG_RECOVER
1217c478bd9Sstevel@tonic-gate 	(void)__txn_xa_regop_print(logp, dbtp, lsnp, redo, info);
1227c478bd9Sstevel@tonic-gate #endif
1237c478bd9Sstevel@tonic-gate 	COMPQUIET(redo, 0);
1247c478bd9Sstevel@tonic-gate 	COMPQUIET(logp, NULL);
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate 	if ((ret = __txn_xa_regop_read(dbtp->data, &argp)) != 0)
1277c478bd9Sstevel@tonic-gate 		return (ret);
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate 	if (argp->opcode != TXN_PREPARE)
1307c478bd9Sstevel@tonic-gate 		ret = EINVAL;
1317c478bd9Sstevel@tonic-gate 	else {
1327c478bd9Sstevel@tonic-gate 		/*
1337c478bd9Sstevel@tonic-gate 		 * Whether we are in XA or not, we need to call
1347c478bd9Sstevel@tonic-gate 		 * __db_txnlist_find so that we update the maxid.
1357c478bd9Sstevel@tonic-gate 		 * If this is an XA transaction, then we treat
1367c478bd9Sstevel@tonic-gate 		 * prepares like commits so that we roll forward to
1377c478bd9Sstevel@tonic-gate 		 * a point where we can handle commit/abort calls
1387c478bd9Sstevel@tonic-gate 		 * from the TMS.  If this isn't XA, then a prepare
1397c478bd9Sstevel@tonic-gate 		 * is treated like a No-op; we only care about the
1407c478bd9Sstevel@tonic-gate 		 * commit.
1417c478bd9Sstevel@tonic-gate 		 */
1427c478bd9Sstevel@tonic-gate 		ret = __db_txnlist_find(info, argp->txnid->txnid);
1437c478bd9Sstevel@tonic-gate 		if (IS_XA_TXN(argp) && ret == DB_NOTFOUND) {
1447c478bd9Sstevel@tonic-gate 			/*
1457c478bd9Sstevel@tonic-gate 			 * This is an XA prepared, but not yet committed
1467c478bd9Sstevel@tonic-gate 			 * transaction.  We need to add it to the
1477c478bd9Sstevel@tonic-gate 			 * transaction list, so that it gets rolled
1487c478bd9Sstevel@tonic-gate 			 * forward. We also have to add it to the region's
1497c478bd9Sstevel@tonic-gate 			 * internal state so it can be properly aborted
1507c478bd9Sstevel@tonic-gate 			 * or recovered.
1517c478bd9Sstevel@tonic-gate 			 */
1527c478bd9Sstevel@tonic-gate 			ret = __db_txnlist_add(info, argp->txnid->txnid);
1537c478bd9Sstevel@tonic-gate 			if (ret == 0)
1547c478bd9Sstevel@tonic-gate 				ret = __txn_restore_txn(logp->dbenv,
1557c478bd9Sstevel@tonic-gate 				    lsnp, argp);
1567c478bd9Sstevel@tonic-gate 		}
1577c478bd9Sstevel@tonic-gate 	}
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate 	if (ret == 0)
1607c478bd9Sstevel@tonic-gate 		*lsnp = argp->prev_lsn;
1617c478bd9Sstevel@tonic-gate 	__os_free(argp, 0);
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate 	return (ret);
1647c478bd9Sstevel@tonic-gate }
1657c478bd9Sstevel@tonic-gate 
1667c478bd9Sstevel@tonic-gate /*
1677c478bd9Sstevel@tonic-gate  * PUBLIC: int __txn_ckp_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
1687c478bd9Sstevel@tonic-gate  */
1697c478bd9Sstevel@tonic-gate int
__txn_ckp_recover(logp,dbtp,lsnp,redo,info)1707c478bd9Sstevel@tonic-gate __txn_ckp_recover(logp, dbtp, lsnp, redo, info)
1717c478bd9Sstevel@tonic-gate 	DB_LOG *logp;
1727c478bd9Sstevel@tonic-gate 	DBT *dbtp;
1737c478bd9Sstevel@tonic-gate 	DB_LSN *lsnp;
1747c478bd9Sstevel@tonic-gate 	int redo;
1757c478bd9Sstevel@tonic-gate 	void *info;
1767c478bd9Sstevel@tonic-gate {
1777c478bd9Sstevel@tonic-gate 	__txn_ckp_args *argp;
1787c478bd9Sstevel@tonic-gate 	int ret;
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate #ifdef DEBUG_RECOVER
1817c478bd9Sstevel@tonic-gate 	__txn_ckp_print(logp, dbtp, lsnp, redo, info);
1827c478bd9Sstevel@tonic-gate #endif
1837c478bd9Sstevel@tonic-gate 	COMPQUIET(logp, NULL);
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate 	if ((ret = __txn_ckp_read(dbtp->data, &argp)) != 0)
1867c478bd9Sstevel@tonic-gate 		return (ret);
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate 	/*
1897c478bd9Sstevel@tonic-gate 	 * Check for 'restart' checkpoint record.  This occurs when the
1907c478bd9Sstevel@tonic-gate 	 * checkpoint lsn is equal to the lsn of the checkpoint record
1917c478bd9Sstevel@tonic-gate 	 * and means that we could set the transaction ID back to 1, so
1927c478bd9Sstevel@tonic-gate 	 * that we don't exhaust the transaction ID name space.
1937c478bd9Sstevel@tonic-gate 	 */
1947c478bd9Sstevel@tonic-gate 	if (argp->ckp_lsn.file == lsnp->file &&
1957c478bd9Sstevel@tonic-gate 	    argp->ckp_lsn.offset == lsnp->offset)
1967c478bd9Sstevel@tonic-gate 		__db_txnlist_gen(info, redo ? -1 : 1);
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate 	*lsnp = argp->last_ckp;
1997c478bd9Sstevel@tonic-gate 	__os_free(argp, 0);
2007c478bd9Sstevel@tonic-gate 	return (DB_TXN_CKP);
2017c478bd9Sstevel@tonic-gate }
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate /*
2047c478bd9Sstevel@tonic-gate  * __txn_child_recover
2057c478bd9Sstevel@tonic-gate  *	Recover a commit record for a child transaction.
2067c478bd9Sstevel@tonic-gate  *
2077c478bd9Sstevel@tonic-gate  * PUBLIC: int __txn_child_recover
2087c478bd9Sstevel@tonic-gate  * PUBLIC:    __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
2097c478bd9Sstevel@tonic-gate  */
2107c478bd9Sstevel@tonic-gate int
__txn_child_recover(logp,dbtp,lsnp,redo,info)2117c478bd9Sstevel@tonic-gate __txn_child_recover(logp, dbtp, lsnp, redo, info)
2127c478bd9Sstevel@tonic-gate 	DB_LOG *logp;
2137c478bd9Sstevel@tonic-gate 	DBT *dbtp;
2147c478bd9Sstevel@tonic-gate 	DB_LSN *lsnp;
2157c478bd9Sstevel@tonic-gate 	int redo;
2167c478bd9Sstevel@tonic-gate 	void *info;
2177c478bd9Sstevel@tonic-gate {
2187c478bd9Sstevel@tonic-gate 	__txn_child_args *argp;
2197c478bd9Sstevel@tonic-gate 	int ret;
2207c478bd9Sstevel@tonic-gate 
2217c478bd9Sstevel@tonic-gate #ifdef DEBUG_RECOVER
2227c478bd9Sstevel@tonic-gate 	(void)__txn_child_print(logp, dbtp, lsnp, redo, info);
2237c478bd9Sstevel@tonic-gate #endif
2247c478bd9Sstevel@tonic-gate 	COMPQUIET(redo, 0);
2257c478bd9Sstevel@tonic-gate 	COMPQUIET(logp, NULL);
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate 	if ((ret = __txn_child_read(dbtp->data, &argp)) != 0)
2287c478bd9Sstevel@tonic-gate 		return (ret);
2297c478bd9Sstevel@tonic-gate 
2307c478bd9Sstevel@tonic-gate 	/*
2317c478bd9Sstevel@tonic-gate 	 * We count the child as committed only if its parent committed.
2327c478bd9Sstevel@tonic-gate 	 * So, if we are not yet in the transaction list, but our parent
2337c478bd9Sstevel@tonic-gate 	 * is, then we should go ahead and commit.
2347c478bd9Sstevel@tonic-gate 	 */
2357c478bd9Sstevel@tonic-gate 	if (argp->opcode != TXN_COMMIT)
2367c478bd9Sstevel@tonic-gate 		ret = EINVAL;
2377c478bd9Sstevel@tonic-gate 	else
2387c478bd9Sstevel@tonic-gate 		if (__db_txnlist_find(info, argp->parent) == 0 &&
2397c478bd9Sstevel@tonic-gate 		    __db_txnlist_find(info, argp->txnid->txnid) == DB_NOTFOUND)
2407c478bd9Sstevel@tonic-gate 			ret = __db_txnlist_add(info, argp->txnid->txnid);
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate 	if (ret == 0)
2437c478bd9Sstevel@tonic-gate 		*lsnp = argp->prev_lsn;
2447c478bd9Sstevel@tonic-gate 	__os_free(argp, 0);
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate 	return (ret);
2477c478bd9Sstevel@tonic-gate }
2487c478bd9Sstevel@tonic-gate 
2497c478bd9Sstevel@tonic-gate /*
2507c478bd9Sstevel@tonic-gate  * __txn_restore_txn --
2517c478bd9Sstevel@tonic-gate  *	Using only during XA recovery.  If we find any transactions that are
2527c478bd9Sstevel@tonic-gate  * prepared, but not yet committed, then we need to restore the transaction's
2537c478bd9Sstevel@tonic-gate  * state into the shared region, because the TM is going to issue a txn_abort
2547c478bd9Sstevel@tonic-gate  * or txn_commit and we need to respond correctly.
2557c478bd9Sstevel@tonic-gate  *
2567c478bd9Sstevel@tonic-gate  * lsnp is the LSN of the returned LSN
2577c478bd9Sstevel@tonic-gate  * argp is the perpare record (in an appropriate structure)
2587c478bd9Sstevel@tonic-gate  */
2597c478bd9Sstevel@tonic-gate static int
__txn_restore_txn(dbenv,lsnp,argp)2607c478bd9Sstevel@tonic-gate __txn_restore_txn(dbenv, lsnp, argp)
2617c478bd9Sstevel@tonic-gate 	DB_ENV *dbenv;
2627c478bd9Sstevel@tonic-gate 	DB_LSN *lsnp;
2637c478bd9Sstevel@tonic-gate 	__txn_xa_regop_args *argp;
2647c478bd9Sstevel@tonic-gate {
2657c478bd9Sstevel@tonic-gate 	DB_TXNMGR *mgr;
2667c478bd9Sstevel@tonic-gate 	TXN_DETAIL *td;
2677c478bd9Sstevel@tonic-gate 	int ret;
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate 	if (argp->xid.size == 0)
2707c478bd9Sstevel@tonic-gate 		return(0);
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate 	mgr = dbenv->tx_info;
2737c478bd9Sstevel@tonic-gate 	LOCK_TXNREGION(mgr);
2747c478bd9Sstevel@tonic-gate 
2757c478bd9Sstevel@tonic-gate 	/* Allocate a new transaction detail structure. */
2767c478bd9Sstevel@tonic-gate 	if ((ret = __db_shalloc(mgr->mem, sizeof(TXN_DETAIL), 0, &td)) != 0)
2777c478bd9Sstevel@tonic-gate 		return (ret);
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate 	/* Place transaction on active transaction list. */
2807c478bd9Sstevel@tonic-gate 	SH_TAILQ_INSERT_HEAD(&mgr->region->active_txn, td, links, __txn_detail);
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate 	td->txnid = argp->txnid->txnid;
2837c478bd9Sstevel@tonic-gate 	td->begin_lsn = argp->begin_lsn;
2847c478bd9Sstevel@tonic-gate 	td->last_lsn = *lsnp;
2857c478bd9Sstevel@tonic-gate 	td->last_lock = 0;
2867c478bd9Sstevel@tonic-gate 	td->parent = 0;
2877c478bd9Sstevel@tonic-gate 	td->status = TXN_PREPARED;
2887c478bd9Sstevel@tonic-gate 	td->xa_status = TXN_XA_PREPARED;
2897c478bd9Sstevel@tonic-gate 	memcpy(td->xid, argp->xid.data, argp->xid.size);
2907c478bd9Sstevel@tonic-gate 	td->bqual = argp->bqual;
2917c478bd9Sstevel@tonic-gate 	td->gtrid = argp->gtrid;
2927c478bd9Sstevel@tonic-gate 	td->format = argp->formatID;
2937c478bd9Sstevel@tonic-gate 
2947c478bd9Sstevel@tonic-gate 	UNLOCK_TXNREGION(mgr);
2957c478bd9Sstevel@tonic-gate 	return (0);
2967c478bd9Sstevel@tonic-gate }
297