Asterisk - The Open Source Telephony Project  18.5.0
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 = "30ef0c93b36035ec78c9cfd712d36d9b" , .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.

Referenced by load_config().

◆ 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.

Referenced by odbc_log().

◆ LENGTHEN_BUF2

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

Definition at line 376 of file cdr_adaptive_odbc.c.

Referenced by odbc_log().

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 835 of file cdr_adaptive_odbc.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 835 of file cdr_adaptive_odbc.c.

◆ AST_MODULE_SELF_SYM()

struct ast_module* AST_MODULE_SELF_SYM ( void  )

Definition at line 835 of file cdr_adaptive_odbc.c.

◆ free_config()

static int free_config ( void  )
static

Definition at line 314 of file cdr_adaptive_odbc.c.

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

Referenced by reload(), and unload_module().

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

◆ generic_prepare()

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

Definition at line 327 of file cdr_adaptive_odbc.c.

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

Referenced by free_zone(), and odbc_log().

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 }
SQLHDBC con
Definition: res_odbc.h:47
#define LOG_WARNING
Definition: logger.h:274
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 NULL
Definition: resample.c:96
#define ast_log
Definition: astobj2.c:42

◆ load_config()

static int load_config ( void  )
static

Definition at line 92 of file cdr_adaptive_odbc.c.

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, columns::cdrname, tables::columns, odbc_obj::con, CONFIG, CONFIG_STATUS_FILEINVALID, tables::connection, columns::decimals, columns::filtervalue, item, columns::list, LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_variable::name, columns::name, columns::negatefiltervalue, ast_variable::next, NULL, columns::nullable, columns::octetlen, tables::quoted_identifiers, columns::radix, schema, tables::schema, columns::size, columns::staticvalue, table, tables::table, tmp(), columns::type, usegmtime, tables::usegmtime, ast_variable::value, and var.

Referenced by load_module(), and reload().

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"))) {
130  usegmtime = ast_true(tmp);
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 }
SQLHDBC con
Definition: res_odbc.h:47
struct ast_variable * next
SQLSMALLINT radix
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:420
char * filtervalue
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1216
#define LOG_WARNING
Definition: logger.h:274
#define CONFIG_STATUS_FILEINVALID
SQLSMALLINT nullable
static int tmp()
Definition: bt_open.c:389
Structure for variables, used for configurations and for channel variables.
#define var
Definition: ast_expr2f.c:614
static struct aco_type item
Definition: test_config.c:1463
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3328
SQLINTEGER octetlen
#define NULL
Definition: resample.c:96
#define ast_verb(level,...)
Definition: logger.h:463
#define ast_strlen_zero(foo)
Definition: strings.h:52
static char * table
Definition: cdr_odbc.c:58
#define ast_log
Definition: astobj2.c:42
SQLSMALLINT decimals
#define ast_config_load(filename, flags)
Load a config file.
SQLINTEGER size
ODBC container.
Definition: res_odbc.h:46
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:219
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: extconf.c:1290
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
char * cdrname
char * connection
#define LOG_ERROR
Definition: logger.h:285
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:730
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: main/utils.c:1951
char * schema
#define LOG_NOTICE
Definition: logger.h:263
char * ast_trim_blanks(char *str)
Trims trailing whitespace characters from a string.
Definition: strings.h:182
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
#define CONFIG
#define ast_odbc_request_obj(a, b)
Definition: res_odbc.h:122
Structure used to handle boolean flags.
Definition: utils.h:199
char * table
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:694
#define AST_RWLIST_INSERT_TAIL
Definition: linkedlists.h:740
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
struct tables::mysql_columns columns
char * staticvalue
Definition: search.h:40
static char * schema
Definition: cel_pgsql.c:72
char quoted_identifiers
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
unsigned int negatefiltervalue
static int usegmtime
Definition: cdr_csv.c:54
unsigned int usegmtime

◆ load_module()

static int load_module ( void  )
static

