ODBC resource manager. More...
#include "asterisk.h"#include "asterisk/file.h"#include "asterisk/channel.h"#include "asterisk/config.h"#include "asterisk/pbx.h"#include "asterisk/module.h"#include "asterisk/cli.h"#include "asterisk/lock.h"#include "asterisk/res_odbc.h"#include "asterisk/time.h"#include "asterisk/astobj2.h"#include "asterisk/app.h"#include "asterisk/strings.h"#include "asterisk/threadstorage.h"
Go to the source code of this file.
Data Structures | |
| struct | odbc_class |
| struct | odbc_tables |
| struct | odbc_txn_frame |
Defines | |
| #define | EOR_TX (void *)(long)3 |
| #define | NO_TX (void *)(long)2 |
| #define | USE_TX (void *)(long)1 |
Functions | |
| static void | __init_errors_buf (void) |
| static void | __reg_module (void) |
| static void | __unreg_module (void) |
| static int | acf_transaction_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
| static int | acf_transaction_write (struct ast_channel *chan, const char *cmd, char *s, const char *value) |
| static int | aoro2_class_cb (void *obj, void *arg, int flags) |
| static int | aoro2_obj_cb (void *vobj, void *arg, int flags) |
| 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. | |
| 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 odbc_request_obj(). | |
| struct odbc_obj * | ast_odbc_request_obj (const char *name, int check) |
| struct odbc_obj * | ast_odbc_request_obj2 (const char *name, struct ast_flags flags) |
| Retrieves a connected ODBC object. | |
| 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. | |
| static int | commit_exec (struct ast_channel *chan, void *data) |
| static void | destroy_table_cache (struct odbc_cache_tables *table) |
| static struct odbc_txn_frame * | find_transaction (struct ast_channel *chan, struct odbc_obj *obj, const char *name, int active) |
| static char * | handle_cli_odbc_show (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
| static const char * | isolation2text (int iso) |
| static int | load_module (void) |
| static int | load_odbc_config (void) |
| static int | mark_transaction_active (struct ast_channel *chan, struct odbc_txn_frame *tx) |
| static int | null_hash_fn (const void *obj, const int flags) |
| static void | odbc_class_destructor (void *data) |
| static odbc_status | odbc_obj_connect (struct odbc_obj *obj) |
| static void | odbc_obj_destructor (void *data) |
| static odbc_status | odbc_obj_disconnect (struct odbc_obj *obj) |
| static int | odbc_register_class (struct odbc_class *class, int connect) |
| static void | odbc_release_obj2 (struct odbc_obj *obj, struct odbc_txn_frame *tx) |
| static void | odbc_txn_free (void *data) |
| static struct odbc_txn_frame * | release_transaction (struct odbc_txn_frame *tx) |
| static int | reload (void) |
| static int | rollback_exec (struct ast_channel *chan, void *data) |
| static int | text2isolation (const char *txt) |
| static int | unload_module (void) |
Variables | |
| static struct ast_module_info | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS , .description = "ODBC resource" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "0901e4e500243c855563a2d78b0c03e4" , .load = load_module, .unload = unload_module, .reload = reload, } |
| static const char * | app_commit = "ODBC_Commit" |
| static const char * | app_rollback = "ODBC_Rollback" |
| static struct ast_module_info * | ast_module_info = &__mod_info |
| struct ao2_container * | class_container |
| static struct ast_cli_entry | cli_odbc [] |
| static struct ast_threadstorage | errors_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_errors_buf , .custom_init = NULL , } |
| static struct ast_custom_function | odbc_function |
| static struct ast_datastore_info | txn_info |
ODBC resource manager.
Definition in file res_odbc.c.
| #define EOR_TX (void *)(long)3 |
Definition at line 1138 of file res_odbc.c.
Referenced by aoro2_obj_cb(), and ast_odbc_request_obj2().
| #define NO_TX (void *)(long)2 |
Definition at line 1137 of file res_odbc.c.
Referenced by aoro2_obj_cb(), and ast_odbc_request_obj2().
| #define USE_TX (void *)(long)1 |
Definition at line 1136 of file res_odbc.c.
Referenced by aoro2_obj_cb(), and ast_odbc_request_obj2().
| static void __init_errors_buf | ( | void | ) | [static] |
Definition at line 146 of file res_odbc.c.
| static void __reg_module | ( | void | ) | [static] |
Definition at line 1702 of file res_odbc.c.
| static void __unreg_module | ( | void | ) | [static] |
Definition at line 1702 of file res_odbc.c.
| static int acf_transaction_read | ( | struct ast_channel * | chan, | |
| const char * | cmd, | |||
| char * | data, | |||
| char * | buf, | |||
| size_t | len | |||
| ) | [static] |
Definition at line 1464 of file res_odbc.c.
References AST_APP_ARG, ast_copy_string(), AST_DECLARE_APP_ARGS, AST_STANDARD_APP_ARGS, ast_strlen_zero(), find_transaction(), odbc_txn_frame::forcecommit, odbc_txn_frame::isolation, isolation2text(), and odbc_txn_frame::name.
01465 { 01466 AST_DECLARE_APP_ARGS(args, 01467 AST_APP_ARG(property); 01468 AST_APP_ARG(opt); 01469 ); 01470 struct odbc_txn_frame *tx; 01471 01472 AST_STANDARD_APP_ARGS(args, data); 01473 if (strcasecmp(args.property, "transaction") == 0) { 01474 if ((tx = find_transaction(chan, NULL, NULL, 1))) { 01475 ast_copy_string(buf, tx->name, len); 01476 return 0; 01477 } 01478 } else if (strcasecmp(args.property, "isolation") == 0) { 01479 if (!ast_strlen_zero(args.opt)) { 01480 tx = find_transaction(chan, NULL, args.opt, 0); 01481 } else { 01482 tx = find_transaction(chan, NULL, NULL, 1); 01483 } 01484 if (tx) { 01485 ast_copy_string(buf, isolation2text(tx->isolation), len); 01486 return 0; 01487 } 01488 } else if (strcasecmp(args.property, "forcecommit") == 0) { 01489 if (!ast_strlen_zero(args.opt)) { 01490 tx = find_transaction(chan, NULL, args.opt, 0); 01491 } else { 01492 tx = find_transaction(chan, NULL, NULL, 1); 01493 } 01494 if (tx) { 01495 ast_copy_string(buf, tx->forcecommit ? "1" : "0", len); 01496 return 0; 01497 } 01498 } 01499 return -1; 01500 }
| static int acf_transaction_write | ( | struct ast_channel * | chan, | |
| const char * | cmd, | |||
| char * | s, | |||
| const char * | value | |||
| ) | [static] |
Definition at line 1502 of file res_odbc.c.
References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_false(), ast_log(), ast_odbc_request_obj2(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), ast_true(), odbc_obj::con, find_transaction(), odbc_txn_frame::forcecommit, odbc_txn_frame::isolation, odbc_class::isolation, LOG_ERROR, LOG_WARNING, mark_transaction_active(), odbc_txn_frame::obj, pbx_builtin_setvar_helper(), RES_ODBC_INDEPENDENT_CONNECTION, S_OR, text2isolation(), and odbc_obj::tx.
01503 { 01504 AST_DECLARE_APP_ARGS(args, 01505 AST_APP_ARG(property); 01506 AST_APP_ARG(opt); 01507 ); 01508 struct odbc_txn_frame *tx; 01509 SQLINTEGER nativeerror=0, numfields=0; 01510 SQLSMALLINT diagbytes=0, i; 01511 unsigned char state[10], diagnostic[256]; 01512 01513 AST_STANDARD_APP_ARGS(args, s); 01514 if (strcasecmp(args.property, "transaction") == 0) { 01515 /* Set active transaction */ 01516 struct odbc_obj *obj; 01517 if ((tx = find_transaction(chan, NULL, value, 0))) { 01518 mark_transaction_active(chan, tx); 01519 } else { 01520 /* No such transaction, create one */ 01521 struct ast_flags flags = { RES_ODBC_INDEPENDENT_CONNECTION }; 01522 if (ast_strlen_zero(args.opt) || !(obj = ast_odbc_request_obj2(args.opt, flags))) { 01523 ast_log(LOG_ERROR, "Could not create transaction: invalid database specification '%s'\n", S_OR(args.opt, "")); 01524 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_DB"); 01525 return -1; 01526 } 01527 if (!(tx = find_transaction(chan, obj, value, 0))) { 01528 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE"); 01529 return -1; 01530 } 01531 obj->tx = 1; 01532 } 01533 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK"); 01534 return 0; 01535 } else if (strcasecmp(args.property, "forcecommit") == 0) { 01536 /* Set what happens when an uncommitted transaction ends without explicit Commit or Rollback */ 01537 if (ast_strlen_zero(args.opt)) { 01538 tx = find_transaction(chan, NULL, NULL, 1); 01539 } else { 01540 tx = find_transaction(chan, NULL, args.opt, 0); 01541 } 01542 if (!tx) { 01543 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE"); 01544 return -1; 01545 } 01546 if (ast_true(value)) { 01547 tx->forcecommit = 1; 01548 } else if (ast_false(value)) { 01549 tx->forcecommit = 0; 01550 } else { 01551 ast_log(LOG_ERROR, "Invalid value for forcecommit: '%s'\n", S_OR(value, "")); 01552 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE"); 01553 return -1; 01554 } 01555 01556 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK"); 01557 return 0; 01558 } else if (strcasecmp(args.property, "isolation") == 0) { 01559 /* How do uncommitted transactions affect reads? */ 01560 int isolation = text2isolation(value); 01561 if (ast_strlen_zero(args.opt)) { 01562 tx = find_transaction(chan, NULL, NULL, 1); 01563 } else { 01564 tx = find_transaction(chan, NULL, args.opt, 0); 01565 } 01566 if (!tx) { 01567 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE"); 01568 return -1; 01569 } 01570 if (isolation == 0) { 01571 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE"); 01572 ast_log(LOG_ERROR, "Invalid isolation specification: '%s'\n", S_OR(value, "")); 01573 } else if (SQLSetConnectAttr(tx->obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)isolation, 0) == SQL_ERROR) { 01574 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "SQL_ERROR"); 01575 SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01576 for (i = 0; i < numfields; i++) { 01577 SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01578 ast_log(LOG_WARNING, "SetConnectAttr (Txn isolation) returned an error: %s: %s\n", state, diagnostic); 01579 if (i > 10) { 01580 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01581 break; 01582 } 01583 } 01584 } else { 01585 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK"); 01586 tx->isolation = isolation; 01587 } 01588 return 0; 01589 } else { 01590 ast_log(LOG_ERROR, "Unknown property: '%s'\n", args.property); 01591 return -1; 01592 } 01593 }
| static int aoro2_class_cb | ( | void * | obj, | |
| void * | arg, | |||
| int | flags | |||
| ) | [static] |
Definition at line 1126 of file res_odbc.c.
References CMP_MATCH, CMP_STOP, and odbc_class::name.
Referenced by ast_odbc_request_obj2().
01127 { 01128 struct odbc_class *class = obj; 01129 char *name = arg; 01130 if (!strcmp(class->name, name) && !class->delme) { 01131 return CMP_MATCH | CMP_STOP; 01132 } 01133 return 0; 01134 }
| static int aoro2_obj_cb | ( | void * | vobj, | |
| void * | arg, | |||
| int | flags | |||
| ) | [static] |
Definition at line 1140 of file res_odbc.c.
References ast_mutex_lock(), ast_mutex_unlock(), CMP_MATCH, CMP_STOP, EOR_TX, odbc_obj::lock, NO_TX, odbc_obj::tx, USE_TX, and odbc_obj::used.
Referenced by ast_odbc_request_obj2().
01141 { 01142 struct odbc_obj *obj = vobj; 01143 ast_mutex_lock(&obj->lock); 01144 if ((arg == NO_TX && !obj->tx) || (arg == EOR_TX && !obj->used) || (arg == USE_TX && obj->tx && !obj->used)) { 01145 obj->used = 1; 01146 ast_mutex_unlock(&obj->lock); 01147 return CMP_MATCH | CMP_STOP; 01148 } 01149 ast_mutex_unlock(&obj->lock); 01150 return 0; 01151 }
| 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 678 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().
00679 { 00680 SQLRETURN res; 00681 00682 if (pmaxlen == 0) { 00683 if (SQLGetData(StatementHandle, ColumnNumber, TargetType, ast_str_buffer(*buf), 0, StrLen_or_Ind) == SQL_SUCCESS_WITH_INFO) { 00684 ast_str_make_space(buf, *StrLen_or_Ind + 1); 00685 } 00686 } else if (pmaxlen > 0) { 00687 ast_str_make_space(buf, pmaxlen); 00688 } 00689 res = SQLGetData(StatementHandle, ColumnNumber, TargetType, ast_str_buffer(*buf), ast_str_size(*buf), StrLen_or_Ind); 00690 ast_str_update(*buf); 00691 00692 return res; 00693 }
| 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 1047 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().
01048 { 01049 return obj->parent->backslash_is_escape; 01050 }
| int ast_odbc_clear_cache | ( | const char * | database, | |
| const char * | tablename | |||
| ) |
Remove a cache entry from memory.
| 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 551 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.
00552 { 00553 struct odbc_cache_tables *tableptr; 00554 00555 AST_RWLIST_WRLOCK(&odbc_tables); 00556 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&odbc_tables, tableptr, list) { 00557 if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) { 00558 AST_LIST_REMOVE_CURRENT(list); 00559 destroy_table_cache(tableptr); 00560 break; 00561 } 00562 } 00563 AST_RWLIST_TRAVERSE_SAFE_END 00564 AST_RWLIST_UNLOCK(&odbc_tables); 00565 return tableptr ? 0 : -1; 00566 }
| 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 568 of file res_odbc.c.
References ast_log(), LOG_WARNING, odbc_obj_connect(), odbc_obj_disconnect(), odbc_obj::tx, and odbc_obj::up.
Referenced by acf_odbc_read(), acf_odbc_write(), cli_odbc_read(), cli_odbc_write(), and odbc_log().
00569 { 00570 int attempt; 00571 SQLHSTMT stmt; 00572 00573 for (attempt = 0; attempt < 2; attempt++) { 00574 stmt = exec_cb(obj, data); 00575 00576 if (stmt) { 00577 break; 00578 } else if (obj->tx) { 00579 ast_log(LOG_WARNING, "Failed to execute, but unable to reconnect, as we're transactional.\n"); 00580 break; 00581 } else { 00582 obj->up = 0; 00583 ast_log(LOG_WARNING, "SQL Exec Direct failed. Attempting a reconnect...\n"); 00584 00585 odbc_obj_disconnect(obj); 00586 odbc_obj_connect(obj); 00587 } 00588 } 00589 00590 return stmt; 00591 }
| 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 540 of file res_odbc.c.
References AST_RWLIST_TRAVERSE, odbc_cache_tables::columns, odbc_class::list, and odbc_cache_columns::name.
Referenced by update2_prepare(), and update_odbc().
00541 { 00542 struct odbc_cache_columns *col; 00543 AST_RWLIST_TRAVERSE(&table->columns, col, list) { 00544 if (strcasecmp(col->name, colname) == 0) { 00545 return col; 00546 } 00547 } 00548 return NULL; 00549 }
| 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. |
Definition at line 425 of file res_odbc.c.
References ast_calloc, AST_LIST_INSERT_TAIL, ast_log(), ast_odbc_release_obj(), ast_odbc_request_obj(), ast_odbc_sanity_check(), AST_RWLIST_HEAD_INIT, AST_RWLIST_INSERT_TAIL, AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, ast_verb, odbc_cache_tables::columns, odbc_obj::con, odbc_cache_tables::connection, odbc_cache_columns::decimals, destroy_table_cache(), odbc_class::list, 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().
00426 { 00427 struct odbc_cache_tables *tableptr; 00428 struct odbc_cache_columns *entry; 00429 char columnname[80]; 00430 SQLLEN sqlptr; 00431 SQLHSTMT stmt = NULL; 00432 int res = 0, error = 0, try = 0; 00433 struct odbc_obj *obj = ast_odbc_request_obj(database, 0); 00434 00435 AST_RWLIST_RDLOCK(&odbc_tables); 00436 AST_RWLIST_TRAVERSE(&odbc_tables, tableptr, list) { 00437 if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) { 00438 break; 00439 } 00440 } 00441 if (tableptr) { 00442 AST_RWLIST_RDLOCK(&tableptr->columns); 00443 AST_RWLIST_UNLOCK(&odbc_tables); 00444 if (obj) { 00445 ast_odbc_release_obj(obj); 00446 } 00447 return tableptr; 00448 } 00449 00450 if (!obj) { 00451 ast_log(LOG_WARNING, "Unable to retrieve database handle for table description '%s@%s'\n", tablename, database); 00452 AST_RWLIST_UNLOCK(&odbc_tables); 00453 return NULL; 00454 } 00455 00456 /* Table structure not already cached; build it now. */ 00457 do { 00458 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt); 00459 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00460 if (try == 0) { 00461 try = 1; 00462 ast_odbc_sanity_check(obj); 00463 continue; 00464 } 00465 ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", database); 00466 break; 00467 } 00468 00469 res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)tablename, SQL_NTS, (unsigned char *)"%", SQL_NTS); 00470 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00471 if (try == 0) { 00472 try = 1; 00473 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 00474 ast_odbc_sanity_check(obj); 00475 continue; 00476 } 00477 ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'.\n", database); 00478 break; 00479 } 00480 00481 if (!(tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + strlen(database) + 1 + strlen(tablename) + 1))) { 00482 ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", tablename, database); 00483 break; 00484 } 00485 00486 tableptr->connection = (char *)tableptr + sizeof(*tableptr); 00487 tableptr->table = (char *)tableptr + sizeof(*tableptr) + strlen(database) + 1; 00488 strcpy(tableptr->connection, database); /* SAFE */ 00489 strcpy(tableptr->table, tablename); /* SAFE */ 00490 AST_RWLIST_HEAD_INIT(&(tableptr->columns)); 00491 00492 while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) { 00493 SQLGetData(stmt, 4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr); 00494 00495 if (!(entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1))) { 00496 ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, tablename, database); 00497 error = 1; 00498 break; 00499 } 00500 entry->name = (char *)entry + sizeof(*entry); 00501 strcpy(entry->name, columnname); 00502 00503 SQLGetData(stmt, 5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL); 00504 SQLGetData(stmt, 7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL); 00505 SQLGetData(stmt, 9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL); 00506 SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL); 00507 SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL); 00508 SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL); 00509 00510 /* Specification states that the octenlen should be the maximum number of bytes 00511 * returned in a char or binary column, but it seems that some drivers just set 00512 * it to NULL. (Bad Postgres! No biscuit!) */ 00513 if (entry->octetlen == 0) { 00514 entry->octetlen = entry->size; 00515 } 00516 00517 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); 00518 /* Insert column info into column list */ 00519 AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list); 00520 } 00521 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 00522 00523 AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list); 00524 AST_RWLIST_RDLOCK(&(tableptr->columns)); 00525 break; 00526 } while (1); 00527 00528 AST_RWLIST_UNLOCK(&odbc_tables); 00529 00530 if (error) { 00531 destroy_table_cache(tableptr); 00532 tableptr = NULL; 00533 } 00534 if (obj) { 00535 ast_odbc_release_obj(obj); 00536 } 00537 return tableptr; 00538 }
| 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 593 of file res_odbc.c.
References ast_log(), ast_odbc_sanity_check(), ast_tvnow(), odbc_obj::last_used, LOG_WARNING, 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().
00594 { 00595 int res = 0, i, attempt; 00596 SQLINTEGER nativeerror=0, numfields=0; 00597 SQLSMALLINT diagbytes=0; 00598 unsigned char state[10], diagnostic[256]; 00599 SQLHSTMT stmt; 00600 00601 for (attempt = 0; attempt < 2; attempt++) { 00602 /* This prepare callback may do more than just prepare -- it may also 00603 * bind parameters, bind results, etc. The real key, here, is that 00604 * when we disconnect, all handles become invalid for most databases. 00605 * We must therefore redo everything when we establish a new 00606 * connection. */ 00607 stmt = prepare_cb(obj, data); 00608 00609 if (stmt) { 00610 res = SQLExecute(stmt); 00611 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) { 00612 if (res == SQL_ERROR) { 00613 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 00614 for (i = 0; i < numfields; i++) { 00615 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 00616 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes); 00617 if (i > 10) { 00618 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 00619 break; 00620 } 00621 } 00622 } 00623 00624 if (obj->tx) { 00625 ast_log(LOG_WARNING, "SQL Execute error, but unable to reconnect, as we're transactional.\n"); 00626 break; 00627 } else { 00628 ast_log(LOG_WARNING, "SQL Execute error %d! Attempting a reconnect...\n", res); 00629 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 00630 stmt = NULL; 00631 00632 obj->up = 0; 00633 /* 00634 * While this isn't the best way to try to correct an error, this won't automatically 00635 * fail when the statement handle invalidates. 00636 */ 00637 ast_odbc_sanity_check(obj); 00638 continue; 00639 } 00640 } else { 00641 obj->last_used = ast_tvnow(); 00642 } 00643 break; 00644 } else if (attempt == 0) { 00645 ast_odbc_sanity_check(obj); 00646 } 00647 } 00648 00649 return stmt; 00650 }
| void ast_odbc_release_obj | ( | struct odbc_obj * | obj | ) |
Releases an ODBC object previously allocated by odbc_request_obj().
| obj | The ODBC object |
Definition at line 1041 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().
01042 { 01043 struct odbc_txn_frame *tx = find_transaction(NULL, obj, NULL, 0); 01044 odbc_release_obj2(obj, tx); 01045 }
| struct odbc_obj* ast_odbc_request_obj | ( | const char * | name, | |
| int | check | |||
| ) | [read] |
Definition at line 1331 of file res_odbc.c.
References ast_odbc_request_obj2(), and RES_ODBC_SANITY_CHECK.
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().
01333 { 01334 struct ast_flags flags = { check ? RES_ODBC_SANITY_CHECK : 0 }; 01335 #ifdef DEBUG_THREADS 01336 return _ast_odbc_request_obj2(name, flags, file, function, lineno); 01337 #else 01338 return ast_odbc_request_obj2(name, flags); 01339 #endif 01340 }
Retrieves a connected ODBC object.
| name | The name of the ODBC class for which a connection is needed. | |
| flags | Set of flags used to control which connection is returned. |
| ODBC | object | |
| 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 odbc_release_obj(), below.
Definition at line 1156 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_copy_string(), ast_log(), ast_mutex_init(), 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_obj::last_used, odbc_obj::lock, LOG_WARNING, NO_TX, odbc_class::obj_container, ODBC_FAIL, odbc_obj_connect(), odbc_obj_destructor(), odbc_obj::parent, RES_ODBC_INDEPENDENT_CONNECTION, RES_ODBC_SANITY_CHECK, USE_TX, and odbc_obj::used.
Referenced by acf_transaction_write(), and ast_odbc_request_obj().
01158 { 01159 struct odbc_obj *obj = NULL; 01160 struct odbc_class *class; 01161 SQLINTEGER nativeerror=0, numfields=0; 01162 SQLSMALLINT diagbytes=0, i; 01163 unsigned char state[10], diagnostic[256]; 01164 01165 if (!(class = ao2_callback(class_container, 0, aoro2_class_cb, (char *) name))) { 01166 return NULL; 01167 } 01168 01169 ast_assert(ao2_ref(class, 0) > 1); 01170 01171 if (class->haspool) { 01172 /* Recycle connections before building another */ 01173 obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, EOR_TX); 01174 01175 if (obj) { 01176 ast_assert(ao2_ref(obj, 0) > 1); 01177 } 01178 01179 if (!obj && (class->count < class->limit)) { 01180 obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor); 01181 if (!obj) { 01182 ao2_ref(class, -1); 01183 return NULL; 01184 } 01185 ast_assert(ao2_ref(obj, 0) == 1); 01186 ast_mutex_init(&obj->lock); 01187 /* obj inherits the outstanding reference to class */ 01188 obj->parent = class; 01189 class = NULL; 01190 if (odbc_obj_connect(obj) == ODBC_FAIL) { 01191 ast_log(LOG_WARNING, "Failed to connect to %s\n", name); 01192 ao2_ref(obj, -1); 01193 ast_assert(ao2_ref(class, 0) > 0); 01194 obj = NULL; 01195 } else { 01196 obj->used = 1; 01197 ao2_link(obj->parent->obj_container, obj); 01198 ast_atomic_fetchadd_int(&obj->parent->count, +1); 01199 } 01200 } else { 01201 /* Object is not constructed, so delete outstanding reference to class. */ 01202 ao2_ref(class, -1); 01203 class = NULL; 01204 } 01205 01206 if (obj && ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) { 01207 /* Ensure this connection has autocommit turned off. */ 01208 if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) { 01209 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01210 for (i = 0; i < numfields; i++) { 01211 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01212 ast_log(LOG_WARNING, "SQLSetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic); 01213 if (i > 10) { 01214 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01215 break; 01216 } 01217 } 01218 } 01219 } 01220 } else if (ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) { 01221 /* Non-pooled connections -- but must use a separate connection handle */ 01222 if (!(obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, USE_TX))) { 01223 obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor); 01224 if (!obj) { 01225 ao2_ref(class, -1); 01226 return NULL; 01227 } 01228 ast_mutex_init(&obj->lock); 01229 /* obj inherits the outstanding reference to class */ 01230 obj->parent = class; 01231 class = NULL; 01232 if (odbc_obj_connect(obj) == ODBC_FAIL) { 01233 ast_log(LOG_WARNING, "Failed to connect to %s\n", name); 01234 ao2_ref(obj, -1); 01235 obj = NULL; 01236 } else { 01237 obj->used = 1; 01238 ao2_link(obj->parent->obj_container, obj); 01239 ast_atomic_fetchadd_int(&obj->parent->count, +1); 01240 } 01241 } 01242 01243 if (obj && SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) { 01244 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01245 for (i = 0; i < numfields; i++) { 01246 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01247 ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic); 01248 if (i > 10) { 01249 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01250 break; 01251 } 01252 } 01253 } 01254 } else { 01255 /* Non-pooled connection: multiple modules can use the same connection. */ 01256 if ((obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, NO_TX))) { 01257 /* Object is not constructed, so delete outstanding reference to class. */ 01258 ast_assert(ao2_ref(class, 0) > 1); 01259 ao2_ref(class, -1); 01260 class = NULL; 01261 } else { 01262 /* No entry: build one */ 01263 if (!(obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor))) { 01264 ast_assert(ao2_ref(class, 0) > 1); 01265 ao2_ref(class, -1); 01266 return NULL; 01267 } 01268 ast_mutex_init(&obj->lock); 01269 /* obj inherits the outstanding reference to class */ 01270 obj->parent = class; 01271 class = NULL; 01272 if (odbc_obj_connect(obj) == ODBC_FAIL) { 01273 ast_log(LOG_WARNING, "Failed to connect to %s\n", name); 01274 ao2_ref(obj, -1); 01275 obj = NULL; 01276 } else { 01277 ao2_link(obj->parent->obj_container, obj); 01278 ast_assert(ao2_ref(obj, 0) > 1); 01279 } 01280 } 01281 01282 if (obj && SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) { 01283 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01284 for (i = 0; i < numfields; i++) { 01285 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01286 ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic); 01287 if (i > 10) { 01288 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01289 break; 01290 } 01291 } 01292 } 01293 } 01294 01295 /* Set the isolation property */ 01296 if (obj && SQLSetConnectAttr(obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)obj->parent->isolation, 0) == SQL_ERROR) { 01297 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01298 for (i = 0; i < numfields; i++) { 01299 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01300 ast_log(LOG_WARNING, "SetConnectAttr (Txn isolation) returned an error: %s: %s\n", state, diagnostic); 01301 if (i > 10) { 01302 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01303 break; 01304 } 01305 } 01306 } 01307 01308 if (obj && ast_test_flag(&flags, RES_ODBC_SANITY_CHECK)) { 01309 ast_odbc_sanity_check(obj); 01310 } else if (obj && obj->parent->idlecheck > 0 && ast_tvdiff_sec(ast_tvnow(), obj->last_used) > obj->parent->idlecheck) 01311 odbc_obj_connect(obj); 01312 01313 #ifdef DEBUG_THREADS 01314 if (obj) { 01315 ast_copy_string(obj->file, file, sizeof(obj->file)); 01316 ast_copy_string(obj->function, function, sizeof(obj->function)); 01317 obj->lineno = lineno; 01318 } 01319 #endif 01320 ast_assert(class == NULL); 01321 01322 if (obj) { 01323 ast_assert(ao2_ref(obj, 0) > 1); 01324 } 01325 return obj; 01326 }
| 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 1342 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::list, odbc_class::name, odbc_txn_frame::obj, odbc_obj::parent, and txn_info.
Referenced by acf_odbc_write().
01343 { 01344 struct ast_datastore *txn_store; 01345 AST_LIST_HEAD(, odbc_txn_frame) *oldlist; 01346 struct odbc_txn_frame *txn = NULL; 01347 01348 if (!chan) { 01349 /* No channel == no transaction */ 01350 return NULL; 01351 } 01352 01353 ast_channel_lock(chan); 01354 if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) { 01355 oldlist = txn_store->data; 01356 } else { 01357 ast_channel_unlock(chan); 01358 return NULL; 01359 } 01360 01361 AST_LIST_LOCK(oldlist); 01362 ast_channel_unlock(chan); 01363 01364 AST_LIST_TRAVERSE(oldlist, txn, list) { 01365 if (txn->obj && txn->obj->parent && !strcmp(txn->obj->parent->name, objname)) { 01366 AST_LIST_UNLOCK(oldlist); 01367 return txn->obj; 01368 } 01369 } 01370 AST_LIST_UNLOCK(oldlist); 01371 return NULL; 01372 }
| 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 695 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_find_table(), ast_odbc_prepare_and_execute(), ast_odbc_request_obj2(), and handle_cli_odbc_show().
00696 { 00697 char *test_sql = "select 1"; 00698 SQLHSTMT stmt; 00699 int res = 0; 00700 00701 if (!ast_strlen_zero(obj->parent->sanitysql)) 00702 test_sql = obj->parent->sanitysql; 00703 00704 if (obj->up) { 00705 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt); 00706 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00707 obj->up = 0; 00708 } else { 00709 res = SQLPrepare(stmt, (unsigned char *)test_sql, SQL_NTS); 00710 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00711 obj->up = 0; 00712 } else { 00713 res = SQLExecute(stmt); 00714 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00715 obj->up = 0; 00716 } 00717 } 00718 } 00719 SQLFreeHandle (SQL_HANDLE_STMT, stmt); 00720 } 00721 00722 if (!obj->up && !obj->tx) { /* Try to reconnect! */ 00723 ast_log(LOG_WARNING, "Connection is down attempting to reconnect...\n"); 00724 odbc_obj_disconnect(obj); 00725 odbc_obj_connect(obj); 00726 } 00727 return obj->up; 00728 }
| 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 652 of file res_odbc.c.
References ast_log(), ast_tvnow(), odbc_obj::last_used, and LOG_WARNING.
00653 { 00654 int res = 0, i; 00655 SQLINTEGER nativeerror=0, numfields=0; 00656 SQLSMALLINT diagbytes=0; 00657 unsigned char state[10], diagnostic[256]; 00658 00659 res = SQLExecute(stmt); 00660 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) { 00661 if (res == SQL_ERROR) { 00662 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 00663 for (i = 0; i < numfields; i++) { 00664 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 00665 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes); 00666 if (i > 10) { 00667 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 00668 break; 00669 } 00670 } 00671 } 00672 } else 00673 obj->last_used = ast_tvnow(); 00674 00675 return res; 00676 }
| static int commit_exec | ( | struct ast_channel * | chan, | |
| void * | data | |||
| ) | [static] |
Definition at line 1052 of file res_odbc.c.
References ast_log(), ast_str_append(), ast_str_buffer(), ast_str_reset(), ast_str_strlen(), ast_str_thread_get(), ast_strlen_zero(), odbc_obj::con, errors_buf, find_transaction(), LOG_WARNING, odbc_txn_frame::obj, and pbx_builtin_setvar_helper().
Referenced by load_module().
01053 { 01054 struct odbc_txn_frame *tx; 01055 SQLINTEGER nativeerror=0, numfields=0; 01056 SQLSMALLINT diagbytes=0, i; 01057 unsigned char state[10], diagnostic[256]; 01058 01059 if (ast_strlen_zero(data)) { 01060 tx = find_transaction(chan, NULL, NULL, 1); 01061 } else { 01062 tx = find_transaction(chan, NULL, data, 0); 01063 } 01064 01065 pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", "OK"); 01066 01067 if (tx) { 01068 if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_COMMIT) == SQL_ERROR) { 01069 struct ast_str *errors = ast_str_thread_get(&errors_buf, 16); 01070 ast_str_reset(errors); 01071 01072 /* Handle possible transaction commit failure */ 01073 SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01074 for (i = 0; i < numfields; i++) { 01075 SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01076 ast_str_append(&errors, 0, "%s%s", ast_str_strlen(errors) ? "," : "", state); 01077 ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic); 01078 if (i > 10) { 01079 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01080 break; 01081 } 01082 } 01083 pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", ast_str_buffer(errors)); 01084 } 01085 } 01086 return 0; 01087 }
| static void destroy_table_cache | ( | struct odbc_cache_tables * | table | ) | [static] |
Definition at line 404 of file res_odbc.c.
References ast_debug, ast_free, AST_RWLIST_HEAD_DESTROY, AST_RWLIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, odbc_cache_tables::columns, odbc_class::list, and odbc_cache_tables::table.
Referenced by ast_odbc_clear_cache(), ast_odbc_find_table(), and reload().
00404 { 00405 struct odbc_cache_columns *col; 00406 ast_debug(1, "Destroying table cache for %s\n", table->table); 00407 AST_RWLIST_WRLOCK(&table->columns); 00408 while ((col = AST_RWLIST_REMOVE_HEAD(&table->columns, list))) { 00409 ast_free(col); 00410 } 00411 AST_RWLIST_UNLOCK(&table->columns); 00412 AST_RWLIST_HEAD_DESTROY(&table->columns); 00413 ast_free(table); 00414 }
| static struct odbc_txn_frame* find_transaction | ( | struct ast_channel * | chan, | |
| struct odbc_obj * | obj, | |||
| const char * | name, | |||
| int | active | |||
| ) | [static, read] |
Definition at line 204 of file res_odbc.c.
References odbc_txn_frame::active, ast_calloc, ast_channel_datastore_add(), ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, ast_datastore_alloc, ast_datastore_free(), AST_LIST_HEAD, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_datastore::data, odbc_class::forcecommit, odbc_txn_frame::forcecommit, odbc_class::isolation, odbc_txn_frame::isolation, odbc_class::list, LOG_ERROR, odbc_txn_frame::name, odbc_txn_frame::obj, odbc_txn_frame::owner, odbc_obj::parent, odbc_obj::tx, odbc_obj::txf, and txn_info.
Referenced by acf_transaction_read(), acf_transaction_write(), ast_odbc_release_obj(), commit_exec(), and rollback_exec().
00205 { 00206 struct ast_datastore *txn_store; 00207 AST_LIST_HEAD(, odbc_txn_frame) *oldlist; 00208 struct odbc_txn_frame *txn = NULL; 00209 00210 if (!chan && obj && obj->txf && obj->txf->owner) { 00211 chan = obj->txf->owner; 00212 } else if (!chan) { 00213 /* No channel == no transaction */ 00214 return NULL; 00215 } 00216 00217 ast_channel_lock(chan); 00218 if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) { 00219 oldlist = txn_store->data; 00220 } else { 00221 /* Need to create a new datastore */ 00222 if (!(txn_store = ast_datastore_alloc(&txn_info, NULL))) { 00223 ast_log(LOG_ERROR, "Unable to allocate a new datastore. Cannot create a new transaction.\n"); 00224 ast_channel_unlock(chan); 00225 return NULL; 00226 } 00227 00228 if (!(oldlist = ast_calloc(1, sizeof(*oldlist)))) { 00229 ast_log(LOG_ERROR, "Unable to allocate datastore list head. Cannot create a new transaction.\n"); 00230 ast_datastore_free(txn_store); 00231 ast_channel_unlock(chan); 00232 return NULL; 00233 } 00234 00235 txn_store->data = oldlist; 00236 AST_LIST_HEAD_INIT(oldlist); 00237 ast_channel_datastore_add(chan, txn_store); 00238 } 00239 00240 AST_LIST_LOCK(oldlist); 00241 ast_channel_unlock(chan); 00242 00243 /* Scanning for an object is *fast*. Scanning for a name is much slower. */ 00244 if (obj != NULL || active == 1) { 00245 AST_LIST_TRAVERSE(oldlist, txn, list) { 00246 if (txn->obj == obj || txn->active) { 00247 AST_LIST_UNLOCK(oldlist); 00248 return txn; 00249 } 00250 } 00251 } 00252 00253 if (name != NULL) { 00254 AST_LIST_TRAVERSE(oldlist, txn, list) { 00255 if (!strcasecmp(txn->name, name)) { 00256 AST_LIST_UNLOCK(oldlist); 00257 return txn; 00258 } 00259 } 00260 } 00261 00262 /* Nothing found, create one */ 00263 if (name && obj && (txn = ast_calloc(1, sizeof(*txn) + strlen(name) + 1))) { 00264 struct odbc_txn_frame *otxn; 00265 00266 strcpy(txn->name, name); /* SAFE */ 00267 txn->obj = obj; 00268 txn->isolation = obj->parent->isolation; 00269 txn->forcecommit = obj->parent->forcecommit; 00270 txn->owner = chan; 00271 txn->active = 1; 00272 00273 /* On creation, the txn becomes active, and all others inactive */ 00274 AST_LIST_TRAVERSE(oldlist, otxn, list) { 00275 otxn->active = 0; 00276 } 00277 AST_LIST_INSERT_TAIL(oldlist, txn, list); 00278 00279 obj->txf = txn; 00280 obj->tx = 1; 00281 } 00282 AST_LIST_UNLOCK(oldlist); 00283 00284 return txn; 00285 }
| static char* handle_cli_odbc_show | ( | struct ast_cli_entry * | e, | |
| int | cmd, | |||
| struct ast_cli_args * | a | |||
| ) | [static] |
Definition at line 871 of file res_odbc.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_cli_args::argc, ast_cli_args::argv, ast_cli(), ast_mutex_lock(), ast_mutex_unlock(), ast_odbc_sanity_check(), ast_strdup, class_container, CLI_GENERATE, CLI_INIT, CLI_SUCCESS, ast_cli_entry::command, odbc_class::count, ast_cli_args::fd, odbc_obj::lock, ast_cli_args::n, ast_cli_args::pos, odbc_obj::up, ast_cli_entry::usage, odbc_obj::used, and ast_cli_args::word.
00872 { 00873 struct ao2_iterator aoi = ao2_iterator_init(class_container, 0); 00874 struct odbc_class *class; 00875 struct odbc_obj *current; 00876 int length = 0; 00877 int which = 0; 00878 char *ret = NULL; 00879 00880 switch (cmd) { 00881 case CLI_INIT: 00882 e->command = "odbc show"; 00883 e->usage = 00884 "Usage: odbc show [class]\n" 00885 " List settings of a particular ODBC class or,\n" 00886 " if not specified, all classes.\n"; 00887 return NULL; 00888 case CLI_GENERATE: 00889 if (a->pos != 2) 00890 return NULL; 00891 length = strlen(a->word); 00892 while ((class = ao2_iterator_next(&aoi))) { 00893 if (!strncasecmp(a->word, class->name, length) && ++which > a->n) { 00894 ret = ast_strdup(class->name); 00895 } 00896 ao2_ref(class, -1); 00897 if (ret) { 00898 break; 00899 } 00900 } 00901 ao2_iterator_destroy(&aoi); 00902 if (!ret && !strncasecmp(a->word, "all", length) && ++which > a->n) { 00903 ret = ast_strdup("all"); 00904 } 00905 return ret; 00906 } 00907 00908 ast_cli(a->fd, "\nODBC DSN Settings\n"); 00909 ast_cli(a->fd, "-----------------\n\n"); 00910 aoi = ao2_iterator_init(class_container, 0); 00911 while ((class = ao2_iterator_next(&aoi))) { 00912 if ((a->argc == 2) || (a->argc == 3 && !strcmp(a->argv[2], "all")) || (!strcmp(a->argv[2], class->name))) { 00913 int count = 0; 00914 ast_cli(a->fd, " Name: %s\n DSN: %s\n", class->name, class->dsn); 00915 00916 if (class->haspool) { 00917 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0); 00918 00919 ast_cli(a->fd, " Pooled: Yes\n Limit: %d\n Connections in use: %d\n", class->limit, class->count); 00920 00921 while ((current = ao2_iterator_next(&aoi2))) { 00922 ast_mutex_lock(¤t->lock); 00923 #ifdef DEBUG_THREADS 00924 ast_cli(a->fd, " - Connection %d: %s (%s:%d %s)\n", ++count, 00925 current->used ? "in use" : 00926 current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected", 00927 current->file, current->lineno, current->function); 00928 #else 00929 ast_cli(a->fd, " - Connection %d: %s\n", ++count, 00930 current->used ? "in use" : 00931 current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected"); 00932 #endif 00933 ast_mutex_unlock(¤t->lock); 00934 ao2_ref(current, -1); 00935 } 00936 ao2_iterator_destroy(&aoi2); 00937 } else { 00938 /* Should only ever be one of these (unless there are transactions) */ 00939 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0); 00940 while ((current = ao2_iterator_next(&aoi2))) { 00941 ast_cli(a->fd, " Pooled: No\n Connected: %s\n", current->used ? "In use" : 00942 current->up && ast_odbc_sanity_check(current) ? "Yes" : "No"); 00943 ao2_ref(current, -1); 00944 } 00945 ao2_iterator_destroy(&aoi2); 00946 } 00947 ast_cli(a->fd, "\n"); 00948 } 00949 ao2_ref(class, -1); 00950 } 00951 ao2_iterator_destroy(&aoi); 00952 00953 return CLI_SUCCESS; 00954 }
| static const char* isolation2text | ( | int | iso | ) | [static] |
Definition at line 170 of file res_odbc.c.
Referenced by acf_transaction_read().
00171 { 00172 if (iso == SQL_TXN_READ_COMMITTED) { 00173 return "read_committed"; 00174 } else if (iso == SQL_TXN_READ_UNCOMMITTED) { 00175 return "read_uncommitted"; 00176 } else if (iso == SQL_TXN_SERIALIZABLE) { 00177 return "serializable"; 00178 } else if (iso == SQL_TXN_REPEATABLE_READ) { 00179 return "repeatable_read"; 00180 } else { 00181 return "unknown"; 00182 } 00183 }
| static int load_module | ( | void | ) | [static] |
Definition at line 1684 of file res_odbc.c.
References ao2_container_alloc, ao2_match_by_addr, app_commit, app_rollback, ARRAY_LEN, ast_cli_register_multiple(), ast_custom_function_register, ast_log(), AST_MODULE_LOAD_DECLINE, ast_register_application_xml, class_container, cli_odbc, commit_exec(), load_odbc_config(), LOG_NOTICE, null_hash_fn(), odbc_function, and rollback_exec().
01685 { 01686 if (!(class_container = ao2_container_alloc(1, null_hash_fn, ao2_match_by_addr))) 01687 return AST_MODULE_LOAD_DECLINE; 01688 if (load_odbc_config() == -1) 01689 return AST_MODULE_LOAD_DECLINE; 01690 ast_cli_register_multiple(cli_odbc, ARRAY_LEN(cli_odbc)); 01691 ast_register_application_xml(app_commit, commit_exec); 01692 ast_register_application_xml(app_rollback, rollback_exec); 01693 ast_custom_function_register(&odbc_function); 01694 ast_log(LOG_NOTICE, "res_odbc loaded.\n"); 01695 return 0; 01696 }
| static int load_odbc_config | ( | void | ) | [static] |
Definition at line 730 of file res_odbc.c.
References ao2_alloc, ao2_container_alloc, ao2_match_by_addr, ao2_ref, ast_category_browse(), ast_config_destroy(), ast_config_load, ast_copy_string(), ast_false(), ast_log(), ast_strdup, ast_strlen_zero(), ast_true(), ast_variable_browse(), config, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEMISSING, odbc_class::dsn, enabled, odbc_class::forcecommit, odbc_class::idlecheck, odbc_class::isolation, odbc_class::limit, LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_variable::name, ast_variable::next, null_hash_fn(), odbc_class_destructor(), odbc_register_class(), odbc_class::password, odbc_class::sanitysql, text2isolation(), odbc_class::username, and ast_variable::value.
Referenced by load_module(), and reload().
00731 { 00732 static char *cfg = "res_odbc.conf"; 00733 struct ast_config *config; 00734 struct ast_variable *v; 00735 char *cat; 00736 const char *dsn, *username, *password, *sanitysql; 00737 int enabled, pooling, limit, bse, forcecommit, isolation; 00738 unsigned int idlecheck; 00739 int preconnect = 0, res = 0; 00740 struct ast_flags config_flags = { 0 }; 00741 00742 struct odbc_class *new; 00743 00744 config = ast_config_load(cfg, config_flags); 00745 if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) { 00746 ast_log(LOG_WARNING, "Unable to load config file res_odbc.conf\n"); 00747 return -1; 00748 } 00749 for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) { 00750 if (!strcasecmp(cat, "ENV")) { 00751 for (v = ast_variable_browse(config, cat); v; v = v->next) { 00752 setenv(v->name, v->value, 1); 00753 ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value); 00754 } 00755 } else { 00756 /* Reset all to defaults for each class of odbc connections */ 00757 dsn = username = password = sanitysql = NULL; 00758 enabled = 1; 00759 preconnect = idlecheck = 0; 00760 pooling = 0; 00761 limit = 0; 00762 bse = 1; 00763 forcecommit = 0; 00764 isolation = SQL_TXN_READ_COMMITTED; 00765 for (v = ast_variable_browse(config, cat); v; v = v->next) { 00766 if (!strcasecmp(v->name, "pooling")) { 00767 if (ast_true(v->value)) 00768 pooling = 1; 00769 } else if (!strncasecmp(v->name, "share", 5)) { 00770 /* "shareconnections" is a little clearer in meaning than "pooling" */ 00771 if (ast_false(v->value)) 00772 pooling = 1; 00773 } else if (!strcasecmp(v->name, "limit")) { 00774 sscanf(v->value, "%30d", &limit); 00775 if (ast_true(v->value) && !limit) { 00776 ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'. Setting limit to 1023 for ODBC class '%s'.\n", v->value, cat); 00777 limit = 1023; 00778 } else if (ast_false(v->value)) { 00779 ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'. Disabling ODBC class '%s'.\n", v->value, cat); 00780 enabled = 0; 00781 break; 00782 } 00783 } else if (!strcasecmp(v->name, "idlecheck")) { 00784 sscanf(v->value, "%30u", &idlecheck); 00785 } else if (!strcasecmp(v->name, "enabled")) { 00786 enabled = ast_true(v->value); 00787 } else if (!strcasecmp(v->name, "pre-connect")) { 00788 preconnect = ast_true(v->value); 00789 } else if (!strcasecmp(v->name, "dsn")) { 00790 dsn = v->value; 00791 } else if (!strcasecmp(v->name, "username")) { 00792 username = v->value; 00793 } else if (!strcasecmp(v->name, "password")) { 00794 password = v->value; 00795 } else if (!strcasecmp(v->name, "sanitysql")) { 00796 sanitysql = v->value; 00797 } else if (!strcasecmp(v->name, "backslash_is_escape")) { 00798 bse = ast_true(v->value); 00799 } else if (!strcasecmp(v->name, "forcecommit")) { 00800 forcecommit = ast_true(v->value); 00801 } else if (!strcasecmp(v->name, "isolation")) { 00802 if ((isolation = text2isolation(v->value)) == 0) { 00803 ast_log(LOG_ERROR, "Unrecognized value for 'isolation': '%s' in section '%s'\n", v->value, cat); 00804 isolation = SQL_TXN_READ_COMMITTED; 00805 } 00806 } 00807 } 00808 00809 if (enabled && !ast_strlen_zero(dsn)) { 00810 new = ao2_alloc(sizeof(*new), odbc_class_destructor); 00811 00812 if (!new) { 00813 res = -1; 00814 break; 00815 } 00816 00817 SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env); 00818 res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0); 00819 00820 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00821 ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n"); 00822 ao2_ref(new, -1); 00823 return res; 00824 } 00825 00826 new->obj_container = ao2_container_alloc(1, null_hash_fn, ao2_match_by_addr); 00827 00828 if (pooling) { 00829 new->haspool = pooling; 00830 if (limit) { 00831 new->limit = limit; 00832 } else { 00833 ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless. Changing limit from 0 to 5.\n"); 00834 new->limit = 5; 00835 } 00836 } 00837 00838 new->backslash_is_escape = bse ? 1 : 0; 00839 new->forcecommit = forcecommit ? 1 : 0; 00840 new->isolation = isolation; 00841 new->idlecheck = idlecheck; 00842 00843 if (cat) 00844 ast_copy_string(new->name, cat, sizeof(new->name)); 00845 if (dsn) 00846 ast_copy_string(new->dsn, dsn, sizeof(new->dsn)); 00847 if (username && !(new->username = ast_strdup(username))) { 00848 ao2_ref(new, -1); 00849 break; 00850 } 00851 if (password && !(new->password = ast_strdup(password))) { 00852 ao2_ref(new, -1); 00853 break; 00854 } 00855 if (sanitysql && !(new->sanitysql = ast_strdup(sanitysql))) { 00856 ao2_ref(new, -1); 00857 break; 00858 } 00859 00860 odbc_register_class(new, preconnect); 00861 ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn); 00862 ao2_ref(new, -1); 00863 new = NULL; 00864 } 00865 } 00866 } 00867 ast_config_destroy(config); 00868 return res; 00869 }
| static int mark_transaction_active | ( | struct ast_channel * | chan, | |
| struct odbc_txn_frame * | tx | |||
| ) | [static] |
Definition at line 339 of file res_odbc.c.
References odbc_txn_frame::active, 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::list, odbc_txn_frame::owner, and txn_info.
Referenced by acf_transaction_write().
00340 { 00341 struct ast_datastore *txn_store; 00342 AST_LIST_HEAD(, odbc_txn_frame) *oldlist; 00343 struct odbc_txn_frame *active = NULL, *txn; 00344 00345 if (!chan && tx && tx->owner) { 00346 chan = tx->owner; 00347 } 00348 00349 ast_channel_lock(chan); 00350 if (!(txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) { 00351 ast_channel_unlock(chan); 00352 return -1; 00353 } 00354 00355 oldlist = txn_store->data; 00356 AST_LIST_LOCK(oldlist); 00357 AST_LIST_TRAVERSE(oldlist, txn, list) { 00358 if (txn == tx) { 00359 txn->active = 1; 00360 active = txn; 00361 } else { 00362 txn->active = 0; 00363 } 00364 } 00365 AST_LIST_UNLOCK(oldlist); 00366 ast_channel_unlock(chan); 00367 return active ? 0 : -1; 00368 }
| static int null_hash_fn | ( | const void * | obj, | |
| const int | flags | |||
| ) | [static] |
Definition at line 389 of file res_odbc.c.
Referenced by load_module(), and load_odbc_config().
| static void odbc_class_destructor | ( | void * | data | ) | [static] |
Definition at line 370 of file res_odbc.c.
References ao2_ref, and ast_free.
Referenced by load_odbc_config().
00371 { 00372 struct odbc_class *class = data; 00373 /* Due to refcounts, we can safely assume that any objects with a reference 00374 * to us will prevent our destruction, so we don't need to worry about them. 00375 */ 00376 if (class->username) { 00377 ast_free(class->username); 00378 } 00379 if (class->password) { 00380 ast_free(class->password); 00381 } 00382 if (class->sanitysql) { 00383 ast_free(class->sanitysql); 00384 } 00385 ao2_ref(class->obj_container, -1); 00386 SQLFreeHandle(SQL_HANDLE_ENV, class->env); 00387 }
| static odbc_status odbc_obj_connect | ( | struct odbc_obj * | obj | ) | [static] |
Definition at line 1411 of file res_odbc.c.
References ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_tvnow(), odbc_obj::con, odbc_class::dsn, odbc_class::env, odbc_obj::last_used, odbc_obj::lock, LOG_NOTICE, LOG_WARNING, msg, odbc_class::name, ODBC_FAIL, odbc_obj_disconnect(), ODBC_SUCCESS, odbc_obj::parent, odbc_class::password, odbc_obj::up, and odbc_class::username.
Referenced by ast_odbc_direct_execute(), ast_odbc_request_obj2(), and ast_odbc_sanity_check().
01412 { 01413 int res; 01414 SQLINTEGER err; 01415 short int mlen; 01416 unsigned char msg[200], state[10]; 01417 #ifdef NEEDTRACE 01418 SQLINTEGER enable = 1; 01419 char *tracefile = "/tmp/odbc.trace"; 01420 #endif 01421 ast_mutex_lock(&obj->lock); 01422 01423 if (obj->up) { 01424 odbc_obj_disconnect(obj); 01425 ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name); 01426 } else { 01427 ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name); 01428 } 01429 01430 res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &obj->con); 01431 01432 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 01433 ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res); 01434 ast_mutex_unlock(&obj->lock); 01435 return ODBC_FAIL; 01436 } 01437 SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) 10, 0); 01438 SQLSetConnectAttr(obj->con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *) 10, 0); 01439 #ifdef NEEDTRACE 01440 SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER); 01441 SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile)); 01442 #endif 01443 01444 res = SQLConnect(obj->con, 01445 (SQLCHAR *) obj->parent->dsn, SQL_NTS, 01446 (SQLCHAR *) obj->parent->username, SQL_NTS, 01447 (SQLCHAR *) obj->parent->password, SQL_NTS); 01448 01449 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 01450 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, state, &err, msg, 100, &mlen); 01451 ast_mutex_unlock(&obj->lock); 01452 ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg); 01453 return ODBC_FAIL; 01454 } else { 01455 ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn); 01456 obj->up = 1; 01457 obj->last_used = ast_tvnow(); 01458 } 01459 01460 ast_mutex_unlock(&obj->lock); 01461 return ODBC_SUCCESS; 01462 }
| static void odbc_obj_destructor | ( | void * | data | ) | [static] |
Definition at line 394 of file res_odbc.c.
References ao2_ref, ast_mutex_destroy(), odbc_obj::lock, odbc_obj_disconnect(), and odbc_obj::parent.
Referenced by ast_odbc_request_obj2().
00395 { 00396 struct odbc_obj *obj = data; 00397 struct odbc_class *class = obj->parent; 00398 obj->parent = NULL; 00399 odbc_obj_disconnect(obj); 00400 ast_mutex_destroy(&obj->lock); 00401 ao2_ref(class, -1); 00402 }
| static odbc_status odbc_obj_disconnect | ( | struct odbc_obj * | obj | ) | [static] |
Definition at line 1374 of file res_odbc.c.
References ast_log(), ast_mutex_lock(), ast_mutex_unlock(), odbc_obj::con, odbc_class::dsn, odbc_obj::lock, LOG_DEBUG, LOG_WARNING, msg, odbc_class::name, ODBC_SUCCESS, odbc_obj::parent, and odbc_obj::up.
Referenced by ast_odbc_direct_execute(), ast_odbc_sanity_check(), odbc_obj_connect(), and odbc_obj_destructor().
01375 { 01376 int res; 01377 SQLINTEGER err; 01378 short int mlen; 01379 unsigned char msg[200], state[10]; 01380 01381 /* Nothing to disconnect */ 01382 if (!obj->con) { 01383 return ODBC_SUCCESS; 01384 } 01385 01386 ast_mutex_lock(&obj->lock); 01387 01388 res = SQLDisconnect(obj->con); 01389 01390 if (obj->parent) { 01391 if (res == SQL_SUCCESS || res == SQL_SUCCESS_WITH_INFO) { 01392 ast_log(LOG_DEBUG, "Disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn); 01393 } else { 01394 ast_log(LOG_DEBUG, "res_odbc: %s [%s] already disconnected\n", obj->parent->name, obj->parent->dsn); 01395 } 01396 } 01397 01398 if ((res = SQLFreeHandle(SQL_HANDLE_DBC, obj->con) == SQL_SUCCESS)) { 01399 obj->con = NULL; 01400 ast_log(LOG_DEBUG, "Database handle deallocated\n"); 01401 } else { 01402 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, state, &err, msg, 100, &mlen); 01403 ast_log(LOG_WARNING, "Unable to deallocate database handle? %d errno=%d %s\n", res, (int)err, msg); 01404 } 01405 01406 obj->up = 0; 01407 ast_mutex_unlock(&obj->lock); 01408 return ODBC_SUCCESS; 01409 }
| static int odbc_register_class | ( | struct odbc_class * | class, | |
| int | connect | |||
| ) | [static] |
Definition at line 960 of file res_odbc.c.
References ao2_link, ast_log(), ast_odbc_release_obj(), ast_odbc_request_obj(), class_container, and LOG_WARNING.
Referenced by load_odbc_config().
00961 { 00962 struct odbc_obj *obj; 00963 if (class) { 00964 ao2_link(class_container, class); 00965 /* I still have a reference in the caller, so a deref is NOT missing here. */ 00966 00967 if (preconnect) { 00968 /* Request and release builds a connection */ 00969 obj = ast_odbc_request_obj(class->name, 0); 00970 if (obj) { 00971 ast_odbc_release_obj(obj); 00972 } 00973 } 00974 00975 return 0; 00976 } else { 00977 ast_log(LOG_WARNING, "Attempted to register a NULL class?\n"); 00978 return -1; 00979 } 00980 }
| static void odbc_release_obj2 | ( | struct odbc_obj * | obj, | |
| struct odbc_txn_frame * | tx | |||
| ) | [static] |
Definition at line 982 of file res_odbc.c.
References ao2_ref, ast_debug, ast_log(), odbc_obj::con, odbc_txn_frame::forcecommit, LOG_WARNING, odbc_txn_frame::obj, release_transaction(), odbc_obj::txf, and odbc_obj::used.
Referenced by ast_odbc_release_obj(), and release_transaction().
00983 { 00984 SQLINTEGER nativeerror=0, numfields=0; 00985 SQLSMALLINT diagbytes=0, i; 00986 unsigned char state[10], diagnostic[256]; 00987 00988 ast_debug(2, "odbc_release_obj2(%p) called (obj->txf = %p)\n", obj, obj->txf); 00989 if (tx) { 00990 ast_debug(1, "called on a transactional handle with %s\n", tx->forcecommit ? "COMMIT" : "ROLLBACK"); 00991 if (SQLEndTran(SQL_HANDLE_DBC, obj->con, tx->forcecommit ? SQL_COMMIT : SQL_ROLLBACK) == SQL_ERROR) { 00992 /* Handle possible transaction commit failure */ 00993 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 00994 for (i = 0; i < numfields; i++) { 00995 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 00996 ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic); 00997 if (!strcmp((char *)state, "25S02") || !strcmp((char *)state, "08007")) { 00998 /* These codes mean that a commit failed and a transaction 00999 * is still active. We must rollback, or things will get 01000 * very, very weird for anybody using the handle next. */ 01001 SQLEndTran(SQL_HANDLE_DBC, obj->con, SQL_ROLLBACK); 01002 } 01003 if (i > 10) { 01004 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01005 break; 01006 } 01007 } 01008 } 01009 01010 /* Transaction is done, reset autocommit */ 01011 if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) { 01012 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01013 for (i = 0; i < numfields; i++) { 01014 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01015 ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic); 01016 if (i > 10) { 01017 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01018 break; 01019 } 01020 } 01021 } 01022 } 01023 01024 #ifdef DEBUG_THREADS 01025 obj->file[0] = '\0'; 01026 obj->function[0] = '\0'; 01027 obj->lineno = 0; 01028 #endif 01029 01030 /* For pooled connections, this frees the connection to be 01031 * reused. For non-pooled connections, it does nothing. */ 01032 obj->used = 0; 01033 if (obj->txf) { 01034 /* Prevent recursion -- transaction is already closed out. */ 01035 obj->txf->obj = NULL; 01036 obj->txf = release_transaction(obj->txf); 01037 } 01038 ao2_ref(obj, -1); 01039 }
| static void odbc_txn_free | ( | void * | data | ) | [static] |
Definition at line 323 of file res_odbc.c.
References ast_debug, ast_free, AST_LIST_HEAD, AST_LIST_HEAD_DESTROY, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, odbc_class::list, and release_transaction().
00324 { 00325 struct odbc_txn_frame *tx; 00326 AST_LIST_HEAD(, odbc_txn_frame) *oldlist = vdata; 00327 00328 ast_debug(2, "odbc_txn_free(%p) called\n", vdata); 00329 00330 AST_LIST_LOCK(oldlist); 00331 while ((tx = AST_LIST_REMOVE_HEAD(oldlist, list))) { 00332 release_transaction(tx); 00333 } 00334 AST_LIST_UNLOCK(oldlist); 00335 AST_LIST_HEAD_DESTROY(oldlist); 00336 ast_free(oldlist); 00337 }
| static struct odbc_txn_frame* release_transaction | ( | struct odbc_txn_frame * | tx | ) | [static, read] |
Definition at line 287 of file res_odbc.c.
References ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, ast_debug, ast_free, AST_LIST_HEAD, AST_LIST_LOCK, AST_LIST_REMOVE, AST_LIST_UNLOCK, ast_datastore::data, odbc_class::list, odbc_txn_frame::obj, odbc_release_obj2(), odbc_txn_frame::owner, odbc_obj::txf, and txn_info.
Referenced by odbc_release_obj2(), and odbc_txn_free().
00288 { 00289 if (!tx) { 00290 return NULL; 00291 } 00292 00293 ast_debug(2, "release_transaction(%p) called (tx->obj = %p, tx->obj->txf = %p)\n", tx, tx->obj, tx->obj ? tx->obj->txf : NULL); 00294 00295 /* If we have an owner, disassociate */ 00296 if (tx->owner) { 00297 struct ast_datastore *txn_store; 00298 AST_LIST_HEAD(, odbc_txn_frame) *oldlist; 00299 00300 ast_channel_lock(tx->owner); 00301 if ((txn_store = ast_channel_datastore_find(tx->owner, &txn_info, NULL))) { 00302 oldlist = txn_store->data; 00303 AST_LIST_LOCK(oldlist); 00304 AST_LIST_REMOVE(oldlist, tx, list); 00305 AST_LIST_UNLOCK(oldlist); 00306 } 00307 ast_channel_unlock(tx->owner); 00308 tx->owner = NULL; 00309 } 00310 00311 if (tx->obj) { 00312 /* If we have any uncommitted transactions, they are handled when we release the object */ 00313 struct odbc_obj *obj = tx->obj; 00314 /* Prevent recursion during destruction */ 00315 tx->obj->txf = NULL; 00316 tx->obj = NULL; 00317 odbc_release_obj2(obj, tx); 00318 } 00319 ast_free(tx); 00320 return NULL; 00321 }
| static int reload | ( | void | ) | [static] |
Definition at line 1604 of file res_odbc.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ao2_unlink, AST_RWLIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, class_container, destroy_table_cache(), odbc_class::list, load_odbc_config(), and table.
01605 { 01606 struct odbc_cache_tables *table; 01607 struct odbc_class *class; 01608 struct odbc_obj *current; 01609 struct ao2_iterator aoi = ao2_iterator_init(class_container, 0); 01610 01611 /* First, mark all to be purged */ 01612 while ((class = ao2_iterator_next(&aoi))) { 01613 class->delme = 1; 01614 ao2_ref(class, -1); 01615 } 01616 ao2_iterator_destroy(&aoi); 01617 01618 load_odbc_config(); 01619 01620 /* Purge remaining classes */ 01621 01622 /* Note on how this works; this is a case of circular references, so we 01623 * explicitly do NOT want to use a callback here (or we wind up in 01624 * recursive hell). 01625 * 01626 * 1. Iterate through all the classes. Note that the classes will currently 01627 * contain two classes of the same name, one of which is marked delme and 01628 * will be purged when all remaining objects of the class are released, and 01629 * the other, which was created above when we re-parsed the config file. 01630 * 2. On each class, there is a reference held by the master container and 01631 * a reference held by each connection object. There are two cases for 01632 * destruction of the class, noted below. However, in all cases, all O-refs 01633 * (references to objects) will first be freed, which will cause the C-refs 01634 * (references to classes) to be decremented (but never to 0, because the 01635 * class container still has a reference). 01636 * a) If the class has outstanding objects, the C-ref by the class 01637 * container will then be freed, which leaves only C-refs by any 01638 * outstanding objects. When the final outstanding object is released 01639 * (O-refs held by applications and dialplan functions), it will in turn 01640 * free the final C-ref, causing class destruction. 01641 * b) If the class has no outstanding objects, when the class container 01642 * removes the final C-ref, the class will be destroyed. 01643 */ 01644 aoi = ao2_iterator_init(class_container, 0); 01645 while ((class = ao2_iterator_next(&aoi))) { /* C-ref++ (by iterator) */ 01646 if (class->delme) { 01647 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0); 01648 while ((current = ao2_iterator_next(&aoi2))) { /* O-ref++ (by iterator) */ 01649 ao2_unlink(class->obj_container, current); /* unlink O-ref from class (reference handled implicitly) */ 01650 ao2_ref(current, -1); /* O-ref-- (by iterator) */ 01651 /* At this point, either 01652 * a) there's an outstanding O-ref, or 01653 * b) the object has already been destroyed. 01654 */ 01655 } 01656 ao2_iterator_destroy(&aoi2); 01657 ao2_unlink(class_container, class); /* unlink C-ref from container (reference handled implicitly) */ 01658 /* At this point, either 01659 * a) there's an outstanding O-ref, which holds an outstanding C-ref, or 01660 * b) the last remaining C-ref is held by the iterator, which will be 01661 * destroyed in the next step. 01662 */ 01663 } 01664 ao2_ref(class, -1); /* C-ref-- (by iterator) */ 01665 } 01666 ao2_iterator_destroy(&aoi); 01667 01668 /* Empty the cache; it will get rebuilt the next time the tables are needed. */ 01669 AST_RWLIST_WRLOCK(&odbc_tables); 01670 while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) { 01671 destroy_table_cache(table); 01672 } 01673 AST_RWLIST_UNLOCK(&odbc_tables); 01674 01675 return 0; 01676 }
| static int rollback_exec | ( | struct ast_channel * | chan, | |
| void * | data | |||
| ) | [static] |
Definition at line 1089 of file res_odbc.c.
References ast_log(), ast_str_append(), ast_str_buffer(), ast_str_reset(), ast_str_strlen(), ast_str_thread_get(), ast_strlen_zero(), odbc_obj::con, errors_buf, find_transaction(), LOG_WARNING, odbc_txn_frame::obj, and pbx_builtin_setvar_helper().
Referenced by load_module().
01090 { 01091 struct odbc_txn_frame *tx; 01092 SQLINTEGER nativeerror=0, numfields=0; 01093 SQLSMALLINT diagbytes=0, i; 01094 unsigned char state[10], diagnostic[256]; 01095 01096 if (ast_strlen_zero(data)) { 01097 tx = find_transaction(chan, NULL, NULL, 1); 01098 } else { 01099 tx = find_transaction(chan, NULL, data, 0); 01100 } 01101 01102 pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", "OK"); 01103 01104 if (tx) { 01105 if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_ROLLBACK) == SQL_ERROR) { 01106 struct ast_str *errors = ast_str_thread_get(&errors_buf, 16); 01107 ast_str_reset(errors); 01108 01109 /* Handle possible transaction commit failure */ 01110 SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01111 for (i = 0; i < numfields; i++) { 01112 SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01113 ast_str_append(&errors, 0, "%s%s", ast_str_strlen(errors) ? "," : "", state); 01114 ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic); 01115 if (i > 10) { 01116 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01117 break; 01118 } 01119 } 01120 pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", ast_str_buffer(errors)); 01121 } 01122 } 01123 return 0; 01124 }
| static int text2isolation | ( | const char * | txt | ) | [static] |
Definition at line 185 of file res_odbc.c.
Referenced by acf_transaction_write(), and load_odbc_config().
00186 { 00187 if (strncasecmp(txt, "read_", 5) == 0) { 00188 if (strncasecmp(txt + 5, "c", 1) == 0) { 00189 return SQL_TXN_READ_COMMITTED; 00190 } else if (strncasecmp(txt + 5, "u", 1) == 0) { 00191 return SQL_TXN_READ_UNCOMMITTED; 00192 } else { 00193 return 0; 00194 } 00195 } else if (strncasecmp(txt, "ser", 3) == 0) { 00196 return SQL_TXN_SERIALIZABLE; 00197 } else if (strncasecmp(txt, "rep", 3) == 0) { 00198 return SQL_TXN_REPEATABLE_READ; 00199 } else { 00200 return 0; 00201 } 00202 }
| static int unload_module | ( | void | ) | [static] |
Definition at line 1678 of file res_odbc.c.
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS , .description = "ODBC resource" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "0901e4e500243c855563a2d78b0c03e4" , .load = load_module, .unload = unload_module, .reload = reload, } [static] |
Definition at line 1702 of file res_odbc.c.
const char* app_commit = "ODBC_Commit" [static] |
Definition at line 1601 of file res_odbc.c.
Referenced by load_module().
const char* app_rollback = "ODBC_Rollback" [static] |
Definition at line 1602 of file res_odbc.c.
Referenced by load_module().
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 1702 of file res_odbc.c.
| struct ao2_container* class_container |
Definition at line 136 of file res_odbc.c.
Referenced by ast_odbc_request_obj2(), handle_cli_odbc_show(), load_module(), odbc_register_class(), and reload().
struct ast_cli_entry cli_odbc[] [static] |
{
AST_CLI_DEFINE(handle_cli_odbc_show, "List ODBC DSN(s)")
}
Definition at line 956 of file res_odbc.c.
Referenced by load_module().
struct ast_threadstorage errors_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_errors_buf , .custom_init = NULL , } [static] |
Definition at line 146 of file res_odbc.c.
Referenced by commit_exec(), and rollback_exec().
struct ast_custom_function odbc_function [static] |
{
.name = "ODBC",
.read = acf_transaction_read,
.write = acf_transaction_write,
}
Definition at line 1595 of file res_odbc.c.
Referenced by load_module().
struct ast_datastore_info txn_info [static] |
{
.type = "ODBC_Transaction",
.destroy = odbc_txn_free,
}
Definition at line 148 of file res_odbc.c.
Referenced by ast_odbc_retrieve_transaction_obj(), find_transaction(), mark_transaction_active(), and release_transaction().
1.6.2