Mon Aug 31 12:30:11 2015

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 - 2010, 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    <support_level>core</support_level>
00034  ***/
00035 
00036 #include "asterisk.h"
00037 
00038 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 414693 $")
00039 
00040 #include "asterisk/file.h"
00041 #include "asterisk/channel.h"
00042 #include "asterisk/pbx.h"
00043 #include "asterisk/config.h"
00044 #include "asterisk/module.h"
00045 #include "asterisk/lock.h"
00046 #include "asterisk/res_odbc.h"
00047 #include "asterisk/utils.h"
00048 #include "asterisk/stringfields.h"
00049 
00050 AST_THREADSTORAGE(sql_buf);
00051 AST_THREADSTORAGE(rowdata_buf);
00052 
00053 struct custom_prepare_struct {
00054    const char *sql;
00055    const char *extra;
00056    AST_DECLARE_STRING_FIELDS(
00057       AST_STRING_FIELD(encoding)[256];
00058    );
00059    va_list ap;
00060    unsigned long long skip;
00061 };
00062 
00063 #define ENCODE_CHUNK(buffer, s) \
00064    do { \
00065       char *eptr = buffer; \
00066       const char *vptr = s; \
00067       for (; *vptr && eptr < buffer + sizeof(buffer); vptr++) { \
00068          if (strchr("^;", *vptr)) { \
00069             /* We use ^XX, instead of %XX because '%' is a special character in SQL */ \
00070             snprintf(eptr, buffer + sizeof(buffer) - eptr, "^%02hhX", *vptr); \
00071             eptr += 3; \
00072          } else { \
00073             *eptr++ = *vptr; \
00074          } \
00075       } \
00076       if (eptr < buffer + sizeof(buffer)) { \
00077          *eptr = '\0'; \
00078       } else { \
00079          buffer[sizeof(buffer) - 1] = '\0'; \
00080       } \
00081    } while(0)
00082 
00083 static void decode_chunk(char *chunk)
00084 {
00085    for (; *chunk; chunk++) {
00086       if (*chunk == '^' && strchr("0123456789ABCDEF", chunk[1]) && strchr("0123456789ABCDEF", chunk[2])) {
00087          sscanf(chunk + 1, "%02hhX", (unsigned char *)chunk);
00088          memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
00089       }
00090    }
00091 }
00092 
00093 static inline int is_text(const struct odbc_cache_columns *column)
00094 {
00095    return column->type == SQL_CHAR || column->type == SQL_VARCHAR || column->type == SQL_LONGVARCHAR
00096       || column->type == SQL_WCHAR || column->type == SQL_WVARCHAR || column->type == SQL_WLONGVARCHAR;
00097 }
00098 
00099 static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data)
00100 {
00101    int res, x = 1, count = 0;
00102    struct custom_prepare_struct *cps = data;
00103    const char *newparam, *newval;
00104    char encodebuf[1024];
00105    SQLHSTMT stmt;
00106    va_list ap;
00107 
00108    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00109    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00110       ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00111       return NULL;
00112    }
00113 
00114    ast_debug(1, "Skip: %llu; SQL: %s\n", cps->skip, cps->sql);
00115 
00116    res = SQLPrepare(stmt, (unsigned char *)cps->sql, SQL_NTS);
00117    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00118       ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", cps->sql);
00119       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00120       return NULL;
00121    }
00122 
00123    va_copy(ap, cps->ap);
00124    while ((newparam = va_arg(ap, const char *))) {
00125       newval = va_arg(ap, const char *);
00126       if ((1LL << count++) & cps->skip) {
00127          ast_debug(1, "Skipping field '%s'='%s' (%llo/%llo)\n", newparam, newval, 1ULL << (count - 1), cps->skip);
00128          continue;
00129       }
00130       ast_debug(1, "Parameter %d ('%s') = '%s'\n", x, newparam, newval);
00131       if (strchr(newval, ';') || strchr(newval, '^')) {
00132          ENCODE_CHUNK(encodebuf, newval);
00133          ast_string_field_set(cps, encoding[x], encodebuf);
00134          newval = cps->encoding[x];
00135       }
00136       SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
00137    }
00138    va_end(ap);
00139 
00140    if (!ast_strlen_zero(cps->extra)) {
00141       const char *newval = cps->extra;
00142       if (strchr(newval, ';') || strchr(newval, '^')) {
00143          ENCODE_CHUNK(encodebuf, newval);
00144          ast_string_field_set(cps, encoding[x], encodebuf);
00145          newval = cps->encoding[x];
00146       } 
00147       SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
00148    }
00149 
00150    return stmt;
00151 }
00152 
00153 /*!
00154  * \brief Excute an SQL query and return ast_variable list
00155  * \param database
00156  * \param table
00157  * \param ap list containing one or more field/operator/value set.
00158  *
00159  * Select database and preform query on table, prepare the sql statement
00160  * Sub-in the values to the prepared statement and execute it. Return results
00161  * as a ast_variable list.
00162  *
00163  * \retval var on success
00164  * \retval NULL on failure
00165 */
00166 static struct ast_variable *realtime_odbc(const char *database, const char *table, va_list ap)
00167 {
00168    struct odbc_obj *obj;
00169    SQLHSTMT stmt;
00170    char sql[1024];
00171    char coltitle[256];
00172    struct ast_str *rowdata = ast_str_thread_get(&rowdata_buf, 128);
00173    char *op;
00174    const char *newparam;
00175    char *stringp;
00176    char *chunk;
00177    SQLSMALLINT collen;
00178    int res;
00179    int x;
00180    struct ast_variable *var=NULL, *prev=NULL;
00181    SQLULEN colsize;
00182    SQLSMALLINT colcount=0;
00183    SQLSMALLINT datatype;
00184    SQLSMALLINT decimaldigits;
00185    SQLSMALLINT nullable;
00186    SQLLEN indicator;
00187    va_list aq;
00188    struct custom_prepare_struct cps = { .sql = sql };
00189    struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
00190 
00191    if (!table) {
00192       return NULL;
00193    }
00194 
00195    obj = ast_odbc_request_obj2(database, connected_flag);
00196 
00197    if (!obj) {
00198       ast_log(LOG_ERROR, "No database handle available with the name of '%s' (check res_odbc.conf)\n", database);
00199       return NULL;
00200    }
00201 
00202    va_copy(aq, ap);
00203    newparam = va_arg(aq, const char *);
00204    if (!newparam) {
00205       va_end(aq);
00206       ast_odbc_release_obj(obj);
00207       return NULL;
00208    }
00209    va_arg(aq, const char *);
00210    op = !strchr(newparam, ' ') ? " =" : "";
00211    snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, newparam, op,
00212       strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
00213    while((newparam = va_arg(aq, const char *))) {
00214       op = !strchr(newparam, ' ') ? " =" : "";
00215       snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", newparam, op,
00216          strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
00217       va_arg(aq, const char *);
00218    }
00219    va_end(aq);
00220 
00221    if (ast_string_field_init(&cps, 256)) {
00222       ast_odbc_release_obj(obj);
00223       return NULL;
00224    }
00225    va_copy(cps.ap, ap);
00226    stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
00227    va_end(cps.ap);
00228    ast_string_field_free_memory(&cps);
00229 
00230    if (!stmt) {
00231       ast_odbc_release_obj(obj);
00232       return NULL;
00233    }
00234 
00235    res = SQLNumResultCols(stmt, &colcount);
00236    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00237       ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
00238       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00239       ast_odbc_release_obj(obj);
00240       return NULL;
00241    }
00242 
00243    res = SQLFetch(stmt);
00244    if (res == SQL_NO_DATA) {
00245       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00246       ast_odbc_release_obj(obj);
00247       return NULL;
00248    }
00249    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00250       ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
00251       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00252       ast_odbc_release_obj(obj);
00253       return NULL;
00254    }
00255    for (x = 0; x < colcount; x++) {
00256       colsize = 0;
00257       collen = sizeof(coltitle);
00258       res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
00259                &datatype, &colsize, &decimaldigits, &nullable);
00260       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00261          ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
00262          if (var)
00263             ast_variables_destroy(var);
00264          ast_odbc_release_obj(obj);
00265          return NULL;
00266       }
00267 
00268       ast_str_reset(rowdata);
00269       indicator = 0;
00270 
00271       res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata), ast_str_size(rowdata), &indicator);
00272       ast_str_update(rowdata);
00273       if (indicator == SQL_NULL_DATA) {
00274          ast_str_reset(rowdata);
00275       } else if (!ast_str_strlen(rowdata)) {
00276          /* Because we encode the empty string for a NULL, we will encode
00277           * actual empty strings as a string containing a single whitespace. */
00278          ast_str_set(&rowdata, -1, "%s", " ");
00279       } else if ((res == SQL_SUCCESS) || (res == SQL_SUCCESS_WITH_INFO)) {
00280          if (indicator != ast_str_strlen(rowdata)) {
00281             /* If the available space was not enough to contain the row data enlarge and read in the rest */
00282             ast_str_make_space(&rowdata, indicator + 1);
00283             res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata) + ast_str_strlen(rowdata),
00284                ast_str_size(rowdata) - ast_str_strlen(rowdata), &indicator);
00285             ast_str_update(rowdata);
00286          }
00287       }
00288 
00289       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00290          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
00291          if (var)
00292             ast_variables_destroy(var);
00293          ast_odbc_release_obj(obj);
00294          return NULL;
00295       }
00296 
00297       stringp = ast_str_buffer(rowdata);
00298       while (stringp) {
00299          chunk = strsep(&stringp, ";");
00300          if (!ast_strlen_zero(ast_strip(chunk))) {
00301             if (strchr(chunk, '^')) {
00302                decode_chunk(chunk);
00303             }
00304             if (prev) {
00305                prev->next = ast_variable_new(coltitle, chunk, "");
00306                if (prev->next) {
00307                   prev = prev->next;
00308                }
00309             } else {
00310                prev = var = ast_variable_new(coltitle, chunk, "");
00311             }
00312          }
00313       }
00314    }
00315 
00316    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00317    ast_odbc_release_obj(obj);
00318    return var;
00319 }
00320 
00321 /*!
00322  * \brief Excute an Select query and return ast_config list
00323  * \param database
00324  * \param table
00325  * \param ap list containing one or more field/operator/value set.
00326  *
00327  * Select database and preform query on table, prepare the sql statement
00328  * Sub-in the values to the prepared statement and execute it. 
00329  * Execute this prepared query against several ODBC connected databases.
00330  * Return results as an ast_config variable.
00331  *
00332  * \retval var on success
00333  * \retval NULL on failure
00334 */
00335 static struct ast_config *realtime_multi_odbc(const char *database, const char *table, va_list ap)
00336 {
00337    struct odbc_obj *obj;
00338    SQLHSTMT stmt;
00339    char sql[1024];
00340    char coltitle[256];
00341    struct ast_str *rowdata = ast_str_thread_get(&rowdata_buf, 128);
00342    const char *initfield;
00343    char *op;
00344    const char *newparam;
00345    char *stringp;
00346    char *chunk;
00347    SQLSMALLINT collen;
00348    int res;
00349    int x;
00350    struct ast_variable *var=NULL;
00351    struct ast_config *cfg=NULL;
00352    struct ast_category *cat=NULL;
00353    struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
00354    SQLULEN colsize;
00355    SQLSMALLINT colcount=0;
00356    SQLSMALLINT datatype;
00357    SQLSMALLINT decimaldigits;
00358    SQLSMALLINT nullable;
00359    SQLLEN indicator;
00360    struct custom_prepare_struct cps = { .sql = sql };
00361    va_list aq;
00362 
00363    if (!table) {
00364       return NULL;
00365    }
00366 
00367    obj = ast_odbc_request_obj2(database, connected_flag);
00368    if (!obj) {
00369       return NULL;
00370    }
00371 
00372    va_copy(aq, ap);
00373    newparam = va_arg(aq, const char *);
00374    if (!newparam)  {
00375       va_end(aq);
00376       ast_odbc_release_obj(obj);
00377       return NULL;
00378    }
00379 
00380    initfield = ast_strdupa(newparam);
00381    if ((op = strchr(initfield, ' '))) {
00382       *op = '\0';
00383    }
00384 
00385    va_arg(aq, const char *);
00386    op = !strchr(newparam, ' ') ? " =" : "";
00387    snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, newparam, op,
00388       strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
00389    while((newparam = va_arg(aq, const char *))) {
00390       op = !strchr(newparam, ' ') ? " =" : "";
00391       snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", newparam, op,
00392          strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
00393       va_arg(aq, const char *);
00394    }
00395    va_end(aq);
00396 
00397    snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
00398 
00399    if (ast_string_field_init(&cps, 256)) {
00400       ast_odbc_release_obj(obj);
00401       return NULL;
00402    }
00403    va_copy(cps.ap, ap);
00404    stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
00405    va_end(cps.ap);
00406    ast_string_field_free_memory(&cps);
00407 
00408    if (!stmt) {
00409       ast_odbc_release_obj(obj);
00410       return NULL;
00411    }
00412 
00413    res = SQLNumResultCols(stmt, &colcount);
00414    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00415       ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
00416       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00417       ast_odbc_release_obj(obj);
00418       return NULL;
00419    }
00420 
00421    cfg = ast_config_new();
00422    if (!cfg) {
00423       ast_log(LOG_WARNING, "Out of memory!\n");
00424       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00425       ast_odbc_release_obj(obj);
00426       return NULL;
00427    }
00428 
00429    while ((res=SQLFetch(stmt)) != SQL_NO_DATA) {
00430       var = NULL;
00431       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00432          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
00433          continue;
00434       }
00435       cat = ast_category_new("","",99999);
00436       if (!cat) {
00437          ast_log(LOG_WARNING, "Out of memory!\n");
00438          continue;
00439       }
00440       for (x=0;x<colcount;x++) {
00441          colsize = 0;
00442          collen = sizeof(coltitle);
00443          res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
00444                   &datatype, &colsize, &decimaldigits, &nullable);
00445          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00446             ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
00447             ast_category_destroy(cat);
00448             goto next_sql_fetch;
00449          }
00450 
00451          ast_str_reset(rowdata);
00452          indicator = 0;
00453 
00454          res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata), ast_str_size(rowdata), &indicator);
00455          ast_str_update(rowdata);
00456          if (indicator == SQL_NULL_DATA) {
00457             continue;
00458          }
00459 
00460          if ((res == SQL_SUCCESS) || (res == SQL_SUCCESS_WITH_INFO)) {
00461             if (indicator != ast_str_strlen(rowdata)) {
00462                /* If the available space was not enough to contain the row data enlarge and read in the rest */
00463                ast_str_make_space(&rowdata, indicator + 1);
00464                res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata) + ast_str_strlen(rowdata),
00465                   ast_str_size(rowdata) - ast_str_strlen(rowdata), &indicator);
00466                ast_str_update(rowdata);
00467             }
00468          }
00469 
00470          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00471             ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
00472             ast_category_destroy(cat);
00473             goto next_sql_fetch;
00474          }
00475          stringp = ast_str_buffer(rowdata);
00476          while (stringp) {
00477             chunk = strsep(&stringp, ";");
00478             if (!ast_strlen_zero(ast_strip(chunk))) {
00479                if (strchr(chunk, '^')) {
00480                   decode_chunk(chunk);
00481                }
00482                if (!strcmp(initfield, coltitle)) {
00483                   ast_category_rename(cat, chunk);
00484                }
00485                var = ast_variable_new(coltitle, chunk, "");
00486                ast_variable_append(cat, var);
00487             }
00488          }
00489       }
00490       ast_category_append(cfg, cat);
00491 next_sql_fetch:;
00492    }
00493 
00494    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00495    ast_odbc_release_obj(obj);
00496    return cfg;
00497 }
00498 
00499 /*!
00500  * \brief Excute an UPDATE query
00501  * \param database
00502  * \param table
00503  * \param keyfield where clause field
00504  * \param lookup value of field for where clause
00505  * \param ap list containing one or more field/value set(s).
00506  *
00507  * Update a database table, prepare the sql statement using keyfield and lookup
00508  * control the number of records to change. All values to be changed are stored in ap list.
00509  * Sub-in the values to the prepared statement and execute it.
00510  *
00511  * \retval number of rows affected
00512  * \retval -1 on failure
00513 */
00514 static int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
00515 {
00516    struct odbc_obj *obj;
00517    SQLHSTMT stmt;
00518    char sql[256];
00519    SQLLEN rowcount=0;
00520    const char *newparam, *newval;
00521    int res, count = 0, paramcount = 0;
00522    va_list aq;
00523    struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
00524    struct odbc_cache_tables *tableptr;
00525    struct odbc_cache_columns *column = NULL;
00526    struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
00527 
00528    if (!table || !keyfield) {
00529       return -1;
00530    }
00531 
00532    tableptr = ast_odbc_find_table(database, table);
00533    if (!(obj = ast_odbc_request_obj2(database, connected_flag))) {
00534       ast_odbc_release_table(tableptr);
00535       return -1;
00536    }
00537 
00538    if (tableptr && !ast_odbc_find_column(tableptr, keyfield)) {
00539       ast_log(LOG_WARNING, "Key field '%s' does not exist in table '%s@%s'.  Update will fail\n", keyfield, table, database);
00540    }
00541 
00542    va_copy(aq, ap);
00543 
00544    snprintf(sql, sizeof(sql), "UPDATE %s SET ", table);
00545    while((newparam = va_arg(aq, const char *))) {
00546       newval = va_arg(aq, const char *);
00547       if ((tableptr && (column = ast_odbc_find_column(tableptr, newparam))) || count >= 64) {
00548          if (paramcount++) {
00549             snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", ");
00550          }
00551          /* NULL test for non-text columns */
00552          if (count < 64 && ast_strlen_zero(newval) && column->nullable && !is_text(column) && !ast_odbc_allow_empty_string_in_nontext(obj)) {
00553             snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=NULL", newparam);
00554             cps.skip |= (1LL << count);
00555          } else {
00556             /* Value is not an empty string, or column accepts empty strings, or we couldn't fit any more into cps.skip (count >= 64 ?!). */
00557             snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=?", newparam);
00558          }
00559       } else { /* the column does not exist in the table */
00560          cps.skip |= (1LL << count);
00561       }
00562       ++count;
00563    }
00564    va_end(aq);
00565    snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield);
00566    ast_odbc_release_table(tableptr);
00567 
00568    if (ast_string_field_init(&cps, 256)) {
00569       ast_odbc_release_obj(obj);
00570       return -1;
00571    }
00572    va_copy(cps.ap, ap);
00573    stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
00574    va_end(cps.ap);
00575    ast_string_field_free_memory(&cps);
00576 
00577    if (!stmt) {
00578       ast_odbc_release_obj(obj);
00579       return -1;
00580    }
00581 
00582    res = SQLRowCount(stmt, &rowcount);
00583    SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00584    ast_odbc_release_obj(obj);
00585 
00586    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00587       ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
00588       return -1;
00589    }
00590 
00591    if (rowcount >= 0) {
00592       return (int) rowcount;
00593    }
00594 
00595    return -1;
00596 }
00597 
00598 struct update2_prepare_struct {
00599    const char *database;
00600    const char *table;
00601    va_list ap;
00602 };
00603 
00604 static SQLHSTMT update2_prepare(struct odbc_obj *obj, void *data)
00605 {
00606    int res, x = 1, first = 1;
00607    struct update2_prepare_struct *ups = data;
00608    const char *newparam, *newval;
00609    struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
00610    SQLHSTMT stmt;
00611    va_list ap;
00612    struct odbc_cache_tables *tableptr = ast_odbc_find_table(ups->database, ups->table);
00613 
00614    if (!sql) {
00615       if (tableptr) {
00616          ast_odbc_release_table(tableptr);
00617       }
00618       return NULL;
00619    }
00620 
00621    if (!tableptr) {
00622       ast_log(LOG_ERROR, "Could not retrieve metadata for table '%s@%s'.  Update will fail!\n", ups->table, ups->database);
00623       return NULL;
00624    }
00625 
00626    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00627    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00628       ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00629       ast_odbc_release_table(tableptr);
00630       return NULL;
00631    }
00632 
00633    ast_str_set(&sql, 0, "UPDATE %s SET ", ups->table);
00634 
00635    /* Start by finding the second set of parameters */
00636    va_copy(ap, ups->ap);
00637 
00638    while ((newparam = va_arg(ap, const char *))) {
00639       newval = va_arg(ap, const char *);
00640    }
00641 
00642    while ((newparam = va_arg(ap, const char *))) {
00643       newval = va_arg(ap, const char *);
00644       if (ast_odbc_find_column(tableptr, newparam)) {
00645          ast_str_append(&sql, 0, "%s%s=? ", first ? "" : ", ", newparam);
00646          SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
00647          first = 0;
00648       } else {
00649          ast_log(LOG_NOTICE, "Not updating column '%s' in '%s@%s' because that column does not exist!\n", newparam, ups->table, ups->database);
00650       }
00651    }
00652    va_end(ap);
00653 
00654    ast_str_append(&sql, 0, "WHERE");
00655    first = 1;
00656 
00657    /* Restart search, because we need to add the search parameters */
00658    va_copy(ap, ups->ap);
00659 
00660    while ((newparam = va_arg(ap, const char *))) {
00661       newval = va_arg(ap, const char *);
00662       if (!ast_odbc_find_column(tableptr, newparam)) {
00663          va_end(ap);
00664          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);
00665          ast_odbc_release_table(tableptr);
00666          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00667          return NULL;
00668       }
00669       ast_str_append(&sql, 0, "%s %s=?", first ? "" : " AND", newparam);
00670       SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
00671       first = 0;
00672    }
00673    va_end(ap);
00674 
00675    /* Done with the table metadata */
00676    ast_odbc_release_table(tableptr);
00677 
00678    res = SQLPrepare(stmt, (unsigned char *)ast_str_buffer(sql), SQL_NTS);
00679    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00680       ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", ast_str_buffer(sql));
00681       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00682       return NULL;
00683    }
00684 
00685    return stmt;
00686 }
00687 
00688 /*!
00689  * \brief Execute an UPDATE query
00690  * \param database
00691  * \param table
00692  * \param ap list containing one or more field/value set(s).
00693  *
00694  * Update a database table, preparing the sql statement from a list of
00695  * key/value pairs specified in ap.  The lookup pairs are specified first
00696  * and are separated from the update pairs by a sentinel value.
00697  * Sub-in the values to the prepared statement and execute it.
00698  *
00699  * \retval number of rows affected
00700  * \retval -1 on failure
00701 */
00702 static int update2_odbc(const char *database, const char *table, va_list ap)
00703 {
00704    struct odbc_obj *obj;
00705    SQLHSTMT stmt;
00706    struct update2_prepare_struct ups = { .database = database, .table = table, };
00707    struct ast_str *sql;
00708    int res;
00709    SQLLEN rowcount = 0;
00710 
00711    if (!(obj = ast_odbc_request_obj(database, 0))) {
00712       return -1;
00713    }
00714 
00715    va_copy(ups.ap, ap);
00716    if (!(stmt = ast_odbc_prepare_and_execute(obj, update2_prepare, &ups))) {
00717       va_end(ups.ap);
00718       ast_odbc_release_obj(obj);
00719       return -1;
00720    }
00721    va_end(ups.ap);
00722 
00723    res = SQLRowCount(stmt, &rowcount);
00724    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00725    ast_odbc_release_obj(obj);
00726 
00727    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00728       /* Since only a single thread can access this memory, we can retrieve what would otherwise be lost. */
00729       sql = ast_str_thread_get(&sql_buf, 16);
00730       ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n", ast_str_buffer(sql));
00731       return -1;
00732    }
00733 
00734    if (rowcount >= 0) {
00735       return (int)rowcount;
00736    }
00737 
00738    return -1;
00739 }
00740 
00741 /*!
00742  * \brief Excute an INSERT query
00743  * \param database
00744  * \param table
00745  * \param ap list containing one or more field/value set(s)
00746  *
00747  * Insert a new record into database table, prepare the sql statement.
00748  * All values to be changed are stored in ap list.
00749  * Sub-in the values to the prepared statement and execute it.
00750  *
00751  * \retval number of rows affected
00752  * \retval -1 on failure
00753 */
00754 static int store_odbc(const char *database, const char *table, va_list ap)
00755 {
00756    struct odbc_obj *obj;
00757    SQLHSTMT stmt;
00758    char sql[256];
00759    char keys[256];
00760    char vals[256];
00761    SQLLEN rowcount=0;
00762    const char *newparam;
00763    int res;
00764    va_list aq;
00765    struct custom_prepare_struct cps = { .sql = sql, .extra = NULL };
00766    struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
00767 
00768    if (!table) {
00769       return -1;
00770    }
00771 
00772    obj = ast_odbc_request_obj2(database, connected_flag);
00773    if (!obj) {
00774       return -1;
00775    }
00776 
00777    va_copy(aq, ap);
00778 
00779    newparam = va_arg(aq, const char *);
00780    if (!newparam)  {
00781       va_end(aq);
00782       ast_odbc_release_obj(obj);
00783       return -1;
00784    }
00785    va_arg(aq, const char *);
00786    snprintf(keys, sizeof(keys), "%s", newparam);
00787    ast_copy_string(vals, "?", sizeof(vals));
00788    while ((newparam = va_arg(aq, const char *))) {
00789       snprintf(keys + strlen(keys), sizeof(keys) - strlen(keys), ", %s", newparam);
00790       snprintf(vals + strlen(vals), sizeof(vals) - strlen(vals), ", ?");
00791       va_arg(aq, const char *);
00792    }
00793    va_end(aq);
00794    snprintf(sql, sizeof(sql), "INSERT INTO %s (%s) VALUES (%s)", table, keys, vals);
00795 
00796    if (ast_string_field_init(&cps, 256)) {
00797       ast_odbc_release_obj(obj);
00798       return -1;
00799    }
00800    va_copy(cps.ap, ap);
00801    stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
00802    va_end(cps.ap);
00803    ast_string_field_free_memory(&cps);
00804 
00805    if (!stmt) {
00806       ast_odbc_release_obj(obj);
00807       return -1;
00808    }
00809 
00810    res = SQLRowCount(stmt, &rowcount);
00811    SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00812    ast_odbc_release_obj(obj);
00813 
00814    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00815       ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
00816       return -1;
00817    }
00818 
00819    if (rowcount >= 0)
00820       return (int)rowcount;
00821 
00822    return -1;
00823 }
00824 
00825 /*!
00826  * \brief Excute an DELETE query
00827  * \param database
00828  * \param table
00829  * \param keyfield where clause field
00830  * \param lookup value of field for where clause
00831  * \param ap list containing one or more field/value set(s)
00832  *
00833  * Delete a row from a database table, prepare the sql statement using keyfield and lookup
00834  * control the number of records to change. Additional params to match rows are stored in ap list.
00835  * Sub-in the values to the prepared statement and execute it.
00836  *
00837  * \retval number of rows affected
00838  * \retval -1 on failure
00839 */
00840 static int destroy_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
00841 {
00842    struct odbc_obj *obj;
00843    SQLHSTMT stmt;
00844    char sql[256];
00845    SQLLEN rowcount=0;
00846    const char *newparam;
00847    int res;
00848    va_list aq;
00849    struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
00850    struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
00851 
00852    if (!table) {
00853       return -1;
00854    }
00855 
00856    obj = ast_odbc_request_obj2(database, connected_flag);
00857    if (!obj) {
00858       return -1;
00859    }
00860 
00861    snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE ", table);
00862 
00863    va_copy(aq, ap);
00864    while((newparam = va_arg(aq, const char *))) {
00865       snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=? AND ", newparam);
00866       va_arg(aq, const char *);
00867    }
00868    va_end(aq);
00869    snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=?", keyfield);
00870 
00871    if (ast_string_field_init(&cps, 256)) {
00872       ast_odbc_release_obj(obj);
00873       return -1;
00874    }
00875    va_copy(cps.ap, ap);
00876    stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
00877    va_end(cps.ap);
00878    ast_string_field_free_memory(&cps);
00879 
00880    if (!stmt) {
00881       ast_odbc_release_obj(obj);
00882       return -1;
00883    }
00884 
00885    res = SQLRowCount(stmt, &rowcount);
00886    SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00887    ast_odbc_release_obj(obj);
00888 
00889    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00890       ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
00891       return -1;
00892    }
00893 
00894    if (rowcount >= 0)
00895       return (int)rowcount;
00896 
00897    return -1;
00898 }
00899 
00900 struct config_odbc_obj {
00901    char *sql;
00902    unsigned long cat_metric;
00903    char category[128];
00904    char var_name[128];
00905    char *var_val;
00906    unsigned long var_val_size;
00907    SQLLEN err;
00908 };
00909 
00910 
00911 static SQLHSTMT length_determination_odbc_prepare(struct odbc_obj *obj, void *data)
00912 {
00913    struct config_odbc_obj *q = data;
00914    SQLHSTMT sth;
00915    int res;
00916 
00917    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &sth);
00918    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00919       ast_verb(4, "Failure in AllocStatement %d\n", res);
00920       return NULL;
00921    }
00922 
00923    res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS);
00924    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00925       ast_verb(4, "Error in PREPARE %d\n", res);
00926       SQLFreeHandle(SQL_HANDLE_STMT, sth);
00927       return NULL;
00928    }
00929 
00930    SQLBindCol(sth, 1, SQL_C_ULONG, &q->var_val_size, sizeof(q->var_val_size), &q->err);
00931 
00932    return sth;
00933 }
00934 
00935 static SQLHSTMT config_odbc_prepare(struct odbc_obj *obj, void *data)
00936 {
00937    struct config_odbc_obj *q = data;
00938    SQLHSTMT sth;
00939    int res;
00940 
00941    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &sth);
00942    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00943       ast_verb(4, "Failure in AllocStatement %d\n", res);
00944       return NULL;
00945    }
00946 
00947    res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS);
00948    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00949       ast_verb(4, "Error in PREPARE %d\n", res);
00950       SQLFreeHandle(SQL_HANDLE_STMT, sth);
00951       return NULL;
00952    }
00953 
00954    SQLBindCol(sth, 1, SQL_C_ULONG, &q->cat_metric, sizeof(q->cat_metric), &q->err);
00955    SQLBindCol(sth, 2, SQL_C_CHAR, q->category, sizeof(q->category), &q->err);
00956    SQLBindCol(sth, 3, SQL_C_CHAR, q->var_name, sizeof(q->var_name), &q->err);
00957    SQLBindCol(sth, 4, SQL_C_CHAR, q->var_val, q->var_val_size, &q->err);
00958 
00959    return sth;
00960 }
00961 
00962 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)
00963 {
00964    struct ast_variable *new_v;
00965    struct ast_category *cur_cat;
00966    int res = 0;
00967    struct odbc_obj *obj;
00968    char sqlbuf[1024] = "";
00969    char *sql = sqlbuf;
00970    size_t sqlleft = sizeof(sqlbuf);
00971    unsigned int last_cat_metric = 0;
00972    SQLSMALLINT rowcount = 0;
00973    SQLHSTMT stmt;
00974    char last[128] = "";
00975    struct config_odbc_obj q;
00976    struct ast_flags loader_flags = { 0 };
00977    struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
00978 
00979    memset(&q, 0, sizeof(q));
00980 
00981    if (!file || !strcmp (file, "res_config_odbc.conf"))
00982       return NULL;      /* cant configure myself with myself ! */
00983 
00984    obj = ast_odbc_request_obj2(database, connected_flag);
00985    if (!obj)
00986       return NULL;
00987 
00988    q.sql = sqlbuf;
00989 
00990    ast_build_string(&sql, &sqlleft, "SELECT MAX(LENGTH(var_val)) FROM %s WHERE filename='%s'", table, file);
00991 
00992    stmt = ast_odbc_prepare_and_execute(obj, length_determination_odbc_prepare, &q);
00993 
00994    if (!stmt) {
00995       ast_log(LOG_WARNING, "SQL select error!\n[%s]\n\n", sql);
00996       ast_odbc_release_obj(obj);
00997       return NULL;
00998    }
00999 
01000    res = SQLNumResultCols(stmt, &rowcount);
01001 
01002    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01003       ast_log(LOG_WARNING, "SQL NumResultCols error!\n[%s]\n\n", sql);
01004       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01005       ast_odbc_release_obj(obj);
01006       return NULL;
01007    }
01008 
01009    if (!rowcount) {
01010       ast_log(LOG_NOTICE, "found nothing\n");
01011       ast_odbc_release_obj(obj);
01012       return cfg;
01013    }
01014 
01015    /* There will be only one result for this, the maximum length of a variable value */
01016    if (SQLFetch(stmt) == SQL_NO_DATA) {
01017       ast_log(LOG_NOTICE, "Failed to determine maximum length of a configuration value\n");
01018       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01019       ast_odbc_release_obj(obj);
01020       return NULL;
01021    }
01022 
01023    /* Reset stuff to a fresh state for the actual query which will retrieve all configuration */
01024    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01025    sql = sqlbuf;
01026    sqlleft = sizeof(sqlbuf);
01027 
01028    ast_build_string(&sql, &sqlleft, "SELECT cat_metric, category, var_name, var_val FROM %s ", table);
01029    ast_build_string(&sql, &sqlleft, "WHERE filename='%s' AND commented=0 ", file);
01030    ast_build_string(&sql, &sqlleft, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ");
01031 
01032    q.var_val_size += 1;
01033    q.var_val = ast_malloc(q.var_val_size);
01034    if (!q.var_val) {
01035       ast_log(LOG_WARNING, "Could not create buffer for reading in configuration values for '%s'\n", file);
01036       ast_odbc_release_obj(obj);
01037       return NULL;
01038    }
01039 
01040    stmt = ast_odbc_prepare_and_execute(obj, config_odbc_prepare, &q);
01041 
01042    if (!stmt) {
01043       ast_log(LOG_WARNING, "SQL select error!\n[%s]\n\n", sql);
01044       ast_odbc_release_obj(obj);
01045       ast_free(q.var_val);
01046       return NULL;
01047    }
01048 
01049    res = SQLNumResultCols(stmt, &rowcount);
01050 
01051    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01052       ast_log(LOG_WARNING, "SQL NumResultCols error!\n[%s]\n\n", sql);
01053       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01054       ast_odbc_release_obj(obj);
01055       ast_free(q.var_val);
01056       return NULL;
01057    }
01058 
01059    if (!rowcount) {
01060       ast_log(LOG_NOTICE, "found nothing\n");
01061       ast_odbc_release_obj(obj);
01062       ast_free(q.var_val);
01063       return cfg;
01064    }
01065 
01066    cur_cat = ast_config_get_current_category(cfg);
01067 
01068    while ((res = SQLFetch(stmt)) != SQL_NO_DATA) {
01069       if (!strcmp (q.var_name, "#include")) {
01070          if (!ast_config_internal_load(q.var_val, cfg, loader_flags, "", who_asked)) {
01071             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01072             ast_odbc_release_obj(obj);
01073             ast_free(q.var_val);
01074             return NULL;
01075          }
01076          continue;
01077       } 
01078       if (strcmp(last, q.category) || last_cat_metric != q.cat_metric) {
01079          cur_cat = ast_category_new(q.category, "", 99999);
01080          if (!cur_cat) {
01081             ast_log(LOG_WARNING, "Out of memory!\n");
01082             break;
01083          }
01084          strcpy(last, q.category);
01085          last_cat_metric   = q.cat_metric;
01086          ast_category_append(cfg, cur_cat);
01087       }
01088 
01089       new_v = ast_variable_new(q.var_name, q.var_val, "");
01090       ast_variable_append(cur_cat, new_v);
01091    }
01092 
01093    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01094    ast_odbc_release_obj(obj);
01095    ast_free(q.var_val);
01096    return cfg;
01097 }
01098 
01099 #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)
01100 #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)
01101 
01102 static int require_odbc(const char *database, const char *table, va_list ap)
01103 {
01104    struct odbc_cache_tables *tableptr = ast_odbc_find_table(database, table);
01105    struct odbc_cache_columns *col;
01106    char *elm;
01107    int type, size;
01108 
01109    if (!tableptr) {
01110       return -1;
01111    }
01112 
01113    while ((elm = va_arg(ap, char *))) {
01114       type = va_arg(ap, require_type);
01115       size = va_arg(ap, int);
01116       /* Check if the field matches the criteria */
01117       AST_RWLIST_TRAVERSE(&tableptr->columns, col, list) {
01118          if (strcmp(col->name, elm) == 0) {
01119             /* Type check, first.  Some fields are more particular than others */
01120             switch (col->type) {
01121             case SQL_CHAR:
01122             case SQL_VARCHAR:
01123             case SQL_LONGVARCHAR:
01124 #ifdef HAVE_ODBC_WCHAR
01125             case SQL_WCHAR:
01126             case SQL_WVARCHAR:
01127             case SQL_WLONGVARCHAR:
01128 #endif
01129             case SQL_BINARY:
01130             case SQL_VARBINARY:
01131             case SQL_LONGVARBINARY:
01132             case SQL_GUID:
01133 #define CHECK_SIZE(n) \
01134                   if (col->size < n) {      \
01135                      warn_length(col, n);  \
01136                   }                         \
01137                   break;
01138                switch (type) {
01139                case RQ_UINTEGER1: CHECK_SIZE(3)  /*         255 */
01140                case RQ_INTEGER1:  CHECK_SIZE(4)  /*        -128 */
01141                case RQ_UINTEGER2: CHECK_SIZE(5)  /*       65535 */
01142                case RQ_INTEGER2:  CHECK_SIZE(6)  /*      -32768 */
01143                case RQ_UINTEGER3:                /*    16777215 */
01144                case RQ_INTEGER3:  CHECK_SIZE(8)  /*    -8388608 */
01145                case RQ_DATE:                     /*  2008-06-09 */
01146                case RQ_UINTEGER4: CHECK_SIZE(10) /*  4200000000 */
01147                case RQ_INTEGER4:  CHECK_SIZE(11) /* -2100000000 */
01148                case RQ_DATETIME:                 /* 2008-06-09 16:03:47 */
01149                case RQ_UINTEGER8: CHECK_SIZE(19) /* trust me    */
01150                case RQ_INTEGER8:  CHECK_SIZE(20) /* ditto       */
01151                case RQ_FLOAT:
01152                case RQ_CHAR:      CHECK_SIZE(size)
01153                }
01154 #undef CHECK_SIZE
01155                break;
01156             case SQL_TYPE_DATE:
01157                if (type != RQ_DATE) {
01158                   warn_type(col, type);
01159                }
01160                break;
01161             case SQL_TYPE_TIMESTAMP:
01162             case SQL_TIMESTAMP:
01163                if (type != RQ_DATE && type != RQ_DATETIME) {
01164                   warn_type(col, type);
01165                }
01166                break;
01167             case SQL_BIT:
01168                warn_length(col, size);
01169                break;
01170 #define WARN_TYPE_OR_LENGTH(n)   \
01171                   if (!ast_rq_is_int(type)) {  \
01172                      warn_type(col, type);    \
01173                   } else {                     \
01174                      warn_length(col, n);  \
01175                   }
01176             case SQL_TINYINT:
01177                if (type != RQ_UINTEGER1) {
01178                   WARN_TYPE_OR_LENGTH(size)
01179                }
01180                break;
01181             case SQL_C_STINYINT:
01182                if (type != RQ_INTEGER1) {
01183                   WARN_TYPE_OR_LENGTH(size)
01184                }
01185                break;
01186             case SQL_C_USHORT:
01187                if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_UINTEGER2) {
01188                   WARN_TYPE_OR_LENGTH(size)
01189                }
01190                break;
01191             case SQL_SMALLINT:
01192             case SQL_C_SSHORT:
01193                if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_INTEGER2) {
01194                   WARN_TYPE_OR_LENGTH(size)
01195                }
01196                break;
01197             case SQL_C_ULONG:
01198                if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
01199                   type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
01200                   type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
01201                   type != RQ_INTEGER4) {
01202                   WARN_TYPE_OR_LENGTH(size)
01203                }
01204                break;
01205             case SQL_INTEGER:
01206             case SQL_C_SLONG:
01207                if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
01208                   type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
01209                   type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
01210                   type != RQ_INTEGER4) {
01211                   WARN_TYPE_OR_LENGTH(size)
01212                }
01213                break;
01214             case SQL_C_UBIGINT:
01215                if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
01216                   type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
01217                   type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
01218                   type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
01219                   type != RQ_INTEGER8) {
01220                   WARN_TYPE_OR_LENGTH(size)
01221                }
01222                break;
01223             case SQL_BIGINT:
01224             case SQL_C_SBIGINT:
01225                if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
01226                   type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
01227                   type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
01228                   type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
01229                   type != RQ_INTEGER8) {
01230                   WARN_TYPE_OR_LENGTH(size)
01231                }
01232                break;
01233 #undef WARN_TYPE_OR_LENGTH
01234             case SQL_NUMERIC:
01235             case SQL_DECIMAL:
01236             case SQL_FLOAT:
01237             case SQL_REAL:
01238             case SQL_DOUBLE:
01239                if (!ast_rq_is_int(type) && type != RQ_FLOAT) {
01240                   warn_type(col, type);
01241                }
01242                break;
01243             default:
01244                ast_log(LOG_WARNING, "Realtime table %s@%s: column type (%d) unrecognized for column '%s'\n", table, database, col->type, elm);
01245             }
01246             break;
01247          }
01248       }
01249       if (!col) {
01250          ast_log(LOG_WARNING, "Realtime table %s@%s requires column '%s', but that column does not exist!\n", table, database, elm);
01251       }
01252    }
01253    AST_RWLIST_UNLOCK(&tableptr->columns);
01254    return 0;
01255 }
01256 #undef warn_length
01257 #undef warn_type
01258 
01259 static int unload_odbc(const char *a, const char *b)
01260 {
01261    return ast_odbc_clear_cache(a, b);
01262 }
01263 
01264 static struct ast_config_engine odbc_engine = {
01265    .name = "odbc",
01266    .load_func = config_odbc,
01267    .realtime_func = realtime_odbc,
01268    .realtime_multi_func = realtime_multi_odbc,
01269    .store_func = store_odbc,
01270    .destroy_func = destroy_odbc,
01271    .update_func = update_odbc,
01272    .update2_func = update2_odbc,
01273    .require_func = require_odbc,
01274    .unload_func = unload_odbc,
01275 };
01276 
01277 static int unload_module (void)
01278 {
01279    ast_config_engine_deregister(&odbc_engine);
01280 
01281    ast_verb(1, "res_config_odbc unloaded.\n");
01282    return 0;
01283 }
01284 
01285 static int load_module (void)
01286 {
01287    ast_config_engine_register(&odbc_engine);
01288    ast_verb(1, "res_config_odbc loaded.\n");
01289    return 0;
01290 }
01291 
01292 static int reload_module(void)
01293 {
01294    return 0;
01295 }
01296 
01297 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Realtime ODBC configuration",
01298       .load = load_module,
01299       .unload = unload_module,
01300       .reload = reload_module,
01301       .load_pri = AST_MODPRI_REALTIME_DRIVER,
01302       );

Generated on 31 Aug 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1