[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