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, .support_level = AST_MODULE_SUPPORT_CORE, .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 60 of file cdr_adaptive_odbc.c.

Referenced by load_config().

#define LENGTHEN_BUF1 ( size   ) 

Definition at line 348 of file cdr_adaptive_odbc.c.

Referenced by odbc_log(), and pgsql_log().

#define LENGTHEN_BUF2 ( size   ) 

Definition at line 362 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 90 of file cdr_adaptive_odbc.c.

00093 {

static void __init_odbc_tables ( void   )  [static]

Definition at line 90 of file cdr_adaptive_odbc.c.

00093 {

static void __reg_module ( void   )  [static]

Definition at line 817 of file cdr_adaptive_odbc.c.

static void __unreg_module ( void   )  [static]

Definition at line 817 of file cdr_adaptive_odbc.c.

static int free_config ( void   )  [static]

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

00303 {
00304    struct tables *table;
00305    struct columns *entry;
00306    while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
00307       while ((entry = AST_LIST_REMOVE_HEAD(&(table->columns), list))) {
00308          ast_free(entry);
00309       }
00310       ast_free(table);
00311    }
00312    return 0;
00313 }

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

Definition at line 315 of file cdr_adaptive_odbc.c.

References ast_log, odbc_obj::con, LOG_WARNING, and NULL.

Referenced by odbc_log().

00316 {
00317    int res, i;
00318    SQLHSTMT stmt;
00319    SQLINTEGER nativeerror = 0, numfields = 0;
00320    SQLSMALLINT diagbytes = 0;
00321    unsigned char state[10], diagnostic[256];
00322 
00323    res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
00324    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00325       ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00326       return NULL;
00327    }
00328 
00329    res = SQLPrepare(stmt, (unsigned char *) data, SQL_NTS);
00330    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00331       ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", (char *) data);
00332       SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00333       for (i = 0; i < numfields; i++) {
00334          SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00335          ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00336          if (i > 10) {
00337             ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
00338             break;
00339          }
00340       }
00341       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00342       return NULL;
00343    }
00344 
00345    return stmt;
00346 }

static int load_config ( void   )  [static]

Definition at line 92 of file cdr_adaptive_odbc.c.

References ast_calloc, ast_category_browse(), ast_config_destroy(), 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, item, LOG_ERROR, LOG_NOTICE, LOG_WARNING, columns::name, ast_variable::name, columns::negatefiltervalue, ast_variable::next, NULL, columns::nullable, columns::octetlen, columns::radix, tables::schema, columns::size, columns::staticvalue, tables::table, tmp(), columns::type, tables::usegmtime, usegmtime, ast_variable::value, and var.

