Tue Aug 20 16:35:14 2013

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  _columns
struct  odbc_cache_columns
 These structures are used for adaptive capabilities. More...
struct  odbc_cache_tables
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

struct odbc_obj_ast_odbc_request_obj (const char *name, int check, const char *file, const char *function, int lineno)
struct odbc_obj_ast_odbc_request_obj2 (const char *name, struct ast_flags flags, const char *file, const char *function, int lineno)
 Retrieves a connected ODBC object.
SQLRETURN ast_odbc_ast_str_SQLGetData (struct ast_str **buf, int pmaxlen, SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, SQLLEN *StrLen_or_Ind)
 Wrapper for SQLGetData to use with dynamic strings.
int ast_odbc_backslash_is_escape (struct odbc_obj *obj)
 Checks if the database natively supports backslash as an escape character.
int ast_odbc_clear_cache (const char *database, const char *tablename)
 Remove a cache entry from memory This function may be called to clear entries created and cached by the ast_odbc_find_table() API call.
SQLHSTMT ast_odbc_direct_execute (struct odbc_obj *obj, SQLHSTMT(*exec_cb)(struct odbc_obj *obj, void *data), void *data)
 Executes an non prepared statement and returns the resulting statement handle.
struct odbc_cache_columnsast_odbc_find_column (struct odbc_cache_tables *table, const char *colname)
 Find a column entry within a cached table structure.
struct odbc_cache_tablesast_odbc_find_table (const char *database, const char *tablename)
 Find or create an entry describing the table specified.
SQLHSTMT ast_odbc_prepare_and_execute (struct odbc_obj *obj, SQLHSTMT(*prepare_cb)(struct odbc_obj *obj, void *data), void *data)
 Prepares, executes, and returns the resulting statement handle.
void ast_odbc_release_obj (struct odbc_obj *obj)
 Releases an ODBC object previously allocated by ast_odbc_request_obj().
struct odbc_objast_odbc_retrieve_transaction_obj (struct ast_channel *chan, const char *objname)
 Retrieve a stored ODBC object, if a transaction has been started.
int ast_odbc_sanity_check (struct odbc_obj *obj)
 Checks an ODBC object to ensure it is still connected.
int ast_odbc_smart_execute (struct odbc_obj *obj, SQLHSTMT stmt)
 Executes a prepared statement handle.

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__)
#define ast_odbc_request_obj2 ( a,
 )     _ast_odbc_request_obj2(a, b, __FILE__, __PRETTY_FUNCTION__, __LINE__)

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 };

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 
) [read]

Definition at line 1440 of file res_odbc.c.

References _ast_odbc_request_obj2(), and RES_ODBC_SANITY_CHECK.

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

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

Retrieves a connected ODBC object.

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

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

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

Definition at line 1231 of file res_odbc.c.

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

Referenced by _ast_odbc_request_obj().

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

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

Wrapper for SQLGetData to use with dynamic strings.

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

Definition at line 717 of file res_odbc.c.

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

Referenced by acf_odbc_read(), and cli_odbc_read().

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

int ast_odbc_backslash_is_escape ( struct odbc_obj obj  ) 

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

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

Definition at line 1113 of file res_odbc.c.

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

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

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

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

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

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

Definition at line 576 of file res_odbc.c.

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

Referenced by unload_odbc().

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

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

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

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

Definition at line 593 of file res_odbc.c.

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

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

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

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

Find a column entry within a cached table structure.

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

Definition at line 565 of file res_odbc.c.

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

Referenced by update2_prepare(), and update_odbc().

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

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

Find or create an entry describing the table specified.

Parameters:
database Name of an ODBC class on which to query the table
tablename Tablename to describe
Return values:
A structure describing the table layout, or NULL, if the table is not found or another error occurs. When a structure is returned, the contained columns list will be rdlock'ed, to ensure that it will be retained in memory. The information will be cached until a reload event or when ast_odbc_clear_cache() is called with the relevant parameters.
Since:
1.6.1
Parameters:
database Name of an ODBC class on which to query the table
tablename Tablename to describe
Return values:
A structure describing the table layout, or NULL, if the table is not found or another error occurs. When a structure is returned, the contained columns list will be rdlock'ed, to ensure that it will be retained in memory.
Since:
1.6.1

Definition at line 448 of file res_odbc.c.

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

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

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

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

Prepares, executes, and returns the resulting statement handle.

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

Definition at line 621 of file res_odbc.c.

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

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

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

void ast_odbc_release_obj ( struct odbc_obj obj  ) 

Releases an ODBC object previously allocated by ast_odbc_request_obj().

Parameters:
obj The ODBC object

Definition at line 1107 of file res_odbc.c.

References find_transaction(), and odbc_release_obj2().

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

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

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

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

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

Definition at line 1446 of file res_odbc.c.

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

Referenced by acf_odbc_write().

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

int ast_odbc_sanity_check ( struct odbc_obj obj  ) 

Checks an ODBC object to ensure it is still connected.

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

Definition at line 734 of file res_odbc.c.

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

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

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

int ast_odbc_smart_execute ( struct odbc_obj obj,
SQLHSTMT  stmt 
)

Executes a prepared statement handle.

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

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

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

Definition at line 686 of file res_odbc.c.

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

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


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