Fri Nov 12 12:08:33 2010

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 dependency graph for res_odbc.c:

Go to the source code of this file.

Data Structures

struct  odbc_class
struct  odbc_tables
struct  odbc_txn_frame

Defines

#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)
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)
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.
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 odbc_request_obj().
struct odbc_objast_odbc_request_obj (const char *name, int check)
struct odbc_objast_odbc_request_obj2 (const char *name, struct ast_flags flags)
 Retrieves a connected ODBC object.
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, void *data)
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, void *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 , .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 = "0901e4e500243c855563a2d78b0c03e4" , .load = load_module, .unload = unload_module, .reload = reload, }
static const char * app_commit = "ODBC_Commit"
static const char * app_rollback = "ODBC_Rollback"
static struct ast_module_infoast_module_info = &__mod_info
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_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 EOR_TX   (void *)(long)3

Definition at line 1138 of file res_odbc.c.

Referenced by aoro2_obj_cb(), and ast_odbc_request_obj2().

#define NO_TX   (void *)(long)2

Definition at line 1137 of file res_odbc.c.

Referenced by aoro2_obj_cb(), and ast_odbc_request_obj2().

#define USE_TX   (void *)(long)1

Definition at line 1136 of file res_odbc.c.

Referenced by aoro2_obj_cb(), and ast_odbc_request_obj2().


Function Documentation

static void __init_errors_buf ( void   )  [static]

Definition at line 146 of file res_odbc.c.

00148 {

static void __reg_module ( void   )  [static]

Definition at line 1702 of file res_odbc.c.

static void __unreg_module ( void   )  [static]

Definition at line 1702 of file res_odbc.c.

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

Definition at line 1464 of file res_odbc.c.

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

01465 {
01466    AST_DECLARE_APP_ARGS(args,
01467       AST_APP_ARG(property);
01468       AST_APP_ARG(opt);
01469    );
01470    struct odbc_txn_frame *tx;
01471 
01472    AST_STANDARD_APP_ARGS(args, data);
01473    if (strcasecmp(args.property, "transaction") == 0) {
01474       if ((tx = find_transaction(chan, NULL, NULL, 1))) {
01475          ast_copy_string(buf, tx->name, len);
01476          return 0;
01477       }
01478    } else if (strcasecmp(args.property, "isolation") == 0) {
01479       if (!ast_strlen_zero(args.opt)) {
01480          tx = find_transaction(chan, NULL, args.opt, 0);
01481       } else {
01482          tx = find_transaction(chan, NULL, NULL, 1);
01483       }
01484       if (tx) {
01485          ast_copy_string(buf, isolation2text(tx->isolation), len);
01486          return 0;
01487       }
01488    } else if (strcasecmp(args.property, "forcecommit") == 0) {
01489       if (!ast_strlen_zero(args.opt)) {
01490          tx = find_transaction(chan, NULL, args.opt, 0);
01491       } else {
01492          tx = find_transaction(chan, NULL, NULL, 1);
01493       }
01494       if (tx) {
01495          ast_copy_string(buf, tx->forcecommit ? "1" : "0", len);
01496          return 0;
01497       }
01498    }
01499    return -1;
01500 }

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

Definition at line 1502 of file res_odbc.c.

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

01503 {
01504    AST_DECLARE_APP_ARGS(args,
01505       AST_APP_ARG(property);
01506       AST_APP_ARG(opt);
01507    );
01508    struct odbc_txn_frame *tx;
01509    SQLINTEGER nativeerror=0, numfields=0;
01510    SQLSMALLINT diagbytes=0, i;
01511    unsigned char state[10], diagnostic[256];
01512 
01513    AST_STANDARD_APP_ARGS(args, s);
01514    if (strcasecmp(args.property, "transaction") == 0) {
01515       /* Set active transaction */
01516       struct odbc_obj *obj;
01517       if ((tx = find_transaction(chan, NULL, value, 0))) {
01518          mark_transaction_active(chan, tx);
01519       } else {
01520          /* No such transaction, create one */
01521          struct ast_flags flags = { RES_ODBC_INDEPENDENT_CONNECTION };
01522          if (ast_strlen_zero(args.opt) || !(obj = ast_odbc_request_obj2(args.opt, flags))) {
01523             ast_log(LOG_ERROR, "Could not create transaction: invalid database specification '%s'\n", S_OR(args.opt, ""));
01524             pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_DB");
01525             return -1;
01526          }
01527          if (!(tx = find_transaction(chan, obj, value, 0))) {
01528             pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
01529             return -1;
01530          }
01531          obj->tx = 1;
01532       }
01533       pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
01534       return 0;
01535    } else if (strcasecmp(args.property, "forcecommit") == 0) {
01536       /* Set what happens when an uncommitted transaction ends without explicit Commit or Rollback */
01537       if (ast_strlen_zero(args.opt)) {
01538          tx = find_transaction(chan, NULL, NULL, 1);
01539       } else {
01540          tx = find_transaction(chan, NULL, args.opt, 0);
01541       }
01542       if (!tx) {
01543          pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
01544          return -1;
01545       }
01546       if (ast_true(value)) {
01547          tx->forcecommit = 1;
01548       } else if (ast_false(value)) {
01549          tx->forcecommit = 0;
01550       } else {
01551          ast_log(LOG_ERROR, "Invalid value for forcecommit: '%s'\n", S_OR(value, ""));
01552          pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE");
01553          return -1;
01554       }
01555 
01556       pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
01557       return 0;
01558    } else if (strcasecmp(args.property, "isolation") == 0) {
01559       /* How do uncommitted transactions affect reads? */
01560       int isolation = text2isolation(value);
01561       if (ast_strlen_zero(args.opt)) {
01562          tx = find_transaction(chan, NULL, NULL, 1);
01563       } else {
01564          tx = find_transaction(chan, NULL, args.opt, 0);
01565       }
01566       if (!tx) {
01567          pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
01568          return -1;
01569       }
01570       if (isolation == 0) {
01571          pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE");
01572          ast_log(LOG_ERROR, "Invalid isolation specification: '%s'\n", S_OR(value, ""));
01573       } else if (SQLSetConnectAttr(tx->obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)isolation, 0) == SQL_ERROR) {
01574          pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "SQL_ERROR");
01575          SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01576          for (i = 0; i < numfields; i++) {
01577             SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01578             ast_log(LOG_WARNING, "SetConnectAttr (Txn isolation) returned an error: %s: %s\n", state, diagnostic);
01579             if (i > 10) {
01580                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01581                break;
01582             }
01583          }
01584       } else {
01585          pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
01586          tx->isolation = isolation;
01587       }
01588       return 0;
01589    } else {
01590       ast_log(LOG_ERROR, "Unknown property: '%s'\n", args.property);
01591       return -1;
01592    }
01593 }

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

