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, .support_level = AST_MODULE_SUPPORT_CORE, .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 103 of file func_odbc.c.

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


Function Documentation

static void __fini_queries ( void   )  [static]

Definition at line 139 of file func_odbc.c.

00151 {

static void __init_coldata_buf ( void   )  [static]

Definition at line 145 of file func_odbc.c.

00151 {

static void __init_colnames_buf ( void   )  [static]

Definition at line 146 of file func_odbc.c.

00151 {

static void __init_queries ( void   )  [static]

Definition at line 139 of file func_odbc.c.

00151 {

static void __init_sql2_buf ( void   )  [static]

Definition at line 144 of file func_odbc.c.

00151 {

static void __init_sql_buf ( void   )  [static]

Definition at line 143 of file func_odbc.c.

00151 {

static void __reg_module ( void   )  [static]

Definition at line 1587 of file func_odbc.c.

static void __unreg_module ( void   )  [static]

Definition at line 1587 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 780 of file func_odbc.c.

References out.

00781 {
00782    char *out = buf;
00783 
00784    for (; *data && out - buf < len; data++) {
00785       if (*data == '\'') {
00786          *out = '\'';
00787          out++;
00788       }
00789       *out++ = *data;
00790    }
00791    *out = '\0';
00792 
00793    return 0;
00794 }

static int acf_fetch ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 802 of file func_odbc.c.

References ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_channel_lock, ast_channel_unlock, ast_copy_string(), ast_datastore_free(), ast_free, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_log, odbc_datastore_row::data, ast_datastore::data, acf_odbc_query::list, LOG_WARNING, odbc_datastore::names, odbc_info, and pbx_builtin_setvar_helper().

Referenced by acf_odbc_read().

00803 {
00804    struct ast_datastore *store;
00805    struct odbc_datastore *resultset;
00806    struct odbc_datastore_row *row;
00807 
00808    if (!chan) {
00809       ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
00810       return -1;
00811    }
00812 
00813    ast_channel_lock(chan);
00814    store = ast_channel_datastore_find(chan, &odbc_info, data);
00815    if (!store) {
00816       ast_channel_unlock(chan);
00817       pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "FAILURE");
00818       return -1;
00819    }
00820    resultset = store->data;
00821    AST_LIST_LOCK(resultset);
00822    row = AST_LIST_REMOVE_HEAD(resultset, list);
00823    AST_LIST_UNLOCK(resultset);
00824    if (!row) {
00825       /* Cleanup datastore */
00826       ast_channel_datastore_remove(chan, store);
00827       ast_datastore_free(store);
00828       ast_channel_unlock(chan);
00829       pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "FAILURE");
00830       return -1;
00831    }
00832    pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", resultset->names);
00833    ast_channel_unlock(chan);
00834    ast_copy_string(buf, row->data, len);
00835    ast_free(row);
00836    pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "SUCCESS");
00837    return 0;
00838 }

static int acf_odbc_read ( struct ast_channel chan,
const char *  cmd,
char *  s,
char *  buf,
size_t  len 
) [static]

Definition at line 420 of file func_odbc.c.

References acf_odbc_query::acf, acf_fetch(), args, 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_unlock, ast_channel_unref, ast_copy_string(), ast_datastore_alloc, ast_datastore_free(), ast_debug, AST_DECLARE_APP_ARGS, ast_dummy_channel_alloc(), 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(), indicator, acf_odbc_query::list, LOG_ERROR, LOG_WARNING, ast_custom_function::name, NULL, 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, status, tmp(), and ast_datastore::uid.

Referenced by init_acf_query().

00421 {
00422    struct odbc_obj *obj = NULL;
00423    struct acf_odbc_query *query;
00424    char varname[15], rowcount[12] = "-1";
00425    struct ast_str *colnames = ast_str_thread_get(&colnames_buf, 16);
00426    int res, x, y, buflen = 0, escapecommas, rowlimit = 1, multirow = 0, dsn, bogus_chan = 0;
00427    AST_DECLARE_APP_ARGS(args,
00428       AST_APP_ARG(field)[100];
00429    );
00430    SQLHSTMT stmt = NULL;
00431    SQLSMALLINT colcount=0;
00432    SQLLEN indicator;
00433    SQLSMALLINT collength;
00434    struct odbc_datastore *resultset = NULL;
00435    struct odbc_datastore_row *row = NULL;
00436    struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
00437    const char *status = "FAILURE";
00438 
00439    if (!sql || !colnames) {
00440       if (chan) {
00441          pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00442       }
00443       return -1;
00444    }
00445 
00446    ast_str_reset(colnames);
00447 
00448    AST_RWLIST_RDLOCK(&queries);
00449    AST_RWLIST_TRAVERSE(&queries, query, list) {
00450       if (!strcmp(query->acf->name, cmd)) {
00451          break;
00452       }
00453    }
00454 
00455    if (!query) {
00456       ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
00457       AST_RWLIST_UNLOCK(&queries);
00458       if (chan) {
00459          pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00460          pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00461       }
00462       return -1;
00463    }
00464 
00465    if (!chan) {
00466       if (!(chan = ast_dummy_channel_alloc())) {
00467          AST_RWLIST_UNLOCK(&queries);
00468          return -1;
00469       }
00470       bogus_chan = 1;
00471    }
00472 
00473    if (!bogus_chan) {
00474       ast_autoservice_start(chan);
00475    }
00476 
00477    AST_STANDARD_APP_ARGS(args, s);
00478    for (x = 0; x < args.argc; x++) {
00479       snprintf(varname, sizeof(varname), "ARG%d", x + 1);
00480       pbx_builtin_pushvar_helper(chan, varname, args.field[x]);
00481    }
00482 
00483    ast_str_substitute_variables(&sql, 0, chan, query->sql_read);
00484 
00485    if (bogus_chan) {
00486       chan = ast_channel_unref(chan);
00487    } else {
00488       /* Restore prior values */
00489       for (x = 0; x < args.argc; x++) {
00490          snprintf(varname, sizeof(varname), "ARG%d", x + 1);
00491          pbx_builtin_setvar_helper(chan, varname, NULL);
00492       }
00493    }
00494 
00495    /* Save these flags, so we can release the lock */
00496    escapecommas = ast_test_flag(query, OPT_ESCAPECOMMAS);
00497    if (!bogus_chan && ast_test_flag(query, OPT_MULTIROW)) {
00498       if (!(resultset = ast_calloc(1, sizeof(*resultset)))) {
00499          pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00500          pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00501          ast_autoservice_stop(chan);
00502          return -1;
00503       }
00504       AST_LIST_HEAD_INIT(resultset);
00505       if (query->rowlimit) {
00506          rowlimit = query->rowlimit;
00507       } else {
00508          rowlimit = INT_MAX;
00509       }
00510       multirow = 1;
00511    } else if (!bogus_chan) {
00512       if (query->rowlimit > 1) {
00513          rowlimit = query->rowlimit;
00514          if (!(resultset = ast_calloc(1, sizeof(*resultset)))) {
00515             pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00516             pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00517             ast_autoservice_stop(chan);
00518             return -1;
00519          }
00520          AST_LIST_HEAD_INIT(resultset);
00521       }
00522    }
00523    AST_RWLIST_UNLOCK(&queries);
00524 
00525    for (dsn = 0; dsn < 5; dsn++) {
00526       if (!ast_strlen_zero(query->readhandle[dsn])) {
00527          obj = ast_odbc_request_obj(query->readhandle[dsn], 0);
00528          if (obj) {
00529             stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql));
00530          }
00531       }
00532       if (stmt) {
00533          break;
00534       }
00535       if (obj) {
00536          ast_odbc_release_obj(obj);
00537          obj = NULL;
00538       }
00539    }
00540 
00541    if (!stmt) {
00542       ast_log(LOG_ERROR, "Unable to execute query [%s]\n", ast_str_buffer(sql));
00543       if (obj) {
00544          ast_odbc_release_obj(obj);
00545          obj = NULL;
00546       }
00547       if (!bogus_chan) {
00548          pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00549          ast_autoservice_stop(chan);
00550       }
00551       odbc_datastore_free(resultset);
00552       return -1;
00553    }
00554 
00555    res = SQLNumResultCols(stmt, &colcount);
00556    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00557       ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", ast_str_buffer(sql));
00558       SQLCloseCursor(stmt);
00559       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00560       ast_odbc_release_obj(obj);
00561       obj = NULL;
00562       if (!bogus_chan) {
00563          pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00564          ast_autoservice_stop(chan);
00565       }
00566       odbc_datastore_free(resultset);
00567       return -1;
00568    }
00569 
00570    res = SQLFetch(stmt);
00571    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00572       int res1 = -1;
00573       if (res == SQL_NO_DATA) {
00574          ast_verb(4, "Found no rows [%s]\n", ast_str_buffer(sql));
00575          res1 = 0;
00576          buf[0] = '\0';
00577          ast_copy_string(rowcount, "0", sizeof(rowcount));
00578          status = "NODATA";
00579       } else {
00580          ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
00581          status = "FETCHERROR";
00582       }
00583       SQLCloseCursor(stmt);
00584       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00585       ast_odbc_release_obj(obj);
00586       obj = NULL;
00587       if (!bogus_chan) {
00588          pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00589          pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00590          ast_autoservice_stop(chan);
00591       }
00592       odbc_datastore_free(resultset);
00593       return res1;
00594    }
00595 
00596    status = "SUCCESS";
00597 
00598    for (y = 0; y < rowlimit; y++) {
00599       buf[0] = '\0';
00600       for (x = 0; x < colcount; x++) {
00601          int i;
00602          struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
00603          char *ptrcoldata;
00604 
00605          if (!coldata) {
00606             odbc_datastore_free(resultset);
00607             SQLCloseCursor(stmt);
00608             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00609             ast_odbc_release_obj(obj);
00610             obj = NULL;
00611             if (!bogus_chan) {
00612                pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
00613                ast_autoservice_stop(chan);
00614             }
00615             return -1;
00616          }
00617 
00618          if (y == 0) {
00619             char colname[256];
00620             SQLULEN maxcol = 0;
00621 
00622             res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL);
00623             ast_debug(3, "Got collength of %d and maxcol of %d for column '%s' (offset %d)\n", (int)collength, (int)maxcol, colname, x);
00624             if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
00625                snprintf(colname, sizeof(colname), "field%d", x);
00626             }
00627 
00628             ast_str_make_space(&coldata, maxcol + 1);
00629 
00630             if (ast_str_strlen(colnames)) {
00631                ast_str_append(&colnames, 0, ",");
00632             }
00633             ast_str_append_escapecommas(&colnames, 0, colname, sizeof(colname));
00634 
00635             if (resultset) {
00636                void *tmp = ast_realloc(resultset, sizeof(*resultset) + ast_str_strlen(colnames) + 1);
00637                if (!tmp) {
00638                   ast_log(LOG_ERROR, "No space for a new resultset?\n");
00639                   odbc_datastore_free(resultset);
00640                   SQLCloseCursor(stmt);
00641                   SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00642                   ast_odbc_release_obj(obj);
00643                   obj = NULL;
00644                   if (!bogus_chan) {
00645                      pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00646                      pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
00647                      ast_autoservice_stop(chan);
00648                   }
00649                   return -1;
00650                }
00651                resultset = tmp;
00652                strcpy((char *)resultset + sizeof(*resultset), ast_str_buffer(colnames));
00653             }
00654          }
00655 
00656          buflen = strlen(buf);
00657          res = ast_odbc_ast_str_SQLGetData(&coldata, -1, stmt, x + 1, SQL_CHAR, &indicator);
00658          if (indicator == SQL_NULL_DATA) {
00659             ast_debug(3, "Got NULL data\n");
00660             ast_str_reset(coldata);
00661             res = SQL_SUCCESS;
00662          }
00663 
00664          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00665             ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", ast_str_buffer(sql));
00666             y = -1;
00667             buf[0] = '\0';
00668             goto end_acf_read;
00669          }
00670 
00671          ast_debug(2, "Got coldata of '%s'\n", ast_str_buffer(coldata));
00672 
00673          if (x) {
00674             buf[buflen++] = ',';
00675          }
00676 
00677          /* Copy data, encoding '\' and ',' for the argument parser */
00678          ptrcoldata = ast_str_buffer(coldata);
00679          for (i = 0; i < ast_str_strlen(coldata); i++) {
00680             if (escapecommas && (ptrcoldata[i] == '\\' || ptrcoldata[i] == ',')) {
00681                buf[buflen++] = '\\';
00682             }
00683             buf[buflen++] = ptrcoldata[i];
00684 
00685             if (buflen >= len - 2) {
00686                break;
00687             }
00688 
00689             if (ptrcoldata[i] == '\0') {
00690                break;
00691             }
00692          }
00693 
00694          buf[buflen] = '\0';
00695          ast_debug(2, "buf is now set to '%s'\n", buf);
00696       }
00697       ast_debug(2, "buf is now set to '%s'\n", buf);
00698 
00699       if (resultset) {
00700          row = ast_calloc(1, sizeof(*row) + buflen + 1);
00701          if (!row) {
00702             ast_log(LOG_ERROR, "Unable to allocate space for more rows in this resultset.\n");
00703             status = "MEMERROR";
00704             goto end_acf_read;
00705          }
00706          strcpy((char *)row + sizeof(*row), buf);
00707          AST_LIST_INSERT_TAIL(resultset, row, list);
00708 
00709          /* Get next row */
00710          res = SQLFetch(stmt);
00711          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00712             if (res != SQL_NO_DATA) {
00713                ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
00714             }
00715             /* Number of rows in the resultset */
00716             y++;
00717             break;
00718          }
00719       }
00720    }
00721 
00722 end_acf_read:
00723    if (!bogus_chan) {
00724       snprintf(rowcount, sizeof(rowcount), "%d", y);
00725       pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00726       pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00727       pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(colnames));
00728       if (resultset) {
00729          struct ast_datastore *odbc_store;
00730          if (multirow) {
00731             int uid;
00732             uid = ast_atomic_fetchadd_int(&resultcount, +1) + 1;
00733             snprintf(buf, len, "%d", uid);
00734          } else {
00735             /* Name of the query is name of the resultset */
00736             ast_copy_string(buf, cmd, len);
00737 
00738             /* If there's one with the same name already, free it */
00739             ast_channel_lock(chan);
00740             if ((odbc_store = ast_channel_datastore_find(chan, &odbc_info, buf))) {
00741                ast_channel_datastore_remove(chan, odbc_store);
00742                ast_datastore_free(odbc_store);
00743             }
00744             ast_channel_unlock(chan);
00745          }
00746          odbc_store = ast_datastore_alloc(&odbc_info, buf);
00747          if (!odbc_store) {
00748             ast_log(LOG_ERROR, "Rows retrieved, but unable to store it in the channel.  Results fail.\n");
00749             odbc_datastore_free(resultset);
00750             SQLCloseCursor(stmt);
00751             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00752             ast_odbc_release_obj(obj);
00753             obj = NULL;
00754             pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
00755             ast_autoservice_stop(chan);
00756             return -1;
00757          }
00758          odbc_store->data = resultset;
00759          ast_channel_lock(chan);
00760          ast_channel_datastore_add(chan, odbc_store);
00761          ast_channel_unlock(chan);
00762       }
00763    }
00764    SQLCloseCursor(stmt);
00765    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00766    ast_odbc_release_obj(obj);
00767    obj = NULL;
00768    if (resultset && !multirow) {
00769       /* Fetch the first resultset */
00770       if (!acf_fetch(chan, "", buf, buf, len)) {
00771          buf[0] = '\0';
00772       }
00773    }
00774    if (!bogus_chan) {
00775       ast_autoservice_stop(chan);
00776    }
00777    return 0;
00778 }

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 211 of file func_odbc.c.

