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