Definition at line 1126 of file res_odbc.c.

References CMP_MATCH, CMP_STOP, and odbc_class::name.

Referenced by ast_odbc_request_obj2().

01127 {
01128    struct odbc_class *class = obj;
01129    char *name = arg;
01130    if (!strcmp(class->name, name) && !class->delme) {
01131       return CMP_MATCH | CMP_STOP;
01132    }
01133    return 0;
01134 }

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

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

01141 {
01142    struct odbc_obj *obj = vobj;
01143    ast_mutex_lock(&obj->lock);
01144    if ((arg == NO_TX && !obj->tx) || (arg == EOR_TX && !obj->used) || (arg == USE_TX && obj->tx && !obj->used)) {
01145       obj->used = 1;
01146       ast_mutex_unlock(&obj->lock);
01147       return CMP_MATCH | CMP_STOP;
01148    }
01149    ast_mutex_unlock(&obj->lock);
01150    return 0;
01151 }

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

00679 {
00680    SQLRETURN res;
00681 
00682    if (pmaxlen == 0) {
00683       if (SQLGetData(StatementHandle, ColumnNumber, TargetType, ast_str_buffer(*buf), 0, StrLen_or_Ind) == SQL_SUCCESS_WITH_INFO) {
00684          ast_str_make_space(buf, *StrLen_or_Ind + 1);
00685       }
00686    } else if (pmaxlen > 0) {
00687       ast_str_make_space(buf, pmaxlen);
00688    }
00689    res = SQLGetData(StatementHandle, ColumnNumber, TargetType, ast_str_buffer(*buf), ast_str_size(*buf), StrLen_or_Ind);
00690    ast_str_update(*buf);
00691 
00692    return res;
00693 }

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

01048 {
01049    return obj->parent->backslash_is_escape;
01050 }

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

Remove a cache entry from memory.

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 551 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(), odbc_class::list, and odbc_cache_tables::table.

00552 {
00553    struct odbc_cache_tables *tableptr;
00554 
00555    AST_RWLIST_WRLOCK(&odbc_tables);
00556    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&odbc_tables, tableptr, list) {
00557       if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) {
00558          AST_LIST_REMOVE_CURRENT(list);
00559          destroy_table_cache(tableptr);
00560          break;
00561       }
00562    }
00563    AST_RWLIST_TRAVERSE_SAFE_END
00564    AST_RWLIST_UNLOCK(&odbc_tables);
00565    return tableptr ? 0 : -1;
00566 }

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

References ast_log(), LOG_WARNING, odbc_obj_connect(), odbc_obj_disconnect(), odbc_obj::tx, and odbc_obj::up.

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

00569 {
00570    int attempt;
00571    SQLHSTMT stmt;
00572 
00573    for (attempt = 0; attempt < 2; attempt++) {
00574       stmt = exec_cb(obj, data);
00575 
00576       if (stmt) {
00577          break;
00578       } else if (obj->tx) {
00579          ast_log(LOG_WARNING, "Failed to execute, but unable to reconnect, as we're transactional.\n");
00580          break;
00581       } else {
00582          obj->up = 0;
00583          ast_log(LOG_WARNING, "SQL Exec Direct failed.  Attempting a reconnect...\n");
00584 
00585          odbc_obj_disconnect(obj);
00586          odbc_obj_connect(obj);
00587       }
00588    }
00589 
00590    return stmt;
00591 }

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

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

Referenced by update2_prepare(), and update_odbc().

00541 {
00542    struct odbc_cache_columns *col;
00543    AST_RWLIST_TRAVERSE(&table->columns, col, list) {
00544       if (strcasecmp(col->name, colname) == 0) {
00545          return col;
00546       }
00547    }
00548    return NULL;
00549 }

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

References ast_calloc, AST_LIST_INSERT_TAIL, ast_log(), 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_class::list, 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().

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

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

References ast_log(), ast_odbc_sanity_check(), ast_tvnow(), odbc_obj::last_used, LOG_WARNING, 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().

00594 {
00595    int res = 0, i, attempt;
00596    SQLINTEGER nativeerror=0, numfields=0;
00597    SQLSMALLINT diagbytes=0;
00598    unsigned char state[10], diagnostic[256];
00599    SQLHSTMT stmt;
00600 
00601    for (attempt = 0; attempt < 2; attempt++) {
00602       /* This prepare callback may do more than just prepare -- it may also
00603        * bind parameters, bind results, etc.  The real key, here, is that
00604        * when we disconnect, all handles become invalid for most databases.
00605        * We must therefore redo everything when we establish a new
00606        * connection. */
00607       stmt = prepare_cb(obj, data);
00608 
00609       if (stmt) {
00610          res = SQLExecute(stmt);
00611          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
00612             if (res == SQL_ERROR) {
00613                SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00614                for (i = 0; i < numfields; i++) {
00615                   SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00616                   ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00617                   if (i > 10) {
00618                      ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
00619                      break;
00620                   }
00621                }
00622             }
00623 
00624             if (obj->tx) {
00625                ast_log(LOG_WARNING, "SQL Execute error, but unable to reconnect, as we're transactional.\n");
00626                break;
00627             } else {
00628                ast_log(LOG_WARNING, "SQL Execute error %d! Attempting a reconnect...\n", res);
00629                SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00630                stmt = NULL;
00631 
00632                obj->up = 0;
00633                /*
00634                 * While this isn't the best way to try to correct an error, this won't automatically
00635                 * fail when the statement handle invalidates.
00636                 */
00637                ast_odbc_sanity_check(obj);
00638                continue;
00639             }
00640          } else {
00641             obj->last_used = ast_tvnow();
00642          }
00643          break;
00644       } else if (attempt == 0) {
00645          ast_odbc_sanity_check(obj);
00646       }
00647    }
00648 
00649    return stmt;
00650 }

void ast_odbc_release_obj ( struct odbc_obj obj  ) 

Releases an ODBC object previously allocated by odbc_request_obj().

Parameters:
obj The ODBC object

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

01042 {
01043    struct odbc_txn_frame *tx = find_transaction(NULL, obj, NULL, 0);
01044    odbc_release_obj2(obj, tx);
01045 }

struct odbc_obj* ast_odbc_request_obj ( const char *  name,
int  check 
) [read]

