#include "asterisk.h"
#include "asterisk/module.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/config.h"
#include "asterisk/res_odbc.h"
#include "asterisk/app.h"
#include "asterisk/cli.h"
#include "asterisk/strings.h"

Go to the source code of this file.
Data Structures | |
| struct | acf_odbc_query |
| struct | odbc_datastore |
| struct | odbc_datastore_row |
| struct | queries |
Enumerations | |
| enum | odbc_option_flags { OPT_ESCAPECOMMAS = (1 << 0), OPT_MULTIROW = (1 << 1) } |
Functions | |
| static void | __fini_queries (void) |
| static void | __init_coldata_buf (void) |
| static void | __init_colnames_buf (void) |
| static void | __init_queries (void) |
| static void | __init_sql2_buf (void) |
| static void | __init_sql_buf (void) |
| static void | __reg_module (void) |
| static void | __unreg_module (void) |
| static int | acf_escape (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
| static int | acf_fetch (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
| static int | acf_odbc_read (struct ast_channel *chan, const char *cmd, char *s, char *buf, size_t len) |
| static int | acf_odbc_write (struct ast_channel *chan, const char *cmd, char *s, const char *value) |
| static char * | cli_odbc_read (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
| static char * | cli_odbc_write (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
| static int | exec_odbcfinish (struct ast_channel *chan, const char *data) |
| static int | free_acf_query (struct acf_odbc_query *query) |
| static SQLHSTMT | generic_execute (struct odbc_obj *obj, void *data) |
| static int | init_acf_query (struct ast_config *cfg, char *catg, struct acf_odbc_query **query) |
| static int | load_module (void) |
| static void | odbc_datastore_free (void *data) |
| static int | reload (void) |
| static int | unload_module (void) |
Variables | |
| static struct ast_module_info | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "ODBC lookups" , .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 = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, } |
| static char * | app_odbcfinish = "ODBCFinish" |
| static struct ast_module_info * | ast_module_info = &__mod_info |
| static struct ast_cli_entry | cli_func_odbc [] |
| static struct ast_threadstorage | coldata_buf = { .once = PTHREAD_ONCE_INIT, .key_init = __init_coldata_buf , .custom_init = NULL , } |
| static struct ast_threadstorage | colnames_buf = { .once = PTHREAD_ONCE_INIT, .key_init = __init_colnames_buf , .custom_init = NULL , } |
| static char * | config = "func_odbc.conf" |
| static struct ast_custom_function | escape_function |
| static struct ast_custom_function | fetch_function |
| static struct ast_datastore_info | odbc_info |
| static int | resultcount = 0 |
| static struct ast_threadstorage | sql2_buf = { .once = PTHREAD_ONCE_INIT, .key_init = __init_sql2_buf , .custom_init = NULL , } |
| static struct ast_threadstorage | sql_buf = { .once = PTHREAD_ONCE_INIT, .key_init = __init_sql_buf , .custom_init = NULL , } |
Definition in file func_odbc.c.
| enum odbc_option_flags |
Definition at line 102 of file func_odbc.c.
00102 { 00103 OPT_ESCAPECOMMAS = (1 << 0), 00104 OPT_MULTIROW = (1 << 1), 00105 };
| static void __fini_queries | ( | void | ) | [static] |
| static void __init_coldata_buf | ( | void | ) | [static] |
| static void __init_colnames_buf | ( | void | ) | [static] |
| static void __init_queries | ( | void | ) | [static] |
| static void __init_sql2_buf | ( | void | ) | [static] |
| static void __init_sql_buf | ( | void | ) | [static] |
| static void __reg_module | ( | void | ) | [static] |
Definition at line 1493 of file func_odbc.c.
| static void __unreg_module | ( | void | ) | [static] |
Definition at line 1493 of file func_odbc.c.
| static int acf_escape | ( | struct ast_channel * | chan, | |
| const char * | cmd, | |||
| char * | data, | |||
| char * | buf, | |||
| size_t | len | |||
| ) | [static] |
| static int acf_fetch | ( | struct ast_channel * | chan, | |
| const char * | cmd, | |||
| char * | data, | |||
| char * | buf, | |||
| size_t | len | |||
| ) | [static] |
Definition at line 748 of file func_odbc.c.
References ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_copy_string(), ast_datastore_free(), ast_free, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, odbc_datastore_row::data, ast_datastore::data, acf_odbc_query::list, odbc_datastore::names, odbc_info, and pbx_builtin_setvar_helper().
Referenced by acf_odbc_read().
00749 { 00750 struct ast_datastore *store; 00751 struct odbc_datastore *resultset; 00752 struct odbc_datastore_row *row; 00753 store = ast_channel_datastore_find(chan, &odbc_info, data); 00754 if (!store) { 00755 pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "FAILURE"); 00756 return -1; 00757 } 00758 resultset = store->data; 00759 AST_LIST_LOCK(resultset); 00760 row = AST_LIST_REMOVE_HEAD(resultset, list); 00761 AST_LIST_UNLOCK(resultset); 00762 if (!row) { 00763 /* Cleanup datastore */ 00764 ast_channel_datastore_remove(chan, store); 00765 ast_datastore_free(store); 00766 pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "FAILURE"); 00767 return -1; 00768 } 00769 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", resultset->names); 00770 ast_copy_string(buf, row->data, len); 00771 ast_free(row); 00772 pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "SUCCESS"); 00773 return 0; 00774 }
| static int acf_odbc_read | ( | struct ast_channel * | chan, | |
| const char * | cmd, | |||
| char * | s, | |||
| char * | buf, | |||
| size_t | len | |||
| ) | [static] |
Definition at line 387 of file func_odbc.c.
References acf_odbc_query::acf, acf_fetch(), AST_APP_ARG, ast_atomic_fetchadd_int(), ast_autoservice_start(), ast_autoservice_stop(), ast_calloc, ast_channel_datastore_add(), ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_channel_lock, ast_channel_release(), ast_channel_unlock, ast_copy_string(), ast_datastore_alloc, ast_debug, AST_DECLARE_APP_ARGS, ast_dummy_channel_alloc(), ast_free, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, ast_log(), ast_odbc_ast_str_SQLGetData(), ast_odbc_direct_execute(), ast_odbc_release_obj(), ast_odbc_request_obj(), ast_realloc, AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, AST_STANDARD_APP_ARGS, ast_str_append(), ast_str_append_escapecommas(), ast_str_buffer(), ast_str_make_space(), ast_str_reset(), ast_str_strlen(), ast_str_substitute_variables(), ast_str_thread_get(), ast_strlen_zero(), ast_test_flag, ast_verb, coldata_buf, colnames_buf, ast_datastore::data, dsn, generic_execute(), acf_odbc_query::list, LOG_ERROR, LOG_WARNING, ast_custom_function::name, odbc_datastore_free(), odbc_info, OPT_ESCAPECOMMAS, OPT_MULTIROW, pbx_builtin_pushvar_helper(), pbx_builtin_setvar_helper(), acf_odbc_query::readhandle, resultcount, acf_odbc_query::rowlimit, sql_buf, acf_odbc_query::sql_read, and status.
Referenced by init_acf_query().
00388 { 00389 struct odbc_obj *obj = NULL; 00390 struct acf_odbc_query *query; 00391 char varname[15], rowcount[12] = "-1"; 00392 struct ast_str *colnames = ast_str_thread_get(&colnames_buf, 16); 00393 int res, x, y, buflen = 0, escapecommas, rowlimit = 1, multirow = 0, dsn, bogus_chan = 0; 00394 AST_DECLARE_APP_ARGS(args, 00395 AST_APP_ARG(field)[100]; 00396 ); 00397 SQLHSTMT stmt = NULL; 00398 SQLSMALLINT colcount=0; 00399 SQLLEN indicator; 00400 SQLSMALLINT collength; 00401 struct odbc_datastore *resultset = NULL; 00402 struct odbc_datastore_row *row = NULL; 00403 struct ast_str *sql = ast_str_thread_get(&sql_buf, 16); 00404 const char *status = "FAILURE"; 00405 00406 if (!sql) { 00407 if (chan) { 00408 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status); 00409 } 00410 return -1; 00411 } 00412 00413 ast_str_reset(colnames); 00414 00415 AST_RWLIST_RDLOCK(&queries); 00416 AST_RWLIST_TRAVERSE(&queries, query, list) { 00417 if (!strcmp(query->acf->name, cmd)) { 00418 break; 00419 } 00420 } 00421 00422 if (!query) { 00423 ast_log(LOG_ERROR, "No such function '%s'\n", cmd); 00424 AST_RWLIST_UNLOCK(&queries); 00425 if (chan) { 00426 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount); 00427 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status); 00428 } 00429 return -1; 00430 } 00431 00432 if (!chan) { 00433 if (!(chan = ast_dummy_channel_alloc())) { 00434 AST_RWLIST_UNLOCK(&queries); 00435 return -1; 00436 } 00437 bogus_chan = 1; 00438 } 00439 00440 if (!bogus_chan) { 00441 ast_autoservice_start(chan); 00442 } 00443 00444 AST_STANDARD_APP_ARGS(args, s); 00445 for (x = 0; x < args.argc; x++) { 00446 snprintf(varname, sizeof(varname), "ARG%d", x + 1); 00447 pbx_builtin_pushvar_helper(chan, varname, args.field[x]); 00448 } 00449 00450 ast_str_substitute_variables(&sql, 0, chan, query->sql_read); 00451 00452 if (bogus_chan) { 00453 chan = ast_channel_release(chan); 00454 } else { 00455 /* Restore prior values */ 00456 for (x = 0; x < args.argc; x++) { 00457 snprintf(varname, sizeof(varname), "ARG%d", x + 1); 00458 pbx_builtin_setvar_helper(chan, varname, NULL); 00459 } 00460 } 00461 00462 /* Save these flags, so we can release the lock */ 00463 escapecommas = ast_test_flag(query, OPT_ESCAPECOMMAS); 00464 if (!bogus_chan && ast_test_flag(query, OPT_MULTIROW)) { 00465 if (!(resultset = ast_calloc(1, sizeof(*resultset)))) { 00466 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount); 00467 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status); 00468 ast_autoservice_stop(chan); 00469 return -1; 00470 } 00471 AST_LIST_HEAD_INIT(resultset); 00472 if (query->rowlimit) { 00473 rowlimit = query->rowlimit; 00474 } else { 00475 rowlimit = INT_MAX; 00476 } 00477 multirow = 1; 00478 } else if (!bogus_chan) { 00479 if (query->rowlimit > 1) { 00480 rowlimit = query->rowlimit; 00481 if (!(resultset = ast_calloc(1, sizeof(*resultset)))) { 00482 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount); 00483 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status); 00484 ast_autoservice_stop(chan); 00485 return -1; 00486 } 00487 AST_LIST_HEAD_INIT(resultset); 00488 } 00489 } 00490 AST_RWLIST_UNLOCK(&queries); 00491 00492 for (dsn = 0; dsn < 5; dsn++) { 00493 if (!ast_strlen_zero(query->readhandle[dsn])) { 00494 obj = ast_odbc_request_obj(query->readhandle[dsn], 0); 00495 if (obj) { 00496 stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)); 00497 } 00498 } 00499 if (stmt) { 00500 break; 00501 } 00502 ast_odbc_release_obj(obj); 00503 obj = NULL; 00504 } 00505 00506 if (!stmt) { 00507 ast_log(LOG_ERROR, "Unable to execute query [%s]\n", ast_str_buffer(sql)); 00508 if (obj) { 00509 ast_odbc_release_obj(obj); 00510 obj = NULL; 00511 } 00512 if (!bogus_chan) { 00513 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount); 00514 ast_autoservice_stop(chan); 00515 } 00516 return -1; 00517 } 00518 00519 res = SQLNumResultCols(stmt, &colcount); 00520 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00521 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", ast_str_buffer(sql)); 00522 SQLCloseCursor(stmt); 00523 SQLFreeHandle (SQL_HANDLE_STMT, stmt); 00524 ast_odbc_release_obj(obj); 00525 obj = NULL; 00526 if (!bogus_chan) { 00527 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount); 00528 ast_autoservice_stop(chan); 00529 } 00530 return -1; 00531 } 00532 00533 res = SQLFetch(stmt); 00534 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00535 int res1 = -1; 00536 if (res == SQL_NO_DATA) { 00537 ast_verb(4, "Found no rows [%s]\n", ast_str_buffer(sql)); 00538 res1 = 0; 00539 buf[0] = '\0'; 00540 ast_copy_string(rowcount, "0", sizeof(rowcount)); 00541 status = "NODATA"; 00542 } else { 00543 ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql)); 00544 status = "FETCHERROR"; 00545 } 00546 SQLCloseCursor(stmt); 00547 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 00548 ast_odbc_release_obj(obj); 00549 obj = NULL; 00550 if (!bogus_chan) { 00551 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount); 00552 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status); 00553 ast_autoservice_stop(chan); 00554 } 00555 return res1; 00556 } 00557 00558 status = "SUCCESS"; 00559 00560 for (y = 0; y < rowlimit; y++) { 00561 buf[0] = '\0'; 00562 for (x = 0; x < colcount; x++) { 00563 int i; 00564 struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16); 00565 char *ptrcoldata; 00566 00567 if (y == 0) { 00568 char colname[256]; 00569 SQLULEN maxcol; 00570 00571 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL); 00572 ast_debug(3, "Got collength of %d and maxcol of %d for column '%s' (offset %d)\n", (int)collength, (int)maxcol, colname, x); 00573 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) { 00574 snprintf(colname, sizeof(colname), "field%d", x); 00575 } 00576 00577 ast_str_make_space(&coldata, maxcol + 1); 00578 00579 if (ast_str_strlen(colnames)) { 00580 ast_str_append(&colnames, 0, ","); 00581 } 00582 ast_str_append_escapecommas(&colnames, 0, colname, sizeof(colname)); 00583 00584 if (resultset) { 00585 void *tmp = ast_realloc(resultset, sizeof(*resultset) + ast_str_strlen(colnames) + 1); 00586 if (!tmp) { 00587 ast_log(LOG_ERROR, "No space for a new resultset?\n"); 00588 ast_free(resultset); 00589 SQLCloseCursor(stmt); 00590 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 00591 ast_odbc_release_obj(obj); 00592 obj = NULL; 00593 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount); 00594 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR"); 00595 ast_autoservice_stop(chan); 00596 return -1; 00597 } 00598 resultset = tmp; 00599 strcpy((char *)resultset + sizeof(*resultset), ast_str_buffer(colnames)); 00600 } 00601 } 00602 00603 buflen = strlen(buf); 00604 res = ast_odbc_ast_str_SQLGetData(&coldata, -1, stmt, x + 1, SQL_CHAR, &indicator); 00605 if (indicator == SQL_NULL_DATA) { 00606 ast_debug(3, "Got NULL data\n"); 00607 ast_str_reset(coldata); 00608 res = SQL_SUCCESS; 00609 } 00610 00611 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00612 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", ast_str_buffer(sql)); 00613 y = -1; 00614 buf[0] = '\0'; 00615 goto end_acf_read; 00616 } 00617 00618 ast_debug(2, "Got coldata of '%s'\n", ast_str_buffer(coldata)); 00619 00620 if (x) { 00621 buf[buflen++] = ','; 00622 } 00623 00624 /* Copy data, encoding '\' and ',' for the argument parser */ 00625 ptrcoldata = ast_str_buffer(coldata); 00626 for (i = 0; i < ast_str_strlen(coldata); i++) { 00627 if (escapecommas && (ptrcoldata[i] == '\\' || ptrcoldata[i] == ',')) { 00628 buf[buflen++] = '\\'; 00629 } 00630 buf[buflen++] = ptrcoldata[i]; 00631 00632 if (buflen >= len - 2) { 00633 break; 00634 } 00635 00636 if (ptrcoldata[i] == '\0') { 00637 break; 00638 } 00639 } 00640 00641 buf[buflen] = '\0'; 00642 ast_debug(2, "buf is now set to '%s'\n", buf); 00643 } 00644 ast_debug(2, "buf is now set to '%s'\n", buf); 00645 00646 if (resultset) { 00647 row = ast_calloc(1, sizeof(*row) + buflen + 1); 00648 if (!row) { 00649 ast_log(LOG_ERROR, "Unable to allocate space for more rows in this resultset.\n"); 00650 status = "MEMERROR"; 00651 goto end_acf_read; 00652 } 00653 strcpy((char *)row + sizeof(*row), buf); 00654 AST_LIST_INSERT_TAIL(resultset, row, list); 00655 00656 /* Get next row */ 00657 res = SQLFetch(stmt); 00658 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00659 if (res != SQL_NO_DATA) { 00660 ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql)); 00661 } 00662 /* Number of rows in the resultset */ 00663 y++; 00664 break; 00665 } 00666 } 00667 } 00668 00669 end_acf_read: 00670 if (!bogus_chan) { 00671 snprintf(rowcount, sizeof(rowcount), "%d", y); 00672 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount); 00673 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status); 00674 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(colnames)); 00675 if (resultset) { 00676 int uid; 00677 struct ast_datastore *odbc_store; 00678 if (multirow) { 00679 uid = ast_atomic_fetchadd_int(&resultcount, +1) + 1; 00680 snprintf(buf, len, "%d", uid); 00681 } else { 00682 /* Name of the query is name of the resultset */ 00683 ast_copy_string(buf, cmd, len); 00684 00685 /* If there's one with the same name already, free it */ 00686 ast_channel_lock(chan); 00687 if ((odbc_store = ast_channel_datastore_find(chan, &odbc_info, buf))) { 00688 ast_channel_datastore_remove(chan, odbc_store); 00689 odbc_datastore_free(odbc_store->data); 00690 ast_free(odbc_store); 00691 } 00692 ast_channel_unlock(chan); 00693 } 00694 odbc_store = ast_datastore_alloc(&odbc_info, buf); 00695 if (!odbc_store) { 00696 ast_log(LOG_ERROR, "Rows retrieved, but unable to store it in the channel. Results fail.\n"); 00697 odbc_datastore_free(resultset); 00698 SQLCloseCursor(stmt); 00699 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 00700 ast_odbc_release_obj(obj); 00701 obj = NULL; 00702 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR"); 00703 ast_autoservice_stop(chan); 00704 return -1; 00705 } 00706 odbc_store->data = resultset; 00707 ast_channel_datastore_add(chan, odbc_store); 00708 } 00709 } 00710 SQLCloseCursor(stmt); 00711 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 00712 ast_odbc_release_obj(obj); 00713 obj = NULL; 00714 if (resultset && !multirow) { 00715 /* Fetch the first resultset */ 00716 if (!acf_fetch(chan, "", buf, buf, len)) { 00717 buf[0] = '\0'; 00718 } 00719 } 00720 if (!bogus_chan) { 00721 ast_autoservice_stop(chan); 00722 } 00723 return 0; 00724 }
| static int acf_odbc_write | ( | struct ast_channel * | chan, | |
| const char * | cmd, | |||
| char * | s, | |||
| const char * | value | |||
| ) | [static] |
Definition at line 205 of file func_odbc.c.
References acf_odbc_query::acf, AST_APP_ARG, ast_autoservice_start(), ast_autoservice_stop(), ast_channel_release(), AST_DECLARE_APP_ARGS, ast_dummy_channel_alloc(), ast_log(), ast_odbc_direct_execute(), ast_odbc_release_obj(), ast_odbc_request_obj(), ast_odbc_retrieve_transaction_obj(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, AST_STANDARD_APP_ARGS, ast_str_buffer(), ast_str_make_space(), ast_str_strlen(), ast_str_substitute_variables(), ast_str_thread_get(), ast_strdupa, ast_strlen_zero(), buf, dsn, generic_execute(), acf_odbc_query::list, LOG_ERROR, LOG_WARNING, ast_custom_function::name, pbx_builtin_pushvar_helper(), pbx_builtin_setvar_helper(), sql2_buf, sql_buf, acf_odbc_query::sql_insert, acf_odbc_query::sql_write, status, and acf_odbc_query::writehandle.
Referenced by init_acf_query().
00206 { 00207 struct odbc_obj *obj = NULL; 00208 struct acf_odbc_query *query; 00209 char *t, varname[15]; 00210 int i, dsn, bogus_chan = 0; 00211 int transactional = 0; 00212 AST_DECLARE_APP_ARGS(values, 00213 AST_APP_ARG(field)[100]; 00214 ); 00215 AST_DECLARE_APP_ARGS(args, 00216 AST_APP_ARG(field)[100]; 00217 ); 00218 SQLHSTMT stmt = NULL; 00219 SQLLEN rows=0; 00220 struct ast_str *buf = ast_str_thread_get(&sql_buf, 16); 00221 struct ast_str *insertbuf = ast_str_thread_get(&sql2_buf, 16); 00222 const char *status = "FAILURE"; 00223 00224 if (!buf) { 00225 return -1; 00226 } 00227 00228 AST_RWLIST_RDLOCK(&queries); 00229 AST_RWLIST_TRAVERSE(&queries, query, list) { 00230 if (!strcmp(query->acf->name, cmd)) { 00231 break; 00232 } 00233 } 00234 00235 if (!query) { 00236 ast_log(LOG_ERROR, "No such function '%s'\n", cmd); 00237 AST_RWLIST_UNLOCK(&queries); 00238 if (chan) { 00239 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status); 00240 } 00241 return -1; 00242 } 00243 00244 if (!chan) { 00245 if (!(chan = ast_dummy_channel_alloc())) { 00246 AST_RWLIST_UNLOCK(&queries); 00247 return -1; 00248 } 00249 bogus_chan = 1; 00250 } 00251 00252 if (!bogus_chan) { 00253 ast_autoservice_start(chan); 00254 } 00255 00256 ast_str_make_space(&buf, strlen(query->sql_write) * 2 + 300); 00257 ast_str_make_space(&insertbuf, strlen(query->sql_insert) * 2 + 300); 00258 00259 /* Parse our arguments */ 00260 t = value ? ast_strdupa(value) : ""; 00261 00262 if (!s || !t) { 00263 ast_log(LOG_ERROR, "Out of memory\n"); 00264 AST_RWLIST_UNLOCK(&queries); 00265 if (!bogus_chan) { 00266 ast_autoservice_stop(chan); 00267 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status); 00268 } else { 00269 ast_channel_release(chan); 00270 } 00271 return -1; 00272 } 00273 00274 AST_STANDARD_APP_ARGS(args, s); 00275 for (i = 0; i < args.argc; i++) { 00276 snprintf(varname, sizeof(varname), "ARG%d", i + 1); 00277 pbx_builtin_pushvar_helper(chan, varname, args.field[i]); 00278 } 00279 00280 /* Parse values, just like arguments */ 00281 AST_STANDARD_APP_ARGS(values, t); 00282 for (i = 0; i < values.argc; i++) { 00283 snprintf(varname, sizeof(varname), "VAL%d", i + 1); 00284 pbx_builtin_pushvar_helper(chan, varname, values.field[i]); 00285 } 00286 00287 /* Additionally set the value as a whole (but push an empty string if value is NULL) */ 00288 pbx_builtin_pushvar_helper(chan, "VALUE", value ? value : ""); 00289 00290 ast_str_substitute_variables(&buf, 0, chan, query->sql_write); 00291 ast_str_substitute_variables(&insertbuf, 0, chan, query->sql_insert); 00292 00293 if (bogus_chan) { 00294 chan = ast_channel_release(chan); 00295 } else { 00296 /* Restore prior values */ 00297 for (i = 0; i < args.argc; i++) { 00298 snprintf(varname, sizeof(varname), "ARG%d", i + 1); 00299 pbx_builtin_setvar_helper(chan, varname, NULL); 00300 } 00301 00302 for (i = 0; i < values.argc; i++) { 00303 snprintf(varname, sizeof(varname), "VAL%d", i + 1); 00304 pbx_builtin_setvar_helper(chan, varname, NULL); 00305 } 00306 pbx_builtin_setvar_helper(chan, "VALUE", NULL); 00307 00308 /*!\note 00309 * Okay, this part is confusing. Transactions belong to a single database 00310 * handle. Therefore, when working with transactions, we CANNOT failover 00311 * to multiple DSNs. We MUST have a single handle all the way through the 00312 * transaction, or else we CANNOT enforce atomicity. 00313 */ 00314 for (dsn = 0; dsn < 5; dsn++) { 00315 if (transactional) { 00316 /* This can only happen second time through or greater. */ 00317 ast_log(LOG_WARNING, "Transactions do not work well with multiple DSNs for 'writehandle'\n"); 00318 } 00319 00320 if (!ast_strlen_zero(query->writehandle[dsn])) { 00321 if ((obj = ast_odbc_retrieve_transaction_obj(chan, query->writehandle[dsn]))) { 00322 transactional = 1; 00323 } else { 00324 obj = ast_odbc_request_obj(query->writehandle[dsn], 0); 00325 transactional = 0; 00326 } 00327 if (obj && (stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(buf)))) { 00328 break; 00329 } 00330 } 00331 00332 if (obj && !transactional) { 00333 ast_odbc_release_obj(obj); 00334 } 00335 } 00336 } 00337 00338 if (stmt && rows == 0 && ast_str_strlen(insertbuf) != 0) { 00339 SQLCloseCursor(stmt); 00340 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 00341 for (dsn = 0; dsn < 5; dsn++) { 00342 if (!ast_strlen_zero(query->writehandle[dsn])) { 00343 obj = ast_odbc_request_obj(query->writehandle[dsn], 0); 00344 if (obj) { 00345 stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(insertbuf)); 00346 } 00347 } 00348 if (stmt) { 00349 status = "FAILOVER"; 00350 SQLRowCount(stmt, &rows); 00351 break; 00352 } 00353 } 00354 } else if (stmt) { 00355 status = "SUCCESS"; 00356 SQLRowCount(stmt, &rows); 00357 } 00358 00359 AST_RWLIST_UNLOCK(&queries); 00360 00361 /* Output the affected rows, for all cases. In the event of failure, we 00362 * flag this as -1 rows. Note that this is different from 0 affected rows 00363 * which would be the case if we succeeded in our query, but the values did 00364 * not change. */ 00365 if (!bogus_chan) { 00366 snprintf(varname, sizeof(varname), "%d", (int)rows); 00367 pbx_builtin_setvar_helper(chan, "ODBCROWS", varname); 00368 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status); 00369 } 00370 00371 if (stmt) { 00372 SQLCloseCursor(stmt); 00373 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 00374 } 00375 if (obj && !transactional) { 00376 ast_odbc_release_obj(obj); 00377 obj = NULL; 00378 } 00379 00380 if (!bogus_chan) { 00381 ast_autoservice_stop(chan); 00382 } 00383 00384 return 0; 00385 }
| static char* cli_odbc_read | ( | struct ast_cli_entry * | e, | |
| int | cmd, | |||
| struct ast_cli_args * | a | |||
| ) | [static] |
Definition at line 1027 of file func_odbc.c.
References acf_odbc_query::acf, ast_cli_args::argc, ast_cli_args::argv, AST_APP_ARG, ast_channel_release(), ast_cli(), ast_debug, AST_DECLARE_APP_ARGS, ast_dummy_channel_alloc(), ast_odbc_ast_str_SQLGetData(), ast_odbc_direct_execute(), ast_odbc_release_obj(), ast_odbc_request_obj(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, AST_STANDARD_APP_ARGS, ast_str_buffer(), ast_str_make_space(), ast_str_set(), ast_str_substitute_variables(), ast_str_thread_get(), ast_strdup, ast_strdupa, ast_strlen_zero(), chan, CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, coldata_buf, ast_cli_entry::command, dsn, ast_cli_args::fd, generic_execute(), acf_odbc_query::list, ast_cli_args::n, ast_custom_function::name, pbx_builtin_pushvar_helper(), ast_cli_args::pos, acf_odbc_query::readhandle, sql_buf, acf_odbc_query::sql_read, ast_cli_entry::usage, and ast_cli_args::word.
01028 { 01029 AST_DECLARE_APP_ARGS(args, 01030 AST_APP_ARG(field)[100]; 01031 ); 01032 struct ast_str *sql; 01033 char *char_args, varname[10]; 01034 struct acf_odbc_query *query; 01035 struct ast_channel *chan; 01036 int i; 01037 01038 switch (cmd) { 01039 case CLI_INIT: 01040 e->command = "odbc read"; 01041 e->usage = 01042 "Usage: odbc read <name> <args> [exec]\n" 01043 " Evaluates the SQL provided in the ODBC function <name>, and\n" 01044 " optionally executes the function. This function is intended for\n" 01045 " testing purposes. Remember to quote arguments containing spaces.\n"; 01046 return NULL; 01047 case CLI_GENERATE: 01048 if (a->pos == 2) { 01049 int wordlen = strlen(a->word), which = 0; 01050 /* Complete function name */ 01051 AST_RWLIST_RDLOCK(&queries); 01052 AST_RWLIST_TRAVERSE(&queries, query, list) { 01053 if (!strncasecmp(query->acf->name, a->word, wordlen)) { 01054 if (++which > a->n) { 01055 char *res = ast_strdup(query->acf->name); 01056 AST_RWLIST_UNLOCK(&queries); 01057 return res; 01058 } 01059 } 01060 } 01061 AST_RWLIST_UNLOCK(&queries); 01062 return NULL; 01063 } else if (a->pos == 4) { 01064 return a->n == 0 ? ast_strdup("exec") : NULL; 01065 } else { 01066 return NULL; 01067 } 01068 } 01069 01070 if (a->argc < 4 || a->argc > 5) { 01071 return CLI_SHOWUSAGE; 01072 } 01073 01074 sql = ast_str_thread_get(&sql_buf, 16); 01075 if (!sql) { 01076 return CLI_FAILURE; 01077 } 01078 01079 AST_RWLIST_RDLOCK(&queries); 01080 AST_RWLIST_TRAVERSE(&queries, query, list) { 01081 if (!strcmp(query->acf->name, a->argv[2])) { 01082 break; 01083 } 01084 } 01085 01086 if (!query) { 01087 ast_cli(a->fd, "No such query '%s'\n", a->argv[2]); 01088 AST_RWLIST_UNLOCK(&queries); 01089 return CLI_SHOWUSAGE; 01090 } 01091 01092 if (ast_strlen_zero(query->sql_read)) { 01093 ast_cli(a->fd, "The function %s has no writesql parameter.\n", a->argv[2]); 01094 AST_RWLIST_UNLOCK(&queries); 01095 return CLI_SUCCESS; 01096 } 01097 01098 ast_str_make_space(&sql, strlen(query->sql_read) * 2 + 300); 01099 01100 /* Evaluate function */ 01101 char_args = ast_strdupa(a->argv[3]); 01102 01103 chan = ast_dummy_channel_alloc(); 01104 01105 AST_STANDARD_APP_ARGS(args, char_args); 01106 for (i = 0; i < args.argc; i++) { 01107 snprintf(varname, sizeof(varname), "ARG%d", i + 1); 01108 pbx_builtin_pushvar_helper(chan, varname, args.field[i]); 01109 } 01110 01111 ast_str_substitute_variables(&sql, 0, chan, query->sql_read); 01112 chan = ast_channel_release(chan); 01113 01114 if (a->argc == 5 && !strcmp(a->argv[4], "exec")) { 01115 /* Execute the query */ 01116 struct odbc_obj *obj = NULL; 01117 int dsn, executed = 0; 01118 SQLHSTMT stmt; 01119 int rows = 0, res, x; 01120 SQLSMALLINT colcount = 0, collength; 01121 SQLLEN indicator; 01122 struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16); 01123 char colname[256]; 01124 SQLULEN maxcol; 01125 01126 for (dsn = 0; dsn < 5; dsn++) { 01127 if (ast_strlen_zero(query->readhandle[dsn])) { 01128 continue; 01129 } 01130 ast_debug(1, "Found handle %s\n", query->readhandle[dsn]); 01131 if (!(obj = ast_odbc_request_obj(query->readhandle[dsn], 0))) { 01132 continue; 01133 } 01134 01135 ast_debug(1, "Got obj\n"); 01136 if (!(stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)))) { 01137 ast_odbc_release_obj(obj); 01138 obj = NULL; 01139 continue; 01140 } 01141 01142 executed = 1; 01143 01144 res = SQLNumResultCols(stmt, &colcount); 01145 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 01146 ast_cli(a->fd, "SQL Column Count error!\n[%s]\n\n", ast_str_buffer(sql)); 01147 SQLCloseCursor(stmt); 01148 SQLFreeHandle (SQL_HANDLE_STMT, stmt); 01149 ast_odbc_release_obj(obj); 01150 obj = NULL; 01151 AST_RWLIST_UNLOCK(&queries); 01152 return CLI_SUCCESS; 01153 } 01154 01155 res = SQLFetch(stmt); 01156 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 01157 SQLCloseCursor(stmt); 01158 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 01159 ast_odbc_release_obj(obj); 01160 obj = NULL; 01161 if (res == SQL_NO_DATA) { 01162 ast_cli(a->fd, "Returned %d rows. Query executed on handle %d:%s [%s]\n", rows, dsn, query->readhandle[dsn], ast_str_buffer(sql)); 01163 break; 01164 } else { 01165 ast_cli(a->fd, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql)); 01166 } 01167 AST_RWLIST_UNLOCK(&queries); 01168 return CLI_SUCCESS; 01169 } 01170 for (;;) { 01171 for (x = 0; x < colcount; x++) { 01172 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL); 01173 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) { 01174 snprintf(colname, sizeof(colname), "field%d", x); 01175 } 01176 01177 res = ast_odbc_ast_str_SQLGetData(&coldata, maxcol, stmt, x + 1, SQL_CHAR, &indicator); 01178 if (indicator == SQL_NULL_DATA) { 01179 ast_str_set(&coldata, 0, "(nil)"); 01180 res = SQL_SUCCESS; 01181 } 01182 01183 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 01184 ast_cli(a->fd, "SQL Get Data error %d!\n[%s]\n\n", res, ast_str_buffer(sql)); 01185 SQLCloseCursor(stmt); 01186 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 01187 ast_odbc_release_obj(obj); 01188 obj = NULL; 01189 AST_RWLIST_UNLOCK(&queries); 01190 return CLI_SUCCESS; 01191 } 01192 01193 ast_cli(a->fd, "%-20.20s %s\n", colname, ast_str_buffer(coldata)); 01194 } 01195 rows++; 01196 01197 /* Get next row */ 01198 res = SQLFetch(stmt); 01199 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 01200 break; 01201 } 01202 ast_cli(a->fd, "%-20.20s %s\n", "----------", "----------"); 01203 } 01204 SQLCloseCursor(stmt); 01205 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 01206 ast_odbc_release_obj(obj); 01207 obj = NULL; 01208 ast_cli(a->fd, "Returned %d row%s. Query executed on handle %d [%s]\n", rows, rows == 1 ? "" : "s", dsn, query->readhandle[dsn]); 01209 break; 01210 } 01211 if (obj) { 01212 ast_odbc_release_obj(obj); 01213 obj = NULL; 01214 } 01215 01216 if (!executed) { 01217 ast_cli(a->fd, "Failed to execute query. [%s]\n", ast_str_buffer(sql)); 01218 } 01219 } else { /* No execution, just print out the resulting SQL */ 01220 ast_cli(a->fd, "%s\n", ast_str_buffer(sql)); 01221 } 01222 AST_RWLIST_UNLOCK(&queries); 01223 return CLI_SUCCESS; 01224 }
| static char* cli_odbc_write | ( | struct ast_cli_entry * | e, | |
| int | cmd, | |||
| struct ast_cli_args * | a | |||
| ) | [static] |
Definition at line 1226 of file func_odbc.c.
References acf_odbc_query::acf, ast_cli_args::argc, ast_cli_args::argv, AST_APP_ARG, ast_channel_release(), ast_cli(), ast_debug, AST_DECLARE_APP_ARGS, ast_dummy_channel_alloc(), ast_odbc_direct_execute(), ast_odbc_release_obj(), ast_odbc_request_obj(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, AST_STANDARD_APP_ARGS, ast_str_buffer(), ast_str_make_space(), ast_str_substitute_variables(), ast_str_thread_get(), ast_strdup, ast_strdupa, ast_strlen_zero(), chan, CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, dsn, ast_cli_args::fd, generic_execute(), acf_odbc_query::list, ast_cli_args::n, ast_custom_function::name, pbx_builtin_pushvar_helper(), ast_cli_args::pos, S_OR, sql_buf, acf_odbc_query::sql_write, ast_cli_entry::usage, ast_cli_args::word, and acf_odbc_query::writehandle.
01227 { 01228 AST_DECLARE_APP_ARGS(values, 01229 AST_APP_ARG(field)[100]; 01230 ); 01231 AST_DECLARE_APP_ARGS(args, 01232 AST_APP_ARG(field)[100]; 01233 ); 01234 struct ast_str *sql; 01235 char *char_args, *char_values, varname[10]; 01236 struct acf_odbc_query *query; 01237 struct ast_channel *chan; 01238 int i; 01239 01240 switch (cmd) { 01241 case CLI_INIT: 01242 e->command = "odbc write"; 01243 e->usage = 01244 "Usage: odbc write <name> <args> <value> [exec]\n" 01245 " Evaluates the SQL provided in the ODBC function <name>, and\n" 01246 " optionally executes the function. This function is intended for\n" 01247 " testing purposes. Remember to quote arguments containing spaces.\n"; 01248 return NULL; 01249 case CLI_GENERATE: 01250 if (a->pos == 2) { 01251 int wordlen = strlen(a->word), which = 0; 01252 /* Complete function name */ 01253 AST_RWLIST_RDLOCK(&queries); 01254 AST_RWLIST_TRAVERSE(&queries, query, list) { 01255 if (!strncasecmp(query->acf->name, a->word, wordlen)) { 01256 if (++which > a->n) { 01257 char *res = ast_strdup(query->acf->name); 01258 AST_RWLIST_UNLOCK(&queries); 01259 return res; 01260 } 01261 } 01262 } 01263 AST_RWLIST_UNLOCK(&queries); 01264 return NULL; 01265 } else if (a->pos == 5) { 01266 return a->n == 0 ? ast_strdup("exec") : NULL; 01267 } else { 01268 return NULL; 01269 } 01270 } 01271 01272 if (a->argc < 5 || a->argc > 6) { 01273 return CLI_SHOWUSAGE; 01274 } 01275 01276 sql = ast_str_thread_get(&sql_buf, 16); 01277 if (!sql) { 01278 return CLI_FAILURE; 01279 } 01280 01281 AST_RWLIST_RDLOCK(&queries); 01282 AST_RWLIST_TRAVERSE(&queries, query, list) { 01283 if (!strcmp(query->acf->name, a->argv[2])) { 01284 break; 01285 } 01286 } 01287 01288 if (!query) { 01289 ast_cli(a->fd, "No such query '%s'\n", a->argv[2]); 01290 AST_RWLIST_UNLOCK(&queries); 01291 return CLI_SHOWUSAGE; 01292 } 01293 01294 if (ast_strlen_zero(query->sql_write)) { 01295 ast_cli(a->fd, "The function %s has no writesql parameter.\n", a->argv[2]); 01296 AST_RWLIST_UNLOCK(&queries); 01297 return CLI_SUCCESS; 01298 } 01299 01300 ast_str_make_space(&sql, strlen(query->sql_write) * 2 + 300); 01301 01302 /* Evaluate function */ 01303 char_args = ast_strdupa(a->argv[3]); 01304 char_values = ast_strdupa(a->argv[4]); 01305 01306 chan = ast_dummy_channel_alloc(); 01307 01308 AST_STANDARD_APP_ARGS(args, char_args); 01309 for (i = 0; i < args.argc; i++) { 01310 snprintf(varname, sizeof(varname), "ARG%d", i + 1); 01311 pbx_builtin_pushvar_helper(chan, varname, args.field[i]); 01312 } 01313 01314 /* Parse values, just like arguments */ 01315 AST_STANDARD_APP_ARGS(values, char_values); 01316 for (i = 0; i < values.argc; i++) { 01317 snprintf(varname, sizeof(varname), "VAL%d", i + 1); 01318 pbx_builtin_pushvar_helper(chan, varname, values.field[i]); 01319 } 01320 01321 /* Additionally set the value as a whole (but push an empty string if value is NULL) */ 01322 pbx_builtin_pushvar_helper(chan, "VALUE", S_OR(a->argv[4], "")); 01323 ast_str_substitute_variables(&sql, 0, chan, query->sql_write); 01324 ast_debug(1, "SQL is %s\n", ast_str_buffer(sql)); 01325 chan = ast_channel_release(chan); 01326 01327 if (a->argc == 6 && !strcmp(a->argv[5], "exec")) { 01328 /* Execute the query */ 01329 struct odbc_obj *obj = NULL; 01330 int dsn, executed = 0; 01331 SQLHSTMT stmt; 01332 SQLLEN rows = -1; 01333 01334 for (dsn = 0; dsn < 5; dsn++) { 01335 if (ast_strlen_zero(query->writehandle[dsn])) { 01336 continue; 01337 } 01338 if (!(obj = ast_odbc_request_obj(query->writehandle[dsn], 0))) { 01339 continue; 01340 } 01341 if (!(stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)))) { 01342 ast_odbc_release_obj(obj); 01343 obj = NULL; 01344 continue; 01345 } 01346 01347 SQLRowCount(stmt, &rows); 01348 SQLCloseCursor(stmt); 01349 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 01350 ast_odbc_release_obj(obj); 01351 obj = NULL; 01352 ast_cli(a->fd, "Affected %d rows. Query executed on handle %d [%s]\n", (int)rows, dsn, query->writehandle[dsn]); 01353 executed = 1; 01354 break; 01355 } 01356 01357 if (!executed) { 01358 ast_cli(a->fd, "Failed to execute query.\n"); 01359 } 01360 } else { /* No execution, just print out the resulting SQL */ 01361 ast_cli(a->fd, "%s\n", ast_str_buffer(sql)); 01362 } 01363 AST_RWLIST_UNLOCK(&queries); 01364 return CLI_SUCCESS; 01365 }
| static int exec_odbcfinish | ( | struct ast_channel * | chan, | |
| const char * | data | |||
| ) | [static] |
Definition at line 784 of file func_odbc.c.
References ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_datastore_free(), and odbc_info.
Referenced by load_module().
00785 { 00786 struct ast_datastore *store = ast_channel_datastore_find(chan, &odbc_info, data); 00787 if (!store) /* Already freed; no big deal. */ 00788 return 0; 00789 ast_channel_datastore_remove(chan, store); 00790 ast_datastore_free(store); 00791 return 0; 00792 }
| static int free_acf_query | ( | struct acf_odbc_query * | query | ) | [static] |
Definition at line 1013 of file func_odbc.c.
References acf_odbc_query::acf, ast_free, ast_string_field_free_memory, and ast_custom_function::name.
Referenced by reload(), and unload_module().
01014 { 01015 if (query) { 01016 if (query->acf) { 01017 if (query->acf->name) 01018 ast_free((char *)query->acf->name); 01019 ast_string_field_free_memory(query->acf); 01020 ast_free(query->acf); 01021 } 01022 ast_free(query); 01023 } 01024 return 0; 01025 }
| static SQLHSTMT generic_execute | ( | struct odbc_obj * | obj, | |
| void * | data | |||
| ) | [static] |
Definition at line 162 of file func_odbc.c.
References ast_log(), odbc_obj::con, and LOG_WARNING.
Referenced by acf_odbc_read(), acf_odbc_write(), cli_odbc_read(), and cli_odbc_write().
00163 { 00164 int res; 00165 char *sql = data; 00166 SQLHSTMT stmt; 00167 00168 res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt); 00169 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00170 ast_log(LOG_WARNING, "SQL Alloc Handle failed (%d)!\n", res); 00171 return NULL; 00172 } 00173 00174 res = SQLExecDirect(stmt, (unsigned char *)sql, SQL_NTS); 00175 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00176 if (res == SQL_ERROR) { 00177 int i; 00178 SQLINTEGER nativeerror=0, numfields=0; 00179 SQLSMALLINT diagbytes=0; 00180 unsigned char state[10], diagnostic[256]; 00181 00182 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 00183 for (i = 0; i < numfields; i++) { 00184 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 00185 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes); 00186 if (i > 10) { 00187 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 00188 break; 00189 } 00190 } 00191 } 00192 00193 ast_log(LOG_WARNING, "SQL Exec Direct failed (%d)![%s]\n", res, sql); 00194 SQLCloseCursor(stmt); 00195 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 00196 return NULL; 00197 } 00198 00199 return stmt; 00200 }
| static int init_acf_query | ( | struct ast_config * | cfg, | |
| char * | catg, | |||
| struct acf_odbc_query ** | query | |||
| ) | [static] |
Definition at line 794 of file func_odbc.c.
References acf_odbc_query::acf, acf_odbc_read(), acf_odbc_write(), asprintf, AST_APP_ARG, ast_calloc, ast_clear_flag, ast_copy_string(), AST_DECLARE_APP_ARGS, ast_false(), ast_free, ast_log(), ast_set_flag, AST_STANDARD_APP_ARGS, ast_strdupa, ast_string_field_build, ast_string_field_free_memory, ast_string_field_init, ast_string_field_set, ast_strlen_zero(), ast_variable_retrieve(), desc, dsn, errno, LOG_ERROR, LOG_WARNING, OPT_ESCAPECOMMAS, OPT_MULTIROW, ast_custom_function::read, and synopsis.
Referenced by load_module(), and reload().
00795 { 00796 const char *tmp; 00797 int i; 00798 00799 if (!cfg || !catg) { 00800 return EINVAL; 00801 } 00802 00803 *query = ast_calloc(1, sizeof(struct acf_odbc_query)); 00804 if (! (*query)) 00805 return ENOMEM; 00806 00807 if (((tmp = ast_variable_retrieve(cfg, catg, "writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg, "dsn")))) { 00808 char *tmp2 = ast_strdupa(tmp); 00809 AST_DECLARE_APP_ARGS(writeconf, 00810 AST_APP_ARG(dsn)[5]; 00811 ); 00812 AST_STANDARD_APP_ARGS(writeconf, tmp2); 00813 for (i = 0; i < 5; i++) { 00814 if (!ast_strlen_zero(writeconf.dsn[i])) 00815 ast_copy_string((*query)->writehandle[i], writeconf.dsn[i], sizeof((*query)->writehandle[i])); 00816 } 00817 } 00818 00819 if ((tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) { 00820 char *tmp2 = ast_strdupa(tmp); 00821 AST_DECLARE_APP_ARGS(readconf, 00822 AST_APP_ARG(dsn)[5]; 00823 ); 00824 AST_STANDARD_APP_ARGS(readconf, tmp2); 00825 for (i = 0; i < 5; i++) { 00826 if (!ast_strlen_zero(readconf.dsn[i])) 00827 ast_copy_string((*query)->readhandle[i], readconf.dsn[i], sizeof((*query)->readhandle[i])); 00828 } 00829 } else { 00830 /* If no separate readhandle, then use the writehandle for reading */ 00831 for (i = 0; i < 5; i++) { 00832 if (!ast_strlen_zero((*query)->writehandle[i])) 00833 ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i], sizeof((*query)->readhandle[i])); 00834 } 00835 } 00836 00837 if ((tmp = ast_variable_retrieve(cfg, catg, "readsql"))) 00838 ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read)); 00839 else if ((tmp = ast_variable_retrieve(cfg, catg, "read"))) { 00840 ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s. Please use 'readsql' instead.\n", catg); 00841 ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read)); 00842 } 00843 00844 if (!ast_strlen_zero((*query)->sql_read) && ast_strlen_zero((*query)->readhandle[0])) { 00845 ast_free(*query); 00846 *query = NULL; 00847 ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg); 00848 return EINVAL; 00849 } 00850 00851 if ((tmp = ast_variable_retrieve(cfg, catg, "writesql"))) 00852 ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write)); 00853 else if ((tmp = ast_variable_retrieve(cfg, catg, "write"))) { 00854 ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s. Please use 'writesql' instead.\n", catg); 00855 ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write)); 00856 } 00857 00858 if (!ast_strlen_zero((*query)->sql_write) && ast_strlen_zero((*query)->writehandle[0])) { 00859 ast_free(*query); 00860 *query = NULL; 00861 ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg); 00862 return EINVAL; 00863 } 00864 00865 if ((tmp = ast_variable_retrieve(cfg, catg, "insertsql"))) { 00866 ast_copy_string((*query)->sql_insert, tmp, sizeof((*query)->sql_insert)); 00867 } 00868 00869 /* Allow escaping of embedded commas in fields to be turned off */ 00870 ast_set_flag((*query), OPT_ESCAPECOMMAS); 00871 if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) { 00872 if (ast_false(tmp)) 00873 ast_clear_flag((*query), OPT_ESCAPECOMMAS); 00874 } 00875 00876 if ((tmp = ast_variable_retrieve(cfg, catg, "mode"))) { 00877 if (strcasecmp(tmp, "multirow") == 0) 00878 ast_set_flag((*query), OPT_MULTIROW); 00879 if ((tmp = ast_variable_retrieve(cfg, catg, "rowlimit"))) 00880 sscanf(tmp, "%30d", &((*query)->rowlimit)); 00881 } 00882 00883 (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function)); 00884 if (! (*query)->acf) { 00885 ast_free(*query); 00886 *query = NULL; 00887 return ENOMEM; 00888 } 00889 if (ast_string_field_init((*query)->acf, 128)) { 00890 ast_free((*query)->acf); 00891 ast_free(*query); 00892 *query = NULL; 00893 return ENOMEM; 00894 } 00895 00896 if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) { 00897 if (asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg) < 0) { 00898 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno)); 00899 } 00900 } else { 00901 if (asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg) < 0) { 00902 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno)); 00903 } 00904 } 00905 00906 if (!((*query)->acf->name)) { 00907 ast_string_field_free_memory((*query)->acf); 00908 ast_free((*query)->acf); 00909 ast_free(*query); 00910 *query = NULL; 00911 return ENOMEM; 00912 } 00913 00914 if ((tmp = ast_variable_retrieve(cfg, catg, "syntax")) && !ast_strlen_zero(tmp)) { 00915 ast_string_field_build((*query)->acf, syntax, "%s(%s)", (*query)->acf->name, tmp); 00916 } else { 00917 ast_string_field_build((*query)->acf, syntax, "%s(<arg1>[...[,<argN>]])", (*query)->acf->name); 00918 } 00919 00920 if (ast_strlen_zero((*query)->acf->syntax)) { 00921 ast_free((char *)(*query)->acf->name); 00922 ast_string_field_free_memory((*query)->acf); 00923 ast_free((*query)->acf); 00924 ast_free(*query); 00925 *query = NULL; 00926 return ENOMEM; 00927 } 00928 00929 if ((tmp = ast_variable_retrieve(cfg, catg, "synopsis")) && !ast_strlen_zero(tmp)) { 00930 ast_string_field_set((*query)->acf, synopsis, tmp); 00931 } else { 00932 ast_string_field_set((*query)->acf, synopsis, "Runs the referenced query with the specified arguments"); 00933 } 00934 00935 if (ast_strlen_zero((*query)->acf->synopsis)) { 00936 ast_free((char *)(*query)->acf->name); 00937 ast_string_field_free_memory((*query)->acf); 00938 ast_free((*query)->acf); 00939 ast_free(*query); 00940 *query = NULL; 00941 return ENOMEM; 00942 } 00943 00944 if (!ast_strlen_zero((*query)->sql_read) && !ast_strlen_zero((*query)->sql_write)) { 00945 ast_string_field_build((*query)->acf, desc, 00946 "Runs the following query, as defined in func_odbc.conf, performing\n" 00947 "substitution of the arguments into the query as specified by ${ARG1},\n" 00948 "${ARG2}, ... ${ARGn}. When setting the function, the values are provided\n" 00949 "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n" 00950 "%s" 00951 "\nRead:\n%s\n\nWrite:\n%s\n%s%s%s", 00952 ast_strlen_zero((*query)->sql_insert) ? "" : 00953 "If the write query affects no rows, the insert query will be\n" 00954 "performed.\n", 00955 (*query)->sql_read, 00956 (*query)->sql_write, 00957 ast_strlen_zero((*query)->sql_insert) ? "" : "Insert:\n", 00958 ast_strlen_zero((*query)->sql_insert) ? "" : (*query)->sql_insert, 00959 ast_strlen_zero((*query)->sql_insert) ? "" : "\n"); 00960 } else if (!ast_strlen_zero((*query)->sql_read)) { 00961 ast_string_field_build((*query)->acf, desc, 00962 "Runs the following query, as defined in func_odbc.conf, performing\n" 00963 "substitution of the arguments into the query as specified by ${ARG1},\n" 00964 "${ARG2}, ... ${ARGn}. This function may only be read, not set.\n\nSQL:\n%s\n", 00965 (*query)->sql_read); 00966 } else if (!ast_strlen_zero((*query)->sql_write)) { 00967 ast_string_field_build((*query)->acf, desc, 00968 "Runs the following query, as defined in func_odbc.conf, performing\n" 00969 "substitution of the arguments into the query as specified by ${ARG1},\n" 00970 "${ARG2}, ... ${ARGn}. The values are provided either in whole as\n" 00971 "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n" 00972 "This function may only be set.\n%sSQL:\n%s\n%s%s%s", 00973 ast_strlen_zero((*query)->sql_insert) ? "" : 00974 "If the write query affects no rows, the insert query will be\n" 00975 "performed.\n", 00976 (*query)->sql_write, 00977 ast_strlen_zero((*query)->sql_insert) ? "" : "Insert:\n", 00978 ast_strlen_zero((*query)->sql_insert) ? "" : (*query)->sql_insert, 00979 ast_strlen_zero((*query)->sql_insert) ? "" : "\n"); 00980 } else { 00981 ast_string_field_free_memory((*query)->acf); 00982 ast_free((char *)(*query)->acf->name); 00983 ast_free((*query)->acf); 00984 ast_free(*query); 00985 ast_log(LOG_WARNING, "Section '%s' was found, but there was no SQL to execute. Ignoring.\n", catg); 00986 return EINVAL; 00987 } 00988 00989 if (ast_strlen_zero((*query)->acf->desc)) { 00990 ast_string_field_free_memory((*query)->acf); 00991 ast_free((char *)(*query)->acf->name); 00992 ast_free((*query)->acf); 00993 ast_free(*query); 00994 *query = NULL; 00995 return ENOMEM; 00996 } 00997 00998 if (ast_strlen_zero((*query)->sql_read)) { 00999 (*query)->acf->read = NULL; 01000 } else { 01001 (*query)->acf->read = acf_odbc_read; 01002 } 01003 01004 if (ast_strlen_zero((*query)->sql_write)) { 01005 (*query)->acf->write = NULL; 01006 } else { 01007 (*query)->acf->write = acf_odbc_write; 01008 } 01009 01010 return 0; 01011 }
| static int load_module | ( | void | ) | [static] |
Definition at line 1372 of file func_odbc.c.
References acf_odbc_query::acf, app_odbcfinish, ARRAY_LEN, ast_category_browse(), ast_cli_register_multiple(), ast_config_destroy(), ast_config_load, ast_custom_function_register, ast_log(), AST_MODULE_LOAD_DECLINE, ast_register_application_xml, AST_RWLIST_INSERT_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, cli_func_odbc, CONFIG_STATUS_FILEINVALID, escape_function, exec_odbcfinish(), fetch_function, init_acf_query(), acf_odbc_query::list, LOG_ERROR, and LOG_NOTICE.
01373 { 01374 int res = 0; 01375 struct ast_config *cfg; 01376 char *catg; 01377 struct ast_flags config_flags = { 0 }; 01378 01379 res |= ast_custom_function_register(&fetch_function); 01380 res |= ast_register_application_xml(app_odbcfinish, exec_odbcfinish); 01381 AST_RWLIST_WRLOCK(&queries); 01382 01383 cfg = ast_config_load(config, config_flags); 01384 if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) { 01385 ast_log(LOG_NOTICE, "Unable to load config for func_odbc: %s\n", config); 01386 AST_RWLIST_UNLOCK(&queries); 01387 return AST_MODULE_LOAD_DECLINE; 01388 } 01389 01390 for (catg = ast_category_browse(cfg, NULL); 01391 catg; 01392 catg = ast_category_browse(cfg, catg)) { 01393 struct acf_odbc_query *query = NULL; 01394 int err; 01395 01396 if ((err = init_acf_query(cfg, catg, &query))) { 01397 if (err == ENOMEM) 01398 ast_log(LOG_ERROR, "Out of memory\n"); 01399 else if (err == EINVAL) 01400 ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg); 01401 else 01402 ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err); 01403 } else { 01404 AST_RWLIST_INSERT_HEAD(&queries, query, list); 01405 ast_custom_function_register(query->acf); 01406 } 01407 } 01408 01409 ast_config_destroy(cfg); 01410 res |= ast_custom_function_register(&escape_function); 01411 ast_cli_register_multiple(cli_func_odbc, ARRAY_LEN(cli_func_odbc)); 01412 01413 AST_RWLIST_UNLOCK(&queries); 01414 return res; 01415 }
| static void odbc_datastore_free | ( | void * | data | ) | [static] |
Definition at line 149 of file func_odbc.c.
References ast_free, AST_LIST_HEAD_DESTROY, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, and acf_odbc_query::list.
Referenced by acf_odbc_read().
00150 { 00151 struct odbc_datastore *result = data; 00152 struct odbc_datastore_row *row; 00153 AST_LIST_LOCK(result); 00154 while ((row = AST_LIST_REMOVE_HEAD(result, list))) { 00155 ast_free(row); 00156 } 00157 AST_LIST_UNLOCK(result); 00158 AST_LIST_HEAD_DESTROY(result); 00159 ast_free(result); 00160 }
| static int reload | ( | void | ) | [static] |
Definition at line 1443 of file func_odbc.c.
References acf_odbc_query::acf, ast_category_browse(), ast_config_destroy(), ast_config_load, ast_custom_function_register, ast_custom_function_unregister(), ast_log(), AST_RWLIST_EMPTY, AST_RWLIST_INSERT_HEAD, AST_RWLIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, free_acf_query(), init_acf_query(), acf_odbc_query::list, LOG_ERROR, and LOG_WARNING.
01444 { 01445 int res = 0; 01446 struct ast_config *cfg; 01447 struct acf_odbc_query *oldquery; 01448 char *catg; 01449 struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED }; 01450 01451 cfg = ast_config_load(config, config_flags); 01452 if (cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) 01453 return 0; 01454 01455 AST_RWLIST_WRLOCK(&queries); 01456 01457 while (!AST_RWLIST_EMPTY(&queries)) { 01458 oldquery = AST_RWLIST_REMOVE_HEAD(&queries, list); 01459 ast_custom_function_unregister(oldquery->acf); 01460 free_acf_query(oldquery); 01461 } 01462 01463 if (!cfg) { 01464 ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config); 01465 goto reload_out; 01466 } 01467 01468 for (catg = ast_category_browse(cfg, NULL); 01469 catg; 01470 catg = ast_category_browse(cfg, catg)) { 01471 struct acf_odbc_query *query = NULL; 01472 01473 if (init_acf_query(cfg, catg, &query)) { 01474 ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg); 01475 } else { 01476 AST_RWLIST_INSERT_HEAD(&queries, query, list); 01477 ast_custom_function_register(query->acf); 01478 } 01479 } 01480 01481 ast_config_destroy(cfg); 01482 reload_out: 01483 AST_RWLIST_UNLOCK(&queries); 01484 return res; 01485 }
| static int unload_module | ( | void | ) | [static] |
Definition at line 1417 of file func_odbc.c.
References acf_odbc_query::acf, app_odbcfinish, ARRAY_LEN, ast_cli_unregister_multiple(), ast_custom_function_unregister(), AST_RWLIST_EMPTY, AST_RWLIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_unregister_application(), cli_func_odbc, escape_function, fetch_function, free_acf_query(), and acf_odbc_query::list.
01418 { 01419 struct acf_odbc_query *query; 01420 int res = 0; 01421 01422 AST_RWLIST_WRLOCK(&queries); 01423 while (!AST_RWLIST_EMPTY(&queries)) { 01424 query = AST_RWLIST_REMOVE_HEAD(&queries, list); 01425 ast_custom_function_unregister(query->acf); 01426 free_acf_query(query); 01427 } 01428 01429 res |= ast_custom_function_unregister(&escape_function); 01430 res |= ast_custom_function_unregister(&fetch_function); 01431 res |= ast_unregister_application(app_odbcfinish); 01432 ast_cli_unregister_multiple(cli_func_odbc, ARRAY_LEN(cli_func_odbc)); 01433 01434 /* Allow any threads waiting for this lock to pass (avoids a race) */ 01435 AST_RWLIST_UNLOCK(&queries); 01436 usleep(1); 01437 AST_RWLIST_WRLOCK(&queries); 01438 01439 AST_RWLIST_UNLOCK(&queries); 01440 return 0; 01441 }
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "ODBC lookups" , .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 = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, } [static] |
Definition at line 1493 of file func_odbc.c.
char* app_odbcfinish = "ODBCFinish" [static] |
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 1493 of file func_odbc.c.
struct ast_cli_entry cli_func_odbc[] [static] |
Initial value:
{
AST_CLI_DEFINE(cli_odbc_write, "Test setting a func_odbc function"),
AST_CLI_DEFINE(cli_odbc_read, "Test reading a func_odbc function"),
}
Definition at line 1367 of file func_odbc.c.
Referenced by load_module(), and unload_module().
struct ast_threadstorage coldata_buf = { .once = PTHREAD_ONCE_INIT, .key_init = __init_coldata_buf , .custom_init = NULL , } [static] |
struct ast_threadstorage colnames_buf = { .once = PTHREAD_ONCE_INIT, .key_init = __init_colnames_buf , .custom_init = NULL , } [static] |
char* config = "func_odbc.conf" [static] |
Definition at line 100 of file func_odbc.c.
struct ast_custom_function escape_function [static] |
Initial value:
{
.name = "SQL_ESC",
.read = acf_escape,
.write = NULL,
}
Definition at line 742 of file func_odbc.c.
Referenced by load_module(), and unload_module().
struct ast_custom_function fetch_function [static] |
Initial value:
{
.name = "ODBC_FETCH",
.read = acf_fetch,
.write = NULL,
}
Definition at line 776 of file func_odbc.c.
Referenced by load_module(), and unload_module().
struct ast_datastore_info odbc_info [static] |
Initial value:
{
.type = "FUNC_ODBC",
.destroy = odbc_datastore_free,
}
Definition at line 121 of file func_odbc.c.
Referenced by acf_fetch(), acf_odbc_read(), and exec_odbcfinish().
int resultcount = 0 [static] |
struct ast_threadstorage sql2_buf = { .once = PTHREAD_ONCE_INIT, .key_init = __init_sql2_buf , .custom_init = NULL , } [static] |
struct ast_threadstorage sql_buf = { .once = PTHREAD_ONCE_INIT, .key_init = __init_sql_buf , .custom_init = NULL , } [static] |
Definition at line 142 of file func_odbc.c.
Referenced by acf_odbc_read(), acf_odbc_write(), cli_odbc_read(), cli_odbc_write(), config_pgsql(), destroy_pgsql(), realtime_multi_pgsql(), realtime_pgsql(), realtime_update2_handler(), store_pgsql(), update2_odbc(), update2_pgsql(), update2_prepare(), and update_pgsql().
1.5.6