Merged revisions 73316 via svnmerge from
[asterisk/asterisk.git] / res / res_config_odbc.c
old mode 100755 (executable)
new mode 100644 (file)
index 786e14b..7715166
@@ -1,39 +1,62 @@
 /*
- * Asterisk -- A telephony toolkit for Linux.
+ * Asterisk -- An open source telephony toolkit.
  *
- * Copyright (C) 1999-2004, Digium, Inc.
+ * Copyright (C) 1999 - 2005, Digium, Inc.
  *
  * Mark Spencer <markster@digium.com>
  *
- * res_config_odbc.c <odbc+odbc plugin for portable configuration engine >
- * Copyright (C) 2004 Anthony Minessale II <anthmct@yahoo.com>
+ * Copyright (C) 2004 - 2005 Anthony Minessale II <anthmct@yahoo.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief odbc+odbc plugin for portable configuration engine
+ *
+ * \author Mark Spencer <markster@digium.com>
+ * \author Anthony Minessale II <anthmct@yahoo.com>
+ *
+ * \arg http://www.unixodbc.org
  */
 
-#include <asterisk/file.h>
-#include <asterisk/logger.h>
-#include <asterisk/channel.h>
-#include <asterisk/pbx.h>
-#include <asterisk/config.h>
-#include <asterisk/config_pvt.h>
-#include <asterisk/module.h>
-#include <asterisk/lock.h>
-#include <asterisk/options.h>
+/*** MODULEINFO
+       <depend>unixodbc</depend>
+       <depend>ltdl</depend>
+       <depend>res_odbc</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
-#include <asterisk/res_odbc.h>
-#include <asterisk/utils.h>
-
-static char *tdesc = "ODBC Configuration";
-static struct ast_config_reg reg1;
-
-STANDARD_LOCAL_USER;
 
-LOCAL_USER_DECL;
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/options.h"
+#include "asterisk/res_odbc.h"
+#include "asterisk/utils.h"
 
 static struct ast_variable *realtime_odbc(const char *database, const char *table, va_list ap)
 {
-       odbc_obj *obj;
+       struct odbc_obj *obj;
        SQLHSTMT stmt;
        char sql[1024];
        char coltitle[256];
@@ -46,12 +69,12 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl
        int res;
        int x;
        struct ast_variable *var=NULL, *prev=NULL;
-       SQLLEN rowcount=0;
        SQLULEN colsize;
        SQLSMALLINT colcount=0;
        SQLSMALLINT datatype;
        SQLSMALLINT decimaldigits;
        SQLSMALLINT nullable;
+       SQLLEN indicator;
        va_list aq;
        
        va_copy(aq, ap);
@@ -60,19 +83,21 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl
        if (!table)
                return NULL;
 
-       obj = fetch_odbc_obj(database);
+       obj = ast_odbc_request_obj(database, 0);
        if (!obj)
                return NULL;
 
        res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
                ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+               ast_odbc_release_obj(obj);
                return NULL;
        }
 
        newparam = va_arg(aq, const char *);
        if (!newparam)  {
                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
                return NULL;
        }
        newval = va_arg(aq, const char *);
@@ -84,10 +109,11 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl
                newval = va_arg(aq, const char *);
        }
        va_end(aq);
-       res = SQLPrepare(stmt, sql, SQL_NTS);
+       res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
                ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
                return NULL;
        }
        
@@ -98,82 +124,92 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl
                newval = va_arg(ap, const char *);
                SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
        }
-               
-       res = SQLExecute(stmt);
+       
+       res = ast_odbc_smart_execute(obj, stmt);
 
        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
                return NULL;
        }
 
-       res = SQLRowCount(stmt, &rowcount);
+       res = SQLNumResultCols(stmt, &colcount);
        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-               ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
+               ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
                return NULL;
        }
 
-       res = SQLNumResultCols(stmt, &colcount);
+       res = SQLFetch(stmt);
+       if (res == SQL_NO_DATA) {
+               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
+                return NULL;
+       }
        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-               ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
+               ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
                return NULL;
        }
-
-       if (rowcount) {
-               res = SQLFetch(stmt);
+       for (x = 0; x < colcount; x++) {
+               rowdata[0] = '\0';
+               collen = sizeof(coltitle);
+               res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
+                                       &datatype, &colsize, &decimaldigits, &nullable);
                if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+                       ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
+                       if (var)
+                               ast_variables_destroy(var);
+                       ast_odbc_release_obj(obj);
                        return NULL;
                }
-               for (x=0;x<colcount;x++) {
+
+               indicator = 0;
+               res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
+               if (indicator == SQL_NULL_DATA)
                        rowdata[0] = '\0';
-                       collen = sizeof(coltitle);
-                       res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen, 
-                                               &datatype, &colsize, &decimaldigits, &nullable);
-                       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                               ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
-                               if (var)
-                                       ast_destroy_realtime(var);
-                               return NULL;
-                       }
-                       res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
-                       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                               ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
-                               if (var)
-                                       ast_destroy_realtime(var);
-                               return NULL;
-                       }
-                       stringp = rowdata;
-                       while(stringp) {
-                               chunk = strsep(&stringp, ";");
-                               if (chunk && !ast_strlen_zero(ast_strip(chunk))) {
-                                       if (prev) {
-                                               prev->next = ast_new_variable(coltitle, chunk);
-                                               if (prev->next)
-                                                       prev = prev->next;
-                                       } else 
-                                               prev = var = ast_new_variable(coltitle, chunk);
-                                       
-                               }
-                       }
+               else if (ast_strlen_zero(rowdata)) {
+                       /* Because we encode the empty string for a NULL, we will encode
+                        * actual empty strings as a string containing a single whitespace. */
+                       ast_copy_string(rowdata, " ", sizeof(rowdata));
+               }
+
+               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+                       ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+                       if (var)
+                               ast_variables_destroy(var);
+                       ast_odbc_release_obj(obj);
+                       return NULL;
+               }
+               stringp = rowdata;
+               while(stringp) {
+                       chunk = strsep(&stringp, ";");
+                       if (prev) {
+                               prev->next = ast_variable_new(coltitle, chunk);
+                               if (prev->next)
+                                       prev = prev->next;
+                       } else 
+                               prev = var = ast_variable_new(coltitle, chunk);
                }
        }
 
 
        SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+       ast_odbc_release_obj(obj);
        return var;
 }
 
 static struct ast_config *realtime_multi_odbc(const char *database, const char *table, va_list ap)
 {
-       odbc_obj *obj;
+       struct odbc_obj *obj;
        SQLHSTMT stmt;
        char sql[1024];
        char coltitle[256];
        char rowdata[2048];
+       const char *initfield=NULL;
        char *op;
        const char *newparam, *newval;
        char *stringp;
@@ -181,38 +217,42 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char *
        SQLSMALLINT collen;
        int res;
        int x;
-       struct ast_variable *var=NULL, *prev=NULL;
+       struct ast_variable *var=NULL;
        struct ast_config *cfg=NULL;
        struct ast_category *cat=NULL;
-       SQLLEN rowcount=0;
        SQLULEN colsize;
        SQLSMALLINT colcount=0;
        SQLSMALLINT datatype;
        SQLSMALLINT decimaldigits;
        SQLSMALLINT nullable;
+       SQLLEN indicator;
+
        va_list aq;
-       
        va_copy(aq, ap);
-       
-       
+
        if (!table)
                return NULL;
 
-       obj = fetch_odbc_obj(database);
+       obj = ast_odbc_request_obj(database, 0);
        if (!obj)
                return NULL;
 
        res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
                ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+               ast_odbc_release_obj(obj);
                return NULL;
        }
 
        newparam = va_arg(aq, const char *);
        if (!newparam)  {
-               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
                return NULL;
        }
