Asterisk - The Open Source Telephony Project  GIT-master-a24979a
Data Structures | Macros | Functions | Variables
cdr_adaptive_odbc.c File Reference

Adaptive ODBC CDR backend. More...

#include "asterisk.h"
#include <sys/types.h>
#include <time.h>
#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>
#include "asterisk/config.h"
#include "asterisk/channel.h"
#include "asterisk/lock.h"
#include "asterisk/linkedlists.h"
#include "asterisk/res_odbc.h"
#include "asterisk/cdr.h"
#include "asterisk/module.h"
Include dependency graph for cdr_adaptive_odbc.c:

Go to the source code of this file.

Data Structures

struct  columns
 
struct  tables::odbc_columns
 
struct  odbc_tables
 
struct  tables
 

Macros

#define CONFIG   "cdr_adaptive_odbc.conf"
 
#define LENGTHEN_BUF(size, var_sql)
 
#define LENGTHEN_BUF1(size)    LENGTHEN_BUF(size, sql);
 
#define LENGTHEN_BUF2(size)    LENGTHEN_BUF(size, sql2);
 

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
static int free_config (void)
 
static SQLHSTMT generic_prepare (struct odbc_obj *obj, void *data)
 
static int load_config (void)
 
static int load_module (void)
 
static int odbc_log (struct ast_cdr *cdr)
 
static int reload (void)
 
static int unload_module (void)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Adaptive ODBC CDR backend" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CDR_DRIVER, .requires = "cdr,res_odbc", }
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static int maxsize = 512
 
static int maxsize2 = 512
 
static const char name [] = "Adaptive ODBC"
 
static struct odbc_tables odbc_tables = { .first = NULL, .last = NULL, .lock = { PTHREAD_RWLOCK_INITIALIZER , NULL, {1, 0} } , }
 

Detailed Description

Adaptive ODBC CDR backend.

Author
Tilghman Lesher cdr_a.nosp@m.dapt.nosp@m.ive_o.nosp@m.dbc_.nosp@m._v1@t.nosp@m.he-t.nosp@m.ilghm.nosp@m.an.c.nosp@m.om

Definition in file cdr_adaptive_odbc.c.

Macro Definition Documentation

◆ CONFIG

#define CONFIG   "cdr_adaptive_odbc.conf"

Definition at line 59 of file cdr_adaptive_odbc.c.

◆ LENGTHEN_BUF

#define LENGTHEN_BUF (   size,
  var_sql 
)

Definition at line 360 of file cdr_adaptive_odbc.c.

◆ LENGTHEN_BUF1

#define LENGTHEN_BUF1 (   size)     LENGTHEN_BUF(size, sql);

Definition at line 374 of file cdr_adaptive_odbc.c.

◆ LENGTHEN_BUF2

#define LENGTHEN_BUF2 (   size)     LENGTHEN_BUF(size, sql2);

Definition at line 376 of file cdr_adaptive_odbc.c.

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 836 of file cdr_adaptive_odbc.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 836 of file cdr_adaptive_odbc.c.

◆ AST_MODULE_SELF_SYM()

struct ast_module* AST_MODULE_SELF_SYM ( void  )

Definition at line 836 of file cdr_adaptive_odbc.c.

◆ free_config()

static int free_config ( void  )
static

Definition at line 314 of file cdr_adaptive_odbc.c.

315 {
316  struct tables *table;
317  struct columns *entry;
319  while ((entry = AST_LIST_REMOVE_HEAD(&(table->columns), list))) {
320  ast_free(entry);
321  }
322  ast_free(table);
323  }
324  return 0;
325 }
#define ast_free(a)
Definition: astmm.h:180
static char * table
Definition: cdr_odbc.c:55
#define AST_RWLIST_REMOVE_HEAD
Definition: linkedlists.h:844
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
struct columns::@4 list
Definition: search.h:40

References ast_free, AST_LIST_REMOVE_HEAD, AST_RWLIST_REMOVE_HEAD, columns::list, and table.

Referenced by unload_module().

◆ generic_prepare()

static SQLHSTMT generic_prepare ( struct odbc_obj obj,
void *  data 
)
static

Definition at line 327 of file cdr_adaptive_odbc.c.

