Mon Aug 31 12:30:44 2015

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)
int ast_odbc_allow_empty_string_in_nontext (struct odbc_obj *obj)
 Checks if the database natively supports implicit conversion from an empty string to a number (0).
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 179 of file res_odbc.c.

#define EOR_TX   (void *)(long)3

Definition at line 1214 of file res_odbc.c.

Referenced by _ast_odbc_request_obj2(), and aoro2_obj_cb().

#define NO_TX   (void *)(long)2

Definition at line 1213 of file res_odbc.c.

Referenced by _ast_odbc_request_obj2(), and aoro2_obj_cb().

#define USE_TX   (void *)(long)1

Definition at line 1212 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 155 of file res_odbc.c.

00157 {

static void __reg_module ( void   )  [static]

Definition at line 1912 of file res_odbc.c.

static void __unreg_module ( void   )  [static]

Definition at line 1912 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 1450 of file res_odbc.c.

References _ast_odbc_request_obj2(), and RES_ODBC_SANITY_CHECK.

01451 {
01452    struct ast_flags flags = { check ? RES_ODBC_SANITY_CHECK : 0 };
01453    return _ast_odbc_request_obj2(name, flags, file, function, lineno);
01454 }

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 1241 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().

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

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

Definition at line 1582 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.

01583 {
01584    AST_DECLARE_APP_ARGS(args,
01585       AST_APP_ARG(property);
01586       AST_APP_ARG(opt);
01587    );
01588    struct odbc_txn_frame *tx;
01589 
01590    AST_STANDARD_APP_ARGS(args, data);
01591    if (strcasecmp(args.property, "transaction") == 0) {
01592       if ((tx = find_transaction(chan, NULL, NULL, 1))) {
01593          ast_copy_string(buf, tx->name, len);
01594          return 0;
01595       }
01596    } else if (strcasecmp(args.property, "isolation") == 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, isolation2text(tx->isolation), len);
01604          return 0;
01605       }
01606    } else if (strcasecmp(args.property, "forcecommit") == 0) {
01607       if (!ast_strlen_zero(args.opt)) {
01608          tx = find_transaction(chan, NULL, args.opt, 0);
01609       } else {
01610          tx = find_transaction(chan, NULL, NULL, 1);
01611       }
01612       if (tx) {
01613          ast_copy_string(buf, tx->forcecommit ? "1" : "0", len);
01614          return 0;
01615       }
01616    }
01617    return -1;
01618 }

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

Definition at line 1620 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.

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

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

Definition at line 1202 of file res_odbc.c.

References CMP_MATCH, and CMP_STOP.

Referenced by _ast_odbc_request_obj2().

01203 {
01204    struct odbc_class *class = obj;
01205    char *name = arg;
01206    if (!strcmp(class->name, name) && !class->delme) {
01207       return CMP_MATCH | CMP_STOP;
01208    }
01209    return 0;
01210 }

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

Definition at line 1216 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().

01217 {
01218    struct odbc_obj *obj = vobj;
01219    ast_mutex_lock(&obj->lock);
01220    if ((arg == NO_TX && !obj->tx) || (arg == EOR_TX && !obj->used) || (arg == USE_TX && obj->tx && !obj->used)) {
01221       obj->used = 1;
01222       ast_mutex_unlock(&obj->lock);
01223       return CMP_MATCH | CMP_STOP;
01224    }
01225    ast_mutex_unlock(&obj->lock);
01226    return 0;
01227 }

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

Definition at line 1232 of file res_odbc.c.

References CMP_MATCH, CMP_STOP, and odbc_obj::tx.

Referenced by _ast_odbc_request_obj2().

01233 {
01234    struct odbc_obj *obj = vobj;
01235    if (!obj->tx) {
01236       return CMP_MATCH | CMP_STOP;
01237    }
01238    return 0;
01239 }

AST_DATA_STRUCTURE ( odbc_class  ,
DATA_EXPORT_ODBC_CLASS   
)
int ast_odbc_allow_empty_string_in_nontext ( struct odbc_obj obj  ) 

Checks if the database natively supports implicit conversion from an empty string to a number (0).

