Tue Aug 20 16:35:14 2013

Asterisk developer's documentation


res_odbc.c File Reference

ODBC resource manager. More...

#include "asterisk.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/config.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/cli.h"
#include "asterisk/lock.h"
#include "asterisk/res_odbc.h"
#include "asterisk/time.h"
#include "asterisk/astobj2.h"
#include "asterisk/app.h"
#include "asterisk/strings.h"
#include "asterisk/threadstorage.h"
#include "asterisk/data.h"

Go to the source code of this file.

Data Structures

struct  odbc_class
struct  odbc_tables
struct  odbc_txn_frame

Defines

#define DATA_EXPORT_ODBC_CLASS(MEMBER)
#define EOR_TX   (void *)(long)3
#define NO_TX   (void *)(long)2
#define USE_TX   (void *)(long)1

Functions

static void __init_errors_buf (void)
static void __reg_module (void)
static void __unreg_module (void)
struct odbc_obj_ast_odbc_request_obj (const char *name, int check, const char *file, const char *function, int lineno)
struct odbc_obj_ast_odbc_request_obj2 (const char *name, struct ast_flags flags, const char *file, const char *function, int lineno)
 Retrieves a connected ODBC object.
static int acf_transaction_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int acf_transaction_write (struct ast_channel *chan, const char *cmd, char *s, const char *value)
static int aoro2_class_cb (void *obj, void *arg, int flags)
static int aoro2_obj_cb (void *vobj, void *arg, int flags)
static int aoro2_obj_notx_cb (void *vobj, void *arg, int flags)
 AST_DATA_STRUCTURE (odbc_class, DATA_EXPORT_ODBC_CLASS)
SQLRETURN ast_odbc_ast_str_SQLGetData (struct ast_str **buf, int pmaxlen, SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, SQLLEN *StrLen_or_Ind)
 Wrapper for SQLGetData to use with dynamic strings.
int ast_odbc_backslash_is_escape (struct odbc_obj *obj)
 Checks if the database natively supports backslash as an escape character.
int ast_odbc_clear_cache (const char *database, const char *tablename)
 Remove a cache entry from memory This function may be called to clear entries created and cached by the ast_odbc_find_table() API call.
SQLHSTMT ast_odbc_direct_execute (struct odbc_obj *obj, SQLHSTMT(*exec_cb)(struct odbc_obj *obj, void *data), void *data)
 Executes an non prepared statement and returns the resulting statement handle.
struct odbc_cache_columnsast_odbc_find_column (struct odbc_cache_tables *table, const char *colname)
 Find a column entry within a cached table structure.
struct odbc_cache_tablesast_odbc_find_table (const char *database, const char *tablename)
 Find or create an entry describing the table specified.
SQLHSTMT ast_odbc_prepare_and_execute (struct odbc_obj *obj, SQLHSTMT(*prepare_cb)(struct odbc_obj *obj, void *data), void *data)
 Prepares, executes, and returns the resulting statement handle.
void ast_odbc_release_obj (struct odbc_obj *obj)
 Releases an ODBC object previously allocated by ast_odbc_request_obj().
struct odbc_objast_odbc_retrieve_transaction_obj (struct ast_channel *chan, const char *objname)
 Retrieve a stored ODBC object, if a transaction has been started.
int ast_odbc_sanity_check (struct odbc_obj *obj)
 Checks an ODBC object to ensure it is still connected.
int ast_odbc_smart_execute (struct odbc_obj *obj, SQLHSTMT stmt)
 Executes a prepared statement handle.
static int commit_exec (struct ast_channel *chan, const char *data)
static int data_odbc_provider_handler (const struct ast_data_search *search, struct ast_data *root)
static void destroy_table_cache (struct odbc_cache_tables *table)
static struct odbc_txn_framefind_transaction (struct ast_channel *chan, struct odbc_obj *obj, const char *name, int active)
static char * handle_cli_odbc_show (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static const char * isolation2text (int iso)
static int load_module (void)
static int load_odbc_config (void)
static int mark_transaction_active (struct ast_channel *chan, struct odbc_txn_frame *tx)
static int null_hash_fn (const void *obj, const int flags)
static void odbc_class_destructor (void *data)
static odbc_status odbc_obj_connect (struct odbc_obj *obj)
static void odbc_obj_destructor (void *data)
static odbc_status odbc_obj_disconnect (struct odbc_obj *obj)
static int odbc_register_class (struct odbc_class *class, int connect)
static void odbc_release_obj2 (struct odbc_obj *obj, struct odbc_txn_frame *tx)
static void odbc_txn_free (void *data)
static struct odbc_txn_framerelease_transaction (struct odbc_txn_frame *tx)
static int reload (void)
static int rollback_exec (struct ast_channel *chan, const char *data)
static int text2isolation (const char *txt)
static int unload_module (void)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER , .description = "ODBC resource" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_REALTIME_DEPEND, }
static const char *const app_commit = "ODBC_Commit"
static const char *const app_rollback = "ODBC_Rollback"
static struct ast_module_infoast_module_info = &__mod_info
static struct ao2_containerclass_container
static struct ast_cli_entry cli_odbc []
static struct ast_threadstorage errors_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_errors_buf , .custom_init = NULL , }
static struct ast_custom_function odbc_function
static struct ast_data_handler odbc_provider
static struct ast_data_entry odbc_providers []
static struct ast_datastore_info txn_info

Detailed Description

ODBC resource manager.

Author:
Mark Spencer <markster@digium.com>
Anthony Minessale II <anthmct@yahoo.com>
Tilghman Lesher <tilghman@digium.com>

Definition in file res_odbc.c.


Define Documentation

#define DATA_EXPORT_ODBC_CLASS ( MEMBER   ) 

Definition at line 178 of file res_odbc.c.

#define EOR_TX   (void *)(long)3

Definition at line 1204 of file res_odbc.c.

Referenced by _ast_odbc_request_obj2(), and aoro2_obj_cb().

#define NO_TX   (void *)(long)2

Definition at line 1203 of file res_odbc.c.

Referenced by _ast_odbc_request_obj2(), and aoro2_obj_cb().

#define USE_TX   (void *)(long)1

Definition at line 1202 of file res_odbc.c.

Referenced by _ast_odbc_request_obj2(), and aoro2_obj_cb().


Function Documentation

static void __init_errors_buf ( void   )  [static]

Definition at line 154 of file res_odbc.c.

