Sat Mar 10 01:55:42 2012

Asterisk developer's documentation


res_odbc.h File Reference

ODBC resource manager. More...

#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>
#include "asterisk/linkedlists.h"
#include "asterisk/strings.h"

Go to the source code of this file.

Data Structures

struct  odbc_cache_columns
 These structures are used for adaptive capabilities. More...
struct  odbc_cache_tables
struct  odbc_cache_tables::_columns
struct  odbc_obj
 ODBC container. More...

Defines

#define ast_odbc_release_table(ptr)   if (ptr) { AST_RWLIST_UNLOCK(&(ptr)->columns); }
 Release a table returned from ast_odbc_find_table.
#define ast_odbc_request_obj(a, b)   _ast_odbc_request_obj(a, b, __FILE__, __PRETTY_FUNCTION__, __LINE__)
#define ast_odbc_request_obj2(a, b)   _ast_odbc_request_obj2(a, b, __FILE__, __PRETTY_FUNCTION__, __LINE__)

Enumerations

enum  { RES_ODBC_SANITY_CHECK = (1 << 0), RES_ODBC_INDEPENDENT_CONNECTION = (1 << 1), RES_ODBC_CONNECTED = (1 << 2) }
 Flags for use with. More...
enum  odbc_status { ODBC_SUCCESS = 0, ODBC_FAIL = -1 }

Functions

odbc_obj_ast_odbc_request_obj (const char *name, int check, const char *file, const char *function, int lineno)
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.
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.
odbc_cache_columnsast_odbc_find_column (struct odbc_cache_tables *table, const char *colname)
 Find a column entry within a cached table structure.
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().
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.


Detailed Description

ODBC resource manager.

Definition in file res_odbc.h.


Define Documentation

#define ast_odbc_release_table ( ptr   )     if (ptr) { AST_RWLIST_UNLOCK(&(ptr)->columns); }

Release a table returned from ast_odbc_find_table.

Definition at line 213 of file res_odbc.h.

Referenced by update2_prepare(), and update_odbc().

#define ast_odbc_request_obj ( a,
 )     _ast_odbc_request_obj(a, b, __FILE__, __PRETTY_FUNCTION__, __LINE__)

Definition at line 123 of file res_odbc.h.

Referenced by acf_odbc_read(), acf_odbc_write(), ast_odbc_find_table(), load_config(), odbc_log(), odbc_register_class(), and update2_odbc().

#define ast_odbc_request_obj2 ( a,
 )     _ast_odbc_request_obj2(a, b, __FILE__, __PRETTY_FUNCTION__, __LINE__)

Definition at line 122 of file res_odbc.h.

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


Enumeration Type Documentation

anonymous enum

Flags for use with.

See also:
ast_odbc_request_obj2
Enumerator:
RES_ODBC_SANITY_CHECK 
RES_ODBC_INDEPENDENT_CONNECTION 
RES_ODBC_CONNECTED 

Definition at line 39 of file res_odbc.h.

00039      {
00040    RES_ODBC_SANITY_CHECK = (1 << 0),
00041    RES_ODBC_INDEPENDENT_CONNECTION = (1 << 1),
00042    RES_ODBC_CONNECTED = (1 << 2),
00043 };

enum odbc_status

Enumerator:
ODBC_SUCCESS 
ODBC_FAIL 

Definition at line 36 of file res_odbc.h.


Function Documentation

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

Definition at line 1390 of file res_odbc.c.

References _ast_odbc_request_obj2(), ast_flags::flags, and RES_ODBC_SANITY_CHECK.

01391 {
01392    struct ast_flags flags = { check ? RES_ODBC_SANITY_CHECK : 0 };
01393    return _ast_odbc_request_obj2(name, flags, file, function, lineno);
01394 }

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.

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 1201 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_debug, ast_log(), ast_mutex_init, ast_test_flag, class_container, odbc_obj::con, EOR_TX, LOG_WARNING, ODBC_FAIL, odbc_obj_connect(), odbc_obj_destructor(), RES_ODBC_INDEPENDENT_CONNECTION, and USE_TX.

Referenced by _ast_odbc_request_obj().

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

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

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

00700 {
00701    SQLRETURN res;
00702 
00703    if (pmaxlen == 0) {
00704       if (SQLGetData(StatementHandle, ColumnNumber, TargetType, ast_str_buffer(*buf), 0, StrLen_or_Ind) == SQL_SUCCESS_WITH_INFO) {
00705          ast_str_make_space(buf, *StrLen_or_Ind + 1);
00706       }
00707    } else if (pmaxlen > 0) {
00708       ast_str_make_space(buf, pmaxlen);
00709    }
00710    res = SQLGetData(StatementHandle, ColumnNumber, TargetType, ast_str_buffer(*buf), ast_str_size(*buf), StrLen_or_Ind);
00711    ast_str_update(*buf);
00712 
00713    return res;
00714 }

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

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

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

01096 {
01097    return obj->parent->backslash_is_escape;
01098 }

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

Referenced by unload_odbc().

