Merge "ari: Implement 'debug all' and request/response logging"
[asterisk/asterisk.git] / res / res_config_odbc.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2010, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * Copyright (C) 2004 - 2005 Anthony Minessale II <anthmct@yahoo.com>
9  *
10  * See http://www.asterisk.org for more information about
11  * the Asterisk project. Please do not directly contact
12  * any of the maintainers of this project for assistance;
13  * the project provides a web site, mailing lists and IRC
14  * channels for your use.
15  *
16  * This program is free software, distributed under the terms of
17  * the GNU General Public License Version 2. See the LICENSE file
18  * at the top of the source tree.
19  */
20
21 /*! \file
22  *
23  * \brief odbc+odbc plugin for portable configuration engine
24  *
25  * \author Mark Spencer <markster@digium.com>
26  * \author Anthony Minessale II <anthmct@yahoo.com>
27  *
28  * \arg http://www.unixodbc.org
29  */
30
31 /*** MODULEINFO
32         <depend>res_odbc</depend>
33         <support_level>core</support_level>
34  ***/
35
36 #include "asterisk.h"
37
38 #include "asterisk/file.h"
39 #include "asterisk/channel.h"
40 #include "asterisk/pbx.h"
41 #include "asterisk/config.h"
42 #include "asterisk/module.h"
43 #include "asterisk/lock.h"
44 #include "asterisk/res_odbc.h"
45 #include "asterisk/utils.h"
46 #include "asterisk/stringfields.h"
47
48 /*! Initial SQL query buffer size to allocate. */
49 #define SQL_BUF_SIZE    1024
50
51 AST_THREADSTORAGE(sql_buf);
52 AST_THREADSTORAGE(rowdata_buf);
53
54 struct custom_prepare_struct {
55         const char *sql;
56         const char *extra;
57         AST_DECLARE_STRING_FIELDS(
58                 AST_STRING_FIELD(encoding)[256];
59         );
60         const struct ast_variable *fields;
61         unsigned long long skip;
62 };
63
64 #define ENCODE_CHUNK(buffer, s) \
65         do { \
66                 char *eptr = buffer; \
67                 const char *vptr = s; \
68                 for (; *vptr && eptr < buffer + sizeof(buffer); vptr++) { \
69                         if (strchr("^;", *vptr)) { \
70                                 /* We use ^XX, instead of %XX because '%' is a special character in SQL */ \
71                                 snprintf(eptr, buffer + sizeof(buffer) - eptr, "^%02hhX", *vptr); \
72                                 eptr += 3; \
73                         } else { \
74                                 *eptr++ = *vptr; \
75                         } \
76                 } \
77                 if (eptr < buffer + sizeof(buffer)) { \
78                         *eptr = '\0'; \
79                 } else { \
80                         buffer[sizeof(buffer) - 1] = '\0'; \
81                 } \
82         } while(0)
83
84 static void decode_chunk(char *chunk)
85 {
86         for (; *chunk; chunk++) {
87                 if (*chunk == '^' && strchr("0123456789ABCDEF", chunk[1]) && strchr("0123456789ABCDEF", chunk[2])) {
88                         sscanf(chunk + 1, "%02hhX", (unsigned char *)chunk);
89                         memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
90                 }
91         }
92 }
93
94 static inline int is_text(const struct odbc_cache_columns *column)
95 {
96         return column->type == SQL_CHAR || column->type == SQL_VARCHAR || column->type == SQL_LONGVARCHAR
97                 || column->type == SQL_WCHAR || column->type == SQL_WVARCHAR || column->type == SQL_WLONGVARCHAR;
98 }
99
100 static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data)
101 {
102         int res, x = 1, count = 0;
103         struct custom_prepare_struct *cps = data;
104         const struct ast_variable *field;
105         char encodebuf[1024];
106         SQLHSTMT stmt;
107
108         res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
109         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
110                 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
111                 return NULL;
112         }
113
114         ast_debug(1, "Skip: %llu; SQL: %s\n", cps->skip, cps->sql);
115
116         res = SQLPrepare(stmt, (unsigned char *)cps->sql, SQL_NTS);
117         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
118                 ast_log(LOG_WARNING, "SQL Prepare failed! [%s]\n", cps->sql);
119                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
120                 return NULL;
121         }
122
123         for (field = cps->fields; field; field = field->next) {
124                 const char *newval = field->value;
125
126                 if ((1LL << count++) & cps->skip) {
127                         ast_debug(1, "Skipping field '%s'='%s' (%llo/%llo)\n", field->name, newval, 1ULL << (count - 1), cps->skip);
128                         continue;
129                 }
130                 ast_debug(1, "Parameter %d ('%s') = '%s'\n", x, field->name, newval);
131                 if (strchr(newval, ';') || strchr(newval, '^')) {
132                         ENCODE_CHUNK(encodebuf, newval);
133                         ast_string_field_set(cps, encoding[x], encodebuf);
134                         newval = cps->encoding[x];
135                 }
136                 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
137         }
138
139         if (!ast_strlen_zero(cps->extra)) {
140                 const char *newval = cps->extra;
141                 ast_debug(1, "Parameter %d = '%s'\n", x, newval);
142                 if (strchr(newval, ';') || strchr(newval, '^')) {
143                         ENCODE_CHUNK(encodebuf, newval);
144                         ast_string_field_set(cps, encoding[x], encodebuf);
145                         newval = cps->encoding[x];
146                 } 
147                 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
148         }
149
150         return stmt;
151 }
152
153 /*!
154  * \brief Excute an SQL query and return ast_variable list
155  * \param database
156  * \param table
157  * \param ap list containing one or more field/operator/value set.
158  *
159  * Select database and preform query on table, prepare the sql statement
160  * Sub-in the values to the prepared statement and execute it. Return results
161  * as a ast_variable list.
162  *
163  * \retval var on success
164  * \retval NULL on failure
165  */
166 static struct ast_variable *realtime_odbc(const char *database, const char *table, const struct ast_variable *fields)
167 {
168         struct odbc_obj *obj;
169         SQLHSTMT stmt;
170         char coltitle[256];
171         struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
172         struct ast_str *rowdata = ast_str_thread_get(&rowdata_buf, 128);
173         char *op;
174         const struct ast_variable *field = fields;
175         char *stringp;
176         char *chunk;
177         SQLSMALLINT collen;
178         int res;
179         int x;
180         struct ast_variable *var=NULL, *prev=NULL;
181         SQLULEN colsize;
182         SQLSMALLINT colcount=0;
183         SQLSMALLINT datatype;
184         SQLSMALLINT decimaldigits;
185         SQLSMALLINT nullable;
186         SQLLEN indicator;
187         struct custom_prepare_struct cps = { .fields = fields, };
188         struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
189
190         if (!table || !field || !sql || !rowdata) {
191                 return NULL;
192         }
193
194         obj = ast_odbc_request_obj2(database, connected_flag);
195         if (!obj) {
196                 ast_log(LOG_ERROR, "No database handle available with the name of '%s' (check res_odbc.conf)\n", database);
197                 return NULL;
198         }
199
200         op = !strchr(field->name, ' ') ? " =" : "";
201         ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s ?%s", table, field->name, op,
202                 strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\\\'" : "");
203         while ((field = field->next)) {
204                 op = !strchr(field->name, ' ') ? " =" : "";
205                 ast_str_append(&sql, 0, " AND %s%s ?%s", field->name, op,
206                         strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\\\'" : "");
207         }
208
209         cps.sql = ast_str_buffer(sql);
210
211         if (ast_string_field_init(&cps, 256)) {
212                 ast_odbc_release_obj(obj);
213                 return NULL;
214         }
215         stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
216         ast_string_field_free_memory(&cps);
217
218         if (!stmt) {
219                 ast_odbc_release_obj(obj);
220                 return NULL;
221         }
222
223         res = SQLNumResultCols(stmt, &colcount);
224         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
225                 ast_log(LOG_WARNING, "SQL Column Count error! [%s]\n", ast_str_buffer(sql));
226                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
227                 ast_odbc_release_obj(obj);
228                 return NULL;
229         }
230
231         res = SQLFetch(stmt);
232         if (res == SQL_NO_DATA) {
233                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
234                 ast_odbc_release_obj(obj);
235                 return NULL;
236         }
237         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
238                 ast_log(LOG_WARNING, "SQL Fetch error! [%s]\n", ast_str_buffer(sql));
239                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
240                 ast_odbc_release_obj(obj);
241                 return NULL;
242         }
243         for (x = 0; x < colcount; x++) {
244                 colsize = 0;
245                 collen = sizeof(coltitle);
246                 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
247                                         &datatype, &colsize, &decimaldigits, &nullable);
248                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
249                         ast_log(LOG_WARNING, "SQL Describe Column error! [%s]\n", ast_str_buffer(sql));
250                         if (var)
251                                 ast_variables_destroy(var);
252                         ast_odbc_release_obj(obj);
253                         return NULL;
254                 }
255
256                 ast_str_reset(rowdata);
257                 indicator = 0;
258
259                 res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata), ast_str_size(rowdata), &indicator);
260                 ast_str_update(rowdata);
261                 if (indicator == SQL_NULL_DATA) {
262                         ast_str_reset(rowdata);
263                 } else if (!ast_str_strlen(rowdata)) {
264                         /* Because we encode the empty string for a NULL, we will encode
265                          * actual empty strings as a string containing a single whitespace. */
266                         ast_str_set(&rowdata, -1, "%s", " ");
267                 } else if ((res == SQL_SUCCESS) || (res == SQL_SUCCESS_WITH_INFO)) {
268                         if (indicator != ast_str_strlen(rowdata)) {
269                                 /* If the available space was not enough to contain the row data enlarge and read in the rest */
270                                 ast_str_make_space(&rowdata, indicator + 1);
271                                 res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata) + ast_str_strlen(rowdata),
272                                         ast_str_size(rowdata) - ast_str_strlen(rowdata), &indicator);
273                                 ast_str_update(rowdata);
274                         }
275                 }
276
277                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
278                         ast_log(LOG_WARNING, "SQL Get Data error! [%s]\n", ast_str_buffer(sql));
279                         if (var)
280                                 ast_variables_destroy(var);
281                         ast_odbc_release_obj(obj);
282                         return NULL;
283                 }
284
285                 stringp = ast_str_buffer(rowdata);
286                 while (stringp) {
287                         chunk = strsep(&stringp, ";");
288                         if (!ast_strlen_zero(ast_strip(chunk))) {
289                                 if (strchr(chunk, '^')) {
290                                         decode_chunk(chunk);
291                                 }
292                                 if (prev) {
293                                         prev->next = ast_variable_new(coltitle, chunk, "");
294                                         if (prev->next) {
295                                                 prev = prev->next;
296                                         }
297                                 } else {
298                                         prev = var = ast_variable_new(coltitle, chunk, "");
299                                 }
300                         }
301                 }
302         }
303
304         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
305         ast_odbc_release_obj(obj);
306         return var;
307 }
308
309 /*!
310  * \brief Excute an Select query and return ast_config list
311  * \param database
312  * \param table
313  * \param ap list containing one or more field/operator/value set.
314  *
315  * Select database and preform query on table, prepare the sql statement
316  * Sub-in the values to the prepared statement and execute it. 
317  * Execute this prepared query against several ODBC connected databases.
318  * Return results as an ast_config variable.
319  *
320  * \retval var on success
321  * \retval NULL on failure
322  */
323 static struct ast_config *realtime_multi_odbc(const char *database, const char *table, const struct ast_variable *fields)
324 {
325         struct odbc_obj *obj;
326         SQLHSTMT stmt;
327         char coltitle[256];
328         struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
329         struct ast_str *rowdata = ast_str_thread_get(&rowdata_buf, 128);
330         const char *initfield;
331         char *op;
332         const struct ast_variable *field = fields;
333         char *stringp;
334         char *chunk;
335         SQLSMALLINT collen;
336         int res;
337         int x;
338         struct ast_variable *var=NULL;
339         struct ast_config *cfg=NULL;
340         struct ast_category *cat=NULL;
341         struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
342         SQLULEN colsize;
343         SQLSMALLINT colcount=0;
344         SQLSMALLINT datatype;
345         SQLSMALLINT decimaldigits;
346         SQLSMALLINT nullable;
347         SQLLEN indicator;
348         struct custom_prepare_struct cps = { .fields = fields, };
349
350         if (!table || !field || !sql || !rowdata) {
351                 return NULL;
352         }
353
354         obj = ast_odbc_request_obj2(database, connected_flag);
355         if (!obj) {
356                 return NULL;
357         }
358
359         initfield = ast_strdupa(field->name);
360         if ((op = strchr(initfield, ' '))) {
361                 *op = '\0';
362         }
363
364         op = !strchr(field->name, ' ') ? " =" : "";
365         ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s ?%s", table, field->name, op,
366                 strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\\\'" : "");
367         while ((field = field->next)) {
368                 op = !strchr(field->name, ' ') ? " =" : "";
369                 ast_str_append(&sql, 0, " AND %s%s ?%s", field->name, op,
370                         strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\\\'" : "");
371         }
372         ast_str_append(&sql, 0, " ORDER BY %s", initfield);
373
374         cps.sql = ast_str_buffer(sql);
375
376         if (ast_string_field_init(&cps, 256)) {
377                 ast_odbc_release_obj(obj);
378                 return NULL;
379         }
380         stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
381         ast_string_field_free_memory(&cps);
382
383         if (!stmt) {
384                 ast_odbc_release_obj(obj);
385                 return NULL;
386         }
387
388         res = SQLNumResultCols(stmt, &colcount);
389         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
390                 ast_log(LOG_WARNING, "SQL Column Count error! [%s]\n", ast_str_buffer(sql));
391                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
392                 ast_odbc_release_obj(obj);
393                 return NULL;
394         }
395
396         cfg = ast_config_new();
397         if (!cfg) {
398                 ast_log(LOG_WARNING, "Out of memory!\n");
399                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
400                 ast_odbc_release_obj(obj);
401                 return NULL;
402         }
403
404         while ((res=SQLFetch(stmt)) != SQL_NO_DATA) {
405                 var = NULL;
406                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
407                         ast_log(LOG_WARNING, "SQL Fetch error! [%s]\n", ast_str_buffer(sql));
408                         continue;
409                 }
410                 cat = ast_category_new("","",99999);
411                 if (!cat) {
412                         ast_log(LOG_WARNING, "Out of memory!\n");
413                         continue;
414                 }
415                 for (x=0;x<colcount;x++) {
416                         colsize = 0;
417                         collen = sizeof(coltitle);
418                         res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
419                                                 &datatype, &colsize, &decimaldigits, &nullable);
420                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
421                                 ast_log(LOG_WARNING, "SQL Describe Column error! [%s]\n", ast_str_buffer(sql));
422                                 ast_category_destroy(cat);
423                                 goto next_sql_fetch;
424                         }
425
426                         ast_str_reset(rowdata);
427                         indicator = 0;
428
429                         res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata), ast_str_size(rowdata), &indicator);
430                         ast_str_update(rowdata);
431                         if (indicator == SQL_NULL_DATA) {
432                                 continue;
433                         }
434
435                         if ((res == SQL_SUCCESS) || (res == SQL_SUCCESS_WITH_INFO)) {
436                                 if (indicator != ast_str_strlen(rowdata)) {
437                                         /* If the available space was not enough to contain the row data enlarge and read in the rest */
438                                         ast_str_make_space(&rowdata, indicator + 1);
439                                         res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata) + ast_str_strlen(rowdata),
440                                                 ast_str_size(rowdata) - ast_str_strlen(rowdata), &indicator);
441                                         ast_str_update(rowdata);
442                                 }
443                         }
444
445                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
446                                 ast_log(LOG_WARNING, "SQL Get Data error! [%s]\n", ast_str_buffer(sql));
447                                 ast_category_destroy(cat);
448                                 goto next_sql_fetch;
449                         }
450                         stringp = ast_str_buffer(rowdata);
451                         while (stringp) {
452                                 chunk = strsep(&stringp, ";");
453                                 if (!ast_strlen_zero(ast_strip(chunk))) {
454                                         if (strchr(chunk, '^')) {
455                                                 decode_chunk(chunk);
456                                         }
457                                         if (!strcmp(initfield, coltitle)) {
458                                                 ast_category_rename(cat, chunk);
459                                         }
460                                         var = ast_variable_new(coltitle, chunk, "");
461                                         ast_variable_append(cat, var);
462                                 }
463                         }
464                 }
465                 ast_category_append(cfg, cat);
466 next_sql_fetch:;
467         }
468
469         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
470         ast_odbc_release_obj(obj);
471         return cfg;
472 }
473
474 /*!
475  * \brief Excute an UPDATE query
476  * \param database
477  * \param table
478  * \param keyfield where clause field
479  * \param lookup value of field for where clause
480  * \param ap list containing one or more field/value set(s).
481  *
482  * Update a database table, prepare the sql statement using keyfield and lookup
483  * control the number of records to change. All values to be changed are stored in ap list.
484  * Sub-in the values to the prepared statement and execute it.
485  *
486  * \retval number of rows affected
487  * \retval -1 on failure
488  */
489 static int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, const struct ast_variable *fields)
490 {
491         struct odbc_obj *obj;
492         SQLHSTMT stmt;
493         SQLLEN rowcount=0;
494         struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
495         const struct ast_variable *field = fields;
496         int res, count = 0, paramcount = 0;
497         struct custom_prepare_struct cps = { .extra = lookup, .fields = fields, };
498         struct odbc_cache_tables *tableptr;
499         struct odbc_cache_columns *column = NULL;
500         struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
501
502         if (!table || !field || !keyfield || !sql) {
503                 return -1;
504         }
505
506         tableptr = ast_odbc_find_table(database, table);
507         if (!(obj = ast_odbc_request_obj2(database, connected_flag))) {
508                 ast_odbc_release_table(tableptr);
509                 return -1;
510         }
511
512         if (tableptr && !ast_odbc_find_column(tableptr, keyfield)) {
513                 ast_log(LOG_WARNING, "Key field '%s' does not exist in table '%s@%s'.  Update will fail\n", keyfield, table, database);
514         }
515
516         ast_str_set(&sql, 0, "UPDATE %s SET ", table);
517         while (field) {
518                 if ((tableptr && (column = ast_odbc_find_column(tableptr, field->name))) || count >= 64) {
519                         if (paramcount++) {
520                                 ast_str_append(&sql, 0, ", ");
521                         }
522                         /* NULL test for non-text columns */
523                         if (count < 64 && ast_strlen_zero(field->value) && column->nullable && !is_text(column)) {
524                                 ast_str_append(&sql, 0, "%s=NULL", field->name);
525                                 cps.skip |= (1LL << count);
526                         } else {
527                                 /* Value is not an empty string, or column is of text type, or we couldn't fit any more into cps.skip (count >= 64 ?!). */
528                                 ast_str_append(&sql, 0, "%s=?", field->name);
529                         }
530                 } else { /* the column does not exist in the table */
531                         cps.skip |= (1LL << count);
532                 }
533                 ++count;
534                 field = field->next;
535         }
536         ast_str_append(&sql, 0, " WHERE %s=?", keyfield);
537         ast_odbc_release_table(tableptr);
538
539         cps.sql = ast_str_buffer(sql);
540
541         if (ast_string_field_init(&cps, 256)) {
542                 ast_odbc_release_obj(obj);
543                 return -1;
544         }
545         stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
546         ast_string_field_free_memory(&cps);
547
548         if (!stmt) {
549                 ast_odbc_release_obj(obj);
550                 return -1;
551         }
552
553         res = SQLRowCount(stmt, &rowcount);
554         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
555         ast_odbc_release_obj(obj);
556
557         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
558                 ast_log(LOG_WARNING, "SQL Row Count error! [%s]\n", ast_str_buffer(sql));
559                 return -1;
560         }
561
562         if (rowcount >= 0) {
563                 return (int) rowcount;
564         }
565
566         return -1;
567 }
568
569 struct update2_prepare_struct {
570         const char *database;
571         const char *table;
572         const struct ast_variable *lookup_fields;
573         const struct ast_variable *update_fields;
574 };
575
576 static SQLHSTMT update2_prepare(struct odbc_obj *obj, void *data)
577 {
578         int res, x = 1, first = 1;
579         struct update2_prepare_struct *ups = data;
580         const struct ast_variable *field;
581         struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
582         SQLHSTMT stmt;
583         struct odbc_cache_tables *tableptr;
584
585         if (!sql) {
586                 return NULL;
587         }
588
589         tableptr = ast_odbc_find_table(ups->database, ups->table);
590         if (!tableptr) {
591                 ast_log(LOG_ERROR, "Could not retrieve metadata for table '%s@%s'.  Update will fail!\n", ups->table, ups->database);
592                 return NULL;
593         }
594
595         res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
596         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
597                 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
598                 ast_odbc_release_table(tableptr);
599                 return NULL;
600         }
601
602         ast_str_set(&sql, 0, "UPDATE %s SET ", ups->table);
603
604         for (field = ups->update_fields; field; field = field->next) {
605                 if (ast_odbc_find_column(tableptr, field->name)) {
606                         ast_str_append(&sql, 0, "%s%s=? ", first ? "" : ", ", field->name);
607                         SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(field->name), 0, (void *)field->value, 0, NULL);
608                         first = 0;
609                 } else {
610                         ast_log(LOG_NOTICE, "Not updating column '%s' in '%s@%s' because that column does not exist!\n", field->name, ups->table, ups->database);
611                 }
612         }
613
614         ast_str_append(&sql, 0, "WHERE");
615         first = 1;
616
617         for (field = ups->lookup_fields; field; field = field->next) {
618                 if (!ast_odbc_find_column(tableptr, field->name)) {
619                         ast_log(LOG_ERROR, "One or more of the criteria columns '%s' on '%s@%s' for this update does not exist!\n", field->name, ups->table, ups->database);
620                         ast_odbc_release_table(tableptr);
621                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
622                         return NULL;
623                 }
624                 ast_str_append(&sql, 0, "%s %s=?", first ? "" : " AND", field->name);
625                 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(field->value), 0, (void *)field->value, 0, NULL);
626                 first = 0;
627         }
628
629         /* Done with the table metadata */
630         ast_odbc_release_table(tableptr);
631
632         res = SQLPrepare(stmt, (unsigned char *)ast_str_buffer(sql), SQL_NTS);
633         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
634                 ast_log(LOG_WARNING, "SQL Prepare failed! [%s]\n", ast_str_buffer(sql));
635                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
636                 return NULL;
637         }
638
639         return stmt;
640 }
641
642 /*!
643  * \brief Execute an UPDATE query
644  * \param database
645  * \param table
646  * \param ap list containing one or more field/value set(s).
647  *
648  * Update a database table, preparing the sql statement from a list of
649  * key/value pairs specified in ap.  The lookup pairs are specified first
650  * and are separated from the update pairs by a sentinel value.
651  * Sub-in the values to the prepared statement and execute it.
652  *
653  * \retval number of rows affected
654  * \retval -1 on failure
655 */
656 static int update2_odbc(const char *database, const char *table, const struct ast_variable *lookup_fields, const struct ast_variable *update_fields)
657 {
658         struct odbc_obj *obj;
659         SQLHSTMT stmt;
660         struct update2_prepare_struct ups = { .database = database, .table = table, .lookup_fields = lookup_fields, .update_fields = update_fields, };
661         struct ast_str *sql;
662         int res;
663         SQLLEN rowcount = 0;
664
665         if (!(obj = ast_odbc_request_obj(database, 0))) {
666                 return -1;
667         }
668
669         if (!(stmt = ast_odbc_prepare_and_execute(obj, update2_prepare, &ups))) {
670                 ast_odbc_release_obj(obj);
671                 return -1;
672         }
673
674         res = SQLRowCount(stmt, &rowcount);
675         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
676         ast_odbc_release_obj(obj);
677
678         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
679                 /* Since only a single thread can access this memory, we can retrieve what would otherwise be lost. */
680                 sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
681                 ast_assert(sql != NULL);
682                 ast_log(LOG_WARNING, "SQL Row Count error! [%s]\n", ast_str_buffer(sql));
683                 return -1;
684         }
685
686         if (rowcount >= 0) {
687                 return (int)rowcount;
688         }
689
690         return -1;
691 }
692
693 /*!
694  * \brief Excute an INSERT query
695  * \param database
696  * \param table
697  * \param ap list containing one or more field/value set(s)
698  *
699  * Insert a new record into database table, prepare the sql statement.
700  * All values to be changed are stored in ap list.
701  * Sub-in the values to the prepared statement and execute it.
702  *
703  * \retval number of rows affected
704  * \retval -1 on failure
705  */
706 static int store_odbc(const char *database, const char *table, const struct ast_variable *fields)
707 {
708         struct odbc_obj *obj;
709         SQLHSTMT stmt;
710         SQLLEN rowcount=0;
711         const struct ast_variable *field = fields;
712         struct ast_str *keys;
713         struct ast_str *vals;
714         struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
715         int res;
716         struct custom_prepare_struct cps = { .fields = fields, };
717         struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
718
719         keys = ast_str_create(SQL_BUF_SIZE / 2);
720         vals = ast_str_create(SQL_BUF_SIZE / 4);
721         if (!table || !field || !keys || !vals || !sql) {
722                 ast_free(vals);
723                 ast_free(keys);
724                 return -1;
725         }
726
727         obj = ast_odbc_request_obj2(database, connected_flag);
728         if (!obj) {
729                 ast_free(vals);
730                 ast_free(keys);
731                 return -1;
732         }
733
734         ast_str_set(&keys, 0, "%s", field->name);
735         ast_str_set(&vals, 0, "?");
736         while ((field = field->next)) {
737                 ast_str_append(&keys, 0, ", %s", field->name);
738                 ast_str_append(&vals, 0, ", ?");
739         }
740         ast_str_set(&sql, 0, "INSERT INTO %s (%s) VALUES (%s)",
741                 table, ast_str_buffer(keys), ast_str_buffer(vals));
742
743         ast_free(vals);
744         ast_free(keys);
745         cps.sql = ast_str_buffer(sql);
746
747         if (ast_string_field_init(&cps, 256)) {
748                 ast_odbc_release_obj(obj);
749                 return -1;
750         }
751         stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
752         ast_string_field_free_memory(&cps);
753
754         if (!stmt) {
755                 ast_odbc_release_obj(obj);
756                 return -1;
757         }
758
759         res = SQLRowCount(stmt, &rowcount);
760         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
761         ast_odbc_release_obj(obj);
762
763         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
764                 ast_log(LOG_WARNING, "SQL Row Count error! [%s]\n", ast_str_buffer(sql));
765                 return -1;
766         }
767
768         if (rowcount >= 0)
769                 return (int)rowcount;
770
771         return -1;
772 }
773
774 /*!
775  * \brief Excute an DELETE query
776  * \param database
777  * \param table
778  * \param keyfield where clause field
779  * \param lookup value of field for where clause
780  * \param ap list containing one or more field/value set(s)
781  *
782  * Delete a row from a database table, prepare the sql statement using keyfield and lookup
783  * control the number of records to change. Additional params to match rows are stored in ap list.
784  * Sub-in the values to the prepared statement and execute it.
785  *
786  * \retval number of rows affected
787  * \retval -1 on failure
788  */
789 static int destroy_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, const struct ast_variable *fields)
790 {
791         struct odbc_obj *obj;
792         SQLHSTMT stmt;
793         SQLLEN rowcount=0;
794         struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
795         const struct ast_variable *field;
796         int res;
797         struct custom_prepare_struct cps = { .extra = lookup, .fields = fields, };
798         struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
799
800         if (!table || !sql) {
801                 return -1;
802         }
803
804         obj = ast_odbc_request_obj2(database, connected_flag);
805         if (!obj) {
806                 return -1;
807         }
808
809         ast_str_set(&sql, 0, "DELETE FROM %s WHERE ", table);
810         for (field = fields; field; field = field->next) {
811                 ast_str_append(&sql, 0, "%s=? AND ", field->name);
812         }
813         ast_str_append(&sql, 0, "%s=?", keyfield);
814
815         cps.sql = ast_str_buffer(sql);
816
817         if (ast_string_field_init(&cps, 256)) {
818                 ast_odbc_release_obj(obj);
819                 return -1;
820         }
821         stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
822         ast_string_field_free_memory(&cps);
823
824         if (!stmt) {
825                 ast_odbc_release_obj(obj);
826                 return -1;
827         }
828
829         res = SQLRowCount(stmt, &rowcount);
830         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
831         ast_odbc_release_obj(obj);
832
833         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
834                 ast_log(LOG_WARNING, "SQL Row Count error! [%s]\n", ast_str_buffer(sql));
835                 return -1;
836         }
837
838         if (rowcount >= 0)
839                 return (int)rowcount;
840
841         return -1;
842 }
843
844 struct config_odbc_obj {
845         char *sql;
846         unsigned long cat_metric;
847         char category[128];
848         char var_name[128];
849         char *var_val;
850         unsigned long var_val_size;
851         SQLLEN err;
852 };
853
854
855 static SQLHSTMT length_determination_odbc_prepare(struct odbc_obj *obj, void *data)
856 {
857         struct config_odbc_obj *q = data;
858         SQLHSTMT sth;
859         int res;
860
861         res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &sth);
862         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
863                 ast_verb(4, "Failure in AllocStatement %d\n", res);
864                 return NULL;
865         }
866
867         res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS);
868         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
869                 ast_verb(4, "Error in PREPARE %d\n", res);
870                 SQLFreeHandle(SQL_HANDLE_STMT, sth);
871                 return NULL;
872         }
873
874         SQLBindCol(sth, 1, SQL_C_ULONG, &q->var_val_size, sizeof(q->var_val_size), &q->err);
875
876         return sth;
877 }
878
879 static SQLHSTMT config_odbc_prepare(struct odbc_obj *obj, void *data)
880 {
881         struct config_odbc_obj *q = data;
882         SQLHSTMT sth;
883         int res;
884
885         res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &sth);
886         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
887                 ast_verb(4, "Failure in AllocStatement %d\n", res);
888                 return NULL;
889         }
890
891         res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS);
892         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
893                 ast_verb(4, "Error in PREPARE %d\n", res);
894                 SQLFreeHandle(SQL_HANDLE_STMT, sth);
895                 return NULL;
896         }
897
898         SQLBindCol(sth, 1, SQL_C_ULONG, &q->cat_metric, sizeof(q->cat_metric), &q->err);
899         SQLBindCol(sth, 2, SQL_C_CHAR, q->category, sizeof(q->category), &q->err);
900         SQLBindCol(sth, 3, SQL_C_CHAR, q->var_name, sizeof(q->var_name), &q->err);
901         SQLBindCol(sth, 4, SQL_C_CHAR, q->var_val, q->var_val_size, &q->err);
902
903         return sth;
904 }
905
906 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)
907 {
908         struct ast_variable *new_v;
909         struct ast_category *cur_cat;
910         int res = 0;
911         struct odbc_obj *obj;
912         struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
913         unsigned int last_cat_metric = 0;
914         SQLSMALLINT rowcount = 0;
915         SQLHSTMT stmt;
916         char last[128] = "";
917         struct config_odbc_obj q;
918         struct ast_flags loader_flags = { 0 };
919         struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
920
921         memset(&q, 0, sizeof(q));
922
923         if (!file || !strcmp (file, "res_config_odbc.conf") || !sql) {
924                 return NULL;            /* cant configure myself with myself ! */
925         }
926
927         obj = ast_odbc_request_obj2(database, connected_flag);
928         if (!obj)
929                 return NULL;
930
931         ast_str_set(&sql, 0, "SELECT MAX(LENGTH(var_val)) FROM %s WHERE filename='%s'",
932                 table, file);
933         q.sql = ast_str_buffer(sql);
934
935         stmt = ast_odbc_prepare_and_execute(obj, length_determination_odbc_prepare, &q);
936         if (!stmt) {
937                 ast_log(LOG_WARNING, "SQL select error! [%s]\n", ast_str_buffer(sql));
938                 ast_odbc_release_obj(obj);
939                 return NULL;
940         }
941
942         res = SQLNumResultCols(stmt, &rowcount);
943
944         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
945                 ast_log(LOG_WARNING, "SQL NumResultCols error! [%s]\n", ast_str_buffer(sql));
946                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
947                 ast_odbc_release_obj(obj);
948                 return NULL;
949         }
950
951         if (!rowcount) {
952                 ast_log(LOG_NOTICE, "found nothing\n");
953                 ast_odbc_release_obj(obj);
954                 return cfg;
955         }
956
957         /* There will be only one result for this, the maximum length of a variable value */
958         if (SQLFetch(stmt) == SQL_NO_DATA) {
959                 ast_log(LOG_NOTICE, "Failed to determine maximum length of a configuration value\n");
960                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
961                 ast_odbc_release_obj(obj);
962                 return NULL;
963         }
964
965         /* Reset stuff to a fresh state for the actual query which will retrieve all configuration */
966         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
967
968         ast_str_set(&sql, 0, "SELECT cat_metric, category, var_name, var_val FROM %s ", table);
969         ast_str_append(&sql, 0, "WHERE filename='%s' AND commented=0 ", file);
970         ast_str_append(&sql, 0, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ");
971         q.sql = ast_str_buffer(sql);
972
973         q.var_val_size += 1;
974         q.var_val = ast_malloc(q.var_val_size);
975         if (!q.var_val) {
976                 ast_log(LOG_WARNING, "Could not create buffer for reading in configuration values for '%s'\n", file);
977                 ast_odbc_release_obj(obj);
978                 return NULL;
979         }
980
981         stmt = ast_odbc_prepare_and_execute(obj, config_odbc_prepare, &q);
982         if (!stmt) {
983                 ast_log(LOG_WARNING, "SQL select error! [%s]\n", ast_str_buffer(sql));
984                 ast_odbc_release_obj(obj);
985                 ast_free(q.var_val);
986                 return NULL;
987         }
988
989         res = SQLNumResultCols(stmt, &rowcount);
990
991         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
992                 ast_log(LOG_WARNING, "SQL NumResultCols error! [%s]\n", ast_str_buffer(sql));
993                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
994                 ast_odbc_release_obj(obj);
995                 ast_free(q.var_val);
996                 return NULL;
997         }
998
999         if (!rowcount) {
1000                 ast_log(LOG_NOTICE, "found nothing\n");
1001                 ast_odbc_release_obj(obj);
1002                 ast_free(q.var_val);
1003                 return cfg;
1004         }
1005
1006         cur_cat = ast_config_get_current_category(cfg);
1007
1008         while ((res = SQLFetch(stmt)) != SQL_NO_DATA) {
1009                 if (!strcmp (q.var_name, "#include")) {
1010                         if (!ast_config_internal_load(q.var_val, cfg, loader_flags, "", who_asked)) {
1011                                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1012                                 ast_odbc_release_obj(obj);
1013                                 ast_free(q.var_val);
1014                                 return NULL;
1015                         }
1016                         continue;
1017                 } 
1018                 if (strcmp(last, q.category) || last_cat_metric != q.cat_metric) {
1019                         cur_cat = ast_category_new(q.category, "", 99999);
1020                         if (!cur_cat) {
1021                                 ast_log(LOG_WARNING, "Out of memory!\n");
1022                                 break;
1023                         }
1024                         strcpy(last, q.category);
1025                         last_cat_metric = q.cat_metric;
1026                         ast_category_append(cfg, cur_cat);
1027                 }
1028
1029                 new_v = ast_variable_new(q.var_name, q.var_val, "");
1030                 ast_variable_append(cur_cat, new_v);
1031         }
1032
1033         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1034         ast_odbc_release_obj(obj);
1035         ast_free(q.var_val);
1036         return cfg;
1037 }
1038
1039 #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)
1040 #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)
1041
1042 static int require_odbc(const char *database, const char *table, va_list ap)
1043 {
1044         struct odbc_cache_tables *tableptr = ast_odbc_find_table(database, table);
1045         struct odbc_cache_columns *col;
1046         char *elm;
1047         int type, size;
1048
1049         if (!tableptr) {
1050                 return -1;
1051         }
1052
1053         while ((elm = va_arg(ap, char *))) {
1054                 type = va_arg(ap, require_type);
1055                 size = va_arg(ap, int);
1056                 /* Check if the field matches the criteria */
1057                 AST_RWLIST_TRAVERSE(&tableptr->columns, col, list) {
1058                         if (strcmp(col->name, elm) == 0) {
1059                                 /* Type check, first.  Some fields are more particular than others */
1060                                 switch (col->type) {
1061                                 case SQL_CHAR:
1062                                 case SQL_VARCHAR:
1063                                 case SQL_LONGVARCHAR:
1064 #ifdef HAVE_ODBC_WCHAR
1065                                 case SQL_WCHAR:
1066                                 case SQL_WVARCHAR:
1067                                 case SQL_WLONGVARCHAR:
1068 #endif
1069                                 case SQL_BINARY:
1070                                 case SQL_VARBINARY:
1071                                 case SQL_LONGVARBINARY:
1072                                 case SQL_GUID:
1073 #define CHECK_SIZE(n) \
1074                                                 if (col->size < n) {      \
1075                                                         warn_length(col, n);  \
1076                                                 }                         \
1077                                                 break;
1078                                         switch (type) {
1079                                         case RQ_UINTEGER1: CHECK_SIZE(3)  /*         255 */
1080                                         case RQ_INTEGER1:  CHECK_SIZE(4)  /*        -128 */
1081                                         case RQ_UINTEGER2: CHECK_SIZE(5)  /*       65535 */
1082                                         case RQ_INTEGER2:  CHECK_SIZE(6)  /*      -32768 */
1083                                         case RQ_UINTEGER3:                /*    16777215 */
1084                                         case RQ_INTEGER3:  CHECK_SIZE(8)  /*    -8388608 */
1085                                         case RQ_DATE:                     /*  2008-06-09 */
1086                                         case RQ_UINTEGER4: CHECK_SIZE(10) /*  4200000000 */
1087                                         case RQ_INTEGER4:  CHECK_SIZE(11) /* -2100000000 */
1088                                         case RQ_DATETIME:                 /* 2008-06-09 16:03:47 */
1089                                         case RQ_UINTEGER8: CHECK_SIZE(19) /* trust me    */
1090                                         case RQ_INTEGER8:  CHECK_SIZE(20) /* ditto       */
1091                                         case RQ_FLOAT:
1092                                         case RQ_CHAR:      CHECK_SIZE(size)
1093                                         }
1094 #undef CHECK_SIZE
1095                                         break;
1096                                 case SQL_TYPE_DATE:
1097                                         if (type != RQ_DATE) {
1098                                                 warn_type(col, type);
1099                                         }
1100                                         break;
1101                                 case SQL_TYPE_TIMESTAMP:
1102                                 case SQL_TIMESTAMP:
1103                                         if (type != RQ_DATE && type != RQ_DATETIME) {
1104                                                 warn_type(col, type);
1105                                         }
1106                                         break;
1107                                 case SQL_BIT:
1108                                         warn_length(col, size);
1109                                         break;
1110 #define WARN_TYPE_OR_LENGTH(n)  \
1111                                                 if (!ast_rq_is_int(type)) {  \
1112                                                         warn_type(col, type);    \
1113                                                 } else {                     \
1114                                                         warn_length(col, n);  \
1115                                                 }
1116                                 case SQL_TINYINT:
1117                                         if (type != RQ_UINTEGER1) {
1118                                                 WARN_TYPE_OR_LENGTH(size)
1119                                         }
1120                                         break;
1121                                 case SQL_C_STINYINT:
1122                                         if (type != RQ_INTEGER1) {
1123                                                 WARN_TYPE_OR_LENGTH(size)
1124                                         }
1125                                         break;
1126                                 case SQL_C_USHORT:
1127                                         if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_UINTEGER2) {
1128                                                 WARN_TYPE_OR_LENGTH(size)
1129                                         }
1130                                         break;
1131                                 case SQL_SMALLINT:
1132                                 case SQL_C_SSHORT:
1133                                         if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_INTEGER2) {
1134                                                 WARN_TYPE_OR_LENGTH(size)
1135                                         }
1136                                         break;
1137                                 case SQL_C_ULONG:
1138                                         if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
1139                                                 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
1140                                                 type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
1141                                                 type != RQ_INTEGER4) {
1142                                                 WARN_TYPE_OR_LENGTH(size)
1143                                         }
1144                                         break;
1145                                 case SQL_INTEGER:
1146                                 case SQL_C_SLONG:
1147                                         if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
1148                                                 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
1149                                                 type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
1150                                                 type != RQ_INTEGER4) {
1151                                                 WARN_TYPE_OR_LENGTH(size)
1152                                         }
1153                                         break;
1154                                 case SQL_C_UBIGINT:
1155                                         if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
1156                                                 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
1157                                                 type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
1158                                                 type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
1159                                                 type != RQ_INTEGER8) {
1160                                                 WARN_TYPE_OR_LENGTH(size)
1161                                         }
1162                                         break;
1163                                 case SQL_BIGINT:
1164                                 case SQL_C_SBIGINT:
1165                                         if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
1166                                                 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
1167                                                 type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
1168                                                 type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
1169                                                 type != RQ_INTEGER8) {
1170                                                 WARN_TYPE_OR_LENGTH(size)
1171                                         }
1172                                         break;
1173 #undef WARN_TYPE_OR_LENGTH
1174                                 case SQL_NUMERIC:
1175                                 case SQL_DECIMAL:
1176                                 case SQL_FLOAT:
1177                                 case SQL_REAL:
1178                                 case SQL_DOUBLE:
1179                                         if (!ast_rq_is_int(type) && type != RQ_FLOAT) {
1180                                                 warn_type(col, type);
1181                                         }
1182                                         break;
1183                                 default:
1184                                         ast_log(LOG_WARNING, "Realtime table %s@%s: column type (%d) unrecognized for column '%s'\n", table, database, col->type, elm);
1185                                 }
1186                                 break;
1187                         }
1188                 }
1189                 if (!col) {
1190                         ast_log(LOG_WARNING, "Realtime table %s@%s requires column '%s', but that column does not exist!\n", table, database, elm);
1191                 }
1192         }
1193         AST_RWLIST_UNLOCK(&tableptr->columns);
1194         return 0;
1195 }
1196 #undef warn_length
1197 #undef warn_type
1198
1199 static int unload_odbc(const char *a, const char *b)
1200 {
1201         return ast_odbc_clear_cache(a, b);
1202 }
1203
1204 static struct ast_config_engine odbc_engine = {
1205         .name = "odbc",
1206         .load_func = config_odbc,
1207         .realtime_func = realtime_odbc,
1208         .realtime_multi_func = realtime_multi_odbc,
1209         .store_func = store_odbc,
1210         .destroy_func = destroy_odbc,
1211         .update_func = update_odbc,
1212         .update2_func = update2_odbc,
1213         .require_func = require_odbc,
1214         .unload_func = unload_odbc,
1215 };
1216
1217 static int unload_module (void)
1218 {
1219         ast_config_engine_deregister(&odbc_engine);
1220
1221         return 0;
1222 }
1223
1224 static int load_module (void)
1225 {
1226         ast_config_engine_register(&odbc_engine);
1227
1228         return 0;
1229 }
1230
1231 static int reload_module(void)
1232 {
1233         return 0;
1234 }
1235
1236 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Realtime ODBC configuration",
1237         .support_level = AST_MODULE_SUPPORT_CORE,
1238         .load = load_module,
1239         .unload = unload_module,
1240         .reload = reload_module,
1241         .load_pri = AST_MODPRI_REALTIME_DRIVER,
1242 );