References acf_odbc_query::acf, args, AST_APP_ARG, ast_autoservice_start(), ast_autoservice_stop(), ast_channel_unref, 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_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, NULL, 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().

00212 {
00213    struct odbc_obj *obj = NULL;
00214    struct acf_odbc_query *query;
00215    char *t, varname[15];
00216    int i, dsn, bogus_chan = 0;
00217    int transactional = 0;
00218    AST_DECLARE_APP_ARGS(values,
00219       AST_APP_ARG(field)[100];
00220    );
00221    AST_DECLARE_APP_ARGS(args,
00222       AST_APP_ARG(field)[100];
00223    );
00224    SQLHSTMT stmt = NULL;
00225    SQLLEN rows=0;
00226    struct ast_str *buf = ast_str_thread_get(&sql_buf, 16);
00227    struct ast_str *insertbuf = ast_str_thread_get(&sql2_buf, 16);
00228    const char *status = "FAILURE";
00229 
00230    if (!buf || !insertbuf) {
00231       return -1;
00232    }
00233 
00234    AST_RWLIST_RDLOCK(&queries);
00235    AST_RWLIST_TRAVERSE(&queries, query, list) {
00236       if (!strcmp(query->acf->name, cmd)) {
00237          break;
00238       }
00239    }
00240 
00241    if (!query) {
00242       ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
00243       AST_RWLIST_UNLOCK(&queries);
00244       if (chan) {
00245          pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00246       }
00247       return -1;
00248    }
00249 
00250    if (!chan) {
00251       if (!(chan = ast_dummy_channel_alloc())) {
00252          AST_RWLIST_UNLOCK(&queries);
00253          return -1;
00254       }
00255       bogus_chan = 1;
00256    }
00257 
00258    if (!bogus_chan) {
00259       ast_autoservice_start(chan);
00260    }
00261 
00262    ast_str_make_space(&buf, strlen(query->sql_write) * 2 + 300);
00263    /* We only get here if sql_write is set. sql_insert is optional however. */
00264    if (query->sql_insert) {
00265       ast_str_make_space(&insertbuf, strlen(query->sql_insert) * 2 + 300);
00266    }
00267 
00268    /* Parse our arguments */
00269    t = value ? ast_strdupa(value) : "";
00270 
00271    if (!s || !t) {
00272       ast_log(LOG_ERROR, "Out of memory\n");
00273       AST_RWLIST_UNLOCK(&queries);
00274       if (!bogus_chan) {
00275          ast_autoservice_stop(chan);
00276          pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00277       } else {
00278          ast_channel_unref(chan);
00279       }
00280       return -1;
00281    }
00282 
00283    AST_STANDARD_APP_ARGS(args, s);
00284    for (i = 0; i < args.argc; i++) {
00285       snprintf(varname, sizeof(varname), "ARG%d", i + 1);
00286       pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
00287    }
00288 
00289    /* Parse values, just like arguments */
00290    AST_STANDARD_APP_ARGS(values, t);
00291    for (i = 0; i < values.argc; i++) {
00292       snprintf(varname, sizeof(varname), "VAL%d", i + 1);
00293       pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
00294    }
00295 
00296    /* Additionally set the value as a whole (but push an empty string if value is NULL) */
00297    pbx_builtin_pushvar_helper(chan, "VALUE", value ? value : "");
00298 
00299    ast_str_substitute_variables(&buf, 0, chan, query->sql_write);
00300    if (query->sql_insert) {
00301       ast_str_substitute_variables(&insertbuf, 0, chan, query->sql_insert);
00302    }
00303 
00304    if (bogus_chan) {
00305       chan = ast_channel_unref(chan);
00306    } else {
00307       /* Restore prior values */
00308       for (i = 0; i < args.argc; i++) {
00309          snprintf(varname, sizeof(varname), "ARG%d", i + 1);
00310          pbx_builtin_setvar_helper(chan, varname, NULL);
00311       }
00312 
00313       for (i = 0; i < values.argc; i++) {
00314          snprintf(varname, sizeof(varname), "VAL%d", i + 1);
00315          pbx_builtin_setvar_helper(chan, varname, NULL);
00316       }
00317       pbx_builtin_setvar_helper(chan, "VALUE", NULL);
00318    }
00319 
00320    /*!\note
00321     * Okay, this part is confusing.  Transactions belong to a single database
00322     * handle.  Therefore, when working with transactions, we CANNOT failover
00323     * to multiple DSNs.  We MUST have a single handle all the way through the
00324     * transaction, or else we CANNOT enforce atomicity.
00325     */
00326    for (dsn = 0; dsn < 5; dsn++) {
00327       if (!ast_strlen_zero(query->writehandle[dsn])) {
00328          if (transactional) {
00329             /* This can only happen second time through or greater. */
00330             ast_log(LOG_WARNING, "Transactions do not work well with multiple DSNs for 'writehandle'\n");
00331          }
00332 
00333          if ((obj = ast_odbc_retrieve_transaction_obj(chan, query->writehandle[dsn]))) {
00334             transactional = 1;
00335          } else {
00336             obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
00337             transactional = 0;
00338          }
00339 
00340          if (obj && (stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(buf)))) {
00341             break;
00342          }
00343 
00344          if (obj && !transactional) {
00345             ast_odbc_release_obj(obj);
00346             obj = NULL;
00347          }
00348       }
00349    }
00350 
00351    if (stmt) {
00352       SQLRowCount(stmt, &rows);
00353       SQLCloseCursor(stmt);
00354       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00355 
00356       if (rows != 0) {
00357          status = "SUCCESS";
00358 
00359       } else if (query->sql_insert) {
00360          if (obj && !transactional) {
00361             ast_odbc_release_obj(obj);
00362             obj = NULL;
00363          }
00364 
00365          for (transactional = 0, dsn = 0; dsn < 5; dsn++) {
00366             if (!ast_strlen_zero(query->writehandle[dsn])) {
00367                if (transactional) {
00368                   /* This can only happen second time through or greater. */
00369                   ast_log(LOG_WARNING, "Transactions do not work well with multiple DSNs for 'writehandle'\n");
00370                } else if (obj) {
00371                   ast_odbc_release_obj(obj);
00372                   obj = NULL;
00373                }
00374 
00375                if ((obj = ast_odbc_retrieve_transaction_obj(chan, query->writehandle[dsn]))) {
00376                   transactional = 1;
00377                } else {
00378                   obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
00379                   transactional = 0;
00380                }
00381                if (obj) {
00382                   stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(insertbuf));
00383                }
00384             }
00385             if (stmt) {
00386                status = "FAILOVER";
00387                SQLRowCount(stmt, &rows);
00388                SQLCloseCursor(stmt);
00389                SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00390                break;
00391             }
00392          }
00393       }
00394    }
00395 
00396    AST_RWLIST_UNLOCK(&queries);
00397 
00398    /* Output the affected rows, for all cases.  In the event of failure, we
00399     * flag this as -1 rows.  Note that this is different from 0 affected rows
00400     * which would be the case if we succeeded in our query, but the values did
00401     * not change. */
00402    if (!bogus_chan) {
00403       snprintf(varname, sizeof(varname), "%d", (int)rows);
00404       pbx_builtin_setvar_helper(chan, "ODBCROWS", varname);
00405       pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00406    }
00407 
00408    if (obj && !transactional) {
00409       ast_odbc_release_obj(obj);
00410       obj = NULL;
00411    }
00412 
00413    if (!bogus_chan) {
00414       ast_autoservice_stop(chan);
00415    }
00416 
00417    return 0;
00418 }