00156 {

static void __reg_module ( void   )  [static]

Definition at line 1902 of file res_odbc.c.

static void __unreg_module ( void   )  [static]

Definition at line 1902 of file res_odbc.c.

struct odbc_obj* _ast_odbc_request_obj ( const char *  name,
int  check,
const char *  file,
const char *  function,
int  lineno 
) [read]

Definition at line 1440 of file res_odbc.c.

References _ast_odbc_request_obj2(), and RES_ODBC_SANITY_CHECK.

01441 {
01442    struct ast_flags flags = { check ? RES_ODBC_SANITY_CHECK : 0 };
01443    return _ast_odbc_request_obj2(name, flags, file, function, lineno);
01444 }

struct odbc_obj* _ast_odbc_request_obj2 ( const char *  name,
struct ast_flags  flags,
const char *  file,
const char *  function,
int  lineno 
) [read]

Retrieves a connected ODBC object.

Parameters:
name The name of the ODBC class for which a connection is needed.
flags One or more of the following flags:

  • RES_ODBC_SANITY_CHECK Whether to ensure that a connection is valid before returning the handle. Usually unnecessary.
  • RES_ODBC_INDEPENDENT_CONNECTION Return a handle which is independent from all others. Usually used when starting a transaction.
  • RES_ODBC_CONNECTED Only return a connected handle. Intended for use with peers which use idlecheck, which are checked periodically for reachability.
Returns:
ODBC object
Return values:
NULL if there is no connection available with the requested name.

Connection classes may, in fact, contain multiple connection handles. If the connection is pooled, then each connection will be dedicated to the thread which requests it. Note that all connections should be released when the thread is done by calling ast_odbc_release_obj(), below.

Definition at line 1231 of file res_odbc.c.

References ao2_alloc, ao2_callback, ao2_link, ao2_ref, aoro2_class_cb(), aoro2_obj_cb(), aoro2_obj_notx_cb(), ast_assert, ast_atomic_fetchadd_int(), ast_copy_string(), ast_debug, ast_log(), ast_mutex_init, ast_mutex_lock, ast_mutex_unlock, ast_odbc_sanity_check(), ast_test_flag, ast_tvdiff_sec(), ast_tvnow(), class_container, odbc_obj::con, odbc_class::count, EOR_TX, odbc_class::idlecheck, odbc_class::isolation, odbc_class::last_negative_connect, odbc_obj::last_used, odbc_obj::lock, LOG_WARNING, odbc_class::negative_connection_cache, NO_TX, odbc_class::obj_container, ODBC_FAIL, odbc_obj_connect(), odbc_obj_destructor(), odbc_obj::parent, RES_ODBC_CONNECTED, RES_ODBC_INDEPENDENT_CONNECTION, RES_ODBC_SANITY_CHECK, odbc_obj::up, USE_TX, and odbc_obj::used.

Referenced by _ast_odbc_request_obj().

01232 {
01233    struct odbc_obj *obj = NULL;
01234    struct odbc_class *class;
01235    SQLINTEGER nativeerror=0, numfields=0;
01236    SQLSMALLINT diagbytes=0, i;
01237    unsigned char state[10], diagnostic[256];
01238 
01239    if (!(class = ao2_callback(class_container, 0, aoro2_class_cb, (char *) name))) {
01240       ast_debug(1, "Class '%s' not found!\n", name);
01241       return NULL;
01242    }
01243 
01244    ast_assert(ao2_ref(class, 0) > 1);
01245 
01246    if (class->haspool) {
01247       /* Recycle connections before building another */
01248       obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, EOR_TX);
01249 
01250       if (obj) {
01251          ast_assert(ao2_ref(obj, 0) > 1);
01252       }
01253       if (!obj && (ast_atomic_fetchadd_int(&class->count, +1) < class->limit) &&
01254             (time(NULL) > class->last_negative_connect.tv_sec + class->negative_connection_cache.tv_sec)) {
01255          obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
01256          if (!obj) {
01257             class->count--;
01258             ao2_ref(class, -1);
01259             ast_debug(3, "Unable to allocate object\n");
01260             ast_atomic_fetchadd_int(&class->count, -1);
01261             return NULL;
01262          }
01263          ast_assert(ao2_ref(obj, 0) == 1);
01264          ast_mutex_init(&obj->lock);
01265          /* obj inherits the outstanding reference to class */
01266          obj->parent = class;
01267          class = NULL;
01268          if (odbc_obj_connect(obj) == ODBC_FAIL) {
01269             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
01270             ast_assert(ao2_ref(obj->parent, 0) > 0);
01271             /* Because it was never within the container, we have to manually decrement the count here */
01272             ast_atomic_fetchadd_int(&obj->parent->count, -1);
01273             ao2_ref(obj, -1);
01274             obj = NULL;
01275          } else {
01276             obj->used = 1;
01277             ao2_link(obj->parent->obj_container, obj);
01278          }
01279       } else {
01280          /* If construction fails due to the limit (or negative timecache), reverse our increment. */
01281          if (!obj) {
01282             ast_atomic_fetchadd_int(&class->count, -1);
01283          }
01284          /* Object is not constructed, so delete outstanding reference to class. */
01285          ao2_ref(class, -1);
01286          class = NULL;
01287       }
01288 
01289       if (!obj) {
01290          return NULL;
01291       }
01292 
01293       ast_mutex_lock(&obj->lock);
01294 
01295       if (ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) {
01296          /* Ensure this connection has autocommit turned off. */
01297          if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) {
01298             SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01299             for (i = 0; i < numfields; i++) {
01300                SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01301                ast_log(LOG_WARNING, "SQLSetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01302                if (i > 10) {
01303                   ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01304                   break;
01305                }
01306             }
01307          }
01308       }
01309    } else if (ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) {
01310       /* Non-pooled connections -- but must use a separate connection handle */
01311       if (!(obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, USE_TX))) {
01312          ast_debug(1, "Object not found\n");
01313          obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
01314          if (!obj) {
01315             ao2_ref(class, -1);
01316             ast_debug(3, "Unable to allocate object\n");
01317             return NULL;
01318          }
01319          ast_mutex_init(&obj->lock);
01320          /* obj inherits the outstanding reference to class */
01321          obj->parent = class;
01322          class = NULL;
01323          if (odbc_obj_connect(obj) == ODBC_FAIL) {
01324             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
01325             ao2_ref(obj, -1);
01326             obj = NULL;
01327          } else {
01328             obj->used = 1;
01329             ao2_link(obj->parent->obj_container, obj);
01330             ast_atomic_fetchadd_int(&obj->parent->count, +1);
01331          }
01332       }
01333 
01334       if (!obj) {
01335          return NULL;
01336       }
01337 
01338       ast_mutex_lock(&obj->lock);
01339 
01340       if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) {
01341          SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01342          for (i = 0; i < numfields; i++) {
01343             SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01344             ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01345             if (i > 10) {
01346                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01347                break;
01348             }
01349          }
01350       }
01351    } else {
01352       /* Non-pooled connection: multiple modules can use the same connection. */
01353       if ((obj = ao2_callback(class->obj_container, 0, aoro2_obj_notx_cb, NO_TX))) {
01354          /* Object is not constructed, so delete outstanding reference to class. */
01355          ast_assert(ao2_ref(class, 0) > 1);
01356          ao2_ref(class, -1);
01357          class = NULL;
01358       } else {
01359          /* No entry: build one */
01360          if (!(obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor))) {
01361             ast_assert(ao2_ref(class, 0) > 1);
01362             ao2_ref(class, -1);
01363             ast_debug(3, "Unable to allocate object\n");
01364             return NULL;
01365          }
01366          ast_mutex_init(&obj->lock);
01367          /* obj inherits the outstanding reference to class */
01368          obj->parent = class;
01369          class = NULL;
01370          if (odbc_obj_connect(obj) == ODBC_FAIL) {
01371             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
01372             ao2_ref(obj, -1);
01373             obj = NULL;
01374          } else {
01375             ao2_link(obj->parent->obj_container, obj);
01376             ast_assert(ao2_ref(obj, 0) > 1);
01377          }
01378       }
01379 
01380       if (!obj) {
01381          return NULL;
01382       }
01383 
01384       ast_mutex_lock(&obj->lock);
01385 
01386       if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) {
01387          SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01388          for (i = 0; i < numfields; i++) {
01389             SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01390             ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01391             if (i > 10) {
01392                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01393                break;
01394             }
01395          }
01396       }
01397    }
01398 
01399    ast_assert(obj != NULL);
01400 
01401    /* Set the isolation property */
01402    if (SQLSetConnectAttr(obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)obj->parent->isolation, 0) == SQL_ERROR) {
01403       SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01404       for (i = 0; i < numfields; i++) {
01405          SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01406          ast_log(LOG_WARNING, "SetConnectAttr (Txn isolation) returned an error: %s: %s\n", state, diagnostic);
01407          if (i > 10) {
01408             ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01409             break;
01410          }
01411       }
01412    }
01413 
01414    if (ast_test_flag(&flags, RES_ODBC_CONNECTED) && !obj->up) {
01415       /* Check if this connection qualifies for reconnection, with negative connection cache time */
01416       if (time(NULL) > obj->parent->last_negative_connect.tv_sec + obj->parent->negative_connection_cache.tv_sec) {
01417          odbc_obj_connect(obj);
01418       }
01419    } else if (ast_test_flag(&flags, RES_ODBC_SANITY_CHECK)) {
01420       ast_odbc_sanity_check(obj);
01421    } else if (obj->parent->idlecheck > 0 && ast_tvdiff_sec(ast_tvnow(), obj->last_used) > obj->parent->idlecheck) {
01422       odbc_obj_connect(obj);
01423    }
01424 
01425 #ifdef DEBUG_THREADS
01426    ast_copy_string(obj->file, file, sizeof(obj->file));
01427    ast_copy_string(obj->function, function, sizeof(obj->function));
01428    obj->lineno = lineno;
01429 #endif
01430 
01431    /* We had it locked because of the obj_connects we see here. */
01432    ast_mutex_unlock(&obj->lock);
01433 
01434    ast_assert(class == NULL);
01435 
01436    ast_assert(ao2_ref(obj, 0) > 1);
01437    return obj;
01438 }

static int acf_transaction_read ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 1572 of file res_odbc.c.

References args, AST_APP_ARG, ast_copy_string(), AST_DECLARE_APP_ARGS, AST_STANDARD_APP_ARGS, ast_strlen_zero(), find_transaction(), odbc_txn_frame::forcecommit, odbc_txn_frame::isolation, isolation2text(), and odbc_txn_frame::name.

01573 {
01574    AST_DECLARE_APP_ARGS(args,
01575       AST_APP_ARG(property);
01576       AST_APP_ARG(opt);
01577    );
01578    struct odbc_txn_frame *tx;
01579 
01580    AST_STANDARD_APP_ARGS(args, data);
01581    if (strcasecmp(args.property, "transaction") == 0) {
01582       if ((tx = find_transaction(chan, NULL, NULL, 1))) {
01583          ast_copy_string(buf, tx->name, len);
01584          return 0;
01585       }
01586    } else if (strcasecmp(args.property, "isolation") == 0) {
01587       if (!ast_strlen_zero(args.opt)) {
01588          tx = find_transaction(chan, NULL, args.opt, 0);
01589       } else {
01590          tx = find_transaction(chan, NULL, NULL, 1);
01591       }
01592       if (tx) {
01593          ast_copy_string(buf, isolation2text(tx->isolation), len);
01594          return 0;
01595       }
01596    } else if (strcasecmp(args.property, "forcecommit") == 0) {
01597       if (!ast_strlen_zero(args.opt)) {
01598          tx = find_transaction(chan, NULL, args.opt, 0);
01599       } else {
01600          tx = find_transaction(chan, NULL, NULL, 1);
01601       }
01602       if (tx) {
01603          ast_copy_string(buf, tx->forcecommit ? "1" : "0", len);
01604          return 0;
01605       }
01606    }
01607    return -1;
01608 }

static int acf_transaction_write ( struct ast_channel chan,
const char *  cmd,
char *  s,
const char *  value 
) [static]

Definition at line 1610 of file res_odbc.c.

References args, AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_false(), ast_log(), ast_odbc_request_obj2, AST_STANDARD_APP_ARGS, ast_strlen_zero(), ast_true(), odbc_obj::con, find_transaction(), odbc_txn_frame::forcecommit, odbc_txn_frame::isolation, LOG_ERROR, LOG_WARNING, mark_transaction_active(), odbc_txn_frame::obj, pbx_builtin_setvar_helper(), RES_ODBC_INDEPENDENT_CONNECTION, S_OR, text2isolation(), and odbc_obj::tx.

01611 {
01612    AST_DECLARE_APP_ARGS(args,
01613       AST_APP_ARG(property);
01614       AST_APP_ARG(opt);
01615    );
01616    struct odbc_txn_frame *tx;
01617    SQLINTEGER nativeerror=0, numfields=0;
01618    SQLSMALLINT diagbytes=0, i;
01619    unsigned char state[10], diagnostic[256];
01620 
01621    AST_STANDARD_APP_ARGS(args, s);
01622    if (strcasecmp(args.property, "transaction") == 0) {
01623       /* Set active transaction */
01624       struct odbc_obj *obj;
01625       if ((tx = find_transaction(chan, NULL, value, 0))) {
01626          mark_transaction_active(chan, tx);
01627       } else {
01628          /* No such transaction, create one */
01629          struct ast_flags flags = { RES_ODBC_INDEPENDENT_CONNECTION };
01630          if (ast_strlen_zero(args.opt) || !(obj = ast_odbc_request_obj2(args.opt, flags))) {
01631             ast_log(LOG_ERROR, "Could not create transaction: invalid database specification '%s'\n", S_OR(args.opt, ""));
01632             pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_DB");
01633             return -1;
01634          }
01635          if (!find_transaction(chan, obj, value, 0)) {
01636             pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
01637             return -1;
01638          }
01639          obj->tx = 1;
01640       }
01641       pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
01642       return 0;
01643    } else if (strcasecmp(args.property, "forcecommit") == 0) {
01644       /* Set what happens when an uncommitted transaction ends without explicit Commit or Rollback */
01645       if (ast_strlen_zero(args.opt)) {
01646          tx = find_transaction(chan, NULL, NULL, 1);
01647       } else {
01648          tx = find_transaction(chan, NULL, args.opt, 0);
01649       }
01650       if (!tx) {
01651          pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
01652          return -1;
01653       }
01654       if (ast_true(value)) {
01655          tx->forcecommit = 1;
01656       } else if (ast_false(value)) {
01657          tx->forcecommit = 0;
01658       } else {
01659          ast_log(LOG_ERROR, "Invalid value for forcecommit: '%s'\n", S_OR(value, ""));
01660          pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE");
01661          return -1;
01662       }
01663 
01664       pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
01665       return 0;
01666    } else if (strcasecmp(args.property, "isolation") == 0) {
01667       /* How do uncommitted transactions affect reads? */
01668       int isolation = text2isolation(value);
01669       if (ast_strlen_zero(args.opt)) {
01670          tx = find_transaction(chan, NULL, NULL, 1);
01671       } else {
01672          tx = find_transaction(chan, NULL, args.opt, 0);
01673       }
01674       if (!tx) {
01675          pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
01676          return -1;
01677       }
01678       if (isolation == 0) {
01679          pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE");
01680          ast_log(LOG_ERROR, "Invalid isolation specification: '%s'\n", S_OR(value, ""));
01681       } else if (SQLSetConnectAttr(tx->obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)isolation, 0) == SQL_ERROR) {
01682          pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "SQL_ERROR");
01683          SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01684          for (i = 0; i < numfields; i++) {
01685             SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01686             ast_log(LOG_WARNING, "SetConnectAttr (Txn isolation) returned an error: %s: %s\n", state, diagnostic);
01687             if (i > 10) {
01688                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01689                break;
01690             }
01691          }
01692       } else {
01693          pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
01694          tx->isolation = isolation;
01695       }
01696       return 0;
01697    } else {
01698       ast_log(LOG_ERROR, "Unknown property: '%s'\n", args.property);
01699       return -1;
01700    }
01701 }

