xa.c
上传用户:tsgydb
上传日期:2007-04-14
资源大小:10674k
文件大小:16k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /*-
  2.  * See the file LICENSE for redistribution information.
  3.  *
  4.  * Copyright (c) 1998, 1999, 2000
  5.  * Sleepycat Software.  All rights reserved.
  6.  */
  7. #include "db_config.h"
  8. #ifndef lint
  9. static const char revid[] = "$Id: xa.c,v 11.10 2000/12/14 07:39:14 ubell Exp $";
  10. #endif /* not lint */
  11. #ifndef NO_SYSTEM_INCLUDES
  12. #include <sys/types.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #endif
  16. #include "db_int.h"
  17. #include "db_page.h"
  18. #include "log.h"
  19. #include "txn.h"
  20. #include "db_am.h"
  21. #include "db_dispatch.h"
  22. static int  __db_xa_close __P((char *, int, long));
  23. static int  __db_xa_commit __P((XID *, int, long));
  24. static int  __db_xa_complete __P((int *, int *, int, long));
  25. static int  __db_xa_end __P((XID *, int, long));
  26. static int  __db_xa_forget __P((XID *, int, long));
  27. static int  __db_xa_open __P((char *, int, long));
  28. static int  __db_xa_prepare __P((XID *, int, long));
  29. static int  __db_xa_recover __P((XID *, long, int, long));
  30. static int  __db_xa_rollback __P((XID *, int, long));
  31. static int  __db_xa_start __P((XID *, int, long));
  32. static void __xa_txn_end __P((DB_ENV *));
  33. static void __xa_txn_init __P((DB_ENV *, TXN_DETAIL *, size_t));
  34. /*
  35.  * Possible flag values:
  36.  * Dynamic registration 0 => no dynamic registration
  37.  * TMREGISTER => dynamic registration
  38.  * Asynchronous operation 0 => no support for asynchrony
  39.  * TMUSEASYNC => async support
  40.  * Migration support 0 => migration of transactions across
  41.  *      threads is possible
  42.  * TMNOMIGRATE => no migration across threads
  43.  */
  44. const struct xa_switch_t db_xa_switch = {
  45.  "Berkeley DB", /* name[RMNAMESZ] */
  46.  TMNOMIGRATE, /* flags */
  47.  0, /* version */
  48.  __db_xa_open, /* xa_open_entry */
  49.  __db_xa_close, /* xa_close_entry */
  50.  __db_xa_start, /* xa_start_entry */
  51.  __db_xa_end, /* xa_end_entry */
  52.  __db_xa_rollback, /* xa_rollback_entry */
  53.  __db_xa_prepare, /* xa_prepare_entry */
  54.  __db_xa_commit, /* xa_commit_entry */
  55.  __db_xa_recover, /* xa_recover_entry */
  56.  __db_xa_forget, /* xa_forget_entry */
  57.  __db_xa_complete /* xa_complete_entry */
  58. };
  59. /*
  60.  * __db_xa_open --
  61.  * The open call in the XA protocol.  The rmid field is an id number
  62.  * that the TM assigned us and will pass us on every xa call.  We need to
  63.  * map that rmid number into a dbenv structure that we create during
  64.  * initialization.  Since this id number is thread specific, we do not
  65.  * need to store it in shared memory.  The file xa_map.c implements all
  66.  * such xa->db mappings.
  67.  * The xa_info field is instance specific information.  We require
  68.  * that the value of DB_HOME be passed in xa_info.  Since xa_info is the
  69.  * only thing that we get to pass to db_env_create, any config information
  70.  * will have to be done via a config file instead of via the db_env_create
  71.  * call.
  72.  */
  73. static int
  74. __db_xa_open(xa_info, rmid, flags)
  75. char *xa_info;
  76. int rmid;
  77. long flags;
  78. {
  79. DB_ENV *env;
  80. if (LF_ISSET(TMASYNC))
  81. return (XAER_ASYNC);
  82. if (flags != TMNOFLAGS)
  83. return (XAER_INVAL);
  84. /* Verify if we already have this environment open. */
  85. if (__db_rmid_to_env(rmid, &env) == 0)
  86. return (XA_OK);
  87. if (__os_calloc(env, 1, sizeof(DB_ENV), &env) != 0)
  88. return (XAER_RMERR);
  89. /* Open a new environment. */
  90. #define XA_FLAGS 
  91. DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN
  92. if (db_env_create(&env, 0) != 0)
  93. return (XAER_RMERR);
  94. if (env->open(env, xa_info, XA_FLAGS, 0) != 0)
  95. goto err;
  96. /* Create the mapping. */
  97. if (__db_map_rmid(rmid, env) != 0)
  98. goto err;
  99. /* Allocate space for the current transaction. */
  100. if (__os_calloc(env, 1, sizeof(DB_TXN), &env->xa_txn) != 0)
  101. goto err;
  102. env->xa_txn->txnid = TXN_INVALID;
  103. return (XA_OK);
  104. err: (void)env->close(env, 0);
  105. return (XAER_RMERR);
  106. }
  107. /*
  108.  * __db_xa_close --
  109.  * The close call of the XA protocol.  The only trickiness here
  110.  * is that if there are any active transactions, we must fail.  It is
  111.  * *not* an error to call close on an environment that has already been
  112.  * closed (I am interpreting that to mean it's OK to call close on an
  113.  * environment that has never been opened).
  114.  */
  115. static int
  116. __db_xa_close(xa_info, rmid, flags)
  117. char *xa_info;
  118. int rmid;
  119. long flags;
  120. {
  121. DB_ENV *env;
  122. int ret, t_ret;
  123. COMPQUIET(xa_info, NULL);
  124. if (LF_ISSET(TMASYNC))
  125. return (XAER_ASYNC);
  126. if (flags != TMNOFLAGS)
  127. return (XAER_INVAL);
  128. /* If the environment is closed, then we're done. */
  129. if (__db_rmid_to_env(rmid, &env) != 0)
  130. return (XA_OK);
  131. /* Check if there are any pending transactions. */
  132. if (env->xa_txn != NULL && env->xa_txn->txnid != TXN_INVALID)
  133. return (XAER_PROTO);
  134. /* Destroy the mapping. */
  135. ret = __db_unmap_rmid(rmid);
  136. /* Discard space held for the current transaction. */
  137. if (env->xa_txn != NULL)
  138. __os_free(env->xa_txn, sizeof(DB_TXN));
  139. /* Close the environment. */
  140. if ((t_ret = env->close(env, 0)) != 0 && ret == 0)
  141. ret = t_ret;
  142. return (ret == 0 ? XA_OK : XAER_RMERR);
  143. }
  144. /*
  145.  * __db_xa_start --
  146.  * Begin a transaction for the current resource manager.
  147.  */
  148. static int
  149. __db_xa_start(xid, rmid, flags)
  150. XID *xid;
  151. int rmid;
  152. long flags;
  153. {
  154. DB_ENV *env;
  155. TXN_DETAIL *td;
  156. size_t off;
  157. int is_known;
  158. #define OK_FLAGS (TMJOIN | TMRESUME | TMNOWAIT | TMASYNC | TMNOFLAGS)
  159. if (LF_ISSET(~OK_FLAGS))
  160. return (XAER_INVAL);
  161. if (LF_ISSET(TMJOIN) && LF_ISSET(TMRESUME))
  162. return (XAER_INVAL);
  163. if (LF_ISSET(TMASYNC))
  164. return (XAER_ASYNC);
  165. if (__db_rmid_to_env(rmid, &env) != 0)
  166. return (XAER_PROTO);
  167. is_known = __db_xid_to_txn(env, xid, &off) == 0;
  168. if (is_known && !LF_ISSET(TMRESUME) && !LF_ISSET(TMJOIN))
  169. return (XAER_DUPID);
  170. if (!is_known && LF_ISSET(TMRESUME | TMJOIN))
  171. return (XAER_NOTA);
  172. /*
  173.  * This can't block, so we can ignore TMNOWAIT.
  174.  *
  175.  * Other error conditions: RMERR, RMFAIL, OUTSIDE, PROTO, RB*
  176.  */
  177. if (is_known) {
  178. td = (TXN_DETAIL *)
  179.     R_ADDR(&((DB_TXNMGR *)env->tx_handle)->reginfo, off);
  180. if (td->xa_status == TXN_XA_SUSPENDED &&
  181.     !LF_ISSET(TMRESUME | TMJOIN))
  182. return (XAER_PROTO);
  183. if (td->xa_status == TXN_XA_DEADLOCKED)
  184. return (XA_RBDEADLOCK);
  185. if (td->xa_status == TXN_XA_ABORTED)
  186. return (XA_RBOTHER);
  187. /* Now, fill in the global transaction structure. */
  188. __xa_txn_init(env, td, off);
  189. td->xa_status = TXN_XA_STARTED;
  190. } else {
  191. if (__txn_xa_begin(env, env->xa_txn) != 0)
  192. return (XAER_RMERR);
  193. (void)__db_map_xid(env, xid, env->xa_txn->off);
  194. td = (TXN_DETAIL *)
  195.     R_ADDR(&((DB_TXNMGR *)env->tx_handle)->reginfo,
  196.     env->xa_txn->off);
  197. td->xa_status = TXN_XA_STARTED;
  198. }
  199. return (XA_OK);
  200. }
  201. /*
  202.  * __db_xa_end --
  203.  * Disassociate the current transaction from the current process.
  204.  */
  205. static int
  206. __db_xa_end(xid, rmid, flags)
  207. XID *xid;
  208. int rmid;
  209. long flags;
  210. {
  211. DB_ENV *env;
  212. DB_TXN *txn;
  213. TXN_DETAIL *td;
  214. size_t off;
  215. if (flags != TMNOFLAGS && !LF_ISSET(TMSUSPEND | TMSUCCESS | TMFAIL))
  216. return (XAER_INVAL);
  217. if (__db_rmid_to_env(rmid, &env) != 0)
  218. return (XAER_PROTO);
  219. if (__db_xid_to_txn(env, xid, &off) != 0)
  220. return (XAER_NOTA);
  221. txn = env->xa_txn;
  222. if (off != txn->off)
  223. return (XAER_PROTO);
  224. td = (TXN_DETAIL *)R_ADDR(&((DB_TXNMGR *)env->tx_handle)->reginfo, off);
  225. if (td->xa_status == TXN_XA_DEADLOCKED)
  226. return (XA_RBDEADLOCK);
  227. if (td->status == TXN_ABORTED)
  228. return (XA_RBOTHER);
  229. if (td->xa_status != TXN_XA_STARTED)
  230. return (XAER_PROTO);
  231. /* Update the shared memory last_lsn field */
  232. td->last_lsn = txn->last_lsn;
  233. /*
  234.  * If we ever support XA migration, we cannot keep SUSPEND/END
  235.  * status in the shared region; it would have to be process local.
  236.  */
  237. if (LF_ISSET(TMSUSPEND))
  238. td->xa_status = TXN_XA_SUSPENDED;
  239. else
  240. td->xa_status = TXN_XA_ENDED;
  241. txn->txnid = TXN_INVALID;
  242. return (XA_OK);
  243. }
  244. /*
  245.  * __db_xa_prepare --
  246.  * Sync the log to disk so we can guarantee recoverability.
  247.  */
  248. static int
  249. __db_xa_prepare(xid, rmid, flags)
  250. XID *xid;
  251. int rmid;
  252. long flags;
  253. {
  254. DB_ENV *env;
  255. TXN_DETAIL *td;
  256. size_t off;
  257. if (LF_ISSET(TMASYNC))
  258. return (XAER_ASYNC);
  259. if (flags != TMNOFLAGS)
  260. return (XAER_INVAL);
  261. /*
  262.  * We need to know if we've ever called prepare on this.
  263.  * As part of the prepare, we set the xa_status field to
  264.  * reflect that fact that prepare has been called, and if
  265.  * it's ever called again, it's an error.
  266.  */
  267. if (__db_rmid_to_env(rmid, &env) != 0)
  268. return (XAER_PROTO);
  269. if (__db_xid_to_txn(env, xid, &off) != 0)
  270. return (XAER_NOTA);
  271. td = (TXN_DETAIL *)R_ADDR(&((DB_TXNMGR *)env->tx_handle)->reginfo, off);
  272. if (td->xa_status == TXN_XA_DEADLOCKED)
  273. return (XA_RBDEADLOCK);
  274. if (td->xa_status != TXN_XA_ENDED && td->xa_status != TXN_XA_SUSPENDED)
  275. return (XAER_PROTO);
  276. /* Now, fill in the global transaction structure. */
  277. __xa_txn_init(env, td, off);
  278. if (txn_prepare(env->xa_txn) != 0)
  279. return (XAER_RMERR);
  280. td->xa_status = TXN_XA_PREPARED;
  281. /* No fatal value that would require an XAER_RMFAIL. */
  282. __xa_txn_end(env);
  283. return (XA_OK);
  284. }
  285. /*
  286.  * __db_xa_commit --
  287.  * Commit the transaction
  288.  */
  289. static int
  290. __db_xa_commit(xid, rmid, flags)
  291. XID *xid;
  292. int rmid;
  293. long flags;
  294. {
  295. DB_ENV *env;
  296. TXN_DETAIL *td;
  297. size_t off;
  298. if (LF_ISSET(TMASYNC))
  299. return (XAER_ASYNC);
  300. #undef OK_FLAGS
  301. #define OK_FLAGS (TMNOFLAGS | TMNOWAIT | TMONEPHASE)
  302. if (LF_ISSET(~OK_FLAGS))
  303. return (XAER_INVAL);
  304. /*
  305.  * We need to know if we've ever called prepare on this.
  306.  * We can verify this by examining the xa_status field.
  307.  */
  308. if (__db_rmid_to_env(rmid, &env) != 0)
  309. return (XAER_PROTO);
  310. if (__db_xid_to_txn(env, xid, &off) != 0)
  311. return (XAER_NOTA);
  312. td = (TXN_DETAIL *)R_ADDR(&((DB_TXNMGR *)env->tx_handle)->reginfo, off);
  313. if (td->xa_status == TXN_XA_DEADLOCKED)
  314. return (XA_RBDEADLOCK);
  315. if (td->xa_status == TXN_XA_ABORTED)
  316. return (XA_RBOTHER);
  317. if (LF_ISSET(TMONEPHASE) &&
  318.     td->xa_status != TXN_XA_ENDED && td->xa_status != TXN_XA_SUSPENDED)
  319. return (XAER_PROTO);
  320. if (!LF_ISSET(TMONEPHASE) && td->xa_status != TXN_XA_PREPARED)
  321. return (XAER_PROTO);
  322. /* Now, fill in the global transaction structure. */
  323. __xa_txn_init(env, td, off);
  324. if (txn_commit(env->xa_txn, 0) != 0)
  325. return (XAER_RMERR);
  326. /* No fatal value that would require an XAER_RMFAIL. */
  327. __xa_txn_end(env);
  328. return (XA_OK);
  329. }
  330. /*
  331.  * __db_xa_recover --
  332.  * Returns a list of prepared and heuristically completed transactions.
  333.  *
  334.  * The return value is the number of xids placed into the xid array (less
  335.  * than or equal to the count parameter).  The flags are going to indicate
  336.  * whether we are starting a scan or continuing one.
  337.  */
  338. static int
  339. __db_xa_recover(xids, count, rmid, flags)
  340. XID *xids;
  341. long count, flags;
  342. int rmid;
  343. {
  344. __txn_xa_regop_args *argp;
  345. DBT data;
  346. DB_ENV *env;
  347. DB_LOG *log;
  348. XID *xidp;
  349. int err, ret;
  350. u_int32_t rectype, txnid;
  351. ret = 0;
  352. xidp = xids;
  353. /* If the environment is closed, then we're done. */
  354. if (__db_rmid_to_env(rmid, &env) != 0)
  355. return (XAER_PROTO);
  356. /*
  357.  * If we are starting a scan, then we need to figure out where
  358.  * to begin.  If we are not starting a scan, we'll start from
  359.  * wherever the log cursor is.  Since XA apps cannot be threaded,
  360.  * we don't have to worry about someone else having moved it.
  361.  */
  362. log = env->lg_handle;
  363. if (LF_ISSET(TMSTARTRSCAN)) {
  364. if ((err = __log_findckp(env, &log->xa_first)) == DB_NOTFOUND) {
  365. /*
  366.  * If there were no log files, then we have no
  367.  * transactions to return, so we simply return 0.
  368.  */
  369. return (0);
  370. }
  371. if ((err = __db_txnlist_init(env, &log->xa_info)) != 0)
  372. return (XAER_RMERR);
  373. } else {
  374. /*
  375.  * If we are not starting a scan, the log cursor had
  376.  * better be set.
  377.  */
  378. if (IS_ZERO_LSN(log->xa_lsn))
  379. return (XAER_PROTO);
  380. }
  381. /*
  382.  * At this point log->xa_first contains the point in the log
  383.  * to which we need to roll back.  If we are starting a scan,
  384.  * we'll start at the last record; if we're continuing a scan,
  385.  * we'll have to start at log->xa_lsn.
  386.  */
  387. memset(&data, 0, sizeof(data));
  388. for (err = log_get(env, &log->xa_lsn, &data,
  389.     LF_ISSET(TMSTARTRSCAN) ? DB_LAST : DB_SET);
  390.     err == 0 && log_compare(&log->xa_lsn, &log->xa_first) > 0;
  391.     err = log_get(env, &log->xa_lsn, &data, DB_PREV)) {
  392. memcpy(&rectype, data.data, sizeof(rectype));
  393. /*
  394.  * The only record type we care about is an DB_txn_xa_regop.
  395.  * If it's a commit, we have to add it to a txnlist.  If it's
  396.  * a prepare, and we don't have a commit, then we return it.
  397.  * We are redoing some of what's in the xa_regop_recovery
  398.  * code, but we have to do it here so we can get at the xid
  399.  * in the record.
  400.  */
  401. if (rectype != DB_txn_xa_regop && rectype != DB_txn_regop)
  402. continue;
  403. memcpy(&txnid, (u_int8_t *)data.data + sizeof(rectype),
  404.     sizeof(txnid));
  405. err = __db_txnlist_find(log->xa_info, txnid);
  406. switch (rectype) {
  407. case DB_txn_regop:
  408. if (err == DB_NOTFOUND)
  409. __db_txnlist_add(env, log->xa_info, txnid, 0);
  410. err = 0;
  411. break;
  412. case DB_txn_xa_regop:
  413. /*
  414.  * This transaction is committed, so we needn't read
  415.  * the record and do anything.
  416.  */
  417. if (err == 0)
  418. break;
  419. if ((err =
  420.     __txn_xa_regop_read(env, data.data, &argp)) != 0) {
  421. ret = XAER_RMERR;
  422. goto out;
  423. }
  424. xidp->formatID = argp->formatID;
  425. xidp->gtrid_length = argp->gtrid;
  426. xidp->bqual_length = argp->bqual;
  427. memcpy(xidp->data, argp->xid.data, argp->xid.size);
  428. ret++;
  429. xidp++;
  430. __os_free(argp, sizeof(*argp));
  431. if (ret == count)
  432. goto done;
  433. break;
  434. }
  435. }
  436. if (err != 0 && err != DB_NOTFOUND)
  437. goto out;
  438. done: if (LF_ISSET(TMENDRSCAN)) {
  439. ZERO_LSN(log->xa_lsn);
  440. ZERO_LSN(log->xa_first);
  441. out: __db_txnlist_end(env, log->xa_info);
  442. log->xa_info = NULL;
  443. }
  444. return (ret);
  445. }
  446. /*
  447.  * __db_xa_rollback
  448.  * Abort an XA transaction.
  449.  */
  450. static int
  451. __db_xa_rollback(xid, rmid, flags)
  452. XID *xid;
  453. int rmid;
  454. long flags;
  455. {
  456. DB_ENV *env;
  457. TXN_DETAIL *td;
  458. size_t off;
  459. if (LF_ISSET(TMASYNC))
  460. return (XAER_ASYNC);
  461. if (flags != TMNOFLAGS)
  462. return (XAER_INVAL);
  463. if (__db_rmid_to_env(rmid, &env) != 0)
  464. return (XAER_PROTO);
  465. if (__db_xid_to_txn(env, xid, &off) != 0)
  466. return (XAER_NOTA);
  467. td = (TXN_DETAIL *)R_ADDR(&((DB_TXNMGR *)env->tx_handle)->reginfo, off);
  468. if (td->xa_status == TXN_XA_DEADLOCKED)
  469. return (XA_RBDEADLOCK);
  470. if (td->xa_status == TXN_XA_ABORTED)
  471. return (XA_RBOTHER);
  472. if (td->xa_status != TXN_XA_ENDED && td->xa_status != TXN_XA_SUSPENDED
  473.     && td->xa_status != TXN_XA_PREPARED)
  474. return (XAER_PROTO);
  475. /* Now, fill in the global transaction structure. */
  476. __xa_txn_init(env, td, off);
  477. if (txn_abort(env->xa_txn) != 0)
  478. return (XAER_RMERR);
  479. /* No fatal value that would require an XAER_RMFAIL. */
  480. __xa_txn_end(env);
  481. return (XA_OK);
  482. }
  483. /*
  484.  * __db_xa_forget --
  485.  * Forget about an XID for a transaction that was heuristically
  486.  * completed.  Since we do not heuristically complete anything, I
  487.  * don't think we have to do anything here, but we should make sure
  488.  * that we reclaim the slots in the txnid table.
  489.  */
  490. static int
  491. __db_xa_forget(xid, rmid, flags)
  492. XID *xid;
  493. int rmid;
  494. long flags;
  495. {
  496. DB_ENV *env;
  497. size_t off;
  498. if (LF_ISSET(TMASYNC))
  499. return (XAER_ASYNC);
  500. if (flags != TMNOFLAGS)
  501. return (XAER_INVAL);
  502. if (__db_rmid_to_env(rmid, &env) != 0)
  503. return (XAER_PROTO);
  504. /*
  505.  * If mapping is gone, then we're done.
  506.  */
  507. if (__db_xid_to_txn(env, xid, &off) != 0)
  508. return (XA_OK);
  509. __db_unmap_xid(env, xid, off);
  510. /* No fatal value that would require an XAER_RMFAIL. */
  511. return (XA_OK);
  512. }
  513. /*
  514.  * __db_xa_complete --
  515.  * Used to wait for asynchronous operations to complete.  Since we're
  516.  * not doing asynch, this is an invalid operation.
  517.  */
  518. static int
  519. __db_xa_complete(handle, retval, rmid, flags)
  520. int *handle, *retval, rmid;
  521. long flags;
  522. {
  523. COMPQUIET(handle, NULL);
  524. COMPQUIET(retval, NULL);
  525. COMPQUIET(rmid, 0);
  526. COMPQUIET(flags, 0);
  527. return (XAER_INVAL);
  528. }
  529. /*
  530.  * __xa_txn_init --
  531.  * Fill in the fields of the local transaction structure given
  532.  * the detail transaction structure.
  533.  */
  534. static void
  535. __xa_txn_init(env, td, off)
  536. DB_ENV *env;
  537. TXN_DETAIL *td;
  538. size_t off;
  539. {
  540. DB_TXN *txn;
  541. txn = env->xa_txn;
  542. txn->mgrp = env->tx_handle;
  543. txn->parent = NULL;
  544. txn->last_lsn = td->last_lsn;
  545. txn->txnid = td->txnid;
  546. txn->off = off;
  547. txn->flags = 0;
  548. }
  549. /*
  550.  * __xa_txn_end --
  551.  * Invalidate a transaction structure that was generated by xa_txn_init.
  552.  */
  553. static void
  554. __xa_txn_end(env)
  555. DB_ENV *env;
  556. {
  557. DB_TXN *txn;
  558. txn = env->xa_txn;
  559. if (txn != NULL)
  560. txn->txnid = TXN_INVALID;
  561. }