Sat Mar 10 01:54:49 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 = "88eaa8f5c1bd988bedd71113385e0886" , .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 315 of file cdr_adaptive_odbc.c.

Referenced by odbc_log(), and pgsql_log().

#define LENGTHEN_BUF2 ( size   ) 

Definition at line 329 of file cdr_adaptive_odbc.c.

Referenced by odbc_log(), and pgsql_log().


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 762 of file cdr_adaptive_odbc.c.

static void __unreg_module ( void   )  [static]

Definition at line 762 of file cdr_adaptive_odbc.c.

static int free_config ( void   )  [static]

Definition at line 269 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().

00270 {
00271    struct tables *table;
00272    struct columns *entry;
00273    while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
00274       while ((entry = AST_LIST_REMOVE_HEAD(&(table->columns), list))) {
00275          ast_free(entry);
00276       }
00277       ast_free(table);
00278    }
00279    return 0;
00280 }

static SQLHSTMT generic_prepare ( struct odbc_obj obj,
void *  data 
) [static]

Definition at line 282 of file cdr_adaptive_odbc.c.

References ast_log(), odbc_obj::con, and LOG_WARNING.

Referenced by odbc_log().

00283 {
00284    int res, i;
00285    SQLHSTMT stmt;
00286    SQLINTEGER nativeerror = 0, numfields = 0;
00287    SQLSMALLINT diagbytes = 0;
00288    unsigned char state[10], diagnostic[256];
00289 
00290    res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
00291    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00292       ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00293       return NULL;
00294    }
00295 
00296    res = SQLPrepare(stmt, (unsigned char *) data, SQL_NTS);
00297    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00298       ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", (char *) data);
00299       SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00300       for (i = 0; i < numfields; i++) {
00301          SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00302          ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00303          if (i > 10) {
00304             ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
00305             break;
00306          }
00307       }
00308       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00309       return NULL;
00310    }
00311 
00312    return stmt;
00313 }

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_flags, 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    return res;
00267 }

static int load_module ( void   )  [static]

Definition at line 731 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().

00732 {
00733    if (AST_RWLIST_WRLOCK(&odbc_tables)) {
00734       ast_log(LOG_ERROR, "Unable to lock column list.  Load failed.\n");
00735       return 0;
00736    }
00737 
00738    load_config();
00739    AST_RWLIST_UNLOCK(&odbc_tables);
00740    ast_cdr_register(name, ast_module_info->description, odbc_log);
00741    return 0;
00742 }

static int odbc_log ( struct ast_cdr cdr  )  [static]

Definition at line 342 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().