328 {
329  int res, i;
330  SQLHSTMT stmt;
331  SQLINTEGER nativeerror = 0, numfields = 0;
332  SQLSMALLINT diagbytes = 0;
333  unsigned char state[10], diagnostic[256];
334 
335  res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
336  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
337  ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
338  return NULL;
339  }
340 
341  res = ast_odbc_prepare(obj, stmt, data);
342  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
343  ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", (char *) data);
344  SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
345  for (i = 0; i < numfields; i++) {
346  SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
347  ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
348  if (i > 10) {
349  ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
350  break;
351  }
352  }
353  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
354  return NULL;
355  }
356 
357  return stmt;
358 }
#define ast_log
Definition: astobj2.c:42
#define LOG_WARNING
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
#define NULL
Definition: resample.c:96
SQLHDBC con
Definition: res_odbc.h:47

References ast_log, ast_odbc_prepare(), odbc_obj::con, LOG_WARNING, and NULL.

Referenced by odbc_log().

◆ load_config()

static int load_config ( void  )
static

Definition at line 92 of file cdr_adaptive_odbc.c.

93 {
94  struct ast_config *cfg;
95  struct ast_variable *var;
96  const char *tmp, *catg;
97  struct tables *tableptr;
98  struct columns *entry;
99  struct odbc_obj *obj;
100  char columnname[80];
101  char connection[40];
102  char table[40];
103  char schema[40];
104  char quoted_identifiers;
105  int lenconnection, lentable, lenschema, usegmtime = 0;
106  SQLLEN sqlptr;
107  int res = 0;
108  SQLHSTMT stmt = NULL;
109  struct ast_flags config_flags = { 0 }; /* Part of our config comes from the database */
110 
111  cfg = ast_config_load(CONFIG, config_flags);
112  if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
113  ast_log(LOG_WARNING, "Unable to load " CONFIG ". No adaptive ODBC CDRs.\n");
114  return -1;
115  }
116 
117  for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
118  var = ast_variable_browse(cfg, catg);
119  if (!var)
120  continue;
121 
122  if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "connection"))) {
123  ast_log(LOG_WARNING, "No connection parameter found in '%s'. Skipping.\n", catg);
124  continue;
125  }
126  ast_copy_string(connection, tmp, sizeof(connection));
127  lenconnection = strlen(connection);
128 
129  if (!ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "usegmtime"))) {
131  }
132 
133  /* When loading, we want to be sure we can connect. */
134  obj = ast_odbc_request_obj(connection, 1);
135  if (!obj) {
136  ast_log(LOG_WARNING, "No such connection '%s' in the '%s' section of " CONFIG ". Check res_odbc.conf.\n", connection, catg);
137  continue;
138  }
139 
140  if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "table"))) {
141  ast_log(LOG_NOTICE, "No table name found. Assuming 'cdr'.\n");
142  tmp = "cdr";
143  }
144  ast_copy_string(table, tmp, sizeof(table));
145  lentable = strlen(table);
146 
147  if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "schema"))) {
148  tmp = "";
149  }
150  ast_copy_string(schema, tmp, sizeof(schema));
151  lenschema = strlen(schema);
152 
153  if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "quoted_identifiers"))) {
154  tmp = "";
155  }
156  quoted_identifiers = tmp[0];
157  if (strlen(tmp) > 1) {
158  ast_log(LOG_ERROR, "The quoted_identifiers setting only accepts a single character,"
159  " while a value of '%s' was provided. This option has been disabled as a result.\n", tmp);
160  quoted_identifiers = '\0';
161  }
162 
163  res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
164  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
165  ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", connection);
167  continue;
168  }
169 
170  res = SQLColumns(stmt, NULL, 0, lenschema == 0 ? NULL : (unsigned char *)schema, SQL_NTS, (unsigned char *)table, SQL_NTS, (unsigned char *)"%", SQL_NTS);
171  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
172  ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'. Skipping.\n", connection);
173  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
175  continue;
176  }
177 
178  tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + lenconnection + 1 + lentable + 1 + lenschema + 1 + 1);
179  if (!tableptr) {
180  ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'%s%s%s\n", table, connection,
181  lenschema ? " (schema '" : "", lenschema ? schema : "", lenschema ? "')" : "");
182  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
184  res = -1;
185  break;
186  }
187 
188  tableptr->usegmtime = usegmtime;
189  tableptr->connection = (char *)tableptr + sizeof(*tableptr);
190  tableptr->table = (char *)tableptr + sizeof(*tableptr) + lenconnection + 1;
191  tableptr->schema = (char *)tableptr + sizeof(*tableptr) + lenconnection + 1 + lentable + 1;
192  ast_copy_string(tableptr->connection, connection, lenconnection + 1);
193  ast_copy_string(tableptr->table, table, lentable + 1);
194  ast_copy_string(tableptr->schema, schema, lenschema + 1);
195  tableptr->quoted_identifiers = quoted_identifiers;
196 
197  ast_verb(3, "Found adaptive CDR table %s@%s.\n", tableptr->table, tableptr->connection);
198 
199  /* Check for filters first */
200  for (var = ast_variable_browse(cfg, catg); var; var = var->next) {
201  if (strncmp(var->name, "filter", 6) == 0) {
202  int negate = 0;
203  char *cdrvar = ast_strdupa(var->name + 6);
204  cdrvar = ast_strip(cdrvar);
205  if (cdrvar[strlen(cdrvar) - 1] == '!') {
206  negate = 1;
207  cdrvar[strlen(cdrvar) - 1] = '\0';
208  ast_trim_blanks(cdrvar);
209  }
210 
211  ast_verb(3, "Found filter %s'%s' for CDR variable %s in %s@%s\n", negate ? "!" : "", var->value, cdrvar, tableptr->table, tableptr->connection);
212 
213  entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(cdrvar) + 1 + strlen(var->value) + 1);
214  if (!entry) {
215  ast_log(LOG_ERROR, "Out of memory creating filter entry for CDR variable '%s' in table '%s' on connection '%s'\n", cdrvar, table, connection);
216  res = -1;
217  break;
218  }
219 
220  /* NULL column entry means this isn't a column in the database */
221  entry->name = NULL;
222  entry->cdrname = (char *)entry + sizeof(*entry);
223  entry->filtervalue = (char *)entry + sizeof(*entry) + strlen(cdrvar) + 1;
224  strcpy(entry->cdrname, cdrvar);
225  strcpy(entry->filtervalue, var->value);
226  entry->negatefiltervalue = negate;
227 
228  AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
229  }
230  }
231 
232  while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) {
233  char *cdrvar = "", *staticvalue = "";
234 
235  SQLGetData(stmt, 4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr);
236 
237  /* Is there an alias for this column? */
238 
239  /* NOTE: This seems like a non-optimal parse method, but I'm going
240  * for user configuration readability, rather than fast parsing. We
241  * really don't parse this file all that often, anyway.
242  */
243  for (var = ast_variable_browse(cfg, catg); var; var = var->next) {
244  if (strncmp(var->name, "alias", 5) == 0 && strcasecmp(var->value, columnname) == 0) {
245  char *alias = ast_strdupa(var->name + 5);
246  cdrvar = ast_strip(alias);
247  ast_verb(3, "Found alias %s for column %s in %s@%s\n", cdrvar, columnname, tableptr->table, tableptr->connection);
248  break;
249  } else if (strncmp(var->name, "static", 6) == 0 && strcasecmp(var->value, columnname) == 0) {
250  char *item = ast_strdupa(var->name + 6);
251  item = ast_strip(item);
252  if (item[0] == '"' && item[strlen(item) - 1] == '"') {
253  /* Remove surrounding quotes */
254  item[strlen(item) - 1] = '\0';
255  item++;
256  }
257  staticvalue = item;
258  }
259  }
260 
261  entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1 + strlen(cdrvar) + 1 + strlen(staticvalue) + 1);
262  if (!entry) {
263  ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, table, connection);
264  res = -1;
265  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
266  break;
267  }
268  entry->name = (char *)entry + sizeof(*entry);
269  strcpy(entry->name, columnname);
270 
271  if (!ast_strlen_zero(cdrvar)) {
272  entry->cdrname = entry->name + strlen(columnname) + 1;
273  strcpy(entry->cdrname, cdrvar);
274  } else { /* Point to same place as the column name */
275  entry->cdrname = (char *)entry + sizeof(*entry);
276  }
277 
278  if (!ast_strlen_zero(staticvalue)) {
279  entry->staticvalue = entry->cdrname + strlen(entry->cdrname) + 1;
280  strcpy(entry->staticvalue, staticvalue);
281  }
282 
283  SQLGetData(stmt, 5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL);
284  SQLGetData(stmt, 7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL);
285  SQLGetData(stmt, 9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL);
286  SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL);
287  SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL);
288  SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL);
289 
290  /* Specification states that the octenlen should be the maximum number of bytes
291  * returned in a char or binary column, but it seems that some drivers just set
292  * it to NULL. (Bad Postgres! No biscuit!) */
293  if (entry->octetlen == 0)
294  entry->octetlen = entry->size;
295 
296  ast_verb(4, "Found %s column with type %hd with len %ld, octetlen %ld, and numlen (%hd,%hd)\n", entry->name, entry->type, (long) entry->size, (long) entry->octetlen, entry->decimals, entry->radix);
297  /* Insert column info into column list */
298  AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
299  res = 0;
300  }
301 
302  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
304 
305  if (AST_LIST_FIRST(&(tableptr->columns)))
306  AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list);
307  else
308  ast_free(tableptr);
309  }
310  ast_config_destroy(cfg);
311  return res;
312 }
#define var
Definition: ast_expr2f.c:614
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
static int tmp()
Definition: bt_open.c:389
#define CONFIG
static int usegmtime
Definition: cdr_csv.c:54
static char * schema
Definition: cel_pgsql.c:68
#define ast_config_load(filename, flags)
Load a config file.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3327
#define CONFIG_STATUS_FILEINVALID
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:768
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1215
#define LOG_ERROR
#define ast_verb(level,...)
#define LOG_NOTICE
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define AST_RWLIST_INSERT_TAIL
Definition: linkedlists.h:741
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:421
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
#define ast_odbc_request_obj(name, check)
Get a ODBC connection object.
Definition: res_odbc.h:120
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: main/utils.c:2097
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
char * ast_trim_blanks(char *str)
Trims trailing whitespace characters from a string.
Definition: strings.h:186
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:406
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223
Structure used to handle boolean flags.
Definition: utils.h:199
Structure for variables, used for configurations and for channel variables.
ODBC container.
Definition: res_odbc.h:46
char * connection
unsigned int usegmtime
char * schema
char * table
char quoted_identifiers
struct tables::mysql_columns columns
static struct aco_type item
Definition: test_config.c:1463