+       initfield = ast_strdupa(newparam);
+       if ((op = strchr(initfield, ' '))) 
+               *op = '\0';
        newval = va_arg(aq, const char *);
        if (!strchr(newparam, ' ')) op = " ="; else op = "";
        snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?", table, newparam, op);
@@ -221,11 +261,14 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char *
                snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?", newparam, op);
                newval = va_arg(aq, const char *);
        }
+       if (initfield)
+               snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
        va_end(aq);
-       res = SQLPrepare(stmt, sql, SQL_NTS);
+       res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
                ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
-               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
                return NULL;
        }
        
@@ -237,92 +280,85 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char *
                SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
        }
                
-       res = SQLExecute(stmt);
+       res = ast_odbc_smart_execute(obj, stmt);
 
        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
                return NULL;
        }
 
-       res = SQLRowCount(stmt, &rowcount);
+       res = SQLNumResultCols(stmt, &colcount);
        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-               ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
-               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+               ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
+               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
                return NULL;
        }
 
-       res = SQLNumResultCols(stmt, &colcount);
-       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-               ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
-               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+       cfg = ast_config_new();
+       if (!cfg) {
+               ast_log(LOG_WARNING, "Out of memory!\n");
+               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
                return NULL;
        }
 
-       while (rowcount) {
+       while ((res=SQLFetch(stmt)) != SQL_NO_DATA) {
                var = NULL;
-               prev = NULL;
-               res = SQLFetch(stmt);
                if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
                        ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
                        continue;
                }
+               cat = ast_category_new("");
+               if (!cat) {
+                       ast_log(LOG_WARNING, "Out of memory!\n");
+                       continue;
+               }
                for (x=0;x<colcount;x++) {
                        rowdata[0] = '\0';
                        collen = sizeof(coltitle);
-                       res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen, 
+                       res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
                                                &datatype, &colsize, &decimaldigits, &nullable);
                        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
                                ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
-                               if (var)
-                                       ast_destroy_realtime(var);
+                               ast_category_destroy(cat);
                                continue;
                        }
