res_config_odbc.c 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 1999 - 2010, Digium, Inc.
  5. *
  6. * Mark Spencer <markster@digium.com>
  7. *
  8. * Copyright (C) 2004 - 2005 Anthony Minessale II <anthmct@yahoo.com>
  9. *
  10. * See http://www.asterisk.org for more information about
  11. * the Asterisk project. Please do not directly contact
  12. * any of the maintainers of this project for assistance;
  13. * the project provides a web site, mailing lists and IRC
  14. * channels for your use.
  15. *
  16. * This program is free software, distributed under the terms of
  17. * the GNU General Public License Version 2. See the LICENSE file
  18. * at the top of the source tree.
  19. */
  20. /*! \file
  21. *
  22. * \brief odbc+odbc plugin for portable configuration engine
  23. *
  24. * \author Mark Spencer <markster@digium.com>
  25. * \author Anthony Minessale II <anthmct@yahoo.com>
  26. *
  27. * \arg http://www.unixodbc.org
  28. */
  29. /*** MODULEINFO
  30. <depend>res_odbc</depend>
  31. <support_level>core</support_level>
  32. ***/
  33. #include "asterisk.h"
  34. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  35. #include "asterisk/file.h"
  36. #include "asterisk/channel.h"
  37. #include "asterisk/pbx.h"
  38. #include "asterisk/config.h"
  39. #include "asterisk/module.h"
  40. #include "asterisk/lock.h"
  41. #include "asterisk/res_odbc.h"
  42. #include "asterisk/utils.h"
  43. #include "asterisk/stringfields.h"
  44. AST_THREADSTORAGE(sql_buf);
  45. struct custom_prepare_struct {
  46. const char *sql;
  47. const char *extra;
  48. AST_DECLARE_STRING_FIELDS(
  49. AST_STRING_FIELD(encoding)[256];
  50. );
  51. va_list ap;
  52. unsigned long long skip;
  53. };
  54. static void decode_chunk(char *chunk)
  55. {
  56. for (; *chunk; chunk++) {
  57. if (*chunk == '^' && strchr("0123456789ABCDEF", chunk[1]) && strchr("0123456789ABCDEF", chunk[2])) {
  58. sscanf(chunk + 1, "%02hhX", chunk);
  59. memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
  60. }
  61. }
  62. }
  63. static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data)
  64. {
  65. int res, x = 1, count = 0;
  66. struct custom_prepare_struct *cps = data;
  67. const char *newparam, *newval;
  68. char encodebuf[1024];
  69. SQLHSTMT stmt;
  70. va_list ap;
  71. res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
  72. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  73. ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
  74. return NULL;
  75. }
  76. ast_debug(1, "Skip: %lld; SQL: %s\n", cps->skip, cps->sql);
  77. res = SQLPrepare(stmt, (unsigned char *)cps->sql, SQL_NTS);
  78. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  79. ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", cps->sql);
  80. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  81. return NULL;
  82. }
  83. va_copy(ap, cps->ap);
  84. while ((newparam = va_arg(ap, const char *))) {
  85. newval = va_arg(ap, const char *);
  86. if ((1LL << count++) & cps->skip) {
  87. ast_debug(1, "Skipping field '%s'='%s' (%llo/%llo)\n", newparam, newval, 1LL << (count - 1), cps->skip);
  88. continue;
  89. }
  90. ast_debug(1, "Parameter %d ('%s') = '%s'\n", x, newparam, newval);
  91. if (strchr(newval, ';') || strchr(newval, '^')) {
  92. char *eptr = encodebuf;
  93. const char *vptr = newval;
  94. for (; *vptr && eptr < encodebuf + sizeof(encodebuf); vptr++) {
  95. if (strchr("^;", *vptr)) {
  96. /* We use ^XX, instead of %XX because '%' is a special character in SQL */
  97. snprintf(eptr, encodebuf + sizeof(encodebuf) - eptr, "^%02hhX", *vptr);
  98. eptr += 3;
  99. } else {
  100. *eptr++ = *vptr;
  101. }
  102. }
  103. if (eptr < encodebuf + sizeof(encodebuf)) {
  104. *eptr = '\0';
  105. } else {
  106. encodebuf[sizeof(encodebuf) - 1] = '\0';
  107. }
  108. ast_string_field_set(cps, encoding[x], encodebuf);
  109. newval = cps->encoding[x];
  110. }
  111. SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
  112. }
  113. va_end(ap);
  114. if (!ast_strlen_zero(cps->extra))
  115. SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(cps->extra), 0, (void *)cps->extra, 0, NULL);
  116. return stmt;
  117. }
  118. /*!
  119. * \brief Excute an SQL query and return ast_variable list
  120. * \param database
  121. * \param table
  122. * \param ap list containing one or more field/operator/value set.
  123. *
  124. * Select database and preform query on table, prepare the sql statement
  125. * Sub-in the values to the prepared statement and execute it. Return results
  126. * as a ast_variable list.
  127. *
  128. * \retval var on success
  129. * \retval NULL on failure
  130. */
  131. static struct ast_variable *realtime_odbc(const char *database, const char *table, va_list ap)
  132. {
  133. struct odbc_obj *obj;
  134. SQLHSTMT stmt;
  135. char sql[1024];
  136. char coltitle[256];
  137. char rowdata[2048];
  138. char *op;
  139. const char *newparam;
  140. char *stringp;
  141. char *chunk;
  142. SQLSMALLINT collen;
  143. int res;
  144. int x;
  145. struct ast_variable *var=NULL, *prev=NULL;
  146. SQLULEN colsize;
  147. SQLSMALLINT colcount=0;
  148. SQLSMALLINT datatype;
  149. SQLSMALLINT decimaldigits;
  150. SQLSMALLINT nullable;
  151. SQLLEN indicator;
  152. va_list aq;
  153. struct custom_prepare_struct cps = { .sql = sql };
  154. struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
  155. if (ast_string_field_init(&cps, 256)) {
  156. return NULL;
  157. }
  158. if (!table) {
  159. ast_string_field_free_memory(&cps);
  160. return NULL;
  161. }
  162. obj = ast_odbc_request_obj2(database, connected_flag);
  163. if (!obj) {
  164. ast_log(LOG_ERROR, "No database handle available with the name of '%s' (check res_odbc.conf)\n", database);
  165. ast_string_field_free_memory(&cps);
  166. return NULL;
  167. }
  168. va_copy(aq, ap);
  169. newparam = va_arg(aq, const char *);
  170. if (!newparam) {
  171. va_end(aq);
  172. ast_odbc_release_obj(obj);
  173. ast_string_field_free_memory(&cps);
  174. return NULL;
  175. }
  176. va_arg(aq, const char *);
  177. op = !strchr(newparam, ' ') ? " =" : "";
  178. snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, newparam, op,
  179. strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
  180. while((newparam = va_arg(aq, const char *))) {
  181. op = !strchr(newparam, ' ') ? " =" : "";
  182. snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", newparam, op,
  183. strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
  184. va_arg(aq, const char *);
  185. }
  186. va_end(aq);
  187. va_copy(cps.ap, ap);
  188. stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
  189. va_end(cps.ap);
  190. if (!stmt) {
  191. ast_odbc_release_obj(obj);
  192. ast_string_field_free_memory(&cps);
  193. return NULL;
  194. }
  195. res = SQLNumResultCols(stmt, &colcount);
  196. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  197. ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
  198. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  199. ast_odbc_release_obj(obj);
  200. ast_string_field_free_memory(&cps);
  201. return NULL;
  202. }
  203. res = SQLFetch(stmt);
  204. if (res == SQL_NO_DATA) {
  205. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  206. ast_odbc_release_obj(obj);
  207. ast_string_field_free_memory(&cps);
  208. return NULL;
  209. }
  210. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  211. ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
  212. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  213. ast_odbc_release_obj(obj);
  214. ast_string_field_free_memory(&cps);
  215. return NULL;
  216. }
  217. for (x = 0; x < colcount; x++) {
  218. rowdata[0] = '\0';
  219. colsize = 0;
  220. collen = sizeof(coltitle);
  221. res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
  222. &datatype, &colsize, &decimaldigits, &nullable);
  223. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  224. ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
  225. if (var)
  226. ast_variables_destroy(var);
  227. ast_odbc_release_obj(obj);
  228. ast_string_field_free_memory(&cps);
  229. return NULL;
  230. }
  231. indicator = 0;
  232. res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
  233. if (indicator == SQL_NULL_DATA)
  234. rowdata[0] = '\0';
  235. else if (ast_strlen_zero(rowdata)) {
  236. /* Because we encode the empty string for a NULL, we will encode
  237. * actual empty strings as a string containing a single whitespace. */
  238. ast_copy_string(rowdata, " ", sizeof(rowdata));
  239. }
  240. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  241. ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
  242. if (var)
  243. ast_variables_destroy(var);
  244. ast_odbc_release_obj(obj);
  245. return NULL;
  246. }
  247. stringp = rowdata;
  248. while (stringp) {
  249. chunk = strsep(&stringp, ";");
  250. if (!ast_strlen_zero(ast_strip(chunk))) {
  251. if (strchr(chunk, '^')) {
  252. decode_chunk(chunk);
  253. }
  254. if (prev) {
  255. prev->next = ast_variable_new(coltitle, chunk, "");
  256. if (prev->next) {
  257. prev = prev->next;
  258. }
  259. } else {
  260. prev = var = ast_variable_new(coltitle, chunk, "");
  261. }
  262. }
  263. }
  264. }
  265. SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  266. ast_odbc_release_obj(obj);
  267. ast_string_field_free_memory(&cps);
  268. return var;
  269. }
  270. /*!
  271. * \brief Excute an Select query and return ast_config list
  272. * \param database
  273. * \param table
  274. * \param ap list containing one or more field/operator/value set.
  275. *
  276. * Select database and preform query on table, prepare the sql statement
  277. * Sub-in the values to the prepared statement and execute it.
  278. * Execute this prepared query against several ODBC connected databases.
  279. * Return results as an ast_config variable.
  280. *
  281. * \retval var on success
  282. * \retval NULL on failure
  283. */
  284. static struct ast_config *realtime_multi_odbc(const char *database, const char *table, va_list ap)
  285. {
  286. struct odbc_obj *obj;
  287. SQLHSTMT stmt;
  288. char sql[1024];
  289. char coltitle[256];
  290. char rowdata[2048];
  291. const char *initfield;
  292. char *op;
  293. const char *newparam;
  294. char *stringp;
  295. char *chunk;
  296. SQLSMALLINT collen;
  297. int res;
  298. int x;
  299. struct ast_variable *var=NULL;
  300. struct ast_config *cfg=NULL;
  301. struct ast_category *cat=NULL;
  302. struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
  303. SQLULEN colsize;
  304. SQLSMALLINT colcount=0;
  305. SQLSMALLINT datatype;
  306. SQLSMALLINT decimaldigits;
  307. SQLSMALLINT nullable;
  308. SQLLEN indicator;
  309. struct custom_prepare_struct cps = { .sql = sql };
  310. va_list aq;
  311. if (!table || ast_string_field_init(&cps, 256)) {
  312. return NULL;
  313. }
  314. obj = ast_odbc_request_obj2(database, connected_flag);
  315. if (!obj) {
  316. ast_string_field_free_memory(&cps);
  317. return NULL;
  318. }
  319. va_copy(aq, ap);
  320. newparam = va_arg(aq, const char *);
  321. if (!newparam) {
  322. va_end(aq);
  323. ast_odbc_release_obj(obj);
  324. ast_string_field_free_memory(&cps);
  325. return NULL;
  326. }
  327. initfield = ast_strdupa(newparam);
  328. if ((op = strchr(initfield, ' '))) {
  329. *op = '\0';
  330. }
  331. va_arg(aq, const char *);
  332. op = !strchr(newparam, ' ') ? " =" : "";
  333. snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, newparam, op,
  334. strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
  335. while((newparam = va_arg(aq, const char *))) {
  336. op = !strchr(newparam, ' ') ? " =" : "";
  337. snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", newparam, op,
  338. strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
  339. va_arg(aq, const char *);
  340. }
  341. va_end(aq);
  342. snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
  343. va_copy(cps.ap, ap);
  344. stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
  345. va_end(cps.ap);
  346. if (!stmt) {
  347. ast_odbc_release_obj(obj);
  348. ast_string_field_free_memory(&cps);
  349. return NULL;
  350. }
  351. res = SQLNumResultCols(stmt, &colcount);
  352. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  353. ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
  354. SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  355. ast_odbc_release_obj(obj);
  356. ast_string_field_free_memory(&cps);
  357. return NULL;
  358. }
  359. cfg = ast_config_new();
  360. if (!cfg) {
  361. ast_log(LOG_WARNING, "Out of memory!\n");
  362. SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  363. ast_odbc_release_obj(obj);
  364. ast_string_field_free_memory(&cps);
  365. return NULL;
  366. }
  367. while ((res=SQLFetch(stmt)) != SQL_NO_DATA) {
  368. var = NULL;
  369. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  370. ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
  371. continue;
  372. }
  373. cat = ast_category_new("","",99999);
  374. if (!cat) {
  375. ast_log(LOG_WARNING, "Out of memory!\n");
  376. continue;
  377. }
  378. for (x=0;x<colcount;x++) {
  379. rowdata[0] = '\0';
  380. colsize = 0;
  381. collen = sizeof(coltitle);
  382. res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
  383. &datatype, &colsize, &decimaldigits, &nullable);
  384. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  385. ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
  386. ast_category_destroy(cat);
  387. goto next_sql_fetch;
  388. }
  389. indicator = 0;
  390. res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
  391. if (indicator == SQL_NULL_DATA)
  392. continue;
  393. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  394. ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
  395. ast_category_destroy(cat);
  396. goto next_sql_fetch;
  397. }
  398. stringp = rowdata;
  399. while (stringp) {
  400. chunk = strsep(&stringp, ";");
  401. if (!ast_strlen_zero(ast_strip(chunk))) {
  402. if (strchr(chunk, '^')) {
  403. decode_chunk(chunk);
  404. }
  405. if (!strcmp(initfield, coltitle)) {
  406. ast_category_rename(cat, chunk);
  407. }
  408. var = ast_variable_new(coltitle, chunk, "");
  409. ast_variable_append(cat, var);
  410. }
  411. }
  412. }
  413. ast_category_append(cfg, cat);
  414. next_sql_fetch:;
  415. }
  416. SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  417. ast_odbc_release_obj(obj);
  418. ast_string_field_free_memory(&cps);
  419. return cfg;
  420. }
  421. /*!
  422. * \brief Excute an UPDATE query
  423. * \param database
  424. * \param table
  425. * \param keyfield where clause field
  426. * \param lookup value of field for where clause
  427. * \param ap list containing one or more field/value set(s).
  428. *
  429. * Update a database table, prepare the sql statement using keyfield and lookup
  430. * control the number of records to change. All values to be changed are stored in ap list.
  431. * Sub-in the values to the prepared statement and execute it.
  432. *
  433. * \retval number of rows affected
  434. * \retval -1 on failure
  435. */
  436. static int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
  437. {
  438. struct odbc_obj *obj;
  439. SQLHSTMT stmt;
  440. char sql[256];
  441. SQLLEN rowcount=0;
  442. const char *newparam;
  443. int res, count = 1;
  444. va_list aq;
  445. struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
  446. struct odbc_cache_tables *tableptr;
  447. struct odbc_cache_columns *column = NULL;
  448. struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
  449. if (!table) {
  450. return -1;
  451. }
  452. if (ast_string_field_init(&cps, 256)) {
  453. return -1;
  454. }
  455. tableptr = ast_odbc_find_table(database, table);
  456. if (!(obj = ast_odbc_request_obj2(database, connected_flag))) {
  457. ast_odbc_release_table(tableptr);
  458. ast_string_field_free_memory(&cps);
  459. return -1;
  460. }
  461. va_copy(aq, ap);
  462. newparam = va_arg(aq, const char *);
  463. if (!newparam) {
  464. va_end(aq);
  465. ast_odbc_release_obj(obj);
  466. ast_odbc_release_table(tableptr);
  467. ast_string_field_free_memory(&cps);
  468. return -1;
  469. }
  470. va_arg(aq, const char *);
  471. if (tableptr && !ast_odbc_find_column(tableptr, newparam)) {
  472. ast_log(LOG_WARNING, "Key field '%s' does not exist in table '%s@%s'. Update will fail\n", newparam, table, database);
  473. }
  474. snprintf(sql, sizeof(sql), "UPDATE %s SET %s=?", table, newparam);
  475. while((newparam = va_arg(aq, const char *))) {
  476. va_arg(aq, const char *);
  477. if ((tableptr && (column = ast_odbc_find_column(tableptr, newparam))) || count > 63) {
  478. /* NULL test for integer-based columns */
  479. if (ast_strlen_zero(newparam) && tableptr && column && column->nullable && count < 64 &&
  480. (column->type == SQL_INTEGER || column->type == SQL_BIGINT ||
  481. column->type == SQL_SMALLINT || column->type == SQL_TINYINT ||
  482. column->type == SQL_NUMERIC || column->type == SQL_DECIMAL)) {
  483. snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s=NULL", newparam);
  484. cps.skip |= (1LL << count);
  485. } else {
  486. snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s=?", newparam);
  487. }
  488. } else { /* the column does not exist in the table */
  489. cps.skip |= (1LL << count);
  490. }
  491. count++;
  492. }
  493. va_end(aq);
  494. snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield);
  495. ast_odbc_release_table(tableptr);
  496. va_copy(cps.ap, ap);
  497. stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
  498. va_end(cps.ap);
  499. if (!stmt) {
  500. ast_odbc_release_obj(obj);
  501. ast_string_field_free_memory(&cps);
  502. return -1;
  503. }
  504. res = SQLRowCount(stmt, &rowcount);
  505. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  506. ast_odbc_release_obj(obj);
  507. ast_string_field_free_memory(&cps);
  508. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  509. ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
  510. return -1;
  511. }
  512. if (rowcount >= 0) {
  513. return (int) rowcount;
  514. }
  515. return -1;
  516. }
  517. struct update2_prepare_struct {
  518. const char *database;
  519. const char *table;
  520. va_list ap;
  521. };
  522. static SQLHSTMT update2_prepare(struct odbc_obj *obj, void *data)
  523. {
  524. int res, x = 1, first = 1;
  525. struct update2_prepare_struct *ups = data;
  526. const char *newparam, *newval;
  527. struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
  528. SQLHSTMT stmt;
  529. va_list ap;
  530. struct odbc_cache_tables *tableptr = ast_odbc_find_table(ups->database, ups->table);
  531. if (!sql) {
  532. if (tableptr) {
  533. ast_odbc_release_table(tableptr);
  534. }
  535. return NULL;
  536. }
  537. if (!tableptr) {
  538. ast_log(LOG_ERROR, "Could not retrieve metadata for table '%s@%s'. Update will fail!\n", ups->table, ups->database);
  539. return NULL;
  540. }
  541. res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
  542. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  543. ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
  544. ast_odbc_release_table(tableptr);
  545. return NULL;
  546. }
  547. ast_str_set(&sql, 0, "UPDATE %s SET ", ups->table);
  548. /* Start by finding the second set of parameters */
  549. va_copy(ap, ups->ap);
  550. while ((newparam = va_arg(ap, const char *))) {
  551. newval = va_arg(ap, const char *);
  552. }
  553. while ((newparam = va_arg(ap, const char *))) {
  554. newval = va_arg(ap, const char *);
  555. if (ast_odbc_find_column(tableptr, newparam)) {
  556. ast_str_append(&sql, 0, "%s%s=? ", first ? "" : ", ", newparam);
  557. SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
  558. first = 0;
  559. } else {
  560. ast_log(LOG_NOTICE, "Not updating column '%s' in '%s@%s' because that column does not exist!\n", newparam, ups->table, ups->database);
  561. }
  562. }
  563. va_end(ap);
  564. ast_str_append(&sql, 0, "WHERE");
  565. first = 1;
  566. /* Restart search, because we need to add the search parameters */
  567. va_copy(ap, ups->ap);
  568. while ((newparam = va_arg(ap, const char *))) {
  569. newval = va_arg(ap, const char *);
  570. if (!ast_odbc_find_column(tableptr, newparam)) {
  571. va_end(ap);
  572. ast_log(LOG_ERROR, "One or more of the criteria columns '%s' on '%s@%s' for this update does not exist!\n", newparam, ups->table, ups->database);
  573. ast_odbc_release_table(tableptr);
  574. SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  575. return NULL;
  576. }
  577. ast_str_append(&sql, 0, "%s %s=?", first ? "" : " AND", newparam);
  578. SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
  579. first = 0;
  580. }
  581. va_end(ap);
  582. /* Done with the table metadata */
  583. ast_odbc_release_table(tableptr);
  584. res = SQLPrepare(stmt, (unsigned char *)ast_str_buffer(sql), SQL_NTS);
  585. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  586. ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", ast_str_buffer(sql));
  587. SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  588. return NULL;
  589. }
  590. return stmt;
  591. }
  592. /*!
  593. * \brief Execute an UPDATE query
  594. * \param database
  595. * \param table
  596. * \param ap list containing one or more field/value set(s).
  597. *
  598. * Update a database table, preparing the sql statement from a list of
  599. * key/value pairs specified in ap. The lookup pairs are specified first
  600. * and are separated from the update pairs by a sentinel value.
  601. * Sub-in the values to the prepared statement and execute it.
  602. *
  603. * \retval number of rows affected
  604. * \retval -1 on failure
  605. */
  606. static int update2_odbc(const char *database, const char *table, va_list ap)
  607. {
  608. struct odbc_obj *obj;
  609. SQLHSTMT stmt;
  610. struct update2_prepare_struct ups = { .database = database, .table = table, };
  611. struct ast_str *sql;
  612. int res;
  613. SQLLEN rowcount = 0;
  614. if (!(obj = ast_odbc_request_obj(database, 0))) {
  615. return -1;
  616. }
  617. va_copy(ups.ap, ap);
  618. if (!(stmt = ast_odbc_prepare_and_execute(obj, update2_prepare, &ups))) {
  619. va_end(ups.ap);
  620. ast_odbc_release_obj(obj);
  621. return -1;
  622. }
  623. va_end(ups.ap);
  624. res = SQLRowCount(stmt, &rowcount);
  625. SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  626. ast_odbc_release_obj(obj);
  627. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  628. /* Since only a single thread can access this memory, we can retrieve what would otherwise be lost. */
  629. sql = ast_str_thread_get(&sql_buf, 16);
  630. ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n", ast_str_buffer(sql));
  631. return -1;
  632. }
  633. if (rowcount >= 0) {
  634. return (int)rowcount;
  635. }
  636. return -1;
  637. }
  638. /*!
  639. * \brief Excute an INSERT query
  640. * \param database
  641. * \param table
  642. * \param ap list containing one or more field/value set(s)
  643. *
  644. * Insert a new record into database table, prepare the sql statement.
  645. * All values to be changed are stored in ap list.
  646. * Sub-in the values to the prepared statement and execute it.
  647. *
  648. * \retval number of rows affected
  649. * \retval -1 on failure
  650. */
  651. static int store_odbc(const char *database, const char *table, va_list ap)
  652. {
  653. struct odbc_obj *obj;
  654. SQLHSTMT stmt;
  655. char sql[256];
  656. char keys[256];
  657. char vals[256];
  658. SQLLEN rowcount=0;
  659. const char *newparam;
  660. int res;
  661. va_list aq;
  662. struct custom_prepare_struct cps = { .sql = sql, .extra = NULL };
  663. struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
  664. if (!table) {
  665. return -1;
  666. }
  667. obj = ast_odbc_request_obj2(database, connected_flag);
  668. if (!obj) {
  669. return -1;
  670. }
  671. va_copy(aq, ap);
  672. newparam = va_arg(aq, const char *);
  673. if (!newparam) {
  674. va_end(aq);
  675. ast_odbc_release_obj(obj);
  676. return -1;
  677. }
  678. va_arg(aq, const char *);
  679. snprintf(keys, sizeof(keys), "%s", newparam);
  680. ast_copy_string(vals, "?", sizeof(vals));
  681. while ((newparam = va_arg(aq, const char *))) {
  682. snprintf(keys + strlen(keys), sizeof(keys) - strlen(keys), ", %s", newparam);
  683. snprintf(vals + strlen(vals), sizeof(vals) - strlen(vals), ", ?");
  684. va_arg(aq, const char *);
  685. }
  686. va_end(aq);
  687. snprintf(sql, sizeof(sql), "INSERT INTO %s (%s) VALUES (%s)", table, keys, vals);
  688. va_copy(cps.ap, ap);
  689. stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
  690. va_end(cps.ap);
  691. if (!stmt) {
  692. ast_odbc_release_obj(obj);
  693. return -1;
  694. }
  695. res = SQLRowCount(stmt, &rowcount);
  696. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  697. ast_odbc_release_obj(obj);
  698. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  699. ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
  700. return -1;
  701. }
  702. if (rowcount >= 0)
  703. return (int)rowcount;
  704. return -1;
  705. }
  706. /*!
  707. * \brief Excute an DELETE query
  708. * \param database
  709. * \param table
  710. * \param keyfield where clause field
  711. * \param lookup value of field for where clause
  712. * \param ap list containing one or more field/value set(s)
  713. *
  714. * Delete a row from a database table, prepare the sql statement using keyfield and lookup
  715. * control the number of records to change. Additional params to match rows are stored in ap list.
  716. * Sub-in the values to the prepared statement and execute it.
  717. *
  718. * \retval number of rows affected
  719. * \retval -1 on failure
  720. */
  721. static int destroy_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
  722. {
  723. struct odbc_obj *obj;
  724. SQLHSTMT stmt;
  725. char sql[256];
  726. SQLLEN rowcount=0;
  727. const char *newparam;
  728. int res;
  729. va_list aq;
  730. struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
  731. struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
  732. if (!table) {
  733. return -1;
  734. }
  735. obj = ast_odbc_request_obj2(database, connected_flag);
  736. if (!obj) {
  737. return -1;
  738. }
  739. snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE ", table);
  740. va_copy(aq, ap);
  741. while((newparam = va_arg(aq, const char *))) {
  742. snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=? AND ", newparam);
  743. va_arg(aq, const char *);
  744. }
  745. va_end(aq);
  746. snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=?", keyfield);
  747. va_copy(cps.ap, ap);
  748. stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
  749. va_end(cps.ap);
  750. if (!stmt) {
  751. ast_odbc_release_obj(obj);
  752. return -1;
  753. }
  754. res = SQLRowCount(stmt, &rowcount);
  755. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  756. ast_odbc_release_obj(obj);
  757. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  758. ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
  759. return -1;
  760. }
  761. if (rowcount >= 0)
  762. return (int)rowcount;
  763. return -1;
  764. }
  765. struct config_odbc_obj {
  766. char *sql;
  767. unsigned long cat_metric;
  768. char category[128];
  769. char var_name[128];
  770. char var_val[1024]; /* changed from 128 to 1024 via bug 8251 */
  771. SQLLEN err;
  772. };
  773. static SQLHSTMT config_odbc_prepare(struct odbc_obj *obj, void *data)
  774. {
  775. struct config_odbc_obj *q = data;
  776. SQLHSTMT sth;
  777. int res;
  778. res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &sth);
  779. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  780. ast_verb(4, "Failure in AllocStatement %d\n", res);
  781. return NULL;
  782. }
  783. res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS);
  784. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  785. ast_verb(4, "Error in PREPARE %d\n", res);
  786. SQLFreeHandle(SQL_HANDLE_STMT, sth);
  787. return NULL;
  788. }
  789. SQLBindCol(sth, 1, SQL_C_ULONG, &q->cat_metric, sizeof(q->cat_metric), &q->err);
  790. SQLBindCol(sth, 2, SQL_C_CHAR, q->category, sizeof(q->category), &q->err);
  791. SQLBindCol(sth, 3, SQL_C_CHAR, q->var_name, sizeof(q->var_name), &q->err);
  792. SQLBindCol(sth, 4, SQL_C_CHAR, q->var_val, sizeof(q->var_val), &q->err);
  793. return sth;
  794. }
  795. 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)
  796. {
  797. struct ast_variable *new_v;
  798. struct ast_category *cur_cat;
  799. int res = 0;
  800. struct odbc_obj *obj;
  801. char sqlbuf[1024] = "";
  802. char *sql = sqlbuf;
  803. size_t sqlleft = sizeof(sqlbuf);
  804. unsigned int last_cat_metric = 0;
  805. SQLSMALLINT rowcount = 0;
  806. SQLHSTMT stmt;
  807. char last[128] = "";
  808. struct config_odbc_obj q;
  809. struct ast_flags loader_flags = { 0 };
  810. struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
  811. memset(&q, 0, sizeof(q));
  812. if (!file || !strcmp (file, "res_config_odbc.conf"))
  813. return NULL; /* cant configure myself with myself ! */
  814. obj = ast_odbc_request_obj2(database, connected_flag);
  815. if (!obj)
  816. return NULL;
  817. ast_build_string(&sql, &sqlleft, "SELECT cat_metric, category, var_name, var_val FROM %s ", table);
  818. ast_build_string(&sql, &sqlleft, "WHERE filename='%s' AND commented=0 ", file);
  819. ast_build_string(&sql, &sqlleft, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ");
  820. q.sql = sqlbuf;
  821. stmt = ast_odbc_prepare_and_execute(obj, config_odbc_prepare, &q);
  822. if (!stmt) {
  823. ast_log(LOG_WARNING, "SQL select error!\n[%s]\n\n", sql);
  824. ast_odbc_release_obj(obj);
  825. return NULL;
  826. }
  827. res = SQLNumResultCols(stmt, &rowcount);
  828. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  829. ast_log(LOG_WARNING, "SQL NumResultCols error!\n[%s]\n\n", sql);
  830. SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  831. ast_odbc_release_obj(obj);
  832. return NULL;
  833. }
  834. if (!rowcount) {
  835. ast_log(LOG_NOTICE, "found nothing\n");
  836. ast_odbc_release_obj(obj);
  837. return cfg;
  838. }
  839. cur_cat = ast_config_get_current_category(cfg);
  840. while ((res = SQLFetch(stmt)) != SQL_NO_DATA) {
  841. if (!strcmp (q.var_name, "#include")) {
  842. if (!ast_config_internal_load(q.var_val, cfg, loader_flags, "", who_asked)) {
  843. SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  844. ast_odbc_release_obj(obj);
  845. return NULL;
  846. }
  847. continue;
  848. }
  849. if (strcmp(last, q.category) || last_cat_metric != q.cat_metric) {
  850. cur_cat = ast_category_new(q.category, "", 99999);
  851. if (!cur_cat) {
  852. ast_log(LOG_WARNING, "Out of memory!\n");
  853. break;
  854. }
  855. strcpy(last, q.category);
  856. last_cat_metric = q.cat_metric;
  857. ast_category_append(cfg, cur_cat);
  858. }
  859. new_v = ast_variable_new(q.var_name, q.var_val, "");
  860. ast_variable_append(cur_cat, new_v);
  861. }
  862. SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  863. ast_odbc_release_obj(obj);
  864. return cfg;
  865. }
  866. #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)
  867. #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)
  868. static int require_odbc(const char *database, const char *table, va_list ap)
  869. {
  870. struct odbc_cache_tables *tableptr = ast_odbc_find_table(database, table);
  871. struct odbc_cache_columns *col;
  872. char *elm;
  873. int type, size;
  874. if (!tableptr) {
  875. return -1;
  876. }
  877. while ((elm = va_arg(ap, char *))) {
  878. type = va_arg(ap, require_type);
  879. size = va_arg(ap, int);
  880. /* Check if the field matches the criteria */
  881. AST_RWLIST_TRAVERSE(&tableptr->columns, col, list) {
  882. if (strcmp(col->name, elm) == 0) {
  883. /* Type check, first. Some fields are more particular than others */
  884. switch (col->type) {
  885. case SQL_CHAR:
  886. case SQL_VARCHAR:
  887. case SQL_LONGVARCHAR:
  888. #ifdef HAVE_ODBC_WCHAR
  889. case SQL_WCHAR:
  890. case SQL_WVARCHAR:
  891. case SQL_WLONGVARCHAR:
  892. #endif
  893. case SQL_BINARY:
  894. case SQL_VARBINARY:
  895. case SQL_LONGVARBINARY:
  896. case SQL_GUID:
  897. #define CHECK_SIZE(n) \
  898. if (col->size < n) { \
  899. warn_length(col, n); \
  900. } \
  901. break;
  902. switch (type) {
  903. case RQ_UINTEGER1: CHECK_SIZE(3) /* 255 */
  904. case RQ_INTEGER1: CHECK_SIZE(4) /* -128 */
  905. case RQ_UINTEGER2: CHECK_SIZE(5) /* 65535 */
  906. case RQ_INTEGER2: CHECK_SIZE(6) /* -32768 */
  907. case RQ_UINTEGER3: /* 16777215 */
  908. case RQ_INTEGER3: CHECK_SIZE(8) /* -8388608 */
  909. case RQ_DATE: /* 2008-06-09 */
  910. case RQ_UINTEGER4: CHECK_SIZE(10) /* 4200000000 */
  911. case RQ_INTEGER4: CHECK_SIZE(11) /* -2100000000 */
  912. case RQ_DATETIME: /* 2008-06-09 16:03:47 */
  913. case RQ_UINTEGER8: CHECK_SIZE(19) /* trust me */
  914. case RQ_INTEGER8: CHECK_SIZE(20) /* ditto */
  915. case RQ_FLOAT:
  916. case RQ_CHAR: CHECK_SIZE(size)
  917. }
  918. #undef CHECK_SIZE
  919. break;
  920. case SQL_TYPE_DATE:
  921. if (type != RQ_DATE) {
  922. warn_type(col, type);
  923. }
  924. break;
  925. case SQL_TYPE_TIMESTAMP:
  926. case SQL_TIMESTAMP:
  927. if (type != RQ_DATE && type != RQ_DATETIME) {
  928. warn_type(col, type);
  929. }
  930. break;
  931. case SQL_BIT:
  932. warn_length(col, size);
  933. break;
  934. #define WARN_TYPE_OR_LENGTH(n) \
  935. if (!ast_rq_is_int(type)) { \
  936. warn_type(col, type); \
  937. } else { \
  938. warn_length(col, n); \
  939. }
  940. case SQL_TINYINT:
  941. if (type != RQ_UINTEGER1) {
  942. WARN_TYPE_OR_LENGTH(size)
  943. }
  944. break;
  945. case SQL_C_STINYINT:
  946. if (type != RQ_INTEGER1) {
  947. WARN_TYPE_OR_LENGTH(size)
  948. }
  949. break;
  950. case SQL_C_USHORT:
  951. if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_UINTEGER2) {
  952. WARN_TYPE_OR_LENGTH(size)
  953. }
  954. break;
  955. case SQL_SMALLINT:
  956. case SQL_C_SSHORT:
  957. if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_INTEGER2) {
  958. WARN_TYPE_OR_LENGTH(size)
  959. }
  960. break;
  961. case SQL_C_ULONG:
  962. if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
  963. type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
  964. type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
  965. type != RQ_INTEGER4) {
  966. WARN_TYPE_OR_LENGTH(size)
  967. }
  968. break;
  969. case SQL_INTEGER:
  970. case SQL_C_SLONG:
  971. if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
  972. type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
  973. type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
  974. type != RQ_INTEGER4) {
  975. WARN_TYPE_OR_LENGTH(size)
  976. }
  977. break;
  978. case SQL_C_UBIGINT:
  979. if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
  980. type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
  981. type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
  982. type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
  983. type != RQ_INTEGER8) {
  984. WARN_TYPE_OR_LENGTH(size)
  985. }
  986. break;
  987. case SQL_BIGINT:
  988. case SQL_C_SBIGINT:
  989. if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
  990. type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
  991. type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
  992. type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
  993. type != RQ_INTEGER8) {
  994. WARN_TYPE_OR_LENGTH(size)
  995. }
  996. break;
  997. #undef WARN_TYPE_OR_LENGTH
  998. case SQL_NUMERIC:
  999. case SQL_DECIMAL:
  1000. case SQL_FLOAT:
  1001. case SQL_REAL:
  1002. case SQL_DOUBLE:
  1003. if (!ast_rq_is_int(type) && type != RQ_FLOAT) {
  1004. warn_type(col, type);
  1005. }
  1006. break;
  1007. default:
  1008. ast_log(LOG_WARNING, "Realtime table %s@%s: column type (%d) unrecognized for column '%s'\n", table, database, col->type, elm);
  1009. }
  1010. break;
  1011. }
  1012. }
  1013. if (!col) {
  1014. ast_log(LOG_WARNING, "Realtime table %s@%s requires column '%s', but that column does not exist!\n", table, database, elm);
  1015. }
  1016. }
  1017. AST_RWLIST_UNLOCK(&tableptr->columns);
  1018. return 0;
  1019. }
  1020. #undef warn_length
  1021. #undef warn_type
  1022. static int unload_odbc(const char *a, const char *b)
  1023. {
  1024. return ast_odbc_clear_cache(a, b);
  1025. }
  1026. static struct ast_config_engine odbc_engine = {
  1027. .name = "odbc",
  1028. .load_func = config_odbc,
  1029. .realtime_func = realtime_odbc,
  1030. .realtime_multi_func = realtime_multi_odbc,
  1031. .store_func = store_odbc,
  1032. .destroy_func = destroy_odbc,
  1033. .update_func = update_odbc,
  1034. .update2_func = update2_odbc,
  1035. .require_func = require_odbc,
  1036. .unload_func = unload_odbc,
  1037. };
  1038. static int unload_module (void)
  1039. {
  1040. ast_config_engine_deregister(&odbc_engine);
  1041. ast_verb(1, "res_config_odbc unloaded.\n");
  1042. return 0;
  1043. }
  1044. static int load_module (void)
  1045. {
  1046. ast_config_engine_register(&odbc_engine);
  1047. ast_verb(1, "res_config_odbc loaded.\n");
  1048. return 0;
  1049. }
  1050. static int reload_module(void)
  1051. {
  1052. return 0;
  1053. }
  1054. AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Realtime ODBC configuration",
  1055. .load = load_module,
  1056. .unload = unload_module,
  1057. .reload = reload_module,
  1058. .load_pri = AST_MODPRI_REALTIME_DRIVER,
  1059. );