00093 {
00094    struct ast_config *cfg;
00095    struct ast_variable *var;
00096    const char *tmp, *catg;
00097    struct tables *tableptr;
00098    struct columns *entry;
00099    struct odbc_obj *obj;
00100    char columnname[80];
00101    char connection[40];
00102    char table[40];
00103    char schema[40];
00104    int lenconnection, lentable, lenschema, usegmtime = 0;
00105    SQLLEN sqlptr;
00106    int res = 0;
00107    SQLHSTMT stmt = NULL;
00108    struct ast_flags config_flags = { 0 }; /* Part of our config comes from the database */
00109 
00110    cfg = ast_config_load(CONFIG, config_flags);
00111    if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
00112       ast_log(LOG_WARNING, "Unable to load " CONFIG ".  No adaptive ODBC CDRs.\n");
00113       return -1;
00114    }
00115 
00116    for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
00117       var = ast_variable_browse(cfg, catg);
00118       if (!var)
00119          continue;
00120 
00121       if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "connection"))) {
00122          ast_log(LOG_WARNING, "No connection parameter found in '%s'.  Skipping.\n", catg);
00123          continue;
00124       }
00125       ast_copy_string(connection, tmp, sizeof(connection));
00126       lenconnection = strlen(connection);
00127 
00128       if (!ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "usegmtime"))) {
00129          usegmtime = ast_true(tmp);
00130       }
00131 
00132       /* When loading, we want to be sure we can connect. */
00133       obj = ast_odbc_request_obj(connection, 1);
00134       if (!obj) {
00135          ast_log(LOG_WARNING, "No such connection '%s' in the '%s' section of " CONFIG ".  Check res_odbc.conf.\n", connection, catg);
00136          continue;
00137       }
00138 
00139       if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "table"))) {
00140          ast_log(LOG_NOTICE, "No table name found.  Assuming 'cdr'.\n");
00141          tmp = "cdr";
00142       }
00143       ast_copy_string(table, tmp, sizeof(table));
00144       lentable = strlen(table);
00145 
00146       if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "schema"))) {
00147          tmp = "";
00148       }
00149       ast_copy_string(schema, tmp, sizeof(schema));
00150       lenschema = strlen(schema);
00151 
00152       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00153       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00154          ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", connection);
00155          ast_odbc_release_obj(obj);
00156          continue;
00157       }
00158 
00159       res = SQLColumns(stmt, NULL, 0, lenschema == 0 ? NULL : (unsigned char *)schema, SQL_NTS, (unsigned char *)table, SQL_NTS, (unsigned char *)"%", SQL_NTS);
00160       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00161          ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'.  Skipping.\n", connection);
00162          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00163          ast_odbc_release_obj(obj);
00164          continue;
00165       }
00166 
00167       tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + lenconnection + 1 + lentable + 1 + lenschema + 1);
00168       if (!tableptr) {
00169          ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'%s%s%s\n", table, connection,
00170             lenschema ? " (schema '" : "", lenschema ? schema : "", lenschema ? "')" : "");
00171          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00172          ast_odbc_release_obj(obj);
00173          res = -1;
00174          break;
00175       }
00176 
00177       tableptr->usegmtime = usegmtime;
00178       tableptr->connection = (char *)tableptr + sizeof(*tableptr);
00179       tableptr->table = (char *)tableptr + sizeof(*tableptr) + lenconnection + 1;
00180       tableptr->schema = (char *)tableptr + sizeof(*tableptr) + lenconnection + 1 + lentable + 1;
00181       ast_copy_string(tableptr->connection, connection, lenconnection + 1);
00182       ast_copy_string(tableptr->table, table, lentable + 1);
00183       ast_copy_string(tableptr->schema, schema, lenschema + 1);
00184 
00185       ast_verb(3, "Found adaptive CDR table %s@%s.\n", tableptr->table, tableptr->connection);
00186 
00187       /* Check for filters first */
00188       for (var = ast_variable_browse(cfg, catg); var; var = var->next) {
00189          if (strncmp(var->name, "filter", 6) == 0) {
00190             int negate = 0;
00191             char *cdrvar = ast_strdupa(var->name + 6);
00192             cdrvar = ast_strip(cdrvar);
00193             if (cdrvar[strlen(cdrvar) - 1] == '!') {
00194                negate = 1;
00195                cdrvar[strlen(cdrvar) - 1] = '\0';
00196                ast_trim_blanks(cdrvar);
00197             }
00198 
00199             ast_verb(3, "Found filter %s'%s' for CDR variable %s in %s@%s\n", negate ? "!" : "", var->value, cdrvar, tableptr->table, tableptr->connection);
00200 
00201             entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(cdrvar) + 1 + strlen(var->value) + 1);
00202             if (!entry) {
00203                ast_log(LOG_ERROR, "Out of memory creating filter entry for CDR variable '%s' in table '%s' on connection '%s'\n", cdrvar, table, connection);
00204                res = -1;
00205                break;
00206             }
00207 
00208             /* NULL column entry means this isn't a column in the database */
00209             entry->name = NULL;
00210             entry->cdrname = (char *)entry + sizeof(*entry);
00211             entry->filtervalue = (char *)entry + sizeof(*entry) + strlen(cdrvar) + 1;
00212             strcpy(entry->cdrname, cdrvar);
00213             strcpy(entry->filtervalue, var->value);
00214             entry->negatefiltervalue = negate;
00215 
00216             AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
00217          }
00218       }
00219 
00220       while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) {
00221          char *cdrvar = "", *staticvalue = "";
00222 
00223          SQLGetData(stmt,  4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr);
00224 
00225          /* Is there an alias for this column? */
00226 
00227          /* NOTE: This seems like a non-optimal parse method, but I'm going
00228           * for user configuration readability, rather than fast parsing. We
00229           * really don't parse this file all that often, anyway.
00230           */
00231          for (var = ast_variable_browse(cfg, catg); var; var = var->next) {
00232             if (strncmp(var->name, "alias", 5) == 0 && strcasecmp(var->value, columnname) == 0) {
00233                char *alias = ast_strdupa(var->name + 5);
00234                cdrvar = ast_strip(alias);
00235                ast_verb(3, "Found alias %s for column %s in %s@%s\n", cdrvar, columnname, tableptr->table, tableptr->connection);
00236                break;
00237             } else if (strncmp(var->name, "static", 6) == 0 && strcasecmp(var->value, columnname) == 0) {
00238                char *item = ast_strdupa(var->name + 6);
00239                item = ast_strip(item);
00240                if (item[0] == '"' && item[strlen(item) - 1] == '"') {
00241                   /* Remove surrounding quotes */
00242                   item[strlen(item) - 1] = '\0';
00243                   item++;
00244                }
00245                staticvalue = item;
00246             }
00247          }
00248 
00249          entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1 + strlen(cdrvar) + 1 + strlen(staticvalue) + 1);
00250          if (!entry) {
00251             ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, table, connection);
00252             res = -1;
00253             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00254             break;
00255          }
00256          entry->name = (char *)entry + sizeof(*entry);
00257          strcpy(entry->name, columnname);
00258 
00259          if (!ast_strlen_zero(cdrvar)) {
00260             entry->cdrname = entry->name + strlen(columnname) + 1;
00261             strcpy(entry->cdrname, cdrvar);
00262          } else { /* Point to same place as the column name */
00263             entry->cdrname = (char *)entry + sizeof(*entry);
00264          }
00265 
00266          if (!ast_strlen_zero(staticvalue)) {
00267             entry->staticvalue = entry->cdrname + strlen(entry->cdrname) + 1;
00268             strcpy(entry->staticvalue, staticvalue);
00269          }
00270 
00271          SQLGetData(stmt,  5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL);
00272          SQLGetData(stmt,  7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL);
00273          SQLGetData(stmt,  9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL);
00274          SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL);
00275          SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL);
00276          SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL);
00277 
00278          /* Specification states that the octenlen should be the maximum number of bytes
00279           * returned in a char or binary column, but it seems that some drivers just set
00280           * it to NULL. (Bad Postgres! No biscuit!) */
00281          if (entry->octetlen == 0)
00282             entry->octetlen = entry->size;
00283 
00284          ast_verb(4, "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);
00285          /* Insert column info into column list */
00286          AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
00287          res = 0;
00288       }
00289 
00290       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00291       ast_odbc_release_obj(obj);
00292 
00293       if (AST_LIST_FIRST(&(tableptr->columns)))
00294          AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list);
00295       else
00296          ast_free(tableptr);
00297    }
00298    ast_config_destroy(cfg);
00299    return res;
00300 }

