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