static char* cli_odbc_read ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 1101 of file func_odbc.c.

References acf_odbc_query::acf, ast_cli_args::argc, args, ast_cli_args::argv, AST_APP_ARG, ast_channel_unref, 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, CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, coldata_buf, ast_cli_entry::command, dsn, ast_cli_args::fd, generic_execute(), indicator, acf_odbc_query::list, ast_cli_args::n, ast_custom_function::name, NULL, 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.

01102 {
01103    AST_DECLARE_APP_ARGS(args,
01104       AST_APP_ARG(field)[100];
01105    );
01106    struct ast_str *sql;
01107    char *char_args, varname[10];
01108    struct acf_odbc_query *query;
01109    struct ast_channel *chan;
01110    int i;
01111 
01112    switch (cmd) {
01113    case CLI_INIT:
01114       e->command = "odbc read";
01115       e->usage =
01116          "Usage: odbc read <name> <args> [exec]\n"
01117          "       Evaluates the SQL provided in the ODBC function <name>, and\n"
01118          "       optionally executes the function.  This function is intended for\n"
01119          "       testing purposes.  Remember to quote arguments containing spaces.\n";
01120       return NULL;
01121    case CLI_GENERATE:
01122       if (a->pos == 2) {
01123          int wordlen = strlen(a->word), which = 0;
01124          /* Complete function name */
01125          AST_RWLIST_RDLOCK(&queries);
01126          AST_RWLIST_TRAVERSE(&queries, query, list) {
01127             if (!strncasecmp(query->acf->name, a->word, wordlen)) {
01128                if (++which > a->n) {
01129                   char *res = ast_strdup(query->acf->name);
01130                   AST_RWLIST_UNLOCK(&queries);
01131                   return res;
01132                }
01133             }
01134          }
01135          AST_RWLIST_UNLOCK(&queries);
01136          return NULL;
01137       } else if (a->pos == 4) {
01138          return a->n == 0 ? ast_strdup("exec") : NULL;
01139       } else {
01140          return NULL;
01141       }
01142    }
01143 
01144    if (a->argc < 4 || a->argc > 5) {
01145       return CLI_SHOWUSAGE;
01146    }
01147 
01148    sql = ast_str_thread_get(&sql_buf, 16);
01149    if (!sql) {
01150       return CLI_FAILURE;
01151    }
01152 
01153    AST_RWLIST_RDLOCK(&queries);
01154    AST_RWLIST_TRAVERSE(&queries, query, list) {
01155       if (!strcmp(query->acf->name, a->argv[2])) {
01156          break;
01157       }
01158    }
01159 
01160    if (!query) {
01161       ast_cli(a->fd, "No such query '%s'\n", a->argv[2]);
01162       AST_RWLIST_UNLOCK(&queries);
01163       return CLI_SHOWUSAGE;
01164    }
01165 
01166    if (!query->sql_read) {
01167       ast_cli(a->fd, "The function %s has no readsql parameter.\n", a->argv[2]);
01168       AST_RWLIST_UNLOCK(&queries);
01169       return CLI_SUCCESS;
01170    }
01171 
01172    ast_str_make_space(&sql, strlen(query->sql_read) * 2 + 300);
01173 
01174    /* Evaluate function */
01175    char_args = ast_strdupa(a->argv[3]);
01176 
01177    chan = ast_dummy_channel_alloc();
01178    if (!chan) {
01179       AST_RWLIST_UNLOCK(&queries);
01180       return CLI_FAILURE;
01181    }
01182 
01183    AST_STANDARD_APP_ARGS(args, char_args);
01184    for (i = 0; i < args.argc; i++) {
01185       snprintf(varname, sizeof(varname), "ARG%d", i + 1);
01186       pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
01187    }
01188 
01189    ast_str_substitute_variables(&sql, 0, chan, query->sql_read);
01190    chan = ast_channel_unref(chan);
01191 
01192    if (a->argc == 5 && !strcmp(a->argv[4], "exec")) {
01193       /* Execute the query */
01194       struct odbc_obj *obj = NULL;
01195       int dsn, executed = 0;
01196       SQLHSTMT stmt;
01197       int rows = 0, res, x;
01198       SQLSMALLINT colcount = 0, collength;
01199       SQLLEN indicator;
01200       struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
01201       char colname[256];
01202       SQLULEN maxcol;
01203 
01204       if (!coldata) {
01205          AST_RWLIST_UNLOCK(&queries);
01206          return CLI_SUCCESS;
01207       }
01208 
01209       for (dsn = 0; dsn < 5; dsn++) {
01210          if (ast_strlen_zero(query->readhandle[dsn])) {
01211             continue;
01212          }
01213          ast_debug(1, "Found handle %s\n", query->readhandle[dsn]);
01214          if (!(obj = ast_odbc_request_obj(query->readhandle[dsn], 0))) {
01215             continue;
01216          }
01217 
01218          ast_debug(1, "Got obj\n");
01219          if (!(stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)))) {
01220             ast_odbc_release_obj(obj);
01221             obj = NULL;
01222             continue;
01223          }
01224 
01225          executed = 1;
01226 
01227          res = SQLNumResultCols(stmt, &colcount);
01228          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01229             ast_cli(a->fd, "SQL Column Count error!\n[%s]\n\n", ast_str_buffer(sql));
01230             SQLCloseCursor(stmt);
01231             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01232             ast_odbc_release_obj(obj);
01233             obj = NULL;
01234             AST_RWLIST_UNLOCK(&queries);
01235             return CLI_SUCCESS;
01236          }
01237 
01238          res = SQLFetch(stmt);
01239          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01240             SQLCloseCursor(stmt);
01241             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01242             ast_odbc_release_obj(obj);
01243             obj = NULL;
01244             if (res == SQL_NO_DATA) {
01245                ast_cli(a->fd, "Returned %d rows.  Query executed on handle %d:%s [%s]\n", rows, dsn, query->readhandle[dsn], ast_str_buffer(sql));
01246                break;
01247             } else {
01248                ast_cli(a->fd, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
01249             }
01250             AST_RWLIST_UNLOCK(&queries);
01251             return CLI_SUCCESS;
01252          }
01253          for (;;) {
01254             for (x = 0; x < colcount; x++) {
01255                maxcol = 0;
01256 
01257                res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL);
01258                if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
01259                   snprintf(colname, sizeof(colname), "field%d", x);
01260                }
01261 
01262                res = ast_odbc_ast_str_SQLGetData(&coldata, maxcol, stmt, x + 1, SQL_CHAR, &indicator);
01263                if (indicator == SQL_NULL_DATA) {
01264                   ast_str_set(&coldata, 0, "(nil)");
01265                   res = SQL_SUCCESS;
01266                }
01267 
01268                if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01269                   ast_cli(a->fd, "SQL Get Data error %d!\n[%s]\n\n", res, ast_str_buffer(sql));
01270                   SQLCloseCursor(stmt);
01271                   SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01272                   ast_odbc_release_obj(obj);
01273                   obj = NULL;
01274                   AST_RWLIST_UNLOCK(&queries);
01275                   return CLI_SUCCESS;
01276                }
01277 
01278                ast_cli(a->fd, "%-20.20s  %s\n", colname, ast_str_buffer(coldata));
01279             }
01280             rows++;
01281 
01282             /* Get next row */
01283             res = SQLFetch(stmt);
01284             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01285                break;
01286             }
01287             ast_cli(a->fd, "%-20.20s  %s\n", "----------", "----------");
01288          }
01289          SQLCloseCursor(stmt);
01290          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01291          ast_odbc_release_obj(obj);
01292          obj = NULL;
01293          ast_cli(a->fd, "Returned %d row%s.  Query executed on handle %d [%s]\n", rows, rows == 1 ? "" : "s", dsn, query->readhandle[dsn]);
01294          break;
01295       }
01296       if (obj) {
01297          ast_odbc_release_obj(obj);
01298          obj = NULL;
01299       }
01300 
01301       if (!executed) {
01302          ast_cli(a->fd, "Failed to execute query. [%s]\n", ast_str_buffer(sql));
01303       }
01304    } else { /* No execution, just print out the resulting SQL */
01305       ast_cli(a->fd, "%s\n", ast_str_buffer(sql));
01306    }
01307    AST_RWLIST_UNLOCK(&queries);
01308    return CLI_SUCCESS;
01309 }

