44 #include "asterisk/res_odbc_transaction.h"
118 static char *config =
"func_odbc.conf";
120 #define DEFAULT_SINGLE_DB_CONNECTION 0
122 static int single_db_connection;
124 AST_RWLOCK_DEFINE_STATIC(single_db_connection_lock);
126 enum odbc_option_flags {
127 OPT_ESCAPECOMMAS = (1 << 0),
128 OPT_MULTIROW = (1 << 1),
133 char readhandle[5][30];
134 char writehandle[5][30];
144 static void odbc_datastore_free(
void *data);
148 .destroy = odbc_datastore_free,
174 #define DSN_BUCKETS 37
178 static int dsn_hash(
const void *obj,
const int flags)
180 const struct dsn *object;
198 static int dsn_cmp(
void *obj,
void *arg,
int flags)
200 const struct dsn *object_left = obj;
201 const struct dsn *object_right = arg;
202 const char *right_key = arg;
207 right_key = object_right->
name;
210 cmp = strcmp(object_left->
name, right_key);
213 cmp = strncmp(object_left->
name, right_key, strlen(right_key));
227 static void dsn_destructor(
void *obj)
251 dsn = ao2_alloc(
sizeof(*dsn) + strlen(name) + 1, dsn_destructor);
257 strcpy(dsn->
name, name);
273 static SQLHSTMT silent_execute(
struct odbc_obj *obj,
void *data);
292 res = SQLGetConnectAttr(connection->
con, SQL_ATTR_CONNECTION_DEAD, &dead, 0, 0);
293 if (SQL_SUCCEEDED(res)) {
294 return dead == SQL_CD_TRUE ? 1 : 0;
305 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
380 ast_rwlock_rdlock(&single_db_connection_lock);
381 if (single_db_connection) {
385 obj = (*dsn)->connection;
391 ast_rwlock_unlock(&single_db_connection_lock);
415 (*dsn)->connection = NULL;
427 }
else if (obj && *obj) {
435 static int resultcount = 0;
442 static int acf_fetch(
struct ast_channel *chan,
const char *cmd,
char *data,
char *buf,
size_t len);
444 static void odbc_datastore_free(
void *data)
477 res = SQLAllocHandle (SQL_HANDLE_STMT, obj->
con, &stmt);
478 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
479 ast_log(LOG_WARNING,
"SQL Alloc Handle failed (%d)!\n", res);
484 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
485 if (res == SQL_ERROR && !silent) {
487 SQLINTEGER nativeerror=0, numfields=0;
488 SQLSMALLINT diagbytes=0;
489 unsigned char state[10], diagnostic[256];
491 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
492 for (i = 0; i < numfields; i++) {
493 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic,
sizeof(diagnostic), &diagbytes);
494 ast_log(LOG_WARNING,
"SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
496 ast_log(LOG_WARNING,
"Oh, that was good. There are really %d diagnostics?\n", (
int)numfields);
503 ast_log(LOG_WARNING,
"SQL Exec Direct failed (%d)![%s]\n", res, sql);
505 SQLCloseCursor(stmt);
506 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
513 static SQLHSTMT generic_execute(
struct odbc_obj *obj,
void *data)
518 static SQLHSTMT silent_execute(
struct odbc_obj *obj,
void *data)
530 char *t, varname[15];
531 int i, dsn_num, bogus_chan = 0;
532 int transactional = 0;
539 SQLHSTMT stmt = NULL;
543 const char *status =
"FAILURE";
544 struct dsn *dsn = NULL;
546 if (!buf || !insertbuf) {
551 AST_RWLIST_TRAVERSE(&
queries, query, list) {
552 if (!strcmp(query->acf->name, cmd)) {
558 ast_log(LOG_ERROR,
"No such function '%s'\n", cmd);
567 if (args.argc < query->minargs) {
568 ast_log(LOG_ERROR,
"%d arguments supplied to '%s' requiring minimum %d\n",
569 args.argc, cmd, query->minargs);
586 ast_str_make_space(&buf, strlen(query->sql_write) * 2 + 300);
588 if (query->sql_insert) {
589 ast_str_make_space(&insertbuf, strlen(query->sql_insert) * 2 + 300);
596 ast_log(LOG_ERROR,
"Out of memory\n");
607 snprintf(varname,
sizeof(varname),
"%u", args.argc);
609 for (i = 0; i < args.argc; i++) {
610 snprintf(varname,
sizeof(varname),
"ARG%d", i + 1);
616 for (i = 0; i <
values.argc; i++) {
617 snprintf(varname,
sizeof(varname),
"VAL%d", i + 1);
625 if (query->sql_insert) {
635 for (i = 0; i < args.argc; i++) {
636 snprintf(varname,
sizeof(varname),
"ARG%d", i + 1);
640 for (i = 0; i <
values.argc; i++) {
641 snprintf(varname,
sizeof(varname),
"VAL%d", i + 1);
653 for (dsn_num = 0; dsn_num < 5; dsn_num++) {
654 if (!ast_strlen_zero(query->writehandle[dsn_num])) {
657 ast_log(LOG_WARNING,
"Transactions do not work well with multiple DSNs for 'writehandle'\n");
660 if ((obj = ast_odbc_retrieve_transaction_obj(chan, query->writehandle[dsn_num]))) {
670 if (!transactional) {
677 SQLRowCount(stmt, &rows);
678 SQLCloseCursor(stmt);
679 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
684 }
else if (query->sql_insert) {
685 if (!transactional) {
689 for (transactional = 0, dsn_num = 0; dsn_num < 5; dsn_num++) {
690 if (!ast_strlen_zero(query->writehandle[dsn_num])) {
693 ast_log(LOG_WARNING,
"Transactions do not work well with multiple DSNs for 'writehandle'\n");
698 if ((obj = ast_odbc_retrieve_transaction_obj(chan, query->writehandle[dsn_num]))) {
710 SQLRowCount(stmt, &rows);
711 SQLCloseCursor(stmt);
712 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
726 snprintf(varname,
sizeof(varname),
"%d", (
int)rows);
731 if (!transactional) {
742 static int acf_odbc_read(
struct ast_channel *chan,
const char *cmd,
char *s,
char *buf,
size_t len)
746 char varname[15], rowcount[12] =
"-1";
748 int res, x, y, buflen = 0, escapecommas, rowlimit = 1, multirow = 0, dsn_num, bogus_chan = 0;
752 SQLHSTMT stmt = NULL;
753 SQLSMALLINT colcount=0;
755 SQLSMALLINT collength;
759 const char *status =
"FAILURE";
760 struct dsn *dsn = NULL;
762 if (!sql || !colnames) {
772 AST_RWLIST_TRAVERSE(&
queries, query, list) {
773 if (!strcmp(query->acf->name, cmd)) {
779 ast_log(LOG_ERROR,
"No such function '%s'\n", cmd);
789 if (args.argc < query->minargs) {
790 ast_log(LOG_ERROR,
"%d arguments supplied to '%s' requiring minimum %d\n",
791 args.argc, cmd, query->minargs);
808 snprintf(varname,
sizeof(varname),
"%u", args.argc);
810 for (x = 0; x < args.argc; x++) {
811 snprintf(varname,
sizeof(varname),
"ARG%d", x + 1);
823 for (x = 0; x < args.argc; x++) {
824 snprintf(varname,
sizeof(varname),
"ARG%d", x + 1);
830 escapecommas = ast_test_flag(query, OPT_ESCAPECOMMAS);
831 if (!bogus_chan && ast_test_flag(query, OPT_MULTIROW)) {
832 if (!(resultset =
ast_calloc(1,
sizeof(*resultset)))) {
840 if (query->rowlimit) {
841 rowlimit = query->rowlimit;
846 }
else if (!bogus_chan) {
847 if (query->rowlimit > 1) {
848 rowlimit = query->rowlimit;
849 if (!(resultset =
ast_calloc(1,
sizeof(*resultset)))) {
861 for (dsn_num = 0; dsn_num < 5; dsn_num++) {
862 if (!ast_strlen_zero(query->readhandle[dsn_num])) {
876 ast_log(LOG_ERROR,
"Unable to execute query [%s]\n",
ast_str_buffer(sql));
882 odbc_datastore_free(resultset);
886 res = SQLNumResultCols(stmt, &colcount);
887 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
888 ast_log(LOG_WARNING,
"SQL Column Count error!\n[%s]\n\n",
ast_str_buffer(sql));
889 SQLCloseCursor(stmt);
890 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
896 odbc_datastore_free(resultset);
901 ast_verb(4,
"Returned %d columns [%s]\n", colcount,
ast_str_buffer(sql));
903 SQLCloseCursor(stmt);
904 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
911 odbc_datastore_free(resultset);
915 res = SQLFetch(stmt);
916 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
918 if (res == SQL_NO_DATA) {
925 ast_log(LOG_WARNING,
"Error %d in FETCH [%s]\n", res,
ast_str_buffer(sql));
926 status =
"FETCHERROR";
928 SQLCloseCursor(stmt);
929 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
936 odbc_datastore_free(resultset);
942 for (y = 0; y < rowlimit; y++) {
944 for (x = 0; x < colcount; x++) {
950 odbc_datastore_free(resultset);
951 SQLCloseCursor(stmt);
952 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
963 SQLLEN octetlength = 0;
965 res = SQLDescribeCol(stmt, x + 1, (
unsigned char *)colname,
sizeof(colname), &collength, NULL, NULL, NULL, NULL);
966 ast_debug(3,
"Got collength of %d for column '%s' (offset %d)\n", (
int)collength, colname, x);
967 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
968 snprintf(colname,
sizeof(colname),
"field%d", x);
971 SQLColAttribute(stmt, x + 1, SQL_DESC_OCTET_LENGTH, NULL, 0, NULL, &octetlength);
973 ast_str_make_space(&coldata, octetlength + 1);
983 ast_log(LOG_ERROR,
"No space for a new resultset?\n");
984 odbc_datastore_free(resultset);
985 SQLCloseCursor(stmt);
986 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
996 strcpy((
char *)resultset +
sizeof(*resultset),
ast_str_buffer(colnames));
1000 buflen = strlen(buf);
1002 if (indicator == SQL_NULL_DATA) {
1008 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1009 ast_log(LOG_WARNING,
"SQL Get Data error!\n[%s]\n\n",
ast_str_buffer(sql));
1018 buf[buflen++] =
',';
1024 if (escapecommas && (ptrcoldata[i] ==
'\\' || ptrcoldata[i] ==
',')) {
1025 buf[buflen++] =
'\\';
1027 buf[buflen++] = ptrcoldata[i];
1029 if (buflen >= len - 2) {
1033 if (ptrcoldata[i] ==
'\0') {
1039 ast_debug(2,
"buf is now set to '%s'\n", buf);
1041 ast_debug(2,
"buf is now set to '%s'\n", buf);
1044 row =
ast_calloc(1,
sizeof(*row) + buflen + 1);
1046 ast_log(LOG_ERROR,
"Unable to allocate space for more rows in this resultset.\n");
1047 status =
"MEMERROR";
1050 strcpy((
char *)row +
sizeof(*row), buf);
1054 res = SQLFetch(stmt);
1055 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1056 if (res != SQL_NO_DATA) {
1057 ast_log(LOG_WARNING,
"Error %d in FETCH [%s]\n", res,
ast_str_buffer(sql));
1068 snprintf(rowcount,
sizeof(rowcount),
"%d", y);
1077 snprintf(buf, len,
"%d", uid);
1083 ast_channel_lock(chan);
1088 ast_channel_unlock(chan);
1090 odbc_store = ast_datastore_alloc(&odbc_info, buf);
1092 ast_log(LOG_ERROR,
"Rows retrieved, but unable to store it in the channel. Results fail.\n");
1093 odbc_datastore_free(resultset);
1094 SQLCloseCursor(stmt);
1095 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1101 odbc_store->
data = resultset;
1102 ast_channel_lock(chan);
1104 ast_channel_unlock(chan);
1107 SQLCloseCursor(stmt);
1108 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1110 if (resultset && !multirow) {
1112 if (!acf_fetch(chan,
"", buf, buf, len)) {
1122 static int acf_escape(
struct ast_channel *chan,
const char *cmd,
char *data,
char *buf,
size_t len,
char character)
1126 for (; *data && out - buf < len; data++) {
1127 if (*data == character) {
1138 static int acf_escape_ticks(
struct ast_channel *chan,
const char *cmd,
char *data,
char *buf,
size_t len)
1140 return acf_escape(chan, cmd, data, buf, len,
'\'');
1145 .read = acf_escape_ticks,
1149 static int acf_escape_backslashes(
struct ast_channel *chan,
const char *cmd,
char *data,
char *buf,
size_t len)
1151 return acf_escape(chan, cmd, data, buf, len,
'\\');
1155 .
name =
"SQL_ESC_BACKSLASHES",
1156 .read = acf_escape_backslashes,
1160 static int acf_fetch(
struct ast_channel *chan,
const char *cmd,
char *data,
char *buf,
size_t len)
1167 ast_log(LOG_WARNING,
"No channel was provided to %s function.\n", cmd);
1171 ast_channel_lock(chan);
1174 ast_channel_unlock(chan);
1178 resultset = store->
data;
1186 ast_channel_unlock(chan);
1191 ast_channel_unlock(chan);
1199 .
name =
"ODBC_FETCH",
1204 static char *app_odbcfinish =
"ODBCFinish";
1206 static int exec_odbcfinish(
struct ast_channel *chan,
const char *data)
1210 ast_channel_lock(chan);
1216 ast_channel_unlock(chan);
1224 if (query->acf->name)
1225 ast_free((
char *)query->acf->name);
1227 ast_free(query->acf);
1229 ast_free(query->sql_read);
1230 ast_free(query->sql_write);
1231 ast_free(query->sql_insert);
1240 const char *tmp2 = NULL;
1243 if (!cfg || !catg) {
1247 if (!(*query =
ast_calloc(1,
sizeof(**query)))) {
1251 if (((tmp = ast_variable_retrieve(cfg, catg,
"writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg,
"dsn")))) {
1257 for (i = 0; i < 5; i++) {
1258 if (!ast_strlen_zero(writeconf.dsn[i]))
1259 ast_copy_string((*query)->writehandle[i], writeconf.dsn[i],
sizeof((*query)->writehandle[i]));
1263 if ((tmp = ast_variable_retrieve(cfg, catg,
"readhandle"))) {
1269 for (i = 0; i < 5; i++) {
1270 if (!ast_strlen_zero(readconf.dsn[i]))
1271 ast_copy_string((*query)->readhandle[i], readconf.dsn[i],
sizeof((*query)->readhandle[i]));
1275 for (i = 0; i < 5; i++) {
1276 if (!ast_strlen_zero((*query)->writehandle[i]))
1277 ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i],
sizeof((*query)->readhandle[i]));
1281 if ((tmp = ast_variable_retrieve(cfg, catg,
"readsql")) ||
1282 (tmp2 = ast_variable_retrieve(cfg, catg,
"read"))) {
1284 ast_log(LOG_WARNING,
"Parameter 'read' is deprecated for category %s. Please use 'readsql' instead.\n", catg);
1288 if (!((*query)->sql_read =
ast_strdup(tmp))) {
1289 free_acf_query(*query);
1296 if ((*query)->sql_read && ast_strlen_zero((*query)->readhandle[0])) {
1297 free_acf_query(*query);
1299 ast_log(LOG_ERROR,
"There is SQL, but no ODBC class to be used for reading: %s\n", catg);
1303 if ((tmp = ast_variable_retrieve(cfg, catg,
"writesql")) ||
1304 (tmp2 = ast_variable_retrieve(cfg, catg,
"write"))) {
1306 ast_log(LOG_WARNING,
"Parameter 'write' is deprecated for category %s. Please use 'writesql' instead.\n", catg);
1310 if (!((*query)->sql_write =
ast_strdup(tmp))) {
1311 free_acf_query(*query);
1318 if ((*query)->sql_write && ast_strlen_zero((*query)->writehandle[0])) {
1319 free_acf_query(*query);
1321 ast_log(LOG_ERROR,
"There is SQL, but no ODBC class to be used for writing: %s\n", catg);
1325 if ((tmp = ast_variable_retrieve(cfg, catg,
"insertsql"))) {
1327 if (!((*query)->sql_insert =
ast_strdup(tmp))) {
1328 free_acf_query(*query);
1336 ast_set_flag((*query), OPT_ESCAPECOMMAS);
1337 if ((tmp = ast_variable_retrieve(cfg, catg,
"escapecommas"))) {
1339 ast_clear_flag((*query), OPT_ESCAPECOMMAS);
1342 if ((tmp = ast_variable_retrieve(cfg, catg,
"mode"))) {
1343 if (strcasecmp(tmp,
"multirow") == 0)
1344 ast_set_flag((*query), OPT_MULTIROW);
1345 if ((tmp = ast_variable_retrieve(cfg, catg,
"rowlimit")))
1346 sscanf(tmp,
"%30d", &((*query)->rowlimit));
1349 if ((tmp = ast_variable_retrieve(cfg, catg,
"minargs"))) {
1350 sscanf(tmp,
"%30d", &((*query)->minargs));
1354 if (!(*query)->acf) {
1355 free_acf_query(*query);
1360 free_acf_query(*query);
1365 if ((tmp = ast_variable_retrieve(cfg, catg,
"prefix")) && !ast_strlen_zero(tmp)) {
1366 if (
ast_asprintf((
char **)&((*query)->acf->name),
"%s_%s", tmp, catg) < 0) {
1367 (*query)->acf->name = NULL;
1370 if (
ast_asprintf((
char **)&((*query)->acf->name),
"ODBC_%s", catg) < 0) {
1371 (*query)->acf->name = NULL;
1375 if (!(*query)->acf->name) {
1376 free_acf_query(*query);
1381 if ((tmp = ast_variable_retrieve(cfg, catg,
"syntax")) && !ast_strlen_zero(tmp)) {
1387 if (ast_strlen_zero((*query)->acf->syntax)) {
1388 free_acf_query(*query);
1393 if ((tmp = ast_variable_retrieve(cfg, catg,
"synopsis")) && !ast_strlen_zero(tmp)) {
1396 ast_string_field_set((*query)->acf, synopsis,
"Runs the referenced query with the specified arguments");
1399 if (ast_strlen_zero((*query)->acf->synopsis)) {
1400 free_acf_query(*query);
1405 if ((*query)->sql_read && (*query)->sql_write) {
1407 "Runs the following query, as defined in func_odbc.conf, performing\n"
1408 "substitution of the arguments into the query as specified by ${ARG1},\n"
1409 "${ARG2}, ... ${ARGn}. When setting the function, the values are provided\n"
1410 "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
1412 "\nRead:\n%s\n\nWrite:\n%s%s%s",
1413 (*query)->sql_insert ?
1414 "If the write query affects no rows, the insert query will be\n"
1415 "performed.\n" :
"",
1417 (*query)->sql_write,
1418 (*query)->sql_insert ?
"\n\nInsert:\n" :
"",
1419 (*query)->sql_insert ? (*query)->sql_insert :
"");
1420 }
else if ((*query)->sql_read) {
1422 "Runs the following query, as defined in func_odbc.conf, performing\n"
1423 "substitution of the arguments into the query as specified by ${ARG1},\n"
1424 "${ARG2}, ... ${ARGn}. This function may only be read, not set.\n\nSQL:\n%s",
1425 (*query)->sql_read);
1426 }
else if ((*query)->sql_write) {
1428 "Runs the following query, as defined in func_odbc.conf, performing\n"
1429 "substitution of the arguments into the query as specified by ${ARG1},\n"
1430 "${ARG2}, ... ${ARGn}. The values are provided either in whole as\n"
1431 "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
1432 "This function may only be set.\n%s\nSQL:\n%s%s%s",
1433 (*query)->sql_insert ?
1434 "If the write query affects no rows, the insert query will be\n"
1435 "performed.\n" :
"",
1436 (*query)->sql_write,
1437 (*query)->sql_insert ?
"\n\nInsert:\n" :
"",
1438 (*query)->sql_insert ? (*query)->sql_insert :
"");
1440 free_acf_query(*query);
1442 ast_log(LOG_WARNING,
"Section '%s' was found, but there was no SQL to execute. Ignoring.\n", catg);
1446 if (ast_strlen_zero((*query)->acf->desc)) {
1447 free_acf_query(*query);
1452 if ((*query)->sql_read) {
1453 (*query)->acf->read = acf_odbc_read;
1456 if ((*query)->sql_write) {
1469 char *char_args, varname[15];
1478 "Usage: odbc read <name> <args> [exec]\n"
1479 " Evaluates the SQL provided in the ODBC function <name>, and\n"
1480 " optionally executes the function. This function is intended for\n"
1481 " testing purposes. Remember to quote arguments containing spaces.\n";
1485 int wordlen = strlen(a->word), which = 0;
1488 AST_RWLIST_TRAVERSE(&
queries, query, list) {
1489 if (!strncasecmp(query->acf->name, a->word, wordlen)) {
1490 if (++which > a->n) {
1499 }
else if (a->pos == 4) {
1500 static const char *
const completions[] = {
"exec", NULL };
1507 if (a->argc < 4 || a->argc > 5) {
1508 return CLI_SHOWUSAGE;
1517 AST_RWLIST_TRAVERSE(&
queries, query, list) {
1518 if (!strcmp(query->acf->name, a->argv[2])) {
1524 ast_cli(a->fd,
"No such query '%s'\n", a->argv[2]);
1526 return CLI_SHOWUSAGE;
1529 if (!query->sql_read) {
1530 ast_cli(a->fd,
"The function %s has no readsql parameter.\n", a->argv[2]);
1535 ast_str_make_space(&sql, strlen(query->sql_read) * 2 + 300);
1547 for (i = 0; i < args.argc; i++) {
1548 snprintf(varname,
sizeof(varname),
"ARG%d", i + 1);
1555 if (a->argc == 5 && !strcmp(a->argv[4],
"exec")) {
1558 struct dsn *dsn = NULL;
1559 int dsn_num, executed = 0;
1561 int rows = 0, res, x;
1562 SQLSMALLINT colcount = 0, collength;
1563 SQLLEN indicator, octetlength;
1572 for (dsn_num = 0; dsn_num < 5; dsn_num++) {
1573 if (ast_strlen_zero(query->readhandle[dsn_num])) {
1580 ast_debug(1,
"Found handle %s\n", query->readhandle[dsn_num]);
1589 res = SQLNumResultCols(stmt, &colcount);
1590 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1591 ast_cli(a->fd,
"SQL Column Count error!\n[%s]\n\n",
ast_str_buffer(sql));
1592 SQLCloseCursor(stmt);
1593 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1599 if (colcount <= 0) {
1600 SQLCloseCursor(stmt);
1601 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1603 ast_cli(a->fd,
"Returned %d columns. Query executed on handle %d:%s [%s]\n", colcount, dsn_num, query->readhandle[dsn_num],
ast_str_buffer(sql));
1608 res = SQLFetch(stmt);
1609 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1610 SQLCloseCursor(stmt);
1611 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1613 if (res == SQL_NO_DATA) {
1614 ast_cli(a->fd,
"Returned %d rows. Query executed on handle %d:%s [%s]\n", rows, dsn_num, query->readhandle[dsn_num],
ast_str_buffer(sql));
1617 ast_cli(a->fd,
"Error %d in FETCH [%s]\n", res,
ast_str_buffer(sql));
1623 for (x = 0; x < colcount; x++) {
1624 res = SQLDescribeCol(stmt, x + 1, (
unsigned char *)colname,
sizeof(colname), &collength, NULL, NULL, NULL, NULL);
1625 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
1626 snprintf(colname,
sizeof(colname),
"field%d", x);
1630 SQLColAttribute(stmt, x + 1, SQL_DESC_OCTET_LENGTH, NULL, 0, NULL, &octetlength);
1633 if (indicator == SQL_NULL_DATA) {
1638 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1639 ast_cli(a->fd,
"SQL Get Data error %d!\n[%s]\n\n", res,
ast_str_buffer(sql));
1640 SQLCloseCursor(stmt);
1641 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1647 ast_cli(a->fd,
"%-20.20s %s\n", colname,
ast_str_buffer(coldata));
1652 res = SQLFetch(stmt);
1653 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1656 ast_cli(a->fd,
"%-20.20s %s\n",
"----------",
"----------");
1658 SQLCloseCursor(stmt);
1659 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1661 ast_cli(a->fd,
"Returned %d row%s. Query executed on handle %d [%s]\n", rows, rows == 1 ?
"" :
"s", dsn_num, query->readhandle[dsn_num]);
1667 ast_cli(a->fd,
"Failed to execute query. [%s]\n",
ast_str_buffer(sql));
1685 char *char_args, *char_values, varname[15];
1694 "Usage: odbc write <name> <args> <value> [exec]\n"
1695 " Evaluates the SQL provided in the ODBC function <name>, and\n"
1696 " optionally executes the function. This function is intended for\n"
1697 " testing purposes. Remember to quote arguments containing spaces.\n";
1701 int wordlen = strlen(a->word), which = 0;
1704 AST_RWLIST_TRAVERSE(&
queries, query, list) {
1705 if (!strncasecmp(query->acf->name, a->word, wordlen)) {
1706 if (++which > a->n) {
1715 }
else if (a->pos == 5) {
1716 static const char *
const completions[] = {
"exec", NULL };
1723 if (a->argc < 5 || a->argc > 6) {
1724 return CLI_SHOWUSAGE;
1733 AST_RWLIST_TRAVERSE(&
queries, query, list) {
1734 if (!strcmp(query->acf->name, a->argv[2])) {
1740 ast_cli(a->fd,
"No such query '%s'\n", a->argv[2]);
1742 return CLI_SHOWUSAGE;
1745 if (!query->sql_write) {
1746 ast_cli(a->fd,
"The function %s has no writesql parameter.\n", a->argv[2]);
1754 ast_str_make_space(&sql, strlen(query->sql_write) * 2 + 300);
1767 for (i = 0; i < args.argc; i++) {
1768 snprintf(varname,
sizeof(varname),
"ARG%d", i + 1);
1774 for (i = 0; i <
values.argc; i++) {
1775 snprintf(varname,
sizeof(varname),
"VAL%d", i + 1);
1786 if (a->argc == 6 && !strcmp(a->argv[5],
"exec")) {
1789 struct dsn *dsn = NULL;
1790 int dsn_num, executed = 0;
1794 for (dsn_num = 0; dsn_num < 5; dsn_num++) {
1795 if (ast_strlen_zero(query->writehandle[dsn_num])) {
1807 SQLRowCount(stmt, &rows);
1808 SQLCloseCursor(stmt);
1809 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1811 ast_cli(a->fd,
"Affected %d rows. Query executed on handle %d [%s]\n", (
int)rows, dsn_num, query->writehandle[dsn_num]);
1817 ast_cli(a->fd,
"Failed to execute query.\n");
1827 AST_CLI_DEFINE(cli_odbc_write,
"Test setting a func_odbc function"),
1828 AST_CLI_DEFINE(cli_odbc_read,
"Test reading a func_odbc function"),
1831 static int load_module(
void)
1843 if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
1844 ast_log(LOG_NOTICE,
"Unable to load config for func_odbc: %s\n", config);
1848 ast_rwlock_wrlock(&single_db_connection_lock);
1849 if ((s = ast_variable_retrieve(cfg,
"general",
"single_db_connection"))) {
1850 single_db_connection =
ast_true(s);
1852 single_db_connection = DEFAULT_SINGLE_DB_CONNECTION;
1857 if (single_db_connection) {
1859 dsn_hash, NULL, dsn_cmp);
1861 ast_log(LOG_ERROR,
"Could not initialize DSN container\n");
1862 ast_rwlock_unlock(&single_db_connection_lock);
1866 ast_rwlock_unlock(&single_db_connection_lock);
1875 if (!strcasecmp(catg,
"general")) {
1879 if ((err = init_acf_query(cfg, catg, &query))) {
1881 ast_log(LOG_ERROR,
"Out of memory\n");
1882 else if (err == EINVAL)
1883 ast_log(LOG_ERROR,
"Invalid parameters for category %s\n", catg);
1885 ast_log(LOG_ERROR,
"%s (%d)\n", strerror(err), err);
1887 AST_RWLIST_INSERT_HEAD(&
queries, query, list);
1901 static int unload_module(
void)
1907 while (!AST_RWLIST_EMPTY(&
queries)) {
1908 query = AST_RWLIST_REMOVE_HEAD(&
queries, list);
1910 free_acf_query(query);
1932 static int reload(
void)
1942 if (cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID)
1945 ast_rwlock_wrlock(&single_db_connection_lock);
1952 if (cfg && (s = ast_variable_retrieve(cfg,
"general",
"single_db_connection"))) {
1953 single_db_connection =
ast_true(s);
1955 single_db_connection = DEFAULT_SINGLE_DB_CONNECTION;
1958 if (single_db_connection) {
1960 dsn_hash, NULL, dsn_cmp);
1962 ast_log(LOG_ERROR,
"Could not initialize DSN container\n");
1963 ast_rwlock_unlock(&single_db_connection_lock);
1967 ast_rwlock_unlock(&single_db_connection_lock);
1971 while (!AST_RWLIST_EMPTY(&
queries)) {
1972 oldquery = AST_RWLIST_REMOVE_HEAD(&
queries, list);
1974 free_acf_query(oldquery);
1978 ast_log(LOG_WARNING,
"Unable to load config for func_odbc: %s\n", config);
1987 if (!strcasecmp(catg,
"general")) {
1991 if (init_acf_query(cfg, catg, &query)) {
1992 ast_log(LOG_ERROR,
"Cannot initialize query %s\n", catg);
1994 AST_RWLIST_INSERT_HEAD(&
queries, query, list);
2008 .support_level = AST_MODULE_SUPPORT_CORE,
2009 .load = load_module,
2010 .unload = unload_module,
2012 .requires =
"res_odbc",
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Main Channel structure associated with a channel.
#define AST_LIST_LOCK(head)
Locks a list.
Asterisk main include file. File version handling, generic pbx functions.
#define ast_realloc(p, len)
A wrapper for realloc()
#define AST_LIST_HEAD(name, type)
Defines a structure to be used to hold a list of specified type.
static SQLHSTMT execute(struct odbc_obj *obj, void *data, int silent)
Common execution function for SQL queries.
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
String manipulation functions.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
#define ast_channel_unref(c)
Decrease channel reference count.
The arg parameter is a search key, but is not an object.
#define ast_odbc_request_obj(name, check)
Get a ODBC connection object.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
descriptor for a cli entry.
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
struct odbc_obj * connection
Assume that the ao2_container is already locked.
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Structure for a data store type.
char * ast_str_append_escapecommas(struct ast_str **buf, ssize_t maxlen, const char *src, size_t maxsrc)
Append a non-NULL terminated substring to the end of a dynamic string, with escaping of commas...
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
#define ast_cli_register_multiple(e, len)
Register multiple commands.
#define ao2_link_flags(container, obj, flags)
Add an object to a container.
#define ast_strdup(str)
A wrapper for strdup()
Structure for a data store object.
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
void pbx_builtin_pushvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, without removing any previously set value...
int ast_unregister_application(const char *app)
Unregister an application.
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
#define AST_LIST_HEAD_DESTROY(head)
Destroys a list head structure.
char * ast_cli_complete(const char *word, const char *const choices[], int pos)
#define AST_RWLIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a read/write list of specified type, statically initialized...
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Configuration File Parser.
unsigned int ast_odbc_get_max_connections(const char *name)
Return the current configured maximum number of connections for a class.
The arg parameter is a partial search key similar to OBJ_SEARCH_KEY.
#define ast_config_load(filename, flags)
Load a config file.
General Asterisk PBX channel definitions.
static int acf_odbc_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
#define ast_strdupa(s)
duplicate a string in memory from the stack
#define ast_dummy_channel_alloc()
Create a fake channel structure.
Data structure associated with a custom dialplan function.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
#define ast_debug(level,...)
Log a DEBUG message.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Core PBX routines and definitions.
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
static int connection_dead(struct odbc_obj *connection)
Determine if the connection has died.
SQLHSTMT ast_odbc_direct_execute(struct odbc_obj *obj, SQLHSTMT(*exec_cb)(struct odbc_obj *obj, void *data), void *data)
Executes an non prepared statement and returns the resulting statement handle.
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Support for dynamic strings.
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
#define AST_LIST_HEAD_INIT(head)
Initializes a list head structure.
#define ast_calloc(num, len)
A wrapper for calloc()
Module has failed to load, may be in an inconsistent state.
#define ast_string_field_build(x, field, fmt, args...)
Set a field to a complex (built) value.
Structure used to handle boolean flags.
static struct dsn * create_dsn(const char *name)
Create a DSN and connect to the database.
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
SQLRETURN ast_odbc_execute_sql(struct odbc_obj *obj, SQLHSTMT *stmt, const char *sql)
Execute a unprepared SQL query.
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
The arg parameter is an object of the same type.
Standard Command Line Interface.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"...
static struct dsn * get_dsn(const char *name)
Retrieve a DSN, or create it if it does not exist.
static void release_obj_or_dsn(struct odbc_obj **obj, struct dsn **dsn)
Release an ODBC obj or a DSN.
SQLRETURN ast_odbc_ast_str_SQLGetData(struct ast_str **buf, int pmaxlen, SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, SQLLEN *StrLen_or_Ind)
Wrapper for SQLGetData to use with dynamic strings.
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Search option field mask.
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
void ast_odbc_release_obj(struct odbc_obj *obj)
Releases an ODBC object previously allocated by ast_odbc_request_obj()
#define ASTERISK_GPL_KEY
The text the key() function should return.
Asterisk module definitions.
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
#define ast_custom_function_register(acf)
Register a custom function.
static struct odbc_obj * get_odbc_obj(const char *dsn_name, struct dsn **dsn)
Get a DB handle via a DSN or directly.
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
#define AST_APP_ARG(name)
Define an application argument.
static force_inline int attribute_pure ast_str_hash(const char *str)
Compute a hash value on a string.
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.