2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (c) 2005, 2006 Tilghman Lesher
5 * Copyright (c) 2008, 2009 Digium, Inc.
7 * Tilghman Lesher <func_odbc__200508@the-tilghman.com>
9 * See http://www.asterisk.org for more information about
10 * the Asterisk project. Please do not directly contact
11 * any of the maintainers of this project for assistance;
12 * the project provides a web site, mailing lists and IRC
13 * channels for your use.
15 * This program is free software, distributed under the terms of
16 * the GNU General Public License Version 2. See the LICENSE file
17 * at the top of the source tree.
25 * \author Tilghman Lesher <func_odbc__200508@the-tilghman.com>
31 <depend>res_odbc</depend>
36 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38 #include "asterisk/module.h"
39 #include "asterisk/file.h"
40 #include "asterisk/channel.h"
41 #include "asterisk/pbx.h"
42 #include "asterisk/config.h"
43 #include "asterisk/res_odbc.h"
44 #include "asterisk/app.h"
45 #include "asterisk/cli.h"
46 #include "asterisk/strings.h"
49 <function name="ODBC_FETCH" language="en_US">
51 Fetch a row from a multirow query.
54 <parameter name="result-id" required="true" />
57 <para>For queries which are marked as mode=multirow, the original
58 query returns a <replaceable>result-id</replaceable> from which results
59 may be fetched. This function implements the actual fetch of the results.</para>
60 <para>This also sets <variable>ODBC_FETCH_STATUS</variable>.</para>
62 <variable name="ODBC_FETCH_STATUS">
64 If rows are available.
66 <value name="FAILURE">
67 If no rows are available.
73 <application name="ODBCFinish" language="en_US">
75 Clear the resultset of a sucessful multirow query.
78 <parameter name="result-id" required="true" />
81 <para>For queries which are marked as mode=multirow, this will clear
82 any remaining rows of the specified resultset.</para>
85 <function name="SQL_ESC" language="en_US">
87 Escapes single ticks for use in SQL statements.
90 <parameter name="string" required="true" />
93 <para>Used in SQL templates to escape data which may contain single ticks
94 <literal>'</literal> which are otherwise used to delimit data.</para>
95 <para>Example: SELECT foo FROM bar WHERE baz='${SQL_ESC(${ARG1})}'</para>
100 static char *config = "func_odbc.conf";
102 enum odbc_option_flags {
103 OPT_ESCAPECOMMAS = (1 << 0),
104 OPT_MULTIROW = (1 << 1),
107 struct acf_odbc_query {
108 AST_RWLIST_ENTRY(acf_odbc_query) list;
109 char readhandle[5][30];
110 char writehandle[5][30];
112 char sql_write[2048];
113 char sql_insert[2048];
116 struct ast_custom_function *acf;
119 static void odbc_datastore_free(void *data);
121 static struct ast_datastore_info odbc_info = {
123 .destroy = odbc_datastore_free,
126 /* For storing each result row */
127 struct odbc_datastore_row {
128 AST_LIST_ENTRY(odbc_datastore_row) list;
132 /* For storing each result set */
133 struct odbc_datastore {
134 AST_LIST_HEAD(, odbc_datastore_row);
138 static AST_RWLIST_HEAD_STATIC(queries, acf_odbc_query);
140 static int resultcount = 0;
142 AST_THREADSTORAGE(sql_buf);
143 AST_THREADSTORAGE(sql2_buf);
144 AST_THREADSTORAGE(coldata_buf);
145 AST_THREADSTORAGE(colnames_buf);
147 static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
149 static void odbc_datastore_free(void *data)
151 struct odbc_datastore *result = data;
152 struct odbc_datastore_row *row;
153 AST_LIST_LOCK(result);
154 while ((row = AST_LIST_REMOVE_HEAD(result, list))) {
157 AST_LIST_UNLOCK(result);
158 AST_LIST_HEAD_DESTROY(result);
162 static SQLHSTMT generic_execute(struct odbc_obj *obj, void *data)
168 res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
169 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
170 ast_log(LOG_WARNING, "SQL Alloc Handle failed (%d)!\n", res);
174 res = SQLExecDirect(stmt, (unsigned char *)sql, SQL_NTS);
175 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
176 if (res == SQL_ERROR) {
178 SQLINTEGER nativeerror=0, numfields=0;
179 SQLSMALLINT diagbytes=0;
180 unsigned char state[10], diagnostic[256];
182 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
183 for (i = 0; i < numfields; i++) {
184 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
185 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
187 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
193 ast_log(LOG_WARNING, "SQL Exec Direct failed (%d)![%s]\n", res, sql);
194 SQLCloseCursor(stmt);
195 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
203 * Master control routine
205 static int acf_odbc_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
207 struct odbc_obj *obj = NULL;
208 struct acf_odbc_query *query;
209 char *t, varname[15];
210 int i, dsn, bogus_chan = 0;
211 int transactional = 0;
212 AST_DECLARE_APP_ARGS(values,
213 AST_APP_ARG(field)[100];
215 AST_DECLARE_APP_ARGS(args,
216 AST_APP_ARG(field)[100];
218 SQLHSTMT stmt = NULL;
220 struct ast_str *buf = ast_str_thread_get(&sql_buf, 16);
221 struct ast_str *insertbuf = ast_str_thread_get(&sql2_buf, 16);
222 const char *status = "FAILURE";
228 AST_RWLIST_RDLOCK(&queries);
229 AST_RWLIST_TRAVERSE(&queries, query, list) {
230 if (!strcmp(query->acf->name, cmd)) {
236 ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
237 AST_RWLIST_UNLOCK(&queries);
239 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
245 if (!(chan = ast_dummy_channel_alloc())) {
246 AST_RWLIST_UNLOCK(&queries);
253 ast_autoservice_start(chan);
256 ast_str_make_space(&buf, strlen(query->sql_write) * 2 + 300);
257 ast_str_make_space(&insertbuf, strlen(query->sql_insert) * 2 + 300);
259 /* Parse our arguments */
260 t = value ? ast_strdupa(value) : "";
263 ast_log(LOG_ERROR, "Out of memory\n");
264 AST_RWLIST_UNLOCK(&queries);
266 ast_autoservice_stop(chan);
267 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
269 ast_channel_release(chan);
274 AST_STANDARD_APP_ARGS(args, s);
275 for (i = 0; i < args.argc; i++) {
276 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
277 pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
280 /* Parse values, just like arguments */
281 AST_STANDARD_APP_ARGS(values, t);
282 for (i = 0; i < values.argc; i++) {
283 snprintf(varname, sizeof(varname), "VAL%d", i + 1);
284 pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
287 /* Additionally set the value as a whole (but push an empty string if value is NULL) */
288 pbx_builtin_pushvar_helper(chan, "VALUE", value ? value : "");
290 ast_str_substitute_variables(&buf, 0, chan, query->sql_write);
291 ast_str_substitute_variables(&insertbuf, 0, chan, query->sql_insert);
294 chan = ast_channel_release(chan);
296 /* Restore prior values */
297 for (i = 0; i < args.argc; i++) {
298 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
299 pbx_builtin_setvar_helper(chan, varname, NULL);
302 for (i = 0; i < values.argc; i++) {
303 snprintf(varname, sizeof(varname), "VAL%d", i + 1);
304 pbx_builtin_setvar_helper(chan, varname, NULL);
306 pbx_builtin_setvar_helper(chan, "VALUE", NULL);
309 * Okay, this part is confusing. Transactions belong to a single database
310 * handle. Therefore, when working with transactions, we CANNOT failover
311 * to multiple DSNs. We MUST have a single handle all the way through the
312 * transaction, or else we CANNOT enforce atomicity.
314 for (dsn = 0; dsn < 5; dsn++) {
316 /* This can only happen second time through or greater. */
317 ast_log(LOG_WARNING, "Transactions do not work well with multiple DSNs for 'writehandle'\n");
320 if (!ast_strlen_zero(query->writehandle[dsn])) {
321 if ((obj = ast_odbc_retrieve_transaction_obj(chan, query->writehandle[dsn]))) {
324 obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
327 if (obj && (stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(buf)))) {
332 if (obj && !transactional) {
333 ast_odbc_release_obj(obj);
338 if (stmt && rows == 0 && ast_str_strlen(insertbuf) != 0) {
339 SQLCloseCursor(stmt);
340 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
341 for (dsn = 0; dsn < 5; dsn++) {
342 if (!ast_strlen_zero(query->writehandle[dsn])) {
343 obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
345 stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(insertbuf));
350 SQLRowCount(stmt, &rows);
356 SQLRowCount(stmt, &rows);
359 AST_RWLIST_UNLOCK(&queries);
361 /* Output the affected rows, for all cases. In the event of failure, we
362 * flag this as -1 rows. Note that this is different from 0 affected rows
363 * which would be the case if we succeeded in our query, but the values did
366 snprintf(varname, sizeof(varname), "%d", (int)rows);
367 pbx_builtin_setvar_helper(chan, "ODBCROWS", varname);
368 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
372 SQLCloseCursor(stmt);
373 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
375 if (obj && !transactional) {
376 ast_odbc_release_obj(obj);
381 ast_autoservice_stop(chan);
387 static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, char *buf, size_t len)
389 struct odbc_obj *obj = NULL;
390 struct acf_odbc_query *query;
391 char varname[15], rowcount[12] = "-1";
392 struct ast_str *colnames = ast_str_thread_get(&colnames_buf, 16);
393 int res, x, y, buflen = 0, escapecommas, rowlimit = 1, multirow = 0, dsn, bogus_chan = 0;
394 AST_DECLARE_APP_ARGS(args,
395 AST_APP_ARG(field)[100];
397 SQLHSTMT stmt = NULL;
398 SQLSMALLINT colcount=0;
400 SQLSMALLINT collength;
401 struct odbc_datastore *resultset = NULL;
402 struct odbc_datastore_row *row = NULL;
403 struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
404 const char *status = "FAILURE";
408 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
413 ast_str_reset(colnames);
415 AST_RWLIST_RDLOCK(&queries);
416 AST_RWLIST_TRAVERSE(&queries, query, list) {
417 if (!strcmp(query->acf->name, cmd)) {
423 ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
424 AST_RWLIST_UNLOCK(&queries);
426 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
427 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
433 if (!(chan = ast_dummy_channel_alloc())) {
434 AST_RWLIST_UNLOCK(&queries);
441 ast_autoservice_start(chan);
444 AST_STANDARD_APP_ARGS(args, s);
445 for (x = 0; x < args.argc; x++) {
446 snprintf(varname, sizeof(varname), "ARG%d", x + 1);
447 pbx_builtin_pushvar_helper(chan, varname, args.field[x]);
450 ast_str_substitute_variables(&sql, 0, chan, query->sql_read);
453 chan = ast_channel_release(chan);
455 /* Restore prior values */
456 for (x = 0; x < args.argc; x++) {
457 snprintf(varname, sizeof(varname), "ARG%d", x + 1);
458 pbx_builtin_setvar_helper(chan, varname, NULL);
462 /* Save these flags, so we can release the lock */
463 escapecommas = ast_test_flag(query, OPT_ESCAPECOMMAS);
464 if (!bogus_chan && ast_test_flag(query, OPT_MULTIROW)) {
465 if (!(resultset = ast_calloc(1, sizeof(*resultset)))) {
466 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
467 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
468 ast_autoservice_stop(chan);
471 AST_LIST_HEAD_INIT(resultset);
472 if (query->rowlimit) {
473 rowlimit = query->rowlimit;
478 } else if (!bogus_chan) {
479 if (query->rowlimit > 1) {
480 rowlimit = query->rowlimit;
481 if (!(resultset = ast_calloc(1, sizeof(*resultset)))) {
482 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
483 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
484 ast_autoservice_stop(chan);
487 AST_LIST_HEAD_INIT(resultset);
490 AST_RWLIST_UNLOCK(&queries);
492 for (dsn = 0; dsn < 5; dsn++) {
493 if (!ast_strlen_zero(query->readhandle[dsn])) {
494 obj = ast_odbc_request_obj(query->readhandle[dsn], 0);
496 stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql));
502 ast_odbc_release_obj(obj);
507 ast_log(LOG_ERROR, "Unable to execute query [%s]\n", ast_str_buffer(sql));
509 ast_odbc_release_obj(obj);
513 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
514 ast_autoservice_stop(chan);
519 res = SQLNumResultCols(stmt, &colcount);
520 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
521 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", ast_str_buffer(sql));
522 SQLCloseCursor(stmt);
523 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
524 ast_odbc_release_obj(obj);
527 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
528 ast_autoservice_stop(chan);
533 res = SQLFetch(stmt);
534 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
536 if (res == SQL_NO_DATA) {
537 ast_verb(4, "Found no rows [%s]\n", ast_str_buffer(sql));
540 ast_copy_string(rowcount, "0", sizeof(rowcount));
543 ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
544 status = "FETCHERROR";
546 SQLCloseCursor(stmt);
547 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
548 ast_odbc_release_obj(obj);
551 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
552 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
553 ast_autoservice_stop(chan);
560 for (y = 0; y < rowlimit; y++) {
562 for (x = 0; x < colcount; x++) {
564 struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
571 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL);
572 ast_debug(3, "Got collength of %d and maxcol of %d for column '%s' (offset %d)\n", (int)collength, (int)maxcol, colname, x);
573 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
574 snprintf(colname, sizeof(colname), "field%d", x);
577 ast_str_make_space(&coldata, maxcol + 1);
579 if (ast_str_strlen(colnames)) {
580 ast_str_append(&colnames, 0, ",");
582 ast_str_append_escapecommas(&colnames, 0, colname, sizeof(colname));
585 void *tmp = ast_realloc(resultset, sizeof(*resultset) + ast_str_strlen(colnames) + 1);
587 ast_log(LOG_ERROR, "No space for a new resultset?\n");
589 SQLCloseCursor(stmt);
590 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
591 ast_odbc_release_obj(obj);
593 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
594 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
595 ast_autoservice_stop(chan);
599 strcpy((char *)resultset + sizeof(*resultset), ast_str_buffer(colnames));
603 buflen = strlen(buf);
604 res = ast_odbc_ast_str_SQLGetData(&coldata, -1, stmt, x + 1, SQL_CHAR, &indicator);
605 if (indicator == SQL_NULL_DATA) {
606 ast_debug(3, "Got NULL data\n");
607 ast_str_reset(coldata);
611 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
612 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", ast_str_buffer(sql));
618 ast_debug(2, "Got coldata of '%s'\n", ast_str_buffer(coldata));
624 /* Copy data, encoding '\' and ',' for the argument parser */
625 ptrcoldata = ast_str_buffer(coldata);
626 for (i = 0; i < ast_str_strlen(coldata); i++) {
627 if (escapecommas && (ptrcoldata[i] == '\\' || ptrcoldata[i] == ',')) {
628 buf[buflen++] = '\\';
630 buf[buflen++] = ptrcoldata[i];
632 if (buflen >= len - 2) {
636 if (ptrcoldata[i] == '\0') {
642 ast_debug(2, "buf is now set to '%s'\n", buf);
644 ast_debug(2, "buf is now set to '%s'\n", buf);
647 row = ast_calloc(1, sizeof(*row) + buflen + 1);
649 ast_log(LOG_ERROR, "Unable to allocate space for more rows in this resultset.\n");
653 strcpy((char *)row + sizeof(*row), buf);
654 AST_LIST_INSERT_TAIL(resultset, row, list);
657 res = SQLFetch(stmt);
658 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
659 if (res != SQL_NO_DATA) {
660 ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
662 /* Number of rows in the resultset */
671 snprintf(rowcount, sizeof(rowcount), "%d", y);
672 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
673 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
674 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(colnames));
677 struct ast_datastore *odbc_store;
679 uid = ast_atomic_fetchadd_int(&resultcount, +1) + 1;
680 snprintf(buf, len, "%d", uid);
682 /* Name of the query is name of the resultset */
683 ast_copy_string(buf, cmd, len);
685 /* If there's one with the same name already, free it */
686 ast_channel_lock(chan);
687 if ((odbc_store = ast_channel_datastore_find(chan, &odbc_info, buf))) {
688 ast_channel_datastore_remove(chan, odbc_store);
689 odbc_datastore_free(odbc_store->data);
690 ast_free(odbc_store);
692 ast_channel_unlock(chan);
694 odbc_store = ast_datastore_alloc(&odbc_info, buf);
696 ast_log(LOG_ERROR, "Rows retrieved, but unable to store it in the channel. Results fail.\n");
697 odbc_datastore_free(resultset);
698 SQLCloseCursor(stmt);
699 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
700 ast_odbc_release_obj(obj);
702 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
703 ast_autoservice_stop(chan);
706 odbc_store->data = resultset;
707 ast_channel_datastore_add(chan, odbc_store);
710 SQLCloseCursor(stmt);
711 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
712 ast_odbc_release_obj(obj);
714 if (resultset && !multirow) {
715 /* Fetch the first resultset */
716 if (!acf_fetch(chan, "", buf, buf, len)) {
721 ast_autoservice_stop(chan);
726 static int acf_escape(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
730 for (; *data && out - buf < len; data++) {
742 static struct ast_custom_function escape_function = {
748 static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
750 struct ast_datastore *store;
751 struct odbc_datastore *resultset;
752 struct odbc_datastore_row *row;
753 store = ast_channel_datastore_find(chan, &odbc_info, data);
755 pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "FAILURE");
758 resultset = store->data;
759 AST_LIST_LOCK(resultset);
760 row = AST_LIST_REMOVE_HEAD(resultset, list);
761 AST_LIST_UNLOCK(resultset);
763 /* Cleanup datastore */
764 ast_channel_datastore_remove(chan, store);
765 ast_datastore_free(store);
766 pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "FAILURE");
769 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", resultset->names);
770 ast_copy_string(buf, row->data, len);
772 pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "SUCCESS");
776 static struct ast_custom_function fetch_function = {
777 .name = "ODBC_FETCH",
782 static char *app_odbcfinish = "ODBCFinish";
784 static int exec_odbcfinish(struct ast_channel *chan, const char *data)
786 struct ast_datastore *store = ast_channel_datastore_find(chan, &odbc_info, data);
787 if (!store) /* Already freed; no big deal. */
789 ast_channel_datastore_remove(chan, store);
790 ast_datastore_free(store);
794 static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
803 *query = ast_calloc(1, sizeof(struct acf_odbc_query));
807 if (((tmp = ast_variable_retrieve(cfg, catg, "writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg, "dsn")))) {
808 char *tmp2 = ast_strdupa(tmp);
809 AST_DECLARE_APP_ARGS(writeconf,
812 AST_STANDARD_APP_ARGS(writeconf, tmp2);
813 for (i = 0; i < 5; i++) {
814 if (!ast_strlen_zero(writeconf.dsn[i]))
815 ast_copy_string((*query)->writehandle[i], writeconf.dsn[i], sizeof((*query)->writehandle[i]));
819 if ((tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) {
820 char *tmp2 = ast_strdupa(tmp);
821 AST_DECLARE_APP_ARGS(readconf,
824 AST_STANDARD_APP_ARGS(readconf, tmp2);
825 for (i = 0; i < 5; i++) {
826 if (!ast_strlen_zero(readconf.dsn[i]))
827 ast_copy_string((*query)->readhandle[i], readconf.dsn[i], sizeof((*query)->readhandle[i]));
830 /* If no separate readhandle, then use the writehandle for reading */
831 for (i = 0; i < 5; i++) {
832 if (!ast_strlen_zero((*query)->writehandle[i]))
833 ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i], sizeof((*query)->readhandle[i]));
837 if ((tmp = ast_variable_retrieve(cfg, catg, "readsql")))
838 ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
839 else if ((tmp = ast_variable_retrieve(cfg, catg, "read"))) {
840 ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s. Please use 'readsql' instead.\n", catg);
841 ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
844 if (!ast_strlen_zero((*query)->sql_read) && ast_strlen_zero((*query)->readhandle[0])) {
847 ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg);
851 if ((tmp = ast_variable_retrieve(cfg, catg, "writesql")))
852 ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
853 else if ((tmp = ast_variable_retrieve(cfg, catg, "write"))) {
854 ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s. Please use 'writesql' instead.\n", catg);
855 ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
858 if (!ast_strlen_zero((*query)->sql_write) && ast_strlen_zero((*query)->writehandle[0])) {
861 ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg);
865 if ((tmp = ast_variable_retrieve(cfg, catg, "insertsql"))) {
866 ast_copy_string((*query)->sql_insert, tmp, sizeof((*query)->sql_insert));
869 /* Allow escaping of embedded commas in fields to be turned off */
870 ast_set_flag((*query), OPT_ESCAPECOMMAS);
871 if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) {
873 ast_clear_flag((*query), OPT_ESCAPECOMMAS);
876 if ((tmp = ast_variable_retrieve(cfg, catg, "mode"))) {
877 if (strcasecmp(tmp, "multirow") == 0)
878 ast_set_flag((*query), OPT_MULTIROW);
879 if ((tmp = ast_variable_retrieve(cfg, catg, "rowlimit")))
880 sscanf(tmp, "%30d", &((*query)->rowlimit));
883 (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function));
884 if (! (*query)->acf) {
889 if (ast_string_field_init((*query)->acf, 128)) {
890 ast_free((*query)->acf);
896 if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
897 if (asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg) < 0) {
898 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
901 if (asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg) < 0) {
902 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
906 if (!((*query)->acf->name)) {
907 ast_string_field_free_memory((*query)->acf);
908 ast_free((*query)->acf);
914 if ((tmp = ast_variable_retrieve(cfg, catg, "syntax")) && !ast_strlen_zero(tmp)) {
915 ast_string_field_build((*query)->acf, syntax, "%s(%s)", (*query)->acf->name, tmp);
917 ast_string_field_build((*query)->acf, syntax, "%s(<arg1>[...[,<argN>]])", (*query)->acf->name);
920 if (ast_strlen_zero((*query)->acf->syntax)) {
921 ast_free((char *)(*query)->acf->name);
922 ast_string_field_free_memory((*query)->acf);
923 ast_free((*query)->acf);
929 if ((tmp = ast_variable_retrieve(cfg, catg, "synopsis")) && !ast_strlen_zero(tmp)) {
930 ast_string_field_set((*query)->acf, synopsis, tmp);
932 ast_string_field_set((*query)->acf, synopsis, "Runs the referenced query with the specified arguments");
935 if (ast_strlen_zero((*query)->acf->synopsis)) {
936 ast_free((char *)(*query)->acf->name);
937 ast_string_field_free_memory((*query)->acf);
938 ast_free((*query)->acf);
944 if (!ast_strlen_zero((*query)->sql_read) && !ast_strlen_zero((*query)->sql_write)) {
945 ast_string_field_build((*query)->acf, desc,
946 "Runs the following query, as defined in func_odbc.conf, performing\n"
947 "substitution of the arguments into the query as specified by ${ARG1},\n"
948 "${ARG2}, ... ${ARGn}. When setting the function, the values are provided\n"
949 "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
951 "\nRead:\n%s\n\nWrite:\n%s\n%s%s%s",
952 ast_strlen_zero((*query)->sql_insert) ? "" :
953 "If the write query affects no rows, the insert query will be\n"
957 ast_strlen_zero((*query)->sql_insert) ? "" : "Insert:\n",
958 ast_strlen_zero((*query)->sql_insert) ? "" : (*query)->sql_insert,
959 ast_strlen_zero((*query)->sql_insert) ? "" : "\n");
960 } else if (!ast_strlen_zero((*query)->sql_read)) {
961 ast_string_field_build((*query)->acf, desc,
962 "Runs the following query, as defined in func_odbc.conf, performing\n"
963 "substitution of the arguments into the query as specified by ${ARG1},\n"
964 "${ARG2}, ... ${ARGn}. This function may only be read, not set.\n\nSQL:\n%s\n",
966 } else if (!ast_strlen_zero((*query)->sql_write)) {
967 ast_string_field_build((*query)->acf, desc,
968 "Runs the following query, as defined in func_odbc.conf, performing\n"
969 "substitution of the arguments into the query as specified by ${ARG1},\n"
970 "${ARG2}, ... ${ARGn}. The values are provided either in whole as\n"
971 "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
972 "This function may only be set.\n%sSQL:\n%s\n%s%s%s",
973 ast_strlen_zero((*query)->sql_insert) ? "" :
974 "If the write query affects no rows, the insert query will be\n"
977 ast_strlen_zero((*query)->sql_insert) ? "" : "Insert:\n",
978 ast_strlen_zero((*query)->sql_insert) ? "" : (*query)->sql_insert,
979 ast_strlen_zero((*query)->sql_insert) ? "" : "\n");
981 ast_string_field_free_memory((*query)->acf);
982 ast_free((char *)(*query)->acf->name);
983 ast_free((*query)->acf);
985 ast_log(LOG_WARNING, "Section '%s' was found, but there was no SQL to execute. Ignoring.\n", catg);
989 if (ast_strlen_zero((*query)->acf->desc)) {
990 ast_string_field_free_memory((*query)->acf);
991 ast_free((char *)(*query)->acf->name);
992 ast_free((*query)->acf);
998 if (ast_strlen_zero((*query)->sql_read)) {
999 (*query)->acf->read = NULL;
1001 (*query)->acf->read = acf_odbc_read;
1004 if (ast_strlen_zero((*query)->sql_write)) {
1005 (*query)->acf->write = NULL;
1007 (*query)->acf->write = acf_odbc_write;
1013 static int free_acf_query(struct acf_odbc_query *query)
1017 if (query->acf->name)
1018 ast_free((char *)query->acf->name);
1019 ast_string_field_free_memory(query->acf);
1020 ast_free(query->acf);
1027 static char *cli_odbc_read(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1029 AST_DECLARE_APP_ARGS(args,
1030 AST_APP_ARG(field)[100];
1032 struct ast_str *sql;
1033 char *char_args, varname[10];
1034 struct acf_odbc_query *query;
1035 struct ast_channel *chan;
1040 e->command = "odbc read";
1042 "Usage: odbc read <name> <args> [exec]\n"
1043 " Evaluates the SQL provided in the ODBC function <name>, and\n"
1044 " optionally executes the function. This function is intended for\n"
1045 " testing purposes. Remember to quote arguments containing spaces.\n";
1049 int wordlen = strlen(a->word), which = 0;
1050 /* Complete function name */
1051 AST_RWLIST_RDLOCK(&queries);
1052 AST_RWLIST_TRAVERSE(&queries, query, list) {
1053 if (!strncasecmp(query->acf->name, a->word, wordlen)) {
1054 if (++which > a->n) {
1055 char *res = ast_strdup(query->acf->name);
1056 AST_RWLIST_UNLOCK(&queries);
1061 AST_RWLIST_UNLOCK(&queries);
1063 } else if (a->pos == 4) {
1064 return a->n == 0 ? ast_strdup("exec") : NULL;
1070 if (a->argc < 4 || a->argc > 5) {
1071 return CLI_SHOWUSAGE;
1074 sql = ast_str_thread_get(&sql_buf, 16);
1079 AST_RWLIST_RDLOCK(&queries);
1080 AST_RWLIST_TRAVERSE(&queries, query, list) {
1081 if (!strcmp(query->acf->name, a->argv[2])) {
1087 ast_cli(a->fd, "No such query '%s'\n", a->argv[2]);
1088 AST_RWLIST_UNLOCK(&queries);
1089 return CLI_SHOWUSAGE;
1092 if (ast_strlen_zero(query->sql_read)) {
1093 ast_cli(a->fd, "The function %s has no writesql parameter.\n", a->argv[2]);
1094 AST_RWLIST_UNLOCK(&queries);
1098 ast_str_make_space(&sql, strlen(query->sql_read) * 2 + 300);
1100 /* Evaluate function */
1101 char_args = ast_strdupa(a->argv[3]);
1103 chan = ast_dummy_channel_alloc();
1105 AST_STANDARD_APP_ARGS(args, char_args);
1106 for (i = 0; i < args.argc; i++) {
1107 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
1108 pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
1111 ast_str_substitute_variables(&sql, 0, chan, query->sql_read);
1112 chan = ast_channel_release(chan);
1114 if (a->argc == 5 && !strcmp(a->argv[4], "exec")) {
1115 /* Execute the query */
1116 struct odbc_obj *obj = NULL;
1117 int dsn, executed = 0;
1119 int rows = 0, res, x;
1120 SQLSMALLINT colcount = 0, collength;
1122 struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
1126 for (dsn = 0; dsn < 5; dsn++) {
1127 if (ast_strlen_zero(query->readhandle[dsn])) {
1130 ast_debug(1, "Found handle %s\n", query->readhandle[dsn]);
1131 if (!(obj = ast_odbc_request_obj(query->readhandle[dsn], 0))) {
1135 ast_debug(1, "Got obj\n");
1136 if (!(stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)))) {
1137 ast_odbc_release_obj(obj);
1144 res = SQLNumResultCols(stmt, &colcount);
1145 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1146 ast_cli(a->fd, "SQL Column Count error!\n[%s]\n\n", ast_str_buffer(sql));
1147 SQLCloseCursor(stmt);
1148 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1149 ast_odbc_release_obj(obj);
1151 AST_RWLIST_UNLOCK(&queries);
1155 res = SQLFetch(stmt);
1156 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1157 SQLCloseCursor(stmt);
1158 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1159 ast_odbc_release_obj(obj);
1161 if (res == SQL_NO_DATA) {
1162 ast_cli(a->fd, "Returned %d rows. Query executed on handle %d:%s [%s]\n", rows, dsn, query->readhandle[dsn], ast_str_buffer(sql));
1165 ast_cli(a->fd, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
1167 AST_RWLIST_UNLOCK(&queries);
1171 for (x = 0; x < colcount; x++) {
1172 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL);
1173 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
1174 snprintf(colname, sizeof(colname), "field%d", x);
1177 res = ast_odbc_ast_str_SQLGetData(&coldata, maxcol, stmt, x + 1, SQL_CHAR, &indicator);
1178 if (indicator == SQL_NULL_DATA) {
1179 ast_str_set(&coldata, 0, "(nil)");
1183 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1184 ast_cli(a->fd, "SQL Get Data error %d!\n[%s]\n\n", res, ast_str_buffer(sql));
1185 SQLCloseCursor(stmt);
1186 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1187 ast_odbc_release_obj(obj);
1189 AST_RWLIST_UNLOCK(&queries);
1193 ast_cli(a->fd, "%-20.20s %s\n", colname, ast_str_buffer(coldata));
1198 res = SQLFetch(stmt);
1199 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1202 ast_cli(a->fd, "%-20.20s %s\n", "----------", "----------");
1204 SQLCloseCursor(stmt);
1205 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1206 ast_odbc_release_obj(obj);
1208 ast_cli(a->fd, "Returned %d row%s. Query executed on handle %d [%s]\n", rows, rows == 1 ? "" : "s", dsn, query->readhandle[dsn]);
1212 ast_odbc_release_obj(obj);
1217 ast_cli(a->fd, "Failed to execute query. [%s]\n", ast_str_buffer(sql));
1219 } else { /* No execution, just print out the resulting SQL */
1220 ast_cli(a->fd, "%s\n", ast_str_buffer(sql));
1222 AST_RWLIST_UNLOCK(&queries);
1226 static char *cli_odbc_write(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1228 AST_DECLARE_APP_ARGS(values,
1229 AST_APP_ARG(field)[100];
1231 AST_DECLARE_APP_ARGS(args,
1232 AST_APP_ARG(field)[100];
1234 struct ast_str *sql;
1235 char *char_args, *char_values, varname[10];
1236 struct acf_odbc_query *query;
1237 struct ast_channel *chan;
1242 e->command = "odbc write";
1244 "Usage: odbc write <name> <args> <value> [exec]\n"
1245 " Evaluates the SQL provided in the ODBC function <name>, and\n"
1246 " optionally executes the function. This function is intended for\n"
1247 " testing purposes. Remember to quote arguments containing spaces.\n";
1251 int wordlen = strlen(a->word), which = 0;
1252 /* Complete function name */
1253 AST_RWLIST_RDLOCK(&queries);
1254 AST_RWLIST_TRAVERSE(&queries, query, list) {
1255 if (!strncasecmp(query->acf->name, a->word, wordlen)) {
1256 if (++which > a->n) {
1257 char *res = ast_strdup(query->acf->name);
1258 AST_RWLIST_UNLOCK(&queries);
1263 AST_RWLIST_UNLOCK(&queries);
1265 } else if (a->pos == 5) {
1266 return a->n == 0 ? ast_strdup("exec") : NULL;
1272 if (a->argc < 5 || a->argc > 6) {
1273 return CLI_SHOWUSAGE;
1276 sql = ast_str_thread_get(&sql_buf, 16);
1281 AST_RWLIST_RDLOCK(&queries);
1282 AST_RWLIST_TRAVERSE(&queries, query, list) {
1283 if (!strcmp(query->acf->name, a->argv[2])) {
1289 ast_cli(a->fd, "No such query '%s'\n", a->argv[2]);
1290 AST_RWLIST_UNLOCK(&queries);
1291 return CLI_SHOWUSAGE;
1294 if (ast_strlen_zero(query->sql_write)) {
1295 ast_cli(a->fd, "The function %s has no writesql parameter.\n", a->argv[2]);
1296 AST_RWLIST_UNLOCK(&queries);
1300 ast_str_make_space(&sql, strlen(query->sql_write) * 2 + 300);
1302 /* Evaluate function */
1303 char_args = ast_strdupa(a->argv[3]);
1304 char_values = ast_strdupa(a->argv[4]);
1306 chan = ast_dummy_channel_alloc();
1308 AST_STANDARD_APP_ARGS(args, char_args);
1309 for (i = 0; i < args.argc; i++) {
1310 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
1311 pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
1314 /* Parse values, just like arguments */
1315 AST_STANDARD_APP_ARGS(values, char_values);
1316 for (i = 0; i < values.argc; i++) {
1317 snprintf(varname, sizeof(varname), "VAL%d", i + 1);
1318 pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
1321 /* Additionally set the value as a whole (but push an empty string if value is NULL) */
1322 pbx_builtin_pushvar_helper(chan, "VALUE", S_OR(a->argv[4], ""));
1323 ast_str_substitute_variables(&sql, 0, chan, query->sql_write);
1324 ast_debug(1, "SQL is %s\n", ast_str_buffer(sql));
1325 chan = ast_channel_release(chan);
1327 if (a->argc == 6 && !strcmp(a->argv[5], "exec")) {
1328 /* Execute the query */
1329 struct odbc_obj *obj = NULL;
1330 int dsn, executed = 0;
1334 for (dsn = 0; dsn < 5; dsn++) {
1335 if (ast_strlen_zero(query->writehandle[dsn])) {
1338 if (!(obj = ast_odbc_request_obj(query->writehandle[dsn], 0))) {
1341 if (!(stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)))) {
1342 ast_odbc_release_obj(obj);
1347 SQLRowCount(stmt, &rows);
1348 SQLCloseCursor(stmt);
1349 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1350 ast_odbc_release_obj(obj);
1352 ast_cli(a->fd, "Affected %d rows. Query executed on handle %d [%s]\n", (int)rows, dsn, query->writehandle[dsn]);
1358 ast_cli(a->fd, "Failed to execute query.\n");
1360 } else { /* No execution, just print out the resulting SQL */
1361 ast_cli(a->fd, "%s\n", ast_str_buffer(sql));
1363 AST_RWLIST_UNLOCK(&queries);
1367 static struct ast_cli_entry cli_func_odbc[] = {
1368 AST_CLI_DEFINE(cli_odbc_write, "Test setting a func_odbc function"),
1369 AST_CLI_DEFINE(cli_odbc_read, "Test reading a func_odbc function"),
1372 static int load_module(void)
1375 struct ast_config *cfg;
1377 struct ast_flags config_flags = { 0 };
1379 res |= ast_custom_function_register(&fetch_function);
1380 res |= ast_register_application_xml(app_odbcfinish, exec_odbcfinish);
1381 AST_RWLIST_WRLOCK(&queries);
1383 cfg = ast_config_load(config, config_flags);
1384 if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
1385 ast_log(LOG_NOTICE, "Unable to load config for func_odbc: %s\n", config);
1386 AST_RWLIST_UNLOCK(&queries);
1387 return AST_MODULE_LOAD_DECLINE;
1390 for (catg = ast_category_browse(cfg, NULL);
1392 catg = ast_category_browse(cfg, catg)) {
1393 struct acf_odbc_query *query = NULL;
1396 if ((err = init_acf_query(cfg, catg, &query))) {
1398 ast_log(LOG_ERROR, "Out of memory\n");
1399 else if (err == EINVAL)
1400 ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg);
1402 ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err);
1404 AST_RWLIST_INSERT_HEAD(&queries, query, list);
1405 ast_custom_function_register(query->acf);
1409 ast_config_destroy(cfg);
1410 res |= ast_custom_function_register(&escape_function);
1411 ast_cli_register_multiple(cli_func_odbc, ARRAY_LEN(cli_func_odbc));
1413 AST_RWLIST_UNLOCK(&queries);
1417 static int unload_module(void)
1419 struct acf_odbc_query *query;
1422 AST_RWLIST_WRLOCK(&queries);
1423 while (!AST_RWLIST_EMPTY(&queries)) {
1424 query = AST_RWLIST_REMOVE_HEAD(&queries, list);
1425 ast_custom_function_unregister(query->acf);
1426 free_acf_query(query);
1429 res |= ast_custom_function_unregister(&escape_function);
1430 res |= ast_custom_function_unregister(&fetch_function);
1431 res |= ast_unregister_application(app_odbcfinish);
1432 ast_cli_unregister_multiple(cli_func_odbc, ARRAY_LEN(cli_func_odbc));
1434 /* Allow any threads waiting for this lock to pass (avoids a race) */
1435 AST_RWLIST_UNLOCK(&queries);
1437 AST_RWLIST_WRLOCK(&queries);
1439 AST_RWLIST_UNLOCK(&queries);
1443 static int reload(void)
1446 struct ast_config *cfg;
1447 struct acf_odbc_query *oldquery;
1449 struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
1451 cfg = ast_config_load(config, config_flags);
1452 if (cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID)
1455 AST_RWLIST_WRLOCK(&queries);
1457 while (!AST_RWLIST_EMPTY(&queries)) {
1458 oldquery = AST_RWLIST_REMOVE_HEAD(&queries, list);
1459 ast_custom_function_unregister(oldquery->acf);
1460 free_acf_query(oldquery);
1464 ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
1468 for (catg = ast_category_browse(cfg, NULL);
1470 catg = ast_category_browse(cfg, catg)) {
1471 struct acf_odbc_query *query = NULL;
1473 if (init_acf_query(cfg, catg, &query)) {
1474 ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
1476 AST_RWLIST_INSERT_HEAD(&queries, query, list);
1477 ast_custom_function_register(query->acf);
1481 ast_config_destroy(cfg);
1483 AST_RWLIST_UNLOCK(&queries);
1487 /* XXX need to revise usecount - set if query_lock is set */
1489 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ODBC lookups",
1490 .load = load_module,
1491 .unload = unload_module,