Definition at line 1331 of file res_odbc.c.

References ast_odbc_request_obj2(), and RES_ODBC_SANITY_CHECK.

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

01333 {
01334    struct ast_flags flags = { check ? RES_ODBC_SANITY_CHECK : 0 };
01335 #ifdef DEBUG_THREADS
01336    return _ast_odbc_request_obj2(name, flags, file, function, lineno);
01337 #else
01338    return ast_odbc_request_obj2(name, flags);
01339 #endif
01340 }

struct odbc_obj* ast_odbc_request_obj2 ( const char *  name,
struct ast_flags  flags 
) [read]

Retrieves a connected ODBC object.

Parameters:
name The name of the ODBC class for which a connection is needed.
flags Set of flags used to control which connection is returned.
Return values:
ODBC object
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 odbc_release_obj(), below.

Definition at line 1156 of file res_odbc.c.

References ao2_alloc, ao2_callback, ao2_link, ao2_ref, aoro2_class_cb(), aoro2_obj_cb(), ast_assert, ast_atomic_fetchadd_int(), ast_copy_string(), ast_log(), ast_mutex_init(), 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_obj::last_used, odbc_obj::lock, LOG_WARNING, NO_TX, odbc_class::obj_container, ODBC_FAIL, odbc_obj_connect(), odbc_obj_destructor(), odbc_obj::parent, RES_ODBC_INDEPENDENT_CONNECTION, RES_ODBC_SANITY_CHECK, USE_TX, and odbc_obj::used.

Referenced by acf_transaction_write(), and ast_odbc_request_obj().

01158 {
01159    struct odbc_obj *obj = NULL;
01160    struct odbc_class *class;
01161    SQLINTEGER nativeerror=0, numfields=0;
01162    SQLSMALLINT diagbytes=0, i;
01163    unsigned char state[10], diagnostic[256];
01164 
01165    if (!(class = ao2_callback(class_container, 0, aoro2_class_cb, (char *) name))) {
01166       return NULL;
01167    }
01168 
01169    ast_assert(ao2_ref(class, 0) > 1);
01170 
01171    if (class->haspool) {
01172       /* Recycle connections before building another */
01173       obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, EOR_TX);
01174 
01175       if (obj) {
01176          ast_assert(ao2_ref(obj, 0) > 1);
01177       }
01178 
01179       if (!obj && (class->count < class->limit)) {
01180          obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
01181          if (!obj) {
01182             ao2_ref(class, -1);
01183             return NULL;
01184          }
01185          ast_assert(ao2_ref(obj, 0) == 1);
01186          ast_mutex_init(&obj->lock);
01187          /* obj inherits the outstanding reference to class */
01188          obj->parent = class;
01189          class = NULL;
01190          if (odbc_obj_connect(obj) == ODBC_FAIL) {
01191             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
01192             ao2_ref(obj, -1);
01193             ast_assert(ao2_ref(class, 0) > 0);
01194             obj = NULL;
01195          } else {
01196             obj->used = 1;
01197             ao2_link(obj->parent->obj_container, obj);
01198             ast_atomic_fetchadd_int(&obj->parent->count, +1);
01199          }
01200       } else {
01201          /* Object is not constructed, so delete outstanding reference to class. */
01202          ao2_ref(class, -1);
01203          class = NULL;
01204       }
01205 
01206       if (obj && ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) {
01207          /* Ensure this connection has autocommit turned off. */
01208          if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) {
01209             SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01210             for (i = 0; i < numfields; i++) {
01211                SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01212                ast_log(LOG_WARNING, "SQLSetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01213                if (i > 10) {
01214                   ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01215                   break;
01216                }
01217             }
01218          }
01219       }
01220    } else if (ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) {
01221       /* Non-pooled connections -- but must use a separate connection handle */
01222       if (!(obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, USE_TX))) {
01223          obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
01224          if (!obj) {
01225             ao2_ref(class, -1);
01226             return NULL;
01227          }
01228          ast_mutex_init(&obj->lock);
01229          /* obj inherits the outstanding reference to class */
01230          obj->parent = class;
01231          class = NULL;
01232          if (odbc_obj_connect(obj) == ODBC_FAIL) {
01233             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
01234             ao2_ref(obj, -1);
01235             obj = NULL;
01236          } else {
01237             obj->used = 1;
01238             ao2_link(obj->parent->obj_container, obj);
01239             ast_atomic_fetchadd_int(&obj->parent->count, +1);
01240          }
01241       }
01242 
01243       if (obj && SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) {
01244          SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01245          for (i = 0; i < numfields; i++) {
01246             SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01247             ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01248             if (i > 10) {
01249                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01250                break;
01251             }
01252          }
01253       }
01254    } else {
01255       /* Non-pooled connection: multiple modules can use the same connection. */
01256       if ((obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, NO_TX))) {
01257          /* Object is not constructed, so delete outstanding reference to class. */
01258          ast_assert(ao2_ref(class, 0) > 1);
01259          ao2_ref(class, -1);
01260          class = NULL;
01261       } else {
01262          /* No entry: build one */
01263          if (!(obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor))) {
01264             ast_assert(ao2_ref(class, 0) > 1);
01265             ao2_ref(class, -1);
01266             return NULL;
01267          }
01268          ast_mutex_init(&obj->lock);
01269          /* obj inherits the outstanding reference to class */
01270          obj->parent = class;
01271          class = NULL;
01272          if (odbc_obj_connect(obj) == ODBC_FAIL) {
01273             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
01274             ao2_ref(obj, -1);
01275             obj = NULL;
01276          } else {
01277             ao2_link(obj->parent->obj_container, obj);
01278             ast_assert(ao2_ref(obj, 0) > 1);
01279          }
01280       }
01281 
01282       if (obj && SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) {
01283          SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01284          for (i = 0; i < numfields; i++) {
01285             SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01286             ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01287             if (i > 10) {
01288                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01289                break;
01290             }
01291          }
01292       }
01293    }
01294 
01295    /* Set the isolation property */
01296    if (obj && SQLSetConnectAttr(obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)obj->parent->isolation, 0) == SQL_ERROR) {
01297       SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01298       for (i = 0; i < numfields; i++) {
01299          SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01300          ast_log(LOG_WARNING, "SetConnectAttr (Txn isolation) returned an error: %s: %s\n", state, diagnostic);
01301          if (i > 10) {
01302             ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01303             break;
01304          }
01305       }
01306    }
01307 
01308    if (obj && ast_test_flag(&flags, RES_ODBC_SANITY_CHECK)) {
01309       ast_odbc_sanity_check(obj);
01310    } else if (obj && obj->parent->idlecheck > 0 && ast_tvdiff_sec(ast_tvnow(), obj->last_used) > obj->parent->idlecheck)
01311       odbc_obj_connect(obj);
01312 
01313 #ifdef DEBUG_THREADS
01314    if (obj) {
01315       ast_copy_string(obj->file, file, sizeof(obj->file));
01316       ast_copy_string(obj->function, function, sizeof(obj->function));
01317       obj->lineno = lineno;
01318    }
01319 #endif
01320    ast_assert(class == NULL);
01321 
01322    if (obj) {
01323       ast_assert(ao2_ref(obj, 0) > 1);
01324    }
01325    return obj;
01326 }

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 1342 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::list, odbc_class::name, odbc_txn_frame::obj, odbc_obj::parent, and txn_info.

