loader: Add dependency fields to module structures.
[asterisk/asterisk.git] / res / res_config_odbc.c
index 7f6093a..186e89d 100644 (file)
@@ -35,8 +35,6 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
 #include "asterisk/pbx.h"
@@ -47,7 +45,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/utils.h"
 #include "asterisk/stringfields.h"
 
+/*! Initial SQL query buffer size to allocate. */
+#define SQL_BUF_SIZE   1024
+
 AST_THREADSTORAGE(sql_buf);
+AST_THREADSTORAGE(rowdata_buf);
 
 struct custom_prepare_struct {
        const char *sql;
@@ -59,11 +61,31 @@ struct custom_prepare_struct {
        unsigned long long skip;
 };
 
+#define ENCODE_CHUNK(buffer, s) \
+       do { \
+               char *eptr = buffer; \
+               const char *vptr = s; \
+               for (; *vptr && eptr < buffer + sizeof(buffer); vptr++) { \
+                       if (strchr("^;", *vptr)) { \
+                               /* We use ^XX, instead of %XX because '%' is a special character in SQL */ \
+                               snprintf(eptr, buffer + sizeof(buffer) - eptr, "^%02hhX", *vptr); \
+                               eptr += 3; \
+                       } else { \
+                               *eptr++ = *vptr; \
+                       } \
+               } \
+               if (eptr < buffer + sizeof(buffer)) { \
+                       *eptr = '\0'; \
+               } else { \
+                       buffer[sizeof(buffer) - 1] = '\0'; \
+               } \
+       } while(0)
+
 static void decode_chunk(char *chunk)
 {
        for (; *chunk; chunk++) {
                if (*chunk == '^' && strchr("0123456789ABCDEF", chunk[1]) && strchr("0123456789ABCDEF", chunk[2])) {
-                       sscanf(chunk + 1, "%02hhX", chunk);
+                       sscanf(chunk + 1, "%02hhX", (unsigned char *)chunk);
                        memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
                }
        }
@@ -89,11 +111,11 @@ static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data)
                return NULL;
        }
 
-       ast_debug(1, "Skip: %lld; SQL: %s\n", cps->skip, cps->sql);
+       ast_debug(1, "Skip: %llu; SQL: %s\n", cps->skip, cps->sql);
 
        res = SQLPrepare(stmt, (unsigned char *)cps->sql, SQL_NTS);
        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-               ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", cps->sql);
+               ast_log(LOG_WARNING, "SQL Prepare failed! [%s]\n", cps->sql);
                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
                return NULL;
        }
@@ -102,35 +124,29 @@ static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data)
                const char *newval = field->value;
 
                if ((1LL << count++) & cps->skip) {
-                       ast_debug(1, "Skipping field '%s'='%s' (%llo/%llo)\n", field->name, newval, 1LL << (count - 1), cps->skip);
+                       ast_debug(1, "Skipping field '%s'='%s' (%llo/%llo)\n", field->name, newval, 1ULL << (count - 1), cps->skip);
                        continue;
                }
                ast_debug(1, "Parameter %d ('%s') = '%s'\n", x, field->name, newval);
                if (strchr(newval, ';') || strchr(newval, '^')) {
-                       char *eptr = encodebuf;
-                       const char *vptr = newval;
-                       for (; *vptr && eptr < encodebuf + sizeof(encodebuf); vptr++) {
-                               if (strchr("^;", *vptr)) {
-                                       /* We use ^XX, instead of %XX because '%' is a special character in SQL */
-                                       snprintf(eptr, encodebuf + sizeof(encodebuf) - eptr, "^%02hhX", *vptr);
-                                       eptr += 3;
-                               } else {
-                                       *eptr++ = *vptr;
-                               }
-                       }
-                       if (eptr < encodebuf + sizeof(encodebuf)) {
-                               *eptr = '\0';
-                       } else {
-                               encodebuf[sizeof(encodebuf) - 1] = '\0';
-                       }
+                       ENCODE_CHUNK(encodebuf, newval);
+                       ast_string_field_set(cps, encoding[x], encodebuf);
+                       newval = cps->encoding[x];
+               }
+               SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
+       }
+
+       if (!ast_strlen_zero(cps->extra)) {
+               const char *newval = cps->extra;
+               ast_debug(1, "Parameter %d = '%s'\n", x, newval);
+               if (strchr(newval, ';') || strchr(newval, '^')) {
+                       ENCODE_CHUNK(encodebuf, newval);
                        ast_string_field_set(cps, encoding[x], encodebuf);
                        newval = cps->encoding[x];
                }
                SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
        }
 