Definition at line 802 of file cdr_adaptive_odbc.c.

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

Referenced by reload().

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

◆ odbc_log()

static int odbc_log ( struct ast_cdr cdr)
static

Definition at line 379 of file cdr_adaptive_odbc.c.

References ast_cdr::answer, ast_cdr_format_var(), ast_copy_string(), ast_debug, ast_free, AST_LIST_TRAVERSE, ast_localtime(), ast_log, AST_LOG_WARNING, 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, columns::cdrname, tables::columns, tables::connection, columns::decimals, ast_cdr::end, columns::filtervalue, generic_prepare(), LENGTHEN_BUF1, LENGTHEN_BUF2, columns::list, LOG_ERROR, LOG_WARNING, maxsize, maxsize2, columns::name, columns::negatefiltervalue, NULL, columns::octetlen, tables::quoted_identifiers, columns::radix, tables::schema, ast_cdr::start, columns::staticvalue, tables::table, tmp(), columns::type, and tables::usegmtime.

Referenced by load_module(), and unload_module().

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  if (ast_strlen_zero(colptr)) {
568  continue;
569  } else {
570  int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
571  int count = sscanf(colptr, "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &minute, &second);
572 
573  if ((count != 3 && count != 5 && count != 6) || year <= 0 ||
574  month <= 0 || month > 12 || day < 0 || day > 31 ||
575  ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
576  (month == 2 && year % 400 == 0 && day > 29) ||
577  (month == 2 && year % 100 == 0 && day > 28) ||
578  (month == 2 && year % 4 == 0 && day > 29) ||
579  (month == 2 && year % 4 != 0 && day > 28) ||
580  hour > 23 || minute > 59 || second > 59 || hour < 0 || minute < 0 || second < 0) {
581  ast_log(LOG_WARNING, "CDR variable %s is not a valid timestamp ('%s').\n", entry->name, colptr);
582  continue;
583  }
584 
585  if (year > 0 && year < 100) {
586  year += 2000;
587  }
588 
589  LENGTHEN_BUF2(26);
590  ast_str_append(&sql2, 0, "%s{ ts '%04d-%02d-%02d %02d:%02d:%02d' }", separator, year, month, day, hour, minute, second);
591  }
592  break;
593  case SQL_INTEGER:
594  if (ast_strlen_zero(colptr)) {
595  continue;
596  } else {
597  int integer = 0;
598  if (sscanf(colptr, "%30d", &integer) != 1) {
599  ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
600  continue;
601  }
602 
603  LENGTHEN_BUF2(12);
604  ast_str_append(&sql2, 0, "%s%d", separator, integer);
605  }
606  break;
607  case SQL_BIGINT:
608  if (ast_strlen_zero(colptr)) {
609  continue;
610  } else {
611  long long integer = 0;
612  if (sscanf(colptr, "%30lld", &integer) != 1) {
613  ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
614  continue;
615  }
616 
617  LENGTHEN_BUF2(24);
618  ast_str_append(&sql2, 0, "%s%lld", separator, integer);
619  }
620  break;
621  case SQL_SMALLINT:
622  if (ast_strlen_zero(colptr)) {
623  continue;
624  } else {
625  short integer = 0;
626  if (sscanf(colptr, "%30hd", &integer) != 1) {
627  ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
628  continue;
629  }
630 
631  LENGTHEN_BUF2(6);
632  ast_str_append(&sql2, 0, "%s%d", separator, integer);
633  }
634  break;
635  case SQL_TINYINT:
636  if (ast_strlen_zero(colptr)) {
637  continue;
638  } else {
639  signed char integer = 0;
640  if (sscanf(colptr, "%30hhd", &integer) != 1) {
641  ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
642  continue;
643  }
644 
645  LENGTHEN_BUF2(4);
646  ast_str_append(&sql2, 0, "%s%d", separator, integer);
647  }
648  break;
649  case SQL_BIT:
650  if (ast_strlen_zero(colptr)) {
651  continue;
652  } else {
653  signed char integer = 0;
654  if (sscanf(colptr, "%30hhd", &integer) != 1) {
655  ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
656  continue;
657  }
658  if (integer != 0)
659  integer = 1;
660 
661  LENGTHEN_BUF2(2);
662  ast_str_append(&sql2, 0, "%s%d", separator, integer);
663  }
664  break;
665  case SQL_NUMERIC:
666  case SQL_DECIMAL:
667  if (ast_strlen_zero(colptr)) {
668  continue;
669  } else {
670  double number = 0.0;
671 
672  if (!strcasecmp(entry->cdrname, "billsec")) {
673  if (!ast_tvzero(cdr->answer)) {
674  snprintf(colbuf, sizeof(colbuf), "%lf",
675  (double) (ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0));
676  } else {
677  ast_copy_string(colbuf, "0", sizeof(colbuf));
678  }
679  } else if (!strcasecmp(entry->cdrname, "duration")) {
680  snprintf(colbuf, sizeof(colbuf), "%lf",
681  (double) (ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0));
682 
683  if (!ast_strlen_zero(colbuf)) {
684  colptr = colbuf;
685  }
686  }
687 
688  if (sscanf(colptr, "%30lf", &number) != 1) {
689  ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
690  continue;
691  }
692 
693  LENGTHEN_BUF2(entry->decimals);
694  ast_str_append(&sql2, 0, "%s%*.*lf", separator, entry->decimals, entry->radix, number);
695  }
696  break;
697  case SQL_FLOAT:
698  case SQL_REAL:
699  case SQL_DOUBLE:
700  if (ast_strlen_zero(colptr)) {
701  continue;
702  } else {
703  double number = 0.0;
704 
705  if (!strcasecmp(entry->cdrname, "billsec")) {
706  if (!ast_tvzero(cdr->answer)) {
707  snprintf(colbuf, sizeof(colbuf), "%lf",
708  (double) (ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0));
709  } else {
710  ast_copy_string(colbuf, "0", sizeof(colbuf));
711  }
712  } else if (!strcasecmp(entry->cdrname, "duration")) {
713  snprintf(colbuf, sizeof(colbuf), "%lf",
714  (double) (ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0));
715 
716  if (!ast_strlen_zero(colbuf)) {
717  colptr = colbuf;
718  }
719  }
720 
721  if (sscanf(colptr, "%30lf", &number) != 1) {
722  ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
723  continue;
724  }
725 
726  LENGTHEN_BUF2(entry->decimals);
727  ast_str_append(&sql2, 0, "%s%lf", separator, number);
728  }
729  break;
730  default:
731  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);
732  continue;
733  }
734  if (quoted) {
735  ast_str_append(&sql, 0, "%s%c%s%c", separator, tableptr->quoted_identifiers, entry->name, tableptr->quoted_identifiers);
736  } else {
737  ast_str_append(&sql, 0, "%s%s", separator, entry->name);
738  }
739  separator = ", ";
740  } else if (entry->filtervalue
741  && ((!entry->negatefiltervalue && entry->filtervalue[0] != '\0')
742  || (entry->negatefiltervalue && entry->filtervalue[0] == '\0'))) {
743  ast_log(AST_LOG_WARNING, "CDR column '%s' was not set and does not match filter of"
744  " %s'%s'. Cancelling this CDR.\n",
745  entry->cdrname, entry->negatefiltervalue ? "!" : "",
746  entry->filtervalue);
747  goto early_release;
748  }
749  }
750 
751  /* Concatenate the two constructed buffers */
753  ast_str_append(&sql, 0, ")");
754  ast_str_append(&sql2, 0, ")");
755  ast_str_append(&sql, 0, "%s", ast_str_buffer(sql2));
756 
757  ast_debug(3, "Executing [%s]\n", ast_str_buffer(sql));
758 
760  if (stmt) {
761  SQLRowCount(stmt, &rows);
762  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
763  }
764  if (rows == 0) {
765  ast_log(LOG_WARNING, "cdr_adaptive_odbc: Insert failed on '%s:%s'. CDR failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql));
766  }
767 early_release:
769  }
771 
772  /* Next time, just allocate buffers that are that big to start with. */
773  if (ast_str_strlen(sql) > maxsize) {
774  maxsize = ast_str_strlen(sql);
775  }
776  if (ast_str_strlen(sql2) > maxsize2) {
777  maxsize2 = ast_str_strlen(sql2);
778  }
779 
780  ast_free(sql);
781  ast_free(sql2);
782  return 0;
783 }
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
SQLSMALLINT radix
char * filtervalue
#define LOG_WARNING
Definition: logger.h:274
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
static int tmp()
Definition: bt_open.c:389
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:150
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
#define AST_LOG_WARNING
Definition: logger.h:279
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.
Definition: time.h:108
#define LENGTHEN_BUF2(size)
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
SQLINTEGER octetlen
#define NULL
Definition: resample.c:96
#define ast_verb(level,...)
Definition: logger.h:463
#define ast_strlen_zero(foo)
Definition: strings.h:52
Number structure.
Definition: app_followme.c:154
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:3050
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
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:77
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
SQLSMALLINT decimals
static int maxsize2
ODBC container.
Definition: res_odbc.h:46
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
char * cdrname
char * connection
struct timeval answer
Definition: cdr.h:296
#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
char * schema
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
struct timeval start
Definition: cdr.h:294
#define ast_free(a)
Definition: astmm.h:182
#define ast_odbc_request_obj(a, b)
Definition: res_odbc.h:122
static int maxsize
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
static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
char * table
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:688
struct timeval end
Definition: cdr.h:298
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
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
struct tables::mysql_columns columns
char * staticvalue
Definition: search.h:40
int64_t ast_tvdiff_us(struct timeval end, struct timeval start)
Computes the difference (in microseconds) between two struct timeval instances.
Definition: time.h:78
#define LENGTHEN_BUF1(size)
char quoted_identifiers
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
unsigned int negatefiltervalue
INT32 integer
Definition: lpc10.h:80
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620
unsigned int usegmtime

