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