-       if (!ast_strlen_zero(cps->extra))
-               SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(cps->extra), 0, (void *)cps->extra, 0, NULL);
        return stmt;
 }
 
@@ -146,14 +162,14 @@ static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data)
  *
  * \retval var on success
  * \retval NULL on failure
-*/
+ */
 static struct ast_variable *realtime_odbc(const char *database, const char *table, const struct ast_variable *fields)
 {
        struct odbc_obj *obj;
        SQLHSTMT stmt;
-       char sql[1024];
        char coltitle[256];
-       char rowdata[2048];
+       struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
+       struct ast_str *rowdata = ast_str_thread_get(&rowdata_buf, 128);
        char *op;
        const struct ast_variable *field = fields;
        char *stringp;
@@ -168,53 +184,47 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl
        SQLSMALLINT decimaldigits;
        SQLSMALLINT nullable;
        SQLLEN indicator;
-       struct custom_prepare_struct cps = { .sql = sql, .fields = fields, };
+       struct custom_prepare_struct cps = { .fields = fields, };
        struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
 
-       if (!fields) {
-               return NULL;
-       }
-
-       if (ast_string_field_init(&cps, 256)) {
-               return NULL;
-       }
-
-       if (!table) {
-               ast_string_field_free_memory(&cps);
+       if (!table || !field || !sql || !rowdata) {
                return NULL;
        }
 
        obj = ast_odbc_request_obj2(database, connected_flag);
-
        if (!obj) {
                ast_log(LOG_ERROR, "No database handle available with the name of '%s' (check res_odbc.conf)\n", database);
-               ast_string_field_free_memory(&cps);
                return NULL;
        }
 
        op = !strchr(field->name, ' ') ? " =" : "";
-       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, field->name, op,
-               strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
+       ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s ?%s", table, field->name, op,
+               strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\\\'" : "");
        while ((field = field->next)) {
                op = !strchr(field->name, ' ') ? " =" : "";
-               snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", field->name, op,
-                       strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
+               ast_str_append(&sql, 0, " AND %s%s ?%s", field->name, op,
+                       strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\\\'" : "");
        }
 
+       cps.sql = ast_str_buffer(sql);
+
+       if (ast_string_field_init(&cps, 256)) {
+               ast_odbc_release_obj(obj);
+               return NULL;
+       }
        stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
+       ast_string_field_free_memory(&cps);
 
        if (!stmt) {
                ast_odbc_release_obj(obj);
-               ast_string_field_free_memory(&cps);
                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);
+               ast_log(LOG_WARNING, "SQL Column Count error! [%s]\n", ast_str_buffer(sql));
                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
                ast_odbc_release_obj(obj);
-               ast_string_field_free_memory(&cps);
                return NULL;
        }
 
@@ -222,49 +232,57 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl
        if (res == SQL_NO_DATA) {
                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
                ast_odbc_release_obj(obj);
-               ast_string_field_free_memory(&cps);
                return NULL;
        }
        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-               ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+               ast_log(LOG_WARNING, "SQL Fetch error! [%s]\n", ast_str_buffer(sql));
                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
                ast_odbc_release_obj(obj);
-               ast_string_field_free_memory(&cps);
                return NULL;
        }
        for (x = 0; x < colcount; x++) {
-               rowdata[0] = '\0';
                colsize = 0;
                collen = sizeof(coltitle);
-               res = SQLDescribeCol(stmt, x + 1, (unsigned char *)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);
+                       ast_log(LOG_WARNING, "SQL Describe Column error! [%s]\n", ast_str_buffer(sql));
                        if (var)
                                ast_variables_destroy(var);
                        ast_odbc_release_obj(obj);
-                       ast_string_field_free_memory(&cps);
                        return NULL;
                }
 
+               ast_str_reset(rowdata);
                indicator = 0;
-               res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
-               if (indicator == SQL_NULL_DATA)
-                       rowdata[0] = '\0';
-               else if (ast_strlen_zero(rowdata)) {
+
+               res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata), ast_str_size(rowdata), &indicator);
+               ast_str_update(rowdata);
+               if (indicator == SQL_NULL_DATA) {
+                       ast_str_reset(rowdata);
+               } else if (!ast_str_strlen(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));
+                       ast_str_set(&rowdata, -1, "%s", " ");
+               } else if ((res == SQL_SUCCESS) || (res == SQL_SUCCESS_WITH_INFO)) {
+                       if (indicator != ast_str_strlen(rowdata)) {
+                               /* If the available space was not enough to contain the row data enlarge and read in the rest */
+                               ast_str_make_space(&rowdata, indicator + 1);
+                               res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata) + ast_str_strlen(rowdata),
+                                       ast_str_size(rowdata) - ast_str_strlen(rowdata), &indicator);
+                               ast_str_update(rowdata);
+                       }
                }
 
                if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+                       ast_log(LOG_WARNING, "SQL Get Data error! [%s]\n", ast_str_buffer(sql));
                        if (var)
                                ast_variables_destroy(var);
                        ast_odbc_release_obj(obj);
                        return NULL;
                }