static int aoro2_class_cb ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 1192 of file res_odbc.c.

References CMP_MATCH, and CMP_STOP.

Referenced by _ast_odbc_request_obj2().

01193 {
01194    struct odbc_class *class = obj;
01195    char *name = arg;
01196    if (!strcmp(class->name, name) && !class->delme) {
01197       return CMP_MATCH | CMP_STOP;
01198    }
01199    return 0;
01200 }

static int aoro2_obj_cb ( void *  vobj,
void *  arg,
int  flags 
) [static]

Definition at line 1206 of file res_odbc.c.

References ast_mutex_lock, ast_mutex_unlock, CMP_MATCH, CMP_STOP, EOR_TX, odbc_obj::lock, NO_TX, odbc_obj::tx, USE_TX, and odbc_obj::used.

Referenced by _ast_odbc_request_obj2().

01207 {
01208    struct odbc_obj *obj = vobj;
01209    ast_mutex_lock(&obj->lock);
01210    if ((arg == NO_TX && !obj->tx) || (arg == EOR_TX && !obj->used) || (arg == USE_TX && obj->tx && !obj->used)) {
01211       obj->used = 1;
01212       ast_mutex_unlock(&obj->lock);
01213       return CMP_MATCH | CMP_STOP;
01214    }
01215    ast_mutex_unlock(&obj->lock);
01216    return 0;
01217 }

static int aoro2_obj_notx_cb ( void *  vobj,
void *  arg,
int  flags 
) [static]

Definition at line 1222 of file res_odbc.c.

References CMP_MATCH, CMP_STOP, and odbc_obj::tx.

Referenced by _ast_odbc_request_obj2().

01223 {
01224    struct odbc_obj *obj = vobj;
01225    if (!obj->tx) {
01226       return CMP_MATCH | CMP_STOP;
01227    }
01228    return 0;
01229 }

AST_DATA_STRUCTURE ( odbc_class  ,
DATA_EXPORT_ODBC_CLASS   
)
SQLRETURN ast_odbc_ast_str_SQLGetData ( struct ast_str **  buf,
int  pmaxlen,
SQLHSTMT  StatementHandle,
SQLUSMALLINT  ColumnNumber,
SQLSMALLINT  TargetType,
SQLLEN *  StrLen_or_Ind 
)

Wrapper for SQLGetData to use with dynamic strings.

Parameters:
buf Address of the pointer to the ast_str structure.
pmaxlen The maximum size of the resulting string, or 0 for no limit.
StatementHandle The statement handle from which to retrieve data.
ColumnNumber Column number (1-based offset) for which to retrieve data.
TargetType The SQL constant indicating what kind of data is to be retrieved (usually SQL_CHAR)
StrLen_or_Ind A pointer to a length indicator, specifying the total length of data.

Definition at line 717 of file res_odbc.c.

References ast_str_buffer(), ast_str_make_space(), ast_str_size(), and ast_str_update().

Referenced by acf_odbc_read(), and cli_odbc_read().

00718 {
00719    SQLRETURN res;
00720 
00721    if (pmaxlen == 0) {
00722       if (SQLGetData(StatementHandle, ColumnNumber, TargetType, ast_str_buffer(*buf), 0, StrLen_or_Ind) == SQL_SUCCESS_WITH_INFO) {
00723          ast_str_make_space(buf, *StrLen_or_Ind + 1);
00724       }
00725    } else if (pmaxlen > 0) {
00726       ast_str_make_space(buf, pmaxlen);
00727    }
00728    res = SQLGetData(StatementHandle, ColumnNumber, TargetType, ast_str_buffer(*buf), ast_str_size(*buf), StrLen_or_Ind);
00729    ast_str_update(*buf);
00730 
00731    return res;
00732 }

int ast_odbc_backslash_is_escape ( struct odbc_obj obj  ) 

Checks if the database natively supports backslash as an escape character.

Parameters:
obj The ODBC object
Returns:
Returns 1 if backslash is a native escape character, 0 if an ESCAPE clause is needed to support '\'

Definition at line 1113 of file res_odbc.c.

References odbc_class::backslash_is_escape, and odbc_obj::parent.

Referenced by odbc_log(), realtime_multi_odbc(), and realtime_odbc().

01114 {
01115    return obj->parent->backslash_is_escape;
01116 }

int ast_odbc_clear_cache ( const char *  database,
const char *  tablename 
)

Remove a cache entry from memory This function may be called to clear entries created and cached by the ast_odbc_find_table() API call.

Parameters:
database Name of an ODBC class (used to ensure like-named tables in different databases are not confused)
tablename Tablename for which a cached record should be removed
Return values:
0 if the cache entry was removed, or -1 if no matching entry was found.
Since:
1.6.1

Definition at line 576 of file res_odbc.c.

References AST_LIST_REMOVE_CURRENT, AST_RWLIST_TRAVERSE_SAFE_BEGIN, AST_RWLIST_TRAVERSE_SAFE_END, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, odbc_cache_tables::connection, destroy_table_cache(), and odbc_cache_tables::table.

Referenced by unload_odbc().

00577 {
00578    struct odbc_cache_tables *tableptr;
00579 
00580    AST_RWLIST_WRLOCK(&odbc_tables);
00581    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&odbc_tables, tableptr, list) {
00582       if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) {
00583          AST_LIST_REMOVE_CURRENT(list);
00584          destroy_table_cache(tableptr);
00585          break;
00586       }
00587    }
00588    AST_RWLIST_TRAVERSE_SAFE_END
00589    AST_RWLIST_UNLOCK(&odbc_tables);
00590    return tableptr ? 0 : -1;
00591 }

SQLHSTMT ast_odbc_direct_execute ( struct odbc_obj obj,
SQLHSTMT(*)(struct odbc_obj *obj, void *data)  exec_cb,
void *  data 
)

Executes an non prepared statement and returns the resulting statement handle.

Parameters:
obj The ODBC object
exec_cb A function callback, which, when called, should return a statement handle with result columns bound.
data A parameter to be passed to the exec_cb parameter function, indicating which statement handle is to be prepared.
Return values:
a statement handle
NULL on error

Definition at line 593 of file res_odbc.c.

References ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_odbc_sanity_check(), odbc_class::dsn, odbc_obj::lock, LOG_WARNING, odbc_class::name, odbc_obj::parent, and odbc_obj::tx.

Referenced by acf_odbc_read(), acf_odbc_write(), cli_odbc_read(), cli_odbc_write(), and odbc_log().

00594 {
00595    int attempt;
00596    SQLHSTMT stmt;
00597 
00598    ast_mutex_lock(&obj->lock);
00599 
00600    for (attempt = 0; attempt < 2; attempt++) {
00601       stmt = exec_cb(obj, data);
00602 
00603       if (stmt) {
00604          break;
00605       } else if (obj->tx) {
00606          ast_log(LOG_WARNING, "Failed to execute, but unable to reconnect, as we're transactional.\n");
00607          break;
00608       } else if (attempt == 0) {
00609          ast_log(LOG_WARNING, "SQL Execute error! Verifying connection to %s [%s]...\n", obj->parent->name, obj->parent->dsn);
00610       }
00611       if (!ast_odbc_sanity_check(obj)) {
00612          break;
00613       }
00614    }
00615 
00616    ast_mutex_unlock(&obj->lock);
00617 
00618    return stmt;
00619 }

struct odbc_cache_columns* ast_odbc_find_column ( struct odbc_cache_tables table,
const char *  colname 
) [read]

Find a column entry within a cached table structure.

Parameters:
table Cached table structure, as returned from ast_odbc_find_table()
colname The column name requested
Return values:
A structure describing the column type, or NULL, if the column is not found.
Since:
1.6.1

Definition at line 565 of file res_odbc.c.

References AST_RWLIST_TRAVERSE, odbc_cache_tables::columns, and odbc_cache_columns::name.

Referenced by update2_prepare(), and update_odbc().

00566 {
00567    struct odbc_cache_columns *col;
00568    AST_RWLIST_TRAVERSE(&table->columns, col, list) {
00569       if (strcasecmp(col->name, colname) == 0) {
00570          return col;
00571       }
00572    }
00573    return NULL;
00574 }

struct odbc_cache_tables* ast_odbc_find_table ( const char *  database,
const char *  tablename 
) [read]

Find or create an entry describing the table specified.

Parameters:
database Name of an ODBC class on which to query the table
tablename Tablename to describe
Return values:
A structure describing the table layout, or NULL, if the table is not found or another error occurs. When a structure is returned, the contained columns list will be rdlock'ed, to ensure that it will be retained in memory.
Since:
1.6.1

Definition at line 448 of file res_odbc.c.

References ast_calloc, AST_LIST_INSERT_TAIL, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_odbc_release_obj(), ast_odbc_request_obj, ast_odbc_sanity_check(), AST_RWLIST_HEAD_INIT, AST_RWLIST_INSERT_TAIL, AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, ast_verb, odbc_cache_tables::columns, odbc_obj::con, odbc_cache_tables::connection, odbc_cache_columns::decimals, destroy_table_cache(), odbc_obj::lock, LOG_ERROR, LOG_WARNING, odbc_cache_columns::name, odbc_cache_columns::nullable, odbc_cache_columns::octetlen, odbc_cache_columns::radix, odbc_cache_columns::size, odbc_cache_tables::table, and odbc_cache_columns::type.

Referenced by require_odbc(), update2_prepare(), and update_odbc().

