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