-               stringp = rowdata;
+
+               stringp = ast_str_buffer(rowdata);
                while (stringp) {
                        chunk = strsep(&stringp, ";");
                        if (!ast_strlen_zero(ast_strip(chunk))) {
@@ -283,10 +301,8 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl
                }
        }
 
-
        SQLFreeHandle(SQL_HANDLE_STMT, stmt);
        ast_odbc_release_obj(obj);
-       ast_string_field_free_memory(&cps);
        return var;
 }
 
@@ -297,20 +313,20 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl
  * \param ap list containing one or more field/operator/value set.
  *
  * Select database and preform query on table, prepare the sql statement
- * Sub-in the values to the prepared statement and execute it. 
+ * Sub-in the values to the prepared statement and execute it.
  * Execute this prepared query against several ODBC connected databases.
  * Return results as an ast_config variable.
  *
  * \retval var on success
  * \retval NULL on failure
-*/
+ */
 static struct ast_config *realtime_multi_odbc(const char *database, const char *table, const struct ast_variable *fields)
 {
        struct odbc_obj *obj;
        SQLHSTMT stmt;
-       char sql[1024];
        char coltitle[256];
-       char rowdata[2048];
+       struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
+       struct ast_str *rowdata = ast_str_thread_get(&rowdata_buf, 128);
        const char *initfield;
        char *op;
        const struct ast_variable *field = fields;
@@ -329,15 +345,14 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char *
        SQLSMALLINT decimaldigits;
        SQLSMALLINT nullable;
        SQLLEN indicator;
-       struct custom_prepare_struct cps = { .sql = sql, .fields = fields, };
+       struct custom_prepare_struct cps = { .fields = fields, };
 
-       if (!table || !field || ast_string_field_init(&cps, 256)) {
+       if (!table || !field || !sql || !rowdata) {
                return NULL;
        }
 
        obj = ast_odbc_request_obj2(database, connected_flag);
        if (!obj) {
-               ast_string_field_free_memory(&cps);
                return NULL;
        }
 
@@ -347,30 +362,34 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char *
        }
 
        op = !strchr(field->name, ' ') ? " =" : "";
-       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, field->name, op,
-               strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
+       ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s ?%s", table, field->name, op,
+               strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\\\'" : "");
        while ((field = field->next)) {
                op = !strchr(field->name, ' ') ? " =" : "";
-               snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", field->name, op,
-                       strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
+               ast_str_append(&sql, 0, " AND %s%s ?%s", field->name, op,
+                       strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\\\'" : "");
        }
+       ast_str_append(&sql, 0, " ORDER BY %s", initfield);
 
-       snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
+       cps.sql = ast_str_buffer(sql);
 
+       if (ast_string_field_init(&cps, 256)) {
+               ast_odbc_release_obj(obj);
+               return NULL;
+       }
        stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
+       ast_string_field_free_memory(&cps);
 
        if (!stmt) {
                ast_odbc_release_obj(obj);
-               ast_string_field_free_memory(&cps);
                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);
+               ast_log(LOG_WARNING, "SQL Column Count error! [%s]\n", ast_str_buffer(sql));
                SQLFreeHandle(SQL_HANDLE_STMT, stmt);
                ast_odbc_release_obj(obj);
-               ast_string_field_free_memory(&cps);
                return NULL;
        }
 
@@ -379,44 +398,55 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char *
                ast_log(LOG_WARNING, "Out of memory!\n");
                SQLFreeHandle(SQL_HANDLE_STMT, stmt);
                ast_odbc_release_obj(obj);
-               ast_string_field_free_memory(&cps);
                return NULL;
        }
 
        while ((res=SQLFetch(stmt)) != SQL_NO_DATA) {
                var = NULL;
                if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+                       ast_log(LOG_WARNING, "SQL Fetch error! [%s]\n", ast_str_buffer(sql));
                        continue;
                }
