Wed Apr 6 11:30:09 2011

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

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

01383 {
01384    struct ast_flags flags = { check ? RES_ODBC_SANITY_CHECK : 0 };
01385    return _ast_odbc_request_obj2(name, flags, file, function, lineno);
01386 }

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

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

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

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

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

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

01094 {
01095    return obj->parent->backslash_is_escape;
01096 }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


Generated on Wed Apr 6 11:30:09 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7