Wed Aug 7 17:16:11 2019

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

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

References _ast_odbc_request_obj2(), and RES_ODBC_SANITY_CHECK.

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

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

Retrieves a connected ODBC object.

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

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

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

Definition at line 1241 of file res_odbc.c.

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

Referenced by _ast_odbc_request_obj().

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

int ast_odbc_allow_empty_string_in_nontext ( struct odbc_obj obj  ) 

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

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

Definition at line 1123 of file res_odbc.c.

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

Referenced by update_odbc().

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

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

Wrapper for SQLGetData to use with dynamic strings.

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

Definition at line 718 of file res_odbc.c.

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

Referenced by acf_odbc_read(), and cli_odbc_read().

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

int ast_odbc_backslash_is_escape ( struct odbc_obj obj  ) 

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

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

Definition at line 1118 of file res_odbc.c.

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

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

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

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

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

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

Definition at line 577 of file res_odbc.c.

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

Referenced by unload_odbc().

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

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

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

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

Definition at line 594 of file res_odbc.c.

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

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

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

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

Find a column entry within a cached table structure.

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

Definition at line 566 of file res_odbc.c.

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

Referenced by update2_prepare(), and update_odbc().

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

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

Find or create an entry describing the table specified.

Parameters:
database Name of an ODBC class on which to query the table
tablename Tablename to describe
Return values:
A structure describing the table layout, or NULL, if the table is not found or another error occurs. When a structure is returned, the contained columns list will be rdlock'ed, to ensure that it will be retained in memory. 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 449 of file res_odbc.c.

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

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

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

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

Prepares, executes, and returns the resulting statement handle.

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

Definition at line 622 of file res_odbc.c.

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

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

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

void ast_odbc_release_obj ( struct odbc_obj obj  ) 

Releases an ODBC object previously allocated by ast_odbc_request_obj().

Parameters:
obj The ODBC object

Definition at line 1112 of file res_odbc.c.

References find_transaction(), and odbc_release_obj2().

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

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

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

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

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

Definition at line 1456 of file res_odbc.c.

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

Referenced by acf_odbc_write().

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

int ast_odbc_sanity_check ( struct odbc_obj obj  ) 

Checks an ODBC object to ensure it is still connected.

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

Definition at line 735 of file res_odbc.c.

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

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

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

int ast_odbc_smart_execute ( struct odbc_obj obj,
SQLHSTMT  stmt 
)

Executes a prepared statement handle.

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

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

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

Definition at line 687 of file res_odbc.c.

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

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


Generated on 7 Aug 2019 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1