static char* cli_odbc_write ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 1311 of file func_odbc.c.

References acf_odbc_query::acf, ast_cli_args::argc, args, ast_cli_args::argv, AST_APP_ARG, ast_channel_unref, 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, 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, NULL, 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.

01312 {
01313    AST_DECLARE_APP_ARGS(values,
01314       AST_APP_ARG(field)[100];
01315    );
01316    AST_DECLARE_APP_ARGS(args,
01317       AST_APP_ARG(field)[100];
01318    );
01319    struct ast_str *sql;
01320    char *char_args, *char_values, varname[10];
01321    struct acf_odbc_query *query;
01322    struct ast_channel *chan;
01323    int i;
01324 
01325    switch (cmd) {
01326    case CLI_INIT:
01327       e->command = "odbc write";
01328       e->usage =
01329          "Usage: odbc write <name> <args> <value> [exec]\n"
01330          "       Evaluates the SQL provided in the ODBC function <name>, and\n"
01331          "       optionally executes the function.  This function is intended for\n"
01332          "       testing purposes.  Remember to quote arguments containing spaces.\n";
01333       return NULL;
01334    case CLI_GENERATE:
01335       if (a->pos == 2) {
01336          int wordlen = strlen(a->word), which = 0;
01337          /* Complete function name */
01338          AST_RWLIST_RDLOCK(&queries);
01339          AST_RWLIST_TRAVERSE(&queries, query, list) {
01340             if (!strncasecmp(query->acf->name, a->word, wordlen)) {
01341                if (++which > a->n) {
01342                   char *res = ast_strdup(query->acf->name);
01343                   AST_RWLIST_UNLOCK(&queries);
01344                   return res;
01345                }
01346             }
01347          }
01348          AST_RWLIST_UNLOCK(&queries);
01349          return NULL;
01350       } else if (a->pos == 5) {
01351          return a->n == 0 ? ast_strdup("exec") : NULL;
01352       } else {
01353          return NULL;
01354       }
01355    }
01356 
01357    if (a->argc < 5 || a->argc > 6) {
01358       return CLI_SHOWUSAGE;
01359    }
01360 
01361    sql = ast_str_thread_get(&sql_buf, 16);
01362    if (!sql) {
01363       return CLI_FAILURE;
01364    }
01365 
01366    AST_RWLIST_RDLOCK(&queries);
01367    AST_RWLIST_TRAVERSE(&queries, query, list) {
01368       if (!strcmp(query->acf->name, a->argv[2])) {
01369          break;
01370       }
01371    }
01372 
01373    if (!query) {
01374       ast_cli(a->fd, "No such query '%s'\n", a->argv[2]);
01375       AST_RWLIST_UNLOCK(&queries);
01376       return CLI_SHOWUSAGE;
01377    }
01378 
01379    if (!query->sql_write) {
01380       ast_cli(a->fd, "The function %s has no writesql parameter.\n", a->argv[2]);
01381       AST_RWLIST_UNLOCK(&queries);
01382       return CLI_SUCCESS;
01383    }
01384 
01385    /* FIXME: The code below duplicates code found in acf_odbc_write but
01386     * lacks the newer sql_insert additions. */
01387 
01388    ast_str_make_space(&sql, strlen(query->sql_write) * 2 + 300);
01389 
01390    /* Evaluate function */
01391    char_args = ast_strdupa(a->argv[3]);
01392    char_values = ast_strdupa(a->argv[4]);
01393 
01394    chan = ast_dummy_channel_alloc();
01395    if (!chan) {
01396       AST_RWLIST_UNLOCK(&queries);
01397       return CLI_FAILURE;
01398    }
01399 
01400    AST_STANDARD_APP_ARGS(args, char_args);
01401    for (i = 0; i < args.argc; i++) {
01402       snprintf(varname, sizeof(varname), "ARG%d", i + 1);
01403       pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
01404    }
01405 
01406    /* Parse values, just like arguments */
01407    AST_STANDARD_APP_ARGS(values, char_values);
01408    for (i = 0; i < values.argc; i++) {
01409       snprintf(varname, sizeof(varname), "VAL%d", i + 1);
01410       pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
01411    }
01412 
01413    /* Additionally set the value as a whole (but push an empty string if value is NULL) */
01414    pbx_builtin_pushvar_helper(chan, "VALUE", S_OR(a->argv[4], ""));
01415    ast_str_substitute_variables(&sql, 0, chan, query->sql_write);
01416    ast_debug(1, "SQL is %s\n", ast_str_buffer(sql));
01417 
01418    chan = ast_channel_unref(chan);
01419 
01420    if (a->argc == 6 && !strcmp(a->argv[5], "exec")) {
01421       /* Execute the query */
01422       struct odbc_obj *obj = NULL;
01423       int dsn, executed = 0;
01424       SQLHSTMT stmt;
01425       SQLLEN rows = -1;
01426 
01427       for (dsn = 0; dsn < 5; dsn++) {
01428          if (ast_strlen_zero(query->writehandle[dsn])) {
01429             continue;
01430          }
01431          if (!(obj = ast_odbc_request_obj(query->writehandle[dsn], 0))) {
01432             continue;
01433          }
01434          if (!(stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)))) {
01435             ast_odbc_release_obj(obj);
01436             obj = NULL;
01437             continue;
01438          }
01439 
01440          SQLRowCount(stmt, &rows);
01441          SQLCloseCursor(stmt);
01442          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01443          ast_odbc_release_obj(obj);
01444          obj = NULL;
01445          ast_cli(a->fd, "Affected %d rows.  Query executed on handle %d [%s]\n", (int)rows, dsn, query->writehandle[dsn]);
01446          executed = 1;
01447          break;
01448       }
01449 
01450       if (!executed) {
01451          ast_cli(a->fd, "Failed to execute query.\n");
01452       }
01453    } else { /* No execution, just print out the resulting SQL */
01454       ast_cli(a->fd, "%s\n", ast_str_buffer(sql));
01455    }
01456    AST_RWLIST_UNLOCK(&queries);
01457    return CLI_SUCCESS;
01458 }