References ast_calloc, ast_category_browse(), ast_config_destroy(), ast_config_load, ast_copy_string(), ast_free, AST_LIST_FIRST, AST_LIST_INSERT_TAIL, ast_log, ast_odbc_release_obj(), ast_odbc_request_obj, AST_RWLIST_INSERT_TAIL, ast_strdupa, ast_strip(), ast_strlen_zero(), ast_trim_blanks(), ast_true(), ast_variable_browse(), ast_variable_retrieve(), ast_verb, tables::columns, odbc_obj::con, CONFIG, CONFIG_STATUS_FILEINVALID, tables::connection, item, LOG_ERROR, LOG_NOTICE, LOG_WARNING, NULL, tables::quoted_identifiers, tables::schema, schema, tables::table, table, tmp(), tables::usegmtime, usegmtime, and var.

Referenced by load_module().

◆ load_module()

static int load_module ( void  )
static

Definition at line 803 of file cdr_adaptive_odbc.c.

804 {
806  ast_log(LOG_ERROR, "Unable to lock column list. Load failed.\n");
807  return 0;
808  }
809 
810  load_config();
813  return 0;
814 }
int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be)
Register a CDR handling engine.
Definition: cdr.c:2965
static const char name[]
static int odbc_log(struct ast_cdr *cdr)
static int load_config(void)
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:52
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:151
const char * description
Definition: module.h:352