Referenced by acf_odbc_write().

01343 {
01344    struct ast_datastore *txn_store;
01345    AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
01346    struct odbc_txn_frame *txn = NULL;
01347 
01348    if (!chan) {
01349       /* No channel == no transaction */
01350       return NULL;
01351    }
01352 
01353    ast_channel_lock(chan);
01354    if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
01355       oldlist = txn_store->data;
01356    } else {
01357       ast_channel_unlock(chan);
01358       return NULL;
01359    }
01360 
01361    AST_LIST_LOCK(oldlist);
01362    ast_channel_unlock(chan);
01363 
01364    AST_LIST_TRAVERSE(oldlist, txn, list) {
01365       if (txn->obj && txn->obj->parent && !strcmp(txn->obj->parent->name, objname)) {
01366          AST_LIST_UNLOCK(oldlist);
01367          return txn->obj;
01368       }
01369    }
01370    AST_LIST_UNLOCK(oldlist);
01371    return NULL;
01372 }

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 695 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_find_table(), ast_odbc_prepare_and_execute(), ast_odbc_request_obj2(), and handle_cli_odbc_show().

00696 {
00697    char *test_sql = "select 1";
00698    SQLHSTMT stmt;
00699    int res = 0;
00700 
00701    if (!ast_strlen_zero(obj->parent->sanitysql))
00702       test_sql = obj->parent->sanitysql;
00703 
00704    if (obj->up) {
00705       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00706       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00707          obj->up = 0;
00708       } else {
00709          res = SQLPrepare(stmt, (unsigned char *)test_sql, SQL_NTS);
00710          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00711             obj->up = 0;
00712          } else {
00713             res = SQLExecute(stmt);
00714             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00715                obj->up = 0;
00716             }
00717          }
00718       }
00719       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00720    }
00721 
00722    if (!obj->up && !obj->tx) { /* Try to reconnect! */
00723       ast_log(LOG_WARNING, "Connection is down attempting to reconnect...\n");
00724       odbc_obj_disconnect(obj);
00725       odbc_obj_connect(obj);
00726    }
00727    return obj->up;
00728 }

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

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

00653 {
00654    int res = 0, i;
00655    SQLINTEGER nativeerror=0, numfields=0;
00656    SQLSMALLINT diagbytes=0;
00657    unsigned char state[10], diagnostic[256];
00658 
00659    res = SQLExecute(stmt);
00660    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
00661       if (res == SQL_ERROR) {
00662          SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00663          for (i = 0; i < numfields; i++) {
00664             SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00665             ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00666             if (i > 10) {
00667                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
00668                break;
00669             }
00670          }
00671       }
00672    } else
00673       obj->last_used = ast_tvnow();
00674    
00675    return res;
00676 }

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

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

01053 {
01054    struct odbc_txn_frame *tx;
01055    SQLINTEGER nativeerror=0, numfields=0;
01056    SQLSMALLINT diagbytes=0, i;
01057    unsigned char state[10], diagnostic[256];
01058 
01059    if (ast_strlen_zero(data)) {
01060       tx = find_transaction(chan, NULL, NULL, 1);
01061    } else {
01062       tx = find_transaction(chan, NULL, data, 0);
01063    }
01064 
01065    pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", "OK");
01066 
01067    if (tx) {
01068       if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_COMMIT) == SQL_ERROR) {
01069          struct ast_str *errors = ast_str_thread_get(&errors_buf, 16);
01070          ast_str_reset(errors);
01071 
01072          /* Handle possible transaction commit failure */
01073          SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01074          for (i = 0; i < numfields; i++) {
01075             SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01076             ast_str_append(&errors, 0, "%s%s", ast_str_strlen(errors) ? "," : "", state);
01077             ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic);
01078             if (i > 10) {
01079                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01080                break;
01081             }
01082          }
01083          pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", ast_str_buffer(errors));
01084       }
01085    }
01086    return 0;
01087 }

static void destroy_table_cache ( struct odbc_cache_tables table  )  [static]

Definition at line 404 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, odbc_class::list, and odbc_cache_tables::table.

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

00404                                                                  {
00405    struct odbc_cache_columns *col;
00406    ast_debug(1, "Destroying table cache for %s\n", table->table);
00407    AST_RWLIST_WRLOCK(&table->columns);
00408    while ((col = AST_RWLIST_REMOVE_HEAD(&table->columns, list))) {
00409       ast_free(col);
00410    }
00411    AST_RWLIST_UNLOCK(&table->columns);
00412    AST_RWLIST_HEAD_DESTROY(&table->columns);
00413    ast_free(table);
00414 }

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 204 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, odbc_class::list, 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().

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

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

Definition at line 871 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_mutex_lock(), ast_mutex_unlock(), ast_odbc_sanity_check(), ast_strdup, 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.

