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