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_columns * | ast_odbc_find_column (struct odbc_cache_tables *table, const char *colname) |
Find a column entry within a cached table structure. | |
struct odbc_cache_tables * | ast_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_obj * | ast_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. |
ODBC resource manager.
Definition in file res_odbc.h.
#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, | |||
b | ) | _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(), cli_odbc_read(), cli_odbc_write(), load_config(), odbc_log(), odbc_register_class(), and update2_odbc().
#define ast_odbc_request_obj2 | ( | a, | |||
b | ) | _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().
anonymous enum |
Flags for use with.
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 |
Definition at line 36 of file res_odbc.h.
00036 { ODBC_SUCCESS=0, ODBC_FAIL=-1} odbc_status;
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.
name | The name of the ODBC class for which a connection is needed. | |
flags | One or more of the following flags:
|
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).
obj | The ODBC object |
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.
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.
obj | The ODBC object |
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.
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 |
0 | if the cache entry was removed, or -1 if no matching entry was found. |
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.
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. |
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.
table | Cached table structure, as returned from ast_odbc_find_table() | |
colname | The column name requested |
A | structure describing the column type, or NULL, if the column is not found. |
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.
database | Name of an ODBC class on which to query the table | |
tablename | Tablename to describe |
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. |
database | Name of an ODBC class on which to query the table | |
tablename | Tablename to describe |
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. |
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.
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. |
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().
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.
chan | Channel associated with the transaction. | |
objname | Name of the database handle. This name corresponds to the name passed to |
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.
obj | The ODBC object |
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.
obj | The non-NULL result of odbc_request_obj() | |
stmt | The prepared statement handle |
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 }