00343 {
00344    struct tables *tableptr;
00345    struct columns *entry;
00346    struct odbc_obj *obj;
00347    struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);
00348    char *tmp;
00349    char colbuf[1024], *colptr;
00350    SQLHSTMT stmt = NULL;
00351    SQLLEN rows = 0;
00352 
00353    if (!sql || !sql2) {
00354       if (sql)
00355          ast_free(sql);
00356       if (sql2)
00357          ast_free(sql2);
00358       return -1;
00359    }
00360 
00361    if (AST_RWLIST_RDLOCK(&odbc_tables)) {
00362       ast_log(LOG_ERROR, "Unable to lock table list.  Insert CDR(s) failed.\n");
00363       ast_free(sql);
00364       ast_free(sql2);
00365       return -1;
00366    }
00367 
00368    AST_LIST_TRAVERSE(&odbc_tables, tableptr, list) {
00369       int first = 1;
00370       ast_str_set(&sql, 0, "INSERT INTO %s (", tableptr->table);
00371       ast_str_set(&sql2, 0, " VALUES (");
00372 
00373       /* No need to check the connection now; we'll handle any failure in prepare_and_execute */
00374       if (!(obj = ast_odbc_request_obj(tableptr->connection, 0))) {
00375          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));
00376          continue;
00377       }
00378 
00379       AST_LIST_TRAVERSE(&(tableptr->columns), entry, list) {
00380          int datefield = 0;
00381          if (strcasecmp(entry->cdrname, "start") == 0) {
00382             datefield = 1;
00383          } else if (strcasecmp(entry->cdrname, "answer") == 0) {
00384             datefield = 2;
00385          } else if (strcasecmp(entry->cdrname, "end") == 0) {
00386             datefield = 3;
00387          }
00388 
00389          /* Check if we have a similarly named variable */
00390          if (entry->staticvalue) {
00391             colptr = ast_strdupa(entry->staticvalue);
00392          } else if (datefield && tableptr->usegmtime) {
00393             struct timeval date_tv = (datefield == 1) ? cdr->start : (datefield == 2) ? cdr->answer : cdr->end;
00394             struct ast_tm tm = { 0, };
00395             ast_localtime(&date_tv, &tm, "UTC");
00396             ast_strftime(colbuf, sizeof(colbuf), "%Y-%m-%d %H:%M:%S", &tm);
00397             colptr = colbuf;
00398          } else {
00399             ast_cdr_getvar(cdr, entry->cdrname, &colptr, colbuf, sizeof(colbuf), 0, datefield ? 0 : 1);
00400          }
00401 
00402          if (colptr) {
00403             /* Check first if the column filters this entry.  Note that this
00404              * is very specifically NOT ast_strlen_zero(), because the filter
00405              * could legitimately specify that the field is blank, which is
00406              * different from the field being unspecified (NULL). */
00407             if (entry->filtervalue && strcasecmp(colptr, entry->filtervalue) != 0) {
00408                ast_verb(4, "CDR column '%s' with value '%s' does not match filter of"
00409                   " '%s'.  Cancelling this CDR.\n",
00410                   entry->cdrname, colptr, entry->filtervalue);
00411                goto early_release;
00412             }
00413 
00414             /* Only a filter? */
00415             if (ast_strlen_zero(entry->name))
00416                continue;
00417 
00418             LENGTHEN_BUF1(strlen(entry->name));
00419 
00420             switch (entry->type) {
00421             case SQL_CHAR:
00422             case SQL_VARCHAR:
00423             case SQL_LONGVARCHAR:
00424             case SQL_BINARY:
00425             case SQL_VARBINARY:
00426             case SQL_LONGVARBINARY:
00427             case SQL_GUID:
00428                /* For these two field names, get the rendered form, instead of the raw
00429                 * form (but only when we're dealing with a character-based field).
00430                 */
00431                if (strcasecmp(entry->name, "disposition") == 0) {
00432                   ast_cdr_getvar(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0, 0);
00433                } else if (strcasecmp(entry->name, "amaflags") == 0) {
00434                   ast_cdr_getvar(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0, 0);
00435                }
00436 
00437                /* Truncate too-long fields */
00438                if (entry->type != SQL_GUID) {
00439                   if (strlen(colptr) > entry->octetlen) {
00440                      colptr[entry->octetlen] = '\0';
00441                   }
00442                }
00443 
00444                ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00445                LENGTHEN_BUF2(strlen(colptr));
00446 
00447                /* Encode value, with escaping */
00448                ast_str_append(&sql2, 0, "%s'", first ? "" : ",");
00449                for (tmp = colptr; *tmp; tmp++) {
00450                   if (*tmp == '\'') {
00451                      ast_str_append(&sql2, 0, "''");
00452                   } else if (*tmp == '\\' && ast_odbc_backslash_is_escape(obj)) {
00453                      ast_str_append(&sql2, 0, "\\\\");
00454                   } else {
00455                      ast_str_append(&sql2, 0, "%c", *tmp);
00456                   }
00457                }
00458                ast_str_append(&sql2, 0, "'");
00459                break;
00460             case SQL_TYPE_DATE:
00461                if (ast_strlen_zero(colptr)) {
00462                   continue;
00463                } else {
00464                   int year = 0, month = 0, day = 0;
00465                   if (sscanf(colptr, "%4d-%2d-%2d", &year, &month, &day) != 3 || year <= 0 ||
00466                      month <= 0 || month > 12 || day < 0 || day > 31 ||
00467                      ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
00468                      (month == 2 && year % 400 == 0 && day > 29) ||
00469                      (month == 2 && year % 100 == 0 && day > 28) ||
00470                      (month == 2 && year % 4 == 0 && day > 29) ||
00471                      (month == 2 && year % 4 != 0 && day > 28)) {
00472                      ast_log(LOG_WARNING, "CDR variable %s is not a valid date ('%s').\n", entry->name, colptr);
00473                      continue;
00474                   }
00475 
00476                   if (year > 0 && year < 100) {
00477                      year += 2000;
00478                   }
00479 
00480                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00481                   LENGTHEN_BUF2(17);
00482                   ast_str_append(&sql2, 0, "%s{ d '%04d-%02d-%02d' }", first ? "" : ",", year, month, day);
00483                }
00484                break;
00485             case SQL_TYPE_TIME:
00486                if (ast_strlen_zero(colptr)) {
00487                   continue;
00488                } else {
00489                   int hour = 0, minute = 0, second = 0;
00490                   int count = sscanf(colptr, "%2d:%2d:%2d", &hour, &minute, &second);
00491 
00492                   if ((count != 2 && count != 3) || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) {
00493                      ast_log(LOG_WARNING, "CDR variable %s is not a valid time ('%s').\n", entry->name, colptr);
00494                      continue;
00495                   }
00496 
00497                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00498                   LENGTHEN_BUF2(15);
00499                   ast_str_append(&sql2, 0, "%s{ t '%02d:%02d:%02d' }", first ? "" : ",", hour, minute, second);
00500                }
00501                break;
00502             case SQL_TYPE_TIMESTAMP:
00503             case SQL_TIMESTAMP:
00504                if (ast_strlen_zero(colptr)) {
00505                   continue;
00506                } else {
00507                   int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
00508                   int count = sscanf(colptr, "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &minute, &second);
00509 
00510                   if ((count != 3 && count != 5 && count != 6) || year <= 0 ||
00511                      month <= 0 || month > 12 || day < 0 || day > 31 ||
00512                      ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
00513                      (month == 2 && year % 400 == 0 && day > 29) ||
00514                      (month == 2 && year % 100 == 0 && day > 28) ||
00515                      (month == 2 && year % 4 == 0 && day > 29) ||
00516                      (month == 2 && year % 4 != 0 && day > 28) ||
00517                      hour > 23 || minute > 59 || second > 59 || hour < 0 || minute < 0 || second < 0) {
00518                      ast_log(LOG_WARNING, "CDR variable %s is not a valid timestamp ('%s').\n", entry->name, colptr);
00519                      continue;
00520                   }
00521 
00522                   if (year > 0 && year < 100) {
00523                      year += 2000;
00524                   }
00525 
00526                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00527                   LENGTHEN_BUF2(26);
00528                   ast_str_append(&sql2, 0, "%s{ ts '%04d-%02d-%02d %02d:%02d:%02d' }", first ? "" : ",", year, month, day, hour, minute, second);
00529                }
00530                break;
00531             case SQL_INTEGER:
00532                if (ast_strlen_zero(colptr)) {
00533                   continue;
00534                } else {
00535                   int integer = 0;
00536                   if (sscanf(colptr, "%30d", &integer) != 1) {
00537                      ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00538                      continue;
00539                   }
00540 
00541                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00542                   LENGTHEN_BUF2(12);
00543                   ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
00544                }
00545                break;
00546             case SQL_BIGINT:
00547                if (ast_strlen_zero(colptr)) {
00548                   continue;
00549                } else {
00550                   long long integer = 0;
00551                   if (sscanf(colptr, "%30lld", &integer) != 1) {
00552                      ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00553                      continue;
00554                   }
00555 
00556                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00557                   LENGTHEN_BUF2(24);
00558                   ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", integer);
00559                }
00560                break;
00561             case SQL_SMALLINT:
00562                if (ast_strlen_zero(colptr)) {
00563                   continue;
00564                } else {
00565                   short integer = 0;
00566                   if (sscanf(colptr, "%30hd", &integer) != 1) {
00567                      ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00568                      continue;
00569                   }
00570 
00571                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00572                   LENGTHEN_BUF2(6);
00573                   ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
00574                }
00575                break;
00576             case SQL_TINYINT:
00577                if (ast_strlen_zero(colptr)) {
00578                   continue;
00579                } else {
00580                   char integer = 0;
00581                   if (sscanf(colptr, "%30hhd", &integer) != 1) {
00582                      ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00583                      continue;
00584                   }
00585 
00586                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00587                   LENGTHEN_BUF2(4);
00588                   ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
00589                }
00590                break;
00591             case SQL_BIT:
00592                if (ast_strlen_zero(colptr)) {
00593                   continue;
00594                } else {
00595                   char integer = 0;
00596                   if (sscanf(colptr, "%30hhd", &integer) != 1) {
00597                      ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00598                      continue;
00599                   }
00600                   if (integer != 0)
00601                      integer = 1;
00602 
00603                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00604                   LENGTHEN_BUF2(2);
00605                   ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
00606                }
00607                break;
00608             case SQL_NUMERIC:
00609             case SQL_DECIMAL:
00610                if (ast_strlen_zero(colptr)) {
00611                   continue;
00612                } else {
00613                   double number = 0.0;
00614 
00615                   if (!strcasecmp(entry->cdrname, "billsec")) {
00616                      if (!ast_tvzero(cdr->answer)) {
00617                         snprintf(colbuf, sizeof(colbuf), "%lf",
00618                                  (double) (ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0));
00619                      } else {
00620                         ast_copy_string(colbuf, "0", sizeof(colbuf));
00621                      }
00622                   } else if (!strcasecmp(entry->cdrname, "duration")) {
00623                      snprintf(colbuf, sizeof(colbuf), "%lf",
00624                               (double) (ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0));
00625 
00626                      if (!ast_strlen_zero(colbuf)) {
00627                         colptr = colbuf;
00628                      }
00629                   }
00630 
00631                   if (sscanf(colptr, "%30lf", &number) != 1) {
00632                      ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
00633                      continue;
00634                   }
00635 
00636                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00637                   LENGTHEN_BUF2(entry->decimals);
00638                   ast_str_append(&sql2, 0, "%s%*.*lf", first ? "" : ",", entry->decimals, entry->radix, number);
00639                }
00640                break;
00641             case SQL_FLOAT:
00642             case SQL_REAL:
00643             case SQL_DOUBLE:
00644                if (ast_strlen_zero(colptr)) {
00645                   continue;
00646                } else {
00647                   double number = 0.0;
00648 
00649                   if (!strcasecmp(entry->cdrname, "billsec")) {
00650                      if (!ast_tvzero(cdr->answer)) {
00651                         snprintf(colbuf, sizeof(colbuf), "%lf",
00652                                  (double) (ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0));
00653                      } else {
00654                         ast_copy_string(colbuf, "0", sizeof(colbuf));
00655                      }
00656                   } else if (!strcasecmp(entry->cdrname, "duration")) {
00657                      snprintf(colbuf, sizeof(colbuf), "%lf",
00658                               (double) (ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0));
00659 
00660                      if (!ast_strlen_zero(colbuf)) {
00661                         colptr = colbuf;
00662                      }
00663                   }
00664 
00665                   if (sscanf(colptr, "%30lf", &number) != 1) {
00666                      ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
00667                      continue;
00668                   }
00669 
00670                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00671                   LENGTHEN_BUF2(entry->decimals);
00672                   ast_str_append(&sql2, 0, "%s%lf", first ? "" : ",", number);
00673                }
00674                break;
00675             default:
00676                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);
00677                continue;
00678             }
00679             first = 0;
00680          }
00681       }
00682 
00683       /* Concatenate the two constructed buffers */
00684       LENGTHEN_BUF1(ast_str_strlen(sql2));
00685       ast_str_append(&sql, 0, ")");
00686       ast_str_append(&sql2, 0, ")");
00687       ast_str_append(&sql, 0, "%s", ast_str_buffer(sql2));
00688 
00689       ast_verb(11, "[%s]\n", ast_str_buffer(sql));
00690 
00691       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, ast_str_buffer(sql));
00692       if (stmt) {
00693          SQLRowCount(stmt, &rows);
00694          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00695       }
00696       if (rows == 0) {
00697          ast_log(LOG_WARNING, "cdr_adaptive_odbc: Insert failed on '%s:%s'.  CDR failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql));
00698       }
00699 early_release:
00700       ast_odbc_release_obj(obj);
00701    }
00702    AST_RWLIST_UNLOCK(&odbc_tables);
00703 
00704    /* Next time, just allocate buffers that are that big to start with. */
00705    if (ast_str_strlen(sql) > maxsize) {
00706       maxsize = ast_str_strlen(sql);
00707    }
00708    if (ast_str_strlen(sql2) > maxsize2) {
00709       maxsize2 = ast_str_strlen(sql2);
00710    }
00711 
00712    ast_free(sql);
00713    ast_free(sql2);
00714    return 0;
00715 }