00872 {
00873    struct ao2_iterator aoi = ao2_iterator_init(class_container, 0);
00874    struct odbc_class *class;
00875    struct odbc_obj *current;
00876    int length = 0;
00877    int which = 0;
00878    char *ret = NULL;
00879 
00880    switch (cmd) {
00881    case CLI_INIT:
00882       e->command = "odbc show";
00883       e->usage =
00884             "Usage: odbc show [class]\n"
00885             "       List settings of a particular ODBC class or,\n"
00886             "       if not specified, all classes.\n";
00887       return NULL;
00888    case CLI_GENERATE:
00889       if (a->pos != 2)
00890          return NULL;
00891       length = strlen(a->word);
00892       while ((class = ao2_iterator_next(&aoi))) {
00893          if (!strncasecmp(a->word, class->name, length) && ++which > a->n) {
00894             ret = ast_strdup(class->name);
00895          }
00896          ao2_ref(class, -1);
00897          if (ret) {
00898             break;
00899          }
00900       }
00901       ao2_iterator_destroy(&aoi);
00902       if (!ret && !strncasecmp(a->word, "all", length) && ++which > a->n) {
00903          ret = ast_strdup("all");
00904       }
00905       return ret;
00906    }
00907 
00908    ast_cli(a->fd, "\nODBC DSN Settings\n");
00909    ast_cli(a->fd,   "-----------------\n\n");
00910    aoi = ao2_iterator_init(class_container, 0);
00911    while ((class = ao2_iterator_next(&aoi))) {
00912       if ((a->argc == 2) || (a->argc == 3 && !strcmp(a->argv[2], "all")) || (!strcmp(a->argv[2], class->name))) {
00913          int count = 0;
00914          ast_cli(a->fd, "  Name:   %s\n  DSN:    %s\n", class->name, class->dsn);
00915 
00916          if (class->haspool) {
00917             struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
00918 
00919             ast_cli(a->fd, "  Pooled: Yes\n  Limit:  %d\n  Connections in use: %d\n", class->limit, class->count);
00920 
00921             while ((current = ao2_iterator_next(&aoi2))) {
00922                ast_mutex_lock(&current->lock);
00923 #ifdef DEBUG_THREADS
00924                ast_cli(a->fd, "    - Connection %d: %s (%s:%d %s)\n", ++count,
00925                   current->used ? "in use" :
00926                   current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected",
00927                   current->file, current->lineno, current->function);
00928 #else
00929                ast_cli(a->fd, "    - Connection %d: %s\n", ++count,
00930                   current->used ? "in use" :
00931                   current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected");
00932 #endif
00933                ast_mutex_unlock(&current->lock);
00934                ao2_ref(current, -1);
00935             }
00936             ao2_iterator_destroy(&aoi2);
00937          } else {
00938             /* Should only ever be one of these (unless there are transactions) */
00939             struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
00940             while ((current = ao2_iterator_next(&aoi2))) {
00941                ast_cli(a->fd, "  Pooled: No\n  Connected: %s\n", current->used ? "In use" :
00942                   current->up && ast_odbc_sanity_check(current) ? "Yes" : "No");
00943                ao2_ref(current, -1);
00944             }
00945             ao2_iterator_destroy(&aoi2);
00946          }
00947          ast_cli(a->fd, "\n");
00948       }
00949       ao2_ref(class, -1);
00950    }
00951    ao2_iterator_destroy(&aoi);
00952 
00953    return CLI_SUCCESS;
00954 }

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

Definition at line 170 of file res_odbc.c.

Referenced by acf_transaction_read().

00171 {
00172    if (iso == SQL_TXN_READ_COMMITTED) {
00173       return "read_committed";
00174    } else if (iso == SQL_TXN_READ_UNCOMMITTED) {
00175       return "read_uncommitted";
00176    } else if (iso == SQL_TXN_SERIALIZABLE) {
00177       return "serializable";
00178    } else if (iso == SQL_TXN_REPEATABLE_READ) {
00179       return "repeatable_read";
00180    } else {
00181       return "unknown";
00182    }
00183 }

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

Definition at line 730 of file res_odbc.c.

References ao2_alloc, ao2_container_alloc, ao2_match_by_addr, ao2_ref, ast_category_browse(), ast_config_destroy(), ast_config_load, ast_copy_string(), ast_false(), ast_log(), ast_strdup, ast_strlen_zero(), ast_true(), ast_variable_browse(), config, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEMISSING, odbc_class::dsn, enabled, odbc_class::forcecommit, odbc_class::idlecheck, odbc_class::isolation, 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().

00731 {
00732    static char *cfg = "res_odbc.conf";
00733    struct ast_config *config;
00734    struct ast_variable *v;
00735    char *cat;
00736    const char *dsn, *username, *password, *sanitysql;
00737    int enabled, pooling, limit, bse, forcecommit, isolation;
00738    unsigned int idlecheck;
00739    int preconnect = 0, res = 0;
00740    struct ast_flags config_flags = { 0 };
00741 
00742    struct odbc_class *new;
00743 
00744    config = ast_config_load(cfg, config_flags);
00745    if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
00746       ast_log(LOG_WARNING, "Unable to load config file res_odbc.conf\n");
00747       return -1;
00748    }
00749    for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) {
00750       if (!strcasecmp(cat, "ENV")) {
00751          for (v = ast_variable_browse(config, cat); v; v = v->next) {
00752             setenv(v->name, v->value, 1);
00753             ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value);
00754          }
00755       } else {
00756          /* Reset all to defaults for each class of odbc connections */
00757          dsn = username = password = sanitysql = NULL;
00758          enabled = 1;
00759          preconnect = idlecheck = 0;
00760          pooling = 0;
00761          limit = 0;
00762          bse = 1;
00763          forcecommit = 0;
00764          isolation = SQL_TXN_READ_COMMITTED;
00765          for (v = ast_variable_browse(config, cat); v; v = v->next) {
00766             if (!strcasecmp(v->name, "pooling")) {
00767                if (ast_true(v->value))
00768                   pooling = 1;
00769             } else if (!strncasecmp(v->name, "share", 5)) {
00770                /* "shareconnections" is a little clearer in meaning than "pooling" */
00771                if (ast_false(v->value))
00772                   pooling = 1;
00773             } else if (!strcasecmp(v->name, "limit")) {
00774                sscanf(v->value, "%30d", &limit);
00775                if (ast_true(v->value) && !limit) {
00776                   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);
00777                   limit = 1023;
00778                } else if (ast_false(v->value)) {
00779                   ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'.  Disabling ODBC class '%s'.\n", v->value, cat);
00780                   enabled = 0;
00781                   break;
00782                }
00783             } else if (!strcasecmp(v->name, "idlecheck")) {
00784                sscanf(v->value, "%30u", &idlecheck);
00785             } else if (!strcasecmp(v->name, "enabled")) {
00786                enabled = ast_true(v->value);
00787             } else if (!strcasecmp(v->name, "pre-connect")) {
00788                preconnect = ast_true(v->value);
00789             } else if (!strcasecmp(v->name, "dsn")) {
00790                dsn = v->value;
00791             } else if (!strcasecmp(v->name, "username")) {
00792                username = v->value;
00793             } else if (!strcasecmp(v->name, "password")) {
00794                password = v->value;
00795             } else if (!strcasecmp(v->name, "sanitysql")) {
00796                sanitysql = v->value;
00797             } else if (!strcasecmp(v->name, "backslash_is_escape")) {
00798                bse = ast_true(v->value);
00799             } else if (!strcasecmp(v->name, "forcecommit")) {
00800                forcecommit = ast_true(v->value);
00801             } else if (!strcasecmp(v->name, "isolation")) {
00802                if ((isolation = text2isolation(v->value)) == 0) {
00803                   ast_log(LOG_ERROR, "Unrecognized value for 'isolation': '%s' in section '%s'\n", v->value, cat);
00804                   isolation = SQL_TXN_READ_COMMITTED;
00805                }
00806             }
00807          }
00808 
00809          if (enabled && !ast_strlen_zero(dsn)) {
00810             new = ao2_alloc(sizeof(*new), odbc_class_destructor);
00811 
00812             if (!new) {
00813                res = -1;
00814                break;
00815             }
00816 
00817             SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env);
00818             res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
00819 
00820             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00821                ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n");
00822                ao2_ref(new, -1);
00823                return res;
00824             }
00825 
00826             new->obj_container = ao2_container_alloc(1, null_hash_fn, ao2_match_by_addr);
00827 
00828             if (pooling) {
00829                new->haspool = pooling;
00830                if (limit) {
00831                   new->limit = limit;
00832                } else {
00833                   ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless.  Changing limit from 0 to 5.\n");
00834                   new->limit = 5;
00835                }
00836             }
00837 
00838             new->backslash_is_escape = bse ? 1 : 0;
00839             new->forcecommit = forcecommit ? 1 : 0;
00840             new->isolation = isolation;
00841             new->idlecheck = idlecheck;
00842 
00843             if (cat)
00844                ast_copy_string(new->name, cat, sizeof(new->name));
00845             if (dsn)
00846                ast_copy_string(new->dsn, dsn, sizeof(new->dsn));
00847             if (username && !(new->username = ast_strdup(username))) {
00848                ao2_ref(new, -1);
00849                break;
00850             }
00851             if (password && !(new->password = ast_strdup(password))) {
00852                ao2_ref(new, -1);
00853                break;
00854             }
00855             if (sanitysql && !(new->sanitysql = ast_strdup(sanitysql))) {
00856                ao2_ref(new, -1);
00857                break;
00858             }
00859 
00860             odbc_register_class(new, preconnect);
00861             ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
00862             ao2_ref(new, -1);
00863             new = NULL;
00864          }
00865       }
00866    }
00867    ast_config_destroy(config);
00868    return res;
00869 }

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