static int exec_odbcfinish ( struct ast_channel chan,
const char *  data 
) [static]

Definition at line 848 of file func_odbc.c.

References ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_channel_lock, ast_channel_unlock, ast_datastore_free(), and odbc_info.

Referenced by load_module().

00849 {
00850    struct ast_datastore *store;
00851 
00852    ast_channel_lock(chan);
00853    store = ast_channel_datastore_find(chan, &odbc_info, data);
00854    if (store) {
00855       ast_channel_datastore_remove(chan, store);
00856       ast_datastore_free(store);
00857    }
00858    ast_channel_unlock(chan);
00859    return 0;
00860 }

static int free_acf_query ( struct acf_odbc_query query  )  [static]

Definition at line 862 of file func_odbc.c.

References acf_odbc_query::acf, ast_free, ast_string_field_free_memory, ast_custom_function::name, acf_odbc_query::sql_insert, acf_odbc_query::sql_read, and acf_odbc_query::sql_write.

Referenced by init_acf_query(), reload(), and unload_module().

00863 {
00864    if (query) {
00865       if (query->acf) {
00866          if (query->acf->name)
00867             ast_free((char *)query->acf->name);
00868          ast_string_field_free_memory(query->acf);
00869          ast_free(query->acf);
00870       }
00871       ast_free(query->sql_read);
00872       ast_free(query->sql_write);
00873       ast_free(query->sql_insert);
00874       ast_free(query);
00875    }
00876    return 0;
00877 }

