realtime: Centralize some common realtime backend code
[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_anonymous();
411                 if (!cat) {
412                         continue;
413                 }
414                 for (x=0;x<colcount;x++) {
415                         colsize = 0;
416                         collen = sizeof(coltitle);
417                         res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
418                                                 &datatype, &colsize, &decimaldigits, &nullable);
419                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
420                                 ast_log(LOG_WARNING, "SQL Describe Column error! [%s]\n", ast_str_buffer(sql));
421                                 ast_category_destroy(cat);
422                                 goto next_sql_fetch;
423                         }
424
425                         ast_str_reset(rowdata);
426                         indicator = 0;
427
428                         res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata), ast_str_size(rowdata), &indicator);
429                         ast_str_update(rowdata);
430                         if (indicator == SQL_NULL_DATA) {
431                                 continue;
432                         }
433
434                         if ((res == SQL_SUCCESS) || (res == SQL_SUCCESS_WITH_INFO)) {
435                                 if (indicator != ast_str_strlen(rowdata)) {
436                                         /* If the available space was not enough to contain the row data enlarge and read in the rest */
437                                         ast_str_make_space(&rowdata, indicator + 1);
438                                         res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata) + ast_str_strlen(rowdata),
439                                                 ast_str_size(rowdata) - ast_str_strlen(rowdata), &indicator);
440                                         ast_str_update(rowdata);
441                                 }
442                         }
443
444                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
445                                 ast_log(LOG_WARNING, "SQL Get Data error! [%s]\n", ast_str_buffer(sql));
446                                 ast_category_destroy(cat);
447                                 goto next_sql_fetch;
448                         }
449                         stringp = ast_str_buffer(rowdata);
450                         while (stringp) {
451                                 chunk = strsep(&stringp, ";");
452                                 if (!ast_strlen_zero(ast_strip(chunk))) {
453                                         if (strchr(chunk, '^')) {
454                                                 decode_chunk(chunk);
455                                         }
456                                         if (!strcmp(initfield, coltitle)) {
457                                                 ast_category_rename(cat, chunk);
458                                         }
459                                         var = ast_variable_new(coltitle, chunk, "");
460                                         ast_variable_append(cat, var);
461                                 }
462                         }
463                 }
464                 ast_category_append(cfg, cat);
465 next_sql_fetch:;
466         }
467
468         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
469         ast_odbc_release_obj(obj);
470         return cfg;
471 }
472
473 /*!
474  * \brief Excute an UPDATE query
475  * \param database
476  * \param table
477  * \param keyfield where clause field
478  * \param lookup value of field for where clause
479  * \param ap list containing one or more field/value set(s).
480  *
481  * Update a database table, prepare the sql statement using keyfield and lookup
482  * control the number of records to change. All values to be changed are stored in ap list.
483  * Sub-in the values to the prepared statement and execute it.
484  *
485  * \retval number of rows affected
486  * \retval -1 on failure
487  */
488 static int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, const struct ast_variable *fields)
489 {
490         struct odbc_obj *obj;
491         SQLHSTMT stmt;
492         SQLLEN rowcount=0;
493         struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
494         const struct ast_variable *field = fields;
495         int res, count = 0, paramcount = 0;
496         struct custom_prepare_struct cps = { .extra = lookup, .fields = fields, };
497         struct odbc_cache_tables *tableptr;
498         struct odbc_cache_columns *column = NULL;
499         struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
500
501         if (!table || !field || !keyfield || !sql) {
502                 return -1;
503         }
504
505         tableptr = ast_odbc_find_table(database, table);
506         if (!(obj = ast_odbc_request_obj2(database, connected_flag))) {
507                 ast_odbc_release_table(tableptr);
508                 return -1;
509         }
510
511         if (tableptr && !ast_odbc_find_column(tableptr, keyfield)) {
512                 ast_log(LOG_WARNING, "Key field '%s' does not exist in table '%s@%s'.  Update will fail\n", keyfield, table, database);
513         }
514
515         ast_str_set(&sql, 0, "UPDATE %s SET ", table);
516         while (field) {
517                 if ((tableptr && (column = ast_odbc_find_column(tableptr, field->name))) || count >= 64) {
518                         if (paramcount++) {
519                                 ast_str_append(&sql, 0, ", ");
520                         }
521                         /* NULL test for non-text columns */
522                         if (count < 64 && ast_strlen_zero(field->value) && column->nullable && !is_text(column)) {
523                                 ast_str_append(&sql, 0, "%s=NULL", field->name);
524                                 cps.skip |= (1LL << count);
525                         } else {
526                                 /* Value is not an empty string, or column is of text type, or we couldn't fit any more into cps.skip (count >= 64 ?!). */
527                                 ast_str_append(&sql, 0, "%s=?", field->name);
528                         }
529                 } else { /* the column does not exist in the table */
530                         cps.skip |= (1LL << count);
531                 }
532                 ++count;
533                 field = field->next;
534         }
535         ast_str_append(&sql, 0, " WHERE %s=?", keyfield);
536         ast_odbc_release_table(tableptr);
537
538         cps.sql = ast_str_buffer(sql);
539
540         if (ast_string_field_init(&cps, 256)) {
541                 ast_odbc_release_obj(obj);
542                 return -1;
543         }
544         stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
545         ast_string_field_free_memory(&cps);
546
547         if (!stmt) {
548                 ast_odbc_release_obj(obj);
549                 return -1;
550         }
551
552         res = SQLRowCount(stmt, &rowcount);
553         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
554         ast_odbc_release_obj(obj);
555
556         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
557                 ast_log(LOG_WARNING, "SQL Row Count error! [%s]\n", ast_str_buffer(sql));
558                 return -1;
559         }
560
561         if (rowcount >= 0) {
562                 return (int) rowcount;
563         }
564
565         return -1;
566 }
567
568 struct update2_prepare_struct {
569         const char *database;
570         const char *table;
571         const struct ast_variable *lookup_fields;
572         const struct ast_variable *update_fields;
573 };
574
575 static SQLHSTMT update2_prepare(struct odbc_obj *obj, void *data)
576 {
577         int res, x = 1, first = 1;
578         struct update2_prepare_struct *ups = data;
579         const struct ast_variable *field;
580         struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
581         SQLHSTMT stmt;
582         struct odbc_cache_tables *tableptr;
583
584         if (!sql) {
585                 return NULL;
586         }
587
588         tableptr = ast_odbc_find_table(ups->database, ups->table);
589         if (!tableptr) {
590                 ast_log(LOG_ERROR, "Could not retrieve metadata for table '%s@%s'.  Update will fail!\n", ups->table, ups->database);
591                 return NULL;
592         }
593
594         res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
595         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
596                 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
597                 ast_odbc_release_table(tableptr);
598                 return NULL;
599         }
600
601         ast_str_set(&sql, 0, "UPDATE %s SET ", ups->table);
602
603         for (field = ups->update_fields; field; field = field->next) {
604                 if (ast_odbc_find_column(tableptr, field->name)) {
605                         ast_str_append(&sql, 0, "%s%s=? ", first ? "" : ", ", field->name);
606                         SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(field->name), 0, (void *)field->value, 0, NULL);
607                         first = 0;
608                 } else {
609                         ast_log(LOG_NOTICE, "Not updating column '%s' in '%s@%s' because that column does not exist!\n", field->name, ups->table, ups->database);
610                 }
611         }
612
613         ast_str_append(&sql, 0, "WHERE");
614         first = 1;
615
616         for (field = ups->lookup_fields; field; field = field->next) {
617                 if (!ast_odbc_find_column(tableptr, field->name)) {
618                         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);
619                         ast_odbc_release_table(tableptr);
620                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
621                         return NULL;
622                 }
623                 ast_str_append(&sql, 0, "%s %s=?", first ? "" : " AND", field->name);
624                 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(field->value), 0, (void *)field->value, 0, NULL);
625                 first = 0;
626         }
627
628         /* Done with the table metadata */
629         ast_odbc_release_table(tableptr);
630
631         res = SQLPrepare(stmt, (unsigned char *)ast_str_buffer(sql), SQL_NTS);
632         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
633                 ast_log(LOG_WARNING, "SQL Prepare failed! [%s]\n", ast_str_buffer(sql));
634                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
635                 return NULL;
636         }
637
638         return stmt;
639 }
640
641 /*!
642  * \brief Execute an UPDATE query
643  * \param database
644  * \param table
645  * \param ap list containing one or more field/value set(s).
646  *
647  * Update a database table, preparing the sql statement from a list of
648  * key/value pairs specified in ap.  The lookup pairs are specified first
649  * and are separated from the update pairs by a sentinel value.
650  * Sub-in the values to the prepared statement and execute it.
651  *
652  * \retval number of rows affected
653  * \retval -1 on failure
654 */
655 static int update2_odbc(const char *database, const char *table, const struct ast_variable *lookup_fields, const struct ast_variable *update_fields)
656 {
657         struct odbc_obj *obj;
658         SQLHSTMT stmt;
659         struct update2_prepare_struct ups = { .database = database, .table = table, .lookup_fields = lookup_fields, .update_fields = update_fields, };
660         struct ast_str *sql;
661         int res;
662         SQLLEN rowcount = 0;
663
664         if (!(obj = ast_odbc_request_obj(database, 0))) {
665                 return -1;
666         }
667
668         if (!(stmt = ast_odbc_prepare_and_execute(obj, update2_prepare, &ups))) {
669                 ast_odbc_release_obj(obj);
670                 return -1;
671         }
672
673         res = SQLRowCount(stmt, &rowcount);
674         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
675         ast_odbc_release_obj(obj);
676
677         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
678                 /* Since only a single thread can access this memory, we can retrieve what would otherwise be lost. */
679                 sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
680                 ast_assert(sql != NULL);
681                 ast_log(LOG_WARNING, "SQL Row Count error! [%s]\n", ast_str_buffer(sql));
682                 return -1;
683         }
684
685         if (rowcount >= 0) {
686                 return (int)rowcount;
687         }
688
689         return -1;
690 }
691
692 /*!
693  * \brief Excute an INSERT query
694  * \param database
695  * \param table
696  * \param ap list containing one or more field/value set(s)
697  *
698  * Insert a new record into database table, prepare the sql statement.
699  * All values to be changed are stored in ap list.
700  * Sub-in the values to the prepared statement and execute it.
701  *
702  * \retval number of rows affected
703  * \retval -1 on failure
704  */
705 static int store_odbc(const char *database, const char *table, const struct ast_variable *fields)
706 {
707         struct odbc_obj *obj;
708         SQLHSTMT stmt;
709         SQLLEN rowcount=0;
710         const struct ast_variable *field = fields;
711         struct ast_str *keys;
712         struct ast_str *vals;
713         struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
714         int res;
715         struct custom_prepare_struct cps = { .fields = fields, };
716         struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
717
718         keys = ast_str_create(SQL_BUF_SIZE / 2);
719         vals = ast_str_create(SQL_BUF_SIZE / 4);
720         if (!table || !field || !keys || !vals || !sql) {
721                 ast_free(vals);
722                 ast_free(keys);
723                 return -1;
724         }
725
726         obj = ast_odbc_request_obj2(database, connected_flag);
727         if (!obj) {
728                 ast_free(vals);
729                 ast_free(keys);
730                 return -1;
731         }
732
733         ast_str_set(&keys, 0, "%s", field->name);
734         ast_str_set(&vals, 0, "?");
735         while ((field = field->next)) {
736                 ast_str_append(&keys, 0, ", %s", field->name);
737                 ast_str_append(&vals, 0, ", ?");
738         }
739         ast_str_set(&sql, 0, "INSERT INTO %s (%s) VALUES (%s)",
740                 table, ast_str_buffer(keys), ast_str_buffer(vals));
741
742         ast_free(vals);
743         ast_free(keys);
744         cps.sql = ast_str_buffer(sql);
745
746         if (ast_string_field_init(&cps, 256)) {
747                 ast_odbc_release_obj(obj);
748                 return -1;
749         }
750         stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
751         ast_string_field_free_memory(&cps);
752
753         if (!stmt) {
754                 ast_odbc_release_obj(obj);
755                 return -1;
756         }
757
758         res = SQLRowCount(stmt, &rowcount);
759         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
760         ast_odbc_release_obj(obj);
761
762         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
763                 ast_log(LOG_WARNING, "SQL Row Count error! [%s]\n", ast_str_buffer(sql));
764                 return -1;
765         }
766
767         if (rowcount >= 0)
768                 return (int)rowcount;
769
770         return -1;
771 }
772
773 /*!
774  * \brief Excute an DELETE query
775  * \param database
776  * \param table
777  * \param keyfield where clause field
778  * \param lookup value of field for where clause
779  * \param ap list containing one or more field/value set(s)
780  *
781  * Delete a row from a database table, prepare the sql statement using keyfield and lookup
782  * control the number of records to change. Additional params to match rows are stored in ap list.
783  * Sub-in the values to the prepared statement and execute it.
784  *
785  * \retval number of rows affected
786  * \retval -1 on failure
787  */
788 static int destroy_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, const struct ast_variable *fields)
789 {
790         struct odbc_obj *obj;
791         SQLHSTMT stmt;
792         SQLLEN rowcount=0;
793         struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
794         const struct ast_variable *field;
795         int res;
796         struct custom_prepare_struct cps = { .extra = lookup, .fields = fields, };
797         struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
798
799         if (!table || !sql) {
800                 return -1;
801         }
802
803         obj = ast_odbc_request_obj2(database, connected_flag);
804         if (!obj) {
805                 return -1;
806         }
807
808         ast_str_set(&sql, 0, "DELETE FROM %s WHERE ", table);
809         for (field = fields; field; field = field->next) {
810                 ast_str_append(&sql, 0, "%s=? AND ", field->name);
811         }
812         ast_str_append(&sql, 0, "%s=?", keyfield);
813
814         cps.sql = ast_str_buffer(sql);
815
816         if (ast_string_field_init(&cps, 256)) {
817                 ast_odbc_release_obj(obj);
818                 return -1;
819         }
820         stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
821         ast_string_field_free_memory(&cps);
822
823         if (!stmt) {
824                 ast_odbc_release_obj(obj);
825                 return -1;
826         }
827
828         res = SQLRowCount(stmt, &rowcount);
829         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
830         ast_odbc_release_obj(obj);
831
832         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
833                 ast_log(LOG_WARNING, "SQL Row Count error! [%s]\n", ast_str_buffer(sql));
834                 return -1;
835         }
836
837         if (rowcount >= 0)
838                 return (int)rowcount;
839
840         return -1;
841 }
842
843 struct config_odbc_obj {
844         char *sql;
845         unsigned long cat_metric;
846         char category[128];
847         char var_name[128];
848         char *var_val;
849         unsigned long var_val_size;
850         SQLLEN err;
851 };
852
853
854 static SQLHSTMT length_determination_odbc_prepare(struct odbc_obj *obj, void *data)
855 {
856         struct config_odbc_obj *q = data;
857         SQLHSTMT sth;
858         int res;
859
860         res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &sth);
861         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
862                 ast_verb(4, "Failure in AllocStatement %d\n", res);
863                 return NULL;
864         }
865
866         res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS);
867         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
868                 ast_verb(4, "Error in PREPARE %d\n", res);
869                 SQLFreeHandle(SQL_HANDLE_STMT, sth);
870                 return NULL;
871         }
872
873         SQLBindCol(sth, 1, SQL_C_ULONG, &q->var_val_size, sizeof(q->var_val_size), &q->err);
874
875         return sth;
876 }
877
878 static SQLHSTMT config_odbc_prepare(struct odbc_obj *obj, void *data)
879 {
880         struct config_odbc_obj *q = data;
881         SQLHSTMT sth;
882         int res;
883
884         res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &sth);
885         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
886                 ast_verb(4, "Failure in AllocStatement %d\n", res);
887                 return NULL;
888         }
889
890         res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS);
891         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
892                 ast_verb(4, "Error in PREPARE %d\n", res);
893                 SQLFreeHandle(SQL_HANDLE_STMT, sth);
894                 return NULL;
895         }
896
897         SQLBindCol(sth, 1, SQL_C_ULONG, &q->cat_metric, sizeof(q->cat_metric), &q->err);
898         SQLBindCol(sth, 2, SQL_C_CHAR, q->category, sizeof(q->category), &q->err);
899         SQLBindCol(sth, 3, SQL_C_CHAR, q->var_name, sizeof(q->var_name), &q->err);
900         SQLBindCol(sth, 4, SQL_C_CHAR, q->var_val, q->var_val_size, &q->err);
901
902         return sth;
903 }
904
905 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)
906 {
907         struct ast_variable *new_v;
908         struct ast_category *cur_cat;
909         int res = 0;
910         struct odbc_obj *obj;
911         struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
912         unsigned int last_cat_metric = 0;
913         SQLSMALLINT rowcount = 0;
914         SQLHSTMT stmt;
915         char last[128] = "";
916         struct config_odbc_obj q;
917         struct ast_flags loader_flags = { 0 };
918         struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
919
920         memset(&q, 0, sizeof(q));
921
922         if (!file || !strcmp (file, "res_config_odbc.conf") || !sql) {
923                 return NULL;            /* cant configure myself with myself ! */
924         }
925
926         obj = ast_odbc_request_obj2(database, connected_flag);
927         if (!obj)
928                 return NULL;
929
930         ast_str_set(&sql, 0, "SELECT MAX(LENGTH(var_val)) FROM %s WHERE filename='%s'",
931                 table, file);
932         q.sql = ast_str_buffer(sql);
933
934         stmt = ast_odbc_prepare_and_execute(obj, length_determination_odbc_prepare, &q);
935         if (!stmt) {
936                 ast_log(LOG_WARNING, "SQL select error! [%s]\n", ast_str_buffer(sql));
937                 ast_odbc_release_obj(obj);
938                 return NULL;
939         }
940
941         res = SQLNumResultCols(stmt, &rowcount);
942
943         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
944                 ast_log(LOG_WARNING, "SQL NumResultCols error! [%s]\n", ast_str_buffer(sql));
945                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
946                 ast_odbc_release_obj(obj);
947                 return NULL;
948         }
949
950         if (!rowcount) {
951                 ast_log(LOG_NOTICE, "found nothing\n");
952                 ast_odbc_release_obj(obj);
953                 return cfg;
954         }
955
956         /* There will be only one result for this, the maximum length of a variable value */
957         if (SQLFetch(stmt) == SQL_NO_DATA) {
958                 ast_log(LOG_NOTICE, "Failed to determine maximum length of a configuration value\n");
959                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
960                 ast_odbc_release_obj(obj);
961                 return NULL;
962         }
963
964         /* Reset stuff to a fresh state for the actual query which will retrieve all configuration */
965         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
966
967         ast_str_set(&sql, 0, "SELECT cat_metric, category, var_name, var_val FROM %s ", table);
968         ast_str_append(&sql, 0, "WHERE filename='%s' AND commented=0 ", file);
969         ast_str_append(&sql, 0, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ");
970         q.sql = ast_str_buffer(sql);
971
972         q.var_val_size += 1;
973         q.var_val = ast_malloc(q.var_val_size);
974         if (!q.var_val) {
975                 ast_log(LOG_WARNING, "Could not create buffer for reading in configuration values for '%s'\n", file);
976                 ast_odbc_release_obj(obj);
977                 return NULL;
978         }
979
980         stmt = ast_odbc_prepare_and_execute(obj, config_odbc_prepare, &q);
981         if (!stmt) {
982                 ast_log(LOG_WARNING, "SQL select error! [%s]\n", ast_str_buffer(sql));
983                 ast_odbc_release_obj(obj);
984                 ast_free(q.var_val);
985                 return NULL;
986         }
987
988         res = SQLNumResultCols(stmt, &rowcount);
989
990         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
991                 ast_log(LOG_WARNING, "SQL NumResultCols error! [%s]\n", ast_str_buffer(sql));
992                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
993                 ast_odbc_release_obj(obj);
994                 ast_free(q.var_val);
995                 return NULL;
996         }
997
998         if (!rowcount) {
999                 ast_log(LOG_NOTICE, "found nothing\n");
1000                 ast_odbc_release_obj(obj);
1001                 ast_free(q.var_val);
1002                 return cfg;
1003         }
1004
1005         cur_cat = ast_config_get_current_category(cfg);
1006
1007         while ((res = SQLFetch(stmt)) != SQL_NO_DATA) {
1008                 if (!strcmp (q.var_name, "#include")) {
1009                         if (!ast_config_internal_load(q.var_val, cfg, loader_flags, "", who_asked)) {
1010                                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1011                                 ast_odbc_release_obj(obj);
1012                                 ast_free(q.var_val);
1013                                 return NULL;
1014                         }
1015                         continue;
1016                 } 
1017                 if (strcmp(last, q.category) || last_cat_metric != q.cat_metric) {
1018                         cur_cat = ast_category_new_dynamic(q.category);
1019                         if (!cur_cat) {
1020                                 break;
1021                         }
1022                         strcpy(last, q.category);
1023                         last_cat_metric = q.cat_metric;
1024                         ast_category_append(cfg, cur_cat);
1025                 }
1026
1027                 new_v = ast_variable_new(q.var_name, q.var_val, "");
1028                 ast_variable_append(cur_cat, new_v);
1029         }
1030
1031         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1032         ast_odbc_release_obj(obj);
1033         ast_free(q.var_val);
1034         return cfg;
1035 }
1036
1037 #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)
1038 #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)
1039
1040 static int require_odbc(const char *database, const char *table, va_list ap)
1041 {
1042         struct odbc_cache_tables *tableptr = ast_odbc_find_table(database, table);
1043         struct odbc_cache_columns *col;
1044         char *elm;
1045         int type, size;
1046
1047         if (!tableptr) {
1048                 return -1;
1049         }
1050
1051         while ((elm = va_arg(ap, char *))) {
1052                 type = va_arg(ap, require_type);
1053                 size = va_arg(ap, int);
1054                 /* Check if the field matches the criteria */
1055                 AST_RWLIST_TRAVERSE(&tableptr->columns, col, list) {
1056                         if (strcmp(col->name, elm) == 0) {
1057                                 /* Type check, first.  Some fields are more particular than others */
1058                                 switch (col->type) {
1059                                 case SQL_CHAR:
1060                                 case SQL_VARCHAR:
1061                                 case SQL_LONGVARCHAR:
1062 #ifdef HAVE_ODBC_WCHAR
1063                                 case SQL_WCHAR:
1064                                 case SQL_WVARCHAR:
1065                                 case SQL_WLONGVARCHAR:
1066 #endif
1067                                 case SQL_BINARY:
1068                                 case SQL_VARBINARY:
1069                                 case SQL_LONGVARBINARY:
1070                                 case SQL_GUID:
1071 #define CHECK_SIZE(n) \
1072                                                 if (col->size < n) {      \
1073                                                         warn_length(col, n);  \
1074                                                 }                         \
1075                                                 break;
1076                                         switch (type) {
1077                                         case RQ_UINTEGER1: CHECK_SIZE(3)  /*         255 */
1078                                         case RQ_INTEGER1:  CHECK_SIZE(4)  /*        -128 */
1079                                         case RQ_UINTEGER2: CHECK_SIZE(5)  /*       65535 */
1080                                         case RQ_INTEGER2:  CHECK_SIZE(6)  /*      -32768 */
1081                                         case RQ_UINTEGER3:                /*    16777215 */
1082                                         case RQ_INTEGER3:  CHECK_SIZE(8)  /*    -8388608 */
1083                                         case RQ_DATE:                     /*  2008-06-09 */
1084                                         case RQ_UINTEGER4: CHECK_SIZE(10) /*  4200000000 */
1085                                         case RQ_INTEGER4:  CHECK_SIZE(11) /* -2100000000 */
1086                                         case RQ_DATETIME:                 /* 2008-06-09 16:03:47 */
1087                                         case RQ_UINTEGER8: CHECK_SIZE(19) /* trust me    */
1088                                         case RQ_INTEGER8:  CHECK_SIZE(20) /* ditto       */
1089                                         case RQ_FLOAT:
1090                                         case RQ_CHAR:      CHECK_SIZE(size)
1091                                         }
1092 #undef CHECK_SIZE
1093                                         break;
1094                                 case SQL_TYPE_DATE:
1095                                         if (type != RQ_DATE) {
1096                                                 warn_type(col, type);
1097                                         }
1098                                         break;
1099                                 case SQL_TYPE_TIMESTAMP:
1100                                 case SQL_TIMESTAMP:
1101                                         if (type != RQ_DATE && type != RQ_DATETIME) {
1102                                                 warn_type(col, type);
1103                                         }
1104                                         break;
1105                                 case SQL_BIT:
1106                                         warn_length(col, size);
1107                                         break;
1108 #define WARN_TYPE_OR_LENGTH(n)  \
1109                                                 if (!ast_rq_is_int(type)) {  \
1110                                                         warn_type(col, type);    \
1111                                                 } else {                     \
1112                                                         warn_length(col, n);  \
1113                                                 }
1114                                 case SQL_TINYINT:
1115                                         if (type != RQ_UINTEGER1) {
1116                                                 WARN_TYPE_OR_LENGTH(size)
1117                                         }
1118                                         break;
1119                                 case SQL_C_STINYINT:
1120                                         if (type != RQ_INTEGER1) {
1121                                                 WARN_TYPE_OR_LENGTH(size)
1122                                         }
1123                                         break;
1124                                 case SQL_C_USHORT:
1125                                         if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_UINTEGER2) {
1126                                                 WARN_TYPE_OR_LENGTH(size)
1127                                         }
1128                                         break;
1129                                 case SQL_SMALLINT:
1130                                 case SQL_C_SSHORT:
1131                                         if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_INTEGER2) {
1132                                                 WARN_TYPE_OR_LENGTH(size)
1133                                         }
1134                                         break;
1135                                 case SQL_C_ULONG:
1136                                         if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
1137                                                 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
1138                                                 type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
1139                                                 type != RQ_INTEGER4) {
1140                                                 WARN_TYPE_OR_LENGTH(size)
1141                                         }
1142                                         break;
1143                                 case SQL_INTEGER:
1144                                 case SQL_C_SLONG:
1145                                         if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
1146                                                 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
1147                                                 type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
1148                                                 type != RQ_INTEGER4) {
1149                                                 WARN_TYPE_OR_LENGTH(size)
1150                                         }
1151                                         break;
1152                                 case SQL_C_UBIGINT:
1153                                         if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
1154                                                 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
1155                                                 type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
1156                                                 type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
1157                                                 type != RQ_INTEGER8) {
1158                                                 WARN_TYPE_OR_LENGTH(size)
1159                                         }
1160                                         break;
1161                                 case SQL_BIGINT:
1162                                 case SQL_C_SBIGINT:
1163                                         if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
1164                                                 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
1165                                                 type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
1166                                                 type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
1167                                                 type != RQ_INTEGER8) {
1168                                                 WARN_TYPE_OR_LENGTH(size)
1169                                         }
1170                                         break;
1171 #undef WARN_TYPE_OR_LENGTH
1172                                 case SQL_NUMERIC:
1173                                 case SQL_DECIMAL:
1174                                 case SQL_FLOAT:
1175                                 case SQL_REAL:
1176                                 case SQL_DOUBLE:
1177                                         if (!ast_rq_is_int(type) && type != RQ_FLOAT) {
1178                                                 warn_type(col, type);
1179                                         }
1180                                         break;
1181                                 default:
1182                                         ast_log(LOG_WARNING, "Realtime table %s@%s: column type (%d) unrecognized for column '%s'\n", table, database, col->type, elm);
1183                                 }
1184                                 break;
1185                         }
1186                 }
1187                 if (!col) {
1188                         ast_log(LOG_WARNING, "Realtime table %s@%s requires column '%s', but that column does not exist!\n", table, database, elm);
1189                 }
1190         }
1191         AST_RWLIST_UNLOCK(&tableptr->columns);
1192         return 0;
1193 }
1194 #undef warn_length
1195 #undef warn_type
1196
1197 static int unload_odbc(const char *a, const char *b)
1198 {
1199         return ast_odbc_clear_cache(a, b);
1200 }
1201
1202 static struct ast_config_engine odbc_engine = {
1203         .name = "odbc",
1204         .load_func = config_odbc,
1205         .realtime_func = realtime_odbc,
1206         .realtime_multi_func = realtime_multi_odbc,
1207         .store_func = store_odbc,
1208         .destroy_func = destroy_odbc,
1209         .update_func = update_odbc,
1210         .update2_func = update2_odbc,
1211         .require_func = require_odbc,
1212         .unload_func = unload_odbc,
1213 };
1214
1215 static int unload_module (void)
1216 {
1217         ast_config_engine_deregister(&odbc_engine);
1218
1219         return 0;
1220 }
1221
1222 static int load_module (void)
1223 {
1224         ast_config_engine_register(&odbc_engine);
1225
1226         return 0;
1227 }
1228
1229 static int reload_module(void)
1230 {
1231         return 0;
1232 }
1233
1234 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Realtime ODBC configuration",
1235         .support_level = AST_MODULE_SUPPORT_CORE,
1236         .load = load_module,
1237         .unload = unload_module,
1238         .reload = reload_module,
1239         .load_pri = AST_MODPRI_REALTIME_DRIVER,
1240 );