Definition at line 339 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_class::list, odbc_txn_frame::owner, and txn_info.

Referenced by acf_transaction_write().

00340 {
00341    struct ast_datastore *txn_store;
00342    AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
00343    struct odbc_txn_frame *active = NULL, *txn;
00344 
00345    if (!chan && tx && tx->owner) {
00346       chan = tx->owner;
00347    }
00348 
00349    ast_channel_lock(chan);
00350    if (!(txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
00351       ast_channel_unlock(chan);
00352       return -1;
00353    }
00354 
00355    oldlist = txn_store->data;
00356    AST_LIST_LOCK(oldlist);
00357    AST_LIST_TRAVERSE(oldlist, txn, list) {
00358       if (txn == tx) {
00359          txn->active = 1;
00360          active = txn;
00361       } else {
00362          txn->active = 0;
00363       }
00364    }
00365    AST_LIST_UNLOCK(oldlist);
00366    ast_channel_unlock(chan);
00367    return active ? 0 : -1;
00368 }

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

Definition at line 389 of file res_odbc.c.

Referenced by load_module(), and load_odbc_config().

00390 {
00391    return 0;
00392 }

static void odbc_class_destructor ( void *  data  )  [static]

Definition at line 370 of file res_odbc.c.

References ao2_ref, and ast_free.

Referenced by load_odbc_config().

00371 {
00372    struct odbc_class *class = data;
00373    /* Due to refcounts, we can safely assume that any objects with a reference
00374     * to us will prevent our destruction, so we don't need to worry about them.
00375     */
00376    if (class->username) {
00377       ast_free(class->username);
00378    }
00379    if (class->password) {
00380       ast_free(class->password);
00381    }
00382    if (class->sanitysql) {
00383       ast_free(class->sanitysql);
00384    }
00385    ao2_ref(class->obj_container, -1);
00386    SQLFreeHandle(SQL_HANDLE_ENV, class->env);
00387 }

static odbc_status odbc_obj_connect ( struct odbc_obj obj  )  [static]

Definition at line 1411 of file res_odbc.c.

References ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_tvnow(), odbc_obj::con, odbc_class::dsn, odbc_class::env, odbc_obj::last_used, odbc_obj::lock, LOG_NOTICE, LOG_WARNING, msg, 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_direct_execute(), ast_odbc_request_obj2(), and ast_odbc_sanity_check().

01412 {
01413    int res;
01414    SQLINTEGER err;
01415    short int mlen;
01416    unsigned char msg[200], state[10];
01417 #ifdef NEEDTRACE
01418    SQLINTEGER enable = 1;
01419    char *tracefile = "/tmp/odbc.trace";
01420 #endif
01421    ast_mutex_lock(&obj->lock);
01422 
01423    if (obj->up) {
01424       odbc_obj_disconnect(obj);
01425       ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name);
01426    } else {
01427       ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name);
01428    }
01429 
01430    res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &obj->con);
01431 
01432    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01433       ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
01434       ast_mutex_unlock(&obj->lock);
01435       return ODBC_FAIL;
01436    }
01437    SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) 10, 0);
01438    SQLSetConnectAttr(obj->con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *) 10, 0);
01439 #ifdef NEEDTRACE
01440    SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
01441    SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
01442 #endif
01443 
01444    res = SQLConnect(obj->con,
01445          (SQLCHAR *) obj->parent->dsn, SQL_NTS,
01446          (SQLCHAR *) obj->parent->username, SQL_NTS,
01447          (SQLCHAR *) obj->parent->password, SQL_NTS);
01448 
01449    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01450       SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, state, &err, msg, 100, &mlen);
01451       ast_mutex_unlock(&obj->lock);
01452       ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
01453       return ODBC_FAIL;
01454    } else {
01455       ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn);
01456       obj->up = 1;
01457       obj->last_used = ast_tvnow();
01458    }
01459 
01460    ast_mutex_unlock(&obj->lock);
01461    return ODBC_SUCCESS;
01462 }

