Mon Oct 8 12:39:12 2012

Asterisk developer's documentation


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"

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 __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 = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CDR_DRIVER, }
static struct ast_module_infoast_module_info = &__mod_info
static int maxsize = 512
static int maxsize2 = 512
static const char name [] = "Adaptive ODBC"


Detailed Description

Adaptive ODBC CDR backend.

Author:
Tilghman Lesher <cdr_adaptive_odbc__v1@the-tilghman.com>

Definition in file cdr_adaptive_odbc.c.


Define Documentation

#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().


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 768 of file cdr_adaptive_odbc.c.

static void __unreg_module ( void   )  [static]

Definition at line 768 of file cdr_adaptive_odbc.c.

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, columns::list, and table.

Referenced by load_config(), load_module(), 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_load, ast_copy_string(), AST_LIST_INSERT_TAIL, ast_log(), ast_odbc_release_obj(), ast_odbc_request_obj, ast_strdupa, ast_strip(), ast_strlen_zero(), ast_true(), ast_variable_browse(), ast_variable_retrieve(), ast_verb, tables::columns, columns, odbc_obj::con, CONFIG, CONFIG_STATUS_FILEINVALID, tables::connection, LOG_ERROR, LOG_NOTICE, LOG_WARNING, columns::staticvalue, tables::table, usegmtime, and var.

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(), odbc_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 }


Variable Documentation

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 = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CDR_DRIVER, } [static]

Definition at line 768 of file cdr_adaptive_odbc.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 768 of file cdr_adaptive_odbc.c.

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(), and pgsql_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(), __ast_data_add_structure(), __dahdi_exception(), __iax2_show_peers(), _sip_show_peers(), acf_curl_helper(), action_atxfer(), action_getvar(), action_hangup(), action_originate(), action_redirect(), action_sendtext(), action_setvar(), action_status(), action_timeout(), add_agent(), adsi_load(), adsi_message(), advanced_options(), aelsub_exec(), agents_show_online(), 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_exception(), analog_ss_thread(), aoc_amount_str(), ast_cel_fabricate_channel_from_event(), ast_connected_line_source_parse(), ast_context_find_or_create(), ast_data_add_codecs(), ast_data_add_password(), ast_data_add_str(), 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(), build_calendar(), callerid_read(), change_monitor_action(), channel_spy(), check_user_full(), cli_tps_ping(), cli_tps_report(), complete_agent_logoff_cmd(), 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_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(), party_name_build_data(), party_name_read(), party_name_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(), softhangup_exec(), start_monitor_action(), stop_monitor_action(), tps_taskprocessor_tab_complete(), unistim_new(), unload_module(), and update_call_counter().


Generated on Mon Oct 8 12:39:12 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7