static int reload ( void   )  [static]

Definition at line 744 of file cdr_adaptive_odbc.c.

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

00745 {
00746    if (AST_RWLIST_WRLOCK(&odbc_tables)) {
00747       ast_log(LOG_ERROR, "Unable to lock column list.  Reload failed.\n");
00748       return -1;
00749    }
00750 
00751    free_config();
00752    load_config();
00753    AST_RWLIST_UNLOCK(&odbc_tables);
00754    return 0;
00755 }

static int unload_module ( void   )  [static]

Definition at line 717 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().

00718 {
00719    ast_cdr_unregister(name);
00720    if (AST_RWLIST_WRLOCK(&odbc_tables)) {
00721       ast_cdr_register(name, ast_module_info->description, odbc_log);
00722       ast_log(LOG_ERROR, "Unable to lock column list.  Unload failed.\n");
00723       return -1;
00724    }
00725 
00726    free_config();
00727    AST_RWLIST_UNLOCK(&odbc_tables);
00728    return 0;
00729 }


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

Definition at line 762 of file cdr_adaptive_odbc.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 762 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(), __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_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(), load_rpt_vars(), lua_get_variable(), lua_get_variable_value(), lua_set_variable(), lua_set_variable_value(), manager_mute_mixmonitor(), manager_rpt_local_nodes(), manager_rpt_status(), 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(), 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(), serialize_showchan(), show_config_description(), sip_acf_channel_read(), sip_prepare_socket(), sip_prune_realtime(), softhangup_exec(), start_monitor_action(), stop_monitor_action(), tps_taskprocessor_tab_complete(), unistim_new(), unload_module(), and update_call_counter().


Generated on Sat Mar 10 01:54:49 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7