-                       res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+
+                       indicator = 0;
+                       res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
+                       if (indicator == SQL_NULL_DATA)
+                               continue;
+
                        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
                                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
-                               if (var)
-                                       ast_destroy_realtime(var);
+                               ast_category_destroy(cat);
                                continue;
                        }
                        stringp = rowdata;
                        while(stringp) {
                                chunk = strsep(&stringp, ";");
-                               if (chunk && !ast_strlen_zero(ast_strip(chunk))) {
-                                       if (prev) {
-                                               prev->next = ast_new_variable(coltitle, chunk);
-                                               if (prev->next)
-                                                       prev = prev->next;
-                                       } else 
-                                               prev = var = ast_new_variable(coltitle, chunk);
-                                       
-                               }
-                       }
-                       if (var) {
-                               cat = ast_new_category("");
-                               if (cat) {
-                                       cat->root = var;
-                                       if (!cfg) 
-                                               cfg = ast_new_config();
-                                       if (cfg)
-                                               ast_category_append(cfg, cat);
-                                       else 
-                                               ast_category_destroy(cat);
-                               } else {
-                                       ast_log(LOG_WARNING, "Out of memory!\n");
-                                       ast_destroy_realtime(var);
+                               if (!ast_strlen_zero(ast_strip(chunk))) {
+                                       if (initfield && !strcmp(initfield, coltitle))
+                                               ast_category_rename(cat, chunk);
+                                       var = ast_variable_new(coltitle, chunk);
+                                       ast_variable_append(cat, var);
                                }
                        }
                }
+               ast_category_append(cfg, cat);
        }
 
