[asterisk-commits] tilghman: branch tilghman/odbc_tx_support r116169 - in /team/tilghman/odbc_tx...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Tue May 13 22:55:56 CDT 2008
Author: tilghman
Date: Tue May 13 22:55:56 2008
New Revision: 116169
URL: http://svn.digium.com/view/asterisk?view=rev&rev=116169
Log:
Still working on this, but committing today's work
Modified:
team/tilghman/odbc_tx_support/funcs/func_odbc.c
team/tilghman/odbc_tx_support/include/asterisk/res_odbc.h
team/tilghman/odbc_tx_support/res/res_odbc.c
Modified: team/tilghman/odbc_tx_support/funcs/func_odbc.c
URL: http://svn.digium.com/view/asterisk/team/tilghman/odbc_tx_support/funcs/func_odbc.c?view=diff&rev=116169&r1=116168&r2=116169
==============================================================================
--- team/tilghman/odbc_tx_support/funcs/func_odbc.c (original)
+++ team/tilghman/odbc_tx_support/funcs/func_odbc.c Tue May 13 22:55:56 2008
@@ -2,6 +2,7 @@
* Asterisk -- An open source telephony toolkit.
*
* Copyright (c) 2005, 2006 Tilghman Lesher
+ * Copyright (c) 2008 Digium, Inc.
*
* Tilghman Lesher <func_odbc__200508 at the-tilghman.com>
*
@@ -63,6 +64,7 @@
};
static void odbc_datastore_free(void *data);
+static void odbc_txn_free(void *data);
struct ast_datastore_info odbc_info = {
.type = "FUNC_ODBC",
@@ -80,6 +82,108 @@
AST_LIST_HEAD(, odbc_datastore_row);
char names[0];
};
+
+static struct ast_datastore_info txn_info = {
+ .type = "FUNC_ODBC_Transaction",
+ .destroy = odbc_txn_free,
+};
+
+struct odbc_txn_frame {
+ AST_LIST_ENTRY(odbc_txn_frame) list;
+ enum { ODBC_STATUS_TXCLOSED = 0, ODBC_STATUS_TXOPEN = 1 } status; /*!< Status of this transaction */
+ struct odbc_obj *obj; /*!< Database handle within which transacted statements are run */
+ unsigned int active:1; /*!< Is this record the current active transaction within the channel? */
+ char transaction_id[0]; /*!< Name of this transaction ID */
+};
+
+static struct odbc_txn_frame *find_transaction(struct ast_channel *chan, struct odbc_obj *obj, const char *name, int active)
+{
+ struct ast_datastore *txn_store = ast_channel_datastore_find(chan, &odbc_info, NULL);
+ AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
+ struct odbc_txn_frame *txn;
+
+ if (txn_store) {
+ oldlist = txn_store->data;
+ } else {
+ /* Need to create a new datastore */
+ if (!(txn_store = ast_channel_datastore_alloc(&odbc_info, NULL))) {
+ ast_log(LOG_ERROR, "Unable to allocate a new datastore. Cannot create a new transaction.\n");
+ return NULL;
+ }
+
+ if (!(oldlist = ast_calloc(1, sizeof(*oldlist)))) {
+ ast_log(LOG_ERROR, "Unable to allocate datastore list head. Cannot create a new transaction.\n");
+ ast_channel_datastore_free(txn_store);
+ return NULL;
+ }
+
+ txn_store->data = oldlist;
+ AST_LIST_HEAD_INIT(oldlist);
+ ast_channel_datastore_add(chan, txn_store);
+ }
+
+ AST_LIST_LOCK(oldlist);
+
+ /* Scanning for an object is *fast*. Scanning for a name is much slower. */
+ if (obj != NULL || active == 1) {
+ AST_LIST_TRAVERSE(oldlist, txn, list) {
+ if (txn->obj == obj || txn->active) {
+ AST_LIST_UNLOCK(oldlist);
+ return txn;
+ }
+ }
+ }
+
+ if (name != NULL) {
+ AST_LIST_TRAVERSE(oldlist, txn, list) {
+ if (!strcasecmp(txn->name, name)) {
+ AST_LIST_UNLOCK(oldlist);
+ return txn;
+ }
+ }
+ }
+
+ /* Nothing found, create one */
+ if (name && obj && (txn = ast_calloc(sizeof(*txn) + strlen(name) + 1))) {
+ struct odbc_txn_frame *otxn;
+
+ strcpy(txn->name, name); /* SAFE */
+ txn->obj = obj;
+
+ /* On creation, the txn becomes active, and all others inactive */
+ AST_LIST_TRAVERSE(oldlist, otxn, list) {
+ otxn->active = 0;
+ }
+ AST_LIST_INSERT_TAIL(oldlist, txn, list);
+ }
+ AST_LIST_UNLOCK(oldlist);
+
+ return NULL;
+}
+
+static int mark_transaction_active(struct ast_channel *chan, struct odbj_obj *obj)
+{
+ struct ast_datastore *txn_store = ast_channel_datastore_find(chan, &odbc_info, NULL);
+ AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
+ struct odbc_txn_frame *active, *txn;
+
+ if (!txn_store) {
+ return -1;
+ }
+
+ oldlist = txn_store->data;
+ AST_LIST_LOCK(oldlist);
+ AST_LIST_TRAVERSE(oldlist, txn, list) {
+ if (txn->obj == obj) {
+ txn->active = 1;
+ active = txn;
+ } else {
+ txn->active = 0;
+ }
+ }
+ AST_LIST_UNLOCK(oldlist);
+ return active ? 0 : -1;
+}
AST_LIST_HEAD_STATIC(queries, acf_odbc_query);
@@ -532,6 +636,31 @@
"SELECT foo FROM bar WHERE baz='${SQL_ESC(${ARG1})}'\n",
.read = acf_escape,
.write = NULL,
+};
+
+static struct ast_custom_function odbc_function = {
+ .name = "ODBC",
+ .synopsis = "Sets/retrieves properties supporting database transactions",
+ .syntax = "ODBC(<property>[,<argument>])",
+ .desc =
+"The ODBC() function allows setting several properties to influence how a\n"
+"connected database processes transactions. The possible properties are as\n"
+"follows:\n"
+" transaction - gets or sets the active transaction ID. If set, and the\n"
+" transaction ID does not exist and a database name is specified\n"
+" as an argument, it will be created.\n"
+" autocommit - controls whether a transaction will be automatically committed\n"
+" when the channel hangs up. Defaults to 0. If a transaction\n"
+" ID is specified in the optional argument, the property will be\n"
+" applied to that ID, otherwise to the current active ID.\n"
+" isolation - controls the data isolation on uncommitted transactions. May\n"
+" be one of the following: read_committed, read_uncommitted,\n"
+" repeatable_read, or serializable. Defaults to the database\n"
+" setting in res_odbc.conf or read_committed if not specified.\n"
+" If a transaction ID is specified as an optional argument, it\n"
+" will be applied to that ID, otherwise the current active ID.\n",
+ .read = acf_transaction_read,
+ .write = acf_transaction_write,
};
static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Modified: team/tilghman/odbc_tx_support/include/asterisk/res_odbc.h
URL: http://svn.digium.com/view/asterisk/team/tilghman/odbc_tx_support/include/asterisk/res_odbc.h?view=diff&rev=116169&r1=116168&r2=116169
==============================================================================
--- team/tilghman/odbc_tx_support/include/asterisk/res_odbc.h (original)
+++ team/tilghman/odbc_tx_support/include/asterisk/res_odbc.h Tue May 13 22:55:56 2008
@@ -41,11 +41,13 @@
/*! \brief ODBC container */
struct odbc_obj {
ast_mutex_t lock;
- SQLHDBC con; /* ODBC Connection Handle */
- struct odbc_class *parent; /* Information about the connection is protected */
- struct timeval last_used;
- unsigned int used:1;
+ SQLHDBC con; /*!< ODBC Connection Handle */
+ struct odbc_class *parent; /*!< Information about the connection is protected */
+ struct timeval last_used; /*!< Used by idlecheck to determine if the connection should be renegotiated */
+ unsigned int used:1; /*!< Is this connection currently in use? */
unsigned int up:1;
+ unsigned int tx:1; /*!< Should this connection be unshared, regardless of the class setting? */
+ unsigned int isolation:2; /*!< The isolation state of this handle, if it differs from the class */
AST_LIST_ENTRY(odbc_obj) list;
};
Modified: team/tilghman/odbc_tx_support/res/res_odbc.c
URL: http://svn.digium.com/view/asterisk/team/tilghman/odbc_tx_support/res/res_odbc.c?view=diff&rev=116169&r1=116168&r2=116169
==============================================================================
--- team/tilghman/odbc_tx_support/res/res_odbc.c (original)
+++ team/tilghman/odbc_tx_support/res/res_odbc.c Tue May 13 22:55:56 2008
@@ -58,12 +58,14 @@
char *password;
char *sanitysql;
SQLHENV env;
- unsigned int haspool:1; /* Boolean - TDS databases need this */
- unsigned int limit:10; /* Gives a limit of 1023 maximum */
- unsigned int count:10; /* Running count of pooled connections */
- unsigned int delme:1; /* Purge the class */
- unsigned int backslash_is_escape:1; /* On this database, the backslash is a native escape sequence */
- unsigned int idlecheck; /* Recheck the connection if it is idle for this long */
+ unsigned int haspool:1; /*!< Boolean - TDS databases need this */
+ unsigned int limit:10; /*!< Gives a limit of 1023 maximum */
+ unsigned int count:10; /*!< Running count of pooled connections */
+ unsigned int delme:1; /*!< Purge the class */
+ unsigned int backslash_is_escape:1; /*!< On this database, the backslash is a native escape sequence */
+ unsigned int commit_uncommitted:1; /*!< Should uncommitted transactions be auto-committed on handle release? */
+ unsigned int isolation:2; /*!< Flags for how the DB should deal with data in uncommitted transactions */
+ unsigned int idlecheck; /*!< Recheck the connection if it is idle for this long */
struct ao2_container *obj_container;
};
@@ -245,7 +247,7 @@
struct ast_variable *v;
char *cat;
const char *dsn, *username, *password, *sanitysql;
- int enabled, pooling, limit, bse;
+ int enabled, pooling, limit, bse, commit_uncommitted, isolation;
unsigned int idlecheck;
int connect = 0, res = 0;
struct ast_flags config_flags = { 0 };
@@ -271,6 +273,7 @@
pooling = 0;
limit = 0;
bse = 1;
+ commit_uncommitted = isolation = 0;
for (v = ast_variable_browse(config, cat); v; v = v->next) {
if (!strcasecmp(v->name, "pooling")) {
if (ast_true(v->value))
@@ -305,6 +308,20 @@
sanitysql = v->value;
} else if (!strcasecmp(v->name, "backslash_is_escape")) {
bse = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "commit_uncommitted")) {
+ commit_uncommitted = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "isolation")) {
+ if (!strcasecmp(v->value, "read_committed")) {
+ isolation = 0;
+ } else if (!strcasecmp(v->value, "read_uncommitted")) {
+ isolation = 1;
+ } else if (!strcasecmp(v->value, "repeatable_read")) {
+ isolation = 2;
+ } else if (!strcasecmp(v->value, "serializable")) {
+ isolation = 3;
+ } else {
+ ast_log(LOG_ERROR, "Unrecognized value for 'isolation': '%s' in section '%s'\n", v->value, cat);
+ }
}
}
@@ -338,6 +355,8 @@
}
new->backslash_is_escape = bse ? 1 : 0;
+ new->commit_uncommitted = commit_uncommitted ? 1 : 0;
+ new->isolation = isolation;
new->idlecheck = idlecheck;
if (cat)
@@ -465,6 +484,11 @@
void ast_odbc_release_obj(struct odbc_obj *obj)
{
+ if (odbc->tx) {
+ /* TODO Find the datastore with this handle and check if the transaction is uncommitted */
+ SQLEndTrans(SQL_HANDLE_DBC, obj->con, obj->parent->commit_uncommitted ? SQL_COMMIT : SQL_ROLLBACK);
+ }
+
/* For pooled connections, this frees the connection to be
* reused. For non-pooled connections, it does nothing. */
obj->used = 0;
More information about the asterisk-commits
mailing list