00449 {
00450    struct odbc_cache_tables *tableptr;
00451    struct odbc_cache_columns *entry;
00452    char columnname[80];
00453    SQLLEN sqlptr;
00454    SQLHSTMT stmt = NULL;
00455    int res = 0, error = 0, try = 0;
00456    struct odbc_obj *obj = ast_odbc_request_obj(database, 0);
00457 
00458    AST_RWLIST_RDLOCK(&odbc_tables);
00459    AST_RWLIST_TRAVERSE(&odbc_tables, tableptr, list) {
00460       if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) {
00461          break;
00462       }
00463    }
00464    if (tableptr) {
00465       AST_RWLIST_RDLOCK(&tableptr->columns);
00466       AST_RWLIST_UNLOCK(&odbc_tables);
00467       if (obj) {
00468          ast_odbc_release_obj(obj);
00469       }
00470       return tableptr;
00471    }
00472 
00473    if (!obj) {
00474       ast_log(LOG_WARNING, "Unable to retrieve database handle for table description '%s@%s'\n", tablename, database);
00475       AST_RWLIST_UNLOCK(&odbc_tables);
00476       return NULL;
00477    }
00478 
00479    /* Table structure not already cached; build it now. */
00480    ast_mutex_lock(&obj->lock);
00481    do {
00482       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00483       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00484          if (try == 0) {
00485             try = 1;
00486             ast_odbc_sanity_check(obj);
00487             continue;
00488          }
00489          ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", database);
00490          break;
00491       }
00492 
00493       res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)tablename, SQL_NTS, (unsigned char *)"%", SQL_NTS);
00494       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00495          if (try == 0) {
00496             try = 1;
00497             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00498             ast_odbc_sanity_check(obj);
00499             continue;
00500          }
00501          ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'.\n", database);
00502          break;
00503       }
00504 
00505       if (!(tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + strlen(database) + 1 + strlen(tablename) + 1))) {
00506          ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", tablename, database);
00507          break;
00508       }
00509 
00510       tableptr->connection = (char *)tableptr + sizeof(*tableptr);
00511       tableptr->table = (char *)tableptr + sizeof(*tableptr) + strlen(database) + 1;
00512       strcpy(tableptr->connection, database); /* SAFE */
00513       strcpy(tableptr->table, tablename); /* SAFE */
00514       AST_RWLIST_HEAD_INIT(&(tableptr->columns));
00515 
00516       while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) {
00517          SQLGetData(stmt,  4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr);
00518 
00519          if (!(entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1))) {
00520             ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, tablename, database);
00521             error = 1;
00522             break;
00523          }
00524          entry->name = (char *)entry + sizeof(*entry);
00525          strcpy(entry->name, columnname);
00526 
00527          SQLGetData(stmt,  5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL);
00528          SQLGetData(stmt,  7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL);
00529          SQLGetData(stmt,  9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL);
00530          SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL);
00531          SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL);
00532          SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL);
00533 
00534          /* Specification states that the octenlen should be the maximum number of bytes
00535           * returned in a char or binary column, but it seems that some drivers just set
00536           * it to NULL. (Bad Postgres! No biscuit!) */
00537          if (entry->octetlen == 0) {
00538             entry->octetlen = entry->size;
00539          }
00540 
00541          ast_verb(10, "Found %s column with type %hd with len %ld, octetlen %ld, and numlen (%hd,%hd)\n", entry->name, entry->type, (long) entry->size, (long) entry->octetlen, entry->decimals, entry->radix);
00542          /* Insert column info into column list */
00543          AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
00544       }
00545       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00546 
00547       AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list);
00548       AST_RWLIST_RDLOCK(&(tableptr->columns));
00549       break;
00550    } while (1);
00551    ast_mutex_unlock(&obj->lock);
00552 
00553    AST_RWLIST_UNLOCK(&odbc_tables);
00554 
00555    if (error) {
00556       destroy_table_cache(tableptr);
00557       tableptr = NULL;
00558    }
00559    if (obj) {
00560       ast_odbc_release_obj(obj);
00561    }
00562    return tableptr;
00563 }

SQLHSTMT ast_odbc_prepare_and_execute ( struct odbc_obj obj,
SQLHSTMT(*)(struct odbc_obj *obj, void *data)  prepare_cb,
void *  data 
)

Prepares, executes, and returns the resulting statement handle.

Parameters:
obj The ODBC object
prepare_cb A function callback, which, when called, should return a statement handle prepared, with any necessary parameters or result columns bound.
data A parameter to be passed to the prepare_cb parameter function, indicating which statement handle is to be prepared.
Return values:
a statement handle
NULL on error

Definition at line 621 of file res_odbc.c.

References ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_odbc_sanity_check(), ast_tvnow(), odbc_class::dsn, odbc_obj::last_used, odbc_obj::lock, LOG_WARNING, odbc_class::name, odbc_obj::parent, odbc_obj::tx, and odbc_obj::up.

Referenced by config_odbc(), destroy_odbc(), odbc_log(), realtime_multi_odbc(), realtime_odbc(), store_odbc(), update2_odbc(), and update_odbc().

00622 {
00623    int res = 0, i, attempt;
00624    SQLINTEGER nativeerror=0, numfields=0;
00625    SQLSMALLINT diagbytes=0;
00626    unsigned char state[10], diagnostic[256];
00627    SQLHSTMT stmt;
00628 
00629    ast_mutex_lock(&obj->lock);
00630 
00631    for (attempt = 0; attempt < 2; attempt++) {
00632       /* This prepare callback may do more than just prepare -- it may also
00633        * bind parameters, bind results, etc.  The real key, here, is that
00634        * when we disconnect, all handles become invalid for most databases.
00635        * We must therefore redo everything when we establish a new
00636        * connection. */
00637       stmt = prepare_cb(obj, data);
00638 
00639       if (stmt) {
00640          res = SQLExecute(stmt);
00641          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
00642             if (res == SQL_ERROR) {
00643                SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00644                for (i = 0; i < numfields; i++) {
00645                   SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00646                   ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00647                   if (i > 10) {
00648                      ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
00649                      break;
00650                   }
00651                }
00652             }
00653 
00654             if (obj->tx) {
00655                ast_log(LOG_WARNING, "SQL Execute error, but unable to reconnect, as we're transactional.\n");
00656                break;
00657             } else {
00658                ast_log(LOG_WARNING, "SQL Execute error %d! Verifying connection to %s [%s]...\n", res, obj->parent->name, obj->parent->dsn);
00659                SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00660                stmt = NULL;
00661 
00662                obj->up = 0;
00663                /*
00664                 * While this isn't the best way to try to correct an error, this won't automatically
00665                 * fail when the statement handle invalidates.
00666                 */
00667                if (!ast_odbc_sanity_check(obj)) {
00668                   break;
00669                }
00670                continue;
00671             }
00672          } else {
00673             obj->last_used = ast_tvnow();
00674          }
00675          break;
00676       } else if (attempt == 0) {
00677          ast_odbc_sanity_check(obj);
00678       }
00679    }
00680 
00681    ast_mutex_unlock(&obj->lock);
00682 
00683    return stmt;
00684 }

void ast_odbc_release_obj ( struct odbc_obj obj  ) 

Releases an ODBC object previously allocated by ast_odbc_request_obj().

Parameters:
obj The ODBC object

Definition at line 1107 of file res_odbc.c.

References find_transaction(), and odbc_release_obj2().

Referenced by acf_odbc_read(), acf_odbc_write(), ast_odbc_find_table(), cli_odbc_read(), cli_odbc_write(), config_odbc(), destroy_odbc(), load_config(), odbc_log(), odbc_register_class(), realtime_multi_odbc(), realtime_odbc(), store_odbc(), update2_odbc(), and update_odbc().

01108 {
01109    struct odbc_txn_frame *tx = find_transaction(NULL, obj, NULL, 0);
01110    odbc_release_obj2(obj, tx);
01111 }

struct odbc_obj* ast_odbc_retrieve_transaction_obj ( struct ast_channel chan,
const char *  objname 
) [read]

Retrieve a stored ODBC object, if a transaction has been started.

Parameters:
chan Channel associated with the transaction.
objname Name of the database handle. This name corresponds to the name passed to
See also:
ast_odbc_request_obj2 (or formerly, to ast_odbc_request_obj). Note that the existence of this parameter name explicitly allows for multiple transactions to be open at once, albeit to different databases.
Return values:
A stored ODBC object, if a transaction was already started.
NULL,if no transaction yet exists.

Definition at line 1446 of file res_odbc.c.

References ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, AST_LIST_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_datastore::data, odbc_class::name, odbc_txn_frame::obj, odbc_obj::parent, and txn_info.

Referenced by acf_odbc_write().

01447 {
01448    struct ast_datastore *txn_store;
01449    AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
01450    struct odbc_txn_frame *txn = NULL;
01451 
01452    if (!chan) {
01453       /* No channel == no transaction */
01454       return NULL;
01455    }
01456 
01457    ast_channel_lock(chan);
01458    if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
01459       oldlist = txn_store->data;
01460    } else {
01461       ast_channel_unlock(chan);
01462       return NULL;
01463    }
01464 
01465    AST_LIST_LOCK(oldlist);
01466    ast_channel_unlock(chan);
01467 
01468    AST_LIST_TRAVERSE(oldlist, txn, list) {
01469       if (txn->obj && txn->obj->parent && !strcmp(txn->obj->parent->name, objname)) {
01470          AST_LIST_UNLOCK(oldlist);
01471          return txn->obj;
01472       }
01473    }
01474    AST_LIST_UNLOCK(oldlist);
01475    return NULL;
01476 }

int ast_odbc_sanity_check ( struct odbc_obj obj  ) 

Checks an ODBC object to ensure it is still connected.

Parameters:
obj The ODBC object
Return values:
0 if connected
-1 otherwise.

Definition at line 734 of file res_odbc.c.

References ast_log(), ast_strlen_zero(), odbc_obj::con, LOG_WARNING, odbc_obj_connect(), odbc_obj_disconnect(), odbc_obj::parent, odbc_class::sanitysql, odbc_obj::tx, and odbc_obj::up.

Referenced by _ast_odbc_request_obj2(), ast_odbc_direct_execute(), ast_odbc_find_table(), ast_odbc_prepare_and_execute(), data_odbc_provider_handler(), and handle_cli_odbc_show().

00735 {
00736    char *test_sql = "select 1";
00737    SQLHSTMT stmt;
00738    int res = 0;
00739 
00740    if (!ast_strlen_zero(obj->parent->sanitysql))
00741       test_sql = obj->parent->sanitysql;
00742 
00743    if (obj->up) {
00744       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00745       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00746          obj->up = 0;
00747       } else {
00748          res = SQLPrepare(stmt, (unsigned char *)test_sql, SQL_NTS);
00749          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00750             obj->up = 0;
00751          } else {
00752             res = SQLExecute(stmt);
00753             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00754                obj->up = 0;
00755             }
00756          }
00757       }
00758       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00759    }
00760 
00761    if (!obj->up && !obj->tx) { /* Try to reconnect! */
00762       ast_log(LOG_WARNING, "Connection is down attempting to reconnect...\n");
00763       odbc_obj_disconnect(obj);
00764       odbc_obj_connect(obj);
00765    }
00766    return obj->up;
00767 }

int ast_odbc_smart_execute ( struct odbc_obj obj,
SQLHSTMT  stmt 
)

Executes a prepared statement handle.

Parameters:
obj The non-NULL result of odbc_request_obj()
stmt The prepared statement handle
Return values:
0 on success
-1 on failure

This function was originally designed simply to execute a prepared statement handle and to retry if the initial execution failed. Unfortunately, it did this by disconnecting and reconnecting the database handle which on most databases causes the statement handle to become invalid. Therefore, this method has been deprecated in favor of odbc_prepare_and_execute() which allows the statement to be prepared multiple times, if necessary, in case of a loss of connection.

This function really only ever worked with MySQL, where the statement handle is not prepared on the server. If you are not using MySQL, you should avoid it.

Definition at line 686 of file res_odbc.c.

References ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_tvnow(), odbc_obj::last_used, odbc_obj::lock, and LOG_WARNING.

