Wed Oct 28 13:32:53 2009

Asterisk developer's documentation


func_odbc.c File Reference

ODBC lookups. More...

#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"

Include dependency graph for func_odbc.c:

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_infoast_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 , }


Detailed Description

ODBC lookups.

Author:
Tilghman Lesher <func_odbc__200508@the-tilghman.com>

Definition in file func_odbc.c.


Enumeration Type Documentation

Enumerator:
OPT_ESCAPECOMMAS 
OPT_MULTIROW 

Definition at line 102 of file func_odbc.c.

00102                        {
00103    OPT_ESCAPECOMMAS =   (1 << 0),
00104    OPT_MULTIROW     =   (1 << 1),
00105 };


Function Documentation

static void __fini_queries ( void   )  [static]

Definition at line 138 of file func_odbc.c.

00150 {

static void __init_coldata_buf ( void   )  [static]

Definition at line 144 of file func_odbc.c.

00150 {

static void __init_colnames_buf ( void   )  [static]

Definition at line 145 of file func_odbc.c.

00150 {

static void __init_queries ( void   )  [static]

Definition at line 138 of file func_odbc.c.

00150 {

static void __init_sql2_buf ( void   )  [static]

Definition at line 143 of file func_odbc.c.

00150 {

static void __init_sql_buf ( void   )  [static]

Definition at line 142 of file func_odbc.c.

00150 {

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]

Definition at line 726 of file func_odbc.c.

00727 {
00728    char *out = buf;
00729 
00730    for (; *data && out - buf < len; data++) {
00731       if (*data == '\'') {
00732          *out = '\'';
00733          out++;
00734       }
00735       *out++ = *data;
00736    }
00737    *out = '\0';
00738 
00739    return 0;
00740 }

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]

Note:
Okay, this part is confusing. Transactions belong to a single database handle. Therefore, when working with transactions, we CANNOT failover to multiple DSNs. We MUST have a single handle all the way through the transaction, or else we CANNOT enforce atomicity.

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]


Variable Documentation

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]

Definition at line 782 of file func_odbc.c.

Referenced by load_module(), and unload_module().

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]

Definition at line 144 of file func_odbc.c.

Referenced by acf_odbc_read(), and cli_odbc_read().

struct ast_threadstorage colnames_buf = { .once = PTHREAD_ONCE_INIT, .key_init = __init_colnames_buf , .custom_init = NULL , } [static]

Definition at line 145 of file func_odbc.c.

Referenced by acf_odbc_read().

char* config = "func_odbc.conf" [static]

Definition at line 100 of file func_odbc.c.

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().

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]

Definition at line 140 of file func_odbc.c.

Referenced by acf_odbc_read().

struct ast_threadstorage sql2_buf = { .once = PTHREAD_ONCE_INIT, .key_init = __init_sql2_buf , .custom_init = NULL , } [static]

Definition at line 143 of file func_odbc.c.

Referenced by acf_odbc_write().

struct ast_threadstorage sql_buf = { .once = PTHREAD_ONCE_INIT, .key_init = __init_sql_buf , .custom_init = NULL , } [static]


Generated on Wed Oct 28 13:32:53 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6