res_config_odbc.c 36 KB

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