00571 {
00572    struct odbc_cache_tables *tableptr;
00573 
00574    AST_RWLIST_WRLOCK(&odbc_tables);
00575    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&odbc_tables, tableptr, list) {
00576       if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) {
00577          AST_LIST_REMOVE_CURRENT(list);
00578          destroy_table_cache(tableptr);
00579          break;
00580       }
00581    }
00582    AST_RWLIST_TRAVERSE_SAFE_END
00583    AST_RWLIST_UNLOCK(&odbc_tables);
00584    return tableptr ? 0 : -1;
00585 }

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

References ast_log(), ast_odbc_sanity_check(), and LOG_WARNING.

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

00588 {
00589    int attempt;
00590    SQLHSTMT stmt;
00591 
00592    for (attempt = 0; attempt < 2; attempt++) {
00593       stmt = exec_cb(obj, data);
00594 
00595       if (stmt) {
00596          break;
00597       } else if (obj->tx) {
00598          ast_log(LOG_WARNING, "Failed to execute, but unable to reconnect, as we're transactional.\n");
00599          break;
00600       } else if (attempt == 0) {
00601          ast_log(LOG_WARNING, "SQL Execute error! Verifying connection to %s [%s]...\n", obj->parent->name, obj->parent->dsn);
00602       }
00603       if (!ast_odbc_sanity_check(obj)) {
00604          break;
00605       }
00606    }
00607 
00608    return stmt;
00609 }

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

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

References AST_RWLIST_TRAVERSE, odbc_class::list, odbc_cache_columns::name, and table.

Referenced by update2_prepare(), and update_odbc().

00560 {
00561    struct odbc_cache_columns *col;
00562    AST_RWLIST_TRAVERSE(&table->columns, col, list) {
00563       if (strcasecmp(col->name, colname) == 0) {
00564          return col;
00565       }
00566    }
00567    return NULL;
00568 }

struct odbc_cache_tables* ast_odbc_find_table ( const char *  database,
const char *  tablename 
)

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 444 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, destroy_table_cache(), odbc_class::list, LOG_ERROR, LOG_WARNING, and odbc_cache_tables::table.

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

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

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

References ast_log(), ast_odbc_sanity_check(), ast_tvnow(), and LOG_WARNING.

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

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

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

References find_transaction(), odbc_txn_frame::obj, and odbc_release_obj2().

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

01090 {
01091    struct odbc_txn_frame *tx = find_transaction(NULL, obj, NULL, 0);
01092    odbc_release_obj2(obj, tx);
01093 }

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

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

Referenced by acf_odbc_write().

01397 {
01398    struct ast_datastore *txn_store;
01399    AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
01400    struct odbc_txn_frame *txn = NULL;
01401 
01402    if (!chan) {
01403       /* No channel == no transaction */
01404       return NULL;
01405    }
01406 
01407    ast_channel_lock(chan);
01408    if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
01409       oldlist = txn_store->data;
01410    } else {
01411       ast_channel_unlock(chan);
01412       return NULL;
01413    }
01414 
01415    AST_LIST_LOCK(oldlist);
01416    ast_channel_unlock(chan);
01417 
01418    AST_LIST_TRAVERSE(oldlist, txn, list) {
01419       if (txn->obj && txn->obj->parent && !strcmp(txn->obj->parent->name, objname)) {
01420          AST_LIST_UNLOCK(oldlist);
01421          return txn->obj;
01422       }
01423    }
01424    AST_LIST_UNLOCK(oldlist);
01425    return NULL;
01426 }

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 716 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_direct_execute(), ast_odbc_find_table(), ast_odbc_prepare_and_execute(), data_odbc_provider_handler(), and handle_cli_odbc_show().

00717 {
00718    char *test_sql = "select 1";
00719    SQLHSTMT stmt;
00720    int res = 0;
00721 
00722    if (!ast_strlen_zero(obj->parent->sanitysql))
00723       test_sql = obj->parent->sanitysql;
00724 
00725    if (obj->up) {
00726       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00727       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00728          obj->up = 0;
00729       } else {
00730          res = SQLPrepare(stmt, (unsigned char *)test_sql, SQL_NTS);
00731          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00732             obj->up = 0;
00733          } else {
00734             res = SQLExecute(stmt);
00735             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00736                obj->up = 0;
00737             }
00738          }
00739       }
00740       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00741    }
00742 
00743    if (!obj->up && !obj->tx) { /* Try to reconnect! */
00744       ast_log(LOG_WARNING, "Connection is down attempting to reconnect...\n");
00745       odbc_obj_disconnect(obj);
00746       odbc_obj_connect(obj);
00747    }
00748    return obj->up;
00749 }

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

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

00673 {
00674    int res = 0, i;
00675    SQLINTEGER nativeerror=0, numfields=0;
00676    SQLSMALLINT diagbytes=0;
00677    unsigned char state[10], diagnostic[256];
00678 
00679    res = SQLExecute(stmt);
00680    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
00681       if (res == SQL_ERROR) {
00682          SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00683          for (i = 0; i < numfields; i++) {
00684             SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00685             ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00686             if (i > 10) {
00687                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
00688                break;
00689             }
00690          }
00691       }
00692    } else {
00693       obj->last_used = ast_tvnow();
00694    }
00695 
00696    return res;
00697 }


Generated on Sat Mar 10 01:55:42 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7