#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"
#include "asterisk/data.h"
Go to the source code of this file.
Data Structures | |
struct | odbc_class |
struct | odbc_tables |
struct | odbc_txn_frame |
Defines | |
#define | DATA_EXPORT_ODBC_CLASS(MEMBER) |
#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) |
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. | |
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) |
AST_DATA_STRUCTURE (odbc_class, DATA_EXPORT_ODBC_CLASS) | |
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. | |
static int | commit_exec (struct ast_channel *chan, const char *data) |
static int | data_odbc_provider_handler (const struct ast_data_search *search, struct ast_data *root) |
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, const char *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 | AST_MODFLAG_LOAD_ORDER , .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 = "8586c2a7d357cb591cc3a6607a8f62d1" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_REALTIME_DEPEND, } |
static const char *const | app_commit = "ODBC_Commit" |
static const char *const | app_rollback = "ODBC_Rollback" |
static struct ast_module_info * | ast_module_info = &__mod_info |
static 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_data_handler | odbc_provider |
static struct ast_data_entry | odbc_providers [] |
static struct ast_datastore_info | txn_info |
Anthony Minessale II <anthmct@yahoo.com>
Tilghman Lesher <tilghman@digium.com>
Definition in file res_odbc.c.
#define DATA_EXPORT_ODBC_CLASS | ( | MEMBER | ) |
Definition at line 177 of file res_odbc.c.
#define EOR_TX (void *)(long)3 |
Definition at line 1184 of file res_odbc.c.
Referenced by _ast_odbc_request_obj2(), and aoro2_obj_cb().
#define NO_TX (void *)(long)2 |
#define USE_TX (void *)(long)1 |
Definition at line 1182 of file res_odbc.c.
Referenced by _ast_odbc_request_obj2(), and aoro2_obj_cb().
static void __init_errors_buf | ( | void | ) | [static] |
static void __reg_module | ( | void | ) | [static] |
Definition at line 1840 of file res_odbc.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 1840 of file res_odbc.c.
struct odbc_obj* _ast_odbc_request_obj | ( | const char * | name, | |
int | check, | |||
const char * | file, | |||
const char * | function, | |||
int | lineno | |||
) |
Definition at line 1382 of file res_odbc.c.
References _ast_odbc_request_obj2(), ast_flags::flags, and RES_ODBC_SANITY_CHECK.
01383 { 01384 struct ast_flags flags = { check ? RES_ODBC_SANITY_CHECK : 0 }; 01385 return _ast_odbc_request_obj2(name, flags, file, function, lineno); 01386 }
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 1199 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_test_flag, class_container, odbc_obj::con, EOR_TX, LOG_WARNING, ODBC_FAIL, odbc_obj_connect(), odbc_obj_destructor(), RES_ODBC_INDEPENDENT_CONNECTION, and USE_TX.
Referenced by _ast_odbc_request_obj().
01200 { 01201 struct odbc_obj *obj = NULL; 01202 struct odbc_class *class; 01203 SQLINTEGER nativeerror=0, numfields=0; 01204 SQLSMALLINT diagbytes=0, i; 01205 unsigned char state[10], diagnostic[256]; 01206 01207 if (!(class = ao2_callback(class_container, 0, aoro2_class_cb, (char *) name))) { 01208 ast_debug(1, "Class '%s' not found!\n", name); 01209 return NULL; 01210 } 01211 01212 ast_assert(ao2_ref(class, 0) > 1); 01213 01214 if (class->haspool) { 01215 /* Recycle connections before building another */ 01216 obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, EOR_TX); 01217 01218 if (obj) { 01219 ast_assert(ao2_ref(obj, 0) > 1); 01220 } 01221 if (!obj && (class->count < class->limit) && 01222 (time(NULL) > class->last_negative_connect.tv_sec + class->negative_connection_cache.tv_sec)) { 01223 obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor); 01224 if (!obj) { 01225 class->count--; 01226 ao2_ref(class, -1); 01227 ast_debug(3, "Unable to allocate object\n"); 01228 return NULL; 01229 } 01230 ast_assert(ao2_ref(obj, 0) == 1); 01231 ast_mutex_init(&obj->lock); 01232 /* obj inherits the outstanding reference to class */ 01233 obj->parent = class; 01234 class = NULL; 01235 if (odbc_obj_connect(obj) == ODBC_FAIL) { 01236 ast_log(LOG_WARNING, "Failed to connect to %s\n", name); 01237 ao2_ref(obj, -1); 01238 ast_assert(ao2_ref(class, 0) > 0); 01239 obj = NULL; 01240 } else { 01241 obj->used = 1; 01242 ao2_link(obj->parent->obj_container, obj); 01243 ast_atomic_fetchadd_int(&obj->parent->count, +1); 01244 } 01245 } else { 01246 /* Object is not constructed, so delete outstanding reference to class. */ 01247 ao2_ref(class, -1); 01248 class = NULL; 01249 } 01250 01251 if (obj && ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) { 01252 /* Ensure this connection has autocommit turned off. */ 01253 if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) { 01254 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01255 for (i = 0; i < numfields; i++) { 01256 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01257 ast_log(LOG_WARNING, "SQLSetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic); 01258 if (i > 10) { 01259 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01260 break; 01261 } 01262 } 01263 } 01264 } 01265 } else if (ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) { 01266 /* Non-pooled connections -- but must use a separate connection handle */ 01267 if (!(obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, USE_TX))) { 01268 ast_debug(1, "Object not found\n"); 01269 obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor); 01270 if (!obj) { 01271 ao2_ref(class, -1); 01272 ast_debug(3, "Unable to allocate object\n"); 01273 return NULL; 01274 } 01275 ast_mutex_init(&obj->lock); 01276 /* obj inherits the outstanding reference to class */ 01277 obj->parent = class; 01278 class = NULL; 01279 if (odbc_obj_connect(obj) == ODBC_FAIL) { 01280 ast_log(LOG_WARNING, "Failed to connect to %s\n", name); 01281 ao2_ref(obj, -1); 01282 obj = NULL; 01283 } else { 01284 obj->used = 1; 01285 ao2_link(obj->parent->obj_container, obj); 01286 ast_atomic_fetchadd_int(&obj->parent->count, +1); 01287 } 01288 } 01289 01290 if (obj && SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) { 01291 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01292 for (i = 0; i < numfields; i++) { 01293 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01294 ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic); 01295 if (i > 10) { 01296 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01297 break; 01298 } 01299 } 01300 } 01301 } else { 01302 /* Non-pooled connection: multiple modules can use the same connection. */ 01303 if ((obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, NO_TX))) { 01304 /* Object is not constructed, so delete outstanding reference to class. */ 01305 ast_assert(ao2_ref(class, 0) > 1); 01306 ao2_ref(class, -1); 01307 class = NULL; 01308 } else { 01309 /* No entry: build one */ 01310 if (!(obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor))) { 01311 ast_assert(ao2_ref(class, 0) > 1); 01312 ao2_ref(class, -1); 01313 ast_debug(3, "Unable to allocate object\n"); 01314 return NULL; 01315 } 01316 ast_mutex_init(&obj->lock); 01317 /* obj inherits the outstanding reference to class */ 01318 obj->parent = class; 01319 class = NULL; 01320 if (odbc_obj_connect(obj) == ODBC_FAIL) { 01321 ast_log(LOG_WARNING, "Failed to connect to %s\n", name); 01322 ao2_ref(obj, -1); 01323 obj = NULL; 01324 } else { 01325 ao2_link(obj->parent->obj_container, obj); 01326 ast_assert(ao2_ref(obj, 0) > 1); 01327 } 01328 } 01329 01330 if (obj && SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) { 01331 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01332 for (i = 0; i < numfields; i++) { 01333 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01334 ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic); 01335 if (i > 10) { 01336 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01337 break; 01338 } 01339 } 01340 } 01341 } 01342 01343 /* Set the isolation property */ 01344 if (obj && SQLSetConnectAttr(obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)obj->parent->isolation, 0) == SQL_ERROR) { 01345 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01346 for (i = 0; i < numfields; i++) { 01347 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01348 ast_log(LOG_WARNING, "SetConnectAttr (Txn isolation) returned an error: %s: %s\n", state, diagnostic); 01349 if (i > 10) { 01350 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01351 break; 01352 } 01353 } 01354 } 01355 01356 if (obj && ast_test_flag(&flags, RES_ODBC_CONNECTED) && !obj->up) { 01357 /* Check if this connection qualifies for reconnection, with negative connection cache time */ 01358 if (time(NULL) > class->last_negative_connect.tv_sec + class->negative_connection_cache.tv_sec) { 01359 odbc_obj_connect(obj); 01360 } 01361 } else if (obj && ast_test_flag(&flags, RES_ODBC_SANITY_CHECK)) { 01362 ast_odbc_sanity_check(obj); 01363 } else if (obj && obj->parent->idlecheck > 0 && ast_tvdiff_sec(ast_tvnow(), obj->last_used) > obj->parent->idlecheck) { 01364 odbc_obj_connect(obj); 01365 } 01366 01367 #ifdef DEBUG_THREADS 01368 if (obj) { 01369 ast_copy_string(obj->file, file, sizeof(obj->file)); 01370 ast_copy_string(obj->function, function, sizeof(obj->function)); 01371 obj->lineno = lineno; 01372 } 01373 #endif 01374 ast_assert(class == NULL); 01375 01376 if (obj) { 01377 ast_assert(ao2_ref(obj, 0) > 1); 01378 } 01379 return obj; 01380 }
static int acf_transaction_read | ( | struct ast_channel * | chan, | |
const char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 1512 of file res_odbc.c.
References args, 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.
01513 { 01514 AST_DECLARE_APP_ARGS(args, 01515 AST_APP_ARG(property); 01516 AST_APP_ARG(opt); 01517 ); 01518 struct odbc_txn_frame *tx; 01519 01520 AST_STANDARD_APP_ARGS(args, data); 01521 if (strcasecmp(args.property, "transaction") == 0) { 01522 if ((tx = find_transaction(chan, NULL, NULL, 1))) { 01523 ast_copy_string(buf, tx->name, len); 01524 return 0; 01525 } 01526 } else if (strcasecmp(args.property, "isolation") == 0) { 01527 if (!ast_strlen_zero(args.opt)) { 01528 tx = find_transaction(chan, NULL, args.opt, 0); 01529 } else { 01530 tx = find_transaction(chan, NULL, NULL, 1); 01531 } 01532 if (tx) { 01533 ast_copy_string(buf, isolation2text(tx->isolation), len); 01534 return 0; 01535 } 01536 } else if (strcasecmp(args.property, "forcecommit") == 0) { 01537 if (!ast_strlen_zero(args.opt)) { 01538 tx = find_transaction(chan, NULL, args.opt, 0); 01539 } else { 01540 tx = find_transaction(chan, NULL, NULL, 1); 01541 } 01542 if (tx) { 01543 ast_copy_string(buf, tx->forcecommit ? "1" : "0", len); 01544 return 0; 01545 } 01546 } 01547 return -1; 01548 }
static int acf_transaction_write | ( | struct ast_channel * | chan, | |
const char * | cmd, | |||
char * | s, | |||
const char * | value | |||
) | [static] |
Definition at line 1550 of file res_odbc.c.
References args, 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(), ast_flags::flags, odbc_txn_frame::forcecommit, odbc_txn_frame::isolation, LOG_ERROR, mark_transaction_active(), odbc_txn_frame::obj, pbx_builtin_setvar_helper(), RES_ODBC_INDEPENDENT_CONNECTION, S_OR, text2isolation(), and odbc_obj::tx.
01551 { 01552 AST_DECLARE_APP_ARGS(args, 01553 AST_APP_ARG(property); 01554 AST_APP_ARG(opt); 01555 ); 01556 struct odbc_txn_frame *tx; 01557 SQLINTEGER nativeerror=0, numfields=0; 01558 SQLSMALLINT diagbytes=0, i; 01559 unsigned char state[10], diagnostic[256]; 01560 01561 AST_STANDARD_APP_ARGS(args, s); 01562 if (strcasecmp(args.property, "transaction") == 0) { 01563 /* Set active transaction */ 01564 struct odbc_obj *obj; 01565 if ((tx = find_transaction(chan, NULL, value, 0))) { 01566 mark_transaction_active(chan, tx); 01567 } else { 01568 /* No such transaction, create one */ 01569 struct ast_flags flags = { RES_ODBC_INDEPENDENT_CONNECTION }; 01570 if (ast_strlen_zero(args.opt) || !(obj = ast_odbc_request_obj2(args.opt, flags))) { 01571 ast_log(LOG_ERROR, "Could not create transaction: invalid database specification '%s'\n", S_OR(args.opt, "")); 01572 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_DB"); 01573 return -1; 01574 } 01575 if (!(tx = find_transaction(chan, obj, value, 0))) { 01576 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE"); 01577 return -1; 01578 } 01579 obj->tx = 1; 01580 } 01581 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK"); 01582 return 0; 01583 } else if (strcasecmp(args.property, "forcecommit") == 0) { 01584 /* Set what happens when an uncommitted transaction ends without explicit Commit or Rollback */ 01585 if (ast_strlen_zero(args.opt)) { 01586 tx = find_transaction(chan, NULL, NULL, 1); 01587 } else { 01588 tx = find_transaction(chan, NULL, args.opt, 0); 01589 } 01590 if (!tx) { 01591 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE"); 01592 return -1; 01593 } 01594 if (ast_true(value)) { 01595 tx->forcecommit = 1; 01596 } else if (ast_false(value)) { 01597 tx->forcecommit = 0; 01598 } else { 01599 ast_log(LOG_ERROR, "Invalid value for forcecommit: '%s'\n", S_OR(value, "")); 01600 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE"); 01601 return -1; 01602 } 01603 01604 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK"); 01605 return 0; 01606 } else if (strcasecmp(args.property, "isolation") == 0) { 01607 /* How do uncommitted transactions affect reads? */ 01608 int isolation = text2isolation(value); 01609 if (ast_strlen_zero(args.opt)) { 01610 tx = find_transaction(chan, NULL, NULL, 1); 01611 } else { 01612 tx = find_transaction(chan, NULL, args.opt, 0); 01613 } 01614 if (!tx) { 01615 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE"); 01616 return -1; 01617 } 01618 if (isolation == 0) { 01619 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE"); 01620 ast_log(LOG_ERROR, "Invalid isolation specification: '%s'\n", S_OR(value, "")); 01621 } else if (SQLSetConnectAttr(tx->obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)isolation, 0) == SQL_ERROR) { 01622 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "SQL_ERROR"); 01623 SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01624 for (i = 0; i < numfields; i++) { 01625 SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01626 ast_log(LOG_WARNING, "SetConnectAttr (Txn isolation) returned an error: %s: %s\n", state, diagnostic); 01627 if (i > 10) { 01628 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01629 break; 01630 } 01631 } 01632 } else { 01633 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK"); 01634 tx->isolation = isolation; 01635 } 01636 return 0; 01637 } else { 01638 ast_log(LOG_ERROR, "Unknown property: '%s'\n", args.property); 01639 return -1; 01640 } 01641 }
static int aoro2_class_cb | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 1172 of file res_odbc.c.
References CMP_MATCH, and CMP_STOP.
Referenced by _ast_odbc_request_obj2().
01173 { 01174 struct odbc_class *class = obj; 01175 char *name = arg; 01176 if (!strcmp(class->name, name) && !class->delme) { 01177 return CMP_MATCH | CMP_STOP; 01178 } 01179 return 0; 01180 }
static int aoro2_obj_cb | ( | void * | vobj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 1186 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().
01187 { 01188 struct odbc_obj *obj = vobj; 01189 ast_mutex_lock(&obj->lock); 01190 if ((arg == NO_TX && !obj->tx) || (arg == EOR_TX && !obj->used) || (arg == USE_TX && obj->tx && !obj->used)) { 01191 obj->used = 1; 01192 ast_mutex_unlock(&obj->lock); 01193 return CMP_MATCH | CMP_STOP; 01194 } 01195 ast_mutex_unlock(&obj->lock); 01196 return 0; 01197 }
AST_DATA_STRUCTURE | ( | odbc_class | , | |
DATA_EXPORT_ODBC_CLASS | ||||
) |
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 698 of file res_odbc.c.
References ast_str_buffer(), ast_str_make_space(), ast_str_size(), and ast_str_update().
00699 { 00700 SQLRETURN res; 00701 00702 if (pmaxlen == 0) { 00703 if (SQLGetData(StatementHandle, ColumnNumber, TargetType, ast_str_buffer(*buf), 0, StrLen_or_Ind) == SQL_SUCCESS_WITH_INFO) { 00704 ast_str_make_space(buf, *StrLen_or_Ind + 1); 00705 } 00706 } else if (pmaxlen > 0) { 00707 ast_str_make_space(buf, pmaxlen); 00708 } 00709 res = SQLGetData(StatementHandle, ColumnNumber, TargetType, ast_str_buffer(*buf), ast_str_size(*buf), StrLen_or_Ind); 00710 ast_str_update(*buf); 00711 00712 return res; 00713 }
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 1093 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().
01094 { 01095 return obj->parent->backslash_is_escape; 01096 }
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 569 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.
00570 { 00571 struct odbc_cache_tables *tableptr; 00572 00573 AST_RWLIST_WRLOCK(&odbc_tables); 00574 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&odbc_tables, tableptr, list) { 00575 if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) { 00576 AST_LIST_REMOVE_CURRENT(list); 00577 destroy_table_cache(tableptr); 00578 break; 00579 } 00580 } 00581 AST_RWLIST_TRAVERSE_SAFE_END 00582 AST_RWLIST_UNLOCK(&odbc_tables); 00583 return tableptr ? 0 : -1; 00584 }
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 586 of file res_odbc.c.
References ast_log(), ast_odbc_sanity_check(), and LOG_WARNING.
Referenced by acf_odbc_read(), acf_odbc_write(), and odbc_log().
00587 { 00588 int attempt; 00589 SQLHSTMT stmt; 00590 00591 for (attempt = 0; attempt < 2; attempt++) { 00592 stmt = exec_cb(obj, data); 00593 00594 if (stmt) { 00595 break; 00596 } else if (obj->tx) { 00597 ast_log(LOG_WARNING, "Failed to execute, but unable to reconnect, as we're transactional.\n"); 00598 break; 00599 } else if (attempt == 0) { 00600 ast_log(LOG_WARNING, "SQL Execute error! Verifying connection to %s [%s]...\n", obj->parent->name, obj->parent->dsn); 00601 } 00602 if (!ast_odbc_sanity_check(obj)) { 00603 break; 00604 } 00605 } 00606 00607 return stmt; 00608 }
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 558 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().
00559 { 00560 struct odbc_cache_columns *col; 00561 AST_RWLIST_TRAVERSE(&table->columns, col, list) { 00562 if (strcasecmp(col->name, colname) == 0) { 00563 return col; 00564 } 00565 } 00566 return NULL; 00567 }
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 443 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, destroy_table_cache(), odbc_class::list, LOG_ERROR, LOG_WARNING, and odbc_cache_tables::table.
Referenced by require_odbc(), update2_prepare(), and update_odbc().
00444 { 00445 struct odbc_cache_tables *tableptr; 00446 struct odbc_cache_columns *entry; 00447 char columnname[80]; 00448 SQLLEN sqlptr; 00449 SQLHSTMT stmt = NULL; 00450 int res = 0, error = 0, try = 0; 00451 struct odbc_obj *obj = ast_odbc_request_obj(database, 0); 00452 00453 AST_RWLIST_RDLOCK(&odbc_tables); 00454 AST_RWLIST_TRAVERSE(&odbc_tables, tableptr, list) { 00455 if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) { 00456 break; 00457 } 00458 } 00459 if (tableptr) { 00460 AST_RWLIST_RDLOCK(&tableptr->columns); 00461 AST_RWLIST_UNLOCK(&odbc_tables); 00462 if (obj) { 00463 ast_odbc_release_obj(obj); 00464 } 00465 return tableptr; 00466 } 00467 00468 if (!obj) { 00469 ast_log(LOG_WARNING, "Unable to retrieve database handle for table description '%s@%s'\n", tablename, database); 00470 AST_RWLIST_UNLOCK(&odbc_tables); 00471 return NULL; 00472 } 00473 00474 /* Table structure not already cached; build it now. */ 00475 do { 00476 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt); 00477 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00478 if (try == 0) { 00479 try = 1; 00480 ast_odbc_sanity_check(obj); 00481 continue; 00482 } 00483 ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", database); 00484 break; 00485 } 00486 00487 res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)tablename, SQL_NTS, (unsigned char *)"%", SQL_NTS); 00488 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00489 if (try == 0) { 00490 try = 1; 00491 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 00492 ast_odbc_sanity_check(obj); 00493 continue; 00494 } 00495 ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'.\n", database); 00496 break; 00497 } 00498 00499 if (!(tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + strlen(database) + 1 + strlen(tablename) + 1))) { 00500 ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", tablename, database); 00501 break; 00502 } 00503 00504 tableptr->connection = (char *)tableptr + sizeof(*tableptr); 00505 tableptr->table = (char *)tableptr + sizeof(*tableptr) + strlen(database) + 1; 00506 strcpy(tableptr->connection, database); /* SAFE */ 00507 strcpy(tableptr->table, tablename); /* SAFE */ 00508 AST_RWLIST_HEAD_INIT(&(tableptr->columns)); 00509 00510 while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) { 00511 SQLGetData(stmt, 4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr); 00512 00513 if (!(entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1))) { 00514 ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, tablename, database); 00515 error = 1; 00516 break; 00517 } 00518 entry->name = (char *)entry + sizeof(*entry); 00519 strcpy(entry->name, columnname); 00520 00521 SQLGetData(stmt, 5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL); 00522 SQLGetData(stmt, 7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL); 00523 SQLGetData(stmt, 9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL); 00524 SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL); 00525 SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL); 00526 SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL); 00527 00528 /* Specification states that the octenlen should be the maximum number of bytes 00529 * returned in a char or binary column, but it seems that some drivers just set 00530 * it to NULL. (Bad Postgres! No biscuit!) */ 00531 if (entry->octetlen == 0) { 00532 entry->octetlen = entry->size; 00533 } 00534 00535 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); 00536 /* Insert column info into column list */ 00537 AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list); 00538 } 00539 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 00540 00541 AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list); 00542 AST_RWLIST_RDLOCK(&(tableptr->columns)); 00543 break; 00544 } while (1); 00545 00546 AST_RWLIST_UNLOCK(&odbc_tables); 00547 00548 if (error) { 00549 destroy_table_cache(tableptr); 00550 tableptr = NULL; 00551 } 00552 if (obj) { 00553 ast_odbc_release_obj(obj); 00554 } 00555 return tableptr; 00556 }
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 610 of file res_odbc.c.
References ast_log(), 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().
00611 { 00612 int res = 0, i, attempt; 00613 SQLINTEGER nativeerror=0, numfields=0; 00614 SQLSMALLINT diagbytes=0; 00615 unsigned char state[10], diagnostic[256]; 00616 SQLHSTMT stmt; 00617 00618 for (attempt = 0; attempt < 2; attempt++) { 00619 /* This prepare callback may do more than just prepare -- it may also 00620 * bind parameters, bind results, etc. The real key, here, is that 00621 * when we disconnect, all handles become invalid for most databases. 00622 * We must therefore redo everything when we establish a new 00623 * connection. */ 00624 stmt = prepare_cb(obj, data); 00625 00626 if (stmt) { 00627 res = SQLExecute(stmt); 00628 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) { 00629 if (res == SQL_ERROR) { 00630 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 00631 for (i = 0; i < numfields; i++) { 00632 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 00633 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes); 00634 if (i > 10) { 00635 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 00636 break; 00637 } 00638 } 00639 } 00640 00641 if (obj->tx) { 00642 ast_log(LOG_WARNING, "SQL Execute error, but unable to reconnect, as we're transactional.\n"); 00643 break; 00644 } else { 00645 ast_log(LOG_WARNING, "SQL Execute error %d! Verifying connection to %s [%s]...\n", res, obj->parent->name, obj->parent->dsn); 00646 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 00647 stmt = NULL; 00648 00649 obj->up = 0; 00650 /* 00651 * While this isn't the best way to try to correct an error, this won't automatically 00652 * fail when the statement handle invalidates. 00653 */ 00654 if (!ast_odbc_sanity_check(obj)) { 00655 break; 00656 } 00657 continue; 00658 } 00659 } else { 00660 obj->last_used = ast_tvnow(); 00661 } 00662 break; 00663 } else if (attempt == 0) { 00664 ast_odbc_sanity_check(obj); 00665 } 00666 } 00667 00668 return stmt; 00669 }
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 1087 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().
01088 { 01089 struct odbc_txn_frame *tx = find_transaction(NULL, obj, NULL, 0); 01090 odbc_release_obj2(obj, tx); 01091 }
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 1388 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().
01389 { 01390 struct ast_datastore *txn_store; 01391 AST_LIST_HEAD(, odbc_txn_frame) *oldlist; 01392 struct odbc_txn_frame *txn = NULL; 01393 01394 if (!chan) { 01395 /* No channel == no transaction */ 01396 return NULL; 01397 } 01398 01399 ast_channel_lock(chan); 01400 if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) { 01401 oldlist = txn_store->data; 01402 } else { 01403 ast_channel_unlock(chan); 01404 return NULL; 01405 } 01406 01407 AST_LIST_LOCK(oldlist); 01408 ast_channel_unlock(chan); 01409 01410 AST_LIST_TRAVERSE(oldlist, txn, list) { 01411 if (txn->obj && txn->obj->parent && !strcmp(txn->obj->parent->name, objname)) { 01412 AST_LIST_UNLOCK(oldlist); 01413 return txn->obj; 01414 } 01415 } 01416 AST_LIST_UNLOCK(oldlist); 01417 return NULL; 01418 }
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 715 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().
00716 { 00717 char *test_sql = "select 1"; 00718 SQLHSTMT stmt; 00719 int res = 0; 00720 00721 if (!ast_strlen_zero(obj->parent->sanitysql)) 00722 test_sql = obj->parent->sanitysql; 00723 00724 if (obj->up) { 00725 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt); 00726 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00727 obj->up = 0; 00728 } else { 00729 res = SQLPrepare(stmt, (unsigned char *)test_sql, SQL_NTS); 00730 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00731 obj->up = 0; 00732 } else { 00733 res = SQLExecute(stmt); 00734 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00735 obj->up = 0; 00736 } 00737 } 00738 } 00739 SQLFreeHandle (SQL_HANDLE_STMT, stmt); 00740 } 00741 00742 if (!obj->up && !obj->tx) { /* Try to reconnect! */ 00743 ast_log(LOG_WARNING, "Connection is down attempting to reconnect...\n"); 00744 odbc_obj_disconnect(obj); 00745 odbc_obj_connect(obj); 00746 } 00747 return obj->up; 00748 }
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 671 of file res_odbc.c.
References ast_log(), ast_tvnow(), odbc_obj::last_used, and LOG_WARNING.
00672 { 00673 int res = 0, i; 00674 SQLINTEGER nativeerror=0, numfields=0; 00675 SQLSMALLINT diagbytes=0; 00676 unsigned char state[10], diagnostic[256]; 00677 00678 res = SQLExecute(stmt); 00679 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) { 00680 if (res == SQL_ERROR) { 00681 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 00682 for (i = 0; i < numfields; i++) { 00683 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 00684 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes); 00685 if (i > 10) { 00686 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 00687 break; 00688 } 00689 } 00690 } 00691 } else { 00692 obj->last_used = ast_tvnow(); 00693 } 00694 00695 return res; 00696 }
static int commit_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
Definition at line 1098 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().
01099 { 01100 struct odbc_txn_frame *tx; 01101 SQLINTEGER nativeerror=0, numfields=0; 01102 SQLSMALLINT diagbytes=0, i; 01103 unsigned char state[10], diagnostic[256]; 01104 01105 if (ast_strlen_zero(data)) { 01106 tx = find_transaction(chan, NULL, NULL, 1); 01107 } else { 01108 tx = find_transaction(chan, NULL, data, 0); 01109 } 01110 01111 pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", "OK"); 01112 01113 if (tx) { 01114 if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_COMMIT) == SQL_ERROR) { 01115 struct ast_str *errors = ast_str_thread_get(&errors_buf, 16); 01116 ast_str_reset(errors); 01117 01118 /* Handle possible transaction commit failure */ 01119 SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01120 for (i = 0; i < numfields; i++) { 01121 SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01122 ast_str_append(&errors, 0, "%s%s", ast_str_strlen(errors) ? "," : "", state); 01123 ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic); 01124 if (i > 10) { 01125 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01126 break; 01127 } 01128 } 01129 pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", ast_str_buffer(errors)); 01130 } 01131 } 01132 return 0; 01133 }
static int data_odbc_provider_handler | ( | const struct ast_data_search * | search, | |
struct ast_data * | root | |||
) | [static] |
Definition at line 1656 of file res_odbc.c.
References ao2_container_count(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_data_add_bool(), ast_data_add_int(), ast_data_add_node(), ast_data_add_str(), ast_data_add_structure, ast_data_remove_node(), ast_data_search_match(), ast_mutex_lock, ast_mutex_unlock, ast_odbc_sanity_check(), class_container, isolation2text(), odbc_obj::lock, odbc_obj::tx, odbc_obj::up, and odbc_obj::used.
01658 { 01659 struct ao2_iterator aoi, aoi2; 01660 struct odbc_class *class; 01661 struct odbc_obj *current; 01662 struct ast_data *data_odbc_class, *data_odbc_connections, *data_odbc_connection; 01663 struct ast_data *enum_node; 01664 int count; 01665 01666 aoi = ao2_iterator_init(class_container, 0); 01667 while ((class = ao2_iterator_next(&aoi))) { 01668 data_odbc_class = ast_data_add_node(root, "class"); 01669 if (!data_odbc_class) { 01670 ao2_ref(class, -1); 01671 continue; 01672 } 01673 01674 ast_data_add_structure(odbc_class, data_odbc_class, class); 01675 01676 if (!ao2_container_count(class->obj_container)) { 01677 ao2_ref(class, -1); 01678 continue; 01679 } 01680 01681 data_odbc_connections = ast_data_add_node(data_odbc_class, "connections"); 01682 if (!data_odbc_connections) { 01683 ao2_ref(class, -1); 01684 continue; 01685 } 01686 01687 ast_data_add_bool(data_odbc_class, "shared", !class->haspool); 01688 /* isolation */ 01689 enum_node = ast_data_add_node(data_odbc_class, "isolation"); 01690 if (!enum_node) { 01691 ao2_ref(class, -1); 01692 continue; 01693 } 01694 ast_data_add_int(enum_node, "value", class->isolation); 01695 ast_data_add_str(enum_node, "text", isolation2text(class->isolation)); 01696 01697 count = 0; 01698 aoi2 = ao2_iterator_init(class->obj_container, 0); 01699 while ((current = ao2_iterator_next(&aoi2))) { 01700 data_odbc_connection = ast_data_add_node(data_odbc_connections, "connection"); 01701 if (!data_odbc_connection) { 01702 ao2_ref(current, -1); 01703 continue; 01704 } 01705 01706 ast_mutex_lock(¤t->lock); 01707 ast_data_add_str(data_odbc_connection, "status", current->used ? "in use" : 01708 current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected"); 01709 ast_data_add_bool(data_odbc_connection, "transactional", current->tx); 01710 ast_mutex_unlock(¤t->lock); 01711 01712 if (class->haspool) { 01713 ast_data_add_int(data_odbc_connection, "number", ++count); 01714 } 01715 01716 ao2_ref(current, -1); 01717 } 01718 ao2_ref(class, -1); 01719 01720 if (!ast_data_search_match(search, data_odbc_class)) { 01721 ast_data_remove_node(root, data_odbc_class); 01722 } 01723 } 01724 return 0; 01725 }
static void destroy_table_cache | ( | struct odbc_cache_tables * | table | ) | [static] |
Definition at line 422 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_class::list, and table.
Referenced by ast_odbc_clear_cache(), ast_odbc_find_table(), and reload().
00422 { 00423 struct odbc_cache_columns *col; 00424 ast_debug(1, "Destroying table cache for %s\n", table->table); 00425 AST_RWLIST_WRLOCK(&table->columns); 00426 while ((col = AST_RWLIST_REMOVE_HEAD(&table->columns, list))) { 00427 ast_free(col); 00428 } 00429 AST_RWLIST_UNLOCK(&table->columns); 00430 AST_RWLIST_HEAD_DESTROY(&table->columns); 00431 ast_free(table); 00432 }
static struct odbc_txn_frame* find_transaction | ( | struct ast_channel * | chan, | |
struct odbc_obj * | obj, | |||
const char * | name, | |||
int | active | |||
) | [static] |
Definition at line 222 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_class::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.
00223 { 00224 struct ast_datastore *txn_store; 00225 AST_LIST_HEAD(, odbc_txn_frame) *oldlist; 00226 struct odbc_txn_frame *txn = NULL; 00227 00228 if (!chan && obj && obj->txf && obj->txf->owner) { 00229 chan = obj->txf->owner; 00230 } else if (!chan) { 00231 /* No channel == no transaction */ 00232 return NULL; 00233 } 00234 00235 ast_channel_lock(chan); 00236 if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) { 00237 oldlist = txn_store->data; 00238 } else { 00239 /* Need to create a new datastore */ 00240 if (!(txn_store = ast_datastore_alloc(&txn_info, NULL))) { 00241 ast_log(LOG_ERROR, "Unable to allocate a new datastore. Cannot create a new transaction.\n"); 00242 ast_channel_unlock(chan); 00243 return NULL; 00244 } 00245 00246 if (!(oldlist = ast_calloc(1, sizeof(*oldlist)))) { 00247 ast_log(LOG_ERROR, "Unable to allocate datastore list head. Cannot create a new transaction.\n"); 00248 ast_datastore_free(txn_store); 00249 ast_channel_unlock(chan); 00250 return NULL; 00251 } 00252 00253 txn_store->data = oldlist; 00254 AST_LIST_HEAD_INIT(oldlist); 00255 ast_channel_datastore_add(chan, txn_store); 00256 } 00257 00258 AST_LIST_LOCK(oldlist); 00259 ast_channel_unlock(chan); 00260 00261 /* Scanning for an object is *fast*. Scanning for a name is much slower. */ 00262 if (obj != NULL || active == 1) { 00263 AST_LIST_TRAVERSE(oldlist, txn, list) { 00264 if (txn->obj == obj || txn->active) { 00265 AST_LIST_UNLOCK(oldlist); 00266 return txn; 00267 } 00268 } 00269 } 00270 00271 if (name != NULL) { 00272 AST_LIST_TRAVERSE(oldlist, txn, list) { 00273 if (!strcasecmp(txn->name, name)) { 00274 AST_LIST_UNLOCK(oldlist); 00275 return txn; 00276 } 00277 } 00278 } 00279 00280 /* Nothing found, create one */ 00281 if (name && obj && (txn = ast_calloc(1, sizeof(*txn) + strlen(name) + 1))) { 00282 struct odbc_txn_frame *otxn; 00283 00284 strcpy(txn->name, name); /* SAFE */ 00285 txn->obj = obj; 00286 txn->isolation = obj->parent->isolation; 00287 txn->forcecommit = obj->parent->forcecommit; 00288 txn->owner = chan; 00289 txn->active = 1; 00290 00291 /* On creation, the txn becomes active, and all others inactive */ 00292 AST_LIST_TRAVERSE(oldlist, otxn, list) { 00293 otxn->active = 0; 00294 } 00295 AST_LIST_INSERT_TAIL(oldlist, txn, list); 00296 00297 obj->txf = txn; 00298 obj->tx = 1; 00299 } 00300 AST_LIST_UNLOCK(oldlist); 00301 00302 return txn; 00303 }
static char* handle_cli_odbc_show | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 911 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_localtime(), ast_mutex_lock, ast_mutex_unlock, ast_odbc_sanity_check(), ast_strdup, ast_strftime(), class_container, CLI_GENERATE, CLI_INIT, CLI_SUCCESS, ast_cli_entry::command, 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.
00912 { 00913 struct ao2_iterator aoi = ao2_iterator_init(class_container, 0); 00914 struct odbc_class *class; 00915 struct odbc_obj *current; 00916 int length = 0; 00917 int which = 0; 00918 char *ret = NULL; 00919 00920 switch (cmd) { 00921 case CLI_INIT: 00922 e->command = "odbc show"; 00923 e->usage = 00924 "Usage: odbc show [class]\n" 00925 " List settings of a particular ODBC class or,\n" 00926 " if not specified, all classes.\n"; 00927 return NULL; 00928 case CLI_GENERATE: 00929 if (a->pos != 2) 00930 return NULL; 00931 length = strlen(a->word); 00932 while ((class = ao2_iterator_next(&aoi))) { 00933 if (!strncasecmp(a->word, class->name, length) && ++which > a->n) { 00934 ret = ast_strdup(class->name); 00935 } 00936 ao2_ref(class, -1); 00937 if (ret) { 00938 break; 00939 } 00940 } 00941 ao2_iterator_destroy(&aoi); 00942 if (!ret && !strncasecmp(a->word, "all", length) && ++which > a->n) { 00943 ret = ast_strdup("all"); 00944 } 00945 return ret; 00946 } 00947 00948 ast_cli(a->fd, "\nODBC DSN Settings\n"); 00949 ast_cli(a->fd, "-----------------\n\n"); 00950 aoi = ao2_iterator_init(class_container, 0); 00951 while ((class = ao2_iterator_next(&aoi))) { 00952 if ((a->argc == 2) || (a->argc == 3 && !strcmp(a->argv[2], "all")) || (!strcmp(a->argv[2], class->name))) { 00953 int count = 0; 00954 char timestr[80]; 00955 struct ast_tm tm; 00956 00957 ast_localtime(&class->last_negative_connect, &tm, NULL); 00958 ast_strftime(timestr, sizeof(timestr), "%Y-%m-%d %T", &tm); 00959 ast_cli(a->fd, " Name: %s\n DSN: %s\n", class->name, class->dsn); 00960 ast_cli(a->fd, " Last connection attempt: %s\n", timestr); 00961 00962 if (class->haspool) { 00963 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0); 00964 00965 ast_cli(a->fd, " Pooled: Yes\n Limit: %d\n Connections in use: %d\n", class->limit, class->count); 00966 00967 while ((current = ao2_iterator_next(&aoi2))) { 00968 ast_mutex_lock(¤t->lock); 00969 #ifdef DEBUG_THREADS 00970 ast_cli(a->fd, " - Connection %d: %s (%s:%d %s)\n", ++count, 00971 current->used ? "in use" : 00972 current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected", 00973 current->file, current->lineno, current->function); 00974 #else 00975 ast_cli(a->fd, " - Connection %d: %s\n", ++count, 00976 current->used ? "in use" : 00977 current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected"); 00978 #endif 00979 ast_mutex_unlock(¤t->lock); 00980 ao2_ref(current, -1); 00981 } 00982 ao2_iterator_destroy(&aoi2); 00983 } else { 00984 /* Should only ever be one of these (unless there are transactions) */ 00985 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0); 00986 while ((current = ao2_iterator_next(&aoi2))) { 00987 ast_cli(a->fd, " Pooled: No\n Connected: %s\n", current->used ? "In use" : 00988 current->up && ast_odbc_sanity_check(current) ? "Yes" : "No"); 00989 ao2_ref(current, -1); 00990 } 00991 ao2_iterator_destroy(&aoi2); 00992 } 00993 ast_cli(a->fd, "\n"); 00994 } 00995 ao2_ref(class, -1); 00996 } 00997 ao2_iterator_destroy(&aoi); 00998 00999 return CLI_SUCCESS; 01000 }
static const char* isolation2text | ( | int | iso | ) | [static] |
Definition at line 188 of file res_odbc.c.
Referenced by acf_transaction_read(), and data_odbc_provider_handler().
00189 { 00190 if (iso == SQL_TXN_READ_COMMITTED) { 00191 return "read_committed"; 00192 } else if (iso == SQL_TXN_READ_UNCOMMITTED) { 00193 return "read_uncommitted"; 00194 } else if (iso == SQL_TXN_SERIALIZABLE) { 00195 return "serializable"; 00196 } else if (iso == SQL_TXN_REPEATABLE_READ) { 00197 return "repeatable_read"; 00198 } else { 00199 return "unknown"; 00200 } 00201 }
static int load_module | ( | void | ) | [static] |
Definition at line 1820 of file res_odbc.c.
References ao2_container_alloc, ao2_match_by_addr, ARRAY_LEN, ast_cli_register_multiple(), ast_custom_function_register, ast_data_register_multiple, 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, odbc_providers, and rollback_exec().
01821 { 01822 if (!(class_container = ao2_container_alloc(1, null_hash_fn, ao2_match_by_addr))) 01823 return AST_MODULE_LOAD_DECLINE; 01824 if (load_odbc_config() == -1) 01825 return AST_MODULE_LOAD_DECLINE; 01826 ast_cli_register_multiple(cli_odbc, ARRAY_LEN(cli_odbc)); 01827 ast_data_register_multiple(odbc_providers, ARRAY_LEN(odbc_providers)); 01828 ast_register_application_xml(app_commit, commit_exec); 01829 ast_register_application_xml(app_rollback, rollback_exec); 01830 ast_custom_function_register(&odbc_function); 01831 ast_log(LOG_NOTICE, "res_odbc loaded.\n"); 01832 return 0; 01833 }
static int load_odbc_config | ( | void | ) | [static] |
Definition at line 750 of file res_odbc.c.
References ast_category_browse(), ast_config_load, ast_false(), ast_log(), ast_true(), ast_variable_browse(), config, config_flags, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEMISSING, odbc_class::conntimeout, odbc_class::dsn, enabled, odbc_class::idlecheck, odbc_class::limit, LOG_NOTICE, LOG_WARNING, ast_variable::name, ast_variable::next, odbc_class::password, odbc_class::sanitysql, setenv(), odbc_class::username, and ast_variable::value.
Referenced by load_module(), and reload().
00751 { 00752 static char *cfg = "res_odbc.conf"; 00753 struct ast_config *config; 00754 struct ast_variable *v; 00755 char *cat; 00756 const char *dsn, *username, *password, *sanitysql; 00757 int enabled, pooling, limit, bse, conntimeout, forcecommit, isolation; 00758 struct timeval ncache = { 0, 0 }; 00759 unsigned int idlecheck; 00760 int preconnect = 0, res = 0; 00761 struct ast_flags config_flags = { 0 }; 00762 00763 struct odbc_class *new; 00764 00765 config = ast_config_load(cfg, config_flags); 00766 if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) { 00767 ast_log(LOG_WARNING, "Unable to load config file res_odbc.conf\n"); 00768 return -1; 00769 } 00770 for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) { 00771 if (!strcasecmp(cat, "ENV")) { 00772 for (v = ast_variable_browse(config, cat); v; v = v->next) { 00773 setenv(v->name, v->value, 1); 00774 ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value); 00775 } 00776 } else { 00777 /* Reset all to defaults for each class of odbc connections */ 00778 dsn = username = password = sanitysql = NULL; 00779 enabled = 1; 00780 preconnect = idlecheck = 0; 00781 pooling = 0; 00782 limit = 0; 00783 bse = 1; 00784 conntimeout = 10; 00785 forcecommit = 0; 00786 isolation = SQL_TXN_READ_COMMITTED; 00787 for (v = ast_variable_browse(config, cat); v; v = v->next) { 00788 if (!strcasecmp(v->name, "pooling")) { 00789 if (ast_true(v->value)) 00790 pooling = 1; 00791 } else if (!strncasecmp(v->name, "share", 5)) { 00792 /* "shareconnections" is a little clearer in meaning than "pooling" */ 00793 if (ast_false(v->value)) 00794 pooling = 1; 00795 } else if (!strcasecmp(v->name, "limit")) { 00796 sscanf(v->value, "%30d", &limit); 00797 if (ast_true(v->value) && !limit) { 00798 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); 00799 limit = 1023; 00800 } else if (ast_false(v->value)) { 00801 ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'. Disabling ODBC class '%s'.\n", v->value, cat); 00802 enabled = 0; 00803 break; 00804 } 00805 } else if (!strcasecmp(v->name, "idlecheck")) { 00806 sscanf(v->value, "%30u", &idlecheck); 00807 } else if (!strcasecmp(v->name, "enabled")) { 00808 enabled = ast_true(v->value); 00809 } else if (!strcasecmp(v->name, "pre-connect")) { 00810 preconnect = ast_true(v->value); 00811 } else if (!strcasecmp(v->name, "dsn")) { 00812 dsn = v->value; 00813 } else if (!strcasecmp(v->name, "username")) { 00814 username = v->value; 00815 } else if (!strcasecmp(v->name, "password")) { 00816 password = v->value; 00817 } else if (!strcasecmp(v->name, "sanitysql")) { 00818 sanitysql = v->value; 00819 } else if (!strcasecmp(v->name, "backslash_is_escape")) { 00820 bse = ast_true(v->value); 00821 } else if (!strcasecmp(v->name, "connect_timeout")) { 00822 if (sscanf(v->value, "%d", &conntimeout) != 1 || conntimeout < 1) { 00823 ast_log(LOG_WARNING, "connect_timeout must be a positive integer\n"); 00824 conntimeout = 10; 00825 } 00826 } else if (!strcasecmp(v->name, "negative_connection_cache")) { 00827 double dncache; 00828 if (sscanf(v->value, "%lf", &dncache) != 1 || dncache < 0) { 00829 ast_log(LOG_WARNING, "negative_connection_cache must be a non-negative integer\n"); 00830 /* 5 minutes sounds like a reasonable default */ 00831 ncache.tv_sec = 300; 00832 ncache.tv_usec = 0; 00833 } else { 00834 ncache.tv_sec = (int)dncache; 00835 ncache.tv_usec = (dncache - ncache.tv_sec) * 1000000; 00836 } 00837 } else if (!strcasecmp(v->name, "forcecommit")) { 00838 forcecommit = ast_true(v->value); 00839 } else if (!strcasecmp(v->name, "isolation")) { 00840 if ((isolation = text2isolation(v->value)) == 0) { 00841 ast_log(LOG_ERROR, "Unrecognized value for 'isolation': '%s' in section '%s'\n", v->value, cat); 00842 isolation = SQL_TXN_READ_COMMITTED; 00843 } 00844 } 00845 } 00846 00847 if (enabled && !ast_strlen_zero(dsn)) { 00848 new = ao2_alloc(sizeof(*new), odbc_class_destructor); 00849 00850 if (!new) { 00851 res = -1; 00852 break; 00853 } 00854 00855 SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env); 00856 res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0); 00857 00858 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00859 ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n"); 00860 ao2_ref(new, -1); 00861 return res; 00862 } 00863 00864 new->obj_container = ao2_container_alloc(1, null_hash_fn, ao2_match_by_addr); 00865 00866 if (pooling) { 00867 new->haspool = pooling; 00868 if (limit) { 00869 new->limit = limit; 00870 } else { 00871 ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless. Changing limit from 0 to 5.\n"); 00872 new->limit = 5; 00873 } 00874 } 00875 00876 new->backslash_is_escape = bse ? 1 : 0; 00877 new->forcecommit = forcecommit ? 1 : 0; 00878 new->isolation = isolation; 00879 new->idlecheck = idlecheck; 00880 new->conntimeout = conntimeout; 00881 new->negative_connection_cache = ncache; 00882 00883 if (cat) 00884 ast_copy_string(new->name, cat, sizeof(new->name)); 00885 if (dsn) 00886 ast_copy_string(new->dsn, dsn, sizeof(new->dsn)); 00887 if (username && !(new->username = ast_strdup(username))) { 00888 ao2_ref(new, -1); 00889 break; 00890 } 00891 if (password && !(new->password = ast_strdup(password))) { 00892 ao2_ref(new, -1); 00893 break; 00894 } 00895 if (sanitysql && !(new->sanitysql = ast_strdup(sanitysql))) { 00896 ao2_ref(new, -1); 00897 break; 00898 } 00899 00900 odbc_register_class(new, preconnect); 00901 ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn); 00902 ao2_ref(new, -1); 00903 new = NULL; 00904 } 00905 } 00906 } 00907 ast_config_destroy(config); 00908 return res; 00909 }
static int mark_transaction_active | ( | struct ast_channel * | chan, | |
struct odbc_txn_frame * | tx | |||
) | [static] |
Definition at line 357 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().
00358 { 00359 struct ast_datastore *txn_store; 00360 AST_LIST_HEAD(, odbc_txn_frame) *oldlist; 00361 struct odbc_txn_frame *active = NULL, *txn; 00362 00363 if (!chan && tx && tx->owner) { 00364 chan = tx->owner; 00365 } 00366 00367 ast_channel_lock(chan); 00368 if (!(txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) { 00369 ast_channel_unlock(chan); 00370 return -1; 00371 } 00372 00373 oldlist = txn_store->data; 00374 AST_LIST_LOCK(oldlist); 00375 AST_LIST_TRAVERSE(oldlist, txn, list) { 00376 if (txn == tx) { 00377 txn->active = 1; 00378 active = txn; 00379 } else { 00380 txn->active = 0; 00381 } 00382 } 00383 AST_LIST_UNLOCK(oldlist); 00384 ast_channel_unlock(chan); 00385 return active ? 0 : -1; 00386 }
static int null_hash_fn | ( | const void * | obj, | |
const int | flags | |||
) | [static] |
static void odbc_class_destructor | ( | void * | data | ) | [static] |
Definition at line 388 of file res_odbc.c.
References ao2_ref, and ast_free.
00389 { 00390 struct odbc_class *class = data; 00391 /* Due to refcounts, we can safely assume that any objects with a reference 00392 * to us will prevent our destruction, so we don't need to worry about them. 00393 */ 00394 if (class->username) { 00395 ast_free(class->username); 00396 } 00397 if (class->password) { 00398 ast_free(class->password); 00399 } 00400 if (class->sanitysql) { 00401 ast_free(class->sanitysql); 00402 } 00403 ao2_ref(class->obj_container, -1); 00404 SQLFreeHandle(SQL_HANDLE_ENV, class->env); 00405 }
static odbc_status odbc_obj_connect | ( | struct odbc_obj * | obj | ) | [static] |
Definition at line 1457 of file res_odbc.c.
References ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_tvnow(), odbc_obj::con, odbc_class::conntimeout, odbc_class::dsn, odbc_class::env, odbc_class::last_negative_connect, odbc_obj::last_used, odbc_obj::lock, LOG_NOTICE, odbc_class::name, odbc_txn_frame::obj, ODBC_FAIL, odbc_obj_disconnect(), ODBC_SUCCESS, odbc_obj::parent, odbc_class::password, odbc_obj::up, and odbc_class::username.
Referenced by _ast_odbc_request_obj2(), and ast_odbc_sanity_check().
01458 { 01459 int res; 01460 SQLINTEGER err; 01461 short int mlen; 01462 unsigned char msg[200], state[10]; 01463 #ifdef NEEDTRACE 01464 SQLINTEGER enable = 1; 01465 char *tracefile = "/tmp/odbc.trace"; 01466 #endif 01467 ast_mutex_lock(&obj->lock); 01468 01469 if (obj->up) { 01470 odbc_obj_disconnect(obj); 01471 ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name); 01472 } else { 01473 ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name); 01474 } 01475 01476 res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &obj->con); 01477 01478 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 01479 ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res); 01480 obj->parent->last_negative_connect = ast_tvnow(); 01481 ast_mutex_unlock(&obj->lock); 01482 return ODBC_FAIL; 01483 } 01484 SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)(long) obj->parent->conntimeout, 0); 01485 SQLSetConnectAttr(obj->con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *)(long) obj->parent->conntimeout, 0); 01486 #ifdef NEEDTRACE 01487 SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER); 01488 SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile)); 01489 #endif 01490 01491 res = SQLConnect(obj->con, 01492 (SQLCHAR *) obj->parent->dsn, SQL_NTS, 01493 (SQLCHAR *) obj->parent->username, SQL_NTS, 01494 (SQLCHAR *) obj->parent->password, SQL_NTS); 01495 01496 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 01497 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, state, &err, msg, 100, &mlen); 01498 obj->parent->last_negative_connect = ast_tvnow(); 01499 ast_mutex_unlock(&obj->lock); 01500 ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg); 01501 return ODBC_FAIL; 01502 } else { 01503 ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn); 01504 obj->up = 1; 01505 obj->last_used = ast_tvnow(); 01506 } 01507 01508 ast_mutex_unlock(&obj->lock); 01509 return ODBC_SUCCESS; 01510 }
static void odbc_obj_destructor | ( | void * | data | ) | [static] |
Definition at line 412 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().
00413 { 00414 struct odbc_obj *obj = data; 00415 struct odbc_class *class = obj->parent; 00416 obj->parent = NULL; 00417 odbc_obj_disconnect(obj); 00418 ast_mutex_destroy(&obj->lock); 00419 ao2_ref(class, -1); 00420 }
static odbc_status odbc_obj_disconnect | ( | struct odbc_obj * | obj | ) | [static] |
Definition at line 1420 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, odbc_class::name, odbc_txn_frame::obj, ODBC_SUCCESS, odbc_obj::parent, and odbc_obj::up.
Referenced by ast_odbc_sanity_check(), odbc_obj_connect(), and odbc_obj_destructor().
01421 { 01422 int res; 01423 SQLINTEGER err; 01424 short int mlen; 01425 unsigned char msg[200], state[10]; 01426 01427 /* Nothing to disconnect */ 01428 if (!obj->con) { 01429 return ODBC_SUCCESS; 01430 } 01431 01432 ast_mutex_lock(&obj->lock); 01433 01434 res = SQLDisconnect(obj->con); 01435 01436 if (obj->parent) { 01437 if (res == SQL_SUCCESS || res == SQL_SUCCESS_WITH_INFO) { 01438 ast_log(LOG_DEBUG, "Disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn); 01439 } else { 01440 ast_log(LOG_DEBUG, "res_odbc: %s [%s] already disconnected\n", obj->parent->name, obj->parent->dsn); 01441 } 01442 } 01443 01444 if ((res = SQLFreeHandle(SQL_HANDLE_DBC, obj->con) == SQL_SUCCESS)) { 01445 obj->con = NULL; 01446 ast_log(LOG_DEBUG, "Database handle deallocated\n"); 01447 } else { 01448 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, state, &err, msg, 100, &mlen); 01449 ast_log(LOG_WARNING, "Unable to deallocate database handle? %d errno=%d %s\n", res, (int)err, msg); 01450 } 01451 01452 obj->up = 0; 01453 ast_mutex_unlock(&obj->lock); 01454 return ODBC_SUCCESS; 01455 }
static int odbc_register_class | ( | struct odbc_class * | class, | |
int | connect | |||
) | [static] |
Definition at line 1006 of file res_odbc.c.
References ao2_link, ast_log(), ast_odbc_release_obj(), ast_odbc_request_obj, class_container, LOG_WARNING, and odbc_class::name.
01007 { 01008 struct odbc_obj *obj; 01009 if (class) { 01010 ao2_link(class_container, class); 01011 /* I still have a reference in the caller, so a deref is NOT missing here. */ 01012 01013 if (preconnect) { 01014 /* Request and release builds a connection */ 01015 obj = ast_odbc_request_obj(class->name, 0); 01016 if (obj) { 01017 ast_odbc_release_obj(obj); 01018 } 01019 } 01020 01021 return 0; 01022 } else { 01023 ast_log(LOG_WARNING, "Attempted to register a NULL class?\n"); 01024 return -1; 01025 } 01026 }
static void odbc_release_obj2 | ( | struct odbc_obj * | obj, | |
struct odbc_txn_frame * | tx | |||
) | [static] |
Definition at line 1028 of file res_odbc.c.
References ast_debug, ast_log(), odbc_obj::con, LOG_WARNING, odbc_obj::tx, and odbc_obj::txf.
Referenced by ast_odbc_release_obj(), and release_transaction().
01029 { 01030 SQLINTEGER nativeerror=0, numfields=0; 01031 SQLSMALLINT diagbytes=0, i; 01032 unsigned char state[10], diagnostic[256]; 01033 01034 ast_debug(2, "odbc_release_obj2(%p) called (obj->txf = %p)\n", obj, obj->txf); 01035 if (tx) { 01036 ast_debug(1, "called on a transactional handle with %s\n", tx->forcecommit ? "COMMIT" : "ROLLBACK"); 01037 if (SQLEndTran(SQL_HANDLE_DBC, obj->con, tx->forcecommit ? SQL_COMMIT : SQL_ROLLBACK) == SQL_ERROR) { 01038 /* Handle possible transaction commit failure */ 01039 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01040 for (i = 0; i < numfields; i++) { 01041 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01042 ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic); 01043 if (!strcmp((char *)state, "25S02") || !strcmp((char *)state, "08007")) { 01044 /* These codes mean that a commit failed and a transaction 01045 * is still active. We must rollback, or things will get 01046 * very, very weird for anybody using the handle next. */ 01047 SQLEndTran(SQL_HANDLE_DBC, obj->con, SQL_ROLLBACK); 01048 } 01049 if (i > 10) { 01050 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01051 break; 01052 } 01053 } 01054 } 01055 01056 /* Transaction is done, reset autocommit */ 01057 if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) { 01058 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01059 for (i = 0; i < numfields; i++) { 01060 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01061 ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic); 01062 if (i > 10) { 01063 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01064 break; 01065 } 01066 } 01067 } 01068 } 01069 01070 #ifdef DEBUG_THREADS 01071 obj->file[0] = '\0'; 01072 obj->function[0] = '\0'; 01073 obj->lineno = 0; 01074 #endif 01075 01076 /* For pooled connections, this frees the connection to be 01077 * reused. For non-pooled connections, it does nothing. */ 01078 obj->used = 0; 01079 if (obj->txf) { 01080 /* Prevent recursion -- transaction is already closed out. */ 01081 obj->txf->obj = NULL; 01082 obj->txf = release_transaction(obj->txf); 01083 } 01084 ao2_ref(obj, -1); 01085 }
static void odbc_txn_free | ( | void * | data | ) | [static] |
Definition at line 341 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().
00342 { 00343 struct odbc_txn_frame *tx; 00344 AST_LIST_HEAD(, odbc_txn_frame) *oldlist = vdata; 00345 00346 ast_debug(2, "odbc_txn_free(%p) called\n", vdata); 00347 00348 AST_LIST_LOCK(oldlist); 00349 while ((tx = AST_LIST_REMOVE_HEAD(oldlist, list))) { 00350 release_transaction(tx); 00351 } 00352 AST_LIST_UNLOCK(oldlist); 00353 AST_LIST_HEAD_DESTROY(oldlist); 00354 ast_free(oldlist); 00355 }
static struct odbc_txn_frame* release_transaction | ( | struct odbc_txn_frame * | tx | ) | [static] |
Definition at line 305 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::tx, odbc_obj::txf, and txn_info.
Referenced by odbc_txn_free().
00306 { 00307 if (!tx) { 00308 return NULL; 00309 } 00310 00311 ast_debug(2, "release_transaction(%p) called (tx->obj = %p, tx->obj->txf = %p)\n", tx, tx->obj, tx->obj ? tx->obj->txf : NULL); 00312 00313 /* If we have an owner, disassociate */ 00314 if (tx->owner) { 00315 struct ast_datastore *txn_store; 00316 AST_LIST_HEAD(, odbc_txn_frame) *oldlist; 00317 00318 ast_channel_lock(tx->owner); 00319 if ((txn_store = ast_channel_datastore_find(tx->owner, &txn_info, NULL))) { 00320 oldlist = txn_store->data; 00321 AST_LIST_LOCK(oldlist); 00322 AST_LIST_REMOVE(oldlist, tx, list); 00323 AST_LIST_UNLOCK(oldlist); 00324 } 00325 ast_channel_unlock(tx->owner); 00326 tx->owner = NULL; 00327 } 00328 00329 if (tx->obj) { 00330 /* If we have any uncommitted transactions, they are handled when we release the object */ 00331 struct odbc_obj *obj = tx->obj; 00332 /* Prevent recursion during destruction */ 00333 tx->obj->txf = NULL; 00334 tx->obj = NULL; 00335 odbc_release_obj2(obj, tx); 00336 } 00337 ast_free(tx); 00338 return NULL; 00339 }
static int reload | ( | void | ) | [static] |
Definition at line 1740 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(), load_odbc_config(), and table.
01741 { 01742 struct odbc_cache_tables *table; 01743 struct odbc_class *class; 01744 struct odbc_obj *current; 01745 struct ao2_iterator aoi = ao2_iterator_init(class_container, 0); 01746 01747 /* First, mark all to be purged */ 01748 while ((class = ao2_iterator_next(&aoi))) { 01749 class->delme = 1; 01750 ao2_ref(class, -1); 01751 } 01752 ao2_iterator_destroy(&aoi); 01753 01754 load_odbc_config(); 01755 01756 /* Purge remaining classes */ 01757 01758 /* Note on how this works; this is a case of circular references, so we 01759 * explicitly do NOT want to use a callback here (or we wind up in 01760 * recursive hell). 01761 * 01762 * 1. Iterate through all the classes. Note that the classes will currently 01763 * contain two classes of the same name, one of which is marked delme and 01764 * will be purged when all remaining objects of the class are released, and 01765 * the other, which was created above when we re-parsed the config file. 01766 * 2. On each class, there is a reference held by the master container and 01767 * a reference held by each connection object. There are two cases for 01768 * destruction of the class, noted below. However, in all cases, all O-refs 01769 * (references to objects) will first be freed, which will cause the C-refs 01770 * (references to classes) to be decremented (but never to 0, because the 01771 * class container still has a reference). 01772 * a) If the class has outstanding objects, the C-ref by the class 01773 * container will then be freed, which leaves only C-refs by any 01774 * outstanding objects. When the final outstanding object is released 01775 * (O-refs held by applications and dialplan functions), it will in turn 01776 * free the final C-ref, causing class destruction. 01777 * b) If the class has no outstanding objects, when the class container 01778 * removes the final C-ref, the class will be destroyed. 01779 */ 01780 aoi = ao2_iterator_init(class_container, 0); 01781 while ((class = ao2_iterator_next(&aoi))) { /* C-ref++ (by iterator) */ 01782 if (class->delme) { 01783 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0); 01784 while ((current = ao2_iterator_next(&aoi2))) { /* O-ref++ (by iterator) */ 01785 ao2_unlink(class->obj_container, current); /* unlink O-ref from class (reference handled implicitly) */ 01786 ao2_ref(current, -1); /* O-ref-- (by iterator) */ 01787 /* At this point, either 01788 * a) there's an outstanding O-ref, or 01789 * b) the object has already been destroyed. 01790 */ 01791 } 01792 ao2_iterator_destroy(&aoi2); 01793 ao2_unlink(class_container, class); /* unlink C-ref from container (reference handled implicitly) */ 01794 /* At this point, either 01795 * a) there's an outstanding O-ref, which holds an outstanding C-ref, or 01796 * b) the last remaining C-ref is held by the iterator, which will be 01797 * destroyed in the next step. 01798 */ 01799 } 01800 ao2_ref(class, -1); /* C-ref-- (by iterator) */ 01801 } 01802 ao2_iterator_destroy(&aoi); 01803 01804 /* Empty the cache; it will get rebuilt the next time the tables are needed. */ 01805 AST_RWLIST_WRLOCK(&odbc_tables); 01806 while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) { 01807 destroy_table_cache(table); 01808 } 01809 AST_RWLIST_UNLOCK(&odbc_tables); 01810 01811 return 0; 01812 }
static int rollback_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
Definition at line 1135 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().
01136 { 01137 struct odbc_txn_frame *tx; 01138 SQLINTEGER nativeerror=0, numfields=0; 01139 SQLSMALLINT diagbytes=0, i; 01140 unsigned char state[10], diagnostic[256]; 01141 01142 if (ast_strlen_zero(data)) { 01143 tx = find_transaction(chan, NULL, NULL, 1); 01144 } else { 01145 tx = find_transaction(chan, NULL, data, 0); 01146 } 01147 01148 pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", "OK"); 01149 01150 if (tx) { 01151 if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_ROLLBACK) == SQL_ERROR) { 01152 struct ast_str *errors = ast_str_thread_get(&errors_buf, 16); 01153 ast_str_reset(errors); 01154 01155 /* Handle possible transaction commit failure */ 01156 SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01157 for (i = 0; i < numfields; i++) { 01158 SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01159 ast_str_append(&errors, 0, "%s%s", ast_str_strlen(errors) ? "," : "", state); 01160 ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic); 01161 if (i > 10) { 01162 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01163 break; 01164 } 01165 } 01166 pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", ast_str_buffer(errors)); 01167 } 01168 } 01169 return 0; 01170 }
static int text2isolation | ( | const char * | txt | ) | [static] |
Definition at line 203 of file res_odbc.c.
Referenced by acf_transaction_write().
00204 { 00205 if (strncasecmp(txt, "read_", 5) == 0) { 00206 if (strncasecmp(txt + 5, "c", 1) == 0) { 00207 return SQL_TXN_READ_COMMITTED; 00208 } else if (strncasecmp(txt + 5, "u", 1) == 0) { 00209 return SQL_TXN_READ_UNCOMMITTED; 00210 } else { 00211 return 0; 00212 } 00213 } else if (strncasecmp(txt, "ser", 3) == 0) { 00214 return SQL_TXN_SERIALIZABLE; 00215 } else if (strncasecmp(txt, "rep", 3) == 0) { 00216 return SQL_TXN_REPEATABLE_READ; 00217 } else { 00218 return 0; 00219 } 00220 }
static int unload_module | ( | void | ) | [static] |
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER , .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 = "8586c2a7d357cb591cc3a6607a8f62d1" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_REALTIME_DEPEND, } [static] |
Definition at line 1840 of file res_odbc.c.
const char* const app_commit = "ODBC_Commit" [static] |
Definition at line 1649 of file res_odbc.c.
const char* const app_rollback = "ODBC_Rollback" [static] |
Definition at line 1650 of file res_odbc.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 1840 of file res_odbc.c.
struct ao2_container* class_container [static] |
Definition at line 143 of file res_odbc.c.
Referenced by _ast_odbc_request_obj2(), data_odbc_provider_handler(), handle_cli_odbc_show(), load_module(), odbc_register_class(), and reload().
struct ast_cli_entry cli_odbc[] [static] |
Initial value:
{ { .handler = handle_cli_odbc_show , .summary = "List ODBC DSN(s)" ,__VA_ARGS__ } }
Definition at line 1002 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] |
struct ast_custom_function odbc_function [static] |
Initial value:
{ .name = "ODBC", .read = acf_transaction_read, .write = acf_transaction_write, }
Definition at line 1643 of file res_odbc.c.
Referenced by load_module().
struct ast_data_handler odbc_provider [static] |
Initial value:
{ .version = AST_DATA_HANDLER_VERSION, .get = data_odbc_provider_handler }
Definition at line 1731 of file res_odbc.c.
struct ast_data_entry odbc_providers[] [static] |
Initial value:
{ AST_DATA_ENTRY("/asterisk/res/odbc", &odbc_provider), }
Definition at line 1736 of file res_odbc.c.
Referenced by load_module().
struct ast_datastore_info txn_info [static] |
Initial value:
{ .type = "ODBC_Transaction", .destroy = odbc_txn_free, }
Definition at line 155 of file res_odbc.c.
Referenced by ast_odbc_retrieve_transaction_obj(), find_transaction(), mark_transaction_active(), and release_transaction().