static void odbc_obj_destructor ( void *  data  )  [static]

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

00395 {
00396    struct odbc_obj *obj = data;
00397    struct odbc_class *class = obj->parent;
00398    obj->parent = NULL;
00399    odbc_obj_disconnect(obj);
00400    ast_mutex_destroy(&obj->lock);
00401    ao2_ref(class, -1);
00402 }

static odbc_status odbc_obj_disconnect ( struct odbc_obj obj  )  [static]

Definition at line 1374 of file res_odbc.c.

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

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

01375 {
01376    int res;
01377    SQLINTEGER err;
01378    short int mlen;
01379    unsigned char msg[200], state[10];
01380 
01381    /* Nothing to disconnect */
01382    if (!obj->con) {
01383       return ODBC_SUCCESS;
01384    }
01385 
01386    ast_mutex_lock(&obj->lock);
01387 
01388    res = SQLDisconnect(obj->con);
01389 
01390    if (obj->parent) {
01391       if (res == SQL_SUCCESS || res == SQL_SUCCESS_WITH_INFO) {
01392          ast_log(LOG_DEBUG, "Disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn);
01393       } else {
01394          ast_log(LOG_DEBUG, "res_odbc: %s [%s] already disconnected\n", obj->parent->name, obj->parent->dsn);
01395       }
01396    }
01397 
01398    if ((res = SQLFreeHandle(SQL_HANDLE_DBC, obj->con) == SQL_SUCCESS)) {
01399       obj->con = NULL;
01400       ast_log(LOG_DEBUG, "Database handle deallocated\n");
01401    } else {
01402       SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, state, &err, msg, 100, &mlen);
01403       ast_log(LOG_WARNING, "Unable to deallocate database handle? %d errno=%d %s\n", res, (int)err, msg);
01404    }
01405 
01406    obj->up = 0;
01407    ast_mutex_unlock(&obj->lock);
01408    return ODBC_SUCCESS;
01409 }

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

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

00961 {
00962    struct odbc_obj *obj;
00963    if (class) {
00964       ao2_link(class_container, class);
00965       /* I still have a reference in the caller, so a deref is NOT missing here. */
00966 
00967       if (preconnect) {
00968          /* Request and release builds a connection */
00969          obj = ast_odbc_request_obj(class->name, 0);
00970          if (obj) {
00971             ast_odbc_release_obj(obj);
00972          }
00973       }
00974 
00975       return 0;
00976    } else {
00977       ast_log(LOG_WARNING, "Attempted to register a NULL class?\n");
00978       return -1;
00979    }
00980 }

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

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

00983 {
00984    SQLINTEGER nativeerror=0, numfields=0;
00985    SQLSMALLINT diagbytes=0, i;
00986    unsigned char state[10], diagnostic[256];
00987 
00988    ast_debug(2, "odbc_release_obj2(%p) called (obj->txf = %p)\n", obj, obj->txf);
00989    if (tx) {
00990       ast_debug(1, "called on a transactional handle with %s\n", tx->forcecommit ? "COMMIT" : "ROLLBACK");
00991       if (SQLEndTran(SQL_HANDLE_DBC, obj->con, tx->forcecommit ? SQL_COMMIT : SQL_ROLLBACK) == SQL_ERROR) {
00992          /* Handle possible transaction commit failure */
00993          SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00994          for (i = 0; i < numfields; i++) {
00995             SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00996             ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic);
00997             if (!strcmp((char *)state, "25S02") || !strcmp((char *)state, "08007")) {
00998                /* These codes mean that a commit failed and a transaction
00999                 * is still active. We must rollback, or things will get
01000                 * very, very weird for anybody using the handle next. */
01001                SQLEndTran(SQL_HANDLE_DBC, obj->con, SQL_ROLLBACK);
01002             }
01003             if (i > 10) {
01004                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01005                break;
01006             }
01007          }
01008       }
01009 
01010       /* Transaction is done, reset autocommit */
01011       if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) {
01012          SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01013          for (i = 0; i < numfields; i++) {
01014             SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01015             ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01016             if (i > 10) {
01017                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01018                break;
01019             }
01020          }
01021       }
01022    }
01023 
01024 #ifdef DEBUG_THREADS
01025    obj->file[0] = '\0';
01026    obj->function[0] = '\0';
01027    obj->lineno = 0;
01028 #endif
01029 
01030    /* For pooled connections, this frees the connection to be
01031     * reused.  For non-pooled connections, it does nothing. */
01032    obj->used = 0;
01033    if (obj->txf) {
01034       /* Prevent recursion -- transaction is already closed out. */
01035       obj->txf->obj = NULL;
01036       obj->txf = release_transaction(obj->txf);
01037    }
01038    ao2_ref(obj, -1);
01039 }

static void odbc_txn_free ( void *  data  )  [static]

Definition at line 323 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, odbc_class::list, and release_transaction().

00324 {
00325    struct odbc_txn_frame *tx;
00326    AST_LIST_HEAD(, odbc_txn_frame) *oldlist = vdata;
00327 
00328    ast_debug(2, "odbc_txn_free(%p) called\n", vdata);
00329 
00330    AST_LIST_LOCK(oldlist);
00331    while ((tx = AST_LIST_REMOVE_HEAD(oldlist, list))) {
00332       release_transaction(tx);
00333    }
00334    AST_LIST_UNLOCK(oldlist);
00335    AST_LIST_HEAD_DESTROY(oldlist);
00336    ast_free(oldlist);
00337 }

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

Definition at line 287 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_class::list, 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().

00288 {
00289    if (!tx) {
00290       return NULL;
00291    }
00292 
00293    ast_debug(2, "release_transaction(%p) called (tx->obj = %p, tx->obj->txf = %p)\n", tx, tx->obj, tx->obj ? tx->obj->txf : NULL);
00294 
00295    /* If we have an owner, disassociate */
00296    if (tx->owner) {
00297       struct ast_datastore *txn_store;
00298       AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
00299 
00300       ast_channel_lock(tx->owner);
00301       if ((txn_store = ast_channel_datastore_find(tx->owner, &txn_info, NULL))) {
00302          oldlist = txn_store->data;
00303          AST_LIST_LOCK(oldlist);
00304          AST_LIST_REMOVE(oldlist, tx, list);
00305          AST_LIST_UNLOCK(oldlist);
00306       }
00307       ast_channel_unlock(tx->owner);
00308       tx->owner = NULL;
00309    }
00310 
00311    if (tx->obj) {
00312       /* If we have any uncommitted transactions, they are handled when we release the object */
00313       struct odbc_obj *obj = tx->obj;
00314       /* Prevent recursion during destruction */
00315       tx->obj->txf = NULL;
00316       tx->obj = NULL;
00317       odbc_release_obj2(obj, tx);
00318    }
00319    ast_free(tx);
00320    return NULL;
00321 }

