xref: /illumos-gate/usr/src/cmd/sendmail/db/txn/txn_rec.c (revision 55fea89d)
1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1996, 1997, 1998
5  *	Sleepycat Software.  All rights reserved.
6  */
7 /*
8  * Copyright (c) 1996
9  *	The President and Fellows of Harvard University.  All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *	This product includes software developed by the University of
22  *	California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  */
39 
40 #include "config.h"
41 
42 #ifndef lint
43 static const char sccsid[] = "@(#)txn_rec.c	10.15 (Sleepycat) 1/3/99";
44 #endif /* not lint */
45 
46 #ifndef NO_SYSTEM_INCLUDES
47 #include <sys/types.h>
48 
49 #include <errno.h>
50 #endif
51 
52 #include "db_int.h"
53 #include "db_page.h"
54 #include "shqueue.h"
55 #include "txn.h"
56 #include "db_am.h"
57 #include "log.h"
58 #include "common_ext.h"
59 
60 static int __txn_restore_txn __P((DB_ENV *, DB_LSN *, __txn_xa_regop_args *));
61 
62 #define	IS_XA_TXN(R) (R->xid.size != 0)
63 
64 /*
65  * PUBLIC: int __txn_regop_recover
66  * PUBLIC:    __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
67  *
68  * These records are only ever written for commits.
69  */
70 int
__txn_regop_recover(logp,dbtp,lsnp,redo,info)71 __txn_regop_recover(logp, dbtp, lsnp, redo, info)
72 	DB_LOG *logp;
73 	DBT *dbtp;
74 	DB_LSN *lsnp;
75 	int redo;
76 	void *info;
77 {
78 	__txn_regop_args *argp;
79 	int ret;
80 
81 #ifdef DEBUG_RECOVER
82 	(void)__txn_regop_print(logp, dbtp, lsnp, redo, info);
83 #endif
84 	COMPQUIET(redo, 0);
85 	COMPQUIET(logp, NULL);
86 
87 	if ((ret = __txn_regop_read(dbtp->data, &argp)) != 0)
88 		return (ret);
89 
90 	if (argp->opcode != TXN_COMMIT)
91 		ret = EINVAL;
92 	else
93 		if (__db_txnlist_find(info, argp->txnid->txnid) == DB_NOTFOUND)
94 			ret = __db_txnlist_add(info, argp->txnid->txnid);
95 
96 	if (ret == 0)
97 		*lsnp = argp->prev_lsn;
98 	__os_free(argp, 0);
99 
100 	return (ret);
101 }
102 
103 /*
104  * PUBLIC: int __txn_xa_regop_recover
105  * PUBLIC:    __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
106  *
107  * These records are only ever written for prepares.
108  */
109 int
__txn_xa_regop_recover(logp,dbtp,lsnp,redo,info)110 __txn_xa_regop_recover(logp, dbtp, lsnp, redo, info)
111 	DB_LOG *logp;
112 	DBT *dbtp;
113 	DB_LSN *lsnp;
114 	int redo;
115 	void *info;
116 {
117 	__txn_xa_regop_args *argp;
118 	int ret;
119 
120 #ifdef DEBUG_RECOVER
121 	(void)__txn_xa_regop_print(logp, dbtp, lsnp, redo, info);
122 #endif
123 	COMPQUIET(redo, 0);
124 	COMPQUIET(logp, NULL);
125 
126 	if ((ret = __txn_xa_regop_read(dbtp->data, &argp)) != 0)
127 		return (ret);
128 
129 	if (argp->opcode != TXN_PREPARE)
130 		ret = EINVAL;
131 	else {
132 		/*
133 		 * Whether we are in XA or not, we need to call
134 		 * __db_txnlist_find so that we update the maxid.
135 		 * If this is an XA transaction, then we treat
136 		 * prepares like commits so that we roll forward to
137 		 * a point where we can handle commit/abort calls
138 		 * from the TMS.  If this isn't XA, then a prepare
139 		 * is treated like a No-op; we only care about the
140 		 * commit.
141 		 */
142 		ret = __db_txnlist_find(info, argp->txnid->txnid);
143 		if (IS_XA_TXN(argp) && ret == DB_NOTFOUND) {
144 			/*
145 			 * This is an XA prepared, but not yet committed
146 			 * transaction.  We need to add it to the
147 			 * transaction list, so that it gets rolled
148 			 * forward. We also have to add it to the region's
149 			 * internal state so it can be properly aborted
150 			 * or recovered.
151 			 */
152 			ret = __db_txnlist_add(info, argp->txnid->txnid);
153 			if (ret == 0)
154 				ret = __txn_restore_txn(logp->dbenv,
155 				    lsnp, argp);
156 		}
157 	}
158 
159 	if (ret == 0)
160 		*lsnp = argp->prev_lsn;
161 	__os_free(argp, 0);
162 
163 	return (ret);
164 }
165 
166 /*
167  * PUBLIC: int __txn_ckp_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
168  */
169 int
__txn_ckp_recover(logp,dbtp,lsnp,redo,info)170 __txn_ckp_recover(logp, dbtp, lsnp, redo, info)
171 	DB_LOG *logp;
172 	DBT *dbtp;
173 	DB_LSN *lsnp;
174 	int redo;
175 	void *info;
176 {
177 	__txn_ckp_args *argp;
178 	int ret;
179 
180 #ifdef DEBUG_RECOVER
181 	__txn_ckp_print(logp, dbtp, lsnp, redo, info);
182 #endif
183 	COMPQUIET(logp, NULL);
184 
185 	if ((ret = __txn_ckp_read(dbtp->data, &argp)) != 0)
186 		return (ret);
187 
188 	/*
189 	 * Check for 'restart' checkpoint record.  This occurs when the
190 	 * checkpoint lsn is equal to the lsn of the checkpoint record
191 	 * and means that we could set the transaction ID back to 1, so
192 	 * that we don't exhaust the transaction ID name space.
193 	 */
194 	if (argp->ckp_lsn.file == lsnp->file &&
195 	    argp->ckp_lsn.offset == lsnp->offset)
196 		__db_txnlist_gen(info, redo ? -1 : 1);
197 
198 	*lsnp = argp->last_ckp;
199 	__os_free(argp, 0);
200 	return (DB_TXN_CKP);
201 }
202 
203 /*
204  * __txn_child_recover
205  *	Recover a commit record for a child transaction.
206  *
207  * PUBLIC: int __txn_child_recover
208  * PUBLIC:    __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
209  */
210 int
__txn_child_recover(logp,dbtp,lsnp,redo,info)211 __txn_child_recover(logp, dbtp, lsnp, redo, info)
212 	DB_LOG *logp;
213 	DBT *dbtp;
214 	DB_LSN *lsnp;
215 	int redo;
216 	void *info;
217 {
218 	__txn_child_args *argp;
219 	int ret;
220 
221 #ifdef DEBUG_RECOVER
222 	(void)__txn_child_print(logp, dbtp, lsnp, redo, info);
223 #endif
224 	COMPQUIET(redo, 0);
225 	COMPQUIET(logp, NULL);
226 
227 	if ((ret = __txn_child_read(dbtp->data, &argp)) != 0)
228 		return (ret);
229 
230 	/*
231 	 * We count the child as committed only if its parent committed.
232 	 * So, if we are not yet in the transaction list, but our parent
233 	 * is, then we should go ahead and commit.
234 	 */
235 	if (argp->opcode != TXN_COMMIT)
236 		ret = EINVAL;
237 	else
238 		if (__db_txnlist_find(info, argp->parent) == 0 &&
239 		    __db_txnlist_find(info, argp->txnid->txnid) == DB_NOTFOUND)
240 			ret = __db_txnlist_add(info, argp->txnid->txnid);
241 
242 	if (ret == 0)
243 		*lsnp = argp->prev_lsn;
244 	__os_free(argp, 0);
245 
246 	return (ret);
247 }
248 
249 /*
250  * __txn_restore_txn --
251  *	Using only during XA recovery.  If we find any transactions that are
252  * prepared, but not yet committed, then we need to restore the transaction's
253  * state into the shared region, because the TM is going to issue a txn_abort
254  * or txn_commit and we need to respond correctly.
255  *
256  * lsnp is the LSN of the returned LSN
257  * argp is the perpare record (in an appropriate structure)
258  */
259 static int
__txn_restore_txn(dbenv,lsnp,argp)260 __txn_restore_txn(dbenv, lsnp, argp)
261 	DB_ENV *dbenv;
262 	DB_LSN *lsnp;
263 	__txn_xa_regop_args *argp;
264 {
265 	DB_TXNMGR *mgr;
266 	TXN_DETAIL *td;
267 	int ret;
268 
269 	if (argp->xid.size == 0)
270 		return(0);
271 
272 	mgr = dbenv->tx_info;
273 	LOCK_TXNREGION(mgr);
274 
275 	/* Allocate a new transaction detail structure. */
276 	if ((ret = __db_shalloc(mgr->mem, sizeof(TXN_DETAIL), 0, &td)) != 0)
277 		return (ret);
278 
279 	/* Place transaction on active transaction list. */
280 	SH_TAILQ_INSERT_HEAD(&mgr->region->active_txn, td, links, __txn_detail);
281 
282 	td->txnid = argp->txnid->txnid;
283 	td->begin_lsn = argp->begin_lsn;
284 	td->last_lsn = *lsnp;
285 	td->last_lock = 0;
286 	td->parent = 0;
287 	td->status = TXN_PREPARED;
288 	td->xa_status = TXN_XA_PREPARED;
289 	memcpy(td->xid, argp->xid.data, argp->xid.size);
290 	td->bqual = argp->bqual;
291 	td->gtrid = argp->gtrid;
292 	td->format = argp->formatID;
293 
294 	UNLOCK_TXNREGION(mgr);
295 	return (0);
296 }
297