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"
Go to the source code of this file.
Data Structures | |
struct | columns |
struct | tables |
Defines | |
#define | CONFIG "cdr_adaptive_odbc.conf" |
#define | LENGTHEN_BUF1(size) |
#define | LENGTHEN_BUF2(size) |
Functions | |
AST_MODULE_INFO (ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER,"Adaptive ODBC CDR backend",.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CDR_DRIVER,) | |
static | AST_RWLIST_HEAD_STATIC (odbc_tables, tables) |
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 int | maxsize = 512 |
static int | maxsize2 = 512 |
static const char | name [] = "Adaptive ODBC" |
Adaptive ODBC CDR backend.
Definition in file cdr_adaptive_odbc.c.
#define CONFIG "cdr_adaptive_odbc.conf" |
Definition at line 51 of file cdr_adaptive_odbc.c.
Referenced by load_config().
#define LENGTHEN_BUF1 | ( | size | ) |
Definition at line 316 of file cdr_adaptive_odbc.c.
Referenced by odbc_log(), and pgsql_log().
#define LENGTHEN_BUF2 | ( | size | ) |
Definition at line 330 of file cdr_adaptive_odbc.c.
Referenced by odbc_log(), and pgsql_log().
AST_MODULE_INFO | ( | ASTERISK_GPL_KEY | , | |
AST_MODFLAG_LOAD_ORDER | , | |||
"Adaptive ODBC CDR backend" | , | |||
. | load = load_module , |
|||
. | unload = unload_module , |
|||
. | reload = reload , |
|||
. | load_pri = AST_MODPRI_CDR_DRIVER | |||
) |
static AST_RWLIST_HEAD_STATIC | ( | odbc_tables | , | |
tables | ||||
) | [static] |
static int free_config | ( | void | ) | [static] |
Definition at line 270 of file cdr_adaptive_odbc.c.
References ast_free, AST_LIST_REMOVE_HEAD, AST_RWLIST_REMOVE_HEAD, and tables::columns.
Referenced by reload(), and unload_module().
00271 { 00272 struct tables *table; 00273 struct columns *entry; 00274 while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) { 00275 while ((entry = AST_LIST_REMOVE_HEAD(&(table->columns), list))) { 00276 ast_free(entry); 00277 } 00278 ast_free(table); 00279 } 00280 return 0; 00281 }
static SQLHSTMT generic_prepare | ( | struct odbc_obj * | obj, | |
void * | data | |||
) | [static] |
Definition at line 283 of file cdr_adaptive_odbc.c.
References ast_log(), odbc_obj::con, and LOG_WARNING.
Referenced by odbc_log().
00284 { 00285 int res, i; 00286 SQLHSTMT stmt; 00287 SQLINTEGER nativeerror = 0, numfields = 0; 00288 SQLSMALLINT diagbytes = 0; 00289 unsigned char state[10], diagnostic[256]; 00290 00291 res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt); 00292 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00293 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n"); 00294 return NULL; 00295 } 00296 00297 res = SQLPrepare(stmt, (unsigned char *) data, SQL_NTS); 00298 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00299 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", (char *) data); 00300 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 00301 for (i = 0; i < numfields; i++) { 00302 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 00303 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes); 00304 if (i > 10) { 00305 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 00306 break; 00307 } 00308 } 00309 SQLFreeHandle (SQL_HANDLE_STMT, stmt); 00310 return NULL; 00311 } 00312 00313 return stmt; 00314 }
static int load_config | ( | void | ) | [static] |
Definition at line 81 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_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, LOG_ERROR, LOG_NOTICE, LOG_WARNING, columns::name, ast_variable::name, ast_variable::next, columns::nullable, columns::octetlen, columns::radix, columns::size, columns::staticvalue, tables::table, columns::type, tables::usegmtime, usegmtime, ast_variable::value, and var.
Referenced by load_module(), and reload().
00082 { 00083 struct ast_config *cfg; 00084 struct ast_variable *var; 00085 const char *tmp, *catg; 00086 struct tables *tableptr; 00087 struct columns *entry; 00088 struct odbc_obj *obj; 00089 char columnname[80]; 00090 char connection[40]; 00091 char table[40]; 00092 int lenconnection, lentable, usegmtime = 0; 00093 SQLLEN sqlptr; 00094 int res = 0; 00095 SQLHSTMT stmt = NULL; 00096 struct ast_flags config_flags = { 0 }; /* Part of our config comes from the database */ 00097 00098 cfg = ast_config_load(CONFIG, config_flags); 00099 if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) { 00100 ast_log(LOG_WARNING, "Unable to load " CONFIG ". No adaptive ODBC CDRs.\n"); 00101 return -1; 00102 } 00103 00104 for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) { 00105 var = ast_variable_browse(cfg, catg); 00106 if (!var) 00107 continue; 00108 00109 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "connection"))) { 00110 ast_log(LOG_WARNING, "No connection parameter found in '%s'. Skipping.\n", catg); 00111 continue; 00112 } 00113 ast_copy_string(connection, tmp, sizeof(connection)); 00114 lenconnection = strlen(connection); 00115 00116 if (!ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "usegmtime"))) { 00117 usegmtime = ast_true(tmp); 00118 } 00119 00120 /* When loading, we want to be sure we can connect. */ 00121 obj = ast_odbc_request_obj(connection, 1); 00122 if (!obj) { 00123 ast_log(LOG_WARNING, "No such connection '%s' in the '%s' section of " CONFIG ". Check res_odbc.conf.\n", connection, catg); 00124 continue; 00125 } 00126 00127 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "table"))) { 00128 ast_log(LOG_NOTICE, "No table name found. Assuming 'cdr'.\n"); 00129 tmp = "cdr"; 00130 } 00131 ast_copy_string(table, tmp, sizeof(table)); 00132 lentable = strlen(table); 00133 00134 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt); 00135 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00136 ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", connection); 00137 ast_odbc_release_obj(obj); 00138 continue; 00139 } 00140 00141 res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)table, SQL_NTS, (unsigned char *)"%", SQL_NTS); 00142 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00143 ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'. Skipping.\n", connection); 00144 ast_odbc_release_obj(obj); 00145 continue; 00146 } 00147 00148 tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + lenconnection + 1 + lentable + 1); 00149 if (!tableptr) { 00150 ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", table, connection); 00151 ast_odbc_release_obj(obj); 00152 res = -1; 00153 break; 00154 } 00155 00156 tableptr->usegmtime = usegmtime; 00157 tableptr->connection = (char *)tableptr + sizeof(*tableptr); 00158 tableptr->table = (char *)tableptr + sizeof(*tableptr) + lenconnection + 1; 00159 ast_copy_string(tableptr->connection, connection, lenconnection + 1); 00160 ast_copy_string(tableptr->table, table, lentable + 1); 00161 00162 ast_verb(3, "Found adaptive CDR table %s@%s.\n", tableptr->table, tableptr->connection); 00163 00164 /* Check for filters first */ 00165 for (var = ast_variable_browse(cfg, catg); var; var = var->next) { 00166 if (strncmp(var->name, "filter", 6) == 0) { 00167 char *cdrvar = ast_strdupa(var->name + 6); 00168 cdrvar = ast_strip(cdrvar); 00169 ast_verb(3, "Found filter %s for cdr variable %s in %s@%s\n", var->value, cdrvar, tableptr->table, tableptr->connection); 00170 00171 entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(cdrvar) + 1 + strlen(var->value) + 1); 00172 if (!entry) { 00173 ast_log(LOG_ERROR, "Out of memory creating filter entry for CDR variable '%s' in table '%s' on connection '%s'\n", cdrvar, table, connection); 00174 res = -1; 00175 break; 00176 } 00177 00178 /* NULL column entry means this isn't a column in the database */ 00179 entry->name = NULL; 00180 entry->cdrname = (char *)entry + sizeof(*entry); 00181 entry->filtervalue = (char *)entry + sizeof(*entry) + strlen(cdrvar) + 1; 00182 strcpy(entry->cdrname, cdrvar); 00183 strcpy(entry->filtervalue, var->value); 00184 00185 AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list); 00186 } 00187 } 00188 00189 while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) { 00190 char *cdrvar = "", *staticvalue = ""; 00191 00192 SQLGetData(stmt, 4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr); 00193 00194 /* Is there an alias for this column? */ 00195 00196 /* NOTE: This seems like a non-optimal parse method, but I'm going 00197 * for user configuration readability, rather than fast parsing. We 00198 * really don't parse this file all that often, anyway. 00199 */ 00200 for (var = ast_variable_browse(cfg, catg); var; var = var->next) { 00201 if (strncmp(var->name, "alias", 5) == 0 && strcasecmp(var->value, columnname) == 0) { 00202 char *alias = ast_strdupa(var->name + 5); 00203 cdrvar = ast_strip(alias); 00204 ast_verb(3, "Found alias %s for column %s in %s@%s\n", cdrvar, columnname, tableptr->table, tableptr->connection); 00205 break; 00206 } else if (strncmp(var->name, "static", 6) == 0 && strcasecmp(var->value, columnname) == 0) { 00207 char *item = ast_strdupa(var->name + 6); 00208 item = ast_strip(item); 00209 if (item[0] == '"' && item[strlen(item) - 1] == '"') { 00210 /* Remove surrounding quotes */ 00211 item[strlen(item) - 1] = '\0'; 00212 item++; 00213 } 00214 staticvalue = item; 00215 } 00216 } 00217 00218 entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1 + strlen(cdrvar) + 1 + strlen(staticvalue) + 1); 00219 if (!entry) { 00220 ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, table, connection); 00221 res = -1; 00222 break; 00223 } 00224 entry->name = (char *)entry + sizeof(*entry); 00225 strcpy(entry->name, columnname); 00226 00227 if (!ast_strlen_zero(cdrvar)) { 00228 entry->cdrname = entry->name + strlen(columnname) + 1; 00229 strcpy(entry->cdrname, cdrvar); 00230 } else { /* Point to same place as the column name */ 00231 entry->cdrname = (char *)entry + sizeof(*entry); 00232 } 00233 00234 if (!ast_strlen_zero(staticvalue)) { 00235 entry->staticvalue = entry->cdrname + strlen(entry->cdrname) + 1; 00236 strcpy(entry->staticvalue, staticvalue); 00237 } 00238 00239 SQLGetData(stmt, 5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL); 00240 SQLGetData(stmt, 7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL); 00241 SQLGetData(stmt, 9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL); 00242 SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL); 00243 SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL); 00244 SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL); 00245 00246 /* Specification states that the octenlen should be the maximum number of bytes 00247 * returned in a char or binary column, but it seems that some drivers just set 00248 * it to NULL. (Bad Postgres! No biscuit!) */ 00249 if (entry->octetlen == 0) 00250 entry->octetlen = entry->size; 00251 00252 ast_verb(10, "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); 00253 /* Insert column info into column list */ 00254 AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list); 00255 res = 0; 00256 } 00257 00258 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 00259 ast_odbc_release_obj(obj); 00260 00261 if (AST_LIST_FIRST(&(tableptr->columns))) 00262 AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list); 00263 else 00264 ast_free(tableptr); 00265 } 00266 ast_config_destroy(cfg); 00267 return res; 00268 }
static int load_module | ( | void | ) | [static] |
Definition at line 737 of file cdr_adaptive_odbc.c.
References ast_cdr_register(), ast_log(), AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, load_config(), LOG_ERROR, and odbc_log().
00738 { 00739 if (AST_RWLIST_WRLOCK(&odbc_tables)) { 00740 ast_log(LOG_ERROR, "Unable to lock column list. Load failed.\n"); 00741 return 0; 00742 } 00743 00744 load_config(); 00745 AST_RWLIST_UNLOCK(&odbc_tables); 00746 ast_cdr_register(name, ast_module_info->description, odbc_log); 00747 return 0; 00748 }
static int odbc_log | ( | struct ast_cdr * | cdr | ) | [static] |
Definition at line 343 of file cdr_adaptive_odbc.c.
References ast_cdr::answer, ast_cdr_getvar(), ast_copy_string(), 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, columns::cdrname, tables::columns, tables::connection, columns::decimals, ast_cdr::end, columns::filtervalue, first, generic_prepare(), LENGTHEN_BUF1, LENGTHEN_BUF2, LOG_ERROR, LOG_WARNING, maxsize2, columns::name, columns::octetlen, columns::radix, ast_cdr::start, columns::staticvalue, tables::table, columns::type, and tables::usegmtime.
Referenced by load_module(), and unload_module().
00344 { 00345 struct tables *tableptr; 00346 struct columns *entry; 00347 struct odbc_obj *obj; 00348 struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2); 00349 char *tmp; 00350 char colbuf[1024], *colptr; 00351 SQLHSTMT stmt = NULL; 00352 SQLLEN rows = 0; 00353 00354 if (!sql || !sql2) { 00355 if (sql) 00356 ast_free(sql); 00357 if (sql2) 00358 ast_free(sql2); 00359 return -1; 00360 } 00361 00362 if (AST_RWLIST_RDLOCK(&odbc_tables)) { 00363 ast_log(LOG_ERROR, "Unable to lock table list. Insert CDR(s) failed.\n"); 00364 ast_free(sql); 00365 ast_free(sql2); 00366 return -1; 00367 } 00368 00369 AST_LIST_TRAVERSE(&odbc_tables, tableptr, list) { 00370 int first = 1; 00371 ast_str_set(&sql, 0, "INSERT INTO %s (", tableptr->table); 00372 ast_str_set(&sql2, 0, " VALUES ("); 00373 00374 /* No need to check the connection now; we'll handle any failure in prepare_and_execute */ 00375 if (!(obj = ast_odbc_request_obj(tableptr->connection, 0))) { 00376 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)); 00377 continue; 00378 } 00379 00380 AST_LIST_TRAVERSE(&(tableptr->columns), entry, list) { 00381 int datefield = 0; 00382 if (strcasecmp(entry->cdrname, "start") == 0) { 00383 datefield = 1; 00384 } else if (strcasecmp(entry->cdrname, "answer") == 0) { 00385 datefield = 2; 00386 } else if (strcasecmp(entry->cdrname, "end") == 0) { 00387 datefield = 3; 00388 } 00389 00390 /* Check if we have a similarly named variable */ 00391 if (entry->staticvalue) { 00392 colptr = ast_strdupa(entry->staticvalue); 00393 } else if (datefield && tableptr->usegmtime) { 00394 struct timeval date_tv = (datefield == 1) ? cdr->start : (datefield == 2) ? cdr->answer : cdr->end; 00395 struct ast_tm tm = { 0, }; 00396 ast_localtime(&date_tv, &tm, "UTC"); 00397 ast_strftime(colbuf, sizeof(colbuf), "%Y-%m-%d %H:%M:%S", &tm); 00398 colptr = colbuf; 00399 } else { 00400 ast_cdr_getvar(cdr, entry->cdrname, &colptr, colbuf, sizeof(colbuf), 0, datefield ? 0 : 1); 00401 } 00402 00403 if (colptr) { 00404 /* Check first if the column filters this entry. Note that this 00405 * is very specifically NOT ast_strlen_zero(), because the filter 00406 * could legitimately specify that the field is blank, which is 00407 * different from the field being unspecified (NULL). */ 00408 if (entry->filtervalue && strcasecmp(colptr, entry->filtervalue) != 0) { 00409 ast_verb(4, "CDR column '%s' with value '%s' does not match filter of" 00410 " '%s'. Cancelling this CDR.\n", 00411 entry->cdrname, colptr, entry->filtervalue); 00412 goto early_release; 00413 } 00414 00415 /* Only a filter? */ 00416 if (ast_strlen_zero(entry->name)) 00417 continue; 00418 00419 LENGTHEN_BUF1(strlen(entry->name)); 00420 00421 switch (entry->type) { 00422 case SQL_CHAR: 00423 case SQL_VARCHAR: 00424 case SQL_LONGVARCHAR: 00425 #ifdef HAVE_ODBC_WCHAR 00426 case SQL_WCHAR: 00427 case SQL_WVARCHAR: 00428 case SQL_WLONGVARCHAR: 00429 #endif 00430 case SQL_BINARY: 00431 case SQL_VARBINARY: 00432 case SQL_LONGVARBINARY: 00433 case SQL_GUID: 00434 /* For these two field names, get the rendered form, instead of the raw 00435 * form (but only when we're dealing with a character-based field). 00436 */ 00437 if (strcasecmp(entry->name, "disposition") == 0) { 00438 ast_cdr_getvar(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0, 0); 00439 } else if (strcasecmp(entry->name, "amaflags") == 0) { 00440 ast_cdr_getvar(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0, 0); 00441 } 00442 00443 /* Truncate too-long fields */ 00444 if (entry->type != SQL_GUID) { 00445 if (strlen(colptr) > entry->octetlen) { 00446 colptr[entry->octetlen] = '\0'; 00447 } 00448 } 00449 00450 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); 00451 LENGTHEN_BUF2(strlen(colptr)); 00452 00453 /* Encode value, with escaping */ 00454 ast_str_append(&sql2, 0, "%s'", first ? "" : ","); 00455 for (tmp = colptr; *tmp; tmp++) { 00456 if (*tmp == '\'') { 00457 ast_str_append(&sql2, 0, "''"); 00458 } else if (*tmp == '\\' && ast_odbc_backslash_is_escape(obj)) { 00459 ast_str_append(&sql2, 0, "\\\\"); 00460 } else { 00461 ast_str_append(&sql2, 0, "%c", *tmp); 00462 } 00463 } 00464 ast_str_append(&sql2, 0, "'"); 00465 break; 00466 case SQL_TYPE_DATE: 00467 if (ast_strlen_zero(colptr)) { 00468 continue; 00469 } else { 00470 int year = 0, month = 0, day = 0; 00471 if (sscanf(colptr, "%4d-%2d-%2d", &year, &month, &day) != 3 || year <= 0 || 00472 month <= 0 || month > 12 || day < 0 || day > 31 || 00473 ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) || 00474 (month == 2 && year % 400 == 0 && day > 29) || 00475 (month == 2 && year % 100 == 0 && day > 28) || 00476 (month == 2 && year % 4 == 0 && day > 29) || 00477 (month == 2 && year % 4 != 0 && day > 28)) { 00478 ast_log(LOG_WARNING, "CDR variable %s is not a valid date ('%s').\n", entry->name, colptr); 00479 continue; 00480 } 00481 00482 if (year > 0 && year < 100) { 00483 year += 2000; 00484 } 00485 00486 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); 00487 LENGTHEN_BUF2(17); 00488 ast_str_append(&sql2, 0, "%s{ d '%04d-%02d-%02d' }", first ? "" : ",", year, month, day); 00489 } 00490 break; 00491 case SQL_TYPE_TIME: 00492 if (ast_strlen_zero(colptr)) { 00493 continue; 00494 } else { 00495 int hour = 0, minute = 0, second = 0; 00496 int count = sscanf(colptr, "%2d:%2d:%2d", &hour, &minute, &second); 00497 00498 if ((count != 2 && count != 3) || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) { 00499 ast_log(LOG_WARNING, "CDR variable %s is not a valid time ('%s').\n", entry->name, colptr); 00500 continue; 00501 } 00502 00503 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); 00504 LENGTHEN_BUF2(15); 00505 ast_str_append(&sql2, 0, "%s{ t '%02d:%02d:%02d' }", first ? "" : ",", hour, minute, second); 00506 } 00507 break; 00508 case SQL_TYPE_TIMESTAMP: 00509 case SQL_TIMESTAMP: 00510 if (ast_strlen_zero(colptr)) { 00511 continue; 00512 } else { 00513 int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0; 00514 int count = sscanf(colptr, "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &minute, &second); 00515 00516 if ((count != 3 && count != 5 && count != 6) || year <= 0 || 00517 month <= 0 || month > 12 || day < 0 || day > 31 || 00518 ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) || 00519 (month == 2 && year % 400 == 0 && day > 29) || 00520 (month == 2 && year % 100 == 0 && day > 28) || 00521 (month == 2 && year % 4 == 0 && day > 29) || 00522 (month == 2 && year % 4 != 0 && day > 28) || 00523 hour > 23 || minute > 59 || second > 59 || hour < 0 || minute < 0 || second < 0) { 00524 ast_log(LOG_WARNING, "CDR variable %s is not a valid timestamp ('%s').\n", entry->name, colptr); 00525 continue; 00526 } 00527 00528 if (year > 0 && year < 100) { 00529 year += 2000; 00530 } 00531 00532 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); 00533 LENGTHEN_BUF2(26); 00534 ast_str_append(&sql2, 0, "%s{ ts '%04d-%02d-%02d %02d:%02d:%02d' }", first ? "" : ",", year, month, day, hour, minute, second); 00535 } 00536 break; 00537 case SQL_INTEGER: 00538 if (ast_strlen_zero(colptr)) { 00539 continue; 00540 } else { 00541 int integer = 0; 00542 if (sscanf(colptr, "%30d", &integer) != 1) { 00543 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name); 00544 continue; 00545 } 00546 00547 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); 00548 LENGTHEN_BUF2(12); 00549 ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer); 00550 } 00551 break; 00552 case SQL_BIGINT: 00553 if (ast_strlen_zero(colptr)) { 00554 continue; 00555 } else { 00556 long long integer = 0; 00557 if (sscanf(colptr, "%30lld", &integer) != 1) { 00558 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name); 00559 continue; 00560 } 00561 00562 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); 00563 LENGTHEN_BUF2(24); 00564 ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", integer); 00565 } 00566 break; 00567 case SQL_SMALLINT: 00568 if (ast_strlen_zero(colptr)) { 00569 continue; 00570 } else { 00571 short integer = 0; 00572 if (sscanf(colptr, "%30hd", &integer) != 1) { 00573 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name); 00574 continue; 00575 } 00576 00577 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); 00578 LENGTHEN_BUF2(6); 00579 ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer); 00580 } 00581 break; 00582 case SQL_TINYINT: 00583 if (ast_strlen_zero(colptr)) { 00584 continue; 00585 } else { 00586 char integer = 0; 00587 if (sscanf(colptr, "%30hhd", &integer) != 1) { 00588 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name); 00589 continue; 00590 } 00591 00592 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); 00593 LENGTHEN_BUF2(4); 00594 ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer); 00595 } 00596 break; 00597 case SQL_BIT: 00598 if (ast_strlen_zero(colptr)) { 00599 continue; 00600 } else { 00601 char integer = 0; 00602 if (sscanf(colptr, "%30hhd", &integer) != 1) { 00603 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name); 00604 continue; 00605 } 00606 if (integer != 0) 00607 integer = 1; 00608 00609 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); 00610 LENGTHEN_BUF2(2); 00611 ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer); 00612 } 00613 break; 00614 case SQL_NUMERIC: 00615 case SQL_DECIMAL: 00616 if (ast_strlen_zero(colptr)) { 00617 continue; 00618 } else { 00619 double number = 0.0; 00620 00621 if (!strcasecmp(entry->cdrname, "billsec")) { 00622 if (!ast_tvzero(cdr->answer)) { 00623 snprintf(colbuf, sizeof(colbuf), "%lf", 00624 (double) (ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0)); 00625 } else { 00626 ast_copy_string(colbuf, "0", sizeof(colbuf)); 00627 } 00628 } else if (!strcasecmp(entry->cdrname, "duration")) { 00629 snprintf(colbuf, sizeof(colbuf), "%lf", 00630 (double) (ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0)); 00631 00632 if (!ast_strlen_zero(colbuf)) { 00633 colptr = colbuf; 00634 } 00635 } 00636 00637 if (sscanf(colptr, "%30lf", &number) != 1) { 00638 ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name); 00639 continue; 00640 } 00641 00642 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); 00643 LENGTHEN_BUF2(entry->decimals); 00644 ast_str_append(&sql2, 0, "%s%*.*lf", first ? "" : ",", entry->decimals, entry->radix, number); 00645 } 00646 break; 00647 case SQL_FLOAT: 00648 case SQL_REAL: 00649 case SQL_DOUBLE: 00650 if (ast_strlen_zero(colptr)) { 00651 continue; 00652 } else { 00653 double number = 0.0; 00654 00655 if (!strcasecmp(entry->cdrname, "billsec")) { 00656 if (!ast_tvzero(cdr->answer)) { 00657 snprintf(colbuf, sizeof(colbuf), "%lf", 00658 (double) (ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0)); 00659 } else { 00660 ast_copy_string(colbuf, "0", sizeof(colbuf)); 00661 } 00662 } else if (!strcasecmp(entry->cdrname, "duration")) { 00663 snprintf(colbuf, sizeof(colbuf), "%lf", 00664 (double) (ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0)); 00665 00666 if (!ast_strlen_zero(colbuf)) { 00667 colptr = colbuf; 00668 } 00669 } 00670 00671 if (sscanf(colptr, "%30lf", &number) != 1) { 00672 ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name); 00673 continue; 00674 } 00675 00676 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); 00677 LENGTHEN_BUF2(entry->decimals); 00678 ast_str_append(&sql2, 0, "%s%lf", first ? "" : ",", number); 00679 } 00680 break; 00681 default: 00682 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); 00683 continue; 00684 } 00685 first = 0; 00686 } 00687 } 00688 00689 /* Concatenate the two constructed buffers */ 00690 LENGTHEN_BUF1(ast_str_strlen(sql2)); 00691 ast_str_append(&sql, 0, ")"); 00692 ast_str_append(&sql2, 0, ")"); 00693 ast_str_append(&sql, 0, "%s", ast_str_buffer(sql2)); 00694 00695 ast_verb(11, "[%s]\n", ast_str_buffer(sql)); 00696 00697 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, ast_str_buffer(sql)); 00698 if (stmt) { 00699 SQLRowCount(stmt, &rows); 00700 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 00701 } 00702 if (rows == 0) { 00703 ast_log(LOG_WARNING, "cdr_adaptive_odbc: Insert failed on '%s:%s'. CDR failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql)); 00704 } 00705 early_release: 00706 ast_odbc_release_obj(obj); 00707 } 00708 AST_RWLIST_UNLOCK(&odbc_tables); 00709 00710 /* Next time, just allocate buffers that are that big to start with. */ 00711 if (ast_str_strlen(sql) > maxsize) { 00712 maxsize = ast_str_strlen(sql); 00713 } 00714 if (ast_str_strlen(sql2) > maxsize2) { 00715 maxsize2 = ast_str_strlen(sql2); 00716 } 00717 00718 ast_free(sql); 00719 ast_free(sql2); 00720 return 0; 00721 }
static int reload | ( | void | ) | [static] |
Definition at line 750 of file cdr_adaptive_odbc.c.
References ast_log(), AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, free_config(), load_config(), and LOG_ERROR.
00751 { 00752 if (AST_RWLIST_WRLOCK(&odbc_tables)) { 00753 ast_log(LOG_ERROR, "Unable to lock column list. Reload failed.\n"); 00754 return -1; 00755 } 00756 00757 free_config(); 00758 load_config(); 00759 AST_RWLIST_UNLOCK(&odbc_tables); 00760 return 0; 00761 }
static int unload_module | ( | void | ) | [static] |
Definition at line 723 of file cdr_adaptive_odbc.c.
References ast_cdr_register(), ast_cdr_unregister(), ast_log(), AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, free_config(), LOG_ERROR, and odbc_log().
00724 { 00725 ast_cdr_unregister(name); 00726 if (AST_RWLIST_WRLOCK(&odbc_tables)) { 00727 ast_cdr_register(name, ast_module_info->description, odbc_log); 00728 ast_log(LOG_ERROR, "Unable to lock column list. Unload failed.\n"); 00729 return -1; 00730 } 00731 00732 free_config(); 00733 AST_RWLIST_UNLOCK(&odbc_tables); 00734 return 0; 00735 }
int maxsize = 512 [static] |
Definition at line 55 of file cdr_adaptive_odbc.c.
Referenced by ast_func_read2().
int maxsize2 = 512 [static] |
Definition at line 55 of file cdr_adaptive_odbc.c.
Referenced by odbc_log().
const char name[] = "Adaptive ODBC" [static] |
Definition at line 53 of file cdr_adaptive_odbc.c.
Referenced by __analog_ss_thread(), __ast_change_name_nolink(), __ast_channel_alloc_ap(), __dahdi_exception(), __iax2_show_peers(), _sip_show_peers(), acf_curl_helper(), adsi_load(), adsi_message(), advanced_options(), aelsub_exec(), aji_cli_create_collection(), aji_cli_create_leafnode(), aji_cli_delete_pubsub_node(), aji_cli_list_pubsub_nodes(), aji_cli_purge_pubsub_nodes(), analog_exception(), analog_ss_thread(), aoc_amount_str(), ast_cel_fabricate_channel_from_event(), ast_connected_line_source_parse(), ast_dsp_set_call_progress_zone(), ast_event_str_to_ie_type(), ast_getformatname_multiple(), ast_jb_read_conf(), ast_module_helper(), ast_monitor_change_fname(), ast_monitor_start(), ast_parse_caller_presentation(), ast_party_name_charset_parse(), ast_redirecting_reason_parse(), ast_rtp_lookup_mime_multiple2(), ast_setstate(), ast_str2tos(), ast_syslog_facility(), ast_syslog_priority(), AST_TEST_DEFINE(), ast_waitfor_nandfds(), build_calendar(), callerid_read(), change_monitor_action(), channel_spy(), check_user_full(), cli_tps_ping(), cli_tps_report(), complete_trans_path_choice(), config_ldap(), count_agents_cb(), do_pause_or_unpause(), dump_ies(), dump_prov_ies(), entry_cmp_fn(), fac2str(), fax_session_tab_complete(), findparkinglotname(), get_esc(), group_cmp_fn(), gtalk_ringing_ack(), handle_cli_core_show_translation(), handle_cli_osp_show(), handle_cli_status(), handle_memory_show_summary(), handle_redirect(), handle_register_message(), handle_showchan(), handle_tcptls_connection(), httpd_helper_thread(), load_module(), lua_get_variable(), lua_get_variable_value(), lua_set_variable(), lua_set_variable_value(), manager_mute_mixmonitor(), map_video_codec(), match_agent(), misdn_cfg_get_config_string(), misdn_cfg_get_name(), mwi_thread(), my_get_callerid(), oss_call(), oss_request(), parse_cookies(), party_id_write(), peek_read(), phone_request(), phoneprov_callback(), play_message_callerid(), process_echocancel(), process_opcode(), process_returncode(), proxy_from_config(), register_verify(), reload_module(), serialize_showchan(), set_hangup_source_and_cause(), show_config_description(), sip_acf_channel_read(), sip_prepare_socket(), sip_prune_realtime(), sip_queue_hangup_cause(), sla_add_trunk_to_station(), sla_build_station(), sla_build_trunk(), softhangup_exec(), start_monitor_action(), stop_monitor_action(), tps_taskprocessor_tab_complete(), unistim_new(), unload_module(), and update_call_counter().