2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, 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>unixodbc</depend>
34 <depend>res_odbc</depend>
39 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
41 #include "asterisk/file.h"
42 #include "asterisk/channel.h"
43 #include "asterisk/pbx.h"
44 #include "asterisk/config.h"
45 #include "asterisk/module.h"
46 #include "asterisk/lock.h"
47 #include "asterisk/res_odbc.h"
48 #include "asterisk/utils.h"
50 struct custom_prepare_struct
{
54 unsigned long long skip
;
57 static SQLHSTMT
custom_prepare(struct odbc_obj
*obj
, void *data
)
59 int res
, x
= 1, count
= 0;
60 struct custom_prepare_struct
*cps
= data
;
61 const char *newparam
, *newval
;
67 res
= SQLAllocHandle(SQL_HANDLE_STMT
, obj
->con
, &stmt
);
68 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
69 ast_log(LOG_WARNING
, "SQL Alloc Handle failed!\n");
73 res
= SQLPrepare(stmt
, (unsigned char *)cps
->sql
, SQL_NTS
);
74 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
75 ast_log(LOG_WARNING
, "SQL Prepare failed![%s]\n", cps
->sql
);
76 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
80 while ((newparam
= va_arg(ap
, const char *))) {
81 newval
= va_arg(ap
, const char *);
82 if ((1 << count
) & cps
->skip
) {
85 SQLBindParameter(stmt
, x
++, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(newval
), 0, (void *)newval
, 0, NULL
);
89 if (!ast_strlen_zero(cps
->extra
))
90 SQLBindParameter(stmt
, x
++, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(cps
->extra
), 0, (void *)cps
->extra
, 0, NULL
);
95 * \brief Excute an SQL query and return ast_variable list
98 * \param ap list containing one or more field/operator/value set.
100 * Select database and preform query on table, prepare the sql statement
101 * Sub-in the values to the prepared statement and execute it. Return results
102 * as a ast_variable list.
104 * \retval var on success
105 * \retval NULL on failure
107 static struct ast_variable
*realtime_odbc(const char *database
, const char *table
, va_list ap
)
109 struct odbc_obj
*obj
;
115 const char *newparam
, *newval
;
121 struct ast_variable
*var
=NULL
, *prev
=NULL
;
123 SQLSMALLINT colcount
=0;
124 SQLSMALLINT datatype
;
125 SQLSMALLINT decimaldigits
;
126 SQLSMALLINT nullable
;
129 struct custom_prepare_struct cps
= { .sql
= sql
};
137 obj
= ast_odbc_request_obj(database
, 0);
140 ast_log(LOG_ERROR
, "No database handle available with the name of '%s' (check res_odbc.conf)\n", database
);
144 newparam
= va_arg(aq
, const char *);
146 ast_odbc_release_obj(obj
);
149 newval
= va_arg(aq
, const char *);
150 op
= !strchr(newparam
, ' ') ? " =" : "";
151 snprintf(sql
, sizeof(sql
), "SELECT * FROM %s WHERE %s%s ?%s", table
, newparam
, op
,
152 strcasestr(newparam
, "LIKE") && !ast_odbc_backslash_is_escape(obj
) ? " ESCAPE '\\'" : "");
153 while((newparam
= va_arg(aq
, const char *))) {
154 op
= !strchr(newparam
, ' ') ? " =" : "";
155 snprintf(sql
+ strlen(sql
), sizeof(sql
) - strlen(sql
), " AND %s%s ?%s", newparam
, op
,
156 strcasestr(newparam
, "LIKE") && !ast_odbc_backslash_is_escape(obj
) ? " ESCAPE '\\'" : "");
157 newval
= va_arg(aq
, const char *);
161 stmt
= ast_odbc_prepare_and_execute(obj
, custom_prepare
, &cps
);
164 ast_odbc_release_obj(obj
);
168 res
= SQLNumResultCols(stmt
, &colcount
);
169 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
170 ast_log(LOG_WARNING
, "SQL Column Count error!\n[%s]\n\n", sql
);
171 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
172 ast_odbc_release_obj(obj
);
176 res
= SQLFetch(stmt
);
177 if (res
== SQL_NO_DATA
) {
178 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
179 ast_odbc_release_obj(obj
);
182 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
183 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
184 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
185 ast_odbc_release_obj(obj
);
188 for (x
= 0; x
< colcount
; x
++) {
190 collen
= sizeof(coltitle
);
191 res
= SQLDescribeCol(stmt
, x
+ 1, (unsigned char *)coltitle
, sizeof(coltitle
), &collen
,
192 &datatype
, &colsize
, &decimaldigits
, &nullable
);
193 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
194 ast_log(LOG_WARNING
, "SQL Describe Column error!\n[%s]\n\n", sql
);
196 ast_variables_destroy(var
);
197 ast_odbc_release_obj(obj
);
202 res
= SQLGetData(stmt
, x
+ 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), &indicator
);
203 if (indicator
== SQL_NULL_DATA
)
205 else if (ast_strlen_zero(rowdata
)) {
206 /* Because we encode the empty string for a NULL, we will encode
207 * actual empty strings as a string containing a single whitespace. */
208 ast_copy_string(rowdata
, " ", sizeof(rowdata
));
211 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
212 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
214 ast_variables_destroy(var
);
215 ast_odbc_release_obj(obj
);
220 chunk
= strsep(&stringp
, ";");
221 if (!ast_strlen_zero(ast_strip(chunk
))) {
223 prev
->next
= ast_variable_new(coltitle
, chunk
, "");
227 prev
= var
= ast_variable_new(coltitle
, chunk
, "");
233 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
234 ast_odbc_release_obj(obj
);
239 * \brief Excute an Select query and return ast_config list
242 * \param ap list containing one or more field/operator/value set.
244 * Select database and preform query on table, prepare the sql statement
245 * Sub-in the values to the prepared statement and execute it.
246 * Execute this prepared query against several ODBC connected databases.
247 * Return results as an ast_config variable.
249 * \retval var on success
250 * \retval NULL on failure
252 static struct ast_config
*realtime_multi_odbc(const char *database
, const char *table
, va_list ap
)
254 struct odbc_obj
*obj
;
259 const char *initfield
=NULL
;
261 const char *newparam
, *newval
;
267 struct ast_variable
*var
=NULL
;
268 struct ast_config
*cfg
=NULL
;
269 struct ast_category
*cat
=NULL
;
271 SQLSMALLINT colcount
=0;
272 SQLSMALLINT datatype
;
273 SQLSMALLINT decimaldigits
;
274 SQLSMALLINT nullable
;
276 struct custom_prepare_struct cps
= { .sql
= sql
};
285 obj
= ast_odbc_request_obj(database
, 0);
289 newparam
= va_arg(aq
, const char *);
291 ast_odbc_release_obj(obj
);
294 initfield
= ast_strdupa(newparam
);
295 if ((op
= strchr(initfield
, ' ')))
297 newval
= va_arg(aq
, const char *);
298 op
= !strchr(newparam
, ' ') ? " =" : "";
299 snprintf(sql
, sizeof(sql
), "SELECT * FROM %s WHERE %s%s ?%s", table
, newparam
, op
,
300 strcasestr(newparam
, "LIKE") && !ast_odbc_backslash_is_escape(obj
) ? " ESCAPE '\\'" : "");
301 while((newparam
= va_arg(aq
, const char *))) {
302 op
= !strchr(newparam
, ' ') ? " =" : "";
303 snprintf(sql
+ strlen(sql
), sizeof(sql
) - strlen(sql
), " AND %s%s ?%s", newparam
, op
,
304 strcasestr(newparam
, "LIKE") && !ast_odbc_backslash_is_escape(obj
) ? " ESCAPE '\\'" : "");
305 newval
= va_arg(aq
, const char *);
308 snprintf(sql
+ strlen(sql
), sizeof(sql
) - strlen(sql
), " ORDER BY %s", initfield
);
311 stmt
= ast_odbc_prepare_and_execute(obj
, custom_prepare
, &cps
);
314 ast_odbc_release_obj(obj
);
318 res
= SQLNumResultCols(stmt
, &colcount
);
319 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
320 ast_log(LOG_WARNING
, "SQL Column Count error!\n[%s]\n\n", sql
);
321 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
322 ast_odbc_release_obj(obj
);
326 cfg
= ast_config_new();
328 ast_log(LOG_WARNING
, "Out of memory!\n");
329 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
330 ast_odbc_release_obj(obj
);
334 while ((res
=SQLFetch(stmt
)) != SQL_NO_DATA
) {
336 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
337 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
340 cat
= ast_category_new("","",99999);
342 ast_log(LOG_WARNING
, "Out of memory!\n");
345 for (x
=0;x
<colcount
;x
++) {
347 collen
= sizeof(coltitle
);
348 res
= SQLDescribeCol(stmt
, x
+ 1, (unsigned char *)coltitle
, sizeof(coltitle
), &collen
,
349 &datatype
, &colsize
, &decimaldigits
, &nullable
);
350 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
351 ast_log(LOG_WARNING
, "SQL Describe Column error!\n[%s]\n\n", sql
);
352 ast_category_destroy(cat
);
357 res
= SQLGetData(stmt
, x
+ 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), &indicator
);
358 if (indicator
== SQL_NULL_DATA
)
361 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
362 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
363 ast_category_destroy(cat
);
368 chunk
= strsep(&stringp
, ";");
369 if (!ast_strlen_zero(ast_strip(chunk
))) {
370 if (initfield
&& !strcmp(initfield
, coltitle
))
371 ast_category_rename(cat
, chunk
);
372 var
= ast_variable_new(coltitle
, chunk
, "");
373 ast_variable_append(cat
, var
);
377 ast_category_append(cfg
, cat
);
380 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
381 ast_odbc_release_obj(obj
);
386 * \brief Excute an UPDATE query
389 * \param keyfield where clause field
390 * \param lookup value of field for where clause
391 * \param ap list containing one or more field/value set(s).
393 * Update a database table, prepare the sql statement using keyfield and lookup
394 * control the number of records to change. All values to be changed are stored in ap list.
395 * Sub-in the values to the prepared statement and execute it.
397 * \retval number of rows affected
398 * \retval -1 on failure
400 static int update_odbc(const char *database
, const char *table
, const char *keyfield
, const char *lookup
, va_list ap
)
402 struct odbc_obj
*obj
;
406 const char *newparam
, *newval
;
409 struct custom_prepare_struct cps
= { .sql
= sql
, .extra
= lookup
};
410 struct odbc_cache_tables
*tableptr
= ast_odbc_find_table(database
, table
);
411 struct odbc_cache_columns
*column
;
417 ast_odbc_release_table(tableptr
);
421 obj
= ast_odbc_request_obj(database
, 0);
423 ast_odbc_release_table(tableptr
);
427 newparam
= va_arg(aq
, const char *);
429 ast_odbc_release_obj(obj
);
430 ast_odbc_release_table(tableptr
);
433 newval
= va_arg(aq
, const char *);
435 if (tableptr
&& !(column
= ast_odbc_find_column(tableptr
, newparam
))) {
436 ast_log(LOG_WARNING
, "Key field '%s' does not exist in table '%s@%s'. Update will fail\n", newparam
, table
, database
);
439 snprintf(sql
, sizeof(sql
), "UPDATE %s SET %s=?", table
, newparam
);
440 while((newparam
= va_arg(aq
, const char *))) {
441 if ((tableptr
&& (column
= ast_odbc_find_column(tableptr
, newparam
))) || count
> 63) {
442 snprintf(sql
+ strlen(sql
), sizeof(sql
) - strlen(sql
), ", %s=?", newparam
);
443 newval
= va_arg(aq
, const char *);
444 } else { /* the column does not exist in the table OR we've exceeded the space in our flag field */
445 cps
.skip
|= (((long long)1) << count
);
450 snprintf(sql
+ strlen(sql
), sizeof(sql
) - strlen(sql
), " WHERE %s=?", keyfield
);
451 ast_odbc_release_table(tableptr
);
453 stmt
= ast_odbc_prepare_and_execute(obj
, custom_prepare
, &cps
);
456 ast_odbc_release_obj(obj
);
460 res
= SQLRowCount(stmt
, &rowcount
);
461 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
462 ast_odbc_release_obj(obj
);
464 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
465 ast_log(LOG_WARNING
, "SQL Row Count error!\n[%s]\n\n", sql
);
470 return (int)rowcount
;
476 * \brief Excute an INSERT query
479 * \param ap list containing one or more field/value set(s)
481 * Insert a new record into database table, prepare the sql statement.
482 * All values to be changed are stored in ap list.
483 * Sub-in the values to the prepared statement and execute it.
485 * \retval number of rows affected
486 * \retval -1 on failure
488 static int store_odbc(const char *database
, const char *table
, va_list ap
)
490 struct odbc_obj
*obj
;
496 const char *newparam
, *newval
;
499 struct custom_prepare_struct cps
= { .sql
= sql
, .extra
= NULL
};
507 obj
= ast_odbc_request_obj(database
, 0);
511 newparam
= va_arg(aq
, const char *);
513 ast_odbc_release_obj(obj
);
516 newval
= va_arg(aq
, const char *);
517 snprintf(keys
, sizeof(keys
), "%s", newparam
);
518 ast_copy_string(vals
, "?", sizeof(vals
));
519 while ((newparam
= va_arg(aq
, const char *))) {
520 snprintf(keys
+ strlen(keys
), sizeof(keys
) - strlen(keys
), ", %s", newparam
);
521 snprintf(vals
+ strlen(vals
), sizeof(vals
) - strlen(vals
), ", ?");
522 newval
= va_arg(aq
, const char *);
525 snprintf(sql
, sizeof(sql
), "INSERT INTO %s (%s) VALUES (%s)", table
, keys
, vals
);
527 stmt
= ast_odbc_prepare_and_execute(obj
, custom_prepare
, &cps
);
530 ast_odbc_release_obj(obj
);
534 res
= SQLRowCount(stmt
, &rowcount
);
535 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
536 ast_odbc_release_obj(obj
);
538 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
539 ast_log(LOG_WARNING
, "SQL Row Count error!\n[%s]\n\n", sql
);
544 return (int)rowcount
;
550 * \brief Excute an DELETE query
553 * \param keyfield where clause field
554 * \param lookup value of field for where clause
555 * \param ap list containing one or more field/value set(s)
557 * Delete a row from a database table, prepare the sql statement using keyfield and lookup
558 * control the number of records to change. Additional params to match rows are stored in ap list.
559 * Sub-in the values to the prepared statement and execute it.
561 * \retval number of rows affected
562 * \retval -1 on failure
564 static int destroy_odbc(const char *database
, const char *table
, const char *keyfield
, const char *lookup
, va_list ap
)
566 struct odbc_obj
*obj
;
570 const char *newparam
, *newval
;
573 struct custom_prepare_struct cps
= { .sql
= sql
, .extra
= lookup
};
581 obj
= ast_odbc_request_obj(database
, 0);
585 snprintf(sql
, sizeof(sql
), "DELETE FROM %s WHERE ", table
);
586 while((newparam
= va_arg(aq
, const char *))) {
587 snprintf(sql
+ strlen(sql
), sizeof(sql
) - strlen(sql
), "%s=? AND ", newparam
);
588 newval
= va_arg(aq
, const char *);
591 snprintf(sql
+ strlen(sql
), sizeof(sql
) - strlen(sql
), "%s=?", keyfield
);
593 stmt
= ast_odbc_prepare_and_execute(obj
, custom_prepare
, &cps
);
596 ast_odbc_release_obj(obj
);
600 res
= SQLRowCount(stmt
, &rowcount
);
601 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
602 ast_odbc_release_obj(obj
);
604 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
605 ast_log(LOG_WARNING
, "SQL Row Count error!\n[%s]\n\n", sql
);
610 return (int)rowcount
;
616 struct config_odbc_obj
{
618 unsigned long cat_metric
;
621 char var_val
[1024]; /* changed from 128 to 1024 via bug 8251 */
625 static SQLHSTMT
config_odbc_prepare(struct odbc_obj
*obj
, void *data
)
627 struct config_odbc_obj
*q
= data
;
631 res
= SQLAllocHandle(SQL_HANDLE_STMT
, obj
->con
, &sth
);
632 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
633 ast_verb(4, "Failure in AllocStatement %d\n", res
);
637 res
= SQLPrepare(sth
, (unsigned char *)q
->sql
, SQL_NTS
);
638 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
639 ast_verb(4, "Error in PREPARE %d\n", res
);
640 SQLFreeHandle(SQL_HANDLE_STMT
, sth
);
644 SQLBindCol(sth
, 1, SQL_C_ULONG
, &q
->cat_metric
, sizeof(q
->cat_metric
), &q
->err
);
645 SQLBindCol(sth
, 2, SQL_C_CHAR
, q
->category
, sizeof(q
->category
), &q
->err
);
646 SQLBindCol(sth
, 3, SQL_C_CHAR
, q
->var_name
, sizeof(q
->var_name
), &q
->err
);
647 SQLBindCol(sth
, 4, SQL_C_CHAR
, q
->var_val
, sizeof(q
->var_val
), &q
->err
);
652 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
)
654 struct ast_variable
*new_v
;
655 struct ast_category
*cur_cat
;
657 struct odbc_obj
*obj
;
658 char sqlbuf
[1024] = "";
660 size_t sqlleft
= sizeof(sqlbuf
);
661 unsigned int last_cat_metric
= 0;
662 SQLSMALLINT rowcount
= 0;
665 struct config_odbc_obj q
;
666 struct ast_flags loader_flags
= { 0 };
668 memset(&q
, 0, sizeof(q
));
670 if (!file
|| !strcmp (file
, "res_config_odbc.conf"))
671 return NULL
; /* cant configure myself with myself ! */
673 obj
= ast_odbc_request_obj(database
, 0);
677 ast_build_string(&sql
, &sqlleft
, "SELECT cat_metric, category, var_name, var_val FROM %s ", table
);
678 ast_build_string(&sql
, &sqlleft
, "WHERE filename='%s' AND commented=0 ", file
);
679 ast_build_string(&sql
, &sqlleft
, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ");
682 stmt
= ast_odbc_prepare_and_execute(obj
, config_odbc_prepare
, &q
);
685 ast_log(LOG_WARNING
, "SQL select error!\n[%s]\n\n", sql
);
686 ast_odbc_release_obj(obj
);
690 res
= SQLNumResultCols(stmt
, &rowcount
);
692 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
693 ast_log(LOG_WARNING
, "SQL NumResultCols error!\n[%s]\n\n", sql
);
694 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
695 ast_odbc_release_obj(obj
);
700 ast_log(LOG_NOTICE
, "found nothing\n");
701 ast_odbc_release_obj(obj
);
705 cur_cat
= ast_config_get_current_category(cfg
);
707 while ((res
= SQLFetch(stmt
)) != SQL_NO_DATA
) {
708 if (!strcmp (q
.var_name
, "#include")) {
709 if (!ast_config_internal_load(q
.var_val
, cfg
, loader_flags
, "", who_asked
)) {
710 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
711 ast_odbc_release_obj(obj
);
716 if (strcmp(last
, q
.category
) || last_cat_metric
!= q
.cat_metric
) {
717 cur_cat
= ast_category_new(q
.category
, "", 99999);
719 ast_log(LOG_WARNING
, "Out of memory!\n");
722 strcpy(last
, q
.category
);
723 last_cat_metric
= q
.cat_metric
;
724 ast_category_append(cfg
, cur_cat
);
727 new_v
= ast_variable_new(q
.var_name
, q
.var_val
, "");
728 ast_variable_append(cur_cat
, new_v
);
731 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
732 ast_odbc_release_obj(obj
);
736 #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)
737 #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)
739 static int require_odbc(const char *database
, const char *table
, va_list ap
)
741 struct odbc_cache_tables
*tableptr
= ast_odbc_find_table(database
, table
);
742 struct odbc_cache_columns
*col
;
750 while ((elm
= va_arg(ap
, char *))) {
751 type
= va_arg(ap
, require_type
);
752 size
= va_arg(ap
, int);
753 /* Check if the field matches the criteria */
754 AST_RWLIST_TRAVERSE(&tableptr
->columns
, col
, list
) {
755 if (strcmp(col
->name
, elm
) == 0) {
756 /* Type check, first. Some fields are more particular than others */
760 case SQL_LONGVARCHAR
:
763 case SQL_LONGVARBINARY
:
765 #define CHECK_SIZE(n) \
766 if (col->size < n) { \
767 warn_length(col, n); \
771 case RQ_UINTEGER1
: CHECK_SIZE(3) /* 255 */
772 case RQ_INTEGER1
: CHECK_SIZE(4) /* -128 */
773 case RQ_UINTEGER2
: CHECK_SIZE(5) /* 65535 */
774 case RQ_INTEGER2
: CHECK_SIZE(6) /* -32768 */
775 case RQ_UINTEGER3
: /* 16777215 */
776 case RQ_INTEGER3
: CHECK_SIZE(8) /* -8388608 */
777 case RQ_DATE
: /* 2008-06-09 */
778 case RQ_UINTEGER4
: CHECK_SIZE(10) /* 4200000000 */
779 case RQ_INTEGER4
: CHECK_SIZE(11) /* -2100000000 */
780 case RQ_DATETIME
: /* 2008-06-09 16:03:47 */
781 case RQ_UINTEGER8
: CHECK_SIZE(19) /* trust me */
782 case RQ_INTEGER8
: CHECK_SIZE(20) /* ditto */
784 case RQ_CHAR
: CHECK_SIZE(size
)
789 if (type
!= RQ_DATE
) {
790 warn_type(col
, type
);
793 case SQL_TYPE_TIMESTAMP
:
795 if (type
!= RQ_DATE
&& type
!= RQ_DATETIME
) {
796 warn_type(col
, type
);
800 warn_length(col
, size
);
802 #define WARN_TYPE_OR_LENGTH(n) \
803 if (!ast_rq_is_int(type)) { \
804 warn_type(col, type); \
806 warn_length(col, n); \
809 if (type
!= RQ_UINTEGER1
) {
810 WARN_TYPE_OR_LENGTH(size
)
814 if (type
!= RQ_INTEGER1
) {
815 WARN_TYPE_OR_LENGTH(size
)
819 if (type
!= RQ_UINTEGER1
&& type
!= RQ_INTEGER1
&& type
!= RQ_UINTEGER2
) {
820 WARN_TYPE_OR_LENGTH(size
)
825 if (type
!= RQ_UINTEGER1
&& type
!= RQ_INTEGER1
&& type
!= RQ_INTEGER2
) {
826 WARN_TYPE_OR_LENGTH(size
)
830 if (type
!= RQ_UINTEGER1
&& type
!= RQ_INTEGER1
&&
831 type
!= RQ_UINTEGER2
&& type
!= RQ_INTEGER2
&&
832 type
!= RQ_UINTEGER3
&& type
!= RQ_INTEGER3
&&
833 type
!= RQ_INTEGER4
) {
834 WARN_TYPE_OR_LENGTH(size
)
839 if (type
!= RQ_UINTEGER1
&& type
!= RQ_INTEGER1
&&
840 type
!= RQ_UINTEGER2
&& type
!= RQ_INTEGER2
&&
841 type
!= RQ_UINTEGER3
&& type
!= RQ_INTEGER3
&&
842 type
!= RQ_UINTEGER4
) {
843 WARN_TYPE_OR_LENGTH(size
)
847 if (type
!= RQ_UINTEGER1
&& type
!= RQ_INTEGER1
&&
848 type
!= RQ_UINTEGER2
&& type
!= RQ_INTEGER2
&&
849 type
!= RQ_UINTEGER3
&& type
!= RQ_INTEGER3
&&
850 type
!= RQ_UINTEGER4
&& type
!= RQ_INTEGER4
&&
851 type
!= RQ_INTEGER8
) {
852 WARN_TYPE_OR_LENGTH(size
)
857 if (type
!= RQ_UINTEGER1
&& type
!= RQ_INTEGER1
&&
858 type
!= RQ_UINTEGER2
&& type
!= RQ_INTEGER2
&&
859 type
!= RQ_UINTEGER3
&& type
!= RQ_INTEGER3
&&
860 type
!= RQ_UINTEGER4
&& type
!= RQ_INTEGER4
&&
861 type
!= RQ_UINTEGER8
) {
862 WARN_TYPE_OR_LENGTH(size
)
865 #undef WARN_TYPE_OR_LENGTH
871 if (!ast_rq_is_int(type
) && type
!= RQ_FLOAT
) {
872 warn_type(col
, type
);
876 ast_log(LOG_WARNING
, "Realtime table %s@%s: column type (%d) unrecognized for column '%s'\n", table
, database
, col
->type
, elm
);
882 ast_log(LOG_WARNING
, "Realtime table %s@%s requires column '%s', but that column does not exist!\n", table
, database
, elm
);
886 AST_RWLIST_UNLOCK(&tableptr
->columns
);
892 static struct ast_config_engine odbc_engine
= {
894 .load_func
= config_odbc
,
895 .realtime_func
= realtime_odbc
,
896 .realtime_multi_func
= realtime_multi_odbc
,
897 .store_func
= store_odbc
,
898 .destroy_func
= destroy_odbc
,
899 .update_func
= update_odbc
,
900 .require_func
= require_odbc
,
901 .unload_func
= ast_odbc_clear_cache
,
904 static int unload_module (void)
906 ast_config_engine_deregister(&odbc_engine
);
908 ast_verb(1, "res_config_odbc unloaded.\n");
912 static int load_module (void)
914 ast_config_engine_register(&odbc_engine
);
915 ast_verb(1, "res_config_odbc loaded.\n");
919 static int reload_module(void)
924 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_GLOBAL_SYMBOLS
, "Realtime ODBC configuration",
926 .unload
= unload_module
,
927 .reload
= reload_module
,