-               cat = ast_category_new("","",99999);
+               cat = ast_category_new_anonymous();
                if (!cat) {
-                       ast_log(LOG_WARNING, "Out of memory!\n");
                        continue;
                }
                for (x=0;x<colcount;x++) {
-                       rowdata[0] = '\0';
                        colsize = 0;
                        collen = sizeof(coltitle);
-                       res = SQLDescribeCol(stmt, x + 1, (unsigned char *)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);
+                               ast_log(LOG_WARNING, "SQL Describe Column error! [%s]\n", ast_str_buffer(sql));
                                ast_category_destroy(cat);
                                goto next_sql_fetch;
                        }
 
+                       ast_str_reset(rowdata);
                        indicator = 0;
-                       res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
-                       if (indicator == SQL_NULL_DATA)
+
+                       res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata), ast_str_size(rowdata), &indicator);
+                       ast_str_update(rowdata);
+                       if (indicator == SQL_NULL_DATA) {
                                continue;
+                       }
+
+                       if ((res == SQL_SUCCESS) || (res == SQL_SUCCESS_WITH_INFO)) {
+                               if (indicator != ast_str_strlen(rowdata)) {
+                                       /* If the available space was not enough to contain the row data enlarge and read in the rest */
+                                       ast_str_make_space(&rowdata, indicator + 1);
+                                       res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata) + ast_str_strlen(rowdata),
+                                               ast_str_size(rowdata) - ast_str_strlen(rowdata), &indicator);
+                                       ast_str_update(rowdata);
+                               }
+                       }
 
                        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                               ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+                               ast_log(LOG_WARNING, "SQL Get Data error! [%s]\n", ast_str_buffer(sql));
                                ast_category_destroy(cat);
                                goto next_sql_fetch;
                        }
