diff options
Diffstat (limited to 'db2/xa')
-rw-r--r-- | db2/xa/xa.c | 682 | ||||
-rw-r--r-- | db2/xa/xa_db.c | 308 | ||||
-rw-r--r-- | db2/xa/xa_map.c | 305 |
3 files changed, 1295 insertions, 0 deletions
diff --git a/db2/xa/xa.c b/db2/xa/xa.c new file mode 100644 index 0000000000..94a96e7e09 --- /dev/null +++ b/db2/xa/xa.c @@ -0,0 +1,682 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1998 + * Sleepycat Software. All rights reserved. + */ + +/* XXX Remove the global transaction and hang it off the environment. */ +#include "config.h" + +#ifndef lint +static const char sccsid[] = "@(#)xa.c 10.4 (Sleepycat) 10/11/98"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#endif + +#include "db_int.h" +#include "db_page.h" +#include "shqueue.h" +#include "log.h" +#include "txn.h" +#include "db_auto.h" +#include "db_ext.h" +#include "db_dispatch.h" + +static int __db_xa_close __P((char *, int, long)); +static int __db_xa_commit __P((XID *, int, long)); +static int __db_xa_complete __P((int *, int *, int, long)); +static int __db_xa_end __P((XID *, int, long)); +static int __db_xa_forget __P((XID *, int, long)); +static int __db_xa_open __P((char *, int, long)); +static int __db_xa_prepare __P((XID *, int, long)); +static int __db_xa_recover __P((XID *, long, int, long)); +static int __db_xa_rollback __P((XID *, int, long)); +static int __db_xa_start __P((XID *, int, long)); +static void __xa_txn_end __P((DB_ENV *)); +static void __xa_txn_init __P((DB_ENV *, TXN_DETAIL *, size_t)); + +/* + * Possible flag values: + * Dynamic registration 0 => no dynamic registration + * TMREGISTER => dynamic registration + * Asynchronous operation 0 => no support for asynchrony + * TMUSEASYNC => async support + * Migration support 0 => migration of transactions across + * threads is possible + * TMNOMIGRATE => no migration across threads + */ +const struct xa_switch_t db_xa_switch = { + "Berkeley DB", /* name[RMNAMESZ] */ + TMNOMIGRATE, /* flags */ + 0, /* version */ + __db_xa_open, /* xa_open_entry */ + __db_xa_close, /* xa_close_entry */ + __db_xa_start, /* xa_start_entry */ + __db_xa_end, /* xa_end_entry */ + __db_xa_rollback, /* xa_rollback_entry */ + __db_xa_prepare, /* xa_prepare_entry */ + __db_xa_commit, /* xa_commit_entry */ + __db_xa_recover, /* xa_recover_entry */ + __db_xa_forget, /* xa_forget_entry */ + __db_xa_complete /* xa_complete_entry */ +}; + +/* + * __db_xa_open -- + * The open call in the XA protocol. The rmid field is an id number + * that the TM assigned us and will pass us on every xa call. We need to + * map that rmid number into a dbenv structure that we create during + * initialization. Since this id number is thread specific, we do not + * need to store it in shared memory. The file xa_map.c implements all + * such xa->db mappings. + * The xa_info field is instance specific information. We require + * that the value of DB_HOME be passed in xa_info. Since xa_info is the + * only thing that we get to pass to db_appinit, any config information + * will have to be done via a config file instead of via the db_appinit + * call. + */ +static int +__db_xa_open(xa_info, rmid, flags) + char *xa_info; + int rmid; + long flags; +{ + DB_ENV *env; + + if (LF_ISSET(TMASYNC)) + return (XAER_ASYNC); + if (flags != TMNOFLAGS) + return (XAER_INVAL); + + /* Verify if we already have this environment open. */ + if (__db_rmid_to_env(rmid, &env, 0) == 0) + return (XA_OK); + + /* + * Since we cannot tell whether the environment is OK or not, + * we can't actually do the db_appinit in xa_open. Instead, + * we save the mapping between the rmid and the xa_info. If + * we next get a call to __xa_recover, we do the db_appinit + * with DB_RECOVER set. If we get any other call, then we + * do the db_appinit. + */ + return (__db_map_rmid_name(rmid, xa_info)); +} + +/* + * __db_xa_close -- + * The close call of the XA protocol. The only trickiness here + * is that if there are any active transactions, we must fail. It is + * *not* an error to call close on an environment that has already been + * closed (I am interpreting that to mean it's OK to call close on an + * environment that has never been opened). + */ +static int +__db_xa_close(xa_info, rmid, flags) + char *xa_info; + int rmid; + long flags; +{ + DB_ENV *env; + int ret, t_ret; + + COMPQUIET(xa_info, NULL); + + if (LF_ISSET(TMASYNC)) + return (XAER_ASYNC); + if (flags != TMNOFLAGS) + return (XAER_INVAL); + + /* If the environment is closed, then we're done. */ + if (__db_rmid_to_env(rmid, &env, 0) != 0) + return (XA_OK); + + /* Check if there are any pending transactions. */ + if (env->xa_txn != NULL && env->xa_txn->txnid != TXN_INVALID) + return (XAER_PROTO); + + /* Now, destroy the mapping and close the environment. */ + ret = __db_unmap_rmid(rmid); + if ((t_ret = db_appexit(env)) != 0 && ret == 0) + ret = t_ret; + + __os_free(env, sizeof(DB_ENV)); + + return (ret == 0 ? XA_OK : XAER_RMERR); +} + +/* + * __db_xa_start -- + * Begin a transaction for the current resource manager. + */ +static int +__db_xa_start(xid, rmid, flags) + XID *xid; + int rmid; + long flags; +{ + DB_ENV *env; + TXN_DETAIL *td; + size_t off; + int is_known; + +#define OK_FLAGS (TMJOIN | TMRESUME | TMNOWAIT | TMASYNC | TMNOFLAGS) + if (LF_ISSET(~OK_FLAGS)) + return (XAER_INVAL); + + if (LF_ISSET(TMJOIN) && LF_ISSET(TMRESUME)) + return (XAER_INVAL); + + if (LF_ISSET(TMASYNC)) + return (XAER_ASYNC); + + if (__db_rmid_to_env(rmid, &env, 1) != 0) + return (XAER_PROTO); + + is_known = __db_xid_to_txn(env, xid, &off) == 0; + + if (is_known && !LF_ISSET(TMRESUME) && !LF_ISSET(TMJOIN)) + return (XAER_DUPID); + + if (!is_known && LF_ISSET(TMRESUME | TMJOIN)) + return (XAER_NOTA); + + /* + * This can't block, so we can ignore TMNOWAIT. + * + * Other error conditions: RMERR, RMFAIL, OUTSIDE, PROTO, RB* + */ + if (is_known) { + td = (TXN_DETAIL *)((u_int8_t *)env->tx_info->region + off); + if (td->xa_status == TXN_XA_SUSPENDED && !LF_SET(TMRESUME)) + return (XAER_PROTO); + if (td->xa_status == TXN_XA_DEADLOCKED) + return (XA_RBDEADLOCK); + if (td->xa_status == TXN_XA_ABORTED) + return (XA_RBOTHER); + + /* Now, fill in the global transaction structure. */ + __xa_txn_init(env, td, off); + td->xa_status = TXN_XA_STARTED; + } else { + if (__txn_xa_begin(env, env->xa_txn) != 0) + return (XAER_RMERR); + (void)__db_map_xid(env, xid, env->xa_txn->off); + td = (TXN_DETAIL *) + ((u_int8_t *)env->tx_info->region + env->xa_txn->off); + td->xa_status = TXN_XA_STARTED; + } + return (XA_OK); +} + +/* + * __db_xa_end -- + * Disassociate the current transaction from the current process. + */ +static int +__db_xa_end(xid, rmid, flags) + XID *xid; + int rmid; + long flags; +{ + DB_ENV *env; + DB_TXN *txn; + TXN_DETAIL *td; + size_t off; + + if (flags != TMNOFLAGS && !LF_ISSET(TMSUSPEND | TMSUCCESS | TMFAIL)) + return (XAER_INVAL); + + if (__db_rmid_to_env(rmid, &env, 0) != 0) + return (XAER_PROTO); + + if (__db_xid_to_txn(env, xid, &off) != 0) + return (XAER_NOTA); + + txn = env->xa_txn; + if (off != txn->off) + return (XAER_PROTO); + + td = (TXN_DETAIL *)((u_int8_t *)env->tx_info->region + off); + if (td->xa_status == TXN_XA_DEADLOCKED) + return (XA_RBDEADLOCK); + + if (td->status == TXN_ABORTED) + return (XA_RBOTHER); + + if (td->xa_status != TXN_XA_STARTED) + return (XAER_PROTO); + + /* Update the shared memory last_lsn field */ + td->last_lsn = txn->last_lsn; + + /* + * If we ever support XA migration, we cannot keep SUSPEND/END + * status in the shared region; it would have to be process local. + */ + if (LF_ISSET(TMSUSPEND)) + td->xa_status = TXN_XA_SUSPENDED; + else + td->xa_status = TXN_XA_ENDED; + + txn->txnid = TXN_INVALID; + return (XA_OK); +} + +/* + * __db_xa_prepare -- + * Sync the log to disk so we can guarantee recoverability. + */ +static int +__db_xa_prepare(xid, rmid, flags) + XID *xid; + int rmid; + long flags; +{ + DB_ENV *env; + TXN_DETAIL *td; + size_t off; + + if (LF_ISSET(TMASYNC)) + return (XAER_ASYNC); + if (flags != TMNOFLAGS) + return (XAER_INVAL); + + /* + * We need to know if we've ever called prepare on this. + * As part of the prepare, we set the xa_status field to + * reflect that fact that prepare has been called, and if + * it's ever called again, it's an error. + */ + if (__db_rmid_to_env(rmid, &env, 1) != 0) + return (XAER_PROTO); + + if (__db_xid_to_txn(env, xid, &off) != 0) + return (XAER_NOTA); + + td = (TXN_DETAIL *)((u_int8_t *)env->tx_info->region + off); + + if (td->xa_status == TXN_XA_DEADLOCKED) + return (XA_RBDEADLOCK); + + if (td->xa_status != TXN_XA_ENDED && td->xa_status != TXN_XA_SUSPENDED) + return (XAER_PROTO); + + /* Now, fill in the global transaction structure. */ + __xa_txn_init(env, td, off); + + if (txn_prepare(env->xa_txn) != 0) + return (XAER_RMERR); + + td->xa_status = TXN_XA_PREPARED; + + /* No fatal value that would require an XAER_RMFAIL. */ + __xa_txn_end(env); + return (XA_OK); +} + +/* + * __db_xa_commit -- + * Commit the transaction + */ +static int +__db_xa_commit(xid, rmid, flags) + XID *xid; + int rmid; + long flags; +{ + DB_ENV *env; + TXN_DETAIL *td; + size_t off; + + if (LF_ISSET(TMASYNC)) + return (XAER_ASYNC); +#undef OK_FLAGS +#define OK_FLAGS (TMNOFLAGS | TMNOWAIT | TMONEPHASE) + if (LF_ISSET(~OK_FLAGS)) + return (XAER_INVAL); + + /* + * We need to know if we've ever called prepare on this. + * We can verify this by examining the xa_status field. + */ + if (__db_rmid_to_env(rmid, &env, 1) != 0) + return (XAER_PROTO); + + if (__db_xid_to_txn(env, xid, &off) != 0) + return (XAER_NOTA); + + td = (TXN_DETAIL *)((u_int8_t *)env->tx_info->region + off); + + if (td->xa_status == TXN_XA_DEADLOCKED) + return (XA_RBDEADLOCK); + + if (td->xa_status == TXN_XA_ABORTED) + return (XA_RBOTHER); + + if (LF_SET(TMONEPHASE) && + td->xa_status != TXN_XA_ENDED && td->xa_status != TXN_XA_SUSPENDED) + return (XAER_PROTO); + + if (!LF_SET(TMONEPHASE) && td->xa_status != TXN_XA_PREPARED) + return (XAER_PROTO); + + /* Now, fill in the global transaction structure. */ + __xa_txn_init(env, td, off); + + if (txn_commit(env->xa_txn) != 0) + return (XAER_RMERR); + + /* No fatal value that would require an XAER_RMFAIL. */ + __xa_txn_end(env); + return (XA_OK); +} + +/* + * __db_xa_recover -- + * Returns a list of prepared and heuristically completed transactions. + * + * The return value is the number of xids placed into the xid array (less + * than or equal to the count parameter). The flags are going to indicate + * whether we are starting a scan or continuing one. + */ +static int +__db_xa_recover(xids, count, rmid, flags) + XID *xids; + long count, flags; + int rmid; +{ + __txn_xa_regop_args *argp; + DBT data; + DB_ENV *env; + DB_LOG *log; + XID *xidp; + char *dbhome; + int err, ret; + u_int32_t rectype, txnid; + + ret = 0; + xidp = xids; + + + /* + * If we are starting a scan, then we need to open the environment + * and run recovery. This recovery puts us in a state where we can + * either commit or abort any transactions that were prepared but not + * yet committed. Once we've done that, we need to figure out where + * to begin checking for such transactions. If we are not starting + * a scan, then the environment had better have already been recovered + * and we'll start from * wherever the log cursor is. Since XA apps + * cannot be threaded, we don't have to worry about someone else + * having moved it. + */ + if (LF_ISSET(TMSTARTRSCAN)) { + /* If the environment is open, we have a problem. */ + if (__db_rmid_to_env(rmid, &env, 0) == XA_OK) + return (XAER_PROTO); + + if ((ret = __os_calloc(1, sizeof(DB_ENV), &env)) != 0) + return (XAER_RMERR); + + if (__db_rmid_to_name(rmid, &dbhome) != 0) + goto err1; + +#undef XA_FLAGS +#define XA_FLAGS DB_RECOVER | \ + DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN + if ((ret = db_appinit(dbhome, NULL, env, XA_FLAGS)) != 0) + goto err1; + + if (__db_map_rmid(rmid, env) != 0) + goto err2; + + /* Now figure out from where to begin scan. */ + log = env->lg_info; + if ((err = __log_findckp(log, &log->xa_first)) == DB_NOTFOUND) { + /* + * If there were no log files, then we have no + * transactions to return, so we simply return 0. + */ + return (0); + } + if ((err = __db_txnlist_init(&log->xa_info)) != 0) + goto err3; + } else { + /* We had better already know about this rmid. */ + if (__db_rmid_to_env(rmid, &env, 0) != 0) + return (XAER_PROTO); + /* + * If we are not starting a scan, the log cursor had + * better be set. + */ + log = env->lg_info; + if (IS_ZERO_LSN(log->xa_lsn)) + return (XAER_PROTO); + } + + /* + * At this point log->xa_first contains the point in the log + * to which we need to roll back. If we are starting a scan, + * we'll start at the last record; if we're continuing a scan, + * we'll have to start at log->xa_lsn. + */ + + memset(&data, 0, sizeof(data)); + for (err = log_get(log, &log->xa_lsn, &data, + LF_ISSET(TMSTARTRSCAN) ? DB_LAST : DB_SET); + err == 0 && log_compare(&log->xa_lsn, &log->xa_first) > 0; + err = log_get(log, &log->xa_lsn, &data, DB_PREV)) { + memcpy(&rectype, data.data, sizeof(rectype)); + + /* + * The only record type we care about is an DB_txn_xa_regop. + * If it's a commit, we have to add it to a txnlist. If it's + * a prepare, and we don't have a commit, then we return it. + * We are redoing some of what's in the xa_regop_recovery + * code, but we have to do it here so we can get at the xid + * in the record. + */ + if (rectype != DB_txn_xa_regop && rectype != DB_txn_regop) + continue; + + memcpy(&txnid, (u_int8_t *)data.data + sizeof(rectype), + sizeof(txnid)); + err = __db_txnlist_find(log->xa_info, txnid); + switch (rectype) { + case DB_txn_regop: + if (err == DB_NOTFOUND) + __db_txnlist_add(log->xa_info, txnid); + err = 0; + break; + case DB_txn_xa_regop: + /* + * This transaction is commited, so we needn't read + * the record and do anything. + */ + if (err == 0) + break; + if ((err = + __txn_xa_regop_read(data.data, &argp)) != 0) { + ret = XAER_RMERR; + goto out; + } + + xidp->formatID = argp->formatID; + xidp->gtrid_length = argp->gtrid; + xidp->bqual_length = argp->bqual; + memcpy(xidp->data, argp->xid.data, argp->xid.size); + ret++; + xidp++; + __os_free(argp, sizeof(*argp)); + if (ret == count) + goto done; + break; + } + } + + if (err != 0 && err != DB_NOTFOUND) + goto out; + +done: if (LF_ISSET(TMENDRSCAN)) { + ZERO_LSN(log->xa_lsn); + ZERO_LSN(log->xa_first); + +out: __db_txnlist_end(log->xa_info); + log->xa_info = NULL; + } + return (ret); + +err3: (void)__db_unmap_rmid(rmid); +err2: (void)db_appexit(env); +err1: __os_free(env, sizeof(DB_ENV)); + return (XAER_RMERR); +} + +/* + * __db_xa_rollback + * Abort an XA transaction. + */ +static int +__db_xa_rollback(xid, rmid, flags) + XID *xid; + int rmid; + long flags; +{ + DB_ENV *env; + TXN_DETAIL *td; + size_t off; + + if (LF_ISSET(TMASYNC)) + return (XAER_ASYNC); + if (flags != TMNOFLAGS) + return (XAER_INVAL); + + if (__db_rmid_to_env(rmid, &env, 1) != 0) + return (XAER_PROTO); + + if (__db_xid_to_txn(env, xid, &off) != 0) + return (XAER_NOTA); + + td = (TXN_DETAIL *)((u_int8_t *)env->tx_info->region + off); + + if (td->xa_status == TXN_XA_DEADLOCKED) + return (XA_RBDEADLOCK); + + if (td->xa_status == TXN_XA_ABORTED) + return (XA_RBOTHER); + + if (LF_SET(TMONEPHASE) && + td->xa_status != TXN_XA_ENDED && td->xa_status != TXN_XA_SUSPENDED) + return (XAER_PROTO); + + if (!LF_SET(TMONEPHASE) && td->xa_status != TXN_XA_PREPARED) + return (XAER_PROTO); + + /* Now, fill in the global transaction structure. */ + __xa_txn_init(env, td, off); + if (txn_abort(env->xa_txn) != 0) + return (XAER_RMERR); + + /* No fatal value that would require an XAER_RMFAIL. */ + __xa_txn_end(env); + return (XA_OK); +} + +/* + * __db_xa_forget -- + * Forget about an XID for a transaction that was heuristically + * completed. Since we do not heuristically complete anything, I + * don't think we have to do anything here, but we should make sure + * that we reclaim the slots in the txnid table. + */ +static int +__db_xa_forget(xid, rmid, flags) + XID *xid; + int rmid; + long flags; +{ + DB_ENV *env; + size_t off; + + if (LF_ISSET(TMASYNC)) + return (XAER_ASYNC); + if (flags != TMNOFLAGS) + return (XAER_INVAL); + + if (__db_rmid_to_env(rmid, &env, 1) != 0) + return (XAER_PROTO); + + /* + * If mapping is gone, then we're done. + */ + if (__db_xid_to_txn(env, xid, &off) != 0) + return (XA_OK); + + __db_unmap_xid(env, xid, off); + + /* No fatal value that would require an XAER_RMFAIL. */ + return (XA_OK); +} + +/* + * __db_xa_complete -- + * Used to wait for asynchronous operations to complete. Since we're + * not doing asynch, this is an invalid operation. + */ +static int +__db_xa_complete(handle, retval, rmid, flags) + int *handle, *retval, rmid; + long flags; +{ + COMPQUIET(handle, NULL); + COMPQUIET(retval, NULL); + COMPQUIET(rmid, 0); + COMPQUIET(flags, 0); + + return (XAER_INVAL); +} + +/* + * __xa_txn_init -- + * Fill in the fields of the local transaction structure given + * the detail transaction structure. + */ +static void +__xa_txn_init(env, td, off) + DB_ENV *env; + TXN_DETAIL *td; + size_t off; +{ + DB_TXN *txn; + + txn = env->xa_txn; + txn->mgrp = env->tx_info; + txn->parent = NULL; + txn->last_lsn = td->last_lsn; + txn->txnid = td->txnid; + txn->off = off; + txn->flags = 0; +} + +/* + * __xa_txn_end -- + * Invalidate a transaction structure that was generated by xa_txn_init. + */ +static void +__xa_txn_end(env) + DB_ENV *env; +{ + DB_TXN *txn; + + txn = env->xa_txn; + if (txn != NULL) + txn->txnid = TXN_INVALID; +} + diff --git a/db2/xa/xa_db.c b/db2/xa/xa_db.c new file mode 100644 index 0000000000..4aaaeff108 --- /dev/null +++ b/db2/xa/xa_db.c @@ -0,0 +1,308 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1998 + * Sleepycat Software. All rights reserved. + */ + +#include "config.h" + +#ifndef lint +static const char sccsid[] = "@(#)xa_db.c 10.6 (Sleepycat) 12/19/98"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#endif + +#undef stat + +#include "db_int.h" +#include "db_page.h" +#include "xa.h" +#include "xa_ext.h" +#include "db_am.h" +#include "db_ext.h" +#include "common_ext.h" + +static int __xa_c_close __P((DBC *)); +static int __xa_c_del __P((DBC *, u_int32_t)); +static int __xa_c_get __P((DBC *, DBT *, DBT *, u_int32_t)); +static int __xa_c_put __P((DBC *, DBT *, DBT *, u_int32_t)); +static int __xa_close __P((DB *, u_int32_t)); +static int __xa_cursor __P((DB *, DB_TXN *, DBC **, u_int32_t)); +static int __xa_del __P((DB *, DB_TXN *, DBT *, u_int32_t)); +static int __xa_fd __P((DB *, int *)); +static int __xa_get __P((DB *, DB_TXN *, DBT *, DBT *, u_int32_t)); +static int __xa_put __P((DB *, DB_TXN *, DBT *, DBT *, u_int32_t)); +static int __xa_stat __P((DB *, void *, void *(*)(size_t), u_int32_t)); +static int __xa_sync __P((DB *, u_int32_t)); + +int +db_xa_open(fname, type, flags, mode, dbinfo, dbpp) + const char *fname; + DBTYPE type; + u_int32_t flags; + int mode; + DB_INFO *dbinfo; + DB **dbpp; +{ + DB *dbp, *real_dbp; + DB_ENV *dbenv; + struct __rmname *rp; + int ret; + + /* + * First try to open up the underlying DB. + * + * !!! + * The dbenv argument is taken from the global list of environments. + * When the transaction manager called xa_start() (__db_xa_start()), + * the "current" DB environment was moved to the start of the list. + * However, if we were called in a tpsvrinit function (which is + * entirely plausible), then it's possible that xa_open was called + * (which simply recorded the name of the environment to open) and + * this is the next call into DB. In that case, we still have to + * open the environment. + * + * The way that we know that xa_open and nothing else was called + * is because the nameq is not NULL. + */ + if ((rp = TAILQ_FIRST(&DB_GLOBAL(db_nameq))) != NULL && + (ret = __db_rmid_to_env(rp->rmid, &dbenv, 1)) != 0) + return (ret); + + dbenv = TAILQ_FIRST(&DB_GLOBAL(db_envq)); + if ((ret = db_open(fname, + type, flags, mode, dbenv, dbinfo, &real_dbp)) != 0) + return (ret); + + /* + * Allocate our own DB handle, and copy the exported fields and + * function pointers into it. The internal pointer references + * the real underlying DB handle. + */ + if ((ret = __os_calloc(1, sizeof(DB), &dbp)) != 0) { + (void)real_dbp->close(real_dbp, 0); + return (ret); + } + dbp->type = real_dbp->type; + dbp->byteswapped = real_dbp->byteswapped; + dbp->dbenv = dbenv; + dbp->internal = real_dbp; + TAILQ_INIT(&dbp->active_queue); + TAILQ_INIT(&dbp->free_queue); + dbp->close = __xa_close; + dbp->cursor = __xa_cursor; + dbp->del = __xa_del; + dbp->fd = __xa_fd; + dbp->get = __xa_get; + dbp->join = real_dbp->join; + dbp->put = __xa_put; + dbp->stat = __xa_stat; + dbp->sync = __xa_sync; + + *dbpp = dbp; + return (0); +} + +static int +__xa_close(dbp, flags) + DB *dbp; + u_int32_t flags; +{ + DB *real_dbp; + DBC *dbc; + int ret; + + /* Close any associated cursors. */ + while ((dbc = TAILQ_FIRST(&dbp->active_queue)) != NULL) + (void)dbc->c_close(dbc); + + /* Close the DB handle. */ + real_dbp = (DB *)dbp->internal; + ret = real_dbp->close(real_dbp, flags); + + __os_free(dbp, sizeof(DB)); + return (ret); +} + +static int +__xa_cursor(dbp, txn, dbcp, flags) + DB *dbp; + DB_TXN *txn; + DBC **dbcp; + u_int32_t flags; +{ + DB *real_dbp; + DBC *real_dbc, *dbc; + int ret; + + real_dbp = (DB *)dbp->internal; + txn = dbp->dbenv->xa_txn; + + if ((ret = real_dbp->cursor(real_dbp, txn, &real_dbc, flags)) != 0) + return (ret); + + /* + * Allocate our own DBC handle, and copy the exported fields and + * function pointers into it. The internal pointer references + * the real underlying DBC handle. + */ + if ((ret = __os_calloc(1, sizeof(DBC), &dbc)) != 0) { + (void)real_dbc->c_close(real_dbc); + return (ret); + } + dbc->dbp = dbp; + dbc->c_close = __xa_c_close; + dbc->c_del = __xa_c_del; + dbc->c_get = __xa_c_get; + dbc->c_put = __xa_c_put; + dbc->internal = real_dbc; + TAILQ_INSERT_TAIL(&dbp->active_queue, dbc, links); + + *dbcp = dbc; + return (0); +} + +static int +__xa_fd(dbp, fdp) + DB *dbp; + int *fdp; +{ + DB *real_dbp; + + COMPQUIET(fdp, NULL); + + real_dbp = (DB *)dbp->internal; + return (__db_eopnotsup(real_dbp->dbenv)); +} + +static int +__xa_del(dbp, txn, key, flags) + DB *dbp; + DB_TXN *txn; + DBT *key; + u_int32_t flags; +{ + DB *real_dbp; + + real_dbp = (DB *)dbp->internal; + txn = dbp->dbenv->xa_txn; + + return (real_dbp->del(real_dbp, txn, key, flags)); +} + +static int +__xa_get(dbp, txn, key, data, flags) + DB *dbp; + DB_TXN *txn; + DBT *key; + DBT *data; + u_int32_t flags; +{ + DB *real_dbp; + + real_dbp = (DB *)dbp->internal; + txn = dbp->dbenv->xa_txn; + + return (real_dbp->get(real_dbp, txn, key, data, flags)); +} + +static int +__xa_put(dbp, txn, key, data, flags) + DB *dbp; + DB_TXN *txn; + DBT *key; + DBT *data; + u_int32_t flags; +{ + DB *real_dbp; + + real_dbp = (DB *)dbp->internal; + txn = dbp->dbenv->xa_txn; + + return (real_dbp->put(real_dbp, txn, key, data, flags)); +} + +static int +__xa_stat(dbp, spp, db_malloc, flags) + DB *dbp; + void *spp; + void *(*db_malloc) __P((size_t)); + u_int32_t flags; +{ + DB *real_dbp; + + real_dbp = (DB *)dbp->internal; + return (real_dbp->stat(real_dbp, spp, db_malloc, flags)); +} + +static int +__xa_sync(dbp, flags) + DB *dbp; + u_int32_t flags; +{ + DB *real_dbp; + + real_dbp = (DB *)dbp->internal; + return (real_dbp->sync(real_dbp, flags)); +} + +static int +__xa_c_close(dbc) + DBC *dbc; +{ + DBC *real_dbc; + int ret; + + real_dbc = (DBC *)dbc->internal; + + ret = real_dbc->c_close(real_dbc); + + TAILQ_REMOVE(&dbc->dbp->active_queue, dbc, links); + __os_free(dbc, sizeof(DBC)); + + return (ret); +} + +static int +__xa_c_del(dbc, flags) + DBC *dbc; + u_int32_t flags; +{ + DBC *real_dbc; + + real_dbc = (DBC *)dbc->internal; + return (real_dbc->c_del(real_dbc, flags)); +} + +static int +__xa_c_get(dbc, key, data, flags) + DBC *dbc; + DBT *key; + DBT *data; + u_int32_t flags; +{ + DBC *real_dbc; + + real_dbc = (DBC *)dbc->internal; + return (real_dbc->c_get(real_dbc, key, data, flags)); +} + +static int +__xa_c_put(dbc, key, data, flags) + DBC *dbc; + DBT *key; + DBT *data; + u_int32_t flags; +{ + DBC *real_dbc; + + real_dbc = (DBC *)dbc->internal; + return (real_dbc->c_put(real_dbc, key, data, flags)); +} diff --git a/db2/xa/xa_map.c b/db2/xa/xa_map.c new file mode 100644 index 0000000000..d4ebbae22f --- /dev/null +++ b/db2/xa/xa_map.c @@ -0,0 +1,305 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1996, 1997, 1998 + * Sleepycat Software. All rights reserved. + */ + +#include "config.h" + +#ifndef lint +static const char sccsid[] = "@(#)xa_map.c 10.4 (Sleepycat) 10/20/98"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#endif + +#include "db_int.h" +#include "shqueue.h" +#include "txn.h" + +/* + * This file contains all the mapping information that we need to support + * the DB/XA interface. + */ + +/* + * __db_rmid_to_env + * Return the environment associated with a given XA rmid. + * + * PUBLIC: int __db_rmid_to_env __P((int rmid, DB_ENV **envp, int open_ok)); + */ +int +__db_rmid_to_env(rmid, envp, open_ok) + int rmid; + DB_ENV **envp; + int open_ok; +{ + DB_ENV *env; + char *dbhome; + + env = TAILQ_FIRST(&DB_GLOBAL(db_envq)); + if (env != NULL && env->xa_rmid == rmid) { + *envp = env; + return (0); + } + + /* + * When we map an rmid, move that environment to be the first one in + * the list of environments, so we pass the correct environment from + * the upcoming db_xa_open() call into db_open(). + */ + for (; env != NULL; env = TAILQ_NEXT(env, links)) + if (env->xa_rmid == rmid) { + TAILQ_REMOVE(&DB_GLOBAL(db_envq), env, links); + TAILQ_INSERT_HEAD(&DB_GLOBAL(db_envq), env, links); + *envp = env; + return (0); + } + + /* + * We have not found the rmid on the environment list. If we + * are allowed to do an open, search for the rmid on the name + * list and, if we find it, then open it. + */ + if (!open_ok) + return (1); + + if (__db_rmid_to_name(rmid, &dbhome) != 0) + return (1); +#undef XA_FLAGS +#define XA_FLAGS \ + DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN + + if (__os_calloc(1, sizeof(DB_ENV), &env) != 0) + return (1); + + if (db_appinit(dbhome, NULL, env, XA_FLAGS) != 0) + goto err; + + if (__db_map_rmid(rmid, env) != 0) + goto err1; + + __db_unmap_rmid_name(rmid); + + *envp = env; + return (0); + +err1: (void)db_appexit(env); +err: __os_free(env, sizeof(DB_ENV)); + return (1); +} + +/* + * __db_xid_to_txn + * Return the txn that corresponds to this XID. + * + * PUBLIC: int __db_xid_to_txn __P((DB_ENV *, XID *, size_t *)); + */ +int +__db_xid_to_txn(dbenv, xid, offp) + DB_ENV *dbenv; + XID *xid; + size_t *offp; +{ + DB_TXNREGION *tmr; + struct __txn_detail *td; + + /* + * Search the internal active transaction table to find the + * matching xid. If this is a performance hit, then we + * can create a hash table, but I doubt it's worth it. + */ + tmr = dbenv->tx_info->region; + + LOCK_TXNREGION(dbenv->tx_info); + for (td = SH_TAILQ_FIRST(&tmr->active_txn, __txn_detail); + td != NULL; + td = SH_TAILQ_NEXT(td, links, __txn_detail)) + if (memcmp(xid->data, td->xid, XIDDATASIZE) == 0) + break; + UNLOCK_TXNREGION(dbenv->tx_info); + + if (td == NULL) + return (EINVAL); + + *offp = (u_int8_t *)td - (u_int8_t *)tmr; + return (0); +} + +/* + * __db_map_rmid + * Create a mapping between the specified rmid and environment. + * + * PUBLIC: int __db_map_rmid __P((int, DB_ENV *)); + */ +int +__db_map_rmid(rmid, env) + int rmid; + DB_ENV *env; +{ + if (__os_calloc(1, sizeof(DB_TXN), &env->xa_txn) != 0) + return (XAER_RMERR); + env->xa_txn->txnid = TXN_INVALID; + env->xa_rmid = rmid; + TAILQ_INSERT_HEAD(&DB_GLOBAL(db_envq), env, links); + return (XA_OK); +} + +/* + * __db_unmap_rmid + * Destroy the mapping for the given rmid. + * + * PUBLIC: int __db_unmap_rmid __P((int)); + */ +int +__db_unmap_rmid(rmid) + int rmid; +{ + DB_ENV *e; + + for (e = TAILQ_FIRST(&DB_GLOBAL(db_envq)); + e->xa_rmid != rmid; + e = TAILQ_NEXT(e, links)); + + if (e == NULL) + return (EINVAL); + + TAILQ_REMOVE(&DB_GLOBAL(db_envq), e, links); + if (e->xa_txn != NULL) + __os_free(e->xa_txn, sizeof(DB_TXN)); + return (0); +} + +/* + * __db_map_xid + * Create a mapping between this XID and the transaction at + * "off" in the shared region. + * + * PUBLIC: int __db_map_xid __P((DB_ENV *, XID *, size_t)); + */ +int +__db_map_xid(env, xid, off) + DB_ENV *env; + XID *xid; + size_t off; +{ + DB_TXNMGR *tm; + TXN_DETAIL *td; + + tm = env->tx_info; + td = (TXN_DETAIL *)((u_int8_t *)tm->region + off); + + LOCK_TXNREGION(tm); + memcpy(td->xid, xid->data, XIDDATASIZE); + UNLOCK_TXNREGION(tm); + + return (0); +} + +/* + * __db_unmap_xid + * Destroy the mapping for the specified XID. + * + * PUBLIC: void __db_unmap_xid __P((DB_ENV *, XID *, size_t)); + */ + +void +__db_unmap_xid(env, xid, off) + DB_ENV *env; + XID *xid; + size_t off; +{ + TXN_DETAIL *td; + + COMPQUIET(xid, NULL); + + td = (TXN_DETAIL *)((u_int8_t *)env->tx_info->region + off); + memset(td->xid, 0, sizeof(td->xid)); +} + +/* + * __db_map_rmid_name -- + * Create a mapping from an rmid to a name (the xa_info argument). + * We use this during create and then at some later point when we are + * trying to map an rmid, we might indicate that it's OK to do an open + * in which case, we'll get the xa_info parameter from here and then + * free it up. + * + * PUBLIC: int __db_map_rmid_name __P((int, char *)); + */ + +int +__db_map_rmid_name(rmid, dbhome) + int rmid; + char *dbhome; +{ + struct __rmname *entry; + int ret; + + if ((ret = __os_malloc(sizeof(struct __rmname), NULL, &entry)) != 0) + return (ret); + + if ((ret = __os_strdup(dbhome, &entry->dbhome)) != 0) { + __os_free(entry, sizeof(struct __rmname)); + return (ret); + } + + entry->rmid = rmid; + + TAILQ_INSERT_HEAD(&DB_GLOBAL(db_nameq), entry, links); + return (0); +} + +/* + * __db_rmid_to_name -- + * Given an rmid, return the name of the home directory for that + * rmid. + * + * PUBLIC: int __db_rmid_to_name __P((int, char **)); + */ +int +__db_rmid_to_name(rmid, dbhomep) + int rmid; + char **dbhomep; +{ + struct __rmname *np; + + for (np = TAILQ_FIRST(&DB_GLOBAL(db_nameq)); np != NULL; + np = TAILQ_NEXT(np, links)) { + if (np->rmid == rmid) { + *dbhomep = np->dbhome; + return (0); + } + } + return (1); +} + +/* + * __db_unmap_rmid_name -- + * Given an rmid, remove its entry from the name list. + * + * PUBLIC: void __db_unmap_rmid_name __P((int)); + */ +void +__db_unmap_rmid_name(rmid) + int rmid; +{ + struct __rmname *np, *next; + + for (np = TAILQ_FIRST(&DB_GLOBAL(db_nameq)); np != NULL; np = next) { + next = TAILQ_NEXT(np, links); + if (np->rmid == rmid) { + TAILQ_REMOVE(&DB_GLOBAL(db_nameq), np, links); + __os_freestr(np->dbhome); + __os_free(np, sizeof(struct __rmname)); + return; + } + } + return; +} |