00687 {
00688    int res = 0, i;
00689    SQLINTEGER nativeerror=0, numfields=0;
00690    SQLSMALLINT diagbytes=0;
00691    unsigned char state[10], diagnostic[256];
00692 
00693    ast_mutex_lock(&obj->lock);
00694 
00695    res = SQLExecute(stmt);
00696    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
00697       if (res == SQL_ERROR) {
00698          SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00699          for (i = 0; i < numfields; i++) {
00700             SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00701             ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00702             if (i > 10) {
00703                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
00704                break;
00705             }
00706          }
00707       }
00708    } else {
00709       obj->last_used = ast_tvnow();
00710    }
00711 
00712    ast_mutex_unlock(&obj->lock);
00713 
00714    return res;
00715 }

static int commit_exec ( struct ast_channel chan,
const char *  data 
) [static]

Definition at line 1118 of file res_odbc.c.

References ast_log(), ast_str_append(), ast_str_buffer(), ast_str_reset(), ast_str_strlen(), ast_str_thread_get(), ast_strlen_zero(), odbc_obj::con, errors_buf, find_transaction(), LOG_WARNING, odbc_txn_frame::obj, and pbx_builtin_setvar_helper().

Referenced by load_module().

01119 {
01120    struct odbc_txn_frame *tx;
01121    SQLINTEGER nativeerror=0, numfields=0;
01122    SQLSMALLINT diagbytes=0, i;
01123    unsigned char state[10], diagnostic[256];
01124 
01125    if (ast_strlen_zero(data)) {
01126       tx = find_transaction(chan, NULL, NULL, 1);
01127    } else {
01128       tx = find_transaction(chan, NULL, data, 0);
01129    }
01130 
01131    pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", "OK");
01132 
01133    if (tx) {
01134       if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_COMMIT) == SQL_ERROR) {
01135          struct ast_str *errors = ast_str_thread_get(&errors_buf, 16);
01136          ast_str_reset(errors);
01137 
01138          /* Handle possible transaction commit failure */
01139          SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01140          for (i = 0; i < numfields; i++) {
01141             SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01142             ast_str_append(&errors, 0, "%s%s", ast_str_strlen(errors) ? "," : "", state);
01143             ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic);
01144             if (i > 10) {
01145                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01146                break;
01147             }
01148          }
01149          pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", ast_str_buffer(errors));
01150       }
01151    }
01152    return 0;
01153 }

static int data_odbc_provider_handler ( const struct ast_data_search search,
struct ast_data root 
) [static]

Definition at line 1716 of file res_odbc.c.

