Sat Feb 11 06:34:22 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"

Include dependency graph for cdr_adaptive_odbc.c:

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_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 338 of file cdr_adaptive_odbc.c.

Referenced by odbc_log(), and pgsql_log().

#define LENGTHEN_BUF2 ( size   ) 

Definition at line 352 of file cdr_adaptive_odbc.c.

Referenced by odbc_log(), and pgsql_log().


Function Documentation

static void __fini_odbc_tables ( void   )  [static]

Definition at line 81 of file cdr_adaptive_odbc.c.

00084 {

static void __init_odbc_tables ( void   )  [static]

Definition at line 81 of file cdr_adaptive_odbc.c.

00084 {

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 }


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 = 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.

Definition at line 790 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(), __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().


Generated on Sat Feb 11 06:34:23 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6