Mon Sep 20 2010 00:23:36

Asterisk developer's documentation


res_config_odbc.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * Copyright (C) 2004 - 2005 Anthony Minessale II <anthmct@yahoo.com>
00009  *
00010  * See http://www.asterisk.org for more information about
00011  * the Asterisk project. Please do not directly contact
00012  * any of the maintainers of this project for assistance;
00013  * the project provides a web site, mailing lists and IRC
00014  * channels for your use.
00015  *
00016  * This program is free software, distributed under the terms of
00017  * the GNU General Public License Version 2. See the LICENSE file
00018  * at the top of the source tree.
00019  */
00020 
00021 /*! \file
00022  *
00023  * \brief odbc+odbc plugin for portable configuration engine
00024  *
00025  * \author Mark Spencer <markster@digium.com>
00026  * \author Anthony Minessale II <anthmct@yahoo.com>
00027  *
00028  * \arg http://www.unixodbc.org
00029  */
00030 
00031 /*** MODULEINFO
00032    <depend>res_odbc</depend>
00033  ***/
00034 
00035 #include "asterisk.h"
00036 
00037 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 217647 $")
00038 
00039 #include "asterisk/file.h"
00040 #include "asterisk/channel.h"
00041 #include "asterisk/pbx.h"
00042 #include "asterisk/config.h"
00043 #include "asterisk/module.h"
00044 #include "asterisk/lock.h"
00045 #include "asterisk/res_odbc.h"
00046 #include "asterisk/utils.h"
00047 
00048 AST_THREADSTORAGE(sql_buf);
00049 
00050 struct custom_prepare_struct {
00051    const char *sql;
00052    const char *extra;
00053    va_list ap;
00054    unsigned long long skip;
00055 };
00056 
00057 static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data)
00058 {
00059    int res, x = 1, count = 0;
00060    struct custom_prepare_struct *cps = data;
00061    const char *newparam, *newval;
00062    SQLHSTMT stmt;
00063    va_list ap;
00064 
00065    va_copy(ap, cps->ap);
00066 
00067    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00068    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00069       ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00070       return NULL;
00071    }
00072 
00073    ast_debug(1, "Skip: %lld; SQL: %s\n", cps->skip, cps->sql);
00074 
00075    res = SQLPrepare(stmt, (unsigned char *)cps->sql, SQL_NTS);
00076    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00077       ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", cps->sql);
00078       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00079       return NULL;
00080    }
00081 
00082    while ((newparam = va_arg(ap, const char *))) {
00083       newval = va_arg(ap, const char *);
00084       if ((1LL << count++) & cps->skip) {
00085          ast_debug(1, "Skipping field '%s'='%s' (%llo/%llo)\n", newparam, newval, 1LL << (count - 1), cps->skip);
00086          continue;
00087       }
00088       ast_debug(1, "Parameter %d ('%s') = '%s'\n", x, newparam, newval);
00089       SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
00090    }
00091    va_end(ap);
00092 
00093    if (!ast_strlen_zero(cps->extra))
00094       SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(cps->extra), 0, (void *)cps->extra, 0, NULL);
00095    return stmt;
00096 }
00097 
00098 /*!
00099  * \brief Excute an SQL query and return ast_variable list
00100  * \param database
00101  * \param table
00102  * \param ap list containing one or more field/operator/value set.
00103  *
00104  * Select database and preform query on table, prepare the sql statement
00105  * Sub-in the values to the prepared statement and execute it. Return results
00106  * as a ast_variable list.
00107  *
00108  * \retval var on success
00109  * \retval NULL on failure
00110 */
00111 static struct ast_variable *realtime_odbc(const char *database, const char *table, va_list ap)
00112 {
00113    struct odbc_obj *obj;
00114    SQLHSTMT stmt;
00115    char sql[1024];
00116    char coltitle[256];
00117    char rowdata[2048];
00118    char *op;
00119    const char *newparam, *newval;
00120    char *stringp;
00121    char *chunk;
00122    SQLSMALLINT collen;
00123    int res;
00124    int x;
00125    struct ast_variable *var=NULL, *prev=NULL;
00126    SQLULEN colsize;
00127    SQLSMALLINT colcount=0;
00128    SQLSMALLINT datatype;
00129    SQLSMALLINT decimaldigits;
00130    SQLSMALLINT nullable;
00131    SQLLEN indicator;
00132    va_list aq;
00133    struct custom_prepare_struct cps = { .sql = sql };
00134 
00135    va_copy(cps.ap, ap);
00136    va_copy(aq, ap);
00137 
00138    if (!table)
00139       return NULL;
00140 
00141    obj = ast_odbc_request_obj(database, 0);
00142 
00143    if (!obj) {
00144       ast_log(LOG_ERROR, "No database handle available with the name of '%s' (check res_odbc.conf)\n", database);
00145       return NULL;
00146    }
00147 
00148    newparam = va_arg(aq, const char *);
00149    if (!newparam) {
00150       ast_odbc_release_obj(obj);
00151       return NULL;
00152    }
00153    newval = va_arg(aq, const char *);
00154    op = !strchr(newparam, ' ') ? " =" : "";
00155    snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, newparam, op,
00156       strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
00157    while((newparam = va_arg(aq, const char *))) {
00158       op = !strchr(newparam, ' ') ? " =" : "";
00159       snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", newparam, op,
00160          strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
00161       newval = va_arg(aq, const char *);
00162    }
00163    va_end(aq);
00164 
00165    stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
00166 
00167    if (!stmt) {
00168       ast_odbc_release_obj(obj);
00169       return NULL;
00170    }
00171 
00172    res = SQLNumResultCols(stmt, &colcount);
00173    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00174       ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
00175       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00176       ast_odbc_release_obj(obj);
00177       return NULL;
00178    }
00179 
00180    res = SQLFetch(stmt);
00181    if (res == SQL_NO_DATA) {
00182       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00183       ast_odbc_release_obj(obj);
00184       return NULL;
00185    }
00186    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00187       ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
00188       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00189       ast_odbc_release_obj(obj);
00190       return NULL;
00191    }
00192    for (x = 0; x < colcount; x++) {
00193       rowdata[0] = '\0';
00194       collen = sizeof(coltitle);
00195       res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
00196                &datatype, &colsize, &decimaldigits, &nullable);
00197       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00198          ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
00199          if (var)
00200             ast_variables_destroy(var);
00201          ast_odbc_release_obj(obj);
00202          return NULL;
00203       }
00204 
00205       indicator = 0;
00206       res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
00207       if (indicator == SQL_NULL_DATA)
00208          rowdata[0] = '\0';
00209       else if (ast_strlen_zero(rowdata)) {
00210          /* Because we encode the empty string for a NULL, we will encode
00211           * actual empty strings as a string containing a single whitespace. */
00212          ast_copy_string(rowdata, " ", sizeof(rowdata));
00213       }
00214 
00215       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00216          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
00217          if (var)
00218             ast_variables_destroy(var);
00219          ast_odbc_release_obj(obj);
00220          return NULL;
00221       }
00222       stringp = rowdata;
00223       while(stringp) {
00224          chunk = strsep(&stringp, ";");
00225          if (!ast_strlen_zero(ast_strip(chunk))) {
00226             if (prev) {
00227                prev->next = ast_variable_new(coltitle, chunk, "");
00228                if (prev->next)
00229                   prev = prev->next;
00230             } else 
00231                prev = var = ast_variable_new(coltitle, chunk, "");
00232          }
00233       }
00234    }
00235 
00236 
00237    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00238    ast_odbc_release_obj(obj);
00239    return var;
00240 }
00241 
00242 /*!
00243  * \brief Excute an Select query and return ast_config list
00244  * \param database
00245  * \param table
00246  * \param ap list containing one or more field/operator/value set.
00247  *
00248  * Select database and preform query on table, prepare the sql statement
00249  * Sub-in the values to the prepared statement and execute it. 
00250  * Execute this prepared query against several ODBC connected databases.
00251  * Return results as an ast_config variable.
00252  *
00253  * \retval var on success
00254  * \retval NULL on failure
00255 */
00256 static struct ast_config *realtime_multi_odbc(const char *database, const char *table, va_list ap)
00257 {
00258    struct odbc_obj *obj;
00259    SQLHSTMT stmt;
00260    char sql[1024];
00261    char coltitle[256];
00262    char rowdata[2048];
00263    const char *initfield=NULL;
00264    char *op;
00265    const char *newparam, *newval;
00266    char *stringp;
00267    char *chunk;
00268    SQLSMALLINT collen;
00269    int res;
00270    int x;
00271    struct ast_variable *var=NULL;
00272    struct ast_config *cfg=NULL;
00273    struct ast_category *cat=NULL;
00274    SQLULEN colsize;
00275    SQLSMALLINT colcount=0;
00276    SQLSMALLINT datatype;
00277    SQLSMALLINT decimaldigits;
00278    SQLSMALLINT nullable;
00279    SQLLEN indicator;
00280    struct custom_prepare_struct cps = { .sql = sql };
00281    va_list aq;
00282 
00283    va_copy(cps.ap, ap);
00284    va_copy(aq, ap);
00285 
00286    if (!table)
00287       return NULL;
00288 
00289    obj = ast_odbc_request_obj(database, 0);
00290    if (!obj)
00291       return NULL;
00292 
00293    newparam = va_arg(aq, const char *);
00294    if (!newparam)  {
00295       ast_odbc_release_obj(obj);
00296       return NULL;
00297    }
00298    initfield = ast_strdupa(newparam);
00299    if ((op = strchr(initfield, ' '))) 
00300       *op = '\0';
00301    newval = va_arg(aq, const char *);
00302    op = !strchr(newparam, ' ') ? " =" : "";
00303    snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, newparam, op,
00304       strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
00305    while((newparam = va_arg(aq, const char *))) {
00306       op = !strchr(newparam, ' ') ? " =" : "";
00307       snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", newparam, op,
00308          strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
00309       newval = va_arg(aq, const char *);
00310    }
00311    if (initfield)
00312       snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
00313    va_end(aq);
00314 
00315    stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
00316 
00317    if (!stmt) {
00318       ast_odbc_release_obj(obj);
00319       return NULL;
00320    }
00321 
00322    res = SQLNumResultCols(stmt, &colcount);
00323    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00324       ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
00325       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00326       ast_odbc_release_obj(obj);
00327       return NULL;
00328    }
00329 
00330    cfg = ast_config_new();
00331    if (!cfg) {
00332       ast_log(LOG_WARNING, "Out of memory!\n");
00333       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00334       ast_odbc_release_obj(obj);
00335       return NULL;
00336    }
00337 
00338    while ((res=SQLFetch(stmt)) != SQL_NO_DATA) {
00339       var = NULL;
00340       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00341          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
00342          continue;
00343       }
00344       cat = ast_category_new("","",99999);
00345       if (!cat) {
00346          ast_log(LOG_WARNING, "Out of memory!\n");
00347          continue;
00348       }
00349       for (x=0;x<colcount;x++) {
00350          rowdata[0] = '\0';
00351          collen = sizeof(coltitle);
00352          res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
00353                   &datatype, &colsize, &decimaldigits, &nullable);
00354          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00355             ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
00356             ast_category_destroy(cat);
00357             continue;
00358          }
00359 
00360          indicator = 0;
00361          res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
00362          if (indicator == SQL_NULL_DATA)
00363             continue;
00364 
00365          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00366             ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
00367             ast_category_destroy(cat);
00368             continue;
00369          }
00370          stringp = rowdata;
00371          while(stringp) {
00372             chunk = strsep(&stringp, ";");
00373             if (!ast_strlen_zero(ast_strip(chunk))) {
00374                if (initfield && !strcmp(initfield, coltitle))
00375                   ast_category_rename(cat, chunk);
00376                var = ast_variable_new(coltitle, chunk, "");
00377                ast_variable_append(cat, var);
00378             }
00379          }
00380       }
00381       ast_category_append(cfg, cat);
00382    }
00383 
00384    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00385    ast_odbc_release_obj(obj);
00386    return cfg;
00387 }
00388 
00389 /*!
00390  * \brief Excute an UPDATE query
00391  * \param database
00392  * \param table
00393  * \param keyfield where clause field
00394  * \param lookup value of field for where clause
00395  * \param ap list containing one or more field/value set(s).
00396  *
00397  * Update a database table, prepare the sql statement using keyfield and lookup
00398  * control the number of records to change. All values to be changed are stored in ap list.
00399  * Sub-in the values to the prepared statement and execute it.
00400  *
00401  * \retval number of rows affected
00402  * \retval -1 on failure
00403 */
00404 static int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
00405 {
00406    struct odbc_obj *obj;
00407    SQLHSTMT stmt;
00408    char sql[256];
00409    SQLLEN rowcount=0;
00410    const char *newparam, *newval;
00411    int res, count = 1;
00412    va_list aq;
00413    struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
00414    struct odbc_cache_tables *tableptr = ast_odbc_find_table(database, table);
00415    struct odbc_cache_columns *column;
00416 
00417    va_copy(cps.ap, ap);
00418    va_copy(aq, ap);
00419    
00420    if (!table) {
00421       ast_odbc_release_table(tableptr);
00422       return -1;
00423    }
00424 
00425    obj = ast_odbc_request_obj(database, 0);
00426    if (!obj) {
00427       ast_odbc_release_table(tableptr);
00428       return -1;
00429    }
00430 
00431    newparam = va_arg(aq, const char *);
00432    if (!newparam)  {
00433       ast_odbc_release_obj(obj);
00434       ast_odbc_release_table(tableptr);
00435       return -1;
00436    }
00437    newval = va_arg(aq, const char *);
00438 
00439    if (tableptr && !(column = ast_odbc_find_column(tableptr, newparam))) {
00440       ast_log(LOG_WARNING, "Key field '%s' does not exist in table '%s@%s'.  Update will fail\n", newparam, table, database);
00441    }
00442 
00443    snprintf(sql, sizeof(sql), "UPDATE %s SET %s=?", table, newparam);
00444    while((newparam = va_arg(aq, const char *))) {
00445       newval = va_arg(aq, const char *);
00446       if ((tableptr && (column = ast_odbc_find_column(tableptr, newparam))) || count > 63) {
00447          snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s=?", newparam);
00448       } else { /* the column does not exist in the table */
00449          cps.skip |= (1LL << count);
00450       }
00451       count++;
00452    }
00453    va_end(aq);
00454    snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield);
00455    ast_odbc_release_table(tableptr);
00456 
00457    stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
00458 
00459    if (!stmt) {
00460       ast_odbc_release_obj(obj);
00461       return -1;
00462    }
00463 
00464    res = SQLRowCount(stmt, &rowcount);
00465    SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00466    ast_odbc_release_obj(obj);
00467 
00468    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00469       ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
00470       return -1;
00471    }
00472 
00473    if (rowcount >= 0)
00474       return (int)rowcount;
00475 
00476    return -1;
00477 }
00478 
00479 struct update2_prepare_struct {
00480    const char *database;
00481    const char *table;
00482    va_list ap;
00483 };
00484 
00485 static SQLHSTMT update2_prepare(struct odbc_obj *obj, void *data)
00486 {
00487    int res, x = 1, first = 1;
00488    struct update2_prepare_struct *ups = data;
00489    const char *newparam, *newval;
00490    struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
00491    SQLHSTMT stmt;
00492    va_list ap;
00493    struct odbc_cache_tables *tableptr = ast_odbc_find_table(ups->database, ups->table);
00494    struct odbc_cache_columns *column;
00495 
00496    if (!sql) {
00497       if (tableptr) {
00498          ast_odbc_release_table(tableptr);
00499       }
00500       return NULL;
00501    }
00502 
00503    if (!tableptr) {
00504       ast_log(LOG_ERROR, "Could not retrieve metadata for table '%s@%s'.  Update will fail!\n", ups->table, ups->database);
00505       return NULL;
00506    }
00507 
00508    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00509    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00510       ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00511       ast_odbc_release_table(tableptr);
00512       return NULL;
00513    }
00514 
00515    ast_str_set(&sql, 0, "UPDATE %s SET ", ups->table);
00516 
00517    /* Start by finding the second set of parameters */
00518    va_copy(ap, ups->ap);
00519 
00520    while ((newparam = va_arg(ap, const char *))) {
00521       newval = va_arg(ap, const char *);
00522    }
00523 
00524    while ((newparam = va_arg(ap, const char *))) {
00525       newval = va_arg(ap, const char *);
00526       if ((column = ast_odbc_find_column(tableptr, newparam))) {
00527          ast_str_append(&sql, 0, "%s%s=? ", first ? "" : ", ", newparam);
00528          SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
00529          first = 0;
00530       } else {
00531          ast_log(LOG_NOTICE, "Not updating column '%s' in '%s@%s' because that column does not exist!\n", newparam, ups->table, ups->database);
00532       }
00533    }
00534    va_end(ap);
00535 
00536    /* Restart search, because we need to add the search parameters */
00537    va_copy(ap, ups->ap);
00538    ast_str_append(&sql, 0, "WHERE");
00539    first = 1;
00540 
00541    while ((newparam = va_arg(ap, const char *))) {
00542       newval = va_arg(ap, const char *);
00543       if (!(column = ast_odbc_find_column(tableptr, newparam))) {
00544          ast_log(LOG_ERROR, "One or more of the criteria columns '%s' on '%s@%s' for this update does not exist!\n", newparam, ups->table, ups->database);
00545          ast_odbc_release_table(tableptr);
00546          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00547          return NULL;
00548       }
00549       ast_str_append(&sql, 0, "%s %s=?", first ? "" : " AND", newparam);
00550       SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
00551       first = 0;
00552    }
00553    va_end(ap);
00554 
00555    /* Done with the table metadata */
00556    ast_odbc_release_table(tableptr);
00557 
00558    res = SQLPrepare(stmt, (unsigned char *)ast_str_buffer(sql), SQL_NTS);
00559    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00560       ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", ast_str_buffer(sql));
00561       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00562       return NULL;
00563    }
00564 
00565    return stmt;
00566 }
00567 
00568 /*!
00569  * \brief Execute an UPDATE query
00570  * \param database
00571  * \param table
00572  * \param ap list containing one or more field/value set(s).
00573  *
00574  * Update a database table, preparing the sql statement from a list of
00575  * key/value pairs specified in ap.  The lookup pairs are specified first
00576  * and are separated from the update pairs by a sentinel value.
00577  * Sub-in the values to the prepared statement and execute it.
00578  *
00579  * \retval number of rows affected
00580  * \retval -1 on failure
00581 */
00582 static int update2_odbc(const char *database, const char *table, va_list ap)
00583 {
00584    struct odbc_obj *obj;
00585    SQLHSTMT stmt;
00586    struct update2_prepare_struct ups = { .database = database, .table = table, };
00587    struct ast_str *sql;
00588    int res;
00589    SQLLEN rowcount = 0;
00590 
00591    va_copy(ups.ap, ap);
00592 
00593    if (!(obj = ast_odbc_request_obj(database, 0))) {
00594       return -1;
00595    }
00596 
00597    if (!(stmt = ast_odbc_prepare_and_execute(obj, update2_prepare, &ups))) {
00598       ast_odbc_release_obj(obj);
00599       return -1;
00600    }
00601 
00602    res = SQLRowCount(stmt, &rowcount);
00603    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00604    ast_odbc_release_obj(obj);
00605 
00606    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00607       /* Since only a single thread can access this memory, we can retrieve what would otherwise be lost. */
00608       sql = ast_str_thread_get(&sql_buf, 16);
00609       ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n", ast_str_buffer(sql));
00610       return -1;
00611    }
00612 
00613    if (rowcount >= 0) {
00614       return (int)rowcount;
00615    }
00616 
00617    return -1;
00618 }
00619 
00620 /*!
00621  * \brief Excute an INSERT query
00622  * \param database
00623  * \param table
00624  * \param ap list containing one or more field/value set(s)
00625  *
00626  * Insert a new record into database table, prepare the sql statement.
00627  * All values to be changed are stored in ap list.
00628  * Sub-in the values to the prepared statement and execute it.
00629  *
00630  * \retval number of rows affected
00631  * \retval -1 on failure
00632 */
00633 static int store_odbc(const char *database, const char *table, va_list ap)
00634 {
00635    struct odbc_obj *obj;
00636    SQLHSTMT stmt;
00637    char sql[256];
00638    char keys[256];
00639    char vals[256];
00640    SQLLEN rowcount=0;
00641    const char *newparam, *newval;
00642    int res;
00643    va_list aq;
00644    struct custom_prepare_struct cps = { .sql = sql, .extra = NULL };
00645 
00646    va_copy(cps.ap, ap);
00647    va_copy(aq, ap);
00648    
00649    if (!table)
00650       return -1;
00651 
00652    obj = ast_odbc_request_obj(database, 0);
00653    if (!obj)
00654       return -1;
00655 
00656    newparam = va_arg(aq, const char *);
00657    if (!newparam)  {
00658       ast_odbc_release_obj(obj);
00659       return -1;
00660    }
00661    newval = va_arg(aq, const char *);
00662    snprintf(keys, sizeof(keys), "%s", newparam);
00663    ast_copy_string(vals, "?", sizeof(vals));
00664    while ((newparam = va_arg(aq, const char *))) {
00665       snprintf(keys + strlen(keys), sizeof(keys) - strlen(keys), ", %s", newparam);
00666       snprintf(vals + strlen(vals), sizeof(vals) - strlen(vals), ", ?");
00667       newval = va_arg(aq, const char *);
00668    }
00669    va_end(aq);
00670    snprintf(sql, sizeof(sql), "INSERT INTO %s (%s) VALUES (%s)", table, keys, vals);
00671 
00672    stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
00673 
00674    if (!stmt) {
00675       ast_odbc_release_obj(obj);
00676       return -1;
00677    }
00678 
00679    res = SQLRowCount(stmt, &rowcount);
00680    SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00681    ast_odbc_release_obj(obj);
00682 
00683    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00684       ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
00685       return -1;
00686    }
00687 
00688    if (rowcount >= 0)
00689       return (int)rowcount;
00690 
00691    return -1;
00692 }
00693 
00694 /*!
00695  * \brief Excute an DELETE query
00696  * \param database
00697  * \param table
00698  * \param keyfield where clause field
00699  * \param lookup value of field for where clause
00700  * \param ap list containing one or more field/value set(s)
00701  *
00702  * Delete a row from a database table, prepare the sql statement using keyfield and lookup
00703  * control the number of records to change. Additional params to match rows are stored in ap list.
00704  * Sub-in the values to the prepared statement and execute it.
00705  *
00706  * \retval number of rows affected
00707  * \retval -1 on failure
00708 */
00709 static int destroy_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
00710 {
00711    struct odbc_obj *obj;
00712    SQLHSTMT stmt;
00713    char sql[256];
00714    SQLLEN rowcount=0;
00715    const char *newparam, *newval;
00716    int res;
00717    va_list aq;
00718    struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
00719 
00720    va_copy(cps.ap, ap);
00721    va_copy(aq, ap);
00722    
00723    if (!table)
00724       return -1;
00725 
00726    obj = ast_odbc_request_obj(database, 0);
00727    if (!obj)
00728       return -1;
00729 
00730    snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE ", table);
00731    while((newparam = va_arg(aq, const char *))) {
00732       snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=? AND ", newparam);
00733       newval = va_arg(aq, const char *);
00734    }
00735    va_end(aq);
00736    snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=?", keyfield);
00737 
00738    stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
00739 
00740    if (!stmt) {
00741       ast_odbc_release_obj(obj);
00742       return -1;
00743    }
00744 
00745    res = SQLRowCount(stmt, &rowcount);
00746    SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00747    ast_odbc_release_obj(obj);
00748 
00749    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00750       ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
00751       return -1;
00752    }
00753 
00754    if (rowcount >= 0)
00755       return (int)rowcount;
00756 
00757    return -1;
00758 }
00759 
00760 
00761 struct config_odbc_obj {
00762    char *sql;
00763    unsigned long cat_metric;
00764    char category[128];
00765    char var_name[128];
00766    char var_val[1024]; /* changed from 128 to 1024 via bug 8251 */
00767    SQLLEN err;
00768 };
00769 
00770 static SQLHSTMT config_odbc_prepare(struct odbc_obj *obj, void *data)
00771 {
00772    struct config_odbc_obj *q = data;
00773    SQLHSTMT sth;
00774    int res;
00775 
00776    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &sth);
00777    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00778       ast_verb(4, "Failure in AllocStatement %d\n", res);
00779       return NULL;
00780    }
00781 
00782    res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS);
00783    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00784       ast_verb(4, "Error in PREPARE %d\n", res);
00785       SQLFreeHandle(SQL_HANDLE_STMT, sth);
00786       return NULL;
00787    }
00788 
00789    SQLBindCol(sth, 1, SQL_C_ULONG, &q->cat_metric, sizeof(q->cat_metric), &q->err);
00790    SQLBindCol(sth, 2, SQL_C_CHAR, q->category, sizeof(q->category), &q->err);
00791    SQLBindCol(sth, 3, SQL_C_CHAR, q->var_name, sizeof(q->var_name), &q->err);
00792    SQLBindCol(sth, 4, SQL_C_CHAR, q->var_val, sizeof(q->var_val), &q->err);
00793 
00794    return sth;
00795 }
00796 
00797 static struct ast_config *config_odbc(const char *database, const char *table, const char *file, struct ast_config *cfg, struct ast_flags flags, const char *sugg_incl, const char *who_asked)
00798 {
00799    struct ast_variable *new_v;
00800    struct ast_category *cur_cat;
00801    int res = 0;
00802    struct odbc_obj *obj;
00803    char sqlbuf[1024] = "";
00804    char *sql = sqlbuf;
00805    size_t sqlleft = sizeof(sqlbuf);
00806    unsigned int last_cat_metric = 0;
00807    SQLSMALLINT rowcount = 0;
00808    SQLHSTMT stmt;
00809    char last[128] = "";
00810    struct config_odbc_obj q;
00811    struct ast_flags loader_flags = { 0 };
00812 
00813    memset(&q, 0, sizeof(q));
00814 
00815    if (!file || !strcmp (file, "res_config_odbc.conf"))
00816       return NULL;      /* cant configure myself with myself ! */
00817 
00818    obj = ast_odbc_request_obj(database, 0);
00819    if (!obj)
00820       return NULL;
00821 
00822    ast_build_string(&sql, &sqlleft, "SELECT cat_metric, category, var_name, var_val FROM %s ", table);
00823    ast_build_string(&sql, &sqlleft, "WHERE filename='%s' AND commented=0 ", file);
00824    ast_build_string(&sql, &sqlleft, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ");
00825    q.sql = sqlbuf;
00826 
00827    stmt = ast_odbc_prepare_and_execute(obj, config_odbc_prepare, &q);
00828 
00829    if (!stmt) {
00830       ast_log(LOG_WARNING, "SQL select error!\n[%s]\n\n", sql);
00831       ast_odbc_release_obj(obj);
00832       return NULL;
00833    }
00834 
00835    res = SQLNumResultCols(stmt, &rowcount);
00836 
00837    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00838       ast_log(LOG_WARNING, "SQL NumResultCols error!\n[%s]\n\n", sql);
00839       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00840       ast_odbc_release_obj(obj);
00841       return NULL;
00842    }
00843 
00844    if (!rowcount) {
00845       ast_log(LOG_NOTICE, "found nothing\n");
00846       ast_odbc_release_obj(obj);
00847       return cfg;
00848    }
00849 
00850    cur_cat = ast_config_get_current_category(cfg);
00851 
00852    while ((res = SQLFetch(stmt)) != SQL_NO_DATA) {
00853       if (!strcmp (q.var_name, "#include")) {
00854          if (!ast_config_internal_load(q.var_val, cfg, loader_flags, "", who_asked)) {
00855             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00856             ast_odbc_release_obj(obj);
00857             return NULL;
00858          }
00859          continue;
00860       } 
00861       if (strcmp(last, q.category) || last_cat_metric != q.cat_metric) {
00862          cur_cat = ast_category_new(q.category, "", 99999);
00863          if (!cur_cat) {
00864             ast_log(LOG_WARNING, "Out of memory!\n");
00865             break;
00866          }
00867          strcpy(last, q.category);
00868          last_cat_metric   = q.cat_metric;
00869          ast_category_append(cfg, cur_cat);
00870       }
00871 
00872       new_v = ast_variable_new(q.var_name, q.var_val, "");
00873       ast_variable_append(cur_cat, new_v);
00874    }
00875 
00876    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00877    ast_odbc_release_obj(obj);
00878    return cfg;
00879 }
00880 
00881 #define warn_length(col, size)   ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' is not long enough to contain realtime data (needs %d)\n", table, database, col->name, size)
00882 #define warn_type(col, type)  ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' is of the incorrect type (%d) to contain the required realtime data\n", table, database, col->name, col->type)
00883 
00884 static int require_odbc(const char *database, const char *table, va_list ap)
00885 {
00886    struct odbc_cache_tables *tableptr = ast_odbc_find_table(database, table);
00887    struct odbc_cache_columns *col;
00888    char *elm;
00889    int type, size;
00890 
00891    if (!tableptr) {
00892       return -1;
00893    }
00894 
00895    while ((elm = va_arg(ap, char *))) {
00896       type = va_arg(ap, require_type);
00897       size = va_arg(ap, int);
00898       /* Check if the field matches the criteria */
00899       AST_RWLIST_TRAVERSE(&tableptr->columns, col, list) {
00900          if (strcmp(col->name, elm) == 0) {
00901             /* Type check, first.  Some fields are more particular than others */
00902             switch (col->type) {
00903             case SQL_CHAR:
00904             case SQL_VARCHAR:
00905             case SQL_LONGVARCHAR:
00906 #ifdef HAVE_ODBC_WCHAR
00907             case SQL_WCHAR:
00908             case SQL_WVARCHAR:
00909             case SQL_WLONGVARCHAR:
00910 #endif
00911             case SQL_BINARY:
00912             case SQL_VARBINARY:
00913             case SQL_LONGVARBINARY:
00914             case SQL_GUID:
00915 #define CHECK_SIZE(n) \
00916                   if (col->size < n) {      \
00917                      warn_length(col, n);  \
00918                   }                         \
00919                   break;
00920                switch (type) {
00921                case RQ_UINTEGER1: CHECK_SIZE(3)  /*         255 */
00922                case RQ_INTEGER1:  CHECK_SIZE(4)  /*        -128 */
00923                case RQ_UINTEGER2: CHECK_SIZE(5)  /*       65535 */
00924                case RQ_INTEGER2:  CHECK_SIZE(6)  /*      -32768 */
00925                case RQ_UINTEGER3:                /*    16777215 */
00926                case RQ_INTEGER3:  CHECK_SIZE(8)  /*    -8388608 */
00927                case RQ_DATE:                     /*  2008-06-09 */
00928                case RQ_UINTEGER4: CHECK_SIZE(10) /*  4200000000 */
00929                case RQ_INTEGER4:  CHECK_SIZE(11) /* -2100000000 */
00930                case RQ_DATETIME:                 /* 2008-06-09 16:03:47 */
00931                case RQ_UINTEGER8: CHECK_SIZE(19) /* trust me    */
00932                case RQ_INTEGER8:  CHECK_SIZE(20) /* ditto       */
00933                case RQ_FLOAT:
00934                case RQ_CHAR:      CHECK_SIZE(size)
00935                }
00936 #undef CHECK_SIZE
00937                break;
00938             case SQL_TYPE_DATE:
00939                if (type != RQ_DATE) {
00940                   warn_type(col, type);
00941                }
00942                break;
00943             case SQL_TYPE_TIMESTAMP:
00944             case SQL_TIMESTAMP:
00945                if (type != RQ_DATE && type != RQ_DATETIME) {
00946                   warn_type(col, type);
00947                }
00948                break;
00949             case SQL_BIT:
00950                warn_length(col, size);
00951                break;
00952 #define WARN_TYPE_OR_LENGTH(n)   \
00953                   if (!ast_rq_is_int(type)) {  \
00954                      warn_type(col, type);    \
00955                   } else {                     \
00956                      warn_length(col, n);  \
00957                   }
00958             case SQL_TINYINT:
00959                if (type != RQ_UINTEGER1) {
00960                   WARN_TYPE_OR_LENGTH(size)
00961                }
00962                break;
00963             case SQL_C_STINYINT:
00964                if (type != RQ_INTEGER1) {
00965                   WARN_TYPE_OR_LENGTH(size)
00966                }
00967                break;
00968             case SQL_C_USHORT:
00969                if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_UINTEGER2) {
00970                   WARN_TYPE_OR_LENGTH(size)
00971                }
00972                break;
00973             case SQL_SMALLINT:
00974             case SQL_C_SSHORT:
00975                if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_INTEGER2) {
00976                   WARN_TYPE_OR_LENGTH(size)
00977                }
00978                break;
00979             case SQL_C_ULONG:
00980                if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
00981                   type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
00982                   type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
00983                   type != RQ_INTEGER4) {
00984                   WARN_TYPE_OR_LENGTH(size)
00985                }
00986                break;
00987             case SQL_INTEGER:
00988             case SQL_C_SLONG:
00989                if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
00990                   type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
00991                   type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
00992                   type != RQ_INTEGER4) {
00993                   WARN_TYPE_OR_LENGTH(size)
00994                }
00995                break;
00996             case SQL_C_UBIGINT:
00997                if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
00998                   type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
00999                   type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
01000                   type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
01001                   type != RQ_INTEGER8) {
01002                   WARN_TYPE_OR_LENGTH(size)
01003                }
01004                break;
01005             case SQL_BIGINT:
01006             case SQL_C_SBIGINT:
01007                if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
01008                   type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
01009                   type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
01010                   type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
01011                   type != RQ_INTEGER8) {
01012                   WARN_TYPE_OR_LENGTH(size)
01013                }
01014                break;
01015 #undef WARN_TYPE_OR_LENGTH
01016             case SQL_NUMERIC:
01017             case SQL_DECIMAL:
01018             case SQL_FLOAT:
01019             case SQL_REAL:
01020             case SQL_DOUBLE:
01021                if (!ast_rq_is_int(type) && type != RQ_FLOAT) {
01022                   warn_type(col, type);
01023                }
01024                break;
01025             default:
01026                ast_log(LOG_WARNING, "Realtime table %s@%s: column type (%d) unrecognized for column '%s'\n", table, database, col->type, elm);
01027             }
01028             break;
01029          }
01030       }
01031       if (!col) {
01032          ast_log(LOG_WARNING, "Realtime table %s@%s requires column '%s', but that column does not exist!\n", table, database, elm);
01033       }
01034    }
01035    va_end(ap);
01036    AST_RWLIST_UNLOCK(&tableptr->columns);
01037    return 0;
01038 }
01039 #undef warn_length
01040 #undef warn_type
01041 
01042 static struct ast_config_engine odbc_engine = {
01043    .name = "odbc",
01044    .load_func = config_odbc,
01045    .realtime_func = realtime_odbc,
01046    .realtime_multi_func = realtime_multi_odbc,
01047    .store_func = store_odbc,
01048    .destroy_func = destroy_odbc,
01049    .update_func = update_odbc,
01050    .update2_func = update2_odbc,
01051    .require_func = require_odbc,
01052    .unload_func = ast_odbc_clear_cache,
01053 };
01054 
01055 static int unload_module (void)
01056 {
01057    ast_config_engine_deregister(&odbc_engine);
01058 
01059    ast_verb(1, "res_config_odbc unloaded.\n");
01060    return 0;
01061 }
01062 
01063 static int load_module (void)
01064 {
01065    ast_config_engine_register(&odbc_engine);
01066    ast_verb(1, "res_config_odbc loaded.\n");
01067    return 0;
01068 }
01069 
01070 static int reload_module(void)
01071 {
01072    return 0;
01073 }
01074 
01075 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Realtime ODBC configuration",
01076       .load = load_module,
01077       .unload = unload_module,
01078       .reload = reload_module,
01079       );