References ao2_container_count(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_data_add_bool(), ast_data_add_int(), ast_data_add_node(), ast_data_add_str(), ast_data_add_structure, ast_data_remove_node(), ast_data_search_match(), ast_mutex_lock, ast_mutex_unlock, ast_odbc_sanity_check(), class_container, odbc_class::count, isolation2text(), odbc_obj::lock, odbc_obj::tx, odbc_obj::up, and odbc_obj::used.

01718 {
01719    struct ao2_iterator aoi, aoi2;
01720    struct odbc_class *class;
01721    struct odbc_obj *current;
01722    struct ast_data *data_odbc_class, *data_odbc_connections, *data_odbc_connection;
01723    struct ast_data *enum_node;
01724    int count;
01725 
01726    aoi = ao2_iterator_init(class_container, 0);
01727    while ((class = ao2_iterator_next(&aoi))) {
01728       data_odbc_class = ast_data_add_node(root, "class");
01729       if (!data_odbc_class) {
01730          ao2_ref(class, -1);
01731          continue;
01732       }
01733 
01734       ast_data_add_structure(odbc_class, data_odbc_class, class);
01735 
01736       if (!ao2_container_count(class->obj_container)) {
01737          ao2_ref(class, -1);
01738          continue;
01739       }
01740 
01741       data_odbc_connections = ast_data_add_node(data_odbc_class, "connections");
01742       if (!data_odbc_connections) {
01743          ao2_ref(class, -1);
01744          continue;
01745       }
01746 
01747       ast_data_add_bool(data_odbc_class, "shared", !class->haspool);
01748       /* isolation */
01749       enum_node = ast_data_add_node(data_odbc_class, "isolation");
01750       if (!enum_node) {
01751          ao2_ref(class, -1);
01752          continue;
01753       }
01754       ast_data_add_int(enum_node, "value", class->isolation);
01755       ast_data_add_str(enum_node, "text", isolation2text(class->isolation));
01756 
01757       count = 0;
01758       aoi2 = ao2_iterator_init(class->obj_container, 0);
01759       while ((current = ao2_iterator_next(&aoi2))) {
01760          data_odbc_connection = ast_data_add_node(data_odbc_connections, "connection");
01761          if (!data_odbc_connection) {
01762             ao2_ref(current, -1);
01763             continue;
01764          }
01765 
01766          ast_mutex_lock(&current->lock);
01767          ast_data_add_str(data_odbc_connection, "status", current->used ? "in use" :
01768                current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected");
01769          ast_data_add_bool(data_odbc_connection, "transactional", current->tx);
01770          ast_mutex_unlock(&current->lock);
01771 
01772          if (class->haspool) {
01773             ast_data_add_int(data_odbc_connection, "number", ++count);
01774          }
01775 
01776          ao2_ref(current, -1);
01777       }
01778       ao2_iterator_destroy(&aoi2);
01779       ao2_ref(class, -1);
01780 
01781       if (!ast_data_search_match(search, data_odbc_class)) {
01782          ast_data_remove_node(root, data_odbc_class);
01783       }
01784    }
01785    ao2_iterator_destroy(&aoi);
01786    return 0;
01787 }

static void destroy_table_cache ( struct odbc_cache_tables table  )  [static]

Definition at line 427 of file res_odbc.c.

References ast_debug, ast_free, AST_RWLIST_HEAD_DESTROY, AST_RWLIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, odbc_cache_tables::columns, and odbc_cache_tables::table.

Referenced by ast_odbc_clear_cache(), ast_odbc_find_table(), and reload().

00427                                                                  {
00428    struct odbc_cache_columns *col;
00429    ast_debug(1, "Destroying table cache for %s\n", table->table);
00430    AST_RWLIST_WRLOCK(&table->columns);
00431    while ((col = AST_RWLIST_REMOVE_HEAD(&table->columns, list))) {
00432       ast_free(col);
00433    }
00434    AST_RWLIST_UNLOCK(&table->columns);
00435    AST_RWLIST_HEAD_DESTROY(&table->columns);
00436    ast_free(table);
00437 }

static struct odbc_txn_frame* find_transaction ( struct ast_channel chan,
struct odbc_obj obj,
const char *  name,
int  active 
) [static, read]

Definition at line 223 of file res_odbc.c.

References odbc_txn_frame::active, ast_calloc, ast_channel_datastore_add(), ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, ast_datastore_alloc, ast_datastore_free(), AST_LIST_HEAD, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_datastore::data, odbc_class::forcecommit, odbc_txn_frame::forcecommit, odbc_class::isolation, odbc_txn_frame::isolation, LOG_ERROR, odbc_txn_frame::name, odbc_txn_frame::obj, odbc_txn_frame::owner, odbc_obj::parent, odbc_obj::tx, odbc_obj::txf, and txn_info.

Referenced by acf_transaction_read(), acf_transaction_write(), ast_odbc_release_obj(), commit_exec(), and rollback_exec().

00224 {
00225    struct ast_datastore *txn_store;
00226    AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
00227    struct odbc_txn_frame *txn = NULL;
00228 
00229    if (!chan && obj && obj->txf && obj->txf->owner) {
00230       chan = obj->txf->owner;
00231    } else if (!chan) {
00232       /* No channel == no transaction */
00233       return NULL;
00234    }
00235 
00236    ast_channel_lock(chan);
00237    if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
00238       oldlist = txn_store->data;
00239    } else {
00240       /* Need to create a new datastore */
00241       if (!(txn_store = ast_datastore_alloc(&txn_info, NULL))) {
00242          ast_log(LOG_ERROR, "Unable to allocate a new datastore.  Cannot create a new transaction.\n");
00243          ast_channel_unlock(chan);
00244          return NULL;
00245       }
00246 
00247       if (!(oldlist = ast_calloc(1, sizeof(*oldlist)))) {
00248          ast_log(LOG_ERROR, "Unable to allocate datastore list head.  Cannot create a new transaction.\n");
00249          ast_datastore_free(txn_store);
00250          ast_channel_unlock(chan);
00251          return NULL;
00252       }
00253 
00254       txn_store->data = oldlist;
00255       AST_LIST_HEAD_INIT(oldlist);
00256       ast_channel_datastore_add(chan, txn_store);
00257    }
00258 
00259    AST_LIST_LOCK(oldlist);
00260    ast_channel_unlock(chan);
00261 
00262    /* Scanning for an object is *fast*.  Scanning for a name is much slower. */
00263    if (obj != NULL || active == 1) {
00264       AST_LIST_TRAVERSE(oldlist, txn, list) {
00265          if (txn->obj == obj || txn->active) {
00266             AST_LIST_UNLOCK(oldlist);
00267             return txn;
00268          }
00269       }
00270    }
00271 
00272    if (name != NULL) {
00273       AST_LIST_TRAVERSE(oldlist, txn, list) {
00274          if (!strcasecmp(txn->name, name)) {
00275             AST_LIST_UNLOCK(oldlist);
00276             return txn;
00277          }
00278       }
00279    }
00280 
00281    /* Nothing found, create one */
00282    if (name && obj && (txn = ast_calloc(1, sizeof(*txn) + strlen(name) + 1))) {
00283       struct odbc_txn_frame *otxn;
00284 
00285       strcpy(txn->name, name); /* SAFE */
00286       txn->obj = obj;
00287       txn->isolation = obj->parent->isolation;
00288       txn->forcecommit = obj->parent->forcecommit;
00289       txn->owner = chan;
00290       txn->active = 1;
00291 
00292       /* On creation, the txn becomes active, and all others inactive */
00293       AST_LIST_TRAVERSE(oldlist, otxn, list) {
00294          otxn->active = 0;
00295       }
00296       AST_LIST_INSERT_TAIL(oldlist, txn, list);
00297 
00298       obj->txf = txn;
00299       obj->tx = 1;
00300    }
00301    AST_LIST_UNLOCK(oldlist);
00302 
00303    return txn;
00304 }

static char* handle_cli_odbc_show ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 930 of file res_odbc.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_cli_args::argc, ast_cli_args::argv, ast_cli(), ast_localtime(), ast_mutex_lock, ast_mutex_unlock, ast_odbc_sanity_check(), ast_strdup, ast_strftime(), class_container, CLI_GENERATE, CLI_INIT, CLI_SUCCESS, ast_cli_entry::command, odbc_class::count, ast_cli_args::fd, odbc_obj::lock, ast_cli_args::n, ast_cli_args::pos, odbc_obj::up, ast_cli_entry::usage, odbc_obj::used, and ast_cli_args::word.

00931 {
00932    struct ao2_iterator aoi;
00933    struct odbc_class *class;
00934    struct odbc_obj *current;
00935    int length = 0;
00936    int which = 0;
00937    char *ret = NULL;
00938 
00939    switch (cmd) {
00940    case CLI_INIT:
00941       e->command = "odbc show";
00942       e->usage =
00943             "Usage: odbc show [class]\n"
00944             "       List settings of a particular ODBC class or,\n"
00945             "       if not specified, all classes.\n";
00946       return NULL;
00947    case CLI_GENERATE:
00948       if (a->pos != 2)
00949          return NULL;
00950       length = strlen(a->word);
00951       aoi = ao2_iterator_init(class_container, 0);
00952       while ((class = ao2_iterator_next(&aoi))) {
00953          if (!strncasecmp(a->word, class->name, length) && ++which > a->n) {
00954             ret = ast_strdup(class->name);
00955          }
00956          ao2_ref(class, -1);
00957          if (ret) {
00958             break;
00959          }
00960       }
00961       ao2_iterator_destroy(&aoi);
00962       if (!ret && !strncasecmp(a->word, "all", length) && ++which > a->n) {
00963          ret = ast_strdup("all");
00964       }
00965       return ret;
00966    }
00967 
00968    ast_cli(a->fd, "\nODBC DSN Settings\n");
00969    ast_cli(a->fd,   "-----------------\n\n");
00970    aoi = ao2_iterator_init(class_container, 0);
00971    while ((class = ao2_iterator_next(&aoi))) {
00972       if ((a->argc == 2) || (a->argc == 3 && !strcmp(a->argv[2], "all")) || (!strcmp(a->argv[2], class->name))) {
00973          int count = 0;
00974          char timestr[80];
00975          struct ast_tm tm;
00976 
00977          ast_localtime(&class->last_negative_connect, &tm, NULL);
00978          ast_strftime(timestr, sizeof(timestr), "%Y-%m-%d %T", &tm);
00979          ast_cli(a->fd, "  Name:   %s\n  DSN:    %s\n", class->name, class->dsn);
00980          ast_cli(a->fd, "    Last connection attempt: %s\n", timestr);
00981 
00982          if (class->haspool) {
00983             struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
00984 
00985             ast_cli(a->fd, "  Pooled: Yes\n  Limit:  %d\n  Connections in use: %d\n", class->limit, class->count);
00986 
00987             while ((current = ao2_iterator_next(&aoi2))) {
00988                ast_mutex_lock(&current->lock);
00989 #ifdef DEBUG_THREADS
00990                ast_cli(a->fd, "    - Connection %d: %s (%s:%d %s)\n", ++count,
00991                   current->used ? "in use" :
00992                   current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected",
00993                   current->file, current->lineno, current->function);
00994 #else
00995                ast_cli(a->fd, "    - Connection %d: %s\n", ++count,
00996                   current->used ? "in use" :
00997                   current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected");
00998 #endif
00999                ast_mutex_unlock(&current->lock);
01000                ao2_ref(current, -1);
01001             }
01002             ao2_iterator_destroy(&aoi2);
01003          } else {
01004             /* Should only ever be one of these (unless there are transactions) */
01005             struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
01006             while ((current = ao2_iterator_next(&aoi2))) {
01007                ast_cli(a->fd, "  Pooled: No\n  Connected: %s\n", current->used ? "In use" :
01008                   current->up && ast_odbc_sanity_check(current) ? "Yes" : "No");
01009                ao2_ref(current, -1);
01010             }
01011             ao2_iterator_destroy(&aoi2);
01012          }
01013          ast_cli(a->fd, "\n");
01014       }
01015       ao2_ref(class, -1);
01016    }
01017    ao2_iterator_destroy(&aoi);
01018 
01019    return CLI_SUCCESS;
01020 }

static const char* isolation2text ( int  iso  )  [static]

Definition at line 189 of file res_odbc.c.

Referenced by acf_transaction_read(), and data_odbc_provider_handler().

00190 {
00191    if (iso == SQL_TXN_READ_COMMITTED) {
00192       return "read_committed";
00193    } else if (iso == SQL_TXN_READ_UNCOMMITTED) {
00194       return "read_uncommitted";
00195    } else if (iso == SQL_TXN_SERIALIZABLE) {
00196       return "serializable";
00197    } else if (iso == SQL_TXN_REPEATABLE_READ) {
00198       return "repeatable_read";
00199    } else {
00200       return "unknown";
00201    }
00202 }

static int load_module ( void   )  [static]
static int load_odbc_config ( void   )  [static]

Definition at line 769 of file res_odbc.c.

References ao2_alloc, ao2_container_alloc, ao2_match_by_addr, ao2_ref, ast_category_browse(), ast_config_destroy(), ast_config_load, ast_copy_string(), ast_false(), ast_log(), ast_strdup, ast_strlen_zero(), ast_true(), ast_variable_browse(), config, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEMISSING, odbc_class::conntimeout, odbc_class::dsn, enabled, odbc_class::idlecheck, odbc_class::limit, LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_variable::name, ast_variable::next, null_hash_fn(), odbc_class_destructor(), odbc_register_class(), odbc_class::password, odbc_class::sanitysql, text2isolation(), odbc_class::username, and ast_variable::value.

Referenced by load_module(), and reload().

00770 {
00771    static char *cfg = "res_odbc.conf";
00772    struct ast_config *config;
00773    struct ast_variable *v;
00774    char *cat;
00775    const char *dsn, *username, *password, *sanitysql;
00776    int enabled, pooling, limit, bse, conntimeout, forcecommit, isolation;
00777    struct timeval ncache = { 0, 0 };
00778    unsigned int idlecheck;
00779    int preconnect = 0, res = 0;
00780    struct ast_flags config_flags = { 0 };
00781 
00782    struct odbc_class *new;
00783 
00784    config = ast_config_load(cfg, config_flags);
00785    if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
00786       ast_log(LOG_WARNING, "Unable to load config file res_odbc.conf\n");
00787       return -1;
00788    }
00789    for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) {
00790       if (!strcasecmp(cat, "ENV")) {
00791          for (v = ast_variable_browse(config, cat); v; v = v->next) {
00792             setenv(v->name, v->value, 1);
00793             ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value);
00794          }
00795       } else {
00796          /* Reset all to defaults for each class of odbc connections */
00797          dsn = username = password = sanitysql = NULL;
00798          enabled = 1;
00799          preconnect = idlecheck = 0;
00800          pooling = 0;
00801          limit = 0;
00802          bse = 1;
00803          conntimeout = 10;
00804          forcecommit = 0;
00805          isolation = SQL_TXN_READ_COMMITTED;
00806          for (v = ast_variable_browse(config, cat); v; v = v->next) {
00807             if (!strcasecmp(v->name, "pooling")) {
00808                if (ast_true(v->value))
00809                   pooling = 1;
00810             } else if (!strncasecmp(v->name, "share", 5)) {
00811                /* "shareconnections" is a little clearer in meaning than "pooling" */
00812                if (ast_false(v->value))
00813                   pooling = 1;
00814             } else if (!strcasecmp(v->name, "limit")) {
00815                sscanf(v->value, "%30d", &limit);
00816                if (ast_true(v->value) && !limit) {
00817                   ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'.  Setting limit to 1023 for ODBC class '%s'.\n", v->value, cat);
00818                   limit = 1023;
00819                } else if (ast_false(v->value)) {
00820                   ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'.  Disabling ODBC class '%s'.\n", v->value, cat);
00821                   enabled = 0;
00822                   break;
00823                }
00824             } else if (!strcasecmp(v->name, "idlecheck")) {
00825                sscanf(v->value, "%30u", &idlecheck);
00826             } else if (!strcasecmp(v->name, "enabled")) {
00827                enabled = ast_true(v->value);
00828             } else if (!strcasecmp(v->name, "pre-connect")) {
00829                preconnect = ast_true(v->value);
00830             } else if (!strcasecmp(v->name, "dsn")) {
00831                dsn = v->value;
00832             } else if (!strcasecmp(v->name, "username")) {
00833                username = v->value;
00834             } else if (!strcasecmp(v->name, "password")) {
00835                password = v->value;
00836             } else if (!strcasecmp(v->name, "sanitysql")) {
00837                sanitysql = v->value;
00838             } else if (!strcasecmp(v->name, "backslash_is_escape")) {
00839                bse = ast_true(v->value);
00840             } else if (!strcasecmp(v->name, "connect_timeout")) {
00841                if (sscanf(v->value, "%d", &conntimeout) != 1 || conntimeout < 1) {
00842                   ast_log(LOG_WARNING, "connect_timeout must be a positive integer\n");
00843                   conntimeout = 10;
00844                }
00845             } else if (!strcasecmp(v->name, "negative_connection_cache")) {
00846                double dncache;
00847                if (sscanf(v->value, "%lf", &dncache) != 1 || dncache < 0) {
00848                   ast_log(LOG_WARNING, "negative_connection_cache must be a non-negative integer\n");
00849                   /* 5 minutes sounds like a reasonable default */
00850                   ncache.tv_sec = 300;
00851                   ncache.tv_usec = 0;
00852                } else {
00853                   ncache.tv_sec = (int)dncache;
00854                   ncache.tv_usec = (dncache - ncache.tv_sec) * 1000000;
00855                }
00856             } else if (!strcasecmp(v->name, "forcecommit")) {
00857                forcecommit = ast_true(v->value);
00858             } else if (!strcasecmp(v->name, "isolation")) {
00859                if ((isolation = text2isolation(v->value)) == 0) {
00860                   ast_log(LOG_ERROR, "Unrecognized value for 'isolation': '%s' in section '%s'\n", v->value, cat);
00861                   isolation = SQL_TXN_READ_COMMITTED;
00862                }
00863             }
00864          }
00865 
00866          if (enabled && !ast_strlen_zero(dsn)) {
00867             new = ao2_alloc(sizeof(*new), odbc_class_destructor);
00868 
00869             if (!new) {
00870                res = -1;
00871                break;
00872             }
00873 
00874             SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env);
00875             res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
00876 
00877             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00878                ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n");
00879                ao2_ref(new, -1);
00880                return res;
00881             }
00882 
00883             new->obj_container = ao2_container_alloc(1, null_hash_fn, ao2_match_by_addr);
00884 
00885             if (pooling) {
00886                new->haspool = pooling;
00887                if (limit) {
00888                   new->limit = limit;
00889                } else {
00890                   ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless.  Changing limit from 0 to 5.\n");
00891                   new->limit = 5;
00892                }
00893             }
00894 
00895             new->backslash_is_escape = bse ? 1 : 0;
00896             new->forcecommit = forcecommit ? 1 : 0;
00897             new->isolation = isolation;
00898             new->idlecheck = idlecheck;
00899             new->conntimeout = conntimeout;
00900             new->negative_connection_cache = ncache;
00901 
00902             if (cat)
00903                ast_copy_string(new->name, cat, sizeof(new->name));
00904             if (dsn)
00905                ast_copy_string(new->dsn, dsn, sizeof(new->dsn));
00906             if (username && !(new->username = ast_strdup(username))) {
00907                ao2_ref(new, -1);
00908                break;
00909             }
00910             if (password && !(new->password = ast_strdup(password))) {
00911                ao2_ref(new, -1);
00912                break;
00913             }
00914             if (sanitysql && !(new->sanitysql = ast_strdup(sanitysql))) {
00915                ao2_ref(new, -1);
00916                break;
00917             }
00918 
00919             odbc_register_class(new, preconnect);
00920             ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
00921             ao2_ref(new, -1);
00922             new = NULL;
00923          }
00924       }
00925    }
00926    ast_config_destroy(config);
00927    return res;
00928 }

