Wed Oct 28 13:31:52 2009

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_DEFAULT , .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, }
static struct ast_module_infoast_module_info = &__mod_info
static int maxsize = 512
static int maxsize2 = 512
static 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 __fini_odbc_tables ( void   )  [static]

Definition at line 79 of file cdr_adaptive_odbc.c.

00082 {

static void __init_odbc_tables ( void   )  [static]

Definition at line 79 of file cdr_adaptive_odbc.c.

00082 {

static void __reg_module ( void   )  [static]

Definition at line 707 of file cdr_adaptive_odbc.c.

static void __unreg_module ( void   )  [static]

Definition at line 707 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, and tables::columns.

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_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_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, ast_variable::next, columns::nullable, columns::octetlen, columns::radix, columns::size, columns::staticvalue, tables::table, columns::type, tables::usegmtime, usegmtime, ast_variable::value, 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 677 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().

00678 {
00679    if (AST_RWLIST_WRLOCK(&odbc_tables)) {
00680       ast_log(LOG_ERROR, "Unable to lock column list.  Load failed.\n");
00681       return 0;
00682    }
00683 
00684    load_config();
00685    AST_RWLIST_UNLOCK(&odbc_tables);
00686    ast_cdr_register(name, ast_module_info->description, odbc_log);
00687    return 0;
00688 }

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_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_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                {
00462                   int year = 0, month = 0, day = 0;
00463                   if (sscanf(colptr, "%4d-%2d-%2d", &year, &month, &day) != 3 || year <= 0 ||
00464                      month <= 0 || month > 12 || day < 0 || day > 31 ||
00465                      ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
00466                      (month == 2 && year % 400 == 0 && day > 29) ||
00467                      (month == 2 && year % 100 == 0 && day > 28) ||
00468                      (month == 2 && year % 4 == 0 && day > 29) ||
00469                      (month == 2 && year % 4 != 0 && day > 28)) {
00470                      ast_log(LOG_WARNING, "CDR variable %s is not a valid date ('%s').\n", entry->name, colptr);
00471                      continue;
00472                   }
00473 
00474                   if (year > 0 && year < 100) {
00475                      year += 2000;
00476                   }
00477 
00478                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00479                   LENGTHEN_BUF2(17);
00480                   ast_str_append(&sql2, 0, "%s{ d '%04d-%02d-%02d' }", first ? "" : ",", year, month, day);
00481                }
00482                break;
00483             case SQL_TYPE_TIME:
00484                {
00485                   int hour = 0, minute = 0, second = 0;
00486                   int count = sscanf(colptr, "%2d:%2d:%2d", &hour, &minute, &second);
00487 
00488                   if ((count != 2 && count != 3) || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) {
00489                      ast_log(LOG_WARNING, "CDR variable %s is not a valid time ('%s').\n", entry->name, colptr);
00490                      continue;
00491                   }
00492 
00493                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00494                   LENGTHEN_BUF2(15);
00495                   ast_str_append(&sql2, 0, "%s{ t '%02d:%02d:%02d' }", first ? "" : ",", hour, minute, second);
00496                }
00497                break;
00498             case SQL_TYPE_TIMESTAMP:
00499             case SQL_TIMESTAMP:
00500                {
00501                   int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
00502                   int count = sscanf(colptr, "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &minute, &second);
00503 
00504                   if ((count != 3 && count != 5 && count != 6) || year <= 0 ||
00505                      month <= 0 || month > 12 || day < 0 || day > 31 ||
00506                      ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
00507                      (month == 2 && year % 400 == 0 && day > 29) ||
00508                      (month == 2 && year % 100 == 0 && day > 28) ||
00509                      (month == 2 && year % 4 == 0 && day > 29) ||
00510                      (month == 2 && year % 4 != 0 && day > 28) ||
00511                      hour > 23 || minute > 59 || second > 59 || hour < 0 || minute < 0 || second < 0) {
00512                      ast_log(LOG_WARNING, "CDR variable %s is not a valid timestamp ('%s').\n", entry->name, colptr);
00513                      continue;
00514                   }
00515 
00516                   if (year > 0 && year < 100) {
00517                      year += 2000;
00518                   }
00519 
00520                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00521                   LENGTHEN_BUF2(26);
00522                   ast_str_append(&sql2, 0, "%s{ ts '%04d-%02d-%02d %02d:%02d:%02d' }", first ? "" : ",", year, month, day, hour, minute, second);
00523                }
00524                break;
00525             case SQL_INTEGER:
00526                {
00527                   int integer = 0;
00528                   if (sscanf(colptr, "%30d", &integer) != 1) {
00529                      ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00530                      continue;
00531                   }
00532 
00533                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00534                   LENGTHEN_BUF2(12);
00535                   ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
00536                }
00537                break;
00538             case SQL_BIGINT:
00539                {
00540                   long long integer = 0;
00541                   if (sscanf(colptr, "%30lld", &integer) != 1) {
00542                      ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00543                      continue;
00544                   }
00545 
00546                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00547                   LENGTHEN_BUF2(24);
00548                   ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", integer);
00549                }
00550                break;
00551             case SQL_SMALLINT:
00552                {
00553                   short integer = 0;
00554                   if (sscanf(colptr, "%30hd", &integer) != 1) {
00555                      ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00556                      continue;
00557                   }
00558 
00559                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00560                   LENGTHEN_BUF2(6);
00561                   ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
00562                }
00563                break;
00564             case SQL_TINYINT:
00565                {
00566                   char integer = 0;
00567                   if (sscanf(colptr, "%30hhd", &integer) != 1) {
00568                      ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00569                      continue;
00570                   }
00571 
00572                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00573                   LENGTHEN_BUF2(4);
00574                   ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
00575                }
00576                break;
00577             case SQL_BIT:
00578                {
00579                   char integer = 0;
00580                   if (sscanf(colptr, "%30hhd", &integer) != 1) {
00581                      ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00582                      continue;
00583                   }
00584                   if (integer != 0)
00585                      integer = 1;
00586 
00587                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00588                   LENGTHEN_BUF2(2);
00589                   ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
00590                }
00591                break;
00592             case SQL_NUMERIC:
00593             case SQL_DECIMAL:
00594                {
00595                   double number = 0.0;
00596                   if (sscanf(colptr, "%30lf", &number) != 1) {
00597                      ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
00598                      continue;
00599                   }
00600 
00601                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00602                   LENGTHEN_BUF2(entry->decimals);
00603                   ast_str_append(&sql2, 0, "%s%*.*lf", first ? "" : ",", entry->decimals, entry->radix, number);
00604                }
00605                break;
00606             case SQL_FLOAT:
00607             case SQL_REAL:
00608             case SQL_DOUBLE:
00609                {
00610                   double number = 0.0;
00611                   if (sscanf(colptr, "%30lf", &number) != 1) {
00612                      ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
00613                      continue;
00614                   }
00615 
00616                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00617                   LENGTHEN_BUF2(entry->decimals);
00618                   ast_str_append(&sql2, 0, "%s%lf", first ? "" : ",", number);
00619                }
00620                break;
00621             default:
00622                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);
00623                continue;
00624             }
00625             first = 0;
00626          }
00627       }
00628 
00629       /* Concatenate the two constructed buffers */
00630       LENGTHEN_BUF1(ast_str_strlen(sql2));
00631       ast_str_append(&sql, 0, ")");
00632       ast_str_append(&sql2, 0, ")");
00633       ast_str_append(&sql, 0, "%s", ast_str_buffer(sql2));
00634 
00635       ast_verb(11, "[%s]\n", ast_str_buffer(sql));
00636 
00637       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, ast_str_buffer(sql));
00638       if (stmt) {
00639          SQLRowCount(stmt, &rows);
00640          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00641       }
00642       if (rows == 0) {
00643          ast_log(LOG_WARNING, "cdr_adaptive_odbc: Insert failed on '%s:%s'.  CDR failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql));
00644       }
00645 early_release:
00646       ast_odbc_release_obj(obj);
00647    }
00648    AST_RWLIST_UNLOCK(&odbc_tables);
00649 
00650    /* Next time, just allocate buffers that are that big to start with. */
00651    if (ast_str_strlen(sql) > maxsize) {
00652       maxsize = ast_str_strlen(sql);
00653    }
00654    if (ast_str_strlen(sql2) > maxsize2) {
00655       maxsize2 = ast_str_strlen(sql2);
00656    }
00657 
00658    ast_free(sql);
00659    ast_free(sql2);
00660    return 0;
00661 }

