Asterisk - The Open Source Telephony Project GIT-master-a358458
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))) {
321 }
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 reload(), and 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);
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 }
311 return res;
312}
#define var
Definition: ast_expr2f.c:605
#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:3326
#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:783
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: utils.c:2199
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:425
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(), and reload().

◆ 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:3005
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 }
768early_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:3112
#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
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
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
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:1139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:730
struct timeval answer
Definition: cdr.h:299
struct timeval start
Definition: cdr.h:297
struct timeval end
Definition: cdr.h:301
Support for dynamic strings.
Definition: strings.h:623
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:87
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.
Definition: time.h:117

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)

References ast_log, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, free_config(), load_config(), and LOG_ERROR.

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 786 of file cdr_adaptive_odbc.c.

787{
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:3050

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 836 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