-       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+       ast_odbc_release_obj(obj);
        return cfg;
 }
 
 static int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
 {
-       odbc_obj *obj;
+       struct odbc_obj *obj;
        SQLHSTMT stmt;
        char sql[256];
        SQLLEN rowcount=0;
@@ -336,19 +372,21 @@ static int update_odbc(const char *database, const char *table, const char *keyf
        if (!table)
                return -1;
 
-       obj = fetch_odbc_obj (database);
+       obj = ast_odbc_request_obj(database, 0);
        if (!obj)
                return -1;
 
        res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
                ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+               ast_odbc_release_obj(obj);
                return -1;
        }
 
        newparam = va_arg(aq, const char *);
        if (!newparam)  {
                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
                return -1;
        }
        newval = va_arg(aq, const char *);
@@ -360,10 +398,11 @@ static int update_odbc(const char *database, const char *table, const char *keyf
        va_end(aq);
        snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield);
        
-       res = SQLPrepare(stmt, sql, SQL_NTS);
+       res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
                ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
                return -1;
        }
        
@@ -377,185 +416,177 @@ static int update_odbc(const char *database, const char *table, const char *keyf
                
        SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(lookup), 0, (void *)lookup, 0, NULL);
 
-       res = SQLExecute(stmt);
+       res = ast_odbc_smart_execute(obj, stmt);
 
        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
                return -1;
        }
 
        res = SQLRowCount(stmt, &rowcount);
        SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+       ast_odbc_release_obj(obj);
 
        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
                ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
                return -1;
        }
 
-       if (rowcount) 
-               return 0;
+       if (rowcount >= 0)
+               return (int)rowcount;
+
        return -1;
 }
 
