00001 /*- 00002 * See the file LICENSE for redistribution information. 00003 * 00004 * Copyright (c) 1998, 1999, 2000 00005 * Sleepycat Software. All rights reserved. 00006 */ 00007 00008 #include "config.h" 00009 00010 #ifndef lint 00011 static const char revid[] = "$Id: xa_8c-source.html,v 1.1 2008年06月08日 10:25:38 sebdiaz Exp $"; 00012 #endif /* not lint */ 00013 00014 #ifndef NO_SYSTEM_INCLUDES 00015 #include <sys/types.h> 00016 00017 #include <stdlib.h> 00018 #include <string.h> 00019 #endif 00020 00021 #include "db_int.h" 00022 #include "db_page.h" 00023 #include "log.h" 00024 #include "txn.h" 00025 #include "db_am.h" 00026 #include "db_dispatch.h" 00027 00028 static int __db_xa_close __P((char *, int, long)); 00029 static int __db_xa_commit __P((XID *, int, long)); 00030 static int __db_xa_complete __P((int *, int *, int, long)); 00031 static int __db_xa_end __P((XID *, int, long)); 00032 static int __db_xa_forget __P((XID *, int, long)); 00033 static int __db_xa_open __P((char *, int, long)); 00034 static int __db_xa_prepare __P((XID *, int, long)); 00035 static int __db_xa_recover __P((XID *, long, int, long)); 00036 static int __db_xa_rollback __P((XID *, int, long)); 00037 static int __db_xa_start __P((XID *, int, long)); 00038 static void __xa_txn_end __P((DB_ENV *)); 00039 static void __xa_txn_init __P((DB_ENV *, TXN_DETAIL *, size_t)); 00040 00041 /* 00042 * Possible flag values: 00043 * Dynamic registration 0 => no dynamic registration 00044 * TMREGISTER => dynamic registration 00045 * Asynchronous operation 0 => no support for asynchrony 00046 * TMUSEASYNC => async support 00047 * Migration support 0 => migration of transactions across 00048 * threads is possible 00049 * TMNOMIGRATE => no migration across threads 00050 */ 00051 const struct xa_switch_t CDB_db_xa_switch = { 00052 "Berkeley DB", /* name[RMNAMESZ] */ 00053 TMNOMIGRATE, /* flags */ 00054 0, /* version */ 00055 __db_xa_open, /* xa_open_entry */ 00056 __db_xa_close, /* xa_close_entry */ 00057 __db_xa_start, /* xa_start_entry */ 00058 __db_xa_end, /* xa_end_entry */ 00059 __db_xa_rollback, /* xa_rollback_entry */ 00060 __db_xa_prepare, /* xa_prepare_entry */ 00061 __db_xa_commit, /* xa_commit_entry */ 00062 __db_xa_recover, /* xa_recover_entry */ 00063 __db_xa_forget, /* xa_forget_entry */ 00064 __db_xa_complete /* xa_complete_entry */ 00065 }; 00066 00067 /* 00068 * __db_xa_open -- 00069 * The open call in the XA protocol. The rmid field is an id number 00070 * that the TM assigned us and will pass us on every xa call. We need to 00071 * map that rmid number into a dbenv structure that we create during 00072 * initialization. Since this id number is thread specific, we do not 00073 * need to store it in shared memory. The file xa_map.c implements all 00074 * such xa->db mappings. 00075 * The xa_info field is instance specific information. We require 00076 * that the value of DB_HOME be passed in xa_info. Since xa_info is the 00077 * only thing that we get to pass to CDB_db_env_create, any config information 00078 * will have to be done via a config file instead of via the CDB_db_env_create 00079 * call. 00080 */ 00081 static int 00082 __db_xa_open(xa_info, rmid, flags) 00083 char *xa_info; 00084 int rmid; 00085 long flags; 00086 { 00087 DB_ENV *env; 00088 00089 if (LF_ISSET(TMASYNC)) 00090 return (XAER_ASYNC); 00091 if (flags != TMNOFLAGS) 00092 return (XAER_INVAL); 00093 00094 /* Verify if we already have this environment open. */ 00095 if (CDB___db_rmid_to_env(rmid, &env) == 0) 00096 return (XA_OK); 00097 if (CDB___os_calloc(env, 1, sizeof(DB_ENV), &env) != 0) 00098 return (XAER_RMERR); 00099 00100 /* Open a new environment. */ 00101 #define XA_FLAGS \ 00102 DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN 00103 if (CDB_db_env_create(&env, 0) != 0) 00104 return (XAER_RMERR); 00105 if (env->open(env, xa_info, XA_FLAGS, 0) != 0) 00106 goto err; 00107 00108 /* Create the mapping. */ 00109 if (CDB___db_map_rmid(rmid, env) != 0) 00110 goto err; 00111 00112 /* Allocate space for the current transaction. */ 00113 if (CDB___os_calloc(env, 1, sizeof(DB_TXN), &env->xa_txn) != 0) 00114 goto err; 00115 env->xa_txn->txnid = TXN_INVALID; 00116 00117 return (XA_OK); 00118 00119 err: (void)env->close(env, 0); 00120 00121 return (XAER_RMERR); 00122 } 00123 00124 /* 00125 * __db_xa_close -- 00126 * The close call of the XA protocol. The only trickiness here 00127 * is that if there are any active transactions, we must fail. It is 00128 * *not* an error to call close on an environment that has already been 00129 * closed (I am interpreting that to mean it's OK to call close on an 00130 * environment that has never been opened). 00131 */ 00132 static int 00133 __db_xa_close(xa_info, rmid, flags) 00134 char *xa_info; 00135 int rmid; 00136 long flags; 00137 { 00138 DB_ENV *env; 00139 int ret, t_ret; 00140 00141 COMPQUIET(xa_info, NULL); 00142 00143 if (LF_ISSET(TMASYNC)) 00144 return (XAER_ASYNC); 00145 if (flags != TMNOFLAGS) 00146 return (XAER_INVAL); 00147 00148 /* If the environment is closed, then we're done. */ 00149 if (CDB___db_rmid_to_env(rmid, &env) != 0) 00150 return (XA_OK); 00151 00152 /* Check if there are any pending transactions. */ 00153 if (env->xa_txn != NULL && env->xa_txn->txnid != TXN_INVALID) 00154 return (XAER_PROTO); 00155 00156 /* Destroy the mapping. */ 00157 ret = CDB___db_unmap_rmid(rmid); 00158 00159 /* Discard space held for the current transaction. */ 00160 if (env->xa_txn != NULL) 00161 CDB___os_free(env->xa_txn, sizeof(DB_TXN)); 00162 00163 /* Close the environment. */ 00164 if ((t_ret = env->close(env, 0)) != 0 && ret == 0) 00165 ret = t_ret; 00166 00167 return (ret == 0 ? XA_OK : XAER_RMERR); 00168 } 00169 00170 /* 00171 * __db_xa_start -- 00172 * Begin a transaction for the current resource manager. 00173 */ 00174 static int 00175 __db_xa_start(xid, rmid, flags) 00176 XID *xid; 00177 int rmid; 00178 long flags; 00179 { 00180 DB_ENV *env; 00181 TXN_DETAIL *td; 00182 size_t off; 00183 int is_known; 00184 00185 #define OK_FLAGS (TMJOIN | TMRESUME | TMNOWAIT | TMASYNC | TMNOFLAGS) 00186 if (LF_ISSET(~OK_FLAGS)) 00187 return (XAER_INVAL); 00188 00189 if (LF_ISSET(TMJOIN) && LF_ISSET(TMRESUME)) 00190 return (XAER_INVAL); 00191 00192 if (LF_ISSET(TMASYNC)) 00193 return (XAER_ASYNC); 00194 00195 if (CDB___db_rmid_to_env(rmid, &env) != 0) 00196 return (XAER_PROTO); 00197 00198 is_known = CDB___db_xid_to_txn(env, xid, &off) == 0; 00199 00200 if (is_known && !LF_ISSET(TMRESUME) && !LF_ISSET(TMJOIN)) 00201 return (XAER_DUPID); 00202 00203 if (!is_known && LF_ISSET(TMRESUME | TMJOIN)) 00204 return (XAER_NOTA); 00205 00206 /* 00207 * This can't block, so we can ignore TMNOWAIT. 00208 * 00209 * Other error conditions: RMERR, RMFAIL, OUTSIDE, PROTO, RB* 00210 */ 00211 if (is_known) { 00212 td = (TXN_DETAIL *) 00213 R_ADDR(&((DB_TXNMGR *)env->tx_handle)->reginfo, off); 00214 if (td->xa_status == TXN_XA_SUSPENDED && 00215 !LF_ISSET(TMRESUME | TMJOIN)) 00216 return (XAER_PROTO); 00217 if (td->xa_status == TXN_XA_DEADLOCKED) 00218 return (XA_RBDEADLOCK); 00219 if (td->xa_status == TXN_XA_ABORTED) 00220 return (XA_RBOTHER); 00221 00222 /* Now, fill in the global transaction structure. */ 00223 __xa_txn_init(env, td, off); 00224 td->xa_status = TXN_XA_STARTED; 00225 } else { 00226 if (CDB___txn_xa_begin(env, env->xa_txn) != 0) 00227 return (XAER_RMERR); 00228 (void)CDB___db_map_xid(env, xid, env->xa_txn->off); 00229 td = (TXN_DETAIL *) 00230 R_ADDR(&((DB_TXNMGR *)env->tx_handle)->reginfo, 00231 env->xa_txn->off); 00232 td->xa_status = TXN_XA_STARTED; 00233 } 00234 return (XA_OK); 00235 } 00236 00237 /* 00238 * __db_xa_end -- 00239 * Disassociate the current transaction from the current process. 00240 */ 00241 static int 00242 __db_xa_end(xid, rmid, flags) 00243 XID *xid; 00244 int rmid; 00245 long flags; 00246 { 00247 DB_ENV *env; 00248 DB_TXN *txn; 00249 TXN_DETAIL *td; 00250 size_t off; 00251 00252 if (flags != TMNOFLAGS && !LF_ISSET(TMSUSPEND | TMSUCCESS | TMFAIL)) 00253 return (XAER_INVAL); 00254 00255 if (CDB___db_rmid_to_env(rmid, &env) != 0) 00256 return (XAER_PROTO); 00257 00258 if (CDB___db_xid_to_txn(env, xid, &off) != 0) 00259 return (XAER_NOTA); 00260 00261 txn = env->xa_txn; 00262 if (off != txn->off) 00263 return (XAER_PROTO); 00264 00265 td = (TXN_DETAIL *)R_ADDR(&((DB_TXNMGR *)env->tx_handle)->reginfo, off); 00266 if (td->xa_status == TXN_XA_DEADLOCKED) 00267 return (XA_RBDEADLOCK); 00268 00269 if (td->status == TXN_ABORTED) 00270 return (XA_RBOTHER); 00271 00272 if (td->xa_status != TXN_XA_STARTED) 00273 return (XAER_PROTO); 00274 00275 /* Update the shared memory last_lsn field */ 00276 td->last_lsn = txn->last_lsn; 00277 00278 /* 00279 * If we ever support XA migration, we cannot keep SUSPEND/END 00280 * status in the shared region; it would have to be process local. 00281 */ 00282 if (LF_ISSET(TMSUSPEND)) 00283 td->xa_status = TXN_XA_SUSPENDED; 00284 else 00285 td->xa_status = TXN_XA_ENDED; 00286 00287 txn->txnid = TXN_INVALID; 00288 return (XA_OK); 00289 } 00290 00291 /* 00292 * __db_xa_prepare -- 00293 * Sync the log to disk so we can guarantee recoverability. 00294 */ 00295 static int 00296 __db_xa_prepare(xid, rmid, flags) 00297 XID *xid; 00298 int rmid; 00299 long flags; 00300 { 00301 DB_ENV *env; 00302 TXN_DETAIL *td; 00303 size_t off; 00304 00305 if (LF_ISSET(TMASYNC)) 00306 return (XAER_ASYNC); 00307 if (flags != TMNOFLAGS) 00308 return (XAER_INVAL); 00309 00310 /* 00311 * We need to know if we've ever called prepare on this. 00312 * As part of the prepare, we set the xa_status field to 00313 * reflect that fact that prepare has been called, and if 00314 * it's ever called again, it's an error. 00315 */ 00316 if (CDB___db_rmid_to_env(rmid, &env) != 0) 00317 return (XAER_PROTO); 00318 00319 if (CDB___db_xid_to_txn(env, xid, &off) != 0) 00320 return (XAER_NOTA); 00321 00322 td = (TXN_DETAIL *)R_ADDR(&((DB_TXNMGR *)env->tx_handle)->reginfo, off); 00323 if (td->xa_status == TXN_XA_DEADLOCKED) 00324 return (XA_RBDEADLOCK); 00325 00326 if (td->xa_status != TXN_XA_ENDED && td->xa_status != TXN_XA_SUSPENDED) 00327 return (XAER_PROTO); 00328 00329 /* Now, fill in the global transaction structure. */ 00330 __xa_txn_init(env, td, off); 00331 00332 if (CDB_txn_prepare(env->xa_txn) != 0) 00333 return (XAER_RMERR); 00334 00335 td->xa_status = TXN_XA_PREPARED; 00336 00337 /* No fatal value that would require an XAER_RMFAIL. */ 00338 __xa_txn_end(env); 00339 return (XA_OK); 00340 } 00341 00342 /* 00343 * __db_xa_commit -- 00344 * Commit the transaction 00345 */ 00346 static int 00347 __db_xa_commit(xid, rmid, flags) 00348 XID *xid; 00349 int rmid; 00350 long flags; 00351 { 00352 DB_ENV *env; 00353 TXN_DETAIL *td; 00354 size_t off; 00355 00356 if (LF_ISSET(TMASYNC)) 00357 return (XAER_ASYNC); 00358 #undef OK_FLAGS 00359 #define OK_FLAGS (TMNOFLAGS | TMNOWAIT | TMONEPHASE) 00360 if (LF_ISSET(~OK_FLAGS)) 00361 return (XAER_INVAL); 00362 00363 /* 00364 * We need to know if we've ever called prepare on this. 00365 * We can verify this by examining the xa_status field. 00366 */ 00367 if (CDB___db_rmid_to_env(rmid, &env) != 0) 00368 return (XAER_PROTO); 00369 00370 if (CDB___db_xid_to_txn(env, xid, &off) != 0) 00371 return (XAER_NOTA); 00372 00373 td = (TXN_DETAIL *)R_ADDR(&((DB_TXNMGR *)env->tx_handle)->reginfo, off); 00374 if (td->xa_status == TXN_XA_DEADLOCKED) 00375 return (XA_RBDEADLOCK); 00376 00377 if (td->xa_status == TXN_XA_ABORTED) 00378 return (XA_RBOTHER); 00379 00380 if (LF_ISSET(TMONEPHASE) && 00381 td->xa_status != TXN_XA_ENDED && td->xa_status != TXN_XA_SUSPENDED) 00382 return (XAER_PROTO); 00383 00384 if (!LF_ISSET(TMONEPHASE) && td->xa_status != TXN_XA_PREPARED) 00385 return (XAER_PROTO); 00386 00387 /* Now, fill in the global transaction structure. */ 00388 __xa_txn_init(env, td, off); 00389 00390 if (CDB_txn_commit(env->xa_txn, 0) != 0) 00391 return (XAER_RMERR); 00392 00393 /* No fatal value that would require an XAER_RMFAIL. */ 00394 __xa_txn_end(env); 00395 return (XA_OK); 00396 } 00397 00398 /* 00399 * __db_xa_recover -- 00400 * Returns a list of prepared and heuristically completed transactions. 00401 * 00402 * The return value is the number of xids placed into the xid array (less 00403 * than or equal to the count parameter). The flags are going to indicate 00404 * whether we are starting a scan or continuing one. 00405 */ 00406 static int 00407 __db_xa_recover(xids, count, rmid, flags) 00408 XID *xids; 00409 long count, flags; 00410 int rmid; 00411 { 00412 __txn_xa_regop_args *argp; 00413 DBT data; 00414 DB_ENV *env; 00415 DB_LOG *log; 00416 XID *xidp; 00417 int err, ret; 00418 u_int32_t rectype, txnid; 00419 00420 ret = 0; 00421 xidp = xids; 00422 00423 /* If the environment is closed, then we're done. */ 00424 if (CDB___db_rmid_to_env(rmid, &env) != 0) 00425 return (XAER_PROTO); 00426 00427 /* 00428 * If we are starting a scan, then we need to figure out where 00429 * to begin. If we are not starting a scan, we'll start from 00430 * wherever the log cursor is. Since XA apps cannot be threaded, 00431 * we don't have to worry about someone else having moved it. 00432 */ 00433 log = env->lg_handle; 00434 if (LF_ISSET(TMSTARTRSCAN)) { 00435 if ((err = CDB___log_findckp(env, &log->xa_first)) == DB_NOTFOUND) { 00436 /* 00437 * If there were no log files, then we have no 00438 * transactions to return, so we simply return 0. 00439 */ 00440 return (0); 00441 } 00442 if ((err = CDB___db_txnlist_init(env, &log->xa_info)) != 0) 00443 return (XAER_RMERR); 00444 } else { 00445 /* 00446 * If we are not starting a scan, the log cursor had 00447 * better be set. 00448 */ 00449 if (IS_ZERO_LSN(log->xa_lsn)) 00450 return (XAER_PROTO); 00451 } 00452 00453 /* 00454 * At this point log->xa_first contains the point in the log 00455 * to which we need to roll back. If we are starting a scan, 00456 * we'll start at the last record; if we're continuing a scan, 00457 * we'll have to start at log->xa_lsn. 00458 */ 00459 00460 memset(&data, 0, sizeof(data)); 00461 for (err = CDB_log_get(env, &log->xa_lsn, &data, 00462 LF_ISSET(TMSTARTRSCAN) ? DB_LAST : DB_SET); 00463 err == 0 && CDB_log_compare(&log->xa_lsn, &log->xa_first) > 0; 00464 err = CDB_log_get(env, &log->xa_lsn, &data, DB_PREV)) { 00465 memcpy(&rectype, data.data, sizeof(rectype)); 00466 00467 /* 00468 * The only record type we care about is an DB_txn_xa_regop. 00469 * If it's a commit, we have to add it to a txnlist. If it's 00470 * a prepare, and we don't have a commit, then we return it. 00471 * We are redoing some of what's in the xa_regop_recovery 00472 * code, but we have to do it here so we can get at the xid 00473 * in the record. 00474 */ 00475 if (rectype != DB_txn_xa_regop && rectype != DB_txn_regop) 00476 continue; 00477 00478 memcpy(&txnid, (u_int8_t *)data.data + sizeof(rectype), 00479 sizeof(txnid)); 00480 err = CDB___db_txnlist_find(log->xa_info, txnid); 00481 switch (rectype) { 00482 case DB_txn_regop: 00483 if (err == DB_NOTFOUND) 00484 CDB___db_txnlist_add(env, log->xa_info, txnid); 00485 err = 0; 00486 break; 00487 case DB_txn_xa_regop: 00488 /* 00489 * This transaction is committed, so we needn't read 00490 * the record and do anything. 00491 */ 00492 if (err == 0) 00493 break; 00494 if ((err = 00495 CDB___txn_xa_regop_read(env, data.data, &argp)) != 0) { 00496 ret = XAER_RMERR; 00497 goto out; 00498 } 00499 00500 xidp->formatID = argp->formatID; 00501 xidp->gtrid_length = argp->gtrid; 00502 xidp->bqual_length = argp->bqual; 00503 memcpy(xidp->data, argp->xid.data, argp->xid.size); 00504 ret++; 00505 xidp++; 00506 CDB___os_free(argp, sizeof(*argp)); 00507 if (ret == count) 00508 goto done; 00509 break; 00510 } 00511 } 00512 00513 if (err != 0 && err != DB_NOTFOUND) 00514 goto out; 00515 00516 done: if (LF_ISSET(TMENDRSCAN)) { 00517 ZERO_LSN(log->xa_lsn); 00518 ZERO_LSN(log->xa_first); 00519 00520 out: CDB___db_txnlist_end(env, log->xa_info); 00521 log->xa_info = NULL; 00522 } 00523 return (ret); 00524 } 00525 00526 /* 00527 * __db_xa_rollback 00528 * Abort an XA transaction. 00529 */ 00530 static int 00531 __db_xa_rollback(xid, rmid, flags) 00532 XID *xid; 00533 int rmid; 00534 long flags; 00535 { 00536 DB_ENV *env; 00537 TXN_DETAIL *td; 00538 size_t off; 00539 00540 if (LF_ISSET(TMASYNC)) 00541 return (XAER_ASYNC); 00542 if (flags != TMNOFLAGS) 00543 return (XAER_INVAL); 00544 00545 if (CDB___db_rmid_to_env(rmid, &env) != 0) 00546 return (XAER_PROTO); 00547 00548 if (CDB___db_xid_to_txn(env, xid, &off) != 0) 00549 return (XAER_NOTA); 00550 00551 td = (TXN_DETAIL *)R_ADDR(&((DB_TXNMGR *)env->tx_handle)->reginfo, off); 00552 if (td->xa_status == TXN_XA_DEADLOCKED) 00553 return (XA_RBDEADLOCK); 00554 00555 if (td->xa_status == TXN_XA_ABORTED) 00556 return (XA_RBOTHER); 00557 00558 if (td->xa_status != TXN_XA_ENDED && td->xa_status != TXN_XA_SUSPENDED 00559 && td->xa_status != TXN_XA_PREPARED) 00560 return (XAER_PROTO); 00561 00562 /* Now, fill in the global transaction structure. */ 00563 __xa_txn_init(env, td, off); 00564 if (CDB_txn_abort(env->xa_txn) != 0) 00565 return (XAER_RMERR); 00566 00567 /* No fatal value that would require an XAER_RMFAIL. */ 00568 __xa_txn_end(env); 00569 return (XA_OK); 00570 } 00571 00572 /* 00573 * __db_xa_forget -- 00574 * Forget about an XID for a transaction that was heuristically 00575 * completed. Since we do not heuristically complete anything, I 00576 * don't think we have to do anything here, but we should make sure 00577 * that we reclaim the slots in the txnid table. 00578 */ 00579 static int 00580 __db_xa_forget(xid, rmid, flags) 00581 XID *xid; 00582 int rmid; 00583 long flags; 00584 { 00585 DB_ENV *env; 00586 size_t off; 00587 00588 if (LF_ISSET(TMASYNC)) 00589 return (XAER_ASYNC); 00590 if (flags != TMNOFLAGS) 00591 return (XAER_INVAL); 00592 00593 if (CDB___db_rmid_to_env(rmid, &env) != 0) 00594 return (XAER_PROTO); 00595 00596 /* 00597 * If mapping is gone, then we're done. 00598 */ 00599 if (CDB___db_xid_to_txn(env, xid, &off) != 0) 00600 return (XA_OK); 00601 00602 CDB___db_unmap_xid(env, xid, off); 00603 00604 /* No fatal value that would require an XAER_RMFAIL. */ 00605 return (XA_OK); 00606 } 00607 00608 /* 00609 * __db_xa_complete -- 00610 * Used to wait for asynchronous operations to complete. Since we're 00611 * not doing asynch, this is an invalid operation. 00612 */ 00613 static int 00614 __db_xa_complete(handle, retval, rmid, flags) 00615 int *handle, *retval, rmid; 00616 long flags; 00617 { 00618 COMPQUIET(handle, NULL); 00619 COMPQUIET(retval, NULL); 00620 COMPQUIET(rmid, 0); 00621 COMPQUIET(flags, 0); 00622 00623 return (XAER_INVAL); 00624 } 00625 00626 /* 00627 * __xa_txn_init -- 00628 * Fill in the fields of the local transaction structure given 00629 * the detail transaction structure. 00630 */ 00631 static void 00632 __xa_txn_init(env, td, off) 00633 DB_ENV *env; 00634 TXN_DETAIL *td; 00635 size_t off; 00636 { 00637 DB_TXN *txn; 00638 00639 txn = env->xa_txn; 00640 txn->mgrp = env->tx_handle; 00641 txn->parent = NULL; 00642 txn->last_lsn = td->last_lsn; 00643 txn->txnid = td->txnid; 00644 txn->off = off; 00645 txn->flags = 0; 00646 } 00647 00648 /* 00649 * __xa_txn_end -- 00650 * Invalidate a transaction structure that was generated by xa_txn_init. 00651 */ 00652 static void 00653 __xa_txn_end(env) 00654 DB_ENV *env; 00655 { 00656 DB_TXN *txn; 00657 00658 txn = env->xa_txn; 00659 if (txn != NULL) 00660 txn->txnid = TXN_INVALID; 00661 }