00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031 #include "asterisk.h"
00032
00033 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 236851 $")
00034
00035 #include <sys/types.h>
00036 #include <time.h>
00037
00038 #include <sql.h>
00039 #include <sqlext.h>
00040 #include <sqltypes.h>
00041
00042 #include "asterisk/config.h"
00043 #include "asterisk/channel.h"
00044 #include "asterisk/lock.h"
00045 #include "asterisk/linkedlists.h"
00046 #include "asterisk/res_odbc.h"
00047 #include "asterisk/cdr.h"
00048 #include "asterisk/module.h"
00049
00050 #define CONFIG "cdr_adaptive_odbc.conf"
00051
00052 static char *name = "Adaptive ODBC";
00053
00054 static int maxsize = 512, maxsize2 = 512;
00055
00056 struct columns {
00057 char *name;
00058 char *cdrname;
00059 char *filtervalue;
00060 SQLSMALLINT type;
00061 SQLINTEGER size;
00062 SQLSMALLINT decimals;
00063 SQLSMALLINT radix;
00064 SQLSMALLINT nullable;
00065 SQLINTEGER octetlen;
00066 AST_LIST_ENTRY(columns) list;
00067 };
00068
00069 struct tables {
00070 char *connection;
00071 char *table;
00072 AST_LIST_HEAD_NOLOCK(odbc_columns, columns) columns;
00073 AST_RWLIST_ENTRY(tables) list;
00074 };
00075
00076 static AST_RWLIST_HEAD_STATIC(odbc_tables, tables);
00077
00078 static int load_config(void)
00079 {
00080 struct ast_config *cfg;
00081 struct ast_variable *var;
00082 const char *tmp, *catg;
00083 struct tables *tableptr;
00084 struct columns *entry;
00085 struct odbc_obj *obj;
00086 char columnname[80];
00087 char connection[40];
00088 char table[40];
00089 int lenconnection, lentable;
00090 SQLLEN sqlptr;
00091 int res = 0;
00092 SQLHSTMT stmt = NULL;
00093 struct ast_flags config_flags = { 0 };
00094
00095 cfg = ast_config_load(CONFIG, config_flags);
00096 if (!cfg) {
00097 ast_log(LOG_WARNING, "Unable to load " CONFIG ". No adaptive ODBC CDRs.\n");
00098 return -1;
00099 }
00100
00101 for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
00102 var = ast_variable_browse(cfg, catg);
00103 if (!var)
00104 continue;
00105
00106 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "connection"))) {
00107 ast_log(LOG_WARNING, "No connection parameter found in '%s'. Skipping.\n", catg);
00108 continue;
00109 }
00110 ast_copy_string(connection, tmp, sizeof(connection));
00111 lenconnection = strlen(connection);
00112
00113
00114 obj = ast_odbc_request_obj(connection, 1);
00115 if (!obj) {
00116 ast_log(LOG_WARNING, "No such connection '%s' in the '%s' section of " CONFIG ". Check res_odbc.conf.\n", connection, catg);
00117 continue;
00118 }
00119
00120 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "table"))) {
00121 ast_log(LOG_NOTICE, "No table name found. Assuming 'cdr'.\n");
00122 tmp = "cdr";
00123 }
00124 ast_copy_string(table, tmp, sizeof(table));
00125 lentable = strlen(table);
00126
00127 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00128 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00129 ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", connection);
00130 ast_odbc_release_obj(obj);
00131 continue;
00132 }
00133
00134 res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)table, SQL_NTS, (unsigned char *)"%", SQL_NTS);
00135 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00136 ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'. Skipping.\n", connection);
00137 ast_odbc_release_obj(obj);
00138 continue;
00139 }
00140
00141 tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + lenconnection + 1 + lentable + 1);
00142 if (!tableptr) {
00143 ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", table, connection);
00144 ast_odbc_release_obj(obj);
00145 res = -1;
00146 break;
00147 }
00148
00149 tableptr->connection = (char *)tableptr + sizeof(*tableptr);
00150 tableptr->table = (char *)tableptr + sizeof(*tableptr) + lenconnection + 1;
00151 ast_copy_string(tableptr->connection, connection, lenconnection + 1);
00152 ast_copy_string(tableptr->table, table, lentable + 1);
00153
00154 ast_verb(3, "Found adaptive CDR table %s@%s.\n", tableptr->table, tableptr->connection);
00155
00156
00157 for (var = ast_variable_browse(cfg, catg); var; var = var->next) {
00158 if (strncmp(var->name, "filter", 6) == 0) {
00159 char *cdrvar = ast_strdupa(var->name + 6);
00160 cdrvar = ast_strip(cdrvar);
00161 ast_verb(3, "Found filter %s for cdr variable %s in %s@%s\n", var->value, cdrvar, tableptr->table, tableptr->connection);
00162
00163 entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(cdrvar) + 1 + strlen(var->value) + 1);
00164 if (!entry) {
00165 ast_log(LOG_ERROR, "Out of memory creating filter entry for CDR variable '%s' in table '%s' on connection '%s'\n", cdrvar, table, connection);
00166 res = -1;
00167 break;
00168 }
00169
00170
00171 entry->name = NULL;
00172 entry->cdrname = (char *)entry + sizeof(*entry);
00173 entry->filtervalue = (char *)entry + sizeof(*entry) + strlen(cdrvar) + 1;
00174 strcpy(entry->cdrname, cdrvar);
00175 strcpy(entry->filtervalue, var->value);
00176
00177 AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
00178 }
00179 }
00180
00181 while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) {
00182 char *cdrvar = "";
00183
00184 SQLGetData(stmt, 4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr);
00185
00186
00187
00188
00189
00190
00191
00192 for (var = ast_variable_browse(cfg, catg); var; var = var->next) {
00193 if (strncmp(var->name, "alias", 5) == 0 && strcasecmp(var->value, columnname) == 0) {
00194 char *alias = ast_strdupa(var->name + 5);
00195 cdrvar = ast_strip(alias);
00196 ast_verb(3, "Found alias %s for column %s in %s@%s\n", cdrvar, columnname, tableptr->table, tableptr->connection);
00197 break;
00198 }
00199 }
00200
00201 entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1 + strlen(cdrvar) + 1);
00202 if (!entry) {
00203 ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, table, connection);
00204 res = -1;
00205 break;
00206 }
00207 entry->name = (char *)entry + sizeof(*entry);
00208 strcpy(entry->name, columnname);
00209
00210 if (!ast_strlen_zero(cdrvar)) {
00211 entry->cdrname = entry->name + strlen(columnname) + 1;
00212 strcpy(entry->cdrname, cdrvar);
00213 } else
00214 entry->cdrname = (char *)entry + sizeof(*entry);
00215
00216 SQLGetData(stmt, 5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL);
00217 SQLGetData(stmt, 7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL);
00218 SQLGetData(stmt, 9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL);
00219 SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL);
00220 SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL);
00221 SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL);
00222
00223
00224
00225
00226 if (entry->octetlen == 0)
00227 entry->octetlen = entry->size;
00228
00229 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);
00230
00231 AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
00232 res = 0;
00233 }
00234
00235 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00236 ast_odbc_release_obj(obj);
00237
00238 if (AST_LIST_FIRST(&(tableptr->columns)))
00239 AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list);
00240 else
00241 ast_free(tableptr);
00242 }
00243 return res;
00244 }
00245
00246 static int free_config(void)
00247 {
00248 struct tables *table;
00249 struct columns *entry;
00250 while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
00251 while ((entry = AST_LIST_REMOVE_HEAD(&(table->columns), list))) {
00252 ast_free(entry);
00253 }
00254 ast_free(table);
00255 }
00256 return 0;
00257 }
00258
00259 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
00260 {
00261 int res, i;
00262 char *sql = data;
00263 SQLHSTMT stmt;
00264 SQLINTEGER nativeerror = 0, numfields = 0;
00265 SQLSMALLINT diagbytes = 0;
00266 unsigned char state[10], diagnostic[256];
00267
00268 res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
00269 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00270 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00271 return NULL;
00272 }
00273
00274 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
00275 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00276 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
00277 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00278 for (i = 0; i < numfields; i++) {
00279 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00280 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00281 if (i > 10) {
00282 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
00283 break;
00284 }
00285 }
00286 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00287 return NULL;
00288 }
00289
00290 return stmt;
00291 }
00292
00293 #define LENGTHEN_BUF1(size) \
00294 do { \
00295 \
00296 if (sql->used + size + 1 > sql->len) { \
00297 if (ast_str_make_space(&sql, ((sql->len + size + 1) / 512 + 1) * 512) != 0) { \
00298 ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR '%s:%s' failed.\n", tableptr->connection, tableptr->table); \
00299 ast_free(sql); \
00300 ast_free(sql2); \
00301 AST_RWLIST_UNLOCK(&odbc_tables); \
00302 return -1; \
00303 } \
00304 } \
00305 } while (0)
00306
00307 #define LENGTHEN_BUF2(size) \
00308 do { \
00309 if (sql2->used + size + 1 > sql2->len) { \
00310 if (ast_str_make_space(&sql2, ((sql2->len + size + 3) / 512 + 1) * 512) != 0) { \
00311 ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR '%s:%s' failed.\n", tableptr->connection, tableptr->table); \
00312 ast_free(sql); \
00313 ast_free(sql2); \
00314 AST_RWLIST_UNLOCK(&odbc_tables); \
00315 return -1; \
00316 } \
00317 } \
00318 } while (0)
00319
00320 static int odbc_log(struct ast_cdr *cdr)
00321 {
00322 struct tables *tableptr;
00323 struct columns *entry;
00324 struct odbc_obj *obj;
00325 struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);
00326 char *tmp;
00327 char colbuf[1024], *colptr;
00328 SQLHSTMT stmt = NULL;
00329 SQLLEN rows = 0;
00330
00331 if (!sql || !sql2) {
00332 if (sql)
00333 ast_free(sql);
00334 if (sql2)
00335 ast_free(sql2);
00336 return -1;
00337 }
00338
00339 if (AST_RWLIST_RDLOCK(&odbc_tables)) {
00340 ast_log(LOG_ERROR, "Unable to lock table list. Insert CDR(s) failed.\n");
00341 ast_free(sql);
00342 ast_free(sql2);
00343 return -1;
00344 }
00345
00346 AST_LIST_TRAVERSE(&odbc_tables, tableptr, list) {
00347 ast_str_set(&sql, 0, "INSERT INTO %s (", tableptr->table);
00348 ast_str_set(&sql2, 0, " VALUES (");
00349
00350
00351 if (!(obj = ast_odbc_request_obj(tableptr->connection, 0))) {
00352 ast_log(LOG_WARNING, "cdr_adaptive_odbc: Unable to retrieve database handle for '%s:%s'. CDR failed: %s\n", tableptr->connection, tableptr->table, sql->str);
00353 continue;
00354 }
00355
00356 AST_LIST_TRAVERSE(&(tableptr->columns), entry, list) {
00357
00358 ast_cdr_getvar(cdr, entry->cdrname, &colptr, colbuf, sizeof(colbuf), 0,
00359 (strcasecmp(entry->cdrname, "start") == 0 ||
00360 strcasecmp(entry->cdrname, "answer") == 0 ||
00361 strcasecmp(entry->cdrname, "end") == 0) ? 0 : 1);
00362
00363 if (colptr) {
00364
00365
00366
00367
00368 if (entry->filtervalue && strcasecmp(colptr, entry->filtervalue) != 0) {
00369 ast_verb(4, "CDR column '%s' with value '%s' does not match filter of"
00370 " '%s'. Cancelling this CDR.\n",
00371 entry->cdrname, colptr, entry->filtervalue);
00372 goto early_release;
00373 }
00374
00375
00376 if (ast_strlen_zero(entry->name))
00377 continue;
00378
00379 LENGTHEN_BUF1(strlen(entry->name));
00380
00381 switch (entry->type) {
00382 case SQL_CHAR:
00383 case SQL_VARCHAR:
00384 case SQL_LONGVARCHAR:
00385 case SQL_BINARY:
00386 case SQL_VARBINARY:
00387 case SQL_LONGVARBINARY:
00388 case SQL_GUID:
00389
00390
00391
00392 if (strcasecmp(entry->name, "disposition") == 0)
00393 ast_cdr_getvar(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0, 0);
00394 else if (strcasecmp(entry->name, "amaflags") == 0)
00395 ast_cdr_getvar(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0, 0);
00396
00397
00398 if (entry->type != SQL_GUID) {
00399 if (strlen(colptr) > entry->octetlen)
00400 colptr[entry->octetlen] = '\0';
00401 }
00402
00403 ast_str_append(&sql, 0, "%s,", entry->name);
00404 LENGTHEN_BUF2(strlen(colptr));
00405
00406
00407 ast_str_append(&sql2, 0, "'");
00408 for (tmp = colptr; *tmp; tmp++) {
00409 if (*tmp == '\'') {
00410 ast_str_append(&sql2, 0, "''");
00411 } else if (*tmp == '\\' && ast_odbc_backslash_is_escape(obj)) {
00412 ast_str_append(&sql2, 0, "\\\\");
00413 } else {
00414 ast_str_append(&sql2, 0, "%c", *tmp);
00415 }
00416 }
00417 ast_str_append(&sql2, 0, "',");
00418 break;
00419 case SQL_TYPE_DATE:
00420 if (ast_strlen_zero(colptr)) {
00421 continue;
00422 } else {
00423 int year = 0, month = 0, day = 0;
00424 if (sscanf(colptr, "%4d-%2d-%2d", &year, &month, &day) != 3 || year <= 0 ||
00425 month <= 0 || month > 12 || day < 0 || day > 31 ||
00426 ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
00427 (month == 2 && year % 400 == 0 && day > 29) ||
00428 (month == 2 && year % 100 == 0 && day > 28) ||
00429 (month == 2 && year % 4 == 0 && day > 29) ||
00430 (month == 2 && year % 4 != 0 && day > 28)) {
00431 ast_log(LOG_WARNING, "CDR variable %s is not a valid date ('%s').\n", entry->name, colptr);
00432 break;
00433 }
00434
00435 if (year > 0 && year < 100)
00436 year += 2000;
00437
00438 ast_str_append(&sql, 0, "%s,", entry->name);
00439 LENGTHEN_BUF2(17);
00440 ast_str_append(&sql2, 0, "{ d '%04d-%02d-%02d' },", year, month, day);
00441 }
00442 break;
00443 case SQL_TYPE_TIME:
00444 if (ast_strlen_zero(colptr)) {
00445 continue;
00446 } else {
00447 int hour = 0, minute = 0, second = 0;
00448 int count = sscanf(colptr, "%2d:%2d:%2d", &hour, &minute, &second);
00449
00450 if ((count != 2 && count != 3) || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) {
00451 ast_log(LOG_WARNING, "CDR variable %s is not a valid time ('%s').\n", entry->name, colptr);
00452 break;
00453 }
00454
00455 ast_str_append(&sql, 0, "%s,", entry->name);
00456 LENGTHEN_BUF2(15);
00457 ast_str_append(&sql2, 0, "{ t '%02d:%02d:%02d' },", hour, minute, second);
00458 }
00459 break;
00460 case SQL_TYPE_TIMESTAMP:
00461 case SQL_TIMESTAMP:
00462 if (ast_strlen_zero(colptr)) {
00463 continue;
00464 } else {
00465 int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
00466 int count = sscanf(colptr, "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &minute, &second);
00467
00468 if ((count != 3 && count != 5 && count != 6) || year <= 0 ||
00469 month <= 0 || month > 12 || day < 0 || day > 31 ||
00470 ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
00471 (month == 2 && year % 400 == 0 && day > 29) ||
00472 (month == 2 && year % 100 == 0 && day > 28) ||
00473 (month == 2 && year % 4 == 0 && day > 29) ||
00474 (month == 2 && year % 4 != 0 && day > 28) ||
00475 hour > 23 || minute > 59 || second > 59 || hour < 0 || minute < 0 || second < 0) {
00476 ast_log(LOG_WARNING, "CDR variable %s is not a valid timestamp ('%s').\n", entry->name, colptr);
00477 break;
00478 }
00479
00480 if (year > 0 && year < 100)
00481 year += 2000;
00482
00483 ast_str_append(&sql, 0, "%s,", entry->name);
00484 LENGTHEN_BUF2(26);
00485 ast_str_append(&sql2, 0, "{ ts '%04d-%02d-%02d %02d:%02d:%02d' },", year, month, day, hour, minute, second);
00486 }
00487 break;
00488 case SQL_INTEGER:
00489 if (ast_strlen_zero(colptr)) {
00490 continue;
00491 } else {
00492 int integer = 0;
00493 if (sscanf(colptr, "%30d", &integer) != 1) {
00494 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00495 break;
00496 }
00497
00498 ast_str_append(&sql, 0, "%s,", entry->name);
00499 LENGTHEN_BUF2(12);
00500 ast_str_append(&sql2, 0, "%d,", integer);
00501 }
00502 break;
00503 case SQL_BIGINT:
00504 if (ast_strlen_zero(colptr)) {
00505 continue;
00506 } else {
00507 long long integer = 0;
00508 if (sscanf(colptr, "%30lld", &integer) != 1) {
00509 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00510 break;
00511 }
00512
00513 ast_str_append(&sql, 0, "%s,", entry->name);
00514 LENGTHEN_BUF2(24);
00515 ast_str_append(&sql2, 0, "%lld,", integer);
00516 }
00517 break;
00518 case SQL_SMALLINT:
00519 if (ast_strlen_zero(colptr)) {
00520 continue;
00521 } else {
00522 short integer = 0;
00523 if (sscanf(colptr, "%30hd", &integer) != 1) {
00524 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00525 break;
00526 }
00527
00528 ast_str_append(&sql, 0, "%s,", entry->name);
00529 LENGTHEN_BUF2(6);
00530 ast_str_append(&sql2, 0, "%d,", integer);
00531 }
00532 break;
00533 case SQL_TINYINT:
00534 if (ast_strlen_zero(colptr)) {
00535 continue;
00536 } else {
00537 char integer = 0;
00538 if (sscanf(colptr, "%30hhd", &integer) != 1) {
00539 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00540 break;
00541 }
00542
00543 ast_str_append(&sql, 0, "%s,", entry->name);
00544 LENGTHEN_BUF2(4);
00545 ast_str_append(&sql2, 0, "%d,", integer);
00546 }
00547 break;
00548 case SQL_BIT:
00549 if (ast_strlen_zero(colptr)) {
00550 continue;
00551 } else {
00552 char integer = 0;
00553 if (sscanf(colptr, "%30hhd", &integer) != 1) {
00554 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00555 break;
00556 }
00557 if (integer != 0)
00558 integer = 1;
00559
00560 ast_str_append(&sql, 0, "%s,", entry->name);
00561 LENGTHEN_BUF2(2);
00562 ast_str_append(&sql2, 0, "%d,", integer);
00563 }
00564 break;
00565 case SQL_NUMERIC:
00566 case SQL_DECIMAL:
00567 if (ast_strlen_zero(colptr)) {
00568 continue;
00569 } else {
00570 double number = 0.0;
00571 if (sscanf(colptr, "%30lf", &number) != 1) {
00572 ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
00573 break;
00574 }
00575
00576 ast_str_append(&sql, 0, "%s,", entry->name);
00577 LENGTHEN_BUF2(entry->decimals);
00578 ast_str_append(&sql2, 0, "%*.*lf,", entry->decimals, entry->radix, number);
00579 }
00580 break;
00581 case SQL_FLOAT:
00582 case SQL_REAL:
00583 case SQL_DOUBLE:
00584 if (ast_strlen_zero(colptr)) {
00585 continue;
00586 } else {
00587 double number = 0.0;
00588 if (sscanf(colptr, "%30lf", &number) != 1) {
00589 ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
00590 break;
00591 }
00592
00593 ast_str_append(&sql, 0, "%s,", entry->name);
00594 LENGTHEN_BUF2(entry->decimals);
00595 ast_str_append(&sql2, 0, "%lf,", number);
00596 }
00597 break;
00598 default:
00599 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);
00600 }
00601 }
00602 }
00603
00604
00605 LENGTHEN_BUF1(sql2->used);
00606 sql->str[sql->used - 1] = ')';
00607 sql2->str[sql2->used - 1] = ')';
00608 ast_str_append(&sql, 0, "%s", sql2->str);
00609
00610 ast_verb(11, "[%s]\n", sql->str);
00611
00612 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, sql->str);
00613 if (stmt) {
00614 SQLRowCount(stmt, &rows);
00615 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00616 }
00617 if (rows == 0) {
00618 ast_log(LOG_WARNING, "cdr_adaptive_odbc: Insert failed on '%s:%s'. CDR failed: %s\n", tableptr->connection, tableptr->table, sql->str);
00619 }
00620 early_release:
00621 ast_odbc_release_obj(obj);
00622 }
00623 AST_RWLIST_UNLOCK(&odbc_tables);
00624
00625
00626 if (sql->used > maxsize)
00627 maxsize = sql->used;
00628 if (sql2->used > maxsize2)
00629 maxsize2 = sql2->used;
00630
00631 ast_free(sql);
00632 ast_free(sql2);
00633 return 0;
00634 }
00635
00636 static int unload_module(void)
00637 {
00638 ast_cdr_unregister(name);
00639 usleep(1);
00640 if (AST_RWLIST_WRLOCK(&odbc_tables)) {
00641 ast_cdr_register(name, ast_module_info->description, odbc_log);
00642 ast_log(LOG_ERROR, "Unable to lock column list. Unload failed.\n");
00643 return -1;
00644 }
00645
00646 free_config();
00647 AST_RWLIST_UNLOCK(&odbc_tables);
00648 return 0;
00649 }
00650
00651 static int load_module(void)
00652 {
00653 if (AST_RWLIST_WRLOCK(&odbc_tables)) {
00654 ast_log(LOG_ERROR, "Unable to lock column list. Load failed.\n");
00655 return 0;
00656 }
00657
00658 load_config();
00659 AST_RWLIST_UNLOCK(&odbc_tables);
00660 ast_cdr_register(name, ast_module_info->description, odbc_log);
00661 return 0;
00662 }
00663
00664 static int reload(void)
00665 {
00666 if (AST_RWLIST_WRLOCK(&odbc_tables)) {
00667 ast_log(LOG_ERROR, "Unable to lock column list. Reload failed.\n");
00668 return -1;
00669 }
00670
00671 free_config();
00672 load_config();
00673 AST_RWLIST_UNLOCK(&odbc_tables);
00674 return 0;
00675 }
00676
00677 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Adaptive ODBC CDR backend",
00678 .load = load_module,
00679 .unload = unload_module,
00680 .reload = reload,
00681 );
00682