2 * See the file LICENSE for redistribution information.
5 * Sleepycat Software. All rights reserved.
8 #pragma ident "%Z%%M% %I% %E% SMI"
10 /* XXX Remove the global transaction and hang it off the environment. */
14 static const char sccsid
[] = "@(#)xa.c 10.4 (Sleepycat) 10/11/98";
17 #ifndef NO_SYSTEM_INCLUDES
18 #include <sys/types.h>
32 #include "db_dispatch.h"
34 static int __db_xa_close
__P((char *, int, long));
35 static int __db_xa_commit
__P((XID
*, int, long));
36 static int __db_xa_complete
__P((int *, int *, int, long));
37 static int __db_xa_end
__P((XID
*, int, long));
38 static int __db_xa_forget
__P((XID
*, int, long));
39 static int __db_xa_open
__P((char *, int, long));
40 static int __db_xa_prepare
__P((XID
*, int, long));
41 static int __db_xa_recover
__P((XID
*, long, int, long));
42 static int __db_xa_rollback
__P((XID
*, int, long));
43 static int __db_xa_start
__P((XID
*, int, long));
44 static void __xa_txn_end
__P((DB_ENV
*));
45 static void __xa_txn_init
__P((DB_ENV
*, TXN_DETAIL
*, size_t));
48 * Possible flag values:
49 * Dynamic registration 0 => no dynamic registration
50 * TMREGISTER => dynamic registration
51 * Asynchronous operation 0 => no support for asynchrony
52 * TMUSEASYNC => async support
53 * Migration support 0 => migration of transactions across
55 * TMNOMIGRATE => no migration across threads
57 const struct xa_switch_t db_xa_switch
= {
58 "Berkeley DB", /* name[RMNAMESZ] */
59 TMNOMIGRATE
, /* flags */
61 __db_xa_open
, /* xa_open_entry */
62 __db_xa_close
, /* xa_close_entry */
63 __db_xa_start
, /* xa_start_entry */
64 __db_xa_end
, /* xa_end_entry */
65 __db_xa_rollback
, /* xa_rollback_entry */
66 __db_xa_prepare
, /* xa_prepare_entry */
67 __db_xa_commit
, /* xa_commit_entry */
68 __db_xa_recover
, /* xa_recover_entry */
69 __db_xa_forget
, /* xa_forget_entry */
70 __db_xa_complete
/* xa_complete_entry */
75 * The open call in the XA protocol. The rmid field is an id number
76 * that the TM assigned us and will pass us on every xa call. We need to
77 * map that rmid number into a dbenv structure that we create during
78 * initialization. Since this id number is thread specific, we do not
79 * need to store it in shared memory. The file xa_map.c implements all
80 * such xa->db mappings.
81 * The xa_info field is instance specific information. We require
82 * that the value of DB_HOME be passed in xa_info. Since xa_info is the
83 * only thing that we get to pass to db_appinit, any config information
84 * will have to be done via a config file instead of via the db_appinit
88 __db_xa_open(xa_info
, rmid
, flags
)
95 if (LF_ISSET(TMASYNC
))
97 if (flags
!= TMNOFLAGS
)
100 /* Verify if we already have this environment open. */
101 if (__db_rmid_to_env(rmid
, &env
, 0) == 0)
105 * Since we cannot tell whether the environment is OK or not,
106 * we can't actually do the db_appinit in xa_open. Instead,
107 * we save the mapping between the rmid and the xa_info. If
108 * we next get a call to __xa_recover, we do the db_appinit
109 * with DB_RECOVER set. If we get any other call, then we
112 return (__db_map_rmid_name(rmid
, xa_info
));
117 * The close call of the XA protocol. The only trickiness here
118 * is that if there are any active transactions, we must fail. It is
119 * *not* an error to call close on an environment that has already been
120 * closed (I am interpreting that to mean it's OK to call close on an
121 * environment that has never been opened).
124 __db_xa_close(xa_info
, rmid
, flags
)
132 COMPQUIET(xa_info
, NULL
);
134 if (LF_ISSET(TMASYNC
))
136 if (flags
!= TMNOFLAGS
)
139 /* If the environment is closed, then we're done. */
140 if (__db_rmid_to_env(rmid
, &env
, 0) != 0)
143 /* Check if there are any pending transactions. */
144 if (env
->xa_txn
!= NULL
&& env
->xa_txn
->txnid
!= TXN_INVALID
)
147 /* Now, destroy the mapping and close the environment. */
148 ret
= __db_unmap_rmid(rmid
);
149 if ((t_ret
= db_appexit(env
)) != 0 && ret
== 0)
152 __os_free(env
, sizeof(DB_ENV
));
154 return (ret
== 0 ? XA_OK
: XAER_RMERR
);
159 * Begin a transaction for the current resource manager.
162 __db_xa_start(xid
, rmid
, flags
)
172 #define OK_FLAGS (TMJOIN | TMRESUME | TMNOWAIT | TMASYNC | TMNOFLAGS)
173 if (LF_ISSET(~OK_FLAGS
))
176 if (LF_ISSET(TMJOIN
) && LF_ISSET(TMRESUME
))
179 if (LF_ISSET(TMASYNC
))
182 if (__db_rmid_to_env(rmid
, &env
, 1) != 0)
185 is_known
= __db_xid_to_txn(env
, xid
, &off
) == 0;
187 if (is_known
&& !LF_ISSET(TMRESUME
) && !LF_ISSET(TMJOIN
))
190 if (!is_known
&& LF_ISSET(TMRESUME
| TMJOIN
))
194 * This can't block, so we can ignore TMNOWAIT.
196 * Other error conditions: RMERR, RMFAIL, OUTSIDE, PROTO, RB*
199 td
= (TXN_DETAIL
*)((u_int8_t
*)env
->tx_info
->region
+ off
);
200 if (td
->xa_status
== TXN_XA_SUSPENDED
&& !LF_ISSET(TMRESUME
))
202 if (td
->xa_status
== TXN_XA_DEADLOCKED
)
203 return (XA_RBDEADLOCK
);
204 if (td
->xa_status
== TXN_XA_ABORTED
)
207 /* Now, fill in the global transaction structure. */
208 __xa_txn_init(env
, td
, off
);
209 td
->xa_status
= TXN_XA_STARTED
;
211 if (__txn_xa_begin(env
, env
->xa_txn
) != 0)
213 (void)__db_map_xid(env
, xid
, env
->xa_txn
->off
);
215 ((u_int8_t
*)env
->tx_info
->region
+ env
->xa_txn
->off
);
216 td
->xa_status
= TXN_XA_STARTED
;
223 * Disassociate the current transaction from the current process.
226 __db_xa_end(xid
, rmid
, flags
)
236 if (flags
!= TMNOFLAGS
&& !LF_ISSET(TMSUSPEND
| TMSUCCESS
| TMFAIL
))
239 if (__db_rmid_to_env(rmid
, &env
, 0) != 0)
242 if (__db_xid_to_txn(env
, xid
, &off
) != 0)
249 td
= (TXN_DETAIL
*)((u_int8_t
*)env
->tx_info
->region
+ off
);
250 if (td
->xa_status
== TXN_XA_DEADLOCKED
)
251 return (XA_RBDEADLOCK
);
253 if (td
->status
== TXN_ABORTED
)
256 if (td
->xa_status
!= TXN_XA_STARTED
)
259 /* Update the shared memory last_lsn field */
260 td
->last_lsn
= txn
->last_lsn
;
263 * If we ever support XA migration, we cannot keep SUSPEND/END
264 * status in the shared region; it would have to be process local.
266 if (LF_ISSET(TMSUSPEND
))
267 td
->xa_status
= TXN_XA_SUSPENDED
;
269 td
->xa_status
= TXN_XA_ENDED
;
271 txn
->txnid
= TXN_INVALID
;
277 * Sync the log to disk so we can guarantee recoverability.
280 __db_xa_prepare(xid
, rmid
, flags
)
289 if (LF_ISSET(TMASYNC
))
291 if (flags
!= TMNOFLAGS
)
295 * We need to know if we've ever called prepare on this.
296 * As part of the prepare, we set the xa_status field to
297 * reflect that fact that prepare has been called, and if
298 * it's ever called again, it's an error.
300 if (__db_rmid_to_env(rmid
, &env
, 1) != 0)
303 if (__db_xid_to_txn(env
, xid
, &off
) != 0)
306 td
= (TXN_DETAIL
*)((u_int8_t
*)env
->tx_info
->region
+ off
);
308 if (td
->xa_status
== TXN_XA_DEADLOCKED
)
309 return (XA_RBDEADLOCK
);
311 if (td
->xa_status
!= TXN_XA_ENDED
&& td
->xa_status
!= TXN_XA_SUSPENDED
)
314 /* Now, fill in the global transaction structure. */
315 __xa_txn_init(env
, td
, off
);
317 if (txn_prepare(env
->xa_txn
) != 0)
320 td
->xa_status
= TXN_XA_PREPARED
;
322 /* No fatal value that would require an XAER_RMFAIL. */
329 * Commit the transaction
332 __db_xa_commit(xid
, rmid
, flags
)
341 if (LF_ISSET(TMASYNC
))
344 #define OK_FLAGS (TMNOFLAGS | TMNOWAIT | TMONEPHASE)
345 if (LF_ISSET(~OK_FLAGS
))
349 * We need to know if we've ever called prepare on this.
350 * We can verify this by examining the xa_status field.
352 if (__db_rmid_to_env(rmid
, &env
, 1) != 0)
355 if (__db_xid_to_txn(env
, xid
, &off
) != 0)
358 td
= (TXN_DETAIL
*)((u_int8_t
*)env
->tx_info
->region
+ off
);
360 if (td
->xa_status
== TXN_XA_DEADLOCKED
)
361 return (XA_RBDEADLOCK
);
363 if (td
->xa_status
== TXN_XA_ABORTED
)
366 if (LF_ISSET(TMONEPHASE
) &&
367 td
->xa_status
!= TXN_XA_ENDED
&& td
->xa_status
!= TXN_XA_SUSPENDED
)
370 if (!LF_ISSET(TMONEPHASE
) && td
->xa_status
!= TXN_XA_PREPARED
)
373 /* Now, fill in the global transaction structure. */
374 __xa_txn_init(env
, td
, off
);
376 if (txn_commit(env
->xa_txn
) != 0)
379 /* No fatal value that would require an XAER_RMFAIL. */
386 * Returns a list of prepared and heuristically completed transactions.
388 * The return value is the number of xids placed into the xid array (less
389 * than or equal to the count parameter). The flags are going to indicate
390 * whether we are starting a scan or continuing one.
393 __db_xa_recover(xids
, count
, rmid
, flags
)
398 __txn_xa_regop_args
*argp
;
405 u_int32_t rectype
, txnid
;
412 * If we are starting a scan, then we need to open the environment
413 * and run recovery. This recovery puts us in a state where we can
414 * either commit or abort any transactions that were prepared but not
415 * yet committed. Once we've done that, we need to figure out where
416 * to begin checking for such transactions. If we are not starting
417 * a scan, then the environment had better have already been recovered
418 * and we'll start from * wherever the log cursor is. Since XA apps
419 * cannot be threaded, we don't have to worry about someone else
422 if (LF_ISSET(TMSTARTRSCAN
)) {
423 /* If the environment is open, we have a problem. */
424 if (__db_rmid_to_env(rmid
, &env
, 0) == XA_OK
)
427 if ((ret
= __os_calloc(1, sizeof(DB_ENV
), &env
)) != 0)
430 if (__db_rmid_to_name(rmid
, &dbhome
) != 0)
434 #define XA_FLAGS DB_RECOVER | \
435 DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN
436 if ((ret
= db_appinit(dbhome
, NULL
, env
, XA_FLAGS
)) != 0)
439 if (__db_map_rmid(rmid
, env
) != 0)
442 /* Now figure out from where to begin scan. */
444 if ((err
= __log_findckp(log
, &log
->xa_first
)) == DB_NOTFOUND
) {
446 * If there were no log files, then we have no
447 * transactions to return, so we simply return 0.
451 if ((err
= __db_txnlist_init(&log
->xa_info
)) != 0)
454 /* We had better already know about this rmid. */
455 if (__db_rmid_to_env(rmid
, &env
, 0) != 0)
458 * If we are not starting a scan, the log cursor had
462 if (IS_ZERO_LSN(log
->xa_lsn
))
467 * At this point log->xa_first contains the point in the log
468 * to which we need to roll back. If we are starting a scan,
469 * we'll start at the last record; if we're continuing a scan,
470 * we'll have to start at log->xa_lsn.
473 memset(&data
, 0, sizeof(data
));
474 for (err
= log_get(log
, &log
->xa_lsn
, &data
,
475 LF_ISSET(TMSTARTRSCAN
) ? DB_LAST
: DB_SET
);
476 err
== 0 && log_compare(&log
->xa_lsn
, &log
->xa_first
) > 0;
477 err
= log_get(log
, &log
->xa_lsn
, &data
, DB_PREV
)) {
478 memcpy(&rectype
, data
.data
, sizeof(rectype
));
481 * The only record type we care about is an DB_txn_xa_regop.
482 * If it's a commit, we have to add it to a txnlist. If it's
483 * a prepare, and we don't have a commit, then we return it.
484 * We are redoing some of what's in the xa_regop_recovery
485 * code, but we have to do it here so we can get at the xid
488 if (rectype
!= DB_txn_xa_regop
&& rectype
!= DB_txn_regop
)
491 memcpy(&txnid
, (u_int8_t
*)data
.data
+ sizeof(rectype
),
493 err
= __db_txnlist_find(log
->xa_info
, txnid
);
496 if (err
== DB_NOTFOUND
)
497 __db_txnlist_add(log
->xa_info
, txnid
);
500 case DB_txn_xa_regop
:
502 * This transaction is commited, so we needn't read
503 * the record and do anything.
508 __txn_xa_regop_read(data
.data
, &argp
)) != 0) {
513 xidp
->formatID
= argp
->formatID
;
514 xidp
->gtrid_length
= argp
->gtrid
;
515 xidp
->bqual_length
= argp
->bqual
;
516 memcpy(xidp
->data
, argp
->xid
.data
, argp
->xid
.size
);
519 __os_free(argp
, sizeof(*argp
));
526 if (err
!= 0 && err
!= DB_NOTFOUND
)
529 done
: if (LF_ISSET(TMENDRSCAN
)) {
530 ZERO_LSN(log
->xa_lsn
);
531 ZERO_LSN(log
->xa_first
);
533 out
: __db_txnlist_end(log
->xa_info
);
538 err3
: (void)__db_unmap_rmid(rmid
);
539 err2
: (void)db_appexit(env
);
540 err1
: __os_free(env
, sizeof(DB_ENV
));
546 * Abort an XA transaction.
549 __db_xa_rollback(xid
, rmid
, flags
)
558 if (LF_ISSET(TMASYNC
))
560 if (flags
!= TMNOFLAGS
)
563 if (__db_rmid_to_env(rmid
, &env
, 1) != 0)
566 if (__db_xid_to_txn(env
, xid
, &off
) != 0)
569 td
= (TXN_DETAIL
*)((u_int8_t
*)env
->tx_info
->region
+ off
);
571 if (td
->xa_status
== TXN_XA_DEADLOCKED
)
572 return (XA_RBDEADLOCK
);
574 if (td
->xa_status
== TXN_XA_ABORTED
)
577 if (LF_ISSET(TMONEPHASE
) &&
578 td
->xa_status
!= TXN_XA_ENDED
&& td
->xa_status
!= TXN_XA_SUSPENDED
)
581 /* Now, fill in the global transaction structure. */
582 __xa_txn_init(env
, td
, off
);
583 if (txn_abort(env
->xa_txn
) != 0)
586 /* No fatal value that would require an XAER_RMFAIL. */
593 * Forget about an XID for a transaction that was heuristically
594 * completed. Since we do not heuristically complete anything, I
595 * don't think we have to do anything here, but we should make sure
596 * that we reclaim the slots in the txnid table.
599 __db_xa_forget(xid
, rmid
, flags
)
607 if (LF_ISSET(TMASYNC
))
609 if (flags
!= TMNOFLAGS
)
612 if (__db_rmid_to_env(rmid
, &env
, 1) != 0)
616 * If mapping is gone, then we're done.
618 if (__db_xid_to_txn(env
, xid
, &off
) != 0)
621 __db_unmap_xid(env
, xid
, off
);
623 /* No fatal value that would require an XAER_RMFAIL. */
628 * __db_xa_complete --
629 * Used to wait for asynchronous operations to complete. Since we're
630 * not doing asynch, this is an invalid operation.
633 __db_xa_complete(handle
, retval
, rmid
, flags
)
634 int *handle
, *retval
, rmid
;
637 COMPQUIET(handle
, NULL
);
638 COMPQUIET(retval
, NULL
);
647 * Fill in the fields of the local transaction structure given
648 * the detail transaction structure.
651 __xa_txn_init(env
, td
, off
)
659 txn
->mgrp
= env
->tx_info
;
661 txn
->last_lsn
= td
->last_lsn
;
662 txn
->txnid
= td
->txnid
;
669 * Invalidate a transaction structure that was generated by xa_txn_init.
679 txn
->txnid
= TXN_INVALID
;