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