Parameters:
obj The ODBC object
Returns:
Returns 1 if the implicit conversion is valid and non-text columns can take empty strings, 0 if the conversion is not valid and NULLs should be used instead '\'

Definition at line 1123 of file res_odbc.c.

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

Referenced by update_odbc().

01124 {
01125    return obj->parent->allow_empty_strings;
01126 }

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 718 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().

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

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 1118 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().

01119 {
01120    return obj->parent->backslash_is_escape;
01121 }

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 577 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().

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

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 594 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().

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

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 566 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().

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

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 449 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().

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

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 622 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().

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

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 1112 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().

01113 {
01114    struct odbc_txn_frame *tx = find_transaction(NULL, obj, NULL, 0);
01115    odbc_release_obj2(obj, tx);
01116 }

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 1456 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().

01457 {
01458    struct ast_datastore *txn_store;
01459    AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
01460    struct odbc_txn_frame *txn = NULL;
01461 
01462    if (!chan) {
01463       /* No channel == no transaction */
01464       return NULL;
01465    }
01466 
01467    ast_channel_lock(chan);
01468    if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
01469       oldlist = txn_store->data;
01470    } else {
01471       ast_channel_unlock(chan);
01472       return NULL;
01473    }
01474 
01475    AST_LIST_LOCK(oldlist);
01476    ast_channel_unlock(chan);
01477 
01478    AST_LIST_TRAVERSE(oldlist, txn, list) {
01479       if (txn->obj && txn->obj->parent && !strcmp(txn->obj->parent->name, objname)) {
01480          AST_LIST_UNLOCK(oldlist);
01481          return txn->obj;
01482       }
01483    }
01484    AST_LIST_UNLOCK(oldlist);
01485    return NULL;
01486 }

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 735 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().

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

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 687 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.

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

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

Definition at line 1128 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().

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

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

Definition at line 1726 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.

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

static void destroy_table_cache ( struct odbc_cache_tables table  )  [static]

Definition at line 428 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().

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

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 224 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().

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

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

Definition at line 935 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.

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

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

Definition at line 190 of file res_odbc.c.

Referenced by acf_transaction_read(), and data_odbc_provider_handler().

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

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

Definition at line 770 of file res_odbc.c.

References odbc_class::allow_empty_strings, 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().

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

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

Definition at line 359 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().

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

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

Definition at line 413 of file res_odbc.c.

Referenced by load_module(), and load_odbc_config().

00414 {
00415    return 0;
00416 }

static void odbc_class_destructor ( void *  data  )  [static]

Definition at line 394 of file res_odbc.c.

References ao2_ref, and ast_free.

Referenced by load_odbc_config().

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

static odbc_status odbc_obj_connect ( struct odbc_obj obj  )  [static]

Definition at line 1524 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().

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

static void odbc_obj_destructor ( void *  data  )  [static]

Definition at line 418 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().

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

static odbc_status odbc_obj_disconnect ( struct odbc_obj obj  )  [static]

Definition at line 1488 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().

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

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

Definition at line 1031 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().

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

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

Definition at line 1053 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().

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

static void odbc_txn_free ( void *  data  )  [static]

Definition at line 343 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().

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

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

Definition at line 307 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().

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

static int reload ( void   )  [static]

Definition at line 1812 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.

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

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

Definition at line 1165 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().

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

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

Definition at line 205 of file res_odbc.c.

Referenced by acf_transaction_write(), and load_odbc_config().

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

static int unload_module ( void   )  [static]

Definition at line 1886 of file res_odbc.c.

01887 {
01888    /* Prohibit unloading */
01889    return -1;
01890 }


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 1912 of file res_odbc.c.

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

Definition at line 1719 of file res_odbc.c.

Referenced by load_module().

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

Definition at line 1720 of file res_odbc.c.

Referenced by load_module().

Definition at line 1912 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 1027 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 155 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 1713 of file res_odbc.c.

Referenced by load_module().

Initial value:

Definition at line 1803 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 1808 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 157 of file res_odbc.c.

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


Generated on 31 Aug 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1