◆ reload()

static int reload ( void  )
static

Definition at line 815 of file cdr_adaptive_odbc.c.

References ast_log, AST_MODFLAG_LOAD_ORDER, AST_MODPRI_CDR_DRIVER, AST_MODULE_INFO(), AST_MODULE_SUPPORT_CORE, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ASTERISK_GPL_KEY, free_config(), load_config(), load_module(), LOG_ERROR, and unload_module().

816 {
818  ast_log(LOG_ERROR, "Unable to lock column list. Reload failed.\n");
819  return -1;
820  }
821 
822  free_config();
823  load_config();
825  return 0;
826 }
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:51
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:150
#define ast_log
Definition: astobj2.c:42
#define LOG_ERROR
Definition: logger.h:285
static int free_config(void)
static int load_config(void)

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 785 of file cdr_adaptive_odbc.c.

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

Referenced by reload().

786 {
787  if (ast_cdr_unregister(name)) {
788  return -1;
789  }
790 
793  ast_log(LOG_ERROR, "Unable to lock column list. Unload failed.\n");
794  return -1;
795  }
796 
797  free_config();
799  return 0;
800 }
const char * description
Definition: module.h:352
int ast_cdr_unregister(const char *name)
Unregister a CDR handling engine.
Definition: cdr.c:2988
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:51
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:150
#define ast_log
Definition: astobj2.c:42
int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be)
Register a CDR handling engine.
Definition: cdr.c:2943
#define LOG_ERROR
Definition: logger.h:285
static int free_config(void)
static const char name[]
static int odbc_log(struct ast_cdr *cdr)

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 = "30ef0c93b36035ec78c9cfd712d36d9b" , .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 835 of file cdr_adaptive_odbc.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 835 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.

◆ odbc_tables

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