2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2010, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * Copyright (C) 2004 - 2005 Anthony Minessale II <anthmct@yahoo.com>
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.
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.
23 * \brief odbc+odbc plugin for portable configuration engine
25 * \author Mark Spencer <markster@digium.com>
26 * \author Anthony Minessale II <anthmct@yahoo.com>
28 * \arg http://www.unixodbc.org
32 <depend>res_odbc</depend>
33 <support_level>core</support_level>
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
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"
50 AST_THREADSTORAGE(sql_buf);
52 struct custom_prepare_struct {
55 AST_DECLARE_STRING_FIELDS(
56 AST_STRING_FIELD(encoding)[256];
59 unsigned long long skip;
62 static void decode_chunk(char *chunk)
64 for (; *chunk; chunk++) {
65 if (*chunk == '^' && strchr("0123456789ABCDEF", chunk[1]) && strchr("0123456789ABCDEF", chunk[2])) {
66 sscanf(chunk + 1, "%02hhX", chunk);
67 memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
72 static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data)
74 int res, x = 1, count = 0;
75 struct custom_prepare_struct *cps = data;
76 const char *newparam, *newval;
81 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
82 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
83 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
87 ast_debug(1, "Skip: %lld; SQL: %s\n", cps->skip, cps->sql);
89 res = SQLPrepare(stmt, (unsigned char *)cps->sql, SQL_NTS);
90 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
91 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", cps->sql);
92 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
97 while ((newparam = va_arg(ap, const char *))) {
98 newval = va_arg(ap, const char *);
99 if ((1LL << count++) & cps->skip) {
100 ast_debug(1, "Skipping field '%s'='%s' (%llo/%llo)\n", newparam, newval, 1LL << (count - 1), cps->skip);
103 ast_debug(1, "Parameter %d ('%s') = '%s'\n", x, newparam, newval);
104 if (strchr(newval, ';') || strchr(newval, '^')) {
105 char *eptr = encodebuf;
106 const char *vptr = newval;
107 for (; *vptr && eptr < encodebuf + sizeof(encodebuf); vptr++) {
108 if (strchr("^;", *vptr)) {
109 /* We use ^XX, instead of %XX because '%' is a special character in SQL */
110 snprintf(eptr, encodebuf + sizeof(encodebuf) - eptr, "^%02hhX", *vptr);
116 if (eptr < encodebuf + sizeof(encodebuf)) {
119 encodebuf[sizeof(encodebuf) - 1] = '\0';
121 ast_string_field_set(cps, encoding[x], encodebuf);
122 newval = cps->encoding[x];
124 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
128 if (!ast_strlen_zero(cps->extra))
129 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(cps->extra), 0, (void *)cps->extra, 0, NULL);
134 * \brief Excute an SQL query and return ast_variable list
137 * \param ap list containing one or more field/operator/value set.
139 * Select database and preform query on table, prepare the sql statement
140 * Sub-in the values to the prepared statement and execute it. Return results
141 * as a ast_variable list.
143 * \retval var on success
144 * \retval NULL on failure
146 static struct ast_variable *realtime_odbc(const char *database, const char *table, va_list ap)
148 struct odbc_obj *obj;
154 const char *newparam;
160 struct ast_variable *var=NULL, *prev=NULL;
162 SQLSMALLINT colcount=0;
163 SQLSMALLINT datatype;
164 SQLSMALLINT decimaldigits;
165 SQLSMALLINT nullable;
168 struct custom_prepare_struct cps = { .sql = sql };
169 struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
171 if (ast_string_field_init(&cps, 256)) {
176 ast_string_field_free_memory(&cps);
180 obj = ast_odbc_request_obj2(database, connected_flag);
183 ast_log(LOG_ERROR, "No database handle available with the name of '%s' (check res_odbc.conf)\n", database);
184 ast_string_field_free_memory(&cps);
189 newparam = va_arg(aq, const char *);
192 ast_odbc_release_obj(obj);
193 ast_string_field_free_memory(&cps);
196 va_arg(aq, const char *);
197 op = !strchr(newparam, ' ') ? " =" : "";
198 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, newparam, op,
199 strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
200 while((newparam = va_arg(aq, const char *))) {
201 op = !strchr(newparam, ' ') ? " =" : "";
202 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", newparam, op,
203 strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
204 va_arg(aq, const char *);
209 stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
213 ast_odbc_release_obj(obj);
214 ast_string_field_free_memory(&cps);
218 res = SQLNumResultCols(stmt, &colcount);
219 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
220 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
221 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
222 ast_odbc_release_obj(obj);
223 ast_string_field_free_memory(&cps);
227 res = SQLFetch(stmt);
228 if (res == SQL_NO_DATA) {
229 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
230 ast_odbc_release_obj(obj);
231 ast_string_field_free_memory(&cps);
234 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
235 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
236 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
237 ast_odbc_release_obj(obj);
238 ast_string_field_free_memory(&cps);
241 for (x = 0; x < colcount; x++) {
244 collen = sizeof(coltitle);
245 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
246 &datatype, &colsize, &decimaldigits, &nullable);
247 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
248 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
250 ast_variables_destroy(var);
251 ast_odbc_release_obj(obj);
252 ast_string_field_free_memory(&cps);
257 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
258 if (indicator == SQL_NULL_DATA)
260 else if (ast_strlen_zero(rowdata)) {
261 /* Because we encode the empty string for a NULL, we will encode
262 * actual empty strings as a string containing a single whitespace. */
263 ast_copy_string(rowdata, " ", sizeof(rowdata));
266 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
267 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
269 ast_variables_destroy(var);
270 ast_odbc_release_obj(obj);
275 chunk = strsep(&stringp, ";");
276 if (!ast_strlen_zero(ast_strip(chunk))) {
277 if (strchr(chunk, '^')) {
281 prev->next = ast_variable_new(coltitle, chunk, "");
286 prev = var = ast_variable_new(coltitle, chunk, "");
293 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
294 ast_odbc_release_obj(obj);
295 ast_string_field_free_memory(&cps);
300 * \brief Excute an Select query and return ast_config list
303 * \param ap list containing one or more field/operator/value set.
305 * Select database and preform query on table, prepare the sql statement
306 * Sub-in the values to the prepared statement and execute it.
307 * Execute this prepared query against several ODBC connected databases.
308 * Return results as an ast_config variable.
310 * \retval var on success
311 * \retval NULL on failure
313 static struct ast_config *realtime_multi_odbc(const char *database, const char *table, va_list ap)
315 struct odbc_obj *obj;
320 const char *initfield;
322 const char *newparam;
328 struct ast_variable *var=NULL;
329 struct ast_config *cfg=NULL;
330 struct ast_category *cat=NULL;
331 struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
333 SQLSMALLINT colcount=0;
334 SQLSMALLINT datatype;
335 SQLSMALLINT decimaldigits;
336 SQLSMALLINT nullable;
338 struct custom_prepare_struct cps = { .sql = sql };
341 if (!table || ast_string_field_init(&cps, 256)) {
346 obj = ast_odbc_request_obj2(database, connected_flag);
348 ast_string_field_free_memory(&cps);
353 newparam = va_arg(aq, const char *);
356 ast_odbc_release_obj(obj);
357 ast_string_field_free_memory(&cps);
361 initfield = ast_strdupa(newparam);
362 if ((op = strchr(initfield, ' '))) {
366 va_arg(aq, const char *);
367 op = !strchr(newparam, ' ') ? " =" : "";
368 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, newparam, op,
369 strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
370 while((newparam = va_arg(aq, const char *))) {
371 op = !strchr(newparam, ' ') ? " =" : "";
372 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", newparam, op,
373 strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
374 va_arg(aq, const char *);
378 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
381 stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
385 ast_odbc_release_obj(obj);
386 ast_string_field_free_memory(&cps);
390 res = SQLNumResultCols(stmt, &colcount);
391 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
392 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
393 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
394 ast_odbc_release_obj(obj);
395 ast_string_field_free_memory(&cps);
399 cfg = ast_config_new();
401 ast_log(LOG_WARNING, "Out of memory!\n");
402 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
403 ast_odbc_release_obj(obj);
404 ast_string_field_free_memory(&cps);
408 while ((res=SQLFetch(stmt)) != SQL_NO_DATA) {
410 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
411 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
414 cat = ast_category_new("","",99999);
416 ast_log(LOG_WARNING, "Out of memory!\n");
419 for (x=0;x<colcount;x++) {
422 collen = sizeof(coltitle);
423 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
424 &datatype, &colsize, &decimaldigits, &nullable);
425 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
426 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
427 ast_category_destroy(cat);
432 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
433 if (indicator == SQL_NULL_DATA)
436 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
437 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
438 ast_category_destroy(cat);
443 chunk = strsep(&stringp, ";");
444 if (!ast_strlen_zero(ast_strip(chunk))) {
445 if (strchr(chunk, '^')) {
448 if (!strcmp(initfield, coltitle)) {
449 ast_category_rename(cat, chunk);
451 var = ast_variable_new(coltitle, chunk, "");
452 ast_variable_append(cat, var);
456 ast_category_append(cfg, cat);
460 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
461 ast_odbc_release_obj(obj);
462 ast_string_field_free_memory(&cps);
467 * \brief Excute an UPDATE query
470 * \param keyfield where clause field
471 * \param lookup value of field for where clause
472 * \param ap list containing one or more field/value set(s).
474 * Update a database table, prepare the sql statement using keyfield and lookup
475 * control the number of records to change. All values to be changed are stored in ap list.
476 * Sub-in the values to the prepared statement and execute it.
478 * \retval number of rows affected
479 * \retval -1 on failure
481 static int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
483 struct odbc_obj *obj;
487 const char *newparam;
490 struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
491 struct odbc_cache_tables *tableptr;
492 struct odbc_cache_columns *column = NULL;
493 struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
499 if (ast_string_field_init(&cps, 256)) {
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 ast_string_field_free_memory(&cps);
511 newparam = va_arg(aq, const char *);
514 ast_odbc_release_obj(obj);
515 ast_odbc_release_table(tableptr);
516 ast_string_field_free_memory(&cps);
519 va_arg(aq, const char *);
521 if (tableptr && !(column = ast_odbc_find_column(tableptr, newparam))) {
522 ast_log(LOG_WARNING, "Key field '%s' does not exist in table '%s@%s'. Update will fail\n", newparam, table, database);
525 snprintf(sql, sizeof(sql), "UPDATE %s SET %s=?", table, newparam);
526 while((newparam = va_arg(aq, const char *))) {
527 va_arg(aq, const char *);
528 if ((tableptr && (column = ast_odbc_find_column(tableptr, newparam))) || count > 63) {
529 /* NULL test for integer-based columns */
530 if (ast_strlen_zero(newparam) && tableptr && column && column->nullable && count < 64 &&
531 (column->type == SQL_INTEGER || column->type == SQL_BIGINT ||
532 column->type == SQL_SMALLINT || column->type == SQL_TINYINT ||
533 column->type == SQL_NUMERIC || column->type == SQL_DECIMAL)) {
534 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s=NULL", newparam);
535 cps.skip |= (1LL << count);
537 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s=?", newparam);
539 } else { /* the column does not exist in the table */
540 cps.skip |= (1LL << count);
545 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield);
546 ast_odbc_release_table(tableptr);
549 stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
553 ast_odbc_release_obj(obj);
554 ast_string_field_free_memory(&cps);
558 res = SQLRowCount(stmt, &rowcount);
559 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
560 ast_odbc_release_obj(obj);
561 ast_string_field_free_memory(&cps);
563 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
564 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
569 return (int) rowcount;
575 struct update2_prepare_struct {
576 const char *database;
581 static SQLHSTMT update2_prepare(struct odbc_obj *obj, void *data)
583 int res, x = 1, first = 1;
584 struct update2_prepare_struct *ups = data;
585 const char *newparam, *newval;
586 struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
589 struct odbc_cache_tables *tableptr = ast_odbc_find_table(ups->database, ups->table);
590 struct odbc_cache_columns *column;
594 ast_odbc_release_table(tableptr);
600 ast_log(LOG_ERROR, "Could not retrieve metadata for table '%s@%s'. Update will fail!\n", ups->table, ups->database);
604 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
605 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
606 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
607 ast_odbc_release_table(tableptr);
611 ast_str_set(&sql, 0, "UPDATE %s SET ", ups->table);
613 /* Start by finding the second set of parameters */
614 va_copy(ap, ups->ap);
616 while ((newparam = va_arg(ap, const char *))) {
617 newval = va_arg(ap, const char *);
620 while ((newparam = va_arg(ap, const char *))) {
621 newval = va_arg(ap, const char *);
622 if ((column = ast_odbc_find_column(tableptr, newparam))) {
623 ast_str_append(&sql, 0, "%s%s=? ", first ? "" : ", ", newparam);
624 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
627 ast_log(LOG_NOTICE, "Not updating column '%s' in '%s@%s' because that column does not exist!\n", newparam, ups->table, ups->database);
632 ast_str_append(&sql, 0, "WHERE");
635 /* Restart search, because we need to add the search parameters */
636 va_copy(ap, ups->ap);
638 while ((newparam = va_arg(ap, const char *))) {
639 newval = va_arg(ap, const char *);
640 if (!(column = ast_odbc_find_column(tableptr, newparam))) {
642 ast_log(LOG_ERROR, "One or more of the criteria columns '%s' on '%s@%s' for this update does not exist!\n", newparam, ups->table, ups->database);
643 ast_odbc_release_table(tableptr);
644 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
647 ast_str_append(&sql, 0, "%s %s=?", first ? "" : " AND", newparam);
648 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
653 /* Done with the table metadata */
654 ast_odbc_release_table(tableptr);
656 res = SQLPrepare(stmt, (unsigned char *)ast_str_buffer(sql), SQL_NTS);
657 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
658 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", ast_str_buffer(sql));
659 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
667 * \brief Execute an UPDATE query
670 * \param ap list containing one or more field/value set(s).
672 * Update a database table, preparing the sql statement from a list of
673 * key/value pairs specified in ap. The lookup pairs are specified first
674 * and are separated from the update pairs by a sentinel value.
675 * Sub-in the values to the prepared statement and execute it.
677 * \retval number of rows affected
678 * \retval -1 on failure
680 static int update2_odbc(const char *database, const char *table, va_list ap)
682 struct odbc_obj *obj;
684 struct update2_prepare_struct ups = { .database = database, .table = table, };
689 if (!(obj = ast_odbc_request_obj(database, 0))) {
694 if (!(stmt = ast_odbc_prepare_and_execute(obj, update2_prepare, &ups))) {
696 ast_odbc_release_obj(obj);
701 res = SQLRowCount(stmt, &rowcount);
702 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
703 ast_odbc_release_obj(obj);
705 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
706 /* Since only a single thread can access this memory, we can retrieve what would otherwise be lost. */
707 sql = ast_str_thread_get(&sql_buf, 16);
708 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n", ast_str_buffer(sql));
713 return (int)rowcount;
720 * \brief Excute an INSERT query
723 * \param ap list containing one or more field/value set(s)
725 * Insert a new record into database table, prepare the sql statement.
726 * All values to be changed are stored in ap list.
727 * Sub-in the values to the prepared statement and execute it.
729 * \retval number of rows affected
730 * \retval -1 on failure
732 static int store_odbc(const char *database, const char *table, va_list ap)
734 struct odbc_obj *obj;
740 const char *newparam;
743 struct custom_prepare_struct cps = { .sql = sql, .extra = NULL };
744 struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
750 obj = ast_odbc_request_obj2(database, connected_flag);
757 newparam = va_arg(aq, const char *);
760 ast_odbc_release_obj(obj);
763 va_arg(aq, const char *);
764 snprintf(keys, sizeof(keys), "%s", newparam);
765 ast_copy_string(vals, "?", sizeof(vals));
766 while ((newparam = va_arg(aq, const char *))) {
767 snprintf(keys + strlen(keys), sizeof(keys) - strlen(keys), ", %s", newparam);
768 snprintf(vals + strlen(vals), sizeof(vals) - strlen(vals), ", ?");
769 va_arg(aq, const char *);
772 snprintf(sql, sizeof(sql), "INSERT INTO %s (%s) VALUES (%s)", table, keys, vals);
776 stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
780 ast_odbc_release_obj(obj);
784 res = SQLRowCount(stmt, &rowcount);
785 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
786 ast_odbc_release_obj(obj);
788 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
789 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
794 return (int)rowcount;
800 * \brief Excute an DELETE query
803 * \param keyfield where clause field
804 * \param lookup value of field for where clause
805 * \param ap list containing one or more field/value set(s)
807 * Delete a row from a database table, prepare the sql statement using keyfield and lookup
808 * control the number of records to change. Additional params to match rows are stored in ap list.
809 * Sub-in the values to the prepared statement and execute it.
811 * \retval number of rows affected
812 * \retval -1 on failure
814 static int destroy_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
816 struct odbc_obj *obj;
820 const char *newparam;
823 struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
824 struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
830 obj = ast_odbc_request_obj2(database, connected_flag);
835 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE ", table);
838 while((newparam = va_arg(aq, const char *))) {
839 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=? AND ", newparam);
840 va_arg(aq, const char *);
843 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=?", keyfield);
846 stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
850 ast_odbc_release_obj(obj);
854 res = SQLRowCount(stmt, &rowcount);
855 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
856 ast_odbc_release_obj(obj);
858 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
859 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
864 return (int)rowcount;
870 struct config_odbc_obj {
872 unsigned long cat_metric;
875 char var_val[1024]; /* changed from 128 to 1024 via bug 8251 */
879 static SQLHSTMT config_odbc_prepare(struct odbc_obj *obj, void *data)
881 struct config_odbc_obj *q = data;
885 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &sth);
886 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
887 ast_verb(4, "Failure in AllocStatement %d\n", res);
891 res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS);
892 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
893 ast_verb(4, "Error in PREPARE %d\n", res);
894 SQLFreeHandle(SQL_HANDLE_STMT, sth);
898 SQLBindCol(sth, 1, SQL_C_ULONG, &q->cat_metric, sizeof(q->cat_metric), &q->err);
899 SQLBindCol(sth, 2, SQL_C_CHAR, q->category, sizeof(q->category), &q->err);
900 SQLBindCol(sth, 3, SQL_C_CHAR, q->var_name, sizeof(q->var_name), &q->err);
901 SQLBindCol(sth, 4, SQL_C_CHAR, q->var_val, sizeof(q->var_val), &q->err);
906 static struct ast_config *config_odbc(const char *database, const char *table, const char *file, struct ast_config *cfg, struct ast_flags flags, const char *sugg_incl, const char *who_asked)
908 struct ast_variable *new_v;
909 struct ast_category *cur_cat;
911 struct odbc_obj *obj;
912 char sqlbuf[1024] = "";
914 size_t sqlleft = sizeof(sqlbuf);
915 unsigned int last_cat_metric = 0;
916 SQLSMALLINT rowcount = 0;
919 struct config_odbc_obj q;
920 struct ast_flags loader_flags = { 0 };
921 struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
923 memset(&q, 0, sizeof(q));
925 if (!file || !strcmp (file, "res_config_odbc.conf"))
926 return NULL; /* cant configure myself with myself ! */
928 obj = ast_odbc_request_obj2(database, connected_flag);
932 ast_build_string(&sql, &sqlleft, "SELECT cat_metric, category, var_name, var_val FROM %s ", table);
933 ast_build_string(&sql, &sqlleft, "WHERE filename='%s' AND commented=0 ", file);
934 ast_build_string(&sql, &sqlleft, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ");
937 stmt = ast_odbc_prepare_and_execute(obj, config_odbc_prepare, &q);
940 ast_log(LOG_WARNING, "SQL select error!\n[%s]\n\n", sql);
941 ast_odbc_release_obj(obj);
945 res = SQLNumResultCols(stmt, &rowcount);
947 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
948 ast_log(LOG_WARNING, "SQL NumResultCols error!\n[%s]\n\n", sql);
949 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
950 ast_odbc_release_obj(obj);
955 ast_log(LOG_NOTICE, "found nothing\n");
956 ast_odbc_release_obj(obj);
960 cur_cat = ast_config_get_current_category(cfg);
962 while ((res = SQLFetch(stmt)) != SQL_NO_DATA) {
963 if (!strcmp (q.var_name, "#include")) {
964 if (!ast_config_internal_load(q.var_val, cfg, loader_flags, "", who_asked)) {
965 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
966 ast_odbc_release_obj(obj);
971 if (strcmp(last, q.category) || last_cat_metric != q.cat_metric) {
972 cur_cat = ast_category_new(q.category, "", 99999);
974 ast_log(LOG_WARNING, "Out of memory!\n");
977 strcpy(last, q.category);
978 last_cat_metric = q.cat_metric;
979 ast_category_append(cfg, cur_cat);
982 new_v = ast_variable_new(q.var_name, q.var_val, "");
983 ast_variable_append(cur_cat, new_v);
986 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
987 ast_odbc_release_obj(obj);
991 #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)
992 #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)
994 static int require_odbc(const char *database, const char *table, va_list ap)
996 struct odbc_cache_tables *tableptr = ast_odbc_find_table(database, table);
997 struct odbc_cache_columns *col;
1005 while ((elm = va_arg(ap, char *))) {
1006 type = va_arg(ap, require_type);
1007 size = va_arg(ap, int);
1008 /* Check if the field matches the criteria */
1009 AST_RWLIST_TRAVERSE(&tableptr->columns, col, list) {
1010 if (strcmp(col->name, elm) == 0) {
1011 /* Type check, first. Some fields are more particular than others */
1012 switch (col->type) {
1015 case SQL_LONGVARCHAR:
1016 #ifdef HAVE_ODBC_WCHAR
1019 case SQL_WLONGVARCHAR:
1023 case SQL_LONGVARBINARY:
1025 #define CHECK_SIZE(n) \
1026 if (col->size < n) { \
1027 warn_length(col, n); \
1031 case RQ_UINTEGER1: CHECK_SIZE(3) /* 255 */
1032 case RQ_INTEGER1: CHECK_SIZE(4) /* -128 */
1033 case RQ_UINTEGER2: CHECK_SIZE(5) /* 65535 */
1034 case RQ_INTEGER2: CHECK_SIZE(6) /* -32768 */
1035 case RQ_UINTEGER3: /* 16777215 */
1036 case RQ_INTEGER3: CHECK_SIZE(8) /* -8388608 */
1037 case RQ_DATE: /* 2008-06-09 */
1038 case RQ_UINTEGER4: CHECK_SIZE(10) /* 4200000000 */
1039 case RQ_INTEGER4: CHECK_SIZE(11) /* -2100000000 */
1040 case RQ_DATETIME: /* 2008-06-09 16:03:47 */
1041 case RQ_UINTEGER8: CHECK_SIZE(19) /* trust me */
1042 case RQ_INTEGER8: CHECK_SIZE(20) /* ditto */
1044 case RQ_CHAR: CHECK_SIZE(size)
1049 if (type != RQ_DATE) {
1050 warn_type(col, type);
1053 case SQL_TYPE_TIMESTAMP:
1055 if (type != RQ_DATE && type != RQ_DATETIME) {
1056 warn_type(col, type);
1060 warn_length(col, size);
1062 #define WARN_TYPE_OR_LENGTH(n) \
1063 if (!ast_rq_is_int(type)) { \
1064 warn_type(col, type); \
1066 warn_length(col, n); \
1069 if (type != RQ_UINTEGER1) {
1070 WARN_TYPE_OR_LENGTH(size)
1073 case SQL_C_STINYINT:
1074 if (type != RQ_INTEGER1) {
1075 WARN_TYPE_OR_LENGTH(size)
1079 if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_UINTEGER2) {
1080 WARN_TYPE_OR_LENGTH(size)
1085 if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_INTEGER2) {
1086 WARN_TYPE_OR_LENGTH(size)
1090 if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
1091 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
1092 type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
1093 type != RQ_INTEGER4) {
1094 WARN_TYPE_OR_LENGTH(size)
1099 if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
1100 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
1101 type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
1102 type != RQ_INTEGER4) {
1103 WARN_TYPE_OR_LENGTH(size)
1107 if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
1108 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
1109 type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
1110 type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
1111 type != RQ_INTEGER8) {
1112 WARN_TYPE_OR_LENGTH(size)
1117 if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
1118 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
1119 type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
1120 type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
1121 type != RQ_INTEGER8) {
1122 WARN_TYPE_OR_LENGTH(size)
1125 #undef WARN_TYPE_OR_LENGTH
1131 if (!ast_rq_is_int(type) && type != RQ_FLOAT) {
1132 warn_type(col, type);
1136 ast_log(LOG_WARNING, "Realtime table %s@%s: column type (%d) unrecognized for column '%s'\n", table, database, col->type, elm);
1142 ast_log(LOG_WARNING, "Realtime table %s@%s requires column '%s', but that column does not exist!\n", table, database, elm);
1145 AST_RWLIST_UNLOCK(&tableptr->columns);
1151 static int unload_odbc(const char *a, const char *b)
1153 return ast_odbc_clear_cache(a, b);
1156 static struct ast_config_engine odbc_engine = {
1158 .load_func = config_odbc,
1159 .realtime_func = realtime_odbc,
1160 .realtime_multi_func = realtime_multi_odbc,
1161 .store_func = store_odbc,
1162 .destroy_func = destroy_odbc,
1163 .update_func = update_odbc,
1164 .update2_func = update2_odbc,
1165 .require_func = require_odbc,
1166 .unload_func = unload_odbc,
1169 static int unload_module (void)
1171 ast_config_engine_deregister(&odbc_engine);
1173 ast_verb(1, "res_config_odbc unloaded.\n");
1177 static int load_module (void)
1179 ast_config_engine_register(&odbc_engine);
1180 ast_verb(1, "res_config_odbc loaded.\n");
1184 static int reload_module(void)
1189 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Realtime ODBC configuration",
1190 .load = load_module,
1191 .unload = unload_module,
1192 .reload = reload_module,
1193 .load_pri = AST_MODPRI_REALTIME_DRIVER,