static int reload ( void   )  [static]

Definition at line 690 of file cdr_adaptive_odbc.c.

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

00691 {
00692    if (AST_RWLIST_WRLOCK(&odbc_tables)) {
00693       ast_log(LOG_ERROR, "Unable to lock column list.  Reload failed.\n");
00694       return -1;
00695    }
00696 
00697    free_config();
00698    load_config();
00699    AST_RWLIST_UNLOCK(&odbc_tables);
00700    return 0;
00701 }

static int unload_module ( void   )  [static]

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

00664 {
00665    ast_cdr_unregister(name);
00666    if (AST_RWLIST_WRLOCK(&odbc_tables)) {
00667       ast_cdr_register(name, ast_module_info->description, odbc_log);
00668       ast_log(LOG_ERROR, "Unable to lock column list.  Unload failed.\n");
00669       return -1;
00670    }
00671 
00672    free_config();
00673    AST_RWLIST_UNLOCK(&odbc_tables);
00674    return 0;
00675 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .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, } [static]

Definition at line 707 of file cdr_adaptive_odbc.c.

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

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(), __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(), adsi_load(), aji_test(), analog_ss_thread(), ast_cel_fabricate_channel_from_event(), ast_channel_destructor(), ast_connected_line_source_parse(), 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_monitor_stop(), ast_parse_caller_presentation(), ast_redirecting_reason_parse(), ast_rtp_lookup_mime_multiple2(), ast_setstate(), ast_str2tos(), ast_syslog_facility(), ast_syslog_priority(), build_calendar(), callerid_read(), callerid_write(), change_monitor_action(), channel_spy(), cli_tps_ping(), cli_tps_report(), config_ldap(), connectedline_write(), do_pause_or_unpause(), dump_ies(), dump_prov_ies(), entry_cmp_fn(), fac2str(), group_cmp_fn(), handle_cli_status(), handle_redirect(), handle_register_message(), handle_showchan(), handle_tcptls_connection(), httpd_helper_thread(), load_module(), load_rpt_vars(), lua_func_read(), lua_get_variable(), lua_get_variable_value(), lua_set_variable(), lua_set_variable_value(), manager_rpt_local_nodes(), manager_rpt_status(), map_video_codec(), misdn_cfg_get_config_string(), misdn_cfg_get_name(), mwi_thread(), my_get_callerid(), oss_call(), oss_request(), parse_cookies(), parse_uri(), peek_read(), phone_request(), phoneprov_callback(), process_echocancel(), process_opcode(), process_returncode(), redirecting_id_write(), register_verify(), 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(), show_config_description(), 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 Wed Oct 28 13:31:53 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6