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"
#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) |
struct odbc_obj * | _ast_odbc_request_obj (const char *name, int check, const char *file, const char *function, int lineno) |
struct odbc_obj * | _ast_odbc_request_obj2 (const char *name, struct ast_flags flags, const char *file, const char *function, int lineno) |
Retrieves a connected ODBC object. | |
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) |
static int | aoro2_obj_notx_cb (void *vobj, void *arg, int flags) |
AST_DATA_STRUCTURE (odbc_class, DATA_EXPORT_ODBC_CLASS) | |
int | ast_odbc_allow_empty_string_in_nontext (struct odbc_obj *obj) |
Checks if the database natively supports implicit conversion from an empty string to a number (0). | |
SQLRETURN | ast_odbc_ast_str_SQLGetData (struct ast_str **buf, int pmaxlen, SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, SQLLEN *StrLen_or_Ind) |
Wrapper for SQLGetData to use with dynamic strings. | |
int | ast_odbc_backslash_is_escape (struct odbc_obj *obj) |
Checks if the database natively supports backslash as an escape character. | |
int | ast_odbc_clear_cache (const char *database, const char *tablename) |
Remove a cache entry from memory This function may be called to clear entries created and cached by the ast_odbc_find_table() API call. | |
SQLHSTMT | ast_odbc_direct_execute (struct odbc_obj *obj, SQLHSTMT(*exec_cb)(struct odbc_obj *obj, void *data), void *data) |
Executes an non prepared statement and returns the resulting statement handle. | |
struct odbc_cache_columns * | ast_odbc_find_column (struct odbc_cache_tables *table, const char *colname) |
Find a column entry within a cached table structure. | |
struct odbc_cache_tables * | ast_odbc_find_table (const char *database, const char *tablename) |
Find or create an entry describing the table specified. | |
SQLHSTMT | ast_odbc_prepare_and_execute (struct odbc_obj *obj, SQLHSTMT(*prepare_cb)(struct odbc_obj *obj, void *data), void *data) |
Prepares, executes, and returns the resulting statement handle. | |
void | ast_odbc_release_obj (struct odbc_obj *obj) |
Releases an ODBC object previously allocated by ast_odbc_request_obj(). | |
struct odbc_obj * | ast_odbc_retrieve_transaction_obj (struct ast_channel *chan, const char *objname) |
Retrieve a stored ODBC object, if a transaction has been started. | |
int | ast_odbc_sanity_check (struct odbc_obj *obj) |
Checks an ODBC object to ensure it is still connected. | |
int | ast_odbc_smart_execute (struct odbc_obj *obj, SQLHSTMT stmt) |
Executes a prepared statement handle. | |
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 = "ac1f6a56484a8820659555499174e588" , .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 |
ODBC resource manager.
Definition in file res_odbc.c.
#define DATA_EXPORT_ODBC_CLASS | ( | MEMBER | ) |
Definition at line 179 of file res_odbc.c.
#define EOR_TX (void *)(long)3 |
Definition at line 1214 of file res_odbc.c.
Referenced by _ast_odbc_request_obj2(), and aoro2_obj_cb().
#define NO_TX (void *)(long)2 |
Definition at line 1213 of file res_odbc.c.
Referenced by _ast_odbc_request_obj2(), and aoro2_obj_cb().
#define USE_TX (void *)(long)1 |
Definition at line 1212 of file res_odbc.c.
Referenced by _ast_odbc_request_obj2(), and aoro2_obj_cb().
static void __init_errors_buf | ( | void | ) | [static] |
Definition at line 155 of file res_odbc.c.
static void __reg_module | ( | void | ) | [static] |
Definition at line 1912 of file res_odbc.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 1912 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 | |||
) | [read] |
Definition at line 1450 of file res_odbc.c.
References _ast_odbc_request_obj2(), and RES_ODBC_SANITY_CHECK.
01451 { 01452 struct ast_flags flags = { check ? RES_ODBC_SANITY_CHECK : 0 }; 01453 return _ast_odbc_request_obj2(name, flags, file, function, lineno); 01454 }
struct odbc_obj* _ast_odbc_request_obj2 | ( | const char * | name, | |
struct ast_flags | flags, | |||
const char * | file, | |||
const char * | function, | |||
int | lineno | |||
) | [read] |
Retrieves a connected ODBC object.
name | The name of the ODBC class for which a connection is needed. | |
flags | One or more of the following flags:
|
NULL | if there is no connection available with the requested name. |
Connection classes may, in fact, contain multiple connection handles. If the connection is pooled, then each connection will be dedicated to the thread which requests it. Note that all connections should be released when the thread is done by calling ast_odbc_release_obj(), below.
Definition at line 1241 of file res_odbc.c.
References ao2_alloc, ao2_callback, ao2_link, ao2_ref, aoro2_class_cb(), aoro2_obj_cb(), aoro2_obj_notx_cb(), ast_assert, ast_atomic_fetchadd_int(), ast_copy_string(), ast_debug, ast_log(), ast_mutex_init, ast_mutex_lock, ast_mutex_unlock, ast_odbc_sanity_check(), ast_test_flag, ast_tvdiff_sec(), ast_tvnow(), class_container, odbc_obj::con, odbc_class::count, EOR_TX, odbc_class::idlecheck, odbc_class::isolation, odbc_class::last_negative_connect, odbc_obj::last_used, odbc_obj::lock, LOG_WARNING, odbc_class::negative_connection_cache, NO_TX, odbc_class::obj_container, ODBC_FAIL, odbc_obj_connect(), odbc_obj_destructor(), odbc_obj::parent, RES_ODBC_CONNECTED, RES_ODBC_INDEPENDENT_CONNECTION, RES_ODBC_SANITY_CHECK, odbc_obj::up, USE_TX, and odbc_obj::used.
Referenced by _ast_odbc_request_obj().
01242 { 01243 struct odbc_obj *obj = NULL; 01244 struct odbc_class *class; 01245 SQLINTEGER nativeerror=0, numfields=0; 01246 SQLSMALLINT diagbytes=0, i; 01247 unsigned char state[10], diagnostic[256]; 01248 01249 if (!(class = ao2_callback(class_container, 0, aoro2_class_cb, (char *) name))) { 01250 ast_debug(1, "Class '%s' not found!\n", name); 01251 return NULL; 01252 } 01253 01254 ast_assert(ao2_ref(class, 0) > 1); 01255 01256 if (class->haspool) { 01257 /* Recycle connections before building another */ 01258 obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, EOR_TX); 01259 01260 if (obj) { 01261 ast_assert(ao2_ref(obj, 0) > 1); 01262 } 01263 if (!obj && (ast_atomic_fetchadd_int(&class->count, +1) < class->limit) && 01264 (time(NULL) > class->last_negative_connect.tv_sec + class->negative_connection_cache.tv_sec)) { 01265 obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor); 01266 if (!obj) { 01267 class->count--; 01268 ao2_ref(class, -1); 01269 ast_debug(3, "Unable to allocate object\n"); 01270 ast_atomic_fetchadd_int(&class->count, -1); 01271 return NULL; 01272 } 01273 ast_assert(ao2_ref(obj, 0) == 1); 01274 ast_mutex_init(&obj->lock); 01275 /* obj inherits the outstanding reference to class */ 01276 obj->parent = class; 01277 class = NULL; 01278 if (odbc_obj_connect(obj) == ODBC_FAIL) { 01279 ast_log(LOG_WARNING, "Failed to connect to %s\n", name); 01280 ast_assert(ao2_ref(obj->parent, 0) > 0); 01281 /* Because it was never within the container, we have to manually decrement the count here */ 01282 ast_atomic_fetchadd_int(&obj->parent->count, -1); 01283 ao2_ref(obj, -1); 01284 obj = NULL; 01285 } else { 01286 obj->used = 1; 01287 ao2_link(obj->parent->obj_container, obj); 01288 } 01289 } else { 01290 /* If construction fails due to the limit (or negative timecache), reverse our increment. */ 01291 if (!obj) { 01292 ast_atomic_fetchadd_int(&class->count, -1); 01293 } 01294 /* Object is not constructed, so delete outstanding reference to class. */ 01295 ao2_ref(class, -1); 01296 class = NULL; 01297 } 01298 01299 if (!obj) { 01300 return NULL; 01301 } 01302 01303 ast_mutex_lock(&obj->lock); 01304 01305 if (ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) { 01306 /* Ensure this connection has autocommit turned off. */ 01307 if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) { 01308 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01309 for (i = 0; i < numfields; i++) { 01310 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01311 ast_log(LOG_WARNING, "SQLSetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic); 01312 if (i > 10) { 01313 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01314 break; 01315 } 01316 } 01317 } 01318 } 01319 } else if (ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) { 01320 /* Non-pooled connections -- but must use a separate connection handle */ 01321 if (!(obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, USE_TX))) { 01322 ast_debug(1, "Object not found\n"); 01323 obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor); 01324 if (!obj) { 01325 ao2_ref(class, -1); 01326 ast_debug(3, "Unable to allocate object\n"); 01327 return NULL; 01328 } 01329 ast_mutex_init(&obj->lock); 01330 /* obj inherits the outstanding reference to class */ 01331 obj->parent = class; 01332 class = NULL; 01333 if (odbc_obj_connect(obj) == ODBC_FAIL) { 01334 ast_log(LOG_WARNING, "Failed to connect to %s\n", name); 01335 ao2_ref(obj, -1); 01336 obj = NULL; 01337 } else { 01338 obj->used = 1; 01339 ao2_link(obj->parent->obj_container, obj); 01340 ast_atomic_fetchadd_int(&obj->parent->count, +1); 01341 } 01342 } 01343 01344 if (!obj) { 01345 return NULL; 01346 } 01347 01348 ast_mutex_lock(&obj->lock); 01349 01350 if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) { 01351 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01352 for (i = 0; i < numfields; i++) { 01353 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01354 ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic); 01355 if (i > 10) { 01356 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01357 break; 01358 } 01359 } 01360 } 01361 } else { 01362 /* Non-pooled connection: multiple modules can use the same connection. */ 01363 if ((obj = ao2_callback(class->obj_container, 0, aoro2_obj_notx_cb, NO_TX))) { 01364 /* Object is not constructed, so delete outstanding reference to class. */ 01365 ast_assert(ao2_ref(class, 0) > 1); 01366 ao2_ref(class, -1); 01367 class = NULL; 01368 } else { 01369 /* No entry: build one */ 01370 if (!(obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor))) { 01371 ast_assert(ao2_ref(class, 0) > 1); 01372 ao2_ref(class, -1); 01373 ast_debug(3, "Unable to allocate object\n"); 01374 return NULL; 01375 } 01376 ast_mutex_init(&obj->lock); 01377 /* obj inherits the outstanding reference to class */ 01378 obj->parent = class; 01379 class = NULL; 01380 if (odbc_obj_connect(obj) == ODBC_FAIL) { 01381 ast_log(LOG_WARNING, "Failed to connect to %s\n", name); 01382 ao2_ref(obj, -1); 01383 obj = NULL; 01384 } else { 01385 ao2_link(obj->parent->obj_container, obj); 01386 ast_assert(ao2_ref(obj, 0) > 1); 01387 } 01388 } 01389 01390 if (!obj) { 01391 return NULL; 01392 } 01393 01394 ast_mutex_lock(&obj->lock); 01395 01396 if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) { 01397 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01398 for (i = 0; i < numfields; i++) { 01399 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01400 ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic); 01401 if (i > 10) { 01402 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01403 break; 01404 } 01405 } 01406 } 01407 } 01408 01409 ast_assert(obj != NULL); 01410 01411 /* Set the isolation property */ 01412 if (SQLSetConnectAttr(obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)obj->parent->isolation, 0) == SQL_ERROR) { 01413 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01414 for (i = 0; i < numfields; i++) { 01415 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01416 ast_log(LOG_WARNING, "SetConnectAttr (Txn isolation) returned an error: %s: %s\n", state, diagnostic); 01417 if (i > 10) { 01418 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01419 break; 01420 } 01421 } 01422 } 01423 01424 if (ast_test_flag(&flags, RES_ODBC_CONNECTED) && !obj->up) { 01425 /* Check if this connection qualifies for reconnection, with negative connection cache time */ 01426 if (time(NULL) > obj->parent->last_negative_connect.tv_sec + obj->parent->negative_connection_cache.tv_sec) { 01427 odbc_obj_connect(obj); 01428 } 01429 } else if (ast_test_flag(&flags, RES_ODBC_SANITY_CHECK)) { 01430 ast_odbc_sanity_check(obj); 01431 } else if (obj->parent->idlecheck > 0 && ast_tvdiff_sec(ast_tvnow(), obj->last_used) > obj->parent->idlecheck) { 01432 odbc_obj_connect(obj); 01433 } 01434 01435 #ifdef DEBUG_THREADS 01436 ast_copy_string(obj->file, file, sizeof(obj->file)); 01437 ast_copy_string(obj->function, function, sizeof(obj->function)); 01438 obj->lineno = lineno; 01439 #endif 01440 01441 /* We had it locked because of the obj_connects we see here. */ 01442 ast_mutex_unlock(&obj->lock); 01443 01444 ast_assert(class == NULL); 01445 01446 ast_assert(ao2_ref(obj, 0) > 1); 01447 return obj; 01448 }
static int acf_transaction_read | ( | struct ast_channel * | chan, | |
const char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 1582 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.
01583 { 01584 AST_DECLARE_APP_ARGS(args, 01585 AST_APP_ARG(property); 01586 AST_APP_ARG(opt); 01587 ); 01588 struct odbc_txn_frame *tx; 01589 01590 AST_STANDARD_APP_ARGS(args, data); 01591 if (strcasecmp(args.property, "transaction") == 0) { 01592 if ((tx = find_transaction(chan, NULL, NULL, 1))) { 01593 ast_copy_string(buf, tx->name, len); 01594 return 0; 01595 } 01596 } else if (strcasecmp(args.property, "isolation") == 0) { 01597 if (!ast_strlen_zero(args.opt)) { 01598 tx = find_transaction(chan, NULL, args.opt, 0); 01599 } else { 01600 tx = find_transaction(chan, NULL, NULL, 1); 01601 } 01602 if (tx) { 01603 ast_copy_string(buf, isolation2text(tx->isolation), len); 01604 return 0; 01605 } 01606 } else if (strcasecmp(args.property, "forcecommit") == 0) { 01607 if (!ast_strlen_zero(args.opt)) { 01608 tx = find_transaction(chan, NULL, args.opt, 0); 01609 } else { 01610 tx = find_transaction(chan, NULL, NULL, 1); 01611 } 01612 if (tx) { 01613 ast_copy_string(buf, tx->forcecommit ? "1" : "0", len); 01614 return 0; 01615 } 01616 } 01617 return -1; 01618 }
static int acf_transaction_write | ( | struct ast_channel * | chan, | |
const char * | cmd, | |||
char * | s, | |||
const char * | value | |||
) | [static] |
Definition at line 1620 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(), odbc_txn_frame::forcecommit, odbc_txn_frame::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.
01621 { 01622 AST_DECLARE_APP_ARGS(args, 01623 AST_APP_ARG(property); 01624 AST_APP_ARG(opt); 01625 ); 01626 struct odbc_txn_frame *tx; 01627 SQLINTEGER nativeerror=0, numfields=0; 01628 SQLSMALLINT diagbytes=0, i; 01629 unsigned char state[10], diagnostic[256]; 01630 01631 AST_STANDARD_APP_ARGS(args, s); 01632 if (strcasecmp(args.property, "transaction") == 0) { 01633 /* Set active transaction */ 01634 struct odbc_obj *obj; 01635 if ((tx = find_transaction(chan, NULL, value, 0))) { 01636 mark_transaction_active(chan, tx); 01637 } else { 01638 /* No such transaction, create one */ 01639 struct ast_flags flags = { RES_ODBC_INDEPENDENT_CONNECTION }; 01640 if (ast_strlen_zero(args.opt) || !(obj = ast_odbc_request_obj2(args.opt, flags))) { 01641 ast_log(LOG_ERROR, "Could not create transaction: invalid database specification '%s'\n", S_OR(args.opt, "")); 01642 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_DB"); 01643 return -1; 01644 } 01645 if (!find_transaction(chan, obj, value, 0)) { 01646 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE"); 01647 return -1; 01648 } 01649 obj->tx = 1; 01650 } 01651 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK"); 01652 return 0; 01653 } else if (strcasecmp(args.property, "forcecommit") == 0) { 01654 /* Set what happens when an uncommitted transaction ends without explicit Commit or Rollback */ 01655 if (ast_strlen_zero(args.opt)) { 01656 tx = find_transaction(chan, NULL, NULL, 1); 01657 } else { 01658 tx = find_transaction(chan, NULL, args.opt, 0); 01659 } 01660 if (!tx) { 01661 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE"); 01662 return -1; 01663 } 01664 if (ast_true(value)) { 01665 tx->forcecommit = 1; 01666 } else if (ast_false(value)) { 01667 tx->forcecommit = 0; 01668 } else { 01669 ast_log(LOG_ERROR, "Invalid value for forcecommit: '%s'\n", S_OR(value, "")); 01670 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE"); 01671 return -1; 01672 } 01673 01674 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK"); 01675 return 0; 01676 } else if (strcasecmp(args.property, "isolation") == 0) { 01677 /* How do uncommitted transactions affect reads? */ 01678 int isolation = text2isolation(value); 01679 if (ast_strlen_zero(args.opt)) { 01680 tx = find_transaction(chan, NULL, NULL, 1); 01681 } else { 01682 tx = find_transaction(chan, NULL, args.opt, 0); 01683 } 01684 if (!tx) { 01685 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE"); 01686 return -1; 01687 } 01688 if (isolation == 0) { 01689 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE"); 01690 ast_log(LOG_ERROR, "Invalid isolation specification: '%s'\n", S_OR(value, "")); 01691 } else if (SQLSetConnectAttr(tx->obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)isolation, 0) == SQL_ERROR) { 01692 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "SQL_ERROR"); 01693 SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01694 for (i = 0; i < numfields; i++) { 01695 SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01696 ast_log(LOG_WARNING, "SetConnectAttr (Txn isolation) returned an error: %s: %s\n", state, diagnostic); 01697 if (i > 10) { 01698 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01699 break; 01700 } 01701 } 01702 } else { 01703 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK"); 01704 tx->isolation = isolation; 01705 } 01706 return 0; 01707 } else { 01708 ast_log(LOG_ERROR, "Unknown property: '%s'\n", args.property); 01709 return -1; 01710 } 01711 }
static int aoro2_class_cb | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 1202 of file res_odbc.c.
References CMP_MATCH, and CMP_STOP.
Referenced by _ast_odbc_request_obj2().
01203 { 01204 struct odbc_class *class = obj; 01205 char *name = arg; 01206 if (!strcmp(class->name, name) && !class->delme) { 01207 return CMP_MATCH | CMP_STOP; 01208 } 01209 return 0; 01210 }
static int aoro2_obj_cb | ( | void * | vobj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 1216 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().
01217 { 01218 struct odbc_obj *obj = vobj; 01219 ast_mutex_lock(&obj->lock); 01220 if ((arg == NO_TX && !obj->tx) || (arg == EOR_TX && !obj->used) || (arg == USE_TX && obj->tx && !obj->used)) { 01221 obj->used = 1; 01222 ast_mutex_unlock(&obj->lock); 01223 return CMP_MATCH | CMP_STOP; 01224 } 01225 ast_mutex_unlock(&obj->lock); 01226 return 0; 01227 }
static int aoro2_obj_notx_cb | ( | void * | vobj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 1232 of file res_odbc.c.
References CMP_MATCH, CMP_STOP, and odbc_obj::tx.
Referenced by _ast_odbc_request_obj2().
AST_DATA_STRUCTURE | ( | odbc_class | , | |
DATA_EXPORT_ODBC_CLASS | ||||
) |
int ast_odbc_allow_empty_string_in_nontext | ( | struct odbc_obj * | obj | ) |
Checks if the database natively supports implicit conversion from an empty string to a number (0).
obj | The ODBC object |
Definition at line 1123 of file res_odbc.c.
References odbc_class::allow_empty_strings, and odbc_obj::parent.
Referenced by update_odbc().
01124 { 01125 return obj->parent->allow_empty_strings; 01126 }
SQLRETURN ast_odbc_ast_str_SQLGetData | ( | struct ast_str ** | buf, | |
int | pmaxlen, | |||
SQLHSTMT | StatementHandle, | |||
SQLUSMALLINT | ColumnNumber, | |||
SQLSMALLINT | TargetType, | |||
SQLLEN * | StrLen_or_Ind | |||
) |
Wrapper for SQLGetData to use with dynamic strings.
buf | Address of the pointer to the ast_str structure. | |
pmaxlen | The maximum size of the resulting string, or 0 for no limit. | |
StatementHandle | The statement handle from which to retrieve data. | |
ColumnNumber | Column number (1-based offset) for which to retrieve data. | |
TargetType | The SQL constant indicating what kind of data is to be retrieved (usually SQL_CHAR) | |
StrLen_or_Ind | A pointer to a length indicator, specifying the total length of data. |
Definition at line 718 of file res_odbc.c.
References ast_str_buffer(), ast_str_make_space(), ast_str_size(), and ast_str_update().
Referenced by acf_odbc_read(), and cli_odbc_read().
00719 { 00720 SQLRETURN res; 00721 00722 if (pmaxlen == 0) { 00723 if (SQLGetData(StatementHandle, ColumnNumber, TargetType, ast_str_buffer(*buf), 0, StrLen_or_Ind) == SQL_SUCCESS_WITH_INFO) { 00724 ast_str_make_space(buf, *StrLen_or_Ind + 1); 00725 } 00726 } else if (pmaxlen > 0) { 00727 ast_str_make_space(buf, pmaxlen); 00728 } 00729 res = SQLGetData(StatementHandle, ColumnNumber, TargetType, ast_str_buffer(*buf), ast_str_size(*buf), StrLen_or_Ind); 00730 ast_str_update(*buf); 00731 00732 return res; 00733 }
int ast_odbc_backslash_is_escape | ( | struct odbc_obj * | obj | ) |
Checks if the database natively supports backslash as an escape character.
obj | The ODBC object |
Definition at line 1118 of file res_odbc.c.
References odbc_class::backslash_is_escape, and odbc_obj::parent.
Referenced by odbc_log(), realtime_multi_odbc(), and realtime_odbc().
01119 { 01120 return obj->parent->backslash_is_escape; 01121 }
int ast_odbc_clear_cache | ( | const char * | database, | |
const char * | tablename | |||
) |
Remove a cache entry from memory This function may be called to clear entries created and cached by the ast_odbc_find_table() API call.
database | Name of an ODBC class (used to ensure like-named tables in different databases are not confused) | |
tablename | Tablename for which a cached record should be removed |
0 | if the cache entry was removed, or -1 if no matching entry was found. |
Definition at line 577 of file res_odbc.c.
References AST_LIST_REMOVE_CURRENT, AST_RWLIST_TRAVERSE_SAFE_BEGIN, AST_RWLIST_TRAVERSE_SAFE_END, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, odbc_cache_tables::connection, destroy_table_cache(), and odbc_cache_tables::table.
Referenced by unload_odbc().
00578 { 00579 struct odbc_cache_tables *tableptr; 00580 00581 AST_RWLIST_WRLOCK(&odbc_tables); 00582 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&odbc_tables, tableptr, list) { 00583 if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) { 00584 AST_LIST_REMOVE_CURRENT(list); 00585 destroy_table_cache(tableptr); 00586 break; 00587 } 00588 } 00589 AST_RWLIST_TRAVERSE_SAFE_END 00590 AST_RWLIST_UNLOCK(&odbc_tables); 00591 return tableptr ? 0 : -1; 00592 }
SQLHSTMT ast_odbc_direct_execute | ( | struct odbc_obj * | obj, | |
SQLHSTMT(*)(struct odbc_obj *obj, void *data) | exec_cb, | |||
void * | data | |||
) |
Executes an non prepared statement and returns the resulting statement handle.
obj | The ODBC object | |
exec_cb | A function callback, which, when called, should return a statement handle with result columns bound. | |
data | A parameter to be passed to the exec_cb parameter function, indicating which statement handle is to be prepared. |
a | statement handle | |
NULL | on error |
Definition at line 594 of file res_odbc.c.
References ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_odbc_sanity_check(), odbc_class::dsn, odbc_obj::lock, LOG_WARNING, odbc_class::name, odbc_obj::parent, and odbc_obj::tx.
Referenced by acf_odbc_read(), acf_odbc_write(), cli_odbc_read(), cli_odbc_write(), and odbc_log().
00595 { 00596 int attempt; 00597 SQLHSTMT stmt; 00598 00599 ast_mutex_lock(&obj->lock); 00600 00601 for (attempt = 0; attempt < 2; attempt++) { 00602 stmt = exec_cb(obj, data); 00603 00604 if (stmt) { 00605 break; 00606 } else if (obj->tx) { 00607 ast_log(LOG_WARNING, "Failed to execute, but unable to reconnect, as we're transactional.\n"); 00608 break; 00609 } else if (attempt == 0) { 00610 ast_log(LOG_WARNING, "SQL Execute error! Verifying connection to %s [%s]...\n", obj->parent->name, obj->parent->dsn); 00611 } 00612 if (!ast_odbc_sanity_check(obj)) { 00613 break; 00614 } 00615 } 00616 00617 ast_mutex_unlock(&obj->lock); 00618 00619 return stmt; 00620 }
struct odbc_cache_columns* ast_odbc_find_column | ( | struct odbc_cache_tables * | table, | |
const char * | colname | |||
) | [read] |
Find a column entry within a cached table structure.
table | Cached table structure, as returned from ast_odbc_find_table() | |
colname | The column name requested |
A | structure describing the column type, or NULL, if the column is not found. |
Definition at line 566 of file res_odbc.c.
References AST_RWLIST_TRAVERSE, odbc_cache_tables::columns, and odbc_cache_columns::name.
Referenced by update2_prepare(), and update_odbc().
00567 { 00568 struct odbc_cache_columns *col; 00569 AST_RWLIST_TRAVERSE(&table->columns, col, list) { 00570 if (strcasecmp(col->name, colname) == 0) { 00571 return col; 00572 } 00573 } 00574 return NULL; 00575 }
struct odbc_cache_tables* ast_odbc_find_table | ( | const char * | database, | |
const char * | tablename | |||
) | [read] |
Find or create an entry describing the table specified.
database | Name of an ODBC class on which to query the table | |
tablename | Tablename to describe |
A | structure describing the table layout, or NULL, if the table is not found or another error occurs. When a structure is returned, the contained columns list will be rdlock'ed, to ensure that it will be retained in memory. |
Definition at line 449 of file res_odbc.c.
References ast_calloc, AST_LIST_INSERT_TAIL, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_odbc_release_obj(), ast_odbc_request_obj, ast_odbc_sanity_check(), AST_RWLIST_HEAD_INIT, AST_RWLIST_INSERT_TAIL, AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, ast_verb, odbc_cache_tables::columns, odbc_obj::con, odbc_cache_tables::connection, odbc_cache_columns::decimals, destroy_table_cache(), odbc_obj::lock, LOG_ERROR, LOG_WARNING, odbc_cache_columns::name, odbc_cache_columns::nullable, odbc_cache_columns::octetlen, odbc_cache_columns::radix, odbc_cache_columns::size, odbc_cache_tables::table, and odbc_cache_columns::type.
Referenced by require_odbc(), update2_prepare(), and update_odbc().
00450 { 00451 struct odbc_cache_tables *tableptr; 00452 struct odbc_cache_columns *entry; 00453 char columnname[80]; 00454 SQLLEN sqlptr; 00455 SQLHSTMT stmt = NULL; 00456 int res = 0, error = 0, try = 0; 00457 struct odbc_obj *obj = ast_odbc_request_obj(database, 0); 00458 00459 AST_RWLIST_RDLOCK(&odbc_tables); 00460 AST_RWLIST_TRAVERSE(&odbc_tables, tableptr, list) { 00461 if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) { 00462 break; 00463 } 00464 } 00465 if (tableptr) { 00466 AST_RWLIST_RDLOCK(&tableptr->columns); 00467 AST_RWLIST_UNLOCK(&odbc_tables); 00468 if (obj) { 00469 ast_odbc_release_obj(obj); 00470 } 00471 return tableptr; 00472 } 00473 00474 if (!obj) { 00475 ast_log(LOG_WARNING, "Unable to retrieve database handle for table description '%s@%s'\n", tablename, database); 00476 AST_RWLIST_UNLOCK(&odbc_tables); 00477 return NULL; 00478 } 00479 00480 /* Table structure not already cached; build it now. */ 00481 ast_mutex_lock(&obj->lock); 00482 do { 00483 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt); 00484 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00485 if (try == 0) { 00486 try = 1; 00487 ast_odbc_sanity_check(obj); 00488 continue; 00489 } 00490 ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", database); 00491 break; 00492 } 00493 00494 res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)tablename, SQL_NTS, (unsigned char *)"%", SQL_NTS); 00495 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00496 if (try == 0) { 00497 try = 1; 00498 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 00499 ast_odbc_sanity_check(obj); 00500 continue; 00501 } 00502 ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'.\n", database); 00503 break; 00504 } 00505 00506 if (!(tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + strlen(database) + 1 + strlen(tablename) + 1))) { 00507 ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", tablename, database); 00508 break; 00509 } 00510 00511 tableptr->connection = (char *)tableptr + sizeof(*tableptr); 00512 tableptr->table = (char *)tableptr + sizeof(*tableptr) + strlen(database) + 1; 00513 strcpy(tableptr->connection, database); /* SAFE */ 00514 strcpy(tableptr->table, tablename); /* SAFE */ 00515 AST_RWLIST_HEAD_INIT(&(tableptr->columns)); 00516 00517 while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) { 00518 SQLGetData(stmt, 4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr); 00519 00520 if (!(entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1))) { 00521 ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, tablename, database); 00522 error = 1; 00523 break; 00524 } 00525 entry->name = (char *)entry + sizeof(*entry); 00526 strcpy(entry->name, columnname); 00527 00528 SQLGetData(stmt, 5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL); 00529 SQLGetData(stmt, 7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL); 00530 SQLGetData(stmt, 9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL); 00531 SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL); 00532 SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL); 00533 SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL); 00534 00535 /* Specification states that the octenlen should be the maximum number of bytes 00536 * returned in a char or binary column, but it seems that some drivers just set 00537 * it to NULL. (Bad Postgres! No biscuit!) */ 00538 if (entry->octetlen == 0) { 00539 entry->octetlen = entry->size; 00540 } 00541 00542 ast_verb(10, "Found %s column with type %hd with len %ld, octetlen %ld, and numlen (%hd,%hd)\n", entry->name, entry->type, (long) entry->size, (long) entry->octetlen, entry->decimals, entry->radix); 00543 /* Insert column info into column list */ 00544 AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list); 00545 } 00546 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 00547 00548 AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list); 00549 AST_RWLIST_RDLOCK(&(tableptr->columns)); 00550 break; 00551 } while (1); 00552 ast_mutex_unlock(&obj->lock); 00553 00554 AST_RWLIST_UNLOCK(&odbc_tables); 00555 00556 if (error) { 00557 destroy_table_cache(tableptr); 00558 tableptr = NULL; 00559 } 00560 if (obj) { 00561 ast_odbc_release_obj(obj); 00562 } 00563 return tableptr; 00564 }
SQLHSTMT ast_odbc_prepare_and_execute | ( | struct odbc_obj * | obj, | |
SQLHSTMT(*)(struct odbc_obj *obj, void *data) | prepare_cb, | |||
void * | data | |||
) |
Prepares, executes, and returns the resulting statement handle.
obj | The ODBC object | |
prepare_cb | A function callback, which, when called, should return a statement handle prepared, with any necessary parameters or result columns bound. | |
data | A parameter to be passed to the prepare_cb parameter function, indicating which statement handle is to be prepared. |
a | statement handle | |
NULL | on error |
Definition at line 622 of file res_odbc.c.
References ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_odbc_sanity_check(), ast_tvnow(), odbc_class::dsn, odbc_obj::last_used, odbc_obj::lock, LOG_WARNING, odbc_class::name, odbc_obj::parent, odbc_obj::tx, and odbc_obj::up.
Referenced by config_odbc(), destroy_odbc(), odbc_log(), realtime_multi_odbc(), realtime_odbc(), store_odbc(), update2_odbc(), and update_odbc().
00623 { 00624 int res = 0, i, attempt; 00625 SQLINTEGER nativeerror=0, numfields=0; 00626 SQLSMALLINT diagbytes=0; 00627 unsigned char state[10], diagnostic[256]; 00628 SQLHSTMT stmt; 00629 00630 ast_mutex_lock(&obj->lock); 00631 00632 for (attempt = 0; attempt < 2; attempt++) { 00633 /* This prepare callback may do more than just prepare -- it may also 00634 * bind parameters, bind results, etc. The real key, here, is that 00635 * when we disconnect, all handles become invalid for most databases. 00636 * We must therefore redo everything when we establish a new 00637 * connection. */ 00638 stmt = prepare_cb(obj, data); 00639 00640 if (stmt) { 00641 res = SQLExecute(stmt); 00642 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) { 00643 if (res == SQL_ERROR) { 00644 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 00645 for (i = 0; i < numfields; i++) { 00646 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 00647 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes); 00648 if (i > 10) { 00649 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 00650 break; 00651 } 00652 } 00653 } 00654 00655 if (obj->tx) { 00656 ast_log(LOG_WARNING, "SQL Execute error, but unable to reconnect, as we're transactional.\n"); 00657 break; 00658 } else { 00659 ast_log(LOG_WARNING, "SQL Execute error %d! Verifying connection to %s [%s]...\n", res, obj->parent->name, obj->parent->dsn); 00660 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 00661 stmt = NULL; 00662 00663 obj->up = 0; 00664 /* 00665 * While this isn't the best way to try to correct an error, this won't automatically 00666 * fail when the statement handle invalidates. 00667 */ 00668 if (!ast_odbc_sanity_check(obj)) { 00669 break; 00670 } 00671 continue; 00672 } 00673 } else { 00674 obj->last_used = ast_tvnow(); 00675 } 00676 break; 00677 } else if (attempt == 0) { 00678 ast_odbc_sanity_check(obj); 00679 } 00680 } 00681 00682 ast_mutex_unlock(&obj->lock); 00683 00684 return stmt; 00685 }
void ast_odbc_release_obj | ( | struct odbc_obj * | obj | ) |
Releases an ODBC object previously allocated by ast_odbc_request_obj().
obj | The ODBC object |
Definition at line 1112 of file res_odbc.c.
References find_transaction(), and odbc_release_obj2().
Referenced by acf_odbc_read(), acf_odbc_write(), ast_odbc_find_table(), cli_odbc_read(), cli_odbc_write(), config_odbc(), destroy_odbc(), load_config(), odbc_log(), odbc_register_class(), realtime_multi_odbc(), realtime_odbc(), store_odbc(), update2_odbc(), and update_odbc().
01113 { 01114 struct odbc_txn_frame *tx = find_transaction(NULL, obj, NULL, 0); 01115 odbc_release_obj2(obj, tx); 01116 }
struct odbc_obj* ast_odbc_retrieve_transaction_obj | ( | struct ast_channel * | chan, | |
const char * | objname | |||
) | [read] |
Retrieve a stored ODBC object, if a transaction has been started.
chan | Channel associated with the transaction. | |
objname | Name of the database handle. This name corresponds to the name passed to |
A | stored ODBC object, if a transaction was already started. | |
NULL,if | no transaction yet exists. |
Definition at line 1456 of file res_odbc.c.
References ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, AST_LIST_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_datastore::data, odbc_class::name, odbc_txn_frame::obj, odbc_obj::parent, and txn_info.
Referenced by acf_odbc_write().
01457 { 01458 struct ast_datastore *txn_store; 01459 AST_LIST_HEAD(, odbc_txn_frame) *oldlist; 01460 struct odbc_txn_frame *txn = NULL; 01461 01462 if (!chan) { 01463 /* No channel == no transaction */ 01464 return NULL; 01465 } 01466 01467 ast_channel_lock(chan); 01468 if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) { 01469 oldlist = txn_store->data; 01470 } else { 01471 ast_channel_unlock(chan); 01472 return NULL; 01473 } 01474 01475 AST_LIST_LOCK(oldlist); 01476 ast_channel_unlock(chan); 01477 01478 AST_LIST_TRAVERSE(oldlist, txn, list) { 01479 if (txn->obj && txn->obj->parent && !strcmp(txn->obj->parent->name, objname)) { 01480 AST_LIST_UNLOCK(oldlist); 01481 return txn->obj; 01482 } 01483 } 01484 AST_LIST_UNLOCK(oldlist); 01485 return NULL; 01486 }
int ast_odbc_sanity_check | ( | struct odbc_obj * | obj | ) |
Checks an ODBC object to ensure it is still connected.
obj | The ODBC object |
0 | if connected | |
-1 | otherwise. |
Definition at line 735 of file res_odbc.c.
References ast_log(), ast_strlen_zero(), odbc_obj::con, LOG_WARNING, odbc_obj_connect(), odbc_obj_disconnect(), odbc_obj::parent, odbc_class::sanitysql, odbc_obj::tx, and odbc_obj::up.
Referenced by _ast_odbc_request_obj2(), ast_odbc_direct_execute(), ast_odbc_find_table(), ast_odbc_prepare_and_execute(), data_odbc_provider_handler(), and handle_cli_odbc_show().
00736 { 00737 char *test_sql = "select 1"; 00738 SQLHSTMT stmt; 00739 int res = 0; 00740 00741 if (!ast_strlen_zero(obj->parent->sanitysql)) 00742 test_sql = obj->parent->sanitysql; 00743 00744 if (obj->up) { 00745 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt); 00746 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00747 obj->up = 0; 00748 } else { 00749 res = SQLPrepare(stmt, (unsigned char *)test_sql, SQL_NTS); 00750 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00751 obj->up = 0; 00752 } else { 00753 res = SQLExecute(stmt); 00754 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00755 obj->up = 0; 00756 } 00757 } 00758 } 00759 SQLFreeHandle (SQL_HANDLE_STMT, stmt); 00760 } 00761 00762 if (!obj->up && !obj->tx) { /* Try to reconnect! */ 00763 ast_log(LOG_WARNING, "Connection is down attempting to reconnect...\n"); 00764 odbc_obj_disconnect(obj); 00765 odbc_obj_connect(obj); 00766 } 00767 return obj->up; 00768 }
int ast_odbc_smart_execute | ( | struct odbc_obj * | obj, | |
SQLHSTMT | stmt | |||
) |
Executes a prepared statement handle.
obj | The non-NULL result of odbc_request_obj() | |
stmt | The prepared statement handle |
0 | on success | |
-1 | on failure |
This function was originally designed simply to execute a prepared statement handle and to retry if the initial execution failed. Unfortunately, it did this by disconnecting and reconnecting the database handle which on most databases causes the statement handle to become invalid. Therefore, this method has been deprecated in favor of odbc_prepare_and_execute() which allows the statement to be prepared multiple times, if necessary, in case of a loss of connection.
This function really only ever worked with MySQL, where the statement handle is not prepared on the server. If you are not using MySQL, you should avoid it.
Definition at line 687 of file res_odbc.c.
References ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_tvnow(), odbc_obj::last_used, odbc_obj::lock, and LOG_WARNING.
00688 { 00689 int res = 0, i; 00690 SQLINTEGER nativeerror=0, numfields=0; 00691 SQLSMALLINT diagbytes=0; 00692 unsigned char state[10], diagnostic[256]; 00693 00694 ast_mutex_lock(&obj->lock); 00695 00696 res = SQLExecute(stmt); 00697 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) { 00698 if (res == SQL_ERROR) { 00699 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 00700 for (i = 0; i < numfields; i++) { 00701 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 00702 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes); 00703 if (i > 10) { 00704 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 00705 break; 00706 } 00707 } 00708 } 00709 } else { 00710 obj->last_used = ast_tvnow(); 00711 } 00712 00713 ast_mutex_unlock(&obj->lock); 00714 00715 return res; 00716 }
static int commit_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
Definition at line 1128 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().
01129 { 01130 struct odbc_txn_frame *tx; 01131 SQLINTEGER nativeerror=0, numfields=0; 01132 SQLSMALLINT diagbytes=0, i; 01133 unsigned char state[10], diagnostic[256]; 01134 01135 if (ast_strlen_zero(data)) { 01136 tx = find_transaction(chan, NULL, NULL, 1); 01137 } else { 01138 tx = find_transaction(chan, NULL, data, 0); 01139 } 01140 01141 pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", "OK"); 01142 01143 if (tx) { 01144 if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_COMMIT) == SQL_ERROR) { 01145 struct ast_str *errors = ast_str_thread_get(&errors_buf, 16); 01146 ast_str_reset(errors); 01147 01148 /* Handle possible transaction commit failure */ 01149 SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01150 for (i = 0; i < numfields; i++) { 01151 SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01152 ast_str_append(&errors, 0, "%s%s", ast_str_strlen(errors) ? "," : "", state); 01153 ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic); 01154 if (i > 10) { 01155 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01156 break; 01157 } 01158 } 01159 pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", ast_str_buffer(errors)); 01160 } 01161 } 01162 return 0; 01163 }
static int data_odbc_provider_handler | ( | const struct ast_data_search * | search, | |
struct ast_data * | root | |||
) | [static] |
Definition at line 1726 of file res_odbc.c.
References ao2_container_count(), ao2_iterator_destroy(), 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, odbc_class::count, isolation2text(), odbc_obj::lock, odbc_obj::tx, odbc_obj::up, and odbc_obj::used.
01728 { 01729 struct ao2_iterator aoi, aoi2; 01730 struct odbc_class *class; 01731 struct odbc_obj *current; 01732 struct ast_data *data_odbc_class, *data_odbc_connections, *data_odbc_connection; 01733 struct ast_data *enum_node; 01734 int count; 01735 01736 aoi = ao2_iterator_init(class_container, 0); 01737 while ((class = ao2_iterator_next(&aoi))) { 01738 data_odbc_class = ast_data_add_node(root, "class"); 01739 if (!data_odbc_class) { 01740 ao2_ref(class, -1); 01741 continue; 01742 } 01743 01744 ast_data_add_structure(odbc_class, data_odbc_class, class); 01745 01746 if (!ao2_container_count(class->obj_container)) { 01747 ao2_ref(class, -1); 01748 continue; 01749 } 01750 01751 data_odbc_connections = ast_data_add_node(data_odbc_class, "connections"); 01752 if (!data_odbc_connections) { 01753 ao2_ref(class, -1); 01754 continue; 01755 } 01756 01757 ast_data_add_bool(data_odbc_class, "shared", !class->haspool); 01758 /* isolation */ 01759 enum_node = ast_data_add_node(data_odbc_class, "isolation"); 01760 if (!enum_node) { 01761 ao2_ref(class, -1); 01762 continue; 01763 } 01764 ast_data_add_int(enum_node, "value", class->isolation); 01765 ast_data_add_str(enum_node, "text", isolation2text(class->isolation)); 01766 01767 count = 0; 01768 aoi2 = ao2_iterator_init(class->obj_container, 0); 01769 while ((current = ao2_iterator_next(&aoi2))) { 01770 data_odbc_connection = ast_data_add_node(data_odbc_connections, "connection"); 01771 if (!data_odbc_connection) { 01772 ao2_ref(current, -1); 01773 continue; 01774 } 01775 01776 ast_mutex_lock(¤t->lock); 01777 ast_data_add_str(data_odbc_connection, "status", current->used ? "in use" : 01778 current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected"); 01779 ast_data_add_bool(data_odbc_connection, "transactional", current->tx); 01780 ast_mutex_unlock(¤t->lock); 01781 01782 if (class->haspool) { 01783 ast_data_add_int(data_odbc_connection, "number", ++count); 01784 } 01785 01786 ao2_ref(current, -1); 01787 } 01788 ao2_iterator_destroy(&aoi2); 01789 ao2_ref(class, -1); 01790 01791 if (!ast_data_search_match(search, data_odbc_class)) { 01792 ast_data_remove_node(root, data_odbc_class); 01793 } 01794 } 01795 ao2_iterator_destroy(&aoi); 01796 return 0; 01797 }
static void destroy_table_cache | ( | struct odbc_cache_tables * | table | ) | [static] |
Definition at line 428 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, and odbc_cache_tables::table.
Referenced by ast_odbc_clear_cache(), ast_odbc_find_table(), and reload().
00428 { 00429 struct odbc_cache_columns *col; 00430 ast_debug(1, "Destroying table cache for %s\n", table->table); 00431 AST_RWLIST_WRLOCK(&table->columns); 00432 while ((col = AST_RWLIST_REMOVE_HEAD(&table->columns, list))) { 00433 ast_free(col); 00434 } 00435 AST_RWLIST_UNLOCK(&table->columns); 00436 AST_RWLIST_HEAD_DESTROY(&table->columns); 00437 ast_free(table); 00438 }
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 224 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, 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().
00225 { 00226 struct ast_datastore *txn_store; 00227 AST_LIST_HEAD(, odbc_txn_frame) *oldlist; 00228 struct odbc_txn_frame *txn = NULL; 00229 00230 if (!chan && obj && obj->txf && obj->txf->owner) { 00231 chan = obj->txf->owner; 00232 } else if (!chan) { 00233 /* No channel == no transaction */ 00234 return NULL; 00235 } 00236 00237 ast_channel_lock(chan); 00238 if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) { 00239 oldlist = txn_store->data; 00240 } else { 00241 /* Need to create a new datastore */ 00242 if (!(txn_store = ast_datastore_alloc(&txn_info, NULL))) { 00243 ast_log(LOG_ERROR, "Unable to allocate a new datastore. Cannot create a new transaction.\n"); 00244 ast_channel_unlock(chan); 00245 return NULL; 00246 } 00247 00248 if (!(oldlist = ast_calloc(1, sizeof(*oldlist)))) { 00249 ast_log(LOG_ERROR, "Unable to allocate datastore list head. Cannot create a new transaction.\n"); 00250 ast_datastore_free(txn_store); 00251 ast_channel_unlock(chan); 00252 return NULL; 00253 } 00254 00255 txn_store->data = oldlist; 00256 AST_LIST_HEAD_INIT(oldlist); 00257 ast_channel_datastore_add(chan, txn_store); 00258 } 00259 00260 AST_LIST_LOCK(oldlist); 00261 ast_channel_unlock(chan); 00262 00263 /* Scanning for an object is *fast*. Scanning for a name is much slower. */ 00264 if (obj != NULL || active == 1) { 00265 AST_LIST_TRAVERSE(oldlist, txn, list) { 00266 if (txn->obj == obj || txn->active) { 00267 AST_LIST_UNLOCK(oldlist); 00268 return txn; 00269 } 00270 } 00271 } 00272 00273 if (name != NULL) { 00274 AST_LIST_TRAVERSE(oldlist, txn, list) { 00275 if (!strcasecmp(txn->name, name)) { 00276 AST_LIST_UNLOCK(oldlist); 00277 return txn; 00278 } 00279 } 00280 } 00281 00282 /* Nothing found, create one */ 00283 if (name && obj && (txn = ast_calloc(1, sizeof(*txn) + strlen(name) + 1))) { 00284 struct odbc_txn_frame *otxn; 00285 00286 strcpy(txn->name, name); /* SAFE */ 00287 txn->obj = obj; 00288 txn->isolation = obj->parent->isolation; 00289 txn->forcecommit = obj->parent->forcecommit; 00290 txn->owner = chan; 00291 txn->active = 1; 00292 00293 /* On creation, the txn becomes active, and all others inactive */ 00294 AST_LIST_TRAVERSE(oldlist, otxn, list) { 00295 otxn->active = 0; 00296 } 00297 AST_LIST_INSERT_TAIL(oldlist, txn, list); 00298 00299 obj->txf = txn; 00300 obj->tx = 1; 00301 } 00302 AST_LIST_UNLOCK(oldlist); 00303 00304 return txn; 00305 }
static char* handle_cli_odbc_show | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 935 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, 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.
00936 { 00937 struct ao2_iterator aoi; 00938 struct odbc_class *class; 00939 struct odbc_obj *current; 00940 int length = 0; 00941 int which = 0; 00942 char *ret = NULL; 00943 00944 switch (cmd) { 00945 case CLI_INIT: 00946 e->command = "odbc show"; 00947 e->usage = 00948 "Usage: odbc show [class]\n" 00949 " List settings of a particular ODBC class or,\n" 00950 " if not specified, all classes.\n"; 00951 return NULL; 00952 case CLI_GENERATE: 00953 if (a->pos != 2) 00954 return NULL; 00955 length = strlen(a->word); 00956 aoi = ao2_iterator_init(class_container, 0); 00957 while ((class = ao2_iterator_next(&aoi))) { 00958 if (!strncasecmp(a->word, class->name, length) && ++which > a->n) { 00959 ret = ast_strdup(class->name); 00960 } 00961 ao2_ref(class, -1); 00962 if (ret) { 00963 break; 00964 } 00965 } 00966 ao2_iterator_destroy(&aoi); 00967 if (!ret && !strncasecmp(a->word, "all", length) && ++which > a->n) { 00968 ret = ast_strdup("all"); 00969 } 00970 return ret; 00971 } 00972 00973 ast_cli(a->fd, "\nODBC DSN Settings\n"); 00974 ast_cli(a->fd, "-----------------\n\n"); 00975 aoi = ao2_iterator_init(class_container, 0); 00976 while ((class = ao2_iterator_next(&aoi))) { 00977 if ((a->argc == 2) || (a->argc == 3 && !strcmp(a->argv[2], "all")) || (!strcmp(a->argv[2], class->name))) { 00978 int count = 0; 00979 char timestr[80]; 00980 struct ast_tm tm; 00981 00982 ast_localtime(&class->last_negative_connect, &tm, NULL); 00983 ast_strftime(timestr, sizeof(timestr), "%Y-%m-%d %T", &tm); 00984 ast_cli(a->fd, " Name: %s\n DSN: %s\n", class->name, class->dsn); 00985 ast_cli(a->fd, " Last connection attempt: %s\n", timestr); 00986 00987 if (class->haspool) { 00988 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0); 00989 00990 ast_cli(a->fd, " Pooled: Yes\n Limit: %u\n Connections in use: %d\n", class->limit, class->count); 00991 00992 while ((current = ao2_iterator_next(&aoi2))) { 00993 ast_mutex_lock(¤t->lock); 00994 #ifdef DEBUG_THREADS 00995 ast_cli(a->fd, " - Connection %d: %s (%s:%d %s)\n", ++count, 00996 current->used ? "in use" : 00997 current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected", 00998 current->file, current->lineno, current->function); 00999 #else 01000 ast_cli(a->fd, " - Connection %d: %s\n", ++count, 01001 current->used ? "in use" : 01002 current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected"); 01003 #endif 01004 ast_mutex_unlock(¤t->lock); 01005 ao2_ref(current, -1); 01006 } 01007 ao2_iterator_destroy(&aoi2); 01008 } else { 01009 /* Should only ever be one of these (unless there are transactions) */ 01010 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0); 01011 while ((current = ao2_iterator_next(&aoi2))) { 01012 ast_cli(a->fd, " Pooled: No\n Connected: %s\n", current->used ? "In use" : 01013 current->up && ast_odbc_sanity_check(current) ? "Yes" : "No"); 01014 ao2_ref(current, -1); 01015 } 01016 ao2_iterator_destroy(&aoi2); 01017 } 01018 ast_cli(a->fd, "\n"); 01019 } 01020 ao2_ref(class, -1); 01021 } 01022 ao2_iterator_destroy(&aoi); 01023 01024 return CLI_SUCCESS; 01025 }
static const char* isolation2text | ( | int | iso | ) | [static] |
Definition at line 190 of file res_odbc.c.
Referenced by acf_transaction_read(), and data_odbc_provider_handler().
00191 { 00192 if (iso == SQL_TXN_READ_COMMITTED) { 00193 return "read_committed"; 00194 } else if (iso == SQL_TXN_READ_UNCOMMITTED) { 00195 return "read_uncommitted"; 00196 } else if (iso == SQL_TXN_SERIALIZABLE) { 00197 return "serializable"; 00198 } else if (iso == SQL_TXN_REPEATABLE_READ) { 00199 return "repeatable_read"; 00200 } else { 00201 return "unknown"; 00202 } 00203 }
static int load_module | ( | void | ) | [static] |
Definition at line 1892 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_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().
01893 { 01894 if (!(class_container = ao2_container_alloc(1, null_hash_fn, ao2_match_by_addr))) 01895 return AST_MODULE_LOAD_DECLINE; 01896 if (load_odbc_config() == -1) 01897 return AST_MODULE_LOAD_DECLINE; 01898 ast_cli_register_multiple(cli_odbc, ARRAY_LEN(cli_odbc)); 01899 ast_data_register_multiple(odbc_providers, ARRAY_LEN(odbc_providers)); 01900 ast_register_application_xml(app_commit, commit_exec); 01901 ast_register_application_xml(app_rollback, rollback_exec); 01902 ast_custom_function_register(&odbc_function); 01903 ast_log(LOG_NOTICE, "res_odbc loaded.\n"); 01904 return 0; 01905 }
static int load_odbc_config | ( | void | ) | [static] |
Definition at line 770 of file res_odbc.c.
References odbc_class::allow_empty_strings, 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::conntimeout, odbc_class::dsn, enabled, odbc_class::idlecheck, 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().
00771 { 00772 static char *cfg = "res_odbc.conf"; 00773 struct ast_config *config; 00774 struct ast_variable *v; 00775 char *cat; 00776 const char *dsn, *username, *password, *sanitysql; 00777 int enabled, pooling, limit, bse, conntimeout, forcecommit, isolation, allow_empty_strings; 00778 struct timeval ncache = { 0, 0 }; 00779 unsigned int idlecheck; 00780 int preconnect = 0, res = 0; 00781 struct ast_flags config_flags = { 0 }; 00782 00783 struct odbc_class *new; 00784 00785 config = ast_config_load(cfg, config_flags); 00786 if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) { 00787 ast_log(LOG_WARNING, "Unable to load config file res_odbc.conf\n"); 00788 return -1; 00789 } 00790 for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) { 00791 if (!strcasecmp(cat, "ENV")) { 00792 for (v = ast_variable_browse(config, cat); v; v = v->next) { 00793 setenv(v->name, v->value, 1); 00794 ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value); 00795 } 00796 } else { 00797 /* Reset all to defaults for each class of odbc connections */ 00798 dsn = username = password = sanitysql = NULL; 00799 enabled = 1; 00800 preconnect = idlecheck = 0; 00801 pooling = 0; 00802 limit = 0; 00803 bse = 1; 00804 conntimeout = 10; 00805 forcecommit = 0; 00806 allow_empty_strings = 1; 00807 isolation = SQL_TXN_READ_COMMITTED; 00808 for (v = ast_variable_browse(config, cat); v; v = v->next) { 00809 if (!strcasecmp(v->name, "pooling")) { 00810 if (ast_true(v->value)) 00811 pooling = 1; 00812 } else if (!strncasecmp(v->name, "share", 5)) { 00813 /* "shareconnections" is a little clearer in meaning than "pooling" */ 00814 if (ast_false(v->value)) 00815 pooling = 1; 00816 } else if (!strcasecmp(v->name, "limit")) { 00817 sscanf(v->value, "%30d", &limit); 00818 if (ast_true(v->value) && !limit) { 00819 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); 00820 limit = 1023; 00821 } else if (ast_false(v->value)) { 00822 ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'. Disabling ODBC class '%s'.\n", v->value, cat); 00823 enabled = 0; 00824 break; 00825 } 00826 } else if (!strcasecmp(v->name, "idlecheck")) { 00827 sscanf(v->value, "%30u", &idlecheck); 00828 } else if (!strcasecmp(v->name, "enabled")) { 00829 enabled = ast_true(v->value); 00830 } else if (!strcasecmp(v->name, "pre-connect")) { 00831 preconnect = ast_true(v->value); 00832 } else if (!strcasecmp(v->name, "dsn")) { 00833 dsn = v->value; 00834 } else if (!strcasecmp(v->name, "username")) { 00835 username = v->value; 00836 } else if (!strcasecmp(v->name, "password")) { 00837 password = v->value; 00838 } else if (!strcasecmp(v->name, "sanitysql")) { 00839 sanitysql = v->value; 00840 } else if (!strcasecmp(v->name, "backslash_is_escape")) { 00841 bse = ast_true(v->value); 00842 } else if (!strcasecmp(v->name, "allow_empty_string_in_nontext")) { 00843 allow_empty_strings = ast_true(v->value); 00844 } else if (!strcasecmp(v->name, "connect_timeout")) { 00845 if (sscanf(v->value, "%d", &conntimeout) != 1 || conntimeout < 1) { 00846 ast_log(LOG_WARNING, "connect_timeout must be a positive integer\n"); 00847 conntimeout = 10; 00848 } 00849 } else if (!strcasecmp(v->name, "negative_connection_cache")) { 00850 double dncache; 00851 if (sscanf(v->value, "%lf", &dncache) != 1 || dncache < 0) { 00852 ast_log(LOG_WARNING, "negative_connection_cache must be a non-negative integer\n"); 00853 /* 5 minutes sounds like a reasonable default */ 00854 ncache.tv_sec = 300; 00855 ncache.tv_usec = 0; 00856 } else { 00857 ncache.tv_sec = (int)dncache; 00858 ncache.tv_usec = (dncache - ncache.tv_sec) * 1000000; 00859 } 00860 } else if (!strcasecmp(v->name, "forcecommit")) { 00861 forcecommit = ast_true(v->value); 00862 } else if (!strcasecmp(v->name, "isolation")) { 00863 if ((isolation = text2isolation(v->value)) == 0) { 00864 ast_log(LOG_ERROR, "Unrecognized value for 'isolation': '%s' in section '%s'\n", v->value, cat); 00865 isolation = SQL_TXN_READ_COMMITTED; 00866 } 00867 } 00868 } 00869 00870 if (enabled && !ast_strlen_zero(dsn)) { 00871 new = ao2_alloc(sizeof(*new), odbc_class_destructor); 00872 00873 if (!new) { 00874 res = -1; 00875 break; 00876 } 00877 00878 SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env); 00879 res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0); 00880 00881 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00882 ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n"); 00883 ao2_ref(new, -1); 00884 return res; 00885 } 00886 00887 new->obj_container = ao2_container_alloc(1, null_hash_fn, ao2_match_by_addr); 00888 00889 if (pooling) { 00890 new->haspool = pooling; 00891 if (limit) { 00892 new->limit = limit; 00893 } else { 00894 ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless. Changing limit from 0 to 5.\n"); 00895 new->limit = 5; 00896 } 00897 } 00898 00899 new->backslash_is_escape = bse ? 1 : 0; 00900 new->forcecommit = forcecommit ? 1 : 0; 00901 new->isolation = isolation; 00902 new->idlecheck = idlecheck; 00903 new->conntimeout = conntimeout; 00904 new->negative_connection_cache = ncache; 00905 new->allow_empty_strings = allow_empty_strings ? 1 : 0; 00906 00907 if (cat) 00908 ast_copy_string(new->name, cat, sizeof(new->name)); 00909 if (dsn) 00910 ast_copy_string(new->dsn, dsn, sizeof(new->dsn)); 00911 if (username && !(new->username = ast_strdup(username))) { 00912 ao2_ref(new, -1); 00913 break; 00914 } 00915 if (password && !(new->password = ast_strdup(password))) { 00916 ao2_ref(new, -1); 00917 break; 00918 } 00919 if (sanitysql && !(new->sanitysql = ast_strdup(sanitysql))) { 00920 ao2_ref(new, -1); 00921 break; 00922 } 00923 00924 odbc_register_class(new, preconnect); 00925 ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn); 00926 ao2_ref(new, -1); 00927 new = NULL; 00928 } 00929 } 00930 } 00931 ast_config_destroy(config); 00932 return res; 00933 }
static int mark_transaction_active | ( | struct ast_channel * | chan, | |
struct odbc_txn_frame * | tx | |||
) | [static] |
Definition at line 359 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_txn_frame::owner, and txn_info.
Referenced by acf_transaction_write().
00360 { 00361 struct ast_datastore *txn_store; 00362 AST_LIST_HEAD(, odbc_txn_frame) *oldlist; 00363 struct odbc_txn_frame *active = NULL, *txn; 00364 00365 if (!chan && tx && tx->owner) { 00366 chan = tx->owner; 00367 } 00368 00369 if (!chan) { 00370 return -1; 00371 } 00372 00373 ast_channel_lock(chan); 00374 if (!(txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) { 00375 ast_channel_unlock(chan); 00376 return -1; 00377 } 00378 00379 oldlist = txn_store->data; 00380 AST_LIST_LOCK(oldlist); 00381 AST_LIST_TRAVERSE(oldlist, txn, list) { 00382 if (txn == tx) { 00383 txn->active = 1; 00384 active = txn; 00385 } else { 00386 txn->active = 0; 00387 } 00388 } 00389 AST_LIST_UNLOCK(oldlist); 00390 ast_channel_unlock(chan); 00391 return active ? 0 : -1; 00392 }
static int null_hash_fn | ( | const void * | obj, | |
const int | flags | |||
) | [static] |
Definition at line 413 of file res_odbc.c.
Referenced by load_module(), and load_odbc_config().
static void odbc_class_destructor | ( | void * | data | ) | [static] |
Definition at line 394 of file res_odbc.c.
References ao2_ref, and ast_free.
Referenced by load_odbc_config().
00395 { 00396 struct odbc_class *class = data; 00397 /* Due to refcounts, we can safely assume that any objects with a reference 00398 * to us will prevent our destruction, so we don't need to worry about them. 00399 */ 00400 if (class->username) { 00401 ast_free(class->username); 00402 } 00403 if (class->password) { 00404 ast_free(class->password); 00405 } 00406 if (class->sanitysql) { 00407 ast_free(class->sanitysql); 00408 } 00409 ao2_ref(class->obj_container, -1); 00410 SQLFreeHandle(SQL_HANDLE_ENV, class->env); 00411 }
static odbc_status odbc_obj_connect | ( | struct odbc_obj * | obj | ) | [static] |
Definition at line 1524 of file res_odbc.c.
References ast_assert, ast_log(), ast_tvnow(), odbc_obj::con, odbc_class::conntimeout, odbc_class::dsn, odbc_class::env, odbc_class::last_negative_connect, odbc_obj::last_used, LOG_NOTICE, LOG_WARNING, 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_request_obj2(), and ast_odbc_sanity_check().
01525 { 01526 int res; 01527 SQLINTEGER err; 01528 short int mlen; 01529 unsigned char msg[200], state[10]; 01530 #ifdef NEEDTRACE 01531 SQLINTEGER enable = 1; 01532 char *tracefile = "/tmp/odbc.trace"; 01533 #endif 01534 SQLHDBC con; 01535 01536 if (obj->up) { 01537 odbc_obj_disconnect(obj); 01538 ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name); 01539 } else { 01540 ast_assert(obj->con == NULL); 01541 ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name); 01542 } 01543 01544 res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &con); 01545 01546 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 01547 ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res); 01548 obj->parent->last_negative_connect = ast_tvnow(); 01549 return ODBC_FAIL; 01550 } 01551 SQLSetConnectAttr(con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)(long) obj->parent->conntimeout, 0); 01552 SQLSetConnectAttr(con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *)(long) obj->parent->conntimeout, 0); 01553 #ifdef NEEDTRACE 01554 SQLSetConnectAttr(con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER); 01555 SQLSetConnectAttr(con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile)); 01556 #endif 01557 01558 res = SQLConnect(con, 01559 (SQLCHAR *) obj->parent->dsn, SQL_NTS, 01560 (SQLCHAR *) obj->parent->username, SQL_NTS, 01561 (SQLCHAR *) obj->parent->password, SQL_NTS); 01562 01563 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 01564 SQLGetDiagRec(SQL_HANDLE_DBC, con, 1, state, &err, msg, 100, &mlen); 01565 obj->parent->last_negative_connect = ast_tvnow(); 01566 ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg); 01567 if ((res = SQLFreeHandle(SQL_HANDLE_DBC, con) != SQL_SUCCESS)) { 01568 SQLGetDiagRec(SQL_HANDLE_DBC, con, 1, state, &err, msg, 100, &mlen); 01569 ast_log(LOG_WARNING, "Unable to deallocate database handle %p? %d errno=%d %s\n", con, res, (int)err, msg); 01570 } 01571 return ODBC_FAIL; 01572 } else { 01573 ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn); 01574 obj->up = 1; 01575 obj->last_used = ast_tvnow(); 01576 } 01577 01578 obj->con = con; 01579 return ODBC_SUCCESS; 01580 }
static void odbc_obj_destructor | ( | void * | data | ) | [static] |
Definition at line 418 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().
00419 { 00420 struct odbc_obj *obj = data; 00421 struct odbc_class *class = obj->parent; 00422 obj->parent = NULL; 00423 odbc_obj_disconnect(obj); 00424 ast_mutex_destroy(&obj->lock); 00425 ao2_ref(class, -1); 00426 }
static odbc_status odbc_obj_disconnect | ( | struct odbc_obj * | obj | ) | [static] |
Definition at line 1488 of file res_odbc.c.
References ast_log(), odbc_obj::con, odbc_class::dsn, LOG_DEBUG, LOG_WARNING, odbc_class::name, ODBC_SUCCESS, odbc_obj::parent, and odbc_obj::up.
Referenced by ast_odbc_sanity_check(), odbc_obj_connect(), and odbc_obj_destructor().
01489 { 01490 int res; 01491 SQLINTEGER err; 01492 short int mlen; 01493 unsigned char msg[200], state[10]; 01494 SQLHDBC con; 01495 01496 /* Nothing to disconnect */ 01497 if (!obj->con) { 01498 return ODBC_SUCCESS; 01499 } 01500 01501 con = obj->con; 01502 obj->con = NULL; 01503 res = SQLDisconnect(con); 01504 01505 if (obj->parent) { 01506 if (res == SQL_SUCCESS || res == SQL_SUCCESS_WITH_INFO) { 01507 ast_log(LOG_DEBUG, "Disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn); 01508 } else { 01509 ast_log(LOG_DEBUG, "res_odbc: %s [%s] already disconnected\n", obj->parent->name, obj->parent->dsn); 01510 } 01511 } 01512 01513 if ((res = SQLFreeHandle(SQL_HANDLE_DBC, con) == SQL_SUCCESS)) { 01514 ast_log(LOG_DEBUG, "Database handle %p deallocated\n", con); 01515 } else { 01516 SQLGetDiagRec(SQL_HANDLE_DBC, con, 1, state, &err, msg, 100, &mlen); 01517 ast_log(LOG_WARNING, "Unable to deallocate database handle %p? %d errno=%d %s\n", con, res, (int)err, msg); 01518 } 01519 01520 obj->up = 0; 01521 return ODBC_SUCCESS; 01522 }
static int odbc_register_class | ( | struct odbc_class * | class, | |
int | connect | |||
) | [static] |
Definition at line 1031 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().
01032 { 01033 struct odbc_obj *obj; 01034 if (class) { 01035 ao2_link(class_container, class); 01036 /* I still have a reference in the caller, so a deref is NOT missing here. */ 01037 01038 if (preconnect) { 01039 /* Request and release builds a connection */ 01040 obj = ast_odbc_request_obj(class->name, 0); 01041 if (obj) { 01042 ast_odbc_release_obj(obj); 01043 } 01044 } 01045 01046 return 0; 01047 } else { 01048 ast_log(LOG_WARNING, "Attempted to register a NULL class?\n"); 01049 return -1; 01050 } 01051 }
static void odbc_release_obj2 | ( | struct odbc_obj * | obj, | |
struct odbc_txn_frame * | tx | |||
) | [static] |
Definition at line 1053 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().
01054 { 01055 SQLINTEGER nativeerror=0, numfields=0; 01056 SQLSMALLINT diagbytes=0, i; 01057 unsigned char state[10], diagnostic[256]; 01058 01059 ast_debug(2, "odbc_release_obj2(%p) called (obj->txf = %p)\n", obj, obj->txf); 01060 if (tx) { 01061 ast_debug(1, "called on a transactional handle with %s\n", tx->forcecommit ? "COMMIT" : "ROLLBACK"); 01062 if (SQLEndTran(SQL_HANDLE_DBC, obj->con, tx->forcecommit ? SQL_COMMIT : SQL_ROLLBACK) == SQL_ERROR) { 01063 /* Handle possible transaction commit failure */ 01064 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01065 for (i = 0; i < numfields; i++) { 01066 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01067 ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic); 01068 if (!strcmp((char *)state, "25S02") || !strcmp((char *)state, "08007")) { 01069 /* These codes mean that a commit failed and a transaction 01070 * is still active. We must rollback, or things will get 01071 * very, very weird for anybody using the handle next. */ 01072 SQLEndTran(SQL_HANDLE_DBC, obj->con, SQL_ROLLBACK); 01073 } 01074 if (i > 10) { 01075 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01076 break; 01077 } 01078 } 01079 } 01080 01081 /* Transaction is done, reset autocommit */ 01082 if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) { 01083 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01084 for (i = 0; i < numfields; i++) { 01085 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01086 ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic); 01087 if (i > 10) { 01088 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01089 break; 01090 } 01091 } 01092 } 01093 } 01094 01095 #ifdef DEBUG_THREADS 01096 obj->file[0] = '\0'; 01097 obj->function[0] = '\0'; 01098 obj->lineno = 0; 01099 #endif 01100 01101 /* For pooled connections, this frees the connection to be 01102 * reused. For non-pooled connections, it does nothing. */ 01103 obj->used = 0; 01104 if (obj->txf) { 01105 /* Prevent recursion -- transaction is already closed out. */ 01106 obj->txf->obj = NULL; 01107 obj->txf = release_transaction(obj->txf); 01108 } 01109 ao2_ref(obj, -1); 01110 }
static void odbc_txn_free | ( | void * | data | ) | [static] |
Definition at line 343 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, and release_transaction().
00344 { 00345 struct odbc_txn_frame *tx; 00346 AST_LIST_HEAD(, odbc_txn_frame) *oldlist = vdata; 00347 00348 ast_debug(2, "odbc_txn_free(%p) called\n", vdata); 00349 00350 AST_LIST_LOCK(oldlist); 00351 while ((tx = AST_LIST_REMOVE_HEAD(oldlist, list))) { 00352 release_transaction(tx); 00353 } 00354 AST_LIST_UNLOCK(oldlist); 00355 AST_LIST_HEAD_DESTROY(oldlist); 00356 ast_free(oldlist); 00357 }
static struct odbc_txn_frame* release_transaction | ( | struct odbc_txn_frame * | tx | ) | [static, read] |
Definition at line 307 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_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().
00308 { 00309 if (!tx) { 00310 return NULL; 00311 } 00312 00313 ast_debug(2, "release_transaction(%p) called (tx->obj = %p, tx->obj->txf = %p)\n", tx, tx->obj, tx->obj ? tx->obj->txf : NULL); 00314 00315 /* If we have an owner, disassociate */ 00316 if (tx->owner) { 00317 struct ast_datastore *txn_store; 00318 AST_LIST_HEAD(, odbc_txn_frame) *oldlist; 00319 00320 ast_channel_lock(tx->owner); 00321 if ((txn_store = ast_channel_datastore_find(tx->owner, &txn_info, NULL))) { 00322 oldlist = txn_store->data; 00323 AST_LIST_LOCK(oldlist); 00324 AST_LIST_REMOVE(oldlist, tx, list); 00325 AST_LIST_UNLOCK(oldlist); 00326 } 00327 ast_channel_unlock(tx->owner); 00328 tx->owner = NULL; 00329 } 00330 00331 if (tx->obj) { 00332 /* If we have any uncommitted transactions, they are handled when we release the object */ 00333 struct odbc_obj *obj = tx->obj; 00334 /* Prevent recursion during destruction */ 00335 tx->obj->txf = NULL; 00336 tx->obj = NULL; 00337 odbc_release_obj2(obj, tx); 00338 } 00339 ast_free(tx); 00340 return NULL; 00341 }
static int reload | ( | void | ) | [static] |
Definition at line 1812 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.
01813 { 01814 struct odbc_cache_tables *table; 01815 struct odbc_class *class; 01816 struct odbc_obj *current; 01817 struct ao2_iterator aoi = ao2_iterator_init(class_container, 0); 01818 01819 /* First, mark all to be purged */ 01820 while ((class = ao2_iterator_next(&aoi))) { 01821 class->delme = 1; 01822 ao2_ref(class, -1); 01823 } 01824 ao2_iterator_destroy(&aoi); 01825 01826 load_odbc_config(); 01827 01828 /* Purge remaining classes */ 01829 01830 /* Note on how this works; this is a case of circular references, so we 01831 * explicitly do NOT want to use a callback here (or we wind up in 01832 * recursive hell). 01833 * 01834 * 1. Iterate through all the classes. Note that the classes will currently 01835 * contain two classes of the same name, one of which is marked delme and 01836 * will be purged when all remaining objects of the class are released, and 01837 * the other, which was created above when we re-parsed the config file. 01838 * 2. On each class, there is a reference held by the master container and 01839 * a reference held by each connection object. There are two cases for 01840 * destruction of the class, noted below. However, in all cases, all O-refs 01841 * (references to objects) will first be freed, which will cause the C-refs 01842 * (references to classes) to be decremented (but never to 0, because the 01843 * class container still has a reference). 01844 * a) If the class has outstanding objects, the C-ref by the class 01845 * container will then be freed, which leaves only C-refs by any 01846 * outstanding objects. When the final outstanding object is released 01847 * (O-refs held by applications and dialplan functions), it will in turn 01848 * free the final C-ref, causing class destruction. 01849 * b) If the class has no outstanding objects, when the class container 01850 * removes the final C-ref, the class will be destroyed. 01851 */ 01852 aoi = ao2_iterator_init(class_container, 0); 01853 while ((class = ao2_iterator_next(&aoi))) { /* C-ref++ (by iterator) */ 01854 if (class->delme) { 01855 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0); 01856 while ((current = ao2_iterator_next(&aoi2))) { /* O-ref++ (by iterator) */ 01857 ao2_unlink(class->obj_container, current); /* unlink O-ref from class (reference handled implicitly) */ 01858 ao2_ref(current, -1); /* O-ref-- (by iterator) */ 01859 /* At this point, either 01860 * a) there's an outstanding O-ref, or 01861 * b) the object has already been destroyed. 01862 */ 01863 } 01864 ao2_iterator_destroy(&aoi2); 01865 ao2_unlink(class_container, class); /* unlink C-ref from container (reference handled implicitly) */ 01866 /* At this point, either 01867 * a) there's an outstanding O-ref, which holds an outstanding C-ref, or 01868 * b) the last remaining C-ref is held by the iterator, which will be 01869 * destroyed in the next step. 01870 */ 01871 } 01872 ao2_ref(class, -1); /* C-ref-- (by iterator) */ 01873 } 01874 ao2_iterator_destroy(&aoi); 01875 01876 /* Empty the cache; it will get rebuilt the next time the tables are needed. */ 01877 AST_RWLIST_WRLOCK(&odbc_tables); 01878 while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) { 01879 destroy_table_cache(table); 01880 } 01881 AST_RWLIST_UNLOCK(&odbc_tables); 01882 01883 return 0; 01884 }
static int rollback_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
Definition at line 1165 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().
01166 { 01167 struct odbc_txn_frame *tx; 01168 SQLINTEGER nativeerror=0, numfields=0; 01169 SQLSMALLINT diagbytes=0, i; 01170 unsigned char state[10], diagnostic[256]; 01171 01172 if (ast_strlen_zero(data)) { 01173 tx = find_transaction(chan, NULL, NULL, 1); 01174 } else { 01175 tx = find_transaction(chan, NULL, data, 0); 01176 } 01177 01178 pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", "OK"); 01179 01180 if (tx) { 01181 if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_ROLLBACK) == SQL_ERROR) { 01182 struct ast_str *errors = ast_str_thread_get(&errors_buf, 16); 01183 ast_str_reset(errors); 01184 01185 /* Handle possible transaction commit failure */ 01186 SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 01187 for (i = 0; i < numfields; i++) { 01188 SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 01189 ast_str_append(&errors, 0, "%s%s", ast_str_strlen(errors) ? "," : "", state); 01190 ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic); 01191 if (i > 10) { 01192 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 01193 break; 01194 } 01195 } 01196 pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", ast_str_buffer(errors)); 01197 } 01198 } 01199 return 0; 01200 }
static int text2isolation | ( | const char * | txt | ) | [static] |
Definition at line 205 of file res_odbc.c.
Referenced by acf_transaction_write(), and load_odbc_config().
00206 { 00207 if (strncasecmp(txt, "read_", 5) == 0) { 00208 if (strncasecmp(txt + 5, "c", 1) == 0) { 00209 return SQL_TXN_READ_COMMITTED; 00210 } else if (strncasecmp(txt + 5, "u", 1) == 0) { 00211 return SQL_TXN_READ_UNCOMMITTED; 00212 } else { 00213 return 0; 00214 } 00215 } else if (strncasecmp(txt, "ser", 3) == 0) { 00216 return SQL_TXN_SERIALIZABLE; 00217 } else if (strncasecmp(txt, "rep", 3) == 0) { 00218 return SQL_TXN_REPEATABLE_READ; 00219 } else { 00220 return 0; 00221 } 00222 }
static int unload_module | ( | void | ) | [static] |
Definition at line 1886 of file res_odbc.c.
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 = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_REALTIME_DEPEND, } [static] |
Definition at line 1912 of file res_odbc.c.
const char* const app_commit = "ODBC_Commit" [static] |
Definition at line 1719 of file res_odbc.c.
Referenced by load_module().
const char* const app_rollback = "ODBC_Rollback" [static] |
Definition at line 1720 of file res_odbc.c.
Referenced by load_module().
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 1912 of file res_odbc.c.
struct ao2_container* class_container [static] |
Definition at line 145 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] |
{ AST_CLI_DEFINE(handle_cli_odbc_show, "List ODBC DSN(s)") }
Definition at line 1027 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 155 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 1713 of file res_odbc.c.
Referenced by load_module().
struct ast_data_handler odbc_provider [static] |
{ .version = AST_DATA_HANDLER_VERSION, .get = data_odbc_provider_handler }
Definition at line 1803 of file res_odbc.c.
struct ast_data_entry odbc_providers[] [static] |
{ AST_DATA_ENTRY("/asterisk/res/odbc", &odbc_provider), }
Definition at line 1808 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 157 of file res_odbc.c.
Referenced by ast_odbc_retrieve_transaction_obj(), find_transaction(), mark_transaction_active(), and release_transaction().