static SQLHSTMT generic_execute ( struct odbc_obj obj,
void *  data 
) [static]

Definition at line 168 of file func_odbc.c.

References ast_log, odbc_obj::con, LOG_WARNING, and NULL.

Referenced by acf_odbc_read(), acf_odbc_write(), cli_odbc_read(), and cli_odbc_write().

00169 {
00170    int res;
00171    char *sql = data;
00172    SQLHSTMT stmt;
00173 
00174    res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
00175    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00176       ast_log(LOG_WARNING, "SQL Alloc Handle failed (%d)!\n", res);
00177       return NULL;
00178    }
00179 
00180    res = SQLExecDirect(stmt, (unsigned char *)sql, SQL_NTS);
00181    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
00182       if (res == SQL_ERROR) {
00183          int i;
00184          SQLINTEGER nativeerror=0, numfields=0;
00185          SQLSMALLINT diagbytes=0;
00186          unsigned char state[10], diagnostic[256];
00187 
00188          SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00189          for (i = 0; i < numfields; i++) {
00190             SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00191             ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00192             if (i > 10) {
00193                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
00194                break;
00195             }
00196          }
00197       }
00198 
00199       ast_log(LOG_WARNING, "SQL Exec Direct failed (%d)![%s]\n", res, sql);
00200       SQLCloseCursor(stmt);
00201       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00202       return NULL;
00203    }
00204 
00205    return stmt;
00206 }

static int init_acf_query ( struct ast_config cfg,
char *  catg,
struct acf_odbc_query **  query 
) [static]

Definition at line 879 of file func_odbc.c.

References acf_odbc_read(), acf_odbc_write(), AST_APP_ARG, ast_asprintf, ast_calloc, ast_clear_flag, ast_copy_string(), AST_DECLARE_APP_ARGS, ast_false(), ast_log, ast_set_flag, AST_STANDARD_APP_ARGS, ast_strdup, ast_strdupa, ast_string_field_build, ast_string_field_init, ast_string_field_set, ast_strlen_zero, ast_variable_retrieve(), desc, dsn, free_acf_query(), LOG_ERROR, LOG_WARNING, NULL, OPT_ESCAPECOMMAS, OPT_MULTIROW, synopsis, and tmp().

Referenced by load_module(), and reload().

