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