static int reload ( void   )  [static]

Definition at line 1604 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(), odbc_class::list, load_odbc_config(), and table.

01605 {
01606    struct odbc_cache_tables *table;
01607    struct odbc_class *class;
01608    struct odbc_obj *current;
01609    struct ao2_iterator aoi = ao2_iterator_init(class_container, 0);
01610 
01611    /* First, mark all to be purged */
01612    while ((class = ao2_iterator_next(&aoi))) {
01613       class->delme = 1;
01614       ao2_ref(class, -1);
01615    }
01616    ao2_iterator_destroy(&aoi);
01617 
01618    load_odbc_config();
01619 
01620    /* Purge remaining classes */
01621 
01622    /* Note on how this works; this is a case of circular references, so we
01623     * explicitly do NOT want to use a callback here (or we wind up in
01624     * recursive hell).
01625     *
01626     * 1. Iterate through all the classes.  Note that the classes will currently
01627     * contain two classes of the same name, one of which is marked delme and
01628     * will be purged when all remaining objects of the class are released, and
01629     * the other, which was created above when we re-parsed the config file.
01630     * 2. On each class, there is a reference held by the master container and
01631     * a reference held by each connection object.  There are two cases for
01632     * destruction of the class, noted below.  However, in all cases, all O-refs
01633     * (references to objects) will first be freed, which will cause the C-refs
01634     * (references to classes) to be decremented (but never to 0, because the
01635     * class container still has a reference).
01636     *    a) If the class has outstanding objects, the C-ref by the class
01637     *    container will then be freed, which leaves only C-refs by any
01638     *    outstanding objects.  When the final outstanding object is released
01639     *    (O-refs held by applications and dialplan functions), it will in turn
01640     *    free the final C-ref, causing class destruction.
01641     *    b) If the class has no outstanding objects, when the class container
01642     *    removes the final C-ref, the class will be destroyed.
01643     */
01644    aoi = ao2_iterator_init(class_container, 0);
01645    while ((class = ao2_iterator_next(&aoi))) { /* C-ref++ (by iterator) */
01646       if (class->delme) {
01647          struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
01648          while ((current = ao2_iterator_next(&aoi2))) { /* O-ref++ (by iterator) */
01649             ao2_unlink(class->obj_container, current); /* unlink O-ref from class (reference handled implicitly) */
01650             ao2_ref(current, -1); /* O-ref-- (by iterator) */
01651             /* At this point, either
01652              * a) there's an outstanding O-ref, or
01653              * b) the object has already been destroyed.
01654              */
01655          }
01656          ao2_iterator_destroy(&aoi2);
01657          ao2_unlink(class_container, class); /* unlink C-ref from container (reference handled implicitly) */
01658          /* At this point, either
01659           * a) there's an outstanding O-ref, which holds an outstanding C-ref, or
01660           * b) the last remaining C-ref is held by the iterator, which will be
01661           * destroyed in the next step.
01662           */
01663       }
01664       ao2_ref(class, -1); /* C-ref-- (by iterator) */
01665    }
01666    ao2_iterator_destroy(&aoi);
01667 
01668    /* Empty the cache; it will get rebuilt the next time the tables are needed. */
01669    AST_RWLIST_WRLOCK(&odbc_tables);
01670    while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
01671       destroy_table_cache(table);
01672    }
01673    AST_RWLIST_UNLOCK(&odbc_tables);
01674 
01675    return 0;
01676 }

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

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

01090 {
01091    struct odbc_txn_frame *tx;
01092    SQLINTEGER nativeerror=0, numfields=0;
01093    SQLSMALLINT diagbytes=0, i;
01094    unsigned char state[10], diagnostic[256];
01095 
01096    if (ast_strlen_zero(data)) {
01097       tx = find_transaction(chan, NULL, NULL, 1);
01098    } else {
01099       tx = find_transaction(chan, NULL, data, 0);
01100    }
01101 
01102    pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", "OK");
01103 
01104    if (tx) {
01105       if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_ROLLBACK) == SQL_ERROR) {
01106          struct ast_str *errors = ast_str_thread_get(&errors_buf, 16);
01107          ast_str_reset(errors);
01108 
01109          /* Handle possible transaction commit failure */
01110          SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01111          for (i = 0; i < numfields; i++) {
01112             SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01113             ast_str_append(&errors, 0, "%s%s", ast_str_strlen(errors) ? "," : "", state);
01114             ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic);
01115             if (i > 10) {
01116                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01117                break;
01118             }
01119          }
01120          pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", ast_str_buffer(errors));
01121       }
01122    }
01123    return 0;
01124 }

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

Definition at line 185 of file res_odbc.c.

Referenced by acf_transaction_write(), and load_odbc_config().

00186 {
00187    if (strncasecmp(txt, "read_", 5) == 0) {
00188       if (strncasecmp(txt + 5, "c", 1) == 0) {
00189          return SQL_TXN_READ_COMMITTED;
00190       } else if (strncasecmp(txt + 5, "u", 1) == 0) {
00191          return SQL_TXN_READ_UNCOMMITTED;
00192       } else {
00193          return 0;
00194       }
00195    } else if (strncasecmp(txt, "ser", 3) == 0) {
00196       return SQL_TXN_SERIALIZABLE;
00197    } else if (strncasecmp(txt, "rep", 3) == 0) {
00198       return SQL_TXN_REPEATABLE_READ;
00199    } else {
00200       return 0;
00201    }
00202 }

static int unload_module ( void   )  [static]

Definition at line 1678 of file res_odbc.c.

01679 {
01680    /* Prohibit unloading */
01681    return -1;
01682 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS , .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 = "0901e4e500243c855563a2d78b0c03e4" , .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 1702 of file res_odbc.c.

const char* app_commit = "ODBC_Commit" [static]

Definition at line 1601 of file res_odbc.c.

Referenced by load_module().

const char* app_rollback = "ODBC_Rollback" [static]

Definition at line 1602 of file res_odbc.c.

Referenced by load_module().

Definition at line 1702 of file res_odbc.c.

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

Definition at line 956 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 146 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 1595 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 148 of file res_odbc.c.

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


Generated by  doxygen 1.6.2