00880 {
00881    const char *tmp;
00882    const char *tmp2;
00883    int i;
00884 
00885    if (!cfg || !catg) {
00886       return EINVAL;
00887    }
00888 
00889    if (!(*query = ast_calloc(1, sizeof(**query)))) {
00890       return ENOMEM;
00891    }
00892 
00893    if (((tmp = ast_variable_retrieve(cfg, catg, "writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg, "dsn")))) {
00894       char *tmp2 = ast_strdupa(tmp);
00895       AST_DECLARE_APP_ARGS(writeconf,
00896          AST_APP_ARG(dsn)[5];
00897       );
00898       AST_STANDARD_APP_ARGS(writeconf, tmp2);
00899       for (i = 0; i < 5; i++) {
00900          if (!ast_strlen_zero(writeconf.dsn[i]))
00901             ast_copy_string((*query)->writehandle[i], writeconf.dsn[i], sizeof((*query)->writehandle[i]));
00902       }
00903    }
00904 
00905    if ((tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) {
00906       char *tmp2 = ast_strdupa(tmp);
00907       AST_DECLARE_APP_ARGS(readconf,
00908          AST_APP_ARG(dsn)[5];
00909       );
00910       AST_STANDARD_APP_ARGS(readconf, tmp2);
00911       for (i = 0; i < 5; i++) {
00912          if (!ast_strlen_zero(readconf.dsn[i]))
00913             ast_copy_string((*query)->readhandle[i], readconf.dsn[i], sizeof((*query)->readhandle[i]));
00914       }
00915    } else {
00916       /* If no separate readhandle, then use the writehandle for reading */
00917       for (i = 0; i < 5; i++) {
00918          if (!ast_strlen_zero((*query)->writehandle[i]))
00919             ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i], sizeof((*query)->readhandle[i]));
00920       }
00921    }
00922 
00923    if ((tmp = ast_variable_retrieve(cfg, catg, "readsql")) ||
00924          (tmp2 = ast_variable_retrieve(cfg, catg, "read"))) {
00925       if (!tmp) {
00926          ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s.  Please use 'readsql' instead.\n", catg);
00927          tmp = tmp2;
00928       }
00929       if (*tmp != '\0') { /* non-empty string */
00930          if (!((*query)->sql_read = ast_strdup(tmp))) {
00931             free_acf_query(*query);
00932             *query = NULL;
00933             return ENOMEM;
00934          }
00935       }
00936    }
00937 
00938    if ((*query)->sql_read && ast_strlen_zero((*query)->readhandle[0])) {
00939       free_acf_query(*query);
00940       *query = NULL;
00941       ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg);
00942       return EINVAL;
00943    }
00944 
00945    if ((tmp = ast_variable_retrieve(cfg, catg, "writesql")) ||
00946          (tmp2 = ast_variable_retrieve(cfg, catg, "write"))) {
00947       if (!tmp) {
00948          ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s.  Please use 'writesql' instead.\n", catg);
00949          tmp = tmp2;
00950       }
00951       if (*tmp != '\0') { /* non-empty string */
00952          if (!((*query)->sql_write = ast_strdup(tmp))) {
00953             free_acf_query(*query);
00954             *query = NULL;
00955             return ENOMEM;
00956          }
00957       }
00958    }
00959 
00960    if ((*query)->sql_write && ast_strlen_zero((*query)->writehandle[0])) {
00961       free_acf_query(*query);
00962       *query = NULL;
00963       ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg);
00964       return EINVAL;
00965    }
00966 
00967    if ((tmp = ast_variable_retrieve(cfg, catg, "insertsql"))) {
00968       if (*tmp != '\0') { /* non-empty string */
00969          if (!((*query)->sql_insert = ast_strdup(tmp))) {
00970             free_acf_query(*query);
00971             *query = NULL;
00972             return ENOMEM;
00973          }
00974       }
00975    }
00976 
00977    /* Allow escaping of embedded commas in fields to be turned off */
00978    ast_set_flag((*query), OPT_ESCAPECOMMAS);
00979    if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) {
00980       if (ast_false(tmp))
00981          ast_clear_flag((*query), OPT_ESCAPECOMMAS);
00982    }
00983 
00984    if ((tmp = ast_variable_retrieve(cfg, catg, "mode"))) {
00985       if (strcasecmp(tmp, "multirow") == 0)
00986          ast_set_flag((*query), OPT_MULTIROW);
00987       if ((tmp = ast_variable_retrieve(cfg, catg, "rowlimit")))
00988          sscanf(tmp, "%30d", &((*query)->rowlimit));
00989    }
00990 
00991    (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function));
00992    if (!(*query)->acf) {
00993       free_acf_query(*query);
00994       *query = NULL;
00995       return ENOMEM;
00996    }
00997    if (ast_string_field_init((*query)->acf, 128)) {
00998       free_acf_query(*query);
00999       *query = NULL;
01000       return ENOMEM;
01001    }
01002 
01003    if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
01004       if (ast_asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg) < 0) {
01005          (*query)->acf->name = NULL;
01006       }
01007    } else {
01008       if (ast_asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg) < 0) {
01009          (*query)->acf->name = NULL;
01010       }
01011    }
01012 
01013    if (!(*query)->acf->name) {
01014       free_acf_query(*query);
01015       *query = NULL;
01016       return ENOMEM;
01017    }
01018 
01019    if ((tmp = ast_variable_retrieve(cfg, catg, "syntax")) && !ast_strlen_zero(tmp)) {
01020       ast_string_field_build((*query)->acf, syntax, "%s(%s)", (*query)->acf->name, tmp);
01021    } else {
01022       ast_string_field_build((*query)->acf, syntax, "%s(<arg1>[...[,<argN>]])", (*query)->acf->name);
01023    }
01024 
01025    if (ast_strlen_zero((*query)->acf->syntax)) {
01026       free_acf_query(*query);
01027       *query = NULL;
01028       return ENOMEM;
01029    }
01030 
01031    if ((tmp = ast_variable_retrieve(cfg, catg, "synopsis")) && !ast_strlen_zero(tmp)) {
01032       ast_string_field_set((*query)->acf, synopsis, tmp);
01033    } else {
01034       ast_string_field_set((*query)->acf, synopsis, "Runs the referenced query with the specified arguments");
01035    }
01036 
01037    if (ast_strlen_zero((*query)->acf->synopsis)) {
01038       free_acf_query(*query);
01039       *query = NULL;
01040       return ENOMEM;
01041    }
01042 
01043    if ((*query)->sql_read && (*query)->sql_write) {
01044       ast_string_field_build((*query)->acf, desc,
01045                "Runs the following query, as defined in func_odbc.conf, performing\n"
01046                "substitution of the arguments into the query as specified by ${ARG1},\n"
01047                "${ARG2}, ... ${ARGn}.  When setting the function, the values are provided\n"
01048                "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
01049                "%s"
01050                "\nRead:\n%s\n\nWrite:\n%s%s%s",
01051                (*query)->sql_insert ?
01052                   "If the write query affects no rows, the insert query will be\n"
01053                   "performed.\n" : "",
01054                (*query)->sql_read,
01055                (*query)->sql_write,
01056                (*query)->sql_insert ? "\n\nInsert:\n" : "",
01057                (*query)->sql_insert ? (*query)->sql_insert : "");
01058    } else if ((*query)->sql_read) {
01059       ast_string_field_build((*query)->acf, desc,
01060                "Runs the following query, as defined in func_odbc.conf, performing\n"
01061                "substitution of the arguments into the query as specified by ${ARG1},\n"
01062                "${ARG2}, ... ${ARGn}.  This function may only be read, not set.\n\nSQL:\n%s",
01063                (*query)->sql_read);
01064    } else if ((*query)->sql_write) {
01065       ast_string_field_build((*query)->acf, desc,
01066                "Runs the following query, as defined in func_odbc.conf, performing\n"
01067                "substitution of the arguments into the query as specified by ${ARG1},\n"
01068                "${ARG2}, ... ${ARGn}.  The values are provided either in whole as\n"
01069                "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
01070                "This function may only be set.\n%s\nSQL:\n%s%s%s",
01071                (*query)->sql_insert ?
01072                   "If the write query affects no rows, the insert query will be\n"
01073                   "performed.\n" : "",
01074                (*query)->sql_write,
01075                (*query)->sql_insert ? "\n\nInsert:\n" : "",
01076                (*query)->sql_insert ? (*query)->sql_insert : "");
01077    } else {
01078       free_acf_query(*query);
01079       *query = NULL;
01080       ast_log(LOG_WARNING, "Section '%s' was found, but there was no SQL to execute.  Ignoring.\n", catg);
01081       return EINVAL;
01082    }
01083 
01084    if (ast_strlen_zero((*query)->acf->desc)) {
01085       free_acf_query(*query);
01086       *query = NULL;
01087       return ENOMEM;
01088    }
01089 
01090    if ((*query)->sql_read) {
01091       (*query)->acf->read = acf_odbc_read;
01092    }
01093 
01094    if ((*query)->sql_write) {
01095       (*query)->acf->write = acf_odbc_write;
01096    }
01097 
01098    return 0;
01099 }