static int mark_transaction_active ( struct ast_channel chan,
struct odbc_txn_frame tx 
) [static]

Definition at line 358 of file res_odbc.c.

References odbc_txn_frame::active, ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, AST_LIST_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_datastore::data, odbc_txn_frame::owner, and txn_info.

Referenced by acf_transaction_write().

00359 {
00360    struct ast_datastore *txn_store;
00361    AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
00362    struct odbc_txn_frame *active = NULL, *txn;
00363 
00364    if (!chan && tx && tx->owner) {
00365       chan = tx->owner;
00366    }
00367 
00368    if (!chan) {
00369       return -1;
00370    }
00371 
00372    ast_channel_lock(chan);
00373    if (!(txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
00374       ast_channel_unlock(chan);
00375       return -1;
00376    }
00377 
00378    oldlist = txn_store->data;
00379    AST_LIST_LOCK(oldlist);
00380    AST_LIST_TRAVERSE(oldlist, txn, list) {
00381       if (txn == tx) {
00382          txn->active = 1;
00383          active = txn;
00384       } else {
00385          txn->active = 0;
00386       }
00387    }
00388    AST_LIST_UNLOCK(oldlist);
00389    ast_channel_unlock(chan);
00390    return active ? 0 : -1;
00391 }

static int null_hash_fn ( const void *  obj,
const int  flags 
) [static]

Definition at line 412 of file res_odbc.c.

Referenced by load_module(), and load_odbc_config().

00413 {
00414    return 0;
00415 }

static void odbc_class_destructor ( void *  data  )  [static]

Definition at line 393 of file res_odbc.c.

References ao2_ref, and ast_free.

Referenced by load_odbc_config().

00394 {
00395    struct odbc_class *class = data;
00396    /* Due to refcounts, we can safely assume that any objects with a reference
00397     * to us will prevent our destruction, so we don't need to worry about them.
00398     */
00399    if (class->username) {
00400       ast_free(class->username);
00401    }
00402    if (class->password) {
00403       ast_free(class->password);
00404    }
00405    if (class->sanitysql) {
00406       ast_free(class->sanitysql);
00407    }
00408    ao2_ref(class->obj_container, -1);
00409    SQLFreeHandle(SQL_HANDLE_ENV, class->env);
00410 }

static odbc_status odbc_obj_connect ( struct odbc_obj obj  )  [static]

Definition at line 1514 of file res_odbc.c.

References ast_assert, ast_log(), ast_tvnow(), odbc_obj::con, odbc_class::conntimeout, odbc_class::dsn, odbc_class::env, odbc_class::last_negative_connect, odbc_obj::last_used, LOG_NOTICE, LOG_WARNING, odbc_class::name, ODBC_FAIL, odbc_obj_disconnect(), ODBC_SUCCESS, odbc_obj::parent, odbc_class::password, odbc_obj::up, and odbc_class::username.

Referenced by _ast_odbc_request_obj2(), and ast_odbc_sanity_check().

01515 {
01516    int res;
01517    SQLINTEGER err;
01518    short int mlen;
01519    unsigned char msg[200], state[10];
01520 #ifdef NEEDTRACE
01521    SQLINTEGER enable = 1;
01522    char *tracefile = "/tmp/odbc.trace";
01523 #endif
01524    SQLHDBC con;
01525 
01526    if (obj->up) {
01527       odbc_obj_disconnect(obj);
01528       ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name);
01529    } else {
01530       ast_assert(obj->con == NULL);
01531       ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name);
01532    }
01533 
01534    res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &con);
01535 
01536    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01537       ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
01538       obj->parent->last_negative_connect = ast_tvnow();
01539       return ODBC_FAIL;
01540    }
01541    SQLSetConnectAttr(con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)(long) obj->parent->conntimeout, 0);
01542    SQLSetConnectAttr(con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *)(long) obj->parent->conntimeout, 0);
01543 #ifdef NEEDTRACE
01544    SQLSetConnectAttr(con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
01545    SQLSetConnectAttr(con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
01546 #endif
01547 
01548    res = SQLConnect(con,
01549          (SQLCHAR *) obj->parent->dsn, SQL_NTS,
01550          (SQLCHAR *) obj->parent->username, SQL_NTS,
01551          (SQLCHAR *) obj->parent->password, SQL_NTS);
01552 
01553    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01554       SQLGetDiagRec(SQL_HANDLE_DBC, con, 1, state, &err, msg, 100, &mlen);
01555       obj->parent->last_negative_connect = ast_tvnow();
01556       ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
01557       if ((res = SQLFreeHandle(SQL_HANDLE_DBC, con) != SQL_SUCCESS)) {
01558          SQLGetDiagRec(SQL_HANDLE_DBC, con, 1, state, &err, msg, 100, &mlen);
01559          ast_log(LOG_WARNING, "Unable to deallocate database handle %p? %d errno=%d %s\n", con, res, (int)err, msg);
01560       }
01561       return ODBC_FAIL;
01562    } else {
01563       ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn);
01564       obj->up = 1;
01565       obj->last_used = ast_tvnow();
01566    }
01567 
01568    obj->con = con;
01569    return ODBC_SUCCESS;
01570 }

static void odbc_obj_destructor ( void *  data  )  [static]

Definition at line 417 of file res_odbc.c.

References ao2_ref, ast_mutex_destroy, odbc_obj::lock, odbc_obj_disconnect(), and odbc_obj::parent.

Referenced by _ast_odbc_request_obj2().

00418 {
00419    struct odbc_obj *obj = data;
00420    struct odbc_class *class = obj->parent;
00421    obj->parent = NULL;
00422    odbc_obj_disconnect(obj);
00423    ast_mutex_destroy(&obj->lock);
00424    ao2_ref(class, -1);
00425 }

static odbc_status odbc_obj_disconnect ( struct odbc_obj obj  )  [static]

Definition at line 1478 of file res_odbc.c.

References ast_log(), odbc_obj::con, odbc_class::dsn, LOG_DEBUG, LOG_WARNING, odbc_class::name, ODBC_SUCCESS, odbc_obj::parent, and odbc_obj::up.

Referenced by ast_odbc_sanity_check(), odbc_obj_connect(), and odbc_obj_destructor().

01479 {
01480    int res;
01481    SQLINTEGER err;
01482    short int mlen;
01483    unsigned char msg[200], state[10];
01484    SQLHDBC con;
01485 
01486    /* Nothing to disconnect */
01487    if (!obj->con) {
01488       return ODBC_SUCCESS;
01489    }
01490 
01491    con = obj->con;
01492    obj->con = NULL;
01493    res = SQLDisconnect(con);
01494 
01495    if (obj->parent) {
01496       if (res == SQL_SUCCESS || res == SQL_SUCCESS_WITH_INFO) {
01497          ast_log(LOG_DEBUG, "Disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn);
01498       } else {
01499          ast_log(LOG_DEBUG, "res_odbc: %s [%s] already disconnected\n", obj->parent->name, obj->parent->dsn);
01500       }
01501    }
01502 
01503    if ((res = SQLFreeHandle(SQL_HANDLE_DBC, con) == SQL_SUCCESS)) {
01504       ast_log(LOG_DEBUG, "Database handle %p deallocated\n", con);
01505    } else {
01506       SQLGetDiagRec(SQL_HANDLE_DBC, con, 1, state, &err, msg, 100, &mlen);
01507       ast_log(LOG_WARNING, "Unable to deallocate database handle %p? %d errno=%d %s\n", con, res, (int)err, msg);
01508    }
01509 
01510    obj->up = 0;
01511    return ODBC_SUCCESS;
01512 }

static int odbc_register_class ( struct odbc_class class,
int  connect 
) [static]

Definition at line 1026 of file res_odbc.c.

References ao2_link, ast_log(), ast_odbc_release_obj(), ast_odbc_request_obj, class_container, and LOG_WARNING.

Referenced by load_odbc_config().

01027 {
01028    struct odbc_obj *obj;
01029    if (class) {
01030       ao2_link(class_container, class);
01031       /* I still have a reference in the caller, so a deref is NOT missing here. */
01032 
01033       if (preconnect) {
01034          /* Request and release builds a connection */
01035          obj = ast_odbc_request_obj(class->name, 0);
01036          if (obj) {
01037             ast_odbc_release_obj(obj);
01038          }
01039       }
01040 
01041       return 0;
01042    } else {
01043       ast_log(LOG_WARNING, "Attempted to register a NULL class?\n");
01044       return -1;
01045    }
01046 }

static void odbc_release_obj2 ( struct odbc_obj obj,
struct odbc_txn_frame tx 
) [static]

Definition at line 1048 of file res_odbc.c.

References ao2_ref, ast_debug, ast_log(), odbc_obj::con, odbc_txn_frame::forcecommit, LOG_WARNING, odbc_txn_frame::obj, release_transaction(), odbc_obj::txf, and odbc_obj::used.

Referenced by ast_odbc_release_obj(), and release_transaction().

01049 {
01050    SQLINTEGER nativeerror=0, numfields=0;
01051    SQLSMALLINT diagbytes=0, i;
01052    unsigned char state[10], diagnostic[256];
01053 
01054    ast_debug(2, "odbc_release_obj2(%p) called (obj->txf = %p)\n", obj, obj->txf);
01055    if (tx) {
01056       ast_debug(1, "called on a transactional handle with %s\n", tx->forcecommit ? "COMMIT" : "ROLLBACK");
01057       if (SQLEndTran(SQL_HANDLE_DBC, obj->con, tx->forcecommit ? SQL_COMMIT : SQL_ROLLBACK) == SQL_ERROR) {
01058          /* Handle possible transaction commit failure */
01059          SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01060          for (i = 0; i < numfields; i++) {
01061             SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01062             ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic);
01063             if (!strcmp((char *)state, "25S02") || !strcmp((char *)state, "08007")) {
01064                /* These codes mean that a commit failed and a transaction
01065                 * is still active. We must rollback, or things will get
01066                 * very, very weird for anybody using the handle next. */
01067                SQLEndTran(SQL_HANDLE_DBC, obj->con, SQL_ROLLBACK);
01068             }
01069             if (i > 10) {
01070                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01071                break;
01072             }
01073          }
01074       }
01075 
01076       /* Transaction is done, reset autocommit */
01077       if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) {
01078          SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01079          for (i = 0; i < numfields; i++) {
01080             SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01081             ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01082             if (i > 10) {
01083                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01084                break;
01085             }
01086          }
01087       }
01088    }
01089 
01090 #ifdef DEBUG_THREADS
01091    obj->file[0] = '\0';
01092    obj->function[0] = '\0';
01093    obj->lineno = 0;
01094 #endif
01095 
01096    /* For pooled connections, this frees the connection to be
01097     * reused.  For non-pooled connections, it does nothing. */
01098    obj->used = 0;
01099    if (obj->txf) {
01100       /* Prevent recursion -- transaction is already closed out. */
01101       obj->txf->obj = NULL;
01102       obj->txf = release_transaction(obj->txf);
01103    }
01104    ao2_ref(obj, -1);
01105 }

