#include "db_config.h"
#include "db_int.h"
#include "dbinc/txn.h"
#include "dbinc_auto/xa_ext.h"
static void corrupted_env __P((ENV *, int));
static int __xa_get_txn __P((ENV *,
XID *, TXN_DETAIL *, DB_TXN **, u_long, int));
static void __xa_put_txn __P((ENV *, DB_TXN *));
static int __xa_txn_get_prepared
__P((ENV *, XID *, DB_PREPLIST *, long, long *, u_int32_t));
static int __xa_thread_enter __P((ENV *, DB_THREAD_INFO **));
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));
const struct xa_switch_t db_xa_switch = {
"Berkeley DB",
TMNOMIGRATE,
0,
__db_xa_open,
__db_xa_close,
__db_xa_start,
__db_xa_end,
__db_xa_rollback,
__db_xa_prepare,
__db_xa_commit,
__db_xa_recover,
__db_xa_forget,
__db_xa_complete
};
static int
__xa_get_txn(env, xid, td, txnp, flags, ending)
ENV *env;
XID *xid;
TXN_DETAIL *td;
DB_TXN **txnp;
u_long flags;
int ending;
{
DB_ENV *dbenv;
DB_THREAD_INFO *ip;
int ret;
dbenv = env->dbenv;
COMPQUIET(ip, NULL);
ENV_ENTER_RET(env, ip, ret);
if (ret != 0)
return (XAER_RMFAIL);
else
ret = XA_OK;
DB_ASSERT(env, ip != NULL);
if (ending != 0)
DB_ASSERT(env,
ip->dbth_xa_status == TXN_XA_THREAD_ASSOCIATED);
else
DB_ASSERT(env,
ip->dbth_xa_status != TXN_XA_THREAD_ASSOCIATED);
if (td == NULL) {
DB_ASSERT(env, ending == 0);
if (LF_ISSET(TMJOIN | TMRESUME))
ret = XAER_NOTA;
else if ((ret = __txn_begin(env,
ip, NULL, txnp, DB_TXN_NOWAIT|DB_TXN_SNAPSHOT)) != 0) {
dbenv->err(dbenv, ret, DB_STR("4540",
"xa_get_txn: transaction begin failed"));
ret = XAER_RMERR;
} else {
SH_TAILQ_INSERT_HEAD(&ip->dbth_xatxn,
*txnp, xa_links, __db_txn);
(*txnp)->xa_thr_status = TXN_XA_THREAD_ASSOCIATED;
ip->dbth_xa_status = TXN_XA_THREAD_ASSOCIATED;
td = (TXN_DETAIL *)((*txnp)->td);
memcpy(td->gid, xid->data, XIDDATASIZE);
td->bqual = (u_int32_t)xid->bqual_length;
td->gtrid = (u_int32_t)xid->gtrid_length;
td->format = (int32_t)xid->formatID;
td->xa_br_status = TXN_XA_ACTIVE;
}
} else {
if (ending == 0 && !LF_ISSET(TMRESUME) && !LF_ISSET(TMJOIN)) {
ret = XAER_DUPID;
goto out;
}
SH_TAILQ_FOREACH(*txnp, &ip->dbth_xatxn, xa_links, __db_txn)
if ((*txnp)->td == td)
break;
if (td->parent != INVALID_ROFF) {
dbenv->err(dbenv, EINVAL, DB_STR("4541",
"xa_get_txn: XA transaction with parent"));
ret = XAER_RMERR;
goto out;
}
if (*txnp != NULL) {
if (ending) {
DB_ASSERT(env, (*txnp)->xa_thr_status ==
TXN_XA_THREAD_ASSOCIATED);
DB_ASSERT(env, (*txnp) ==
SH_TAILQ_FIRST(&ip->dbth_xatxn, __db_txn));
} else if (LF_ISSET(TMRESUME)) {
DB_ASSERT(env, (*txnp)->xa_thr_status ==
TXN_XA_THREAD_SUSPENDED);
DB_ASSERT(env, ip->dbth_xa_status ==
TXN_XA_THREAD_SUSPENDED);
(*txnp)->xa_thr_status =
TXN_XA_THREAD_ASSOCIATED;
ip->dbth_xa_status = TXN_XA_THREAD_ASSOCIATED;
if ((*txnp) !=
SH_TAILQ_FIRST(&ip->dbth_xatxn, __db_txn)) {
SH_TAILQ_REMOVE(&ip->dbth_xatxn,
(*txnp), xa_links, __db_txn);
SH_TAILQ_INSERT_HEAD(&ip->dbth_xatxn,
(*txnp), xa_links, __db_txn);
}
if (td->xa_br_status == TXN_XA_IDLE)
td->xa_br_status = TXN_XA_ACTIVE;
} else
ret = XAER_PROTO;
} else {
if (LF_ISSET(TMRESUME)) {
dbenv->err(dbenv, EINVAL, DB_STR("4542",
"xa_get_txn: transaction does not exist"));
ret = XAER_PROTO;
} else if ((ret =
__os_malloc(env, sizeof(DB_TXN), txnp)) == 0) {
ret = __txn_continue(env, *txnp, td, ip, 1);
if (ret != 0) {
dbenv->err(dbenv, ret, DB_STR("4543",
"xa_get_txn: txn_continue fails"));
ret = XAER_RMFAIL;
}
ip->dbth_xa_status = TXN_XA_THREAD_ASSOCIATED;
(*txnp)->xa_thr_status =
TXN_XA_THREAD_ASSOCIATED;
SH_TAILQ_INSERT_HEAD(&ip->dbth_xatxn,
(*txnp), xa_links, __db_txn);
if (td->xa_br_status == TXN_XA_IDLE)
td->xa_br_status = TXN_XA_ACTIVE;
} else {
dbenv->err(dbenv, ret, DB_STR("4544",
"xa_get_txn: os_malloc failed"));
ret = XAER_RMERR;
}
}
}
out: ENV_LEAVE(env, ip);
return (ret);
}
static void
__xa_put_txn(env, txnp)
ENV *env;
DB_TXN *txnp;
{
DB_THREAD_INFO *ip;
TXN_DETAIL *td;
ip = txnp->thread_info;
DB_ASSERT(env, ip != NULL);
SH_TAILQ_REMOVE(&ip->dbth_xatxn, txnp, xa_links, __db_txn);
TAILQ_REMOVE(&txnp->mgrp->txn_chain, txnp, links);
td = txnp->td;
DB_ASSERT(env, td->xa_ref > 0);
td->xa_ref--;
__os_free(env, txnp);
ip->dbth_xa_status = TXN_XA_THREAD_UNASSOCIATED;
}
static
int __xa_thread_enter(env, ipp)
ENV *env;
DB_THREAD_INFO **ipp;
{
int ret;
DB_THREAD_INFO *ip;
COMPQUIET(ip, NULL);
ENV_ENTER_RET(env, ip, ret);
if (ret == 0)
ip->dbth_xa_status = TXN_XA_THREAD_UNASSOCIATED;
*ipp = ip;
return (ret);
}
static int
__xa_txn_get_prepared(env, xids, txns, count, retp, flags)
ENV *env;
XID *xids;
DB_PREPLIST *txns;
long count;
long *retp;
u_int32_t flags;
{
DB_THREAD_INFO *ip;
int ret;
ip = NULL;
ENV_ENTER(env, ip);
REPLICATION_WRAP(env,
(__txn_get_prepared(env, xids, txns, count, retp, flags)), 0, ret);
ENV_LEAVE(env, ip);
return (ret);
}
#define XA_FLAGS \
(DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | \
DB_INIT_TXN | DB_THREAD | DB_REGISTER | DB_RECOVER)
static int
__db_xa_open(xa_info, rmid, arg_flags)
char *xa_info;
int rmid;
long arg_flags;
{
DB_ENV *dbenv;
DB_THREAD_INFO *ip;
ENV *env;
int inmem, ret;
u_long flags;
flags = (u_long)arg_flags;
ret = 0;
if (LF_ISSET(TMASYNC))
return (XAER_ASYNC);
if (flags != TMNOFLAGS)
return (XAER_INVAL);
if (__db_rmid_to_env(rmid, &env) == 0) {
env->xa_ref++;
if ((ret = __xa_thread_enter(env, &ip)) == 0) {
DB_ASSERT(env, ip != NULL);
ENV_LEAVE(env, ip);
return (XA_OK);
} else
return (XAER_RMERR);
}
if ((ret = db_env_create(&dbenv, 0)) != 0) {
dbenv->err(dbenv, ret, DB_STR("4545",
"xa_open: Failure creating env handle"));
return (XAER_RMERR);
}
if ((ret = dbenv->set_thread_count(dbenv, 25)) != 0) {
dbenv->err(dbenv, ret, DB_STR("4546",
"xa_open: Failure setting thread count"));
goto err;
}
env = dbenv->env;
if ((ret = dbenv->open(dbenv, xa_info, XA_FLAGS, 0)) != 0) {
dbenv->err(dbenv, ret, DB_STR("4547",
"xa_open: Failure opening environment"));
goto err;
}
if ((ret = dbenv->log_get_config(dbenv,
DB_LOG_IN_MEMORY, &inmem)) != 0) {
dbenv->err(dbenv, ret, DB_STR("4548",
"xa_open: Failure getting log configuration"));
goto err;
}
if (inmem != 0) {
dbenv->err(dbenv, EINVAL, DB_STR("4549",
"xa_open: In-memory logging not allowed in XA environment"));
goto err;
}
__db_map_rmid(rmid, env);
env->xa_ref = 1;
if ((ret = __xa_thread_enter(env, &ip)) == 0) {
ENV_LEAVE(env, ip);
return (XA_OK);
} else
return (XAER_RMERR);
err: (void)dbenv->close(dbenv, 0);
if (ret == DB_RUNRECOVERY)
exit(1);
return (XAER_RMERR);
}
static int
__db_xa_close(xa_info, rmid, arg_flags)
char *xa_info;
int rmid;
long arg_flags;
{
DB_THREAD_INFO *ip;
ENV *env;
int ret, t_ret;
u_long flags;
COMPQUIET(xa_info, NULL);
COMPQUIET(ip, NULL);
ret = 0;
flags = (u_long)arg_flags;
if (LF_ISSET(TMASYNC))
return (XAER_ASYNC);
if (flags != TMNOFLAGS)
return (XAER_INVAL);
if (__db_rmid_to_env(rmid, &env) != 0)
return (XA_OK);
ENV_ENTER_RET(env, ip, ret);
if (ret == DB_RUNRECOVERY)
exit(1);
else if (ret != 0)
return (XAER_RMFAIL);
if (ip->dbth_xa_status == TXN_XA_THREAD_NOTA) {
ret = XAER_PROTO;
goto err;
}
if (SH_TAILQ_FIRST(&ip->dbth_xatxn, __db_txn) != NULL) {
ret = XAER_PROTO;
goto err;
}
if (env->xa_ref > 1) {
env->xa_ref--;
goto err;
} else {
ret = __db_unmap_rmid(rmid);
t_ret = env->dbenv->close(env->dbenv, 0);
if (ret != 0 || t_ret != 0)
ret = XAER_RMERR;
goto out;
}
err: ENV_LEAVE(env, ip);
out: return (ret == 0 ? XA_OK : ret);
}
static int
__db_xa_start(xid, rmid, arg_flags)
XID *xid;
int rmid;
long arg_flags;
{
DB_ENV *dbenv;
DB_TXN *txnp;
ENV *env;
TXN_DETAIL *td;
int ret;
u_long flags;
flags = (u_long)arg_flags;
ret = 0;
#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) != 0)
return (XAER_PROTO);
dbenv = env->dbenv;
PANIC_CHECK_RET(env, ret);
if (ret == DB_RUNRECOVERY)
exit(1);
if ((ret = __db_xid_to_txn(env, xid, &td)) != 0) {
dbenv->err(dbenv, ret, DB_STR("4550",
"xa_start: failure mapping xid"));
return (XAER_RMFAIL);
}
if (td != NULL) {
if (td->xa_br_status == TXN_XA_DEADLOCKED)
return (XA_RBDEADLOCK);
if (td->xa_br_status == TXN_XA_ROLLEDBACK)
return (XA_RBOTHER);
}
if ((ret = __xa_get_txn(env, xid, td, &txnp, flags, 0)) != 0)
return (ret);
return (XA_OK);
}
static int
__db_xa_end(xid, rmid, arg_flags)
XID *xid;
int rmid;
long arg_flags;
{
DB_ENV *dbenv;
DB_TXN *txn;
ENV *env;
TXN_DETAIL *td;
int ret;
u_long flags;
flags = (u_long)arg_flags;
if (flags != TMNOFLAGS && !LF_ISSET(TMSUSPEND | TMSUCCESS | TMFAIL))
return (XAER_INVAL);
if (__db_rmid_to_env(rmid, &env) != 0)
return (XAER_PROTO);
dbenv = env->dbenv;
if ((ret = __db_xid_to_txn(env, xid, &td)) != 0) {
dbenv->err(dbenv, ret, DB_STR("4551",
"xa_end: failure mapping xid"));
return (XAER_RMFAIL);
}
if (td == NULL)
return (XAER_NOTA);
if ((ret = __xa_get_txn(env, xid, td, &txn, flags, 1)) != 0)
return (ret);
if (txn->cursors != 0) {
dbenv->err(dbenv, EINVAL, DB_STR("4552",
"xa_end: cannot end with open cursors"));
return (XAER_RMERR);
}
if (td != txn->td) {
dbenv->err(dbenv, ret, DB_STR("4553",
"xa_end: txn_detail mismatch"));
return (XAER_RMERR);
}
if (td->xa_br_status == TXN_XA_DEADLOCKED)
return (XA_RBDEADLOCK);
if (td->status == TXN_NEED_ABORT) {
if (txn->abort(txn) != 0)
return (XAER_RMERR);
__xa_put_txn(env, txn);
return (XA_RBOTHER);
}
if (td->xa_br_status == TXN_XA_IDLE) {
dbenv->err(dbenv, EINVAL, DB_STR("4554",
"xa_end: ending transaction that is idle"));
return (XAER_PROTO);
}
if (td->xa_ref == 1 && td->xa_br_status == TXN_XA_ACTIVE)
td->xa_br_status = TXN_XA_IDLE;
if (LF_ISSET(TMSUSPEND)) {
txn->thread_info->dbth_xa_status = TXN_XA_THREAD_SUSPENDED;
txn->xa_thr_status = TXN_XA_THREAD_SUSPENDED;
} else {
__xa_put_txn(env, txn);
}
return (XA_OK);
}
static void
corrupted_env(env, rmid)
ENV *env;
int rmid;
{
DB_ENV *dbenv;
const char *path;
char *home;
int ret;
ENV *env2;
COMPQUIET(home, NULL);
ret = 0;
dbenv = env->dbenv;
path = NULL;
if (dbenv->get_home(dbenv, &path) != 0)
goto err;
if (path != NULL && (__os_strdup(NULL, path, &home) != 0))
goto err;
if (__db_rmid_to_env(rmid, &env2) == 0) {
PANIC_CHECK_RET(env2, ret);
if (ret != 0)
(void)__db_unmap_rmid(rmid);
}
if ( __db_xa_open(home, rmid, 0) != XA_OK)
goto err;
__os_free(NULL, home);
if (0) {
err: exit(1);
}
}
static int
__db_xa_prepare(xid, rmid, arg_flags)
XID *xid;
int rmid;
long arg_flags;
{
DB_ENV *dbenv;
DB_TXN *txnp;
ENV *env;
TXN_DETAIL *td;
int ret;
u_long flags;
flags = (u_long)arg_flags;
ret = 0;
if (LF_ISSET(TMASYNC))
return (XAER_ASYNC);
if (flags != TMNOFLAGS)
return (XAER_INVAL);
if (__db_rmid_to_env(rmid, &env) != 0)
return (XAER_PROTO);
dbenv = env->dbenv;
PANIC_CHECK_RET(env, ret);
if (ret == DB_RUNRECOVERY) {
corrupted_env(env, rmid);
if (__db_rmid_to_env(rmid, &env) != 0)
return (XAER_PROTO);
dbenv = env->dbenv;
}
if ((ret = __db_xid_to_txn(env, xid, &td)) != 0) {
dbenv->err(dbenv, ret, DB_STR("4555",
"xa_prepare: failure mapping xid"));
return (XAER_RMFAIL);
}
if (td == NULL) {
dbenv->err(dbenv, EINVAL, DB_STR("4556",
"xa_prepare: xid not found"));
return (XAER_NOTA);
}
if (td->xa_br_status == TXN_XA_DEADLOCKED)
return (XA_RBDEADLOCK);
if (td->xa_br_status == TXN_XA_ROLLEDBACK)
return (XA_RBOTHER);
if (td->xa_br_status != TXN_XA_ACTIVE &&
td->xa_br_status != TXN_XA_IDLE) {
dbenv->err(dbenv, EINVAL, DB_STR("4557",
"xa_prepare: transaction neither active nor idle"));
return (XAER_PROTO);
}
if ((ret = __xa_get_txn(env, xid, td, &txnp, TMJOIN, 0)) != 0)
return (ret);
if ((ret = txnp->prepare(txnp, (u_int8_t *)xid->data)) != 0) {
dbenv->err(dbenv, ret, DB_STR("4558",
"xa_prepare: txnp->prepare failed"));
td->xa_br_status = TXN_XA_IDLE;
return (XAER_RMERR);
}
td->xa_br_status = TXN_XA_PREPARED;
__xa_put_txn(env, txnp);
return (XA_OK);
}
static int
__db_xa_commit(xid, rmid, arg_flags)
XID *xid;
int rmid;
long arg_flags;
{
DB_ENV *dbenv;
DB_TXN *txnp;
ENV *env;
TXN_DETAIL *td;
int ret;
u_long flags;
flags = (u_long)arg_flags;
ret = 0;
if (LF_ISSET(TMASYNC))
return (XAER_ASYNC);
#undef OK_FLAGS
#define OK_FLAGS (TMNOFLAGS | TMNOWAIT | TMONEPHASE)
if (LF_ISSET(~OK_FLAGS))
return (XAER_INVAL);
if (__db_rmid_to_env(rmid, &env) != 0)
return (XAER_PROTO);
dbenv = env->dbenv;
PANIC_CHECK_RET(env, ret);
if (ret == DB_RUNRECOVERY) {
corrupted_env(env, rmid);
if (__db_rmid_to_env(rmid, &env) != 0)
return (XAER_PROTO);
dbenv = env->dbenv;
}
if ((ret = __db_xid_to_txn(env, xid, &td)) != 0) {
dbenv->err(dbenv, ret, DB_STR("4559",
"xa_commit: failure mapping xid"));
return (XAER_RMFAIL);
}
if (td == NULL) {
dbenv->err(dbenv, EINVAL, DB_STR("4560",
"xa_commit: xid not found"));
return (XAER_NOTA);
}
if (td->xa_br_status == TXN_XA_DEADLOCKED)
return (XA_RBDEADLOCK);
if (td->xa_br_status == TXN_XA_ROLLEDBACK)
return (XA_RBOTHER);
if (LF_ISSET(TMONEPHASE) && td->xa_br_status != TXN_XA_IDLE) {
dbenv->err(dbenv, EINVAL, DB_STR("4561",
"xa_commit: commiting transaction active in branch"));
return (XAER_PROTO);
}
if (!LF_ISSET(TMONEPHASE) && td->xa_br_status != TXN_XA_PREPARED) {
dbenv->err(dbenv, EINVAL, DB_STR("4562",
"xa_commit: attempting to commit unprepared transaction"));
return (XAER_PROTO);
}
if ((ret = __xa_get_txn(env, xid, td, &txnp, TMJOIN, 0)) != 0)
return (ret);
if ((ret = txnp->commit(txnp, 0)) != 0) {
dbenv->err(dbenv, ret, DB_STR("4563",
"xa_commit: txnp->commit failed"));
return (XAER_RMERR);
}
__xa_put_txn(env, txnp);
return (XA_OK);
}
static int
__db_xa_recover(xids, count, rmid, flags)
XID *xids;
long count, flags;
int rmid;
{
ENV *env;
int ret;
u_int32_t newflags;
long rval;
if (__db_rmid_to_env(rmid, &env) != 0)
return (XAER_PROTO);
if (LF_ISSET(TMSTARTRSCAN))
newflags = DB_FIRST;
else if (LF_ISSET(TMENDRSCAN))
newflags = DB_LAST;
else
newflags = DB_NEXT;
rval = 0;
if ((ret = __xa_txn_get_prepared(env,
xids, NULL, count, &rval, newflags)) != 0) {
env->dbenv->err(env->dbenv, ret, DB_STR("4564",
"xa_recover: txn_get_prepared failed"));
return (XAER_RMERR);
}
return (rval);
}
static int
__db_xa_rollback(xid, rmid, arg_flags)
XID *xid;
int rmid;
long arg_flags;
{
DB_ENV *dbenv;
DB_TXN *txnp;
ENV *env;
TXN_DETAIL *td;
int ret;
u_long flags;
flags = (u_long)arg_flags;
ret = 0;
if (LF_ISSET(TMASYNC))
return (XAER_ASYNC);
if (flags != TMNOFLAGS)
return (XAER_INVAL);
if (__db_rmid_to_env(rmid, &env) != 0)
return (XAER_PROTO);
dbenv = env->dbenv;
PANIC_CHECK_RET(env, ret);
if (ret == DB_RUNRECOVERY) {
corrupted_env(env, rmid);
if (__db_rmid_to_env(rmid, &env) != 0)
return (XAER_PROTO);
dbenv = env->dbenv;
}
if ((ret = __db_xid_to_txn(env, xid, &td)) != 0) {
dbenv->err(dbenv, ret, DB_STR("4565",
"xa_rollback: failure mapping xid"));
return (XAER_RMFAIL);
} if (td == NULL) {
dbenv->err(dbenv, ret, DB_STR("4566",
"xa_rollback: xid not found"));
return (XAER_NOTA);
}
if (td->xa_br_status == TXN_XA_DEADLOCKED)
return (XA_RBDEADLOCK);
if (td->xa_br_status == TXN_XA_ROLLEDBACK)
return (XA_RBOTHER);
if (td->xa_br_status != TXN_XA_ACTIVE &&
td->xa_br_status != TXN_XA_IDLE &&
td->xa_br_status != TXN_XA_PREPARED) {
dbenv->err(dbenv, EINVAL, DB_STR_A("4567",
"xa_rollback: transaction in invalid state %d",
"%d"), (int)td->xa_br_status);
return (XAER_PROTO);
}
if ((ret = __xa_get_txn(env, xid, td, &txnp, TMJOIN, 0)) != 0)
return (ret);
if ((ret = txnp->abort(txnp)) != 0) {
dbenv->err(dbenv, ret, DB_STR("4568",
"xa_rollback: failure aborting transaction"));
return (XAER_RMERR);
}
__xa_put_txn(env, txnp);
return (XA_OK);
}
static int
__db_xa_forget(xid, rmid, arg_flags)
XID *xid;
int rmid;
long arg_flags;
{
DB_ENV *dbenv;
DB_TXN *txnp;
ENV *env;
TXN_DETAIL *td;
int ret;
u_long flags;
flags = (u_long)arg_flags;
if (LF_ISSET(TMASYNC))
return (XAER_ASYNC);
if (flags != TMNOFLAGS)
return (XAER_INVAL);
if (__db_rmid_to_env(rmid, &env) != 0)
return (XAER_PROTO);
dbenv = env->dbenv;
if ((ret = __db_xid_to_txn(env, xid, &td)) != 0) {
dbenv->err(dbenv, ret, DB_STR("4569",
"xa_forget: failure mapping xid"));
return (XAER_RMFAIL);
}
if (td == NULL) {
dbenv->err(dbenv, ret, DB_STR("4570",
"xa_forget: xid not found"));
return (XA_OK);
}
if ((ret = __xa_get_txn(env, xid, td, &txnp, TMJOIN, 0)) != 0)
return (ret);
if ((ret = txnp->discard(txnp, 0)) != 0) {
dbenv->err(dbenv, ret, DB_STR("4571",
"xa_forget: txnp->discard failed"));
return (XAER_RMFAIL);
}
__xa_put_txn(env, txnp);
return (XA_OK);
}
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);
}