-                       stringp = rowdata;
+                       stringp = ast_str_buffer(rowdata);
                        while (stringp) {
                                chunk = strsep(&stringp, ";");
                                if (!ast_strlen_zero(ast_strip(chunk))) {
@@ -437,7 +467,6 @@ next_sql_fetch:;
 
        SQLFreeHandle(SQL_HANDLE_STMT, stmt);
        ast_odbc_release_obj(obj);
-       ast_string_field_free_memory(&cps);
        return cfg;
 }
 
@@ -455,32 +484,27 @@ next_sql_fetch:;
  *
  * \retval number of rows affected
  * \retval -1 on failure
-*/
+ */
 static int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, const struct ast_variable *fields)
 {
        struct odbc_obj *obj;
        SQLHSTMT stmt;
-       char sql[256];
        SQLLEN rowcount=0;
+       struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
        const struct ast_variable *field = fields;
        int res, count = 0, paramcount = 0;
-       struct custom_prepare_struct cps = { .sql = sql, .extra = lookup, .fields = fields, };
+       struct custom_prepare_struct cps = { .extra = lookup, .fields = fields, };
        struct odbc_cache_tables *tableptr;
        struct odbc_cache_columns *column = NULL;
        struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
 
-       if (!table || !field || !keyfield) {
-               return -1;
-       }
-
-       if (ast_string_field_init(&cps, 256)) {
+       if (!table || !field || !keyfield || !sql) {
                return -1;
        }
 
        tableptr = ast_odbc_find_table(database, table);
        if (!(obj = ast_odbc_request_obj2(database, connected_flag))) {
                ast_odbc_release_table(tableptr);
-               ast_string_field_free_memory(&cps);
                return -1;
        }
 
@@ -488,19 +512,19 @@ static int update_odbc(const char *database, const char *table, const char *keyf
                ast_log(LOG_WARNING, "Key field '%s' does not exist in table '%s@%s'.  Update will fail\n", keyfield, table, database);
        }
 
-       snprintf(sql, sizeof(sql), "UPDATE %s SET ", table);
+       ast_str_set(&sql, 0, "UPDATE %s SET ", table);
        while (field) {
                if ((tableptr && (column = ast_odbc_find_column(tableptr, field->name))) || count >= 64) {
                        if (paramcount++) {
-                               snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", ");
+                               ast_str_append(&sql, 0, ", ");
                        }
                        /* NULL test for non-text columns */
                        if (count < 64 && ast_strlen_zero(field->value) && column->nullable && !is_text(column)) {
-                               snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=NULL", field->name);
+                               ast_str_append(&sql, 0, "%s=NULL", field->name);
                                cps.skip |= (1LL << count);
                        } else {
                                /* Value is not an empty string, or column is of text type, or we couldn't fit any more into cps.skip (count >= 64 ?!). */
-                               snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=?", field->name);
+                               ast_str_append(&sql, 0, "%s=?", field->name);
                        }
                } else { /* the column does not exist in the table */
                        cps.skip |= (1LL << count);
@@ -508,24 +532,29 @@ static int update_odbc(const char *database, const char *table, const char *keyf
                ++count;
                field = field->next;
        }
-       snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield);
+       ast_str_append(&sql, 0, " WHERE %s=?", keyfield);
        ast_odbc_release_table(tableptr);
 
+       cps.sql = ast_str_buffer(sql);
+
+       if (ast_string_field_init(&cps, 256)) {
+               ast_odbc_release_obj(obj);
+               return -1;
+       }
        stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
+       ast_string_field_free_memory(&cps);
 
        if (!stmt) {
                ast_odbc_release_obj(obj);
-               ast_string_field_free_memory(&cps);
                return -1;
        }
 
        res = SQLRowCount(stmt, &rowcount);
        SQLFreeHandle (SQL_HANDLE_STMT, stmt);
        ast_odbc_release_obj(obj);
-       ast_string_field_free_memory(&cps);
 
        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 Row Count error! [%s]\n", ast_str_buffer(sql));
                return -1;
        }
 
@@ -548,17 +577,15 @@ static SQLHSTMT update2_prepare(struct odbc_obj *obj, void *data)
        int res, x = 1, first = 1;
        struct update2_prepare_struct *ups = data;
        const struct ast_variable *field;
-       struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
+       struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
        SQLHSTMT stmt;
-       struct odbc_cache_tables *tableptr = ast_odbc_find_table(ups->database, ups->table);
+       struct odbc_cache_tables *tableptr;
 
        if (!sql) {
-               if (tableptr) {
-                       ast_odbc_release_table(tableptr);
-               }
                return NULL;
        }
 
+       tableptr = ast_odbc_find_table(ups->database, ups->table);
        if (!tableptr) {
                ast_log(LOG_ERROR, "Could not retrieve metadata for table '%s@%s'.  Update will fail!\n", ups->table, ups->database);
                return NULL;
@@ -603,7 +630,7 @@ static SQLHSTMT update2_prepare(struct odbc_obj *obj, void *data)
 
        res = SQLPrepare(stmt, (unsigned char *)ast_str_buffer(sql), SQL_NTS);
        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-               ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", ast_str_buffer(sql));
+               ast_log(LOG_WARNING, "SQL Prepare failed! [%s]\n", ast_str_buffer(sql));
                SQLFreeHandle(SQL_HANDLE_STMT, stmt);
                return NULL;
        }
@@ -649,8 +676,9 @@ static int update2_odbc(const char *database, const char *table, const struct as
 
        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
                /* Since only a single thread can access this memory, we can retrieve what would otherwise be lost. */
-               sql = ast_str_thread_get(&sql_buf, 16);
-               ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n", ast_str_buffer(sql));
+               sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
+               ast_assert(sql != NULL);
+               ast_log(LOG_WARNING, "SQL Row Count error! [%s]\n", ast_str_buffer(sql));
                return -1;
        }
 
@@ -673,42 +701,54 @@ static int update2_odbc(const char *database, const char *table, const struct as
  *
  * \retval number of rows affected
  * \retval -1 on failure
-*/
+ */
 static int store_odbc(const char *database, const char *table, const struct ast_variable *fields)
 {
        struct odbc_obj *obj;
        SQLHSTMT stmt;
-       char sql[256];
-       char keys[256];
-       char vals[256];
        SQLLEN rowcount=0;
        const struct ast_variable *field = fields;
+       struct ast_str *keys;
+       struct ast_str *vals;
+       struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
        int res;
-       struct custom_prepare_struct cps = { .sql = sql, .extra = NULL, .fields = fields, };
+       struct custom_prepare_struct cps = { .fields = fields, };
        struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
 
-       if (!table || !field) {
-               return -1;
-       }
-
-       if (ast_string_field_init(&cps, 256)) {
+       keys = ast_str_create(SQL_BUF_SIZE / 2);
+       vals = ast_str_create(SQL_BUF_SIZE / 4);
+       if (!table || !field || !keys || !vals || !sql) {
+               ast_free(vals);
+               ast_free(keys);
                return -1;
        }
 
        obj = ast_odbc_request_obj2(database, connected_flag);
        if (!obj) {
+               ast_free(vals);
+               ast_free(keys);
                return -1;
        }
 
-       snprintf(keys, sizeof(keys), "%s", field->name);
-       ast_copy_string(vals, "?", sizeof(vals));
+       ast_str_set(&keys, 0, "%s", field->name);
+       ast_str_set(&vals, 0, "?");
        while ((field = field->next)) {
-               snprintf(keys + strlen(keys), sizeof(keys) - strlen(keys), ", %s", field->name);
-               snprintf(vals + strlen(vals), sizeof(vals) - strlen(vals), ", ?");
+               ast_str_append(&keys, 0, ", %s", field->name);
+               ast_str_append(&vals, 0, ", ?");
        }
-       snprintf(sql, sizeof(sql), "INSERT INTO %s (%s) VALUES (%s)", table, keys, vals);
+       ast_str_set(&sql, 0, "INSERT INTO %s (%s) VALUES (%s)",
+               table, ast_str_buffer(keys), ast_str_buffer(vals));
+
+       ast_free(vals);
+       ast_free(keys);
+       cps.sql = ast_str_buffer(sql);
 
+       if (ast_string_field_init(&cps, 256)) {
+               ast_odbc_release_obj(obj);
+               return -1;
+       }
        stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
+       ast_string_field_free_memory(&cps);
 
        if (!stmt) {
                ast_odbc_release_obj(obj);
@@ -720,7 +760,7 @@ static int store_odbc(const char *database, const char *table, const struct ast_
        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);
+               ast_log(LOG_WARNING, "SQL Row Count error! [%s]\n", ast_str_buffer(sql));
                return -1;
        }
 
@@ -744,23 +784,19 @@ static int store_odbc(const char *database, const char *table, const struct ast_
  *
  * \retval number of rows affected
  * \retval -1 on failure
-*/
+ */
 static int destroy_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, const struct ast_variable *fields)
 {
        struct odbc_obj *obj;
        SQLHSTMT stmt;
-       char sql[256];
        SQLLEN rowcount=0;
+       struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
        const struct ast_variable *field;
        int res;
-       struct custom_prepare_struct cps = { .sql = sql, .extra = lookup, .fields = fields, };
+       struct custom_prepare_struct cps = { .extra = lookup, .fields = fields, };
        struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
 
-       if (!table) {
-               return -1;
-       }
-
-       if (ast_string_field_init(&cps, 256)) {
+       if (!table || !sql) {
                return -1;
        }
 
@@ -769,14 +805,20 @@ static int destroy_odbc(const char *database, const char *table, const char *key
                return -1;
        }
 
-       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE ", table);
-
+       ast_str_set(&sql, 0, "DELETE FROM %s WHERE ", table);
        for (field = fields; field; field = field->next) {
-               snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=? AND ", field->name);
+               ast_str_append(&sql, 0, "%s=? AND ", field->name);
        }
-       snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=?", keyfield);
+       ast_str_append(&sql, 0, "%s=?", keyfield);
 
+       cps.sql = ast_str_buffer(sql);
+
+       if (ast_string_field_init(&cps, 256)) {
+               ast_odbc_release_obj(obj);
+               return -1;
+       }
        stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
+       ast_string_field_free_memory(&cps);
 
        if (!stmt) {
                ast_odbc_release_obj(obj);
@@ -788,7 +830,7 @@ static int destroy_odbc(const char *database, const char *table, const char *key
        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);
+               ast_log(LOG_WARNING, "SQL Row Count error! [%s]\n", ast_str_buffer(sql));
                return -1;
        }
 
@@ -798,16 +840,41 @@ static int destroy_odbc(const char *database, const char *table, const char *key
        return -1;
 }
 
-
 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 */
+       char *var_val;
+       unsigned long var_val_size;
        SQLLEN err;
 };
 
+
+static SQLHSTMT length_determination_odbc_prepare(struct odbc_obj *obj, void *data)
+{
+       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)) {
+               ast_verb(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)) {
+               ast_verb(4, "Error in PREPARE %d\n", res);
+               SQLFreeHandle(SQL_HANDLE_STMT, sth);
+               return NULL;
+       }
+
+       SQLBindCol(sth, 1, SQL_C_ULONG, &q->var_val_size, sizeof(q->var_val_size), &q->err);
+
+       return sth;
+}
+
 static SQLHSTMT config_odbc_prepare(struct odbc_obj *obj, void *data)
 {
        struct config_odbc_obj *q = data;
@@ -830,7 +897,7 @@ static SQLHSTMT config_odbc_prepare(struct odbc_obj *obj, void *data)
        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);
+       SQLBindCol(sth, 4, SQL_C_CHAR, q->var_val, q->var_val_size, &q->err);
 
        return sth;
 }
@@ -841,9 +908,7 @@ static struct ast_config *config_odbc(const char *database, const char *table, c
        struct ast_category *cur_cat;
        int res = 0;
        struct odbc_obj *obj;
-       char sqlbuf[1024] = "";
-       char *sql = sqlbuf;
-       size_t sqlleft = sizeof(sqlbuf);
+       struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
        unsigned int last_cat_metric = 0;
        SQLSMALLINT rowcount = 0;
        SQLHSTMT stmt;
@@ -854,38 +919,86 @@ static struct ast_config *config_odbc(const char *database, const char *table, c
 
        memset(&q, 0, sizeof(q));
 
-       if (!file || !strcmp (file, "res_config_odbc.conf"))
+       if (!file || !strcmp (file, "res_config_odbc.conf") || !sql) {
                return NULL;            /* cant configure myself with myself ! */
+       }
 
        obj = ast_odbc_request_obj2(database, connected_flag);
        if (!obj)
                return NULL;
 
-       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;
+       ast_str_set(&sql, 0, "SELECT MAX(LENGTH(var_val)) FROM %s WHERE filename='%s'",
+               table, file);
+       q.sql = ast_str_buffer(sql);
 
-       stmt = ast_odbc_prepare_and_execute(obj, config_odbc_prepare, &q);
+       stmt = ast_odbc_prepare_and_execute(obj, length_determination_odbc_prepare, &q);
+       if (!stmt) {
+               ast_log(LOG_WARNING, "SQL select error! [%s]\n", ast_str_buffer(sql));
+               ast_odbc_release_obj(obj);
+               return NULL;
+       }
+
+       res = SQLNumResultCols(stmt, &rowcount);
 
+       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+               ast_log(LOG_WARNING, "SQL NumResultCols error! [%s]\n", ast_str_buffer(sql));
+               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
+               return NULL;
+       }
+
+       if (!rowcount) {
+               ast_log(LOG_NOTICE, "found nothing\n");
+               ast_odbc_release_obj(obj);
+               return cfg;
+       }
+
+       /* There will be only one result for this, the maximum length of a variable value */
+       if (SQLFetch(stmt) == SQL_NO_DATA) {
+               ast_log(LOG_NOTICE, "Failed to determine maximum length of a configuration value\n");
+               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
+               return NULL;
+       }
+
+       /* Reset stuff to a fresh state for the actual query which will retrieve all configuration */
+       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+
+       ast_str_set(&sql, 0, "SELECT cat_metric, category, var_name, var_val FROM %s ", table);
+       ast_str_append(&sql, 0, "WHERE filename='%s' AND commented=0 ", file);
+       ast_str_append(&sql, 0, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ");
+       q.sql = ast_str_buffer(sql);
+
+       q.var_val_size += 1;
+       q.var_val = ast_malloc(q.var_val_size);
+       if (!q.var_val) {
+               ast_log(LOG_WARNING, "Could not create buffer for reading in configuration values for '%s'\n", file);
+               ast_odbc_release_obj(obj);
+               return NULL;
+       }
+
+       stmt = ast_odbc_prepare_and_execute(obj, config_odbc_prepare, &q);
        if (!stmt) {
-               ast_log(LOG_WARNING, "SQL select error!\n[%s]\n\n", sql);
+               ast_log(LOG_WARNING, "SQL select error! [%s]\n", ast_str_buffer(sql));
                ast_odbc_release_obj(obj);
+               ast_free(q.var_val);
                return NULL;
        }
 
        res = SQLNumResultCols(stmt, &rowcount);
 
        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-               ast_log(LOG_WARNING, "SQL NumResultCols error!\n[%s]\n\n", sql);
+               ast_log(LOG_WARNING, "SQL NumResultCols error! [%s]\n", ast_str_buffer(sql));
                SQLFreeHandle(SQL_HANDLE_STMT, stmt);
                ast_odbc_release_obj(obj);
+               ast_free(q.var_val);
                return NULL;
        }
 
        if (!rowcount) {
                ast_log(LOG_NOTICE, "found nothing\n");
                ast_odbc_release_obj(obj);
+               ast_free(q.var_val);
                return cfg;
        }
 
@@ -896,14 +1009,14 @@ static struct ast_config *config_odbc(const char *database, const char *table, c
                        if (!ast_config_internal_load(q.var_val, cfg, loader_flags, "", who_asked)) {
                                SQLFreeHandle(SQL_HANDLE_STMT, stmt);
                                ast_odbc_release_obj(obj);
+                               ast_free(q.var_val);
                                return NULL;
                        }
                        continue;
-               } 
+               }
                if (strcmp(last, q.category) || last_cat_metric != q.cat_metric) {
-                       cur_cat = ast_category_new(q.category, "", 99999);
+                       cur_cat = ast_category_new_dynamic(q.category);
                        if (!cur_cat) {
-                               ast_log(LOG_WARNING, "Out of memory!\n");
                                break;
                        }
                        strcpy(last, q.category);
@@ -917,6 +1030,7 @@ static struct ast_config *config_odbc(const char *database, const char *table, c
 
        SQLFreeHandle(SQL_HANDLE_STMT, stmt);
        ast_odbc_release_obj(obj);
+       ast_free(q.var_val);
        return cfg;
 }
 
@@ -1102,14 +1216,13 @@ static int unload_module (void)
 {
        ast_config_engine_deregister(&odbc_engine);
 
-       ast_verb(1, "res_config_odbc unloaded.\n");
        return 0;
 }
 
 static int load_module (void)
 {
        ast_config_engine_register(&odbc_engine);
-       ast_verb(1, "res_config_odbc loaded.\n");
+
        return 0;
 }
 
@@ -1119,8 +1232,9 @@ static int reload_module(void)
 }
 
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Realtime ODBC configuration",
-               .load = load_module,
-               .unload = unload_module,
-               .reload = reload_module,
-               .load_pri = AST_MODPRI_REALTIME_DRIVER,
-               );
+       .support_level = AST_MODULE_SUPPORT_CORE,
+       .load = load_module,
+       .unload = unload_module,
+       .reload = reload_module,
+       .load_pri = AST_MODPRI_REALTIME_DRIVER,
+);