static void odbc_txn_free ( void *  data  )  [static]

Definition at line 342 of file res_odbc.c.

References ast_debug, ast_free, AST_LIST_HEAD, AST_LIST_HEAD_DESTROY, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, and release_transaction().

00343 {
00344    struct odbc_txn_frame *tx;
00345    AST_LIST_HEAD(, odbc_txn_frame) *oldlist = vdata;
00346 
00347    ast_debug(2, "odbc_txn_free(%p) called\n", vdata);
00348 
00349    AST_LIST_LOCK(oldlist);
00350    while ((tx = AST_LIST_REMOVE_HEAD(oldlist, list))) {
00351       release_transaction(tx);
00352    }
00353    AST_LIST_UNLOCK(oldlist);
00354    AST_LIST_HEAD_DESTROY(oldlist);
00355    ast_free(oldlist);
00356 }

static struct odbc_txn_frame* release_transaction ( struct odbc_txn_frame tx  )  [static, read]

Definition at line 306 of file res_odbc.c.

References ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, ast_debug, ast_free, AST_LIST_HEAD, AST_LIST_LOCK, AST_LIST_REMOVE, AST_LIST_UNLOCK, ast_datastore::data, odbc_txn_frame::obj, odbc_release_obj2(), odbc_txn_frame::owner, odbc_obj::txf, and txn_info.

Referenced by odbc_release_obj2(), and odbc_txn_free().

00307 {
00308    if (!tx) {
00309       return NULL;
00310    }
00311 
00312    ast_debug(2, "release_transaction(%p) called (tx->obj = %p, tx->obj->txf = %p)\n", tx, tx->obj, tx->obj ? tx->obj->txf : NULL);
00313 
00314    /* If we have an owner, disassociate */
00315    if (tx->owner) {
00316       struct ast_datastore *txn_store;
00317       AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
00318 
00319       ast_channel_lock(tx->owner);
00320       if ((txn_store = ast_channel_datastore_find(tx->owner, &txn_info, NULL))) {
00321          oldlist = txn_store->data;
00322          AST_LIST_LOCK(oldlist);
00323          AST_LIST_REMOVE(oldlist, tx, list);
00324          AST_LIST_UNLOCK(oldlist);
00325       }
00326       ast_channel_unlock(tx->owner);
00327       tx->owner = NULL;
00328    }
00329 
00330    if (tx->obj) {
00331       /* If we have any uncommitted transactions, they are handled when we release the object */
00332       struct odbc_obj *obj = tx->obj;
00333       /* Prevent recursion during destruction */
00334       tx->obj->txf = NULL;
00335       tx->obj = NULL;
00336       odbc_release_obj2(obj, tx);
00337    }
00338    ast_free(tx);
00339    return NULL;
00340 }

static int reload ( void   )  [static]

Definition at line 1802 of file res_odbc.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ao2_unlink, AST_RWLIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, class_container, destroy_table_cache(), load_odbc_config(), and table.

01803 {
01804    struct odbc_cache_tables *table;
01805    struct odbc_class *class;
01806    struct odbc_obj *current;
01807    struct ao2_iterator aoi = ao2_iterator_init(class_container, 0);
01808 
01809    /* First, mark all to be purged */
01810    while ((class = ao2_iterator_next(&aoi))) {
01811       class->delme = 1;
01812       ao2_ref(class, -1);
01813    }
01814    ao2_iterator_destroy(&aoi);
01815 
01816    load_odbc_config();
01817 
01818    /* Purge remaining classes */
01819 
01820    /* Note on how this works; this is a case of circular references, so we
01821     * explicitly do NOT want to use a callback here (or we wind up in
01822     * recursive hell).
01823     *
01824     * 1. Iterate through all the classes.  Note that the classes will currently
01825     * contain two classes of the same name, one of which is marked delme and
01826     * will be purged when all remaining objects of the class are released, and
01827     * the other, which was created above when we re-parsed the config file.
01828     * 2. On each class, there is a reference held by the master container and
01829     * a reference held by each connection object.  There are two cases for
01830     * destruction of the class, noted below.  However, in all cases, all O-refs
01831     * (references to objects) will first be freed, which will cause the C-refs
01832     * (references to classes) to be decremented (but never to 0, because the
01833     * class container still has a reference).
01834     *    a) If the class has outstanding objects, the C-ref by the class
01835     *    container will then be freed, which leaves only C-refs by any
01836     *    outstanding objects.  When the final outstanding object is released
01837     *    (O-refs held by applications and dialplan functions), it will in turn
01838     *    free the final C-ref, causing class destruction.
01839     *    b) If the class has no outstanding objects, when the class container
01840     *    removes the final C-ref, the class will be destroyed.
01841     */
01842    aoi = ao2_iterator_init(class_container, 0);
01843    while ((class = ao2_iterator_next(&aoi))) { /* C-ref++ (by iterator) */
01844       if (class->delme) {
01845          struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
01846          while ((current = ao2_iterator_next(&aoi2))) { /* O-ref++ (by iterator) */
01847             ao2_unlink(class->obj_container, current); /* unlink O-ref from class (reference handled implicitly) */
01848             ao2_ref(current, -1); /* O-ref-- (by iterator) */
01849             /* At this point, either
01850              * a) there's an outstanding O-ref, or
01851              * b) the object has already been destroyed.
01852              */
01853          }
01854          ao2_iterator_destroy(&aoi2);
01855          ao2_unlink(class_container, class); /* unlink C-ref from container (reference handled implicitly) */
01856          /* At this point, either
01857           * a) there's an outstanding O-ref, which holds an outstanding C-ref, or
01858           * b) the last remaining C-ref is held by the iterator, which will be
01859           * destroyed in the next step.
01860           */
01861       }
01862       ao2_ref(class, -1); /* C-ref-- (by iterator) */
01863    }
01864    ao2_iterator_destroy(&aoi);
01865 
01866    /* Empty the cache; it will get rebuilt the next time the tables are needed. */
01867    AST_RWLIST_WRLOCK(&odbc_tables);
01868    while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
01869       destroy_table_cache(table);
01870    }
01871    AST_RWLIST_UNLOCK(&odbc_tables);
01872 
01873    return 0;
01874 }

static int rollback_exec ( struct ast_channel chan,
const char *  data 
) [static]

Definition at line 1155 of file res_odbc.c.

References ast_log(), ast_str_append(), ast_str_buffer(), ast_str_reset(), ast_str_strlen(), ast_str_thread_get(), ast_strlen_zero(), odbc_obj::con, errors_buf, find_transaction(), LOG_WARNING, odbc_txn_frame::obj, and pbx_builtin_setvar_helper().

Referenced by load_module().

01156 {
01157    struct odbc_txn_frame *tx;
01158    SQLINTEGER nativeerror=0, numfields=0;
01159    SQLSMALLINT diagbytes=0, i;
01160    unsigned char state[10], diagnostic[256];
01161 
01162    if (ast_strlen_zero(data)) {
01163       tx = find_transaction(chan, NULL, NULL, 1);
01164    } else {
01165       tx = find_transaction(chan, NULL, data, 0);
01166    }
01167 
01168    pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", "OK");
01169 
01170    if (tx) {
01171       if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_ROLLBACK) == SQL_ERROR) {
01172          struct ast_str *errors = ast_str_thread_get(&errors_buf, 16);
01173          ast_str_reset(errors);
01174 
01175          /* Handle possible transaction commit failure */
01176          SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01177          for (i = 0; i < numfields; i++) {
01178             SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01179             ast_str_append(&errors, 0, "%s%s", ast_str_strlen(errors) ? "," : "", state);
01180             ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic);
01181             if (i > 10) {
01182                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01183                break;
01184             }
01185          }
01186          pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", ast_str_buffer(errors));
01187       }
01188    }
01189    return 0;
01190 }

static int text2isolation ( const char *  txt  )  [static]

Definition at line 204 of file res_odbc.c.

Referenced by acf_transaction_write(), and load_odbc_config().

00205 {
00206    if (strncasecmp(txt, "read_", 5) == 0) {
00207       if (strncasecmp(txt + 5, "c", 1) == 0) {
00208          return SQL_TXN_READ_COMMITTED;
00209       } else if (strncasecmp(txt + 5, "u", 1) == 0) {
00210          return SQL_TXN_READ_UNCOMMITTED;
00211       } else {
00212          return 0;
00213       }
00214    } else if (strncasecmp(txt, "ser", 3) == 0) {
00215       return SQL_TXN_SERIALIZABLE;
00216    } else if (strncasecmp(txt, "rep", 3) == 0) {
00217       return SQL_TXN_REPEATABLE_READ;
00218    } else {
00219       return 0;
00220    }
00221 }

static int unload_module ( void   )  [static]

Definition at line 1876 of file res_odbc.c.

01877 {
01878    /* Prohibit unloading */
01879    return -1;
01880 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER , .description = "ODBC resource" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_REALTIME_DEPEND, } [static]

Definition at line 1902 of file res_odbc.c.

const char* const app_commit = "ODBC_Commit" [static]

Definition at line 1709 of file res_odbc.c.

Referenced by load_module().

const char* const app_rollback = "ODBC_Rollback" [static]

Definition at line 1710 of file res_odbc.c.

Referenced by load_module().

Definition at line 1902 of file res_odbc.c.

struct ao2_container* class_container [static]
struct ast_cli_entry cli_odbc[] [static]
Initial value:
 {
   AST_CLI_DEFINE(handle_cli_odbc_show, "List ODBC DSN(s)")
}

Definition at line 1022 of file res_odbc.c.

Referenced by load_module().

struct ast_threadstorage errors_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_errors_buf , .custom_init = NULL , } [static]

Definition at line 154 of file res_odbc.c.

Referenced by commit_exec(), and rollback_exec().

Initial value:
 {
   .name = "ODBC",
   .read = acf_transaction_read,
   .write = acf_transaction_write,
}

Definition at line 1703 of file res_odbc.c.

Referenced by load_module().

Initial value:

Definition at line 1793 of file res_odbc.c.

struct ast_data_entry odbc_providers[] [static]
Initial value:
 {
   AST_DATA_ENTRY("/asterisk/res/odbc", &odbc_provider),
}

Definition at line 1798 of file res_odbc.c.

Referenced by load_module().

struct ast_datastore_info txn_info [static]
Initial value:
 {
   .type = "ODBC_Transaction",
   .destroy = odbc_txn_free,
}

Definition at line 156 of file res_odbc.c.

Referenced by ast_odbc_retrieve_transaction_obj(), find_transaction(), mark_transaction_active(), and release_transaction().


Generated on 20 Aug 2013 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1