static int load_module ( void   )  [static]

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

00786 {
00787    if (AST_RWLIST_WRLOCK(&odbc_tables)) {
00788       ast_log(LOG_ERROR, "Unable to lock column list.  Load failed.\n");
00789       return 0;
00790    }
00791 
00792    load_config();
00793    AST_RWLIST_UNLOCK(&odbc_tables);
00794    ast_cdr_register(name, ast_module_info->description, odbc_log);
00795    return 0;
00796 }

static int odbc_log ( struct ast_cdr cdr  )  [static]

Definition at line 375 of file cdr_adaptive_odbc.c.

References ast_cdr::answer, ast_cdr_format_var(), ast_copy_string(), ast_debug, ast_free, AST_LIST_TRAVERSE, ast_localtime(), ast_log, AST_LOG_WARNING, 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, columns::first, generic_prepare(), LENGTHEN_BUF1, LENGTHEN_BUF2, LOG_ERROR, LOG_WARNING, maxsize2, columns::name, columns::negatefiltervalue, NULL, columns::octetlen, columns::radix, tables::schema, ast_cdr::start, columns::staticvalue, tables::table, tmp(), columns::type, and tables::usegmtime.

Referenced by load_module(), odbc_load_module(), and unload_module().

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

static int reload ( void   )  [static]

Definition at line 798 of file cdr_adaptive_odbc.c.

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

00799 {
00800    if (AST_RWLIST_WRLOCK(&odbc_tables)) {
00801       ast_log(LOG_ERROR, "Unable to lock column list.  Reload failed.\n");
00802       return -1;
00803    }
00804 
00805    free_config();
00806    load_config();
00807    AST_RWLIST_UNLOCK(&odbc_tables);
00808    return 0;
00809 }

static int unload_module ( void   )  [static]

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

00769 {
00770    if (ast_cdr_unregister(name)) {
00771       return -1;
00772    }
00773 
00774    if (AST_RWLIST_WRLOCK(&odbc_tables)) {
00775       ast_cdr_register(name, ast_module_info->description, odbc_log);
00776       ast_log(LOG_ERROR, "Unable to lock column list.  Unload failed.\n");
00777       return -1;
00778    }
00779 
00780    free_config();
00781    AST_RWLIST_UNLOCK(&odbc_tables);
00782    return 0;
00783 }


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

Definition at line 817 of file cdr_adaptive_odbc.c.

Definition at line 817 of file cdr_adaptive_odbc.c.

int maxsize = 512 [static]

Definition at line 64 of file cdr_adaptive_odbc.c.

Referenced by ast_func_read2().

int maxsize2 = 512 [static]

Definition at line 64 of file cdr_adaptive_odbc.c.

Referenced by odbc_log(), and pgsql_log().

const char name[] = "Adaptive ODBC" [static]

Definition at line 62 of file cdr_adaptive_odbc.c.


Generated on Thu Apr 16 06:29:23 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6