Wed Jan 8 2020 09:49:49

Asterisk developer's documentation


res_config_odbc.c
Go to the documentation of this file.
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 
21 /*! \file
22  *
23  * \brief odbc+odbc plugin for portable configuration engine
24  *
25  * \author Mark Spencer <markster@digium.com>
26  * \author Anthony Minessale II <anthmct@yahoo.com>
27  *
28  * \arg http://www.unixodbc.org
29  */
30 
31 /*** MODULEINFO
32  <depend>res_odbc</depend>
33  <support_level>core</support_level>
34  ***/
35 
36 #include "asterisk.h"
37 
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 414693 $")
39 
40 #include "asterisk/file.h"
41 #include "asterisk/channel.h"
42 #include "asterisk/pbx.h"
43 #include "asterisk/config.h"
44 #include "asterisk/module.h"
45 #include "asterisk/lock.h"
46 #include "asterisk/res_odbc.h"
47 #include "asterisk/utils.h"
48 #include "asterisk/stringfields.h"
49 
52 
54  const char *sql;
55  const char *extra;
58  );
59  va_list ap;
60  unsigned long long skip;
61 };
62 
63 #define ENCODE_CHUNK(buffer, s) \
64  do { \
65  char *eptr = buffer; \
66  const char *vptr = s; \
67  for (; *vptr && eptr < buffer + sizeof(buffer); vptr++) { \
68  if (strchr("^;", *vptr)) { \
69  /* We use ^XX, instead of %XX because '%' is a special character in SQL */ \
70  snprintf(eptr, buffer + sizeof(buffer) - eptr, "^%02hhX", *vptr); \
71  eptr += 3; \
72  } else { \
73  *eptr++ = *vptr; \
74  } \
75  } \
76  if (eptr < buffer + sizeof(buffer)) { \
77  *eptr = '\0'; \
78  } else { \
79  buffer[sizeof(buffer) - 1] = '\0'; \
80  } \
81  } while(0)
82 
83 static void decode_chunk(char *chunk)
84 {
85  for (; *chunk; chunk++) {
86  if (*chunk == '^' && strchr("0123456789ABCDEF", chunk[1]) && strchr("0123456789ABCDEF", chunk[2])) {
87  sscanf(chunk + 1, "%02hhX", (unsigned char *)chunk);
88  memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
89  }
90  }
91 }
92 
93 static inline int is_text(const struct odbc_cache_columns *column)
94 {
95  return column->type == SQL_CHAR || column->type == SQL_VARCHAR || column->type == SQL_LONGVARCHAR
96  || column->type == SQL_WCHAR || column->type == SQL_WVARCHAR || column->type == SQL_WLONGVARCHAR;
97 }
98 
99 static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data)
100 {
101  int res, x = 1, count = 0;
102  struct custom_prepare_struct *cps = data;
103  const char *newparam, *newval;
104  char encodebuf[1024];
105  SQLHSTMT stmt;
106  va_list ap;
107 
108  res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
109  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
110  ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
111  return NULL;
112  }
113 
114  ast_debug(1, "Skip: %llu; SQL: %s\n", cps->skip, cps->sql);
115 
116  res = SQLPrepare(stmt, (unsigned char *)cps->sql, SQL_NTS);
117  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
118  ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", cps->sql);
119  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
120  return NULL;
121  }
122 
123  va_copy(ap, cps->ap);
124  while ((newparam = va_arg(ap, const char *))) {
125  newval = va_arg(ap, const char *);
126  if ((1LL << count++) & cps->skip) {
127  ast_debug(1, "Skipping field '%s'='%s' (%llo/%llo)\n", newparam, newval, 1ULL << (count - 1), cps->skip);
128  continue;
129  }
130  ast_debug(1, "Parameter %d ('%s') = '%s'\n", x, newparam, newval);
131  if (strchr(newval, ';') || strchr(newval, '^')) {
132  ENCODE_CHUNK(encodebuf, newval);
133  ast_string_field_set(cps, encoding[x], encodebuf);
134  newval = cps->encoding[x];
135  }
136  SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
137  }
138  va_end(ap);
139 
140  if (!ast_strlen_zero(cps->extra)) {
141  const char *newval = cps->extra;
142  if (strchr(newval, ';') || strchr(newval, '^')) {
143  ENCODE_CHUNK(encodebuf, newval);
144  ast_string_field_set(cps, encoding[x], encodebuf);
145  newval = cps->encoding[x];
146  }
147  SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
148  }
149 
150  return stmt;
151 }
152 
153 /*!
154  * \brief Excute an SQL query and return ast_variable list
155  * \param database
156  * \param table
157  * \param ap list containing one or more field/operator/value set.
158  *
159  * Select database and preform query on table, prepare the sql statement
160  * Sub-in the values to the prepared statement and execute it. Return results
161  * as a ast_variable list.
162  *
163  * \retval var on success
164  * \retval NULL on failure
165 */
166 static struct ast_variable *realtime_odbc(const char *database, const char *table, va_list ap)
167 {
168  struct odbc_obj *obj;
169  SQLHSTMT stmt;
170  char sql[1024];
171  char coltitle[256];
172  struct ast_str *rowdata = ast_str_thread_get(&rowdata_buf, 128);
173  char *op;
174  const char *newparam;
175  char *stringp;
176  char *chunk;
177  SQLSMALLINT collen;
178  int res;
179  int x;
180  struct ast_variable *var=NULL, *prev=NULL;
181  SQLULEN colsize;
182  SQLSMALLINT colcount=0;
183  SQLSMALLINT datatype;
184  SQLSMALLINT decimaldigits;
185  SQLSMALLINT nullable;
186  SQLLEN indicator;
187  va_list aq;
188  struct custom_prepare_struct cps = { .sql = sql };
189  struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
190 
191  if (!table) {
192  return NULL;
193  }
194 
195  obj = ast_odbc_request_obj2(database, connected_flag);
196 
197  if (!obj) {
198  ast_log(LOG_ERROR, "No database handle available with the name of '%s' (check res_odbc.conf)\n", database);
199  return NULL;
200  }
201 
202  va_copy(aq, ap);
203  newparam = va_arg(aq, const char *);
204  if (!newparam) {
205  va_end(aq);
207  return NULL;
208  }
209  va_arg(aq, const char *);
210  op = !strchr(newparam, ' ') ? " =" : "";
211  snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, newparam, op,
212  strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
213  while((newparam = va_arg(aq, const char *))) {
214  op = !strchr(newparam, ' ') ? " =" : "";
215  snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", newparam, op,
216  strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
217  va_arg(aq, const char *);
218  }
219  va_end(aq);
220 
221  if (ast_string_field_init(&cps, 256)) {
223  return NULL;
224  }
225  va_copy(cps.ap, ap);
226  stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
227  va_end(cps.ap);
229 
230  if (!stmt) {
232  return NULL;
233  }
234 
235  res = SQLNumResultCols(stmt, &colcount);
236  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
237  ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
238  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
240  return NULL;
241  }
242 
243  res = SQLFetch(stmt);
244  if (res == SQL_NO_DATA) {
245  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
247  return NULL;
248  }
249  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
250  ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
251  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
253  return NULL;
254  }
255  for (x = 0; x < colcount; x++) {
256  colsize = 0;
257  collen = sizeof(coltitle);
258  res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
259  &datatype, &colsize, &decimaldigits, &nullable);
260  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
261  ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
262  if (var)
265  return NULL;
266  }
267 
268  ast_str_reset(rowdata);
269  indicator = 0;
270 
271  res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata), ast_str_size(rowdata), &indicator);
272  ast_str_update(rowdata);
273  if (indicator == SQL_NULL_DATA) {
274  ast_str_reset(rowdata);
275  } else if (!ast_str_strlen(rowdata)) {
276  /* Because we encode the empty string for a NULL, we will encode
277  * actual empty strings as a string containing a single whitespace. */
278  ast_str_set(&rowdata, -1, "%s", " ");
279  } else if ((res == SQL_SUCCESS) || (res == SQL_SUCCESS_WITH_INFO)) {
280  if (indicator != ast_str_strlen(rowdata)) {
281  /* If the available space was not enough to contain the row data enlarge and read in the rest */
282  ast_str_make_space(&rowdata, indicator + 1);
283  res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata) + ast_str_strlen(rowdata),
284  ast_str_size(rowdata) - ast_str_strlen(rowdata), &indicator);
285  ast_str_update(rowdata);
286  }
287  }
288 
289  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
290  ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
291  if (var)
294  return NULL;
295  }
296 
297  stringp = ast_str_buffer(rowdata);
298  while (stringp) {
299  chunk = strsep(&stringp, ";");
300  if (!ast_strlen_zero(ast_strip(chunk))) {
301  if (strchr(chunk, '^')) {
302  decode_chunk(chunk);
303  }
304  if (prev) {
305  prev->next = ast_variable_new(coltitle, chunk, "");
306  if (prev->next) {
307  prev = prev->next;
308  }
309  } else {
310  prev = var = ast_variable_new(coltitle, chunk, "");
311  }
312  }
313  }
314  }
315 
316  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
318  return var;
319 }
320 
321 /*!
322  * \brief Excute an Select query and return ast_config list
323  * \param database
324  * \param table
325  * \param ap list containing one or more field/operator/value set.
326  *
327  * Select database and preform query on table, prepare the sql statement
328  * Sub-in the values to the prepared statement and execute it.
329  * Execute this prepared query against several ODBC connected databases.
330  * Return results as an ast_config variable.
331  *
332  * \retval var on success
333  * \retval NULL on failure
334 */
335 static struct ast_config *realtime_multi_odbc(const char *database, const char *table, va_list ap)
336 {
337  struct odbc_obj *obj;
338  SQLHSTMT stmt;
339  char sql[1024];
340  char coltitle[256];
341  struct ast_str *rowdata = ast_str_thread_get(&rowdata_buf, 128);
342  const char *initfield;
343  char *op;
344  const char *newparam;
345  char *stringp;
346  char *chunk;
347  SQLSMALLINT collen;
348  int res;
349  int x;
350  struct ast_variable *var=NULL;
351  struct ast_config *cfg=NULL;
352  struct ast_category *cat=NULL;
353  struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
354  SQLULEN colsize;
355  SQLSMALLINT colcount=0;
356  SQLSMALLINT datatype;
357  SQLSMALLINT decimaldigits;
358  SQLSMALLINT nullable;
359  SQLLEN indicator;
360  struct custom_prepare_struct cps = { .sql = sql };
361  va_list aq;
362 
363  if (!table) {
364  return NULL;
365  }
366 
367  obj = ast_odbc_request_obj2(database, connected_flag);
368  if (!obj) {
369  return NULL;
370  }
371 
372  va_copy(aq, ap);
373  newparam = va_arg(aq, const char *);
374  if (!newparam) {
375  va_end(aq);
377  return NULL;
378  }
379 
380  initfield = ast_strdupa(newparam);
381  if ((op = strchr(initfield, ' '))) {
382  *op = '\0';
383  }
384 
385  va_arg(aq, const char *);
386  op = !strchr(newparam, ' ') ? " =" : "";
387  snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, newparam, op,
388  strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
389  while((newparam = va_arg(aq, const char *))) {
390  op = !strchr(newparam, ' ') ? " =" : "";
391  snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", newparam, op,
392  strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
393  va_arg(aq, const char *);
394  }
395  va_end(aq);
396 
397  snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
398 
399  if (ast_string_field_init(&cps, 256)) {
401  return NULL;
402  }
403  va_copy(cps.ap, ap);
404  stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
405  va_end(cps.ap);
407 
408  if (!stmt) {
410  return NULL;
411  }
412 
413  res = SQLNumResultCols(stmt, &colcount);
414  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
415  ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
416  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
418  return NULL;
419  }
420 
421  cfg = ast_config_new();
422  if (!cfg) {
423  ast_log(LOG_WARNING, "Out of memory!\n");
424  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
426  return NULL;
427  }
428 
429  while ((res=SQLFetch(stmt)) != SQL_NO_DATA) {
430  var = NULL;
431  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
432  ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
433  continue;
434  }
435  cat = ast_category_new("","",99999);
436  if (!cat) {
437  ast_log(LOG_WARNING, "Out of memory!\n");
438  continue;
439  }
440  for (x=0;x<colcount;x++) {
441  colsize = 0;
442  collen = sizeof(coltitle);
443  res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
444  &datatype, &colsize, &decimaldigits, &nullable);
445  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
446  ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
448  goto next_sql_fetch;
449  }
450 
451  ast_str_reset(rowdata);
452  indicator = 0;
453 
454  res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata), ast_str_size(rowdata), &indicator);
455  ast_str_update(rowdata);
456  if (indicator == SQL_NULL_DATA) {
457  continue;
458  }
459 
460  if ((res == SQL_SUCCESS) || (res == SQL_SUCCESS_WITH_INFO)) {
461  if (indicator != ast_str_strlen(rowdata)) {
462  /* If the available space was not enough to contain the row data enlarge and read in the rest */
463  ast_str_make_space(&rowdata, indicator + 1);
464  res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata) + ast_str_strlen(rowdata),
465  ast_str_size(rowdata) - ast_str_strlen(rowdata), &indicator);
466  ast_str_update(rowdata);
467  }
468  }
469 
470  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
471  ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
473  goto next_sql_fetch;
474  }
475  stringp = ast_str_buffer(rowdata);
476  while (stringp) {
477  chunk = strsep(&stringp, ";");
478  if (!ast_strlen_zero(ast_strip(chunk))) {
479  if (strchr(chunk, '^')) {
480  decode_chunk(chunk);
481  }
482  if (!strcmp(initfield, coltitle)) {
483  ast_category_rename(cat, chunk);
484  }
485  var = ast_variable_new(coltitle, chunk, "");
486  ast_variable_append(cat, var);
487  }
488  }
489  }
490  ast_category_append(cfg, cat);
491 next_sql_fetch:;
492  }
493 
494  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
496  return cfg;
497 }
498 
499 /*!
500  * \brief Excute an UPDATE query
501  * \param database
502  * \param table
503  * \param keyfield where clause field
504  * \param lookup value of field for where clause
505  * \param ap list containing one or more field/value set(s).
506  *
507  * Update a database table, prepare the sql statement using keyfield and lookup
508  * control the number of records to change. All values to be changed are stored in ap list.
509  * Sub-in the values to the prepared statement and execute it.
510  *
511  * \retval number of rows affected
512  * \retval -1 on failure
513 */
514 static int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
515 {
516  struct odbc_obj *obj;
517  SQLHSTMT stmt;
518  char sql[256];
519  SQLLEN rowcount=0;
520  const char *newparam, *newval;
521  int res, count = 0, paramcount = 0;
522  va_list aq;
523  struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
524  struct odbc_cache_tables *tableptr;
525  struct odbc_cache_columns *column = NULL;
526  struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
527 
528  if (!table || !keyfield) {
529  return -1;
530  }
531 
532  tableptr = ast_odbc_find_table(database, table);
533  if (!(obj = ast_odbc_request_obj2(database, connected_flag))) {
534  ast_odbc_release_table(tableptr);
535  return -1;
536  }
537 
538  if (tableptr && !ast_odbc_find_column(tableptr, keyfield)) {
539  ast_log(LOG_WARNING, "Key field '%s' does not exist in table '%s@%s'. Update will fail\n", keyfield, table, database);
540  }
541 
542  va_copy(aq, ap);
543 
544  snprintf(sql, sizeof(sql), "UPDATE %s SET ", table);
545  while((newparam = va_arg(aq, const char *))) {
546  newval = va_arg(aq, const char *);
547  if ((tableptr && (column = ast_odbc_find_column(tableptr, newparam))) || count >= 64) {
548  if (paramcount++) {
549  snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", ");
550  }
551  /* NULL test for non-text columns */
552  if (count < 64 && ast_strlen_zero(newval) && column->nullable && !is_text(column) && !ast_odbc_allow_empty_string_in_nontext(obj)) {
553  snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=NULL", newparam);
554  cps.skip |= (1LL << count);
555  } else {
556  /* Value is not an empty string, or column accepts empty strings, or we couldn't fit any more into cps.skip (count >= 64 ?!). */
557  snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=?", newparam);
558  }
559  } else { /* the column does not exist in the table */
560  cps.skip |= (1LL << count);
561  }
562  ++count;
563  }
564  va_end(aq);
565  snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield);
566  ast_odbc_release_table(tableptr);
567 
568  if (ast_string_field_init(&cps, 256)) {
570  return -1;
571  }
572  va_copy(cps.ap, ap);
573  stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
574  va_end(cps.ap);
576 
577  if (!stmt) {
579  return -1;
580  }
581 
582  res = SQLRowCount(stmt, &rowcount);
583  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
585 
586  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
587  ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
588  return -1;
589  }
590 
591  if (rowcount >= 0) {
592  return (int) rowcount;
593  }
594 
595  return -1;
596 }
597 
599  const char *database;
600  const char *table;
601  va_list ap;
602 };
603 
604 static SQLHSTMT update2_prepare(struct odbc_obj *obj, void *data)
605 {
606  int res, x = 1, first = 1;
607  struct update2_prepare_struct *ups = data;
608  const char *newparam, *newval;
609  struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
610  SQLHSTMT stmt;
611  va_list ap;
612  struct odbc_cache_tables *tableptr = ast_odbc_find_table(ups->database, ups->table);
613 
614  if (!sql) {
615  if (tableptr) {
616  ast_odbc_release_table(tableptr);
617  }
618  return NULL;
619  }
620 
621  if (!tableptr) {
622  ast_log(LOG_ERROR, "Could not retrieve metadata for table '%s@%s'. Update will fail!\n", ups->table, ups->database);
623  return NULL;
624  }
625 
626  res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
627  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
628  ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
629  ast_odbc_release_table(tableptr);
630  return NULL;
631  }
632 
633  ast_str_set(&sql, 0, "UPDATE %s SET ", ups->table);
634 
635  /* Start by finding the second set of parameters */
636  va_copy(ap, ups->ap);
637 
638  while ((newparam = va_arg(ap, const char *))) {
639  newval = va_arg(ap, const char *);
640  }
641 
642  while ((newparam = va_arg(ap, const char *))) {
643  newval = va_arg(ap, const char *);
644  if (ast_odbc_find_column(tableptr, newparam)) {
645  ast_str_append(&sql, 0, "%s%s=? ", first ? "" : ", ", newparam);
646  SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
647  first = 0;
648  } else {
649  ast_log(LOG_NOTICE, "Not updating column '%s' in '%s@%s' because that column does not exist!\n", newparam, ups->table, ups->database);
650  }
651  }
652  va_end(ap);
653 
654  ast_str_append(&sql, 0, "WHERE");
655  first = 1;
656 
657  /* Restart search, because we need to add the search parameters */
658  va_copy(ap, ups->ap);
659 
660  while ((newparam = va_arg(ap, const char *))) {
661  newval = va_arg(ap, const char *);
662  if (!ast_odbc_find_column(tableptr, newparam)) {
663  va_end(ap);
664  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);
665  ast_odbc_release_table(tableptr);
666  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
667  return NULL;
668  }
669  ast_str_append(&sql, 0, "%s %s=?", first ? "" : " AND", newparam);
670  SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
671  first = 0;
672  }
673  va_end(ap);
674 
675  /* Done with the table metadata */
676  ast_odbc_release_table(tableptr);
677 
678  res = SQLPrepare(stmt, (unsigned char *)ast_str_buffer(sql), SQL_NTS);
679  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
680  ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", ast_str_buffer(sql));
681  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
682  return NULL;
683  }
684 
685  return stmt;
686 }
687 
688 /*!
689  * \brief Execute an UPDATE query
690  * \param database
691  * \param table
692  * \param ap list containing one or more field/value set(s).
693  *
694  * Update a database table, preparing the sql statement from a list of
695  * key/value pairs specified in ap. The lookup pairs are specified first
696  * and are separated from the update pairs by a sentinel value.
697  * Sub-in the values to the prepared statement and execute it.
698  *
699  * \retval number of rows affected
700  * \retval -1 on failure
701 */
702 static int update2_odbc(const char *database, const char *table, va_list ap)
703 {
704  struct odbc_obj *obj;
705  SQLHSTMT stmt;
706  struct update2_prepare_struct ups = { .database = database, .table = table, };
707  struct ast_str *sql;
708  int res;
709  SQLLEN rowcount = 0;
710 
711  if (!(obj = ast_odbc_request_obj(database, 0))) {
712  return -1;
713  }
714 
715  va_copy(ups.ap, ap);
716  if (!(stmt = ast_odbc_prepare_and_execute(obj, update2_prepare, &ups))) {
717  va_end(ups.ap);
719  return -1;
720  }
721  va_end(ups.ap);
722 
723  res = SQLRowCount(stmt, &rowcount);
724  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
726 
727  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
728  /* Since only a single thread can access this memory, we can retrieve what would otherwise be lost. */
729  sql = ast_str_thread_get(&sql_buf, 16);
730  ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n", ast_str_buffer(sql));
731  return -1;
732  }
733 
734  if (rowcount >= 0) {
735  return (int)rowcount;
736  }
737 
738  return -1;
739 }
740 
741 /*!
742  * \brief Excute an INSERT query
743  * \param database
744  * \param table
745  * \param ap list containing one or more field/value set(s)
746  *
747  * Insert a new record into database table, prepare the sql statement.
748  * All values to be changed are stored in ap list.
749  * Sub-in the values to the prepared statement and execute it.
750  *
751  * \retval number of rows affected
752  * \retval -1 on failure
753 */
754 static int store_odbc(const char *database, const char *table, va_list ap)
755 {
756  struct odbc_obj *obj;
757  SQLHSTMT stmt;
758  char sql[256];
759  char keys[256];
760  char vals[256];
761  SQLLEN rowcount=0;
762  const char *newparam;
763  int res;
764  va_list aq;
765  struct custom_prepare_struct cps = { .sql = sql, .extra = NULL };
766  struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
767 
768  if (!table) {
769  return -1;
770  }
771 
772  obj = ast_odbc_request_obj2(database, connected_flag);
773  if (!obj) {
774  return -1;
775  }
776 
777  va_copy(aq, ap);
778 
779  newparam = va_arg(aq, const char *);
780  if (!newparam) {
781  va_end(aq);
783  return -1;
784  }
785  va_arg(aq, const char *);
786  snprintf(keys, sizeof(keys), "%s", newparam);
787  ast_copy_string(vals, "?", sizeof(vals));
788  while ((newparam = va_arg(aq, const char *))) {
789  snprintf(keys + strlen(keys), sizeof(keys) - strlen(keys), ", %s", newparam);
790  snprintf(vals + strlen(vals), sizeof(vals) - strlen(vals), ", ?");
791  va_arg(aq, const char *);
792  }
793  va_end(aq);
794  snprintf(sql, sizeof(sql), "INSERT INTO %s (%s) VALUES (%s)", table, keys, vals);
795 
796  if (ast_string_field_init(&cps, 256)) {
798  return -1;
799  }
800  va_copy(cps.ap, ap);
801  stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
802  va_end(cps.ap);
804 
805  if (!stmt) {
807  return -1;
808  }
809 
810  res = SQLRowCount(stmt, &rowcount);
811  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
813 
814  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
815  ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
816  return -1;
817  }
818 
819  if (rowcount >= 0)
820  return (int)rowcount;
821 
822  return -1;
823 }
824 
825 /*!
826  * \brief Excute an DELETE query
827  * \param database
828  * \param table
829  * \param keyfield where clause field
830  * \param lookup value of field for where clause
831  * \param ap list containing one or more field/value set(s)
832  *
833  * Delete a row from a database table, prepare the sql statement using keyfield and lookup
834  * control the number of records to change. Additional params to match rows are stored in ap list.
835  * Sub-in the values to the prepared statement and execute it.
836  *
837  * \retval number of rows affected
838  * \retval -1 on failure
839 */
840 static int destroy_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
841 {
842  struct odbc_obj *obj;
843  SQLHSTMT stmt;
844  char sql[256];
845  SQLLEN rowcount=0;
846  const char *newparam;
847  int res;
848  va_list aq;
849  struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
850  struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
851 
852  if (!table) {
853  return -1;
854  }
855 
856  obj = ast_odbc_request_obj2(database, connected_flag);
857  if (!obj) {
858  return -1;
859  }
860 
861  snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE ", table);
862 
863  va_copy(aq, ap);
864  while((newparam = va_arg(aq, const char *))) {
865  snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=? AND ", newparam);
866  va_arg(aq, const char *);
867  }
868  va_end(aq);
869  snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=?", keyfield);
870 
871  if (ast_string_field_init(&cps, 256)) {
873  return -1;
874  }
875  va_copy(cps.ap, ap);
876  stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
877  va_end(cps.ap);
879 
880  if (!stmt) {
882  return -1;
883  }
884 
885  res = SQLRowCount(stmt, &rowcount);
886  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
888 
889  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
890  ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
891  return -1;
892  }
893 
894  if (rowcount >= 0)
895  return (int)rowcount;
896 
897  return -1;
898 }
899 
901  char *sql;
902  unsigned long cat_metric;
903  char category[128];
904  char var_name[128];
905  char *var_val;
906  unsigned long var_val_size;
907  SQLLEN err;
908 };
909 
910 
911 static SQLHSTMT length_determination_odbc_prepare(struct odbc_obj *obj, void *data)
912 {
913  struct config_odbc_obj *q = data;
914  SQLHSTMT sth;
915  int res;
916 
917  res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &sth);
918  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
919  ast_verb(4, "Failure in AllocStatement %d\n", res);
920  return NULL;
921  }
922 
923  res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS);
924  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
925  ast_verb(4, "Error in PREPARE %d\n", res);
926  SQLFreeHandle(SQL_HANDLE_STMT, sth);
927  return NULL;
928  }
929 
930  SQLBindCol(sth, 1, SQL_C_ULONG, &q->var_val_size, sizeof(q->var_val_size), &q->err);
931 
932  return sth;
933 }
934 
935 static SQLHSTMT config_odbc_prepare(struct odbc_obj *obj, void *data)
936 {
937  struct config_odbc_obj *q = data;
938  SQLHSTMT sth;
939  int res;
940 
941  res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &sth);
942  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
943  ast_verb(4, "Failure in AllocStatement %d\n", res);
944  return NULL;
945  }
946 
947  res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS);
948  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
949  ast_verb(4, "Error in PREPARE %d\n", res);
950  SQLFreeHandle(SQL_HANDLE_STMT, sth);
951  return NULL;
952  }
953 
954  SQLBindCol(sth, 1, SQL_C_ULONG, &q->cat_metric, sizeof(q->cat_metric), &q->err);
955  SQLBindCol(sth, 2, SQL_C_CHAR, q->category, sizeof(q->category), &q->err);
956  SQLBindCol(sth, 3, SQL_C_CHAR, q->var_name, sizeof(q->var_name), &q->err);
957  SQLBindCol(sth, 4, SQL_C_CHAR, q->var_val, q->var_val_size, &q->err);
958 
959  return sth;
960 }
961 
962 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)
963 {
964  struct ast_variable *new_v;
965  struct ast_category *cur_cat;
966  int res = 0;
967  struct odbc_obj *obj;
968  char sqlbuf[1024] = "";
969  char *sql = sqlbuf;
970  size_t sqlleft = sizeof(sqlbuf);
971  unsigned int last_cat_metric = 0;
972  SQLSMALLINT rowcount = 0;
973  SQLHSTMT stmt;
974  char last[128] = "";
975  struct config_odbc_obj q;
976  struct ast_flags loader_flags = { 0 };
977  struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
978 
979  memset(&q, 0, sizeof(q));
980 
981  if (!file || !strcmp (file, "res_config_odbc.conf"))
982  return NULL; /* cant configure myself with myself ! */
983 
984  obj = ast_odbc_request_obj2(database, connected_flag);
985  if (!obj)
986  return NULL;
987 
988  q.sql = sqlbuf;
989 
990  ast_build_string(&sql, &sqlleft, "SELECT MAX(LENGTH(var_val)) FROM %s WHERE filename='%s'", table, file);
991 
993 
994  if (!stmt) {
995  ast_log(LOG_WARNING, "SQL select error!\n[%s]\n\n", sql);
997  return NULL;
998  }
999 
1000  res = SQLNumResultCols(stmt, &rowcount);
1001 
1002  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1003  ast_log(LOG_WARNING, "SQL NumResultCols error!\n[%s]\n\n", sql);
1004  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1005  ast_odbc_release_obj(obj);
1006  return NULL;
1007  }
1008 
1009  if (!rowcount) {
1010  ast_log(LOG_NOTICE, "found nothing\n");
1011  ast_odbc_release_obj(obj);
1012  return cfg;
1013  }
1014 
1015  /* There will be only one result for this, the maximum length of a variable value */
1016  if (SQLFetch(stmt) == SQL_NO_DATA) {
1017  ast_log(LOG_NOTICE, "Failed to determine maximum length of a configuration value\n");
1018  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1019  ast_odbc_release_obj(obj);
1020  return NULL;
1021  }
1022 
1023  /* Reset stuff to a fresh state for the actual query which will retrieve all configuration */
1024  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1025  sql = sqlbuf;
1026  sqlleft = sizeof(sqlbuf);
1027 
1028  ast_build_string(&sql, &sqlleft, "SELECT cat_metric, category, var_name, var_val FROM %s ", table);
1029  ast_build_string(&sql, &sqlleft, "WHERE filename='%s' AND commented=0 ", file);
1030  ast_build_string(&sql, &sqlleft, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ");
1031 
1032  q.var_val_size += 1;
1034  if (!q.var_val) {
1035  ast_log(LOG_WARNING, "Could not create buffer for reading in configuration values for '%s'\n", file);
1036  ast_odbc_release_obj(obj);
1037  return NULL;
1038  }
1039 
1041 
1042  if (!stmt) {
1043  ast_log(LOG_WARNING, "SQL select error!\n[%s]\n\n", sql);
1044  ast_odbc_release_obj(obj);
1045  ast_free(q.var_val);
1046  return NULL;
1047  }
1048 
1049  res = SQLNumResultCols(stmt, &rowcount);
1050 
1051  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1052  ast_log(LOG_WARNING, "SQL NumResultCols error!\n[%s]\n\n", sql);
1053  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1054  ast_odbc_release_obj(obj);
1055  ast_free(q.var_val);
1056  return NULL;
1057  }
1058 
1059  if (!rowcount) {
1060  ast_log(LOG_NOTICE, "found nothing\n");
1061  ast_odbc_release_obj(obj);
1062  ast_free(q.var_val);
1063  return cfg;
1064  }
1065 
1066  cur_cat = ast_config_get_current_category(cfg);
1067 
1068  while ((res = SQLFetch(stmt)) != SQL_NO_DATA) {
1069  if (!strcmp (q.var_name, "#include")) {
1070  if (!ast_config_internal_load(q.var_val, cfg, loader_flags, "", who_asked)) {
1071  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1072  ast_odbc_release_obj(obj);
1073  ast_free(q.var_val);
1074  return NULL;
1075  }
1076  continue;
1077  }
1078  if (strcmp(last, q.category) || last_cat_metric != q.cat_metric) {
1079  cur_cat = ast_category_new(q.category, "", 99999);
1080  if (!cur_cat) {
1081  ast_log(LOG_WARNING, "Out of memory!\n");
1082  break;
1083  }
1084  strcpy(last, q.category);
1085  last_cat_metric = q.cat_metric;
1086  ast_category_append(cfg, cur_cat);
1087  }
1088 
1089  new_v = ast_variable_new(q.var_name, q.var_val, "");
1090  ast_variable_append(cur_cat, new_v);
1091  }
1092 
1093  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1094  ast_odbc_release_obj(obj);
1095  ast_free(q.var_val);
1096  return cfg;
1097 }
1098 
1099 #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)
1100 #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)
1101 
1102 static int require_odbc(const char *database, const char *table, va_list ap)
1103 {
1104  struct odbc_cache_tables *tableptr = ast_odbc_find_table(database, table);
1105  struct odbc_cache_columns *col;
1106  char *elm;
1107  int type, size;
1108 
1109  if (!tableptr) {
1110  return -1;
1111  }
1112 
1113  while ((elm = va_arg(ap, char *))) {
1114  type = va_arg(ap, require_type);
1115  size = va_arg(ap, int);
1116  /* Check if the field matches the criteria */
1117  AST_RWLIST_TRAVERSE(&tableptr->columns, col, list) {
1118  if (strcmp(col->name, elm) == 0) {
1119  /* Type check, first. Some fields are more particular than others */
1120  switch (col->type) {
1121  case SQL_CHAR:
1122  case SQL_VARCHAR:
1123  case SQL_LONGVARCHAR:
1124 #ifdef HAVE_ODBC_WCHAR
1125  case SQL_WCHAR:
1126  case SQL_WVARCHAR:
1127  case SQL_WLONGVARCHAR:
1128 #endif
1129  case SQL_BINARY:
1130  case SQL_VARBINARY:
1131  case SQL_LONGVARBINARY:
1132  case SQL_GUID:
1133 #define CHECK_SIZE(n) \
1134  if (col->size < n) { \
1135  warn_length(col, n); \
1136  } \
1137  break;
1138  switch (type) {
1139  case RQ_UINTEGER1: CHECK_SIZE(3) /* 255 */
1140  case RQ_INTEGER1: CHECK_SIZE(4) /* -128 */
1141  case RQ_UINTEGER2: CHECK_SIZE(5) /* 65535 */
1142  case RQ_INTEGER2: CHECK_SIZE(6) /* -32768 */
1143  case RQ_UINTEGER3: /* 16777215 */
1144  case RQ_INTEGER3: CHECK_SIZE(8) /* -8388608 */
1145  case RQ_DATE: /* 2008-06-09 */
1146  case RQ_UINTEGER4: CHECK_SIZE(10) /* 4200000000 */
1147  case RQ_INTEGER4: CHECK_SIZE(11) /* -2100000000 */
1148  case RQ_DATETIME: /* 2008-06-09 16:03:47 */
1149  case RQ_UINTEGER8: CHECK_SIZE(19) /* trust me */
1150  case RQ_INTEGER8: CHECK_SIZE(20) /* ditto */
1151  case RQ_FLOAT:
1152  case RQ_CHAR: CHECK_SIZE(size)
1153  }
1154 #undef CHECK_SIZE
1155  break;
1156  case SQL_TYPE_DATE:
1157  if (type != RQ_DATE) {
1158  warn_type(col, type);
1159  }
1160  break;
1161  case SQL_TYPE_TIMESTAMP:
1162  case SQL_TIMESTAMP:
1163  if (type != RQ_DATE && type != RQ_DATETIME) {
1164  warn_type(col, type);
1165  }
1166  break;
1167  case SQL_BIT:
1168  warn_length(col, size);
1169  break;
1170 #define WARN_TYPE_OR_LENGTH(n) \
1171  if (!ast_rq_is_int(type)) { \
1172  warn_type(col, type); \
1173  } else { \
1174  warn_length(col, n); \
1175  }
1176  case SQL_TINYINT:
1177  if (type != RQ_UINTEGER1) {
1178  WARN_TYPE_OR_LENGTH(size)
1179  }
1180  break;
1181  case SQL_C_STINYINT:
1182  if (type != RQ_INTEGER1) {
1183  WARN_TYPE_OR_LENGTH(size)
1184  }
1185  break;
1186  case SQL_C_USHORT:
1187  if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_UINTEGER2) {
1188  WARN_TYPE_OR_LENGTH(size)
1189  }
1190  break;
1191  case SQL_SMALLINT:
1192  case SQL_C_SSHORT:
1193  if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_INTEGER2) {
1194  WARN_TYPE_OR_LENGTH(size)
1195  }
1196  break;
1197  case SQL_C_ULONG:
1198  if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
1199  type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
1200  type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
1201  type != RQ_INTEGER4) {
1202  WARN_TYPE_OR_LENGTH(size)
1203  }
1204  break;
1205  case SQL_INTEGER:
1206  case SQL_C_SLONG:
1207  if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
1208  type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
1209  type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
1210  type != RQ_INTEGER4) {
1211  WARN_TYPE_OR_LENGTH(size)
1212  }
1213  break;
1214  case SQL_C_UBIGINT:
1215  if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
1216  type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
1217  type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
1218  type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
1219  type != RQ_INTEGER8) {
1220  WARN_TYPE_OR_LENGTH(size)
1221  }
1222  break;
1223  case SQL_BIGINT:
1224  case SQL_C_SBIGINT:
1225  if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
1226  type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
1227  type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
1228  type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
1229  type != RQ_INTEGER8) {
1230  WARN_TYPE_OR_LENGTH(size)
1231  }
1232  break;
1233 #undef WARN_TYPE_OR_LENGTH
1234  case SQL_NUMERIC:
1235  case SQL_DECIMAL:
1236  case SQL_FLOAT:
1237  case SQL_REAL:
1238  case SQL_DOUBLE:
1239  if (!ast_rq_is_int(type) && type != RQ_FLOAT) {
1240  warn_type(col, type);
1241  }
1242  break;
1243  default:
1244  ast_log(LOG_WARNING, "Realtime table %s@%s: column type (%d) unrecognized for column '%s'\n", table, database, col->type, elm);
1245  }
1246  break;
1247  }
1248  }
1249  if (!col) {
1250  ast_log(LOG_WARNING, "Realtime table %s@%s requires column '%s', but that column does not exist!\n", table, database, elm);
1251  }
1252  }
1253  AST_RWLIST_UNLOCK(&tableptr->columns);
1254  return 0;
1255 }
1256 #undef warn_length
1257 #undef warn_type
1258 
1259 static int unload_odbc(const char *a, const char *b)
1260 {
1261  return ast_odbc_clear_cache(a, b);
1262 }
1263 
1265  .name = "odbc",
1266  .load_func = config_odbc,
1267  .realtime_func = realtime_odbc,
1268  .realtime_multi_func = realtime_multi_odbc,
1269  .store_func = store_odbc,
1270  .destroy_func = destroy_odbc,
1271  .update_func = update_odbc,
1272  .update2_func = update2_odbc,
1273  .require_func = require_odbc,
1274  .unload_func = unload_odbc,
1275 };
1276 
1277 static int unload_module (void)
1278 {
1279  ast_config_engine_deregister(&odbc_engine);
1280 
1281  ast_verb(1, "res_config_odbc unloaded.\n");
1282  return 0;
1283 }
1284 
1285 static int load_module (void)
1286 {
1287  ast_config_engine_register(&odbc_engine);
1288  ast_verb(1, "res_config_odbc loaded.\n");
1289  return 0;
1290 }
1291 
1292 static int reload_module(void)
1293 {
1294  return 0;
1295 }
1296 
1297 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Realtime ODBC configuration",
1298  .load = load_module,
1299  .unload = unload_module,
1300  .reload = reload_module,
1301  .load_pri = AST_MODPRI_REALTIME_DRIVER,
1302  );
static SQLHSTMT config_odbc_prepare(struct odbc_obj *obj, void *data)
SQLHDBC con
Definition: res_odbc.h:48
static int destroy_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
Excute an DELETE query.
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:81
int ast_odbc_backslash_is_escape(struct odbc_obj *obj)
Checks if the database natively supports backslash as an escape character.
Definition: res_odbc.c:1118
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
static char * encoding
Definition: cdr_pgsql.c:56
char * strsep(char **str, const char *delims)
#define ast_odbc_release_table(ptr)
Release a table returned from ast_odbc_find_table.
Definition: res_odbc.h:213
static int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
Excute an UPDATE query.
static int store_odbc(const char *database, const char *table, va_list ap)
Excute an INSERT query.
static int unload_odbc(const char *a, const char *b)
#define CHECK_SIZE(n)
static SQLHSTMT update2_prepare(struct odbc_obj *obj, void *data)
static SQLHSTMT length_determination_odbc_prepare(struct odbc_obj *obj, void *data)
size_t ast_str_size(const struct ast_str *buf)
Returns the current maximum length (without reallocation) of the current buffer.
Definition: strings.h:482
#define LOG_WARNING
Definition: logger.h:144
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:497
unsigned long cat_metric
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:150
struct ast_category * ast_config_get_current_category(const struct ast_config *cfg)
Retrieve the current category name being built.
Definition: config.c:1055
Structure for variables, used for configurations and for channel variables.
Definition: config.h:75
#define var
Definition: ast_expr2f.c:606
SQLSMALLINT nullable
Definition: res_odbc.h:70
static int load_module(void)
Configuration File Parser.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:900
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:235
#define ast_odbc_request_obj2(a, b)
Definition: res_odbc.h:122
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:374
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: config.c:586
These structures are used for adaptive capabilities.
Definition: res_odbc.h:64
#define ast_verb(level,...)
Definition: logger.h:243
static int unload_module(void)
Configuration engine structure, used to define realtime drivers.
Definition: config.h:121
String fields in structures.
Utility functions.
static struct ast_variable * realtime_odbc(const char *database, const char *table, va_list ap)
Excute an SQL query and return ast_variable list.
struct ast_category * ast_category_new(const char *name, const char *in_file, int lineno)
Create a category structure.
Definition: config.c:673
static char * table
Definition: cdr_odbc.c:50
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:874
Definition: config.h:68
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
void ast_category_destroy(struct ast_category *cat)
Definition: config.c:762
int ast_build_string(char **buffer, size_t *space, const char *fmt,...)
Build a string in a buffer, designed to be called repeatedly.
Definition: utils.c:1521
General Asterisk PBX channel definitions.
ODBC container.
Definition: res_odbc.h:46
int ast_str_make_space(struct ast_str **buf, size_t new_len)
Definition: strings.h:588
struct odbc_cache_columns * ast_odbc_find_column(struct odbc_cache_tables *table, const char *colname)
Find a column entry within a cached table structure.
Definition: res_odbc.c:566
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:249
static int update2_odbc(const char *database, const char *table, va_list ap)
Execute an UPDATE query.
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:63
struct sla_ringing_trunk * last
Definition: app_meetme.c:965
#define AST_RWLIST_TRAVERSE
Definition: linkedlists.h:493
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:155
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:220
ODBC resource manager.
static struct ast_threadstorage rowdata_buf
static struct ast_config_engine odbc_engine
Core PBX routines and definitions.
unsigned long var_val_size
static int reload(void)
Definition: app_amd.c:497
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: utils.h:663
struct odbc_cache_tables * ast_odbc_find_table(const char *database, const char *tablename)
Find or create an entry describing the table specified.
Definition: res_odbc.c:449
#define LOG_ERROR
Definition: logger.h:155
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:364
int ast_odbc_allow_empty_string_in_nontext(struct odbc_obj *obj)
Checks if the database natively supports implicit conversion from an empty string to a number (0)...
Definition: res_odbc.c:1123
static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data)
#define warn_length(col, size)
void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
Definition: config.c:483
struct sla_ringing_trunk * first
Definition: app_meetme.c:965
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
#define LOG_NOTICE
Definition: logger.h:133
int ast_config_engine_register(struct ast_config_engine *newconfig)
Register config engine.
Definition: config.c:2378
static int require_odbc(const char *database, const char *table, va_list ap)
#define ast_free(a)
Definition: astmm.h:97
SQLINTEGER size
Definition: res_odbc.h:67
struct ast_config * ast_config_new(void)
Create a new base configuration structure.
Definition: config.c:888
#define ast_odbc_request_obj(a, b)
Definition: res_odbc.h:123
int ast_odbc_clear_cache(const char *database, const char *tablename)
Remove a cache entry from memory This function may be called to clear entries created and cached by t...
Definition: res_odbc.c:577
static struct ast_threadstorage sql_buf
static const char type[]
Definition: chan_nbs.c:57
Structure used to handle boolean flags.
Definition: utils.h:200
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:436
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:471
SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT(*prepare_cb)(struct odbc_obj *obj, void *data), void *data)
Prepares, executes, and returns the resulting statement handle.
Definition: res_odbc.c:622
#define WARN_TYPE_OR_LENGTH(n)
const ast_string_field encoding[256]
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:223
int ast_rq_is_int(require_type type)
Check if require type is an integer type.
Definition: config.h:768
void ast_str_update(struct ast_str *buf)
Update the length of the buffer, after using ast_str merely as a buffer.
Definition: strings.h:446
static void decode_chunk(char *chunk)
static int reload_module(void)
static struct ast_config * realtime_multi_odbc(const char *database, const char *table, va_list ap)
Excute an Select query and return ast_config list.
#define warn_type(col, type)
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:669
void ast_category_append(struct ast_config *config, struct ast_category *cat)
Definition: config.c:719
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)
SQLSMALLINT type
Definition: res_odbc.h:66
struct ast_config * ast_config_internal_load(const char *configfile, struct ast_config *cfg, struct ast_flags flags, const char *suggested_incl_file, const char *who_asked)
Definition: config.c:2459
void ast_odbc_release_obj(struct odbc_obj *obj)
Releases an ODBC object previously allocated by ast_odbc_request_obj()
Definition: res_odbc.c:1112
void ast_category_rename(struct ast_category *cat, const char *name)
Definition: config.c:867
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:38
#define ast_malloc(a)
Definition: astmm.h:91
static int is_text(const struct odbc_cache_columns *column)
Asterisk module definitions.
char * strcasestr(const char *, const char *)
require_type
Types used in ast_realtime_require_field.
Definition: config.h:57
#define ENCODE_CHUNK(buffer, s)
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:253
Definition: config.h:70
struct ast_variable * ast_variable_new(const char *name, const char *value, const char *filename)
Definition: config.c:278
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180
int ast_config_engine_deregister(struct ast_config_engine *del)
Deregister config engine.
Definition: config.c:2397
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:344
unsigned long long skip
struct odbc_cache_tables::_columns columns