References ast_cdr_register(), ast_log, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_module_info::description, load_config(), LOG_ERROR, name, and odbc_log().

◆ odbc_log()

static int odbc_log ( struct ast_cdr cdr)
static

Definition at line 379 of file cdr_adaptive_odbc.c.

380 {
381  struct tables *tableptr;
382  struct columns *entry;
383  struct odbc_obj *obj;
384  struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);
385  char *tmp;
386  char colbuf[1024], *colptr;
387  SQLHSTMT stmt = NULL;
388  SQLLEN rows = 0;
389  char *separator;
390  int quoted = 0;
391 
392  if (!sql || !sql2) {
393  if (sql)
394  ast_free(sql);
395  if (sql2)
396  ast_free(sql2);
397  return -1;
398  }
399 
401  ast_log(LOG_ERROR, "Unable to lock table list. Insert CDR(s) failed.\n");
402  ast_free(sql);
403  ast_free(sql2);
404  return -1;
405  }
406 
407  AST_LIST_TRAVERSE(&odbc_tables, tableptr, list) {
408  separator = "";
409 
410  quoted = 0;
411  if (tableptr->quoted_identifiers != '\0'){
412  quoted = 1;
413  }
414 
415  if (ast_strlen_zero(tableptr->schema)) {
416  if (quoted) {
417  ast_str_set(&sql, 0, "INSERT INTO %c%s%c (",
418  tableptr->quoted_identifiers, tableptr->table, tableptr->quoted_identifiers );
419  }else{
420  ast_str_set(&sql, 0, "INSERT INTO %s (", tableptr->table);
421  }
422  } else {
423  if (quoted) {
424  ast_str_set(&sql, 0, "INSERT INTO %c%s%c.%c%s%c (",
425  tableptr->quoted_identifiers, tableptr->schema, tableptr->quoted_identifiers,
426  tableptr->quoted_identifiers, tableptr->table, tableptr->quoted_identifiers);
427  }else{
428  ast_str_set(&sql, 0, "INSERT INTO %s.%s (", tableptr->schema, tableptr->table);
429  }
430  }
431  ast_str_set(&sql2, 0, " VALUES (");
432 
433  /* No need to check the connection now; we'll handle any failure in prepare_and_execute */
434  if (!(obj = ast_odbc_request_obj(tableptr->connection, 0))) {
435  ast_log(LOG_WARNING, "cdr_adaptive_odbc: Unable to retrieve database handle for '%s:%s'. CDR failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql));
436  continue;
437  }
438 
439  AST_LIST_TRAVERSE(&(tableptr->columns), entry, list) {
440  int datefield = 0;
441  if (strcasecmp(entry->cdrname, "start") == 0) {
442  datefield = 1;
443  } else if (strcasecmp(entry->cdrname, "answer") == 0) {
444  datefield = 2;
445  } else if (strcasecmp(entry->cdrname, "end") == 0) {
446  datefield = 3;
447  }
448 
449  /* Check if we have a similarly named variable */
450  if (entry->staticvalue) {
451  colptr = ast_strdupa(entry->staticvalue);
452  } else if (datefield && tableptr->usegmtime) {
453  struct timeval date_tv = (datefield == 1) ? cdr->start : (datefield == 2) ? cdr->answer : cdr->end;
454  struct ast_tm tm = { 0, };
455  ast_localtime(&date_tv, &tm, "UTC");
456  ast_strftime(colbuf, sizeof(colbuf), "%Y-%m-%d %H:%M:%S", &tm);
457  colptr = colbuf;
458  } else {
459  ast_cdr_format_var(cdr, entry->cdrname, &colptr, colbuf, sizeof(colbuf), datefield ? 0 : 1);
460  }
461 
462  if (colptr) {
463  /* Check first if the column filters this entry. Note that this
464  * is very specifically NOT ast_strlen_zero(), because the filter
465  * could legitimately specify that the field is blank, which is
466  * different from the field being unspecified (NULL). */
467  if ((entry->filtervalue && !entry->negatefiltervalue && strcasecmp(colptr, entry->filtervalue) != 0) ||
468  (entry->filtervalue && entry->negatefiltervalue && strcasecmp(colptr, entry->filtervalue) == 0)) {
469  ast_verb(4, "CDR column '%s' with value '%s' does not match filter of"
470  " %s'%s'. Cancelling this CDR.\n",
471  entry->cdrname, colptr, entry->negatefiltervalue ? "!" : "", entry->filtervalue);
472  goto early_release;
473  }
474 
475  /* Only a filter? */
476  if (ast_strlen_zero(entry->name))
477  continue;
478 
479  LENGTHEN_BUF1(strlen(entry->name));
480 
481  switch (entry->type) {
482  case SQL_CHAR:
483  case SQL_VARCHAR:
484  case SQL_LONGVARCHAR:
485 #ifdef HAVE_ODBC_WCHAR
486  case SQL_WCHAR:
487  case SQL_WVARCHAR:
488  case SQL_WLONGVARCHAR:
489 #endif
490  case SQL_BINARY:
491  case SQL_VARBINARY:
492  case SQL_LONGVARBINARY:
493  case SQL_GUID:
494  /* For these two field names, get the rendered form, instead of the raw
495  * form (but only when we're dealing with a character-based field).
496  */
497  if (strcasecmp(entry->name, "disposition") == 0) {
498  ast_cdr_format_var(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0);
499  } else if (strcasecmp(entry->name, "amaflags") == 0) {
500  ast_cdr_format_var(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0);
501  }
502 
503  /* Truncate too-long fields */
504  if (entry->type != SQL_GUID) {
505  if (strlen(colptr) > entry->octetlen) {
506  colptr[entry->octetlen] = '\0';
507  }
508  }
509 
510  LENGTHEN_BUF2(strlen(colptr));
511 
512  /* Encode value, with escaping */
513  ast_str_append(&sql2, 0, "%s'", separator);
514  for (tmp = colptr; *tmp; tmp++) {
515  if (*tmp == '\'') {
516  ast_str_append(&sql2, 0, "''");
517  } else if (*tmp == '\\' && ast_odbc_backslash_is_escape(obj)) {
518  ast_str_append(&sql2, 0, "\\\\");
519  } else {
520  ast_str_append(&sql2, 0, "%c", *tmp);
521  }
522  }
523  ast_str_append(&sql2, 0, "'");
524  break;
525  case SQL_TYPE_DATE:
526  if (ast_strlen_zero(colptr)) {
527  continue;
528  } else {
529  int year = 0, month = 0, day = 0;
530  if (sscanf(colptr, "%4d-%2d-%2d", &year, &month, &day) != 3 || year <= 0 ||
531  month <= 0 || month > 12 || day < 0 || day > 31 ||
532  ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
533  (month == 2 && year % 400 == 0 && day > 29) ||
534  (month == 2 && year % 100 == 0 && day > 28) ||
535  (month == 2 && year % 4 == 0 && day > 29) ||
536  (month == 2 && year % 4 != 0 && day > 28)) {
537  ast_log(LOG_WARNING, "CDR variable %s is not a valid date ('%s').\n", entry->name, colptr);
538  continue;
539  }
540 
541  if (year > 0 && year < 100) {
542  year += 2000;
543  }
544 
545  LENGTHEN_BUF2(17);
546  ast_str_append(&sql2, 0, "%s{ d '%04d-%02d-%02d' }", separator, year, month, day);
547  }
548  break;
549  case SQL_TYPE_TIME:
550  if (ast_strlen_zero(colptr)) {
551  continue;
552  } else {
553  int hour = 0, minute = 0, second = 0;
554  int count = sscanf(colptr, "%2d:%2d:%2d", &hour, &minute, &second);
555 
556  if ((count != 2 && count != 3) || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) {
557  ast_log(LOG_WARNING, "CDR variable %s is not a valid time ('%s').\n", entry->name, colptr);
558  continue;
559  }
560 
561  LENGTHEN_BUF2(15);
562  ast_str_append(&sql2, 0, "%s{ t '%02d:%02d:%02d' }", separator, hour, minute, second);
563  }
564  break;
565  case SQL_TYPE_TIMESTAMP:
566  case SQL_TIMESTAMP:
567  case SQL_DATETIME:
568  if (ast_strlen_zero(colptr)) {
569  continue;
570  } else {
571  int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
572  int count = sscanf(colptr, "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &minute, &second);
573 
574  if ((count != 3 && count != 5 && count != 6) || year <= 0 ||
575  month <= 0 || month > 12 || day < 0 || day > 31 ||
576  ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
577  (month == 2 && year % 400 == 0 && day > 29) ||
578  (month == 2 && year % 100 == 0 && day > 28) ||
579  (month == 2 && year % 4 == 0 && day > 29) ||
580  (month == 2 && year % 4 != 0 && day > 28) ||
581  hour > 23 || minute > 59 || second > 59 || hour < 0 || minute < 0 || second < 0) {
582  ast_log(LOG_WARNING, "CDR variable %s is not a valid timestamp ('%s').\n", entry->name, colptr);
583  continue;
584  }
585 
586  if (year > 0 && year < 100) {
587  year += 2000;
588  }
589 
590  LENGTHEN_BUF2(26);
591  ast_str_append(&sql2, 0, "%s{ ts '%04d-%02d-%02d %02d:%02d:%02d' }", separator, year, month, day, hour, minute, second);
592  }
593  break;
594  case SQL_INTEGER:
595  if (ast_strlen_zero(colptr)) {
596  continue;
597  } else {
598  int integer = 0;
599  if (sscanf(colptr, "%30d", &integer) != 1) {
600  ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
601  continue;
602  }
603 
604  LENGTHEN_BUF2(12);
605  ast_str_append(&sql2, 0, "%s%d", separator, integer);
606  }
607  break;
608  case SQL_BIGINT:
609  if (ast_strlen_zero(colptr)) {
610  continue;
611  } else {
612  long long integer = 0;
613  if (sscanf(colptr, "%30lld", &integer) != 1) {
614  ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
615  continue;
616  }
617 
618  LENGTHEN_BUF2(24);
619  ast_str_append(&sql2, 0, "%s%lld", separator, integer);
620  }
621  break;
622  case SQL_SMALLINT:
623  if (ast_strlen_zero(colptr)) {
624  continue;
625  } else {
626  short integer = 0;
627  if (sscanf(colptr, "%30hd", &integer) != 1) {
628  ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
629  continue;
630  }
631 
632  LENGTHEN_BUF2(6);
633  ast_str_append(&sql2, 0, "%s%d", separator, integer);
634  }
635  break;
636  case SQL_TINYINT:
637  if (ast_strlen_zero(colptr)) {
638  continue;
639  } else {
640  signed char integer = 0;
641  if (sscanf(colptr, "%30hhd", &integer) != 1) {
642  ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
643  continue;
644  }
645 
646  LENGTHEN_BUF2(4);
647  ast_str_append(&sql2, 0, "%s%d", separator, integer);
648  }
649  break;
650  case SQL_BIT:
651  if (ast_strlen_zero(colptr)) {
652  continue;
653  } else {
654  signed char integer = 0;
655  if (sscanf(colptr, "%30hhd", &integer) != 1) {
656  ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
657  continue;
658  }
659  if (integer != 0)
660  integer = 1;
661 
662  LENGTHEN_BUF2(2);
663  ast_str_append(&sql2, 0, "%s%d", separator, integer);
664  }
665  break;
666  case SQL_NUMERIC:
667  case SQL_DECIMAL:
668  if (ast_strlen_zero(colptr)) {
669  continue;
670  } else {
671  double number = 0.0;
672 
673  if (!strcasecmp(entry->cdrname, "billsec")) {
674  if (!ast_tvzero(cdr->answer)) {
675  snprintf(colbuf, sizeof(colbuf), "%lf",
676  (double) (ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0));
677  } else {
678  ast_copy_string(colbuf, "0", sizeof(colbuf));
679  }
680  } else if (!strcasecmp(entry->cdrname, "duration")) {
681  snprintf(colbuf, sizeof(colbuf), "%lf",
682  (double) (ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0));
683 
684  if (!ast_strlen_zero(colbuf)) {
685  colptr = colbuf;
686  }
687  }
688 
689  if (sscanf(colptr, "%30lf", &number) != 1) {
690  ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
691  continue;
692  }
693 
694  LENGTHEN_BUF2(entry->decimals);
695  ast_str_append(&sql2, 0, "%s%*.*lf", separator, entry->decimals, entry->radix, number);
696  }
697  break;
698  case SQL_FLOAT:
699  case SQL_REAL:
700  case SQL_DOUBLE:
701  if (ast_strlen_zero(colptr)) {
702  continue;
703  } else {
704  double number = 0.0;
705 
706  if (!strcasecmp(entry->cdrname, "billsec")) {
707  if (!ast_tvzero(cdr->answer)) {
708  snprintf(colbuf, sizeof(colbuf), "%lf",
709  (double) (ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0));
710  } else {
711  ast_copy_string(colbuf, "0", sizeof(colbuf));
712  }
713  } else if (!strcasecmp(entry->cdrname, "duration")) {
714  snprintf(colbuf, sizeof(colbuf), "%lf",
715  (double) (ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0));
716 
717  if (!ast_strlen_zero(colbuf)) {
718  colptr = colbuf;
719  }
720  }
721 
722  if (sscanf(colptr, "%30lf", &number) != 1) {
723  ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
724  continue;
725  }
726 
727  LENGTHEN_BUF2(entry->decimals);
728  ast_str_append(&sql2, 0, "%s%lf", separator, number);
729  }
730  break;
731  default:
732  ast_log(LOG_WARNING, "Column type %d (field '%s:%s:%s') is unsupported at this time.\n", entry->type, tableptr->connection, tableptr->table, entry->name);
733  continue;
734  }
735  if (quoted) {
736  ast_str_append(&sql, 0, "%s%c%s%c", separator, tableptr->quoted_identifiers, entry->name, tableptr->quoted_identifiers);
737  } else {
738  ast_str_append(&sql, 0, "%s%s", separator, entry->name);
739  }
740  separator = ", ";
741  } else if (entry->filtervalue
742  && ((!entry->negatefiltervalue && entry->filtervalue[0] != '\0')
743  || (entry->negatefiltervalue && entry->filtervalue[0] == '\0'))) {
744  ast_verb(4, "CDR column '%s' was not set and does not match filter of"
745  " %s'%s'. Cancelling this CDR.\n",
746  entry->cdrname, entry->negatefiltervalue ? "!" : "",
747  entry->filtervalue);
748  goto early_release;
749  }
750  }
751 
752  /* Concatenate the two constructed buffers */
754  ast_str_append(&sql, 0, ")");
755  ast_str_append(&sql2, 0, ")");
756  ast_str_append(&sql, 0, "%s", ast_str_buffer(sql2));
757 
758  ast_debug(3, "Executing [%s]\n", ast_str_buffer(sql));
759 
761  if (stmt) {
762  SQLRowCount(stmt, &rows);
763  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
764  }
765  if (rows == 0) {
766  ast_log(LOG_WARNING, "cdr_adaptive_odbc: Insert failed on '%s:%s'. CDR failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql));
767  }
768 early_release:
770  }
772 
773  /* Next time, just allocate buffers that are that big to start with. */
774  if (ast_str_strlen(sql) > maxsize) {
775  maxsize = ast_str_strlen(sql);
776  }
777  if (ast_str_strlen(sql2) > maxsize2) {
778  maxsize2 = ast_str_strlen(sql2);
779  }
780 
781  ast_free(sql);
782  ast_free(sql2);
783  return 0;
784 }
void ast_cdr_format_var(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int raw)
Format a CDR variable from an already posted CDR.
Definition: cdr.c:3072
#define LENGTHEN_BUF2(size)
static int maxsize2
static int maxsize
static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
#define LENGTHEN_BUF1(size)
#define ast_debug(level,...)
Log a DEBUG message.
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:78
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
Definition: localtime.c:2524
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition: localtime.c:1739
INT32 integer
Definition: lpc10.h:80
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
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
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:1117
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:739
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:640
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:1091
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:711
struct timeval answer
Definition: cdr.h:297
struct timeval start
Definition: cdr.h:295
struct timeval end
Definition: cdr.h:299
Support for dynamic strings.
Definition: strings.h:604
Number structure.
Definition: app_followme.c:154
int64_t ast_tvdiff_us(struct timeval end, struct timeval start)
Computes the difference (in microseconds) between two struct timeval instances.
Definition: time.h:85
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.
Definition: time.h:115

References ast_cdr::answer, ast_cdr_format_var(), ast_copy_string(), ast_debug, ast_free, AST_LIST_TRAVERSE, ast_localtime(), ast_log, ast_odbc_backslash_is_escape(), ast_odbc_prepare_and_execute(), ast_odbc_release_obj(), ast_odbc_request_obj, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK, ast_str_append(), ast_str_buffer(), ast_str_create, ast_str_set(), ast_str_strlen(), ast_strdupa, ast_strftime(), ast_strlen_zero(), ast_tvdiff_us(), ast_tvzero(), ast_verb, tables::columns, tables::connection, ast_cdr::end, generic_prepare(), LENGTHEN_BUF1, LENGTHEN_BUF2, LOG_ERROR, LOG_WARNING, maxsize, maxsize2, NULL, tables::quoted_identifiers, tables::schema, ast_cdr::start, tables::table, tmp(), and tables::usegmtime.

Referenced by load_module(), and unload_module().

◆ reload()

static int reload ( void  )
static

Definition at line 816 of file cdr_adaptive_odbc.c.

817 {
819  ast_log(LOG_ERROR, "Unable to lock column list. Reload failed.\n");
820  return -1;
821  }
822 
823  free_config();
824  load_config();
826  return 0;
827 }
static int free_config(void)

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 786 of file cdr_adaptive_odbc.c.

787 {
788  if (ast_cdr_unregister(name)) {
789  return -1;
790  }
791 
794  ast_log(LOG_ERROR, "Unable to lock column list. Unload failed.\n");
795  return -1;
796  }
797 
798  free_config();
800  return 0;
801 }
int ast_cdr_unregister(const char *name)
Unregister a CDR handling engine.
Definition: cdr.c:3010

References ast_cdr_register(), ast_cdr_unregister(), ast_log, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_module_info::description, free_config(), LOG_ERROR, name, and odbc_log().

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Adaptive ODBC CDR backend" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CDR_DRIVER, .requires = "cdr,res_odbc", }
static

Definition at line 816 of file cdr_adaptive_odbc.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 836 of file cdr_adaptive_odbc.c.

◆ maxsize

int maxsize = 512
static

Definition at line 63 of file cdr_adaptive_odbc.c.

Referenced by ast_func_read2(), and odbc_log().

◆ maxsize2

int maxsize2 = 512
static

Definition at line 63 of file cdr_adaptive_odbc.c.

Referenced by odbc_log().

◆ name

const char name[] = "Adaptive ODBC"
static

Definition at line 61 of file cdr_adaptive_odbc.c.

Referenced by load_module(), and unload_module().

◆ odbc_tables

struct odbc_tables odbc_tables = { .first = NULL, .last = NULL, .lock = { PTHREAD_RWLOCK_INITIALIZER , NULL, {1, 0} } , }
static