/*-
 * See the file LICENSE for redistribution information.
 *
 * Copyright (c) 1998,2007 Oracle.  All rights reserved.
 *
 * $Id: xa_db.c,v 12.10 2007/05/17 15:16:00 bostic Exp $
 */

#include "db_config.h"

#include "db_int.h"
#include "dbinc/txn.h"

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_get __P((DB *, DB_TXN *, DBT *, DBT *, u_int32_t));
static int __xa_open __P((DB *, DB_TXN *,
	    const char *, const char *, DBTYPE, u_int32_t, int));
static int __xa_put __P((DB *, DB_TXN *, DBT *, DBT *, u_int32_t));
static int __xa_set_txn __P((DB *, DB_TXN **, int));
static int __xa_truncate __P((DB *, DB_TXN *, u_int32_t *, u_int32_t));

typedef struct __xa_methods {
	int (*close) __P((DB *, u_int32_t));
	int (*cursor) __P((DB *, DB_TXN *, DBC **, u_int32_t));
	int (*del) __P((DB *, DB_TXN *, DBT *, u_int32_t));
	int (*get) __P((DB *, DB_TXN *, DBT *, DBT *, u_int32_t));
	int (*open) __P((DB *, DB_TXN *,
	    const char *, const char *, DBTYPE, u_int32_t, int));
	int (*put) __P((DB *, DB_TXN *, DBT *, DBT *, u_int32_t));
	int (*truncate) __P((DB *, DB_TXN *, u_int32_t *, u_int32_t));
} XA_METHODS;

/*
 * __xa_set_txn --
 *	Find a transaction handle.
 */
static int
__xa_set_txn(dbp, txnpp, no_xa_txn)
	DB *dbp;
	DB_TXN **txnpp;
	int no_xa_txn;
{
	DB_ENV *dbenv;
	int ret;

	dbenv = dbp->dbenv;

	/*
	 * It doesn't make sense for a server to specify a DB_TXN handle.
	 * As the server can't know if other operations it has done have
	 * committed/aborted, it can self-deadlock.  If the server wants
	 * other transactions, it can open other DB handles and use them.
	 * Disallow specified DB_TXN handles.
	 */
	if (*txnpp != NULL) {
		__db_errx(dbenv,
    "transaction handles should not be directly specified to XA interfaces");
		return (EINVAL);
	}

	/* See if the TM has declared a transaction. */
	if ((ret = __xa_get_txn(dbenv, txnpp, 0)) != 0)
		return (ret);
	if ((*txnpp)->txnid != TXN_INVALID)
		return (0);

	/*
	 * We may be opening databases in the server initialization routine.
	 * In that case, it's reasonable not to have an XA transaction.  It's
	 * also reasonable to open a database as part of an XA transaction,
	 * allow both.
	 */
	if (no_xa_txn) {
		*txnpp = NULL;
		return (0);
	}

	__db_errx(dbenv, "no XA transaction declared");
	return (EINVAL);
}

/*
 * __db_xa_create --
 *	DB XA constructor.
 *
 * PUBLIC: int __db_xa_create __P((DB *));
 */
int
__db_xa_create(dbp)
	DB *dbp;
{
	XA_METHODS *xam;
	int ret;

	/*
	 * Allocate the XA internal structure, and wrap the open and close
	 * calls.
	 */
	if ((ret = __os_calloc(dbp->dbenv, 1, sizeof(XA_METHODS), &xam)) != 0)
		return (ret);

	dbp->xa_internal = xam;
	xam->open = dbp->open;
	dbp->open = __xa_open;
	xam->close = dbp->close;
	dbp->close = __xa_close;

	return (0);
}

/*
 * __xa_open --
 *	XA open wrapper.
 */
static int
__xa_open(dbp, txn, name, subdb, type, flags, mode)
	DB *dbp;
	DB_TXN *txn;
	const char *name, *subdb;
	DBTYPE type;
	u_int32_t flags;
	int mode;
{
	XA_METHODS *xam;
	int ret;

	xam = (XA_METHODS *)dbp->xa_internal;

	if ((ret =
	    __xa_set_txn(dbp, &txn, LF_ISSET(DB_AUTO_COMMIT) ? 1 : 0)) != 0)
		return (ret);
	if ((ret = xam->open(dbp, txn, name, subdb, type, flags, mode)) != 0)
		return (ret);

	/* Wrap any DB handle method that takes a TXN ID as an argument. */
	xam->cursor = dbp->cursor;
	xam->del = dbp->del;
	xam->get = dbp->get;
	xam->put = dbp->put;
	xam->truncate = dbp->truncate;
	dbp->cursor = __xa_cursor;
	dbp->del = __xa_del;
	dbp->get = __xa_get;
	dbp->put = __xa_put;
	dbp->truncate = __xa_truncate;

	return (0);
}

static int
__xa_cursor(dbp, txn, dbcp, flags)
	DB *dbp;
	DB_TXN *txn;
	DBC **dbcp;
	u_int32_t flags;
{
	int ret;

	if ((ret = __xa_set_txn(dbp, &txn, 0)) != 0)
		return (ret);
	return (((XA_METHODS *)
	    dbp->xa_internal)->cursor(dbp, txn, dbcp, flags));
}

static int
__xa_del(dbp, txn, key, flags)
	DB *dbp;
	DB_TXN *txn;
	DBT *key;
	u_int32_t flags;
{
	int ret;

	if ((ret = __xa_set_txn(dbp, &txn, 0)) != 0)
		return (ret);
	return (((XA_METHODS *)dbp->xa_internal)->del(dbp, txn, key, flags));
}

static int
__xa_close(dbp, flags)
	DB *dbp;
	u_int32_t flags;
{
	int (*real_close) __P((DB *, u_int32_t));

	real_close = ((XA_METHODS *)dbp->xa_internal)->close;

	__os_free(dbp->dbenv, dbp->xa_internal);
	dbp->xa_internal = NULL;

	return (real_close(dbp, flags));
}

static int
__xa_get(dbp, txn, key, data, flags)
	DB *dbp;
	DB_TXN *txn;
	DBT *key, *data;
	u_int32_t flags;
{
	int ret;

	if ((ret = __xa_set_txn(dbp, &txn, 0)) != 0)
		return (ret);
	return (((XA_METHODS *)
	    dbp->xa_internal)->get(dbp, txn, key, data, flags));
}

static int
__xa_put(dbp, txn, key, data, flags)
	DB *dbp;
	DB_TXN *txn;
	DBT *key, *data;
	u_int32_t flags;
{
	int ret;

	if ((ret = __xa_set_txn(dbp, &txn, 0)) != 0)
		return (ret);
	return (((XA_METHODS *)
	    dbp->xa_internal)->put(dbp, txn, key, data, flags));
}

static int
__xa_truncate(dbp, txn, countp, flags)
	DB *dbp;
	DB_TXN *txn;
	u_int32_t *countp, flags;
{
	int ret;

	if ((ret = __xa_set_txn(dbp, &txn, 0)) != 0)
		return (ret);
	return (((XA_METHODS *)
	    dbp->xa_internal)->truncate(dbp, txn, countp, flags));
}