static int load_module ( void   )  [static]

Definition at line 1465 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, LOG_NOTICE, and NULL.

01466 {
01467    int res = 0;
01468    struct ast_config *cfg;
01469    char *catg;
01470    struct ast_flags config_flags = { 0 };
01471 
01472    res |= ast_custom_function_register(&fetch_function);
01473    res |= ast_register_application_xml(app_odbcfinish, exec_odbcfinish);
01474    AST_RWLIST_WRLOCK(&queries);
01475 
01476    cfg = ast_config_load(config, config_flags);
01477    if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
01478       ast_log(LOG_NOTICE, "Unable to load config for func_odbc: %s\n", config);
01479       AST_RWLIST_UNLOCK(&queries);
01480       return AST_MODULE_LOAD_DECLINE;
01481    }
01482 
01483    for (catg = ast_category_browse(cfg, NULL);
01484         catg;
01485         catg = ast_category_browse(cfg, catg)) {
01486       struct acf_odbc_query *query = NULL;
01487       int err;
01488 
01489       if ((err = init_acf_query(cfg, catg, &query))) {
01490          if (err == ENOMEM)
01491             ast_log(LOG_ERROR, "Out of memory\n");
01492          else if (err == EINVAL)
01493             ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg);
01494          else
01495             ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err);
01496       } else {
01497          AST_RWLIST_INSERT_HEAD(&queries, query, list);
01498          ast_custom_function_register(query->acf);
01499       }
01500    }
01501 
01502    ast_config_destroy(cfg);
01503    res |= ast_custom_function_register(&escape_function);
01504    ast_cli_register_multiple(cli_func_odbc, ARRAY_LEN(cli_func_odbc));
01505 
01506    AST_RWLIST_UNLOCK(&queries);
01507    return res;
01508 }

static void odbc_datastore_free ( void *  data  )  [static]

Definition at line 150 of file func_odbc.c.

References ast_free, AST_LIST_HEAD_DESTROY, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, acf_odbc_query::list, and result.

Referenced by acf_odbc_read().

00151 {
00152    struct odbc_datastore *result = data;
00153    struct odbc_datastore_row *row;
00154 
00155    if (!result) {
00156       return;
00157    }
00158 
00159    AST_LIST_LOCK(result);
00160    while ((row = AST_LIST_REMOVE_HEAD(result, list))) {
00161       ast_free(row);
00162    }
00163    AST_LIST_UNLOCK(result);
00164    AST_LIST_HEAD_DESTROY(result);
00165    ast_free(result);
00166 }

static int reload ( void   )  [static]

Definition at line 1536 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, LOG_WARNING, and NULL.

01537 {
01538    int res = 0;
01539    struct ast_config *cfg;
01540    struct acf_odbc_query *oldquery;
01541    char *catg;
01542    struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
01543 
01544    cfg = ast_config_load(config, config_flags);
01545    if (cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID)
01546       return 0;
01547 
01548    AST_RWLIST_WRLOCK(&queries);
01549 
01550    while (!AST_RWLIST_EMPTY(&queries)) {
01551       oldquery = AST_RWLIST_REMOVE_HEAD(&queries, list);
01552       ast_custom_function_unregister(oldquery->acf);
01553       free_acf_query(oldquery);
01554    }
01555 
01556    if (!cfg) {
01557       ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
01558       goto reload_out;
01559    }
01560 
01561    for (catg = ast_category_browse(cfg, NULL);
01562         catg;
01563         catg = ast_category_browse(cfg, catg)) {
01564       struct acf_odbc_query *query = NULL;
01565 
01566       if (init_acf_query(cfg, catg, &query)) {
01567          ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
01568       } else {
01569          AST_RWLIST_INSERT_HEAD(&queries, query, list);
01570          ast_custom_function_register(query->acf);
01571       }
01572    }
01573 
01574    ast_config_destroy(cfg);
01575 reload_out:
01576    AST_RWLIST_UNLOCK(&queries);
01577    return res;
01578 }

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, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 1587 of file func_odbc.c.

char* app_odbcfinish = "ODBCFinish" [static]

Definition at line 846 of file func_odbc.c.

Referenced by load_module(), and unload_module().

Definition at line 1587 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 1460 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 145 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 146 of file func_odbc.c.

Referenced by acf_odbc_read().

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

Definition at line 101 of file func_odbc.c.

Initial value:

 {
   .name = "SQL_ESC",
   .read = acf_escape,
   .write = NULL,
}

Definition at line 796 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 840 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 122 of file func_odbc.c.

Referenced by acf_fetch(), acf_odbc_read(), and exec_odbcfinish().

int resultcount = 0 [static]

Definition at line 141 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 144 of file func_odbc.c.

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

Definition at line 143 of file func_odbc.c.


Generated on Thu Apr 16 06:31:21 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6