-static struct ast_config *config_odbc (const char *database, const char *table, const char *file, struct ast_config *new_config_s, struct ast_category **new_cat_p, struct ast_variable **new_v_p, int recur)
+struct config_odbc_obj {
+       char *sql;
+       unsigned long cat_metric;
+       char category[128];
+       char var_name[128];
+       char var_val[1024]; /* changed from 128 to 1024 via bug 8251 */
+       SQLLEN err;
+};
+
+static SQLHSTMT config_odbc_prepare(struct odbc_obj *obj, void *data)
 {
-       struct ast_config *new;
-       struct ast_variable *cur_v, *new_v;
-       struct ast_category *cur_cat, *new_cat;
+       struct config_odbc_obj *q = data;
+       SQLHSTMT sth;
+       int res;
+
+       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &sth);
+       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+               if (option_verbose > 3)
+                       ast_verbose( VERBOSE_PREFIX_4 "Failure in AllocStatement %d\n", res);
+               return NULL;
+       }
+
+       res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS);
+       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+               if (option_verbose > 3)
+                       ast_verbose( VERBOSE_PREFIX_4 "Error in PREPARE %d\n", res);
+               SQLFreeHandle(SQL_HANDLE_STMT, sth);
+               return NULL;
+       }
+
+       SQLBindCol(sth, 1, SQL_C_ULONG, &q->cat_metric, sizeof(q->cat_metric), &q->err);
+       SQLBindCol(sth, 2, SQL_C_CHAR, q->category, sizeof(q->category), &q->err);
+       SQLBindCol(sth, 3, SQL_C_CHAR, q->var_name, sizeof(q->var_name), &q->err);
+       SQLBindCol(sth, 4, SQL_C_CHAR, q->var_val, sizeof(q->var_val), &q->err);
+
+       return sth;
+}
+
+static struct ast_config *config_odbc(const char *database, const char *table, const char *file, struct ast_config *cfg, int withcomments)
+{
+       struct ast_variable *new_v;
+       struct ast_category *cur_cat;
        int res = 0;
-       odbc_obj *obj;
-       SQLINTEGER err=0, commented=0, cat_metric=0, var_metric=0, last_cat_metric=0;
-       SQLBIGINT id;
-       char sql[255] = "", filename[128], category[128], var_name[128], var_val[128];
-       SQLSMALLINT rowcount=0;
+       struct odbc_obj *obj;
+       char sqlbuf[1024] = "";
+       char *sql = sqlbuf;
+       size_t sqlleft = sizeof(sqlbuf);
+       unsigned int last_cat_metric = 0;
+       SQLSMALLINT rowcount = 0;
        SQLHSTMT stmt;
-       char last[80] = "";
-       int cat_started = 0;
-       int var_started = 0;
+       char last[128] = "";
+       struct config_odbc_obj q;
 
+       memset(&q, 0, sizeof(q));
 
        if (!file || !strcmp (file, "res_config_odbc.conf"))
-               return NULL;            // cant configure myself with myself !
+               return NULL;            /* cant configure myself with myself ! */
 
-       obj = fetch_odbc_obj(database);
+       obj = ast_odbc_request_obj(database, 0);
        if (!obj)
                return NULL;
 
-       last[0] = '\0';
-
-       res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
-
-       SQLBindCol (stmt, 1, SQL_C_ULONG, &id, sizeof (id), &err);
-       SQLBindCol (stmt, 2, SQL_C_ULONG, &cat_metric, sizeof (cat_metric), &err);
-       SQLBindCol (stmt, 3, SQL_C_ULONG, &var_metric, sizeof (var_metric), &err);
-       SQLBindCol (stmt, 4, SQL_C_ULONG, &commented, sizeof (commented), &err);
-       SQLBindCol (stmt, 5, SQL_C_CHAR, &filename, sizeof (filename), &err);
-       SQLBindCol (stmt, 6, SQL_C_CHAR, &category, sizeof (category), &err);
-       SQLBindCol (stmt, 7, SQL_C_CHAR, &var_name, sizeof (var_name), &err);
-       SQLBindCol (stmt, 8, SQL_C_CHAR, &var_val, sizeof (var_val), &err);
+       ast_build_string(&sql, &sqlleft, "SELECT cat_metric, category, var_name, var_val FROM %s ", table);
+       ast_build_string(&sql, &sqlleft, "WHERE filename='%s' AND commented=0 ", file);
+       ast_build_string(&sql, &sqlleft, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ");
+       q.sql = sqlbuf;
 
-       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE filename='%s' and commented=0 ORDER BY filename,cat_metric desc,var_metric asc,category,var_name,var_val,id", table, file);
-       res = SQLExecDirect (stmt, sql, SQL_NTS);
+       stmt = ast_odbc_prepare_and_execute(obj, config_odbc_prepare, &q);
 
-       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-               ast_log (LOG_WARNING, "SQL select error!\n[%s]\n\n", sql);
+       if (!stmt) {
+               ast_log(LOG_WARNING, "SQL select error!\n[%s]\n\n", sql);
+               ast_odbc_release_obj(obj);
                return NULL;
        }
 
-       res = SQLNumResultCols (stmt, &rowcount);
+       res = SQLNumResultCols(stmt, &rowcount);
 
        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-               ast_log (LOG_WARNING, "SQL select error!\n[%s]\n\n", sql);
+               ast_log(LOG_WARNING, "SQL NumResultCols error!\n[%s]\n\n", sql);
+               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
                return NULL;
        }
 
-       if (new_config_s) {
-               new = new_config_s;
-               cat_started++;
-       } else {
-               new = ast_new_config ();
-       }
-       
-       if (!new) {
-               ast_log(LOG_WARNING, "Out of memory!\n");
-               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-               return NULL;
+       if (!rowcount) {
+               ast_log(LOG_NOTICE, "found nothing\n");
+               ast_odbc_release_obj(obj);
+               return cfg;
        }
 
-       if (rowcount) {
-               res = SQLFetch (stmt);
-               cat_started = 0;
-
-               cur_cat = *new_cat_p;
-               cur_v = *new_v_p;
+       cur_cat = ast_config_get_current_category(cfg);
 
-               if (cur_cat)
-                       cat_started = 1;
-               if (cur_v)
-                       var_started = 1;
-
-               while (res != SQL_NO_DATA) {
-                       if (!strcmp (var_name, "#include") && recur < MAX_INCLUDE_LEVEL) {
-
-                               config_odbc(database, table, var_val, new, &cur_cat, &cur_v, recur + 1);
-                       } else {
-                               if (strcmp (last, category) || last_cat_metric != cat_metric) {
-                                       strncpy(last, category, sizeof(last) - 1);
-                                       last_cat_metric = cat_metric;
-                                       new_cat = (struct ast_category *) ast_new_category (category);
-
-                                       if (!cat_started) {
-                                               cat_started++;
-                                               new->root = new_cat;
-                                               cur_cat = new->root;
-                                       } else {
-                                               cur_cat->next = new_cat;
-                                               cur_cat = cur_cat->next;
-                                       }
-                                       var_started = 0;
-
-                               }
-
-                               new_v = ast_new_variable (var_name, var_val);
-
-                               if (!var_started) {
-                                       var_started++;
-                                       cur_cat->root = new_v;
-                                       cur_v = cur_cat->root;
-                               } else {
-                                       cur_v->next = new_v;
-                                       cur_v = cur_v->next;
-                               }
+       while ((res = SQLFetch(stmt)) != SQL_NO_DATA) {
+               if (!strcmp (q.var_name, "#include")) {
+                       if (!ast_config_internal_load(q.var_val, cfg, 0)) {
+                               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+                               ast_odbc_release_obj(obj);
+                               return NULL;
                        }
-
-               // next row 
-                       res = SQLFetch (stmt);
+                       continue;
+               } 
+               if (strcmp(last, q.category) || last_cat_metric != q.cat_metric) {
+                       cur_cat = ast_category_new(q.category);
+                       if (!cur_cat) {
+                               ast_log(LOG_WARNING, "Out of memory!\n");
+                               break;
+                       }
+                       strcpy(last, q.category);
+                       last_cat_metric = q.cat_metric;
+                       ast_category_append(cfg, cur_cat);
                }
 
-               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-       } else {
-               ast_log (LOG_NOTICE, "found nothing\n");
+               new_v = ast_variable_new(q.var_name, q.var_val);
+               ast_variable_append(cur_cat, new_v);
        }
-       return new;
 
+       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+       ast_odbc_release_obj(obj);
+       return cfg;
 }
 
-int unload_module (void)
+static struct ast_config_engine odbc_engine = {
+       .name = "odbc",
+       .load_func = config_odbc,
+       .realtime_func = realtime_odbc,
+       .realtime_multi_func = realtime_multi_odbc,
+       .update_func = update_odbc
+};
+
+static int unload_module (void)
 {
-       ast_cust_config_deregister (&reg1);
+       ast_module_user_hangup_all();
+       ast_config_engine_deregister(&odbc_engine);
        if (option_verbose)
                ast_verbose("res_config_odbc unloaded.\n");
-       STANDARD_HANGUP_LOCALUSERS;
        return 0;
 }
 
-int load_module (void)
+static int load_module (void)
 {
-       memset (&reg1, 0, sizeof (struct ast_config_reg));
-       strncpy(reg1.name, "odbc", sizeof(reg1.name) - 1);
-       reg1.static_func = config_odbc;
-       reg1.realtime_func = realtime_odbc;
-       reg1.realtime_multi_func = realtime_multi_odbc;
-       reg1.update_func = update_odbc;
-       ast_cust_config_register (&reg1);
+       ast_config_engine_register(&odbc_engine);
        if (option_verbose)
                ast_verbose("res_config_odbc loaded.\n");
        return 0;
 }
 
-char *description (void)
-{
-       return tdesc;
-}
-
-int usecount (void)
-{
-       /* never unload a config module */
-       return 1;
-}
-
-char *key ()
-{
-       return ASTERISK_GPL_KEY;
-}
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Realtime ODBC configuration",
+               .load = load_module,
+               .unload = unload_module,
+               );