#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>
#include "asterisk/linkedlists.h"
#include "asterisk/strings.h"
Go to the source code of this file.
Data Structures | |
struct | odbc_cache_columns |
These structures are used for adaptive capabilities. More... | |
struct | odbc_cache_tables |
struct | odbc_cache_tables::_columns |
struct | odbc_obj |
ODBC container. More... | |
Defines | |
#define | ast_odbc_release_table(ptr) if (ptr) { AST_RWLIST_UNLOCK(&(ptr)->columns); } |
Release a table returned from ast_odbc_find_table. | |
#define | ast_odbc_request_obj(a, b) _ast_odbc_request_obj(a, b, __FILE__, __PRETTY_FUNCTION__, __LINE__) |
#define | ast_odbc_request_obj2(a, b) _ast_odbc_request_obj2(a, b, __FILE__, __PRETTY_FUNCTION__, __LINE__) |
Enumerations | |
enum | { RES_ODBC_SANITY_CHECK = (1 << 0), RES_ODBC_INDEPENDENT_CONNECTION = (1 << 1), RES_ODBC_CONNECTED = (1 << 2) } |
Flags for use with. More... | |
enum | odbc_status { ODBC_SUCCESS = 0, ODBC_FAIL = -1 } |
Functions | |
odbc_obj * | _ast_odbc_request_obj (const char *name, int check, const char *file, const char *function, int lineno) |
odbc_obj * | _ast_odbc_request_obj2 (const char *name, struct ast_flags flags, const char *file, const char *function, int lineno) |
Retrieves a connected ODBC object. | |
SQLRETURN | ast_odbc_ast_str_SQLGetData (struct ast_str **buf, int pmaxlen, SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, SQLLEN *StrLen_or_Ind) |
Wrapper for SQLGetData to use with dynamic strings. | |
int | ast_odbc_backslash_is_escape (struct odbc_obj *obj) |
Checks if the database natively supports backslash as an escape character. | |
int | ast_odbc_clear_cache (const char *database, const char *tablename) |
Remove a cache entry from memory This function may be called to clear entries created and cached by the ast_odbc_find_table() API call. | |
SQLHSTMT | ast_odbc_direct_execute (struct odbc_obj *obj, SQLHSTMT(*exec_cb)(struct odbc_obj *obj, void *data), void *data) |
Executes an non prepared statement and returns the resulting statement handle. | |
odbc_cache_columns * | ast_odbc_find_column (struct odbc_cache_tables *table, const char *colname) |
Find a column entry within a cached table structure. | |
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(). | |
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. |
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(), 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 |
struct odbc_obj* _ast_odbc_request_obj | ( | const char * | name, | |
int | check, | |||
const char * | file, | |||
const char * | function, | |||
int | lineno | |||
) |
Definition at line 1440 of file res_odbc.c.
References _ast_odbc_request_obj2(), ast_flags::flags, 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 | |||
) |
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. |
Definition at line 1231 of file res_odbc.c.
References ao2_alloc, ao2_callback, ao2_link, ao2_ref, aoro2_class_cb(), aoro2_obj_cb(), ast_assert, ast_atomic_fetchadd_int(), ast_debug, ast_log(), ast_mutex_init, ast_mutex_lock, ast_test_flag, class_container, odbc_obj::con, EOR_TX, odbc_obj::lock, LOG_WARNING, ODBC_FAIL, odbc_obj_connect(), odbc_obj_destructor(), RES_ODBC_INDEPENDENT_CONNECTION, and USE_TX.
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.
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().
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.
obj | The ODBC object |
Definition at line 1113 of file res_odbc.c.
References odbc_class::backslash_is_escape, odbc_txn_frame::obj, and odbc_obj::parent.
Referenced by odbc_log(), realtime_multi_odbc(), and realtime_odbc().
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.
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 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(), odbc_class::list, 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.
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 593 of file res_odbc.c.
References ast_log(), ast_mutex_lock, ast_odbc_sanity_check(), and LOG_WARNING.
Referenced by acf_odbc_read(), acf_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 | |||
) |
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 565 of file res_odbc.c.
References AST_RWLIST_TRAVERSE, odbc_class::list, odbc_cache_columns::name, and table.
Referenced by update2_prepare(), and update_odbc().
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 | |||
) |
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. |
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, destroy_table_cache(), odbc_class::list, odbc_obj::lock, LOG_ERROR, LOG_WARNING, and odbc_cache_tables::table.
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.
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 621 of file res_odbc.c.
References ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_odbc_sanity_check(), ast_tvnow(), and LOG_WARNING.
Referenced by config_odbc(), destroy_odbc(), odbc_log(), realtime_multi_odbc(), realtime_odbc(), store_odbc(), update2_odbc(), and update_odbc().
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().
obj | The ODBC object |
Definition at line 1107 of file res_odbc.c.
References find_transaction(), odbc_txn_frame::obj, and odbc_release_obj2().
Referenced by acf_odbc_read(), acf_odbc_write(), ast_odbc_find_table(), config_odbc(), destroy_odbc(), load_config(), odbc_log(), odbc_register_class(), realtime_multi_odbc(), realtime_odbc(), store_odbc(), update2_odbc(), and update_odbc().
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 | |||
) |
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 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_txn_frame::list, 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.
obj | The ODBC object |
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_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.
obj | The non-NULL result of odbc_request_obj() | |
stmt | The prepared statement handle |
0 | on success | |
-1 | on failure |
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 }