[asterisk-commits] russell: branch russell/cdr-q r248532 - in /team/russell/cdr-q: ./ cdr/ confi...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Feb 24 00:10:36 CST 2010


Author: russell
Date: Wed Feb 24 00:10:33 2010
New Revision: 248532

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=248532
Log:
Add a new CDR backend registration method and adapt the backends that need it.

The CDR core now allows a CDR module to register multiple "sinks".  That way,
for a module that allows posting of records to multiple destinations, a
failure for posting to one destination can be handled specifically such that
a retry is only made for that specific destination, and not for the whole
CDR backend.  With that in place, the CDR core can ensure that every CDR
record is successfully logged exactly once to every intended destination.

I have also done some manual testing and things are working as expected.  I
still want to devise some good automated testing for this stuff, though ...

Modified:
    team/russell/cdr-q/UPGRADE.txt
    team/russell/cdr-q/cdr/cdr_adaptive_odbc.c
    team/russell/cdr-q/cdr/cdr_csv.c
    team/russell/cdr-q/cdr/cdr_custom.c
    team/russell/cdr-q/cdr/cdr_odbc.c
    team/russell/cdr-q/cdr/cdr_pgsql.c
    team/russell/cdr-q/cdr/cdr_radius.c
    team/russell/cdr-q/cdr/cdr_sqlite.c
    team/russell/cdr-q/cdr/cdr_sqlite3_custom.c
    team/russell/cdr-q/cdr/cdr_syslog.c
    team/russell/cdr-q/cdr/cdr_tds.c
    team/russell/cdr-q/configs/cdr.conf.sample
    team/russell/cdr-q/funcs/func_strings.c
    team/russell/cdr-q/include/asterisk/cdr.h
    team/russell/cdr-q/main/asterisk.c
    team/russell/cdr-q/main/cdr.c

Modified: team/russell/cdr-q/UPGRADE.txt
URL: http://svnview.digium.com/svn/asterisk/team/russell/cdr-q/UPGRADE.txt?view=diff&rev=248532&r1=248531&r2=248532
==============================================================================
--- team/russell/cdr-q/UPGRADE.txt (original)
+++ team/russell/cdr-q/UPGRADE.txt Wed Feb 24 00:10:33 2010
@@ -19,6 +19,14 @@
 ===========================================================
 
 From 1.6.2 to 1.8:
+
+* The cdr.conf options related to "batch mode" have been removed.  Much of the
+  CDR core has been rewritten and asynchronous posting of CDR records is now
+  the only behavior available.
+
+* The cdr_radius module will no longer be automatically enabled when loaded
+  into Asterisk.  Enabling this module is now configurable via an option in
+  the [radius] section in cdr.conf.
 
 * Asterisk-addons no longer exists as an independent package.  Those modules
   now live in the addons directory of the main Asterisk source tree.  They

Modified: team/russell/cdr-q/cdr/cdr_adaptive_odbc.c
URL: http://svnview.digium.com/svn/asterisk/team/russell/cdr-q/cdr/cdr_adaptive_odbc.c?view=diff&rev=248532&r1=248531&r2=248532
==============================================================================
--- team/russell/cdr-q/cdr/cdr_adaptive_odbc.c (original)
+++ team/russell/cdr-q/cdr/cdr_adaptive_odbc.c Wed Feb 24 00:10:33 2010
@@ -2,6 +2,7 @@
  * Asterisk -- An open source telephony toolkit.
  *
  * Copyright (C) 2007, Tilghman Lesher
+ * Copyright (C) 2010, Digium, Inc.
  *
  * Tilghman Lesher <cdr_adaptive_odbc__v1 at the-tilghman.com>
  *
@@ -16,8 +17,8 @@
  * at the top of the source tree.
  */
 
-/*! \file
- *
+/*!
+ * \file
  * \brief Adaptive ODBC CDR backend
  *
  * \author Tilghman Lesher <cdr_adaptive_odbc__v1 at the-tilghman.com>
@@ -32,9 +33,6 @@
 #include "asterisk.h"
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <sys/types.h>
-#include <time.h>
 
 #include <sql.h>
 #include <sqlext.h>
@@ -50,7 +48,8 @@
 
 #define	CONFIG	"cdr_adaptive_odbc.conf"
 
-static char *name = "Adaptive ODBC";
+static const char name[] = "Adaptive ODBC";
+
 /* Optimization to reduce number of memory allocations */
 static int maxsize = 512, maxsize2 = 512;
 
@@ -71,12 +70,15 @@
 struct tables {
 	char *connection;
 	char *table;
+	struct ast_cdr_backend *backend;
 	unsigned int usegmtime:1;
 	AST_LIST_HEAD_NOLOCK(odbc_columns, columns) columns;
 	AST_RWLIST_ENTRY(tables) list;
 };
 
 static AST_RWLIST_HEAD_STATIC(odbc_tables, tables);
+
+static enum ast_cdr_sink_res sink_cb(struct ast_cdr *cdr, void *data);
 
 static int load_config(void)
 {
@@ -103,8 +105,9 @@
 
 	for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
 		var = ast_variable_browse(cfg, catg);
-		if (!var)
+		if (!var) {
 			continue;
+		}
 
 		if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "connection"))) {
 			ast_log(LOG_WARNING, "No connection parameter found in '%s'.  Skipping.\n", catg);
@@ -158,6 +161,9 @@
 		tableptr->table = (char *)tableptr + sizeof(*tableptr) + lenconnection + 1;
 		ast_copy_string(tableptr->connection, connection, lenconnection + 1);
 		ast_copy_string(tableptr->table, table, lentable + 1);
+
+		tableptr->backend = ast_cdr_register_sink(name, ast_module_info->description,
+					tableptr->table, sink_cb, tableptr);
 
 		ast_verb(3, "Found adaptive CDR table %s@%s.\n", tableptr->table, tableptr->connection);
 
@@ -258,24 +264,38 @@
 		SQLFreeHandle(SQL_HANDLE_STMT, stmt);
 		ast_odbc_release_obj(obj);
 
-		if (AST_LIST_FIRST(&(tableptr->columns)))
+		if (AST_LIST_FIRST(&(tableptr->columns))) {
 			AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list);
-		else
+		} else {
 			ast_free(tableptr);
-	}
+		}
+	}
+
 	return res;
+}
+
+static void destroy_table(struct tables *table)
+{
+	struct columns *entry;
+
+	ast_cdr_unregister_sink(table->backend);
+	table->backend = NULL;
+
+	while ((entry = AST_LIST_REMOVE_HEAD(&(table->columns), list))) {
+		ast_free(entry);
+	}
+
+	ast_free(table);
 }
 
 static int free_config(void)
 {
 	struct tables *table;
-	struct columns *entry;
+
 	while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
-		while ((entry = AST_LIST_REMOVE_HEAD(&(table->columns), list))) {
-			ast_free(entry);
-		}
-		ast_free(table);
-	}
+		destroy_table(table);
+	}
+
 	return 0;
 }
 
@@ -339,9 +359,9 @@
 				}																\
 			} while (0)
 
-static int odbc_log(struct ast_cdr *cdr)
+static enum ast_cdr_sink_res sink_cb(struct ast_cdr *cdr, void *data)
 {
-	struct tables *tableptr;
+	struct tables *tableptr = data;
 	struct columns *entry;
 	struct odbc_obj *obj;
 	struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);
@@ -349,323 +369,314 @@
 	char colbuf[1024], *colptr;
 	SQLHSTMT stmt = NULL;
 	SQLLEN rows = 0;
+	int first = 1;
 
 	if (!sql || !sql2) {
 		if (sql)
 			ast_free(sql);
 		if (sql2)
 			ast_free(sql2);
-		return -1;
-	}
-
-	if (AST_RWLIST_RDLOCK(&odbc_tables)) {
-		ast_log(LOG_ERROR, "Unable to lock table list.  Insert CDR(s) failed.\n");
-		ast_free(sql);
-		ast_free(sql2);
-		return -1;
-	}
-
-	AST_LIST_TRAVERSE(&odbc_tables, tableptr, list) {
-		int first = 1;
-		ast_str_set(&sql, 0, "INSERT INTO %s (", tableptr->table);
-		ast_str_set(&sql2, 0, " VALUES (");
-
-		/* No need to check the connection now; we'll handle any failure in prepare_and_execute */
-		if (!(obj = ast_odbc_request_obj(tableptr->connection, 0))) {
-			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));
-			continue;
-		}
-
-		AST_LIST_TRAVERSE(&(tableptr->columns), entry, list) {
-			int datefield = 0;
-			if (strcasecmp(entry->cdrname, "start") == 0) {
-				datefield = 1;
-			} else if (strcasecmp(entry->cdrname, "answer") == 0) {
-				datefield = 2;
-			} else if (strcasecmp(entry->cdrname, "end") == 0) {
-				datefield = 3;
-			}
-
-			/* Check if we have a similarly named variable */
-			if (entry->staticvalue) {
-				colptr = ast_strdupa(entry->staticvalue);
-			} else if (datefield && tableptr->usegmtime) {
-				struct timeval date_tv = (datefield == 1) ? cdr->start : (datefield == 2) ? cdr->answer : cdr->end;
-				struct ast_tm tm = { 0, };
-				ast_localtime(&date_tv, &tm, "UTC");
-				ast_strftime(colbuf, sizeof(colbuf), "%Y-%m-%d %H:%M:%S", &tm);
-				colptr = colbuf;
-			} else {
-				ast_cdr_getvar(cdr, entry->cdrname, &colptr, colbuf, sizeof(colbuf), 0, datefield ? 0 : 1);
-			}
-
-			if (colptr) {
-				/* Check first if the column filters this entry.  Note that this
-				 * is very specifically NOT ast_strlen_zero(), because the filter
-				 * could legitimately specify that the field is blank, which is
-				 * different from the field being unspecified (NULL). */
-				if (entry->filtervalue && strcasecmp(colptr, entry->filtervalue) != 0) {
-					ast_verb(4, "CDR column '%s' with value '%s' does not match filter of"
-						" '%s'.  Cancelling this CDR.\n",
-						entry->cdrname, colptr, entry->filtervalue);
-					goto early_release;
-				}
-
-				/* Only a filter? */
-				if (ast_strlen_zero(entry->name))
-					continue;
-
-				LENGTHEN_BUF1(strlen(entry->name));
-
-				switch (entry->type) {
-				case SQL_CHAR:
-				case SQL_VARCHAR:
-				case SQL_LONGVARCHAR:
-				case SQL_BINARY:
-				case SQL_VARBINARY:
-				case SQL_LONGVARBINARY:
-				case SQL_GUID:
-					/* For these two field names, get the rendered form, instead of the raw
-					 * form (but only when we're dealing with a character-based field).
-					 */
-					if (strcasecmp(entry->name, "disposition") == 0) {
-						ast_cdr_getvar(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0, 0);
-					} else if (strcasecmp(entry->name, "amaflags") == 0) {
-						ast_cdr_getvar(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0, 0);
-					}
-
-					/* Truncate too-long fields */
-					if (entry->type != SQL_GUID) {
-						if (strlen(colptr) > entry->octetlen) {
-							colptr[entry->octetlen] = '\0';
-						}
-					}
-
-					ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
-					LENGTHEN_BUF2(strlen(colptr));
-
-					/* Encode value, with escaping */
-					ast_str_append(&sql2, 0, "%s'", first ? "" : ",");
-					for (tmp = colptr; *tmp; tmp++) {
-						if (*tmp == '\'') {
-							ast_str_append(&sql2, 0, "''");
-						} else if (*tmp == '\\' && ast_odbc_backslash_is_escape(obj)) {
-							ast_str_append(&sql2, 0, "\\\\");
-						} else {
-							ast_str_append(&sql2, 0, "%c", *tmp);
-						}
-					}
-					ast_str_append(&sql2, 0, "'");
-					break;
-				case SQL_TYPE_DATE:
-					if (ast_strlen_zero(colptr)) {
-						continue;
+		return AST_CDR_SINK_FAILURE;
+	}
+
+	ast_str_set(&sql, 0, "INSERT INTO %s (", tableptr->table);
+	ast_str_set(&sql2, 0, " VALUES (");
+
+	/* No need to check the connection now; we'll handle any failure in prepare_and_execute */
+	if (!(obj = ast_odbc_request_obj(tableptr->connection, 0))) {
+		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));
+		return AST_CDR_SINK_FAILURE;
+	}
+
+	AST_LIST_TRAVERSE(&(tableptr->columns), entry, list) {
+		int datefield = 0;
+		if (strcasecmp(entry->cdrname, "start") == 0) {
+			datefield = 1;
+		} else if (strcasecmp(entry->cdrname, "answer") == 0) {
+			datefield = 2;
+		} else if (strcasecmp(entry->cdrname, "end") == 0) {
+			datefield = 3;
+		}
+
+		/* Check if we have a similarly named variable */
+		if (entry->staticvalue) {
+			colptr = ast_strdupa(entry->staticvalue);
+		} else if (datefield && tableptr->usegmtime) {
+			struct timeval date_tv = (datefield == 1) ? cdr->start : (datefield == 2) ? cdr->answer : cdr->end;
+			struct ast_tm tm = { 0, };
+			ast_localtime(&date_tv, &tm, "UTC");
+			ast_strftime(colbuf, sizeof(colbuf), "%Y-%m-%d %H:%M:%S", &tm);
+			colptr = colbuf;
+		} else {
+			ast_cdr_getvar(cdr, entry->cdrname, &colptr, colbuf, sizeof(colbuf), 0, datefield ? 0 : 1);
+		}
+
+		if (colptr) {
+			/* Check first if the column filters this entry.  Note that this
+			 * is very specifically NOT ast_strlen_zero(), because the filter
+			 * could legitimately specify that the field is blank, which is
+			 * different from the field being unspecified (NULL). */
+			if (entry->filtervalue && strcasecmp(colptr, entry->filtervalue) != 0) {
+				ast_verb(4, "CDR column '%s' with value '%s' does not match filter of"
+					" '%s'.  Cancelling this CDR.\n",
+					entry->cdrname, colptr, entry->filtervalue);
+				goto early_release;
+			}
+
+			/* Only a filter? */
+			if (ast_strlen_zero(entry->name)) {
+				continue;
+			}
+
+			LENGTHEN_BUF1(strlen(entry->name));
+
+			switch (entry->type) {
+			case SQL_CHAR:
+			case SQL_VARCHAR:
+			case SQL_LONGVARCHAR:
+			case SQL_BINARY:
+			case SQL_VARBINARY:
+			case SQL_LONGVARBINARY:
+			case SQL_GUID:
+				/* For these two field names, get the rendered form, instead of the raw
+				 * form (but only when we're dealing with a character-based field).
+				 */
+				if (strcasecmp(entry->name, "disposition") == 0) {
+					ast_cdr_getvar(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0, 0);
+				} else if (strcasecmp(entry->name, "amaflags") == 0) {
+					ast_cdr_getvar(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0, 0);
+				}
+
+				/* Truncate too-long fields */
+				if (entry->type != SQL_GUID) {
+					if (strlen(colptr) > entry->octetlen) {
+						colptr[entry->octetlen] = '\0';
+					}
+				}
+
+				ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+				LENGTHEN_BUF2(strlen(colptr));
+
+				/* Encode value, with escaping */
+				ast_str_append(&sql2, 0, "%s'", first ? "" : ",");
+				for (tmp = colptr; *tmp; tmp++) {
+					if (*tmp == '\'') {
+						ast_str_append(&sql2, 0, "''");
+					} else if (*tmp == '\\' && ast_odbc_backslash_is_escape(obj)) {
+						ast_str_append(&sql2, 0, "\\\\");
 					} else {
-						int year = 0, month = 0, day = 0;
-						if (sscanf(colptr, "%4d-%2d-%2d", &year, &month, &day) != 3 || year <= 0 ||
-							month <= 0 || month > 12 || day < 0 || day > 31 ||
-							((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
-							(month == 2 && year % 400 == 0 && day > 29) ||
-							(month == 2 && year % 100 == 0 && day > 28) ||
-							(month == 2 && year % 4 == 0 && day > 29) ||
-							(month == 2 && year % 4 != 0 && day > 28)) {
-							ast_log(LOG_WARNING, "CDR variable %s is not a valid date ('%s').\n", entry->name, colptr);
-							continue;
-						}
-
-						if (year > 0 && year < 100) {
-							year += 2000;
-						}
-
-						ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
-						LENGTHEN_BUF2(17);
-						ast_str_append(&sql2, 0, "%s{ d '%04d-%02d-%02d' }", first ? "" : ",", year, month, day);
-					}
-					break;
-				case SQL_TYPE_TIME:
-					if (ast_strlen_zero(colptr)) {
-						continue;
-					} else {
-						int hour = 0, minute = 0, second = 0;
-						int count = sscanf(colptr, "%2d:%2d:%2d", &hour, &minute, &second);
-
-						if ((count != 2 && count != 3) || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) {
-							ast_log(LOG_WARNING, "CDR variable %s is not a valid time ('%s').\n", entry->name, colptr);
-							continue;
-						}
-
-						ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
-						LENGTHEN_BUF2(15);
-						ast_str_append(&sql2, 0, "%s{ t '%02d:%02d:%02d' }", first ? "" : ",", hour, minute, second);
-					}
-					break;
-				case SQL_TYPE_TIMESTAMP:
-				case SQL_TIMESTAMP:
-					if (ast_strlen_zero(colptr)) {
-						continue;
-					} else {
-						int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
-						int count = sscanf(colptr, "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &minute, &second);
-
-						if ((count != 3 && count != 5 && count != 6) || year <= 0 ||
-							month <= 0 || month > 12 || day < 0 || day > 31 ||
-							((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
-							(month == 2 && year % 400 == 0 && day > 29) ||
-							(month == 2 && year % 100 == 0 && day > 28) ||
-							(month == 2 && year % 4 == 0 && day > 29) ||
-							(month == 2 && year % 4 != 0 && day > 28) ||
-							hour > 23 || minute > 59 || second > 59 || hour < 0 || minute < 0 || second < 0) {
-							ast_log(LOG_WARNING, "CDR variable %s is not a valid timestamp ('%s').\n", entry->name, colptr);
-							continue;
-						}
-
-						if (year > 0 && year < 100) {
-							year += 2000;
-						}
-
-						ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
-						LENGTHEN_BUF2(26);
-						ast_str_append(&sql2, 0, "%s{ ts '%04d-%02d-%02d %02d:%02d:%02d' }", first ? "" : ",", year, month, day, hour, minute, second);
-					}
-					break;
-				case SQL_INTEGER:
-					if (ast_strlen_zero(colptr)) {
-						continue;
-					} else {
-						int integer = 0;
-						if (sscanf(colptr, "%30d", &integer) != 1) {
-							ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
-							continue;
-						}
-
-						ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
-						LENGTHEN_BUF2(12);
-						ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
-					}
-					break;
-				case SQL_BIGINT:
-					if (ast_strlen_zero(colptr)) {
-						continue;
-					} else {
-						long long integer = 0;
-						if (sscanf(colptr, "%30lld", &integer) != 1) {
-							ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
-							continue;
-						}
-
-						ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
-						LENGTHEN_BUF2(24);
-						ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", integer);
-					}
-					break;
-				case SQL_SMALLINT:
-					if (ast_strlen_zero(colptr)) {
-						continue;
-					} else {
-						short integer = 0;
-						if (sscanf(colptr, "%30hd", &integer) != 1) {
-							ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
-							continue;
-						}
-
-						ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
-						LENGTHEN_BUF2(6);
-						ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
-					}
-					break;
-				case SQL_TINYINT:
-					if (ast_strlen_zero(colptr)) {
-						continue;
-					} else {
-						char integer = 0;
-						if (sscanf(colptr, "%30hhd", &integer) != 1) {
-							ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
-							continue;
-						}
-
-						ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
-						LENGTHEN_BUF2(4);
-						ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
-					}
-					break;
-				case SQL_BIT:
-					if (ast_strlen_zero(colptr)) {
-						continue;
-					} else {
-						char integer = 0;
-						if (sscanf(colptr, "%30hhd", &integer) != 1) {
-							ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
-							continue;
-						}
-						if (integer != 0)
-							integer = 1;
-
-						ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
-						LENGTHEN_BUF2(2);
-						ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
-					}
-					break;
-				case SQL_NUMERIC:
-				case SQL_DECIMAL:
-					if (ast_strlen_zero(colptr)) {
-						continue;
-					} else {
-						double number = 0.0;
-						if (sscanf(colptr, "%30lf", &number) != 1) {
-							ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
-							continue;
-						}
-
-						ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
-						LENGTHEN_BUF2(entry->decimals);
-						ast_str_append(&sql2, 0, "%s%*.*lf", first ? "" : ",", entry->decimals, entry->radix, number);
-					}
-					break;
-				case SQL_FLOAT:
-				case SQL_REAL:
-				case SQL_DOUBLE:
-					if (ast_strlen_zero(colptr)) {
-						continue;
-					} else {
-						double number = 0.0;
-						if (sscanf(colptr, "%30lf", &number) != 1) {
-							ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
-							continue;
-						}
-
-						ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
-						LENGTHEN_BUF2(entry->decimals);
-						ast_str_append(&sql2, 0, "%s%lf", first ? "" : ",", number);
-					}
-					break;
-				default:
-					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);
-					continue;
-				}
-				first = 0;
-			}
-		}
-
-		/* Concatenate the two constructed buffers */
-		LENGTHEN_BUF1(ast_str_strlen(sql2));
-		ast_str_append(&sql, 0, ")");
-		ast_str_append(&sql2, 0, ")");
-		ast_str_append(&sql, 0, "%s", ast_str_buffer(sql2));
-
-		ast_verb(11, "[%s]\n", ast_str_buffer(sql));
-
-		stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, ast_str_buffer(sql));
-		if (stmt) {
-			SQLRowCount(stmt, &rows);
-			SQLFreeHandle(SQL_HANDLE_STMT, stmt);
-		}
-		if (rows == 0) {
-			ast_log(LOG_WARNING, "cdr_adaptive_odbc: Insert failed on '%s:%s'.  CDR failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql));
-		}
+						ast_str_append(&sql2, 0, "%c", *tmp);
+					}
+				}
+				ast_str_append(&sql2, 0, "'");
+				break;
+			case SQL_TYPE_DATE:
+				if (ast_strlen_zero(colptr)) {
+					continue;
+				} else {
+					int year = 0, month = 0, day = 0;
+					if (sscanf(colptr, "%4d-%2d-%2d", &year, &month, &day) != 3 || year <= 0 ||
+						month <= 0 || month > 12 || day < 0 || day > 31 ||
+						((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
+						(month == 2 && year % 400 == 0 && day > 29) ||
+						(month == 2 && year % 100 == 0 && day > 28) ||
+						(month == 2 && year % 4 == 0 && day > 29) ||
+						(month == 2 && year % 4 != 0 && day > 28)) {
+						ast_log(LOG_WARNING, "CDR variable %s is not a valid date ('%s').\n", entry->name, colptr);
+						continue;
+					}
+
+					if (year > 0 && year < 100) {
+						year += 2000;
+					}
+
+					ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+					LENGTHEN_BUF2(17);
+					ast_str_append(&sql2, 0, "%s{ d '%04d-%02d-%02d' }", first ? "" : ",", year, month, day);
+				}
+				break;
+			case SQL_TYPE_TIME:
+				if (ast_strlen_zero(colptr)) {
+					continue;
+				} else {
+					int hour = 0, minute = 0, second = 0;
+					int count = sscanf(colptr, "%2d:%2d:%2d", &hour, &minute, &second);
+
+					if ((count != 2 && count != 3) || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) {
+						ast_log(LOG_WARNING, "CDR variable %s is not a valid time ('%s').\n", entry->name, colptr);
+						continue;
+					}
+
+					ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+					LENGTHEN_BUF2(15);
+					ast_str_append(&sql2, 0, "%s{ t '%02d:%02d:%02d' }", first ? "" : ",", hour, minute, second);
+				}
+				break;
+			case SQL_TYPE_TIMESTAMP:
+			case SQL_TIMESTAMP:
+				if (ast_strlen_zero(colptr)) {
+					continue;
+				} else {
+					int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
+					int count = sscanf(colptr, "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &minute, &second);
+
+					if ((count != 3 && count != 5 && count != 6) || year <= 0 ||
+						month <= 0 || month > 12 || day < 0 || day > 31 ||
+						((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
+						(month == 2 && year % 400 == 0 && day > 29) ||
+						(month == 2 && year % 100 == 0 && day > 28) ||
+						(month == 2 && year % 4 == 0 && day > 29) ||
+						(month == 2 && year % 4 != 0 && day > 28) ||
+						hour > 23 || minute > 59 || second > 59 || hour < 0 || minute < 0 || second < 0) {
+						ast_log(LOG_WARNING, "CDR variable %s is not a valid timestamp ('%s').\n", entry->name, colptr);
+						continue;
+					}
+
+					if (year > 0 && year < 100) {
+						year += 2000;
+					}
+
+					ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+					LENGTHEN_BUF2(26);
+					ast_str_append(&sql2, 0, "%s{ ts '%04d-%02d-%02d %02d:%02d:%02d' }", first ? "" : ",", year, month, day, hour, minute, second);
+				}
+				break;
+			case SQL_INTEGER:
+				if (ast_strlen_zero(colptr)) {
+					continue;
+				} else {
+					int integer = 0;
+					if (sscanf(colptr, "%30d", &integer) != 1) {
+						ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
+						continue;
+					}
+
+					ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+					LENGTHEN_BUF2(12);
+					ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
+				}
+				break;
+			case SQL_BIGINT:
+				if (ast_strlen_zero(colptr)) {
+					continue;
+				} else {
+					long long integer = 0;
+					if (sscanf(colptr, "%30lld", &integer) != 1) {
+						ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
+						continue;
+					}
+
+					ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+					LENGTHEN_BUF2(24);
+					ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", integer);
+				}
+				break;
+			case SQL_SMALLINT:
+				if (ast_strlen_zero(colptr)) {
+					continue;
+				} else {
+					short integer = 0;
+					if (sscanf(colptr, "%30hd", &integer) != 1) {
+						ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
+						continue;
+					}
+
+					ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+					LENGTHEN_BUF2(6);
+					ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
+				}
+				break;
+			case SQL_TINYINT:
+				if (ast_strlen_zero(colptr)) {
+					continue;
+				} else {
+					char integer = 0;
+					if (sscanf(colptr, "%30hhd", &integer) != 1) {
+						ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
+						continue;
+					}
+
+					ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+					LENGTHEN_BUF2(4);
+					ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
+				}
+				break;
+			case SQL_BIT:
+				if (ast_strlen_zero(colptr)) {
+					continue;
+				} else {
+					char integer = 0;
+					if (sscanf(colptr, "%30hhd", &integer) != 1) {
+						ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
+						continue;
+					}
+					if (integer != 0)
+						integer = 1;
+
+					ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+					LENGTHEN_BUF2(2);
+					ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
+				}
+				break;
+			case SQL_NUMERIC:
+			case SQL_DECIMAL:
+				if (ast_strlen_zero(colptr)) {
+					continue;
+				} else {
+					double number = 0.0;
+					if (sscanf(colptr, "%30lf", &number) != 1) {
+						ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
+						continue;
+					}
+
+					ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+					LENGTHEN_BUF2(entry->decimals);
+					ast_str_append(&sql2, 0, "%s%*.*lf", first ? "" : ",", entry->decimals, entry->radix, number);
+				}
+				break;
+			case SQL_FLOAT:
+			case SQL_REAL:
+			case SQL_DOUBLE:
+				if (ast_strlen_zero(colptr)) {
+					continue;
+				} else {
+					double number = 0.0;
+					if (sscanf(colptr, "%30lf", &number) != 1) {
+						ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
+						continue;
+					}
+
+					ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+					LENGTHEN_BUF2(entry->decimals);
+					ast_str_append(&sql2, 0, "%s%lf", first ? "" : ",", number);
+				}
+				break;
+			default:
+				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);
+				continue;
+			}
+			first = 0;
+		}
+	}
+
+	/* Concatenate the two constructed buffers */
+	LENGTHEN_BUF1(ast_str_strlen(sql2));
+	ast_str_append(&sql, 0, ")");
+	ast_str_append(&sql2, 0, ")");
+	ast_str_append(&sql, 0, "%s", ast_str_buffer(sql2));
+
+	ast_verb(11, "[%s]\n", ast_str_buffer(sql));
+
+	stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, ast_str_buffer(sql));
+	if (stmt) {
+		SQLRowCount(stmt, &rows);
+		SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+	}
+	if (rows == 0) {
+		ast_log(LOG_WARNING, "cdr_adaptive_odbc: Insert failed on '%s:%s'.  CDR failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql));
+	}
 early_release:
-		ast_odbc_release_obj(obj);
-	}
-	AST_RWLIST_UNLOCK(&odbc_tables);
+	ast_odbc_release_obj(obj);
 
 	/* Next time, just allocate buffers that are that big to start with. */
 	if (ast_str_strlen(sql) > maxsize) {
@@ -677,20 +688,21 @@
 
 	ast_free(sql);
 	ast_free(sql2);
-	return 0;
+
+	return AST_CDR_SINK_SUCCESS;
 }
 
 static int unload_module(void)
 {
-	ast_cdr_unregister(name);
 	if (AST_RWLIST_WRLOCK(&odbc_tables)) {
-		ast_cdr_register(name, ast_module_info->description, odbc_log);
 		ast_log(LOG_ERROR, "Unable to lock column list.  Unload failed.\n");
 		return -1;
 	}
 
 	free_config();
+
 	AST_RWLIST_UNLOCK(&odbc_tables);
+
 	return 0;
 }
 
@@ -702,8 +714,9 @@
 	}
 
 	load_config();
+
 	AST_RWLIST_UNLOCK(&odbc_tables);
-	ast_cdr_register(name, ast_module_info->description, odbc_log);
+
 	return 0;
 }
 
@@ -716,7 +729,9 @@
 
 	free_config();
 	load_config();
+
 	AST_RWLIST_UNLOCK(&odbc_tables);
+
 	return 0;
 }
 

Modified: team/russell/cdr-q/cdr/cdr_csv.c
URL: http://svnview.digium.com/svn/asterisk/team/russell/cdr-q/cdr/cdr_csv.c?view=diff&rev=248532&r1=248531&r2=248532
==============================================================================
--- team/russell/cdr-q/cdr/cdr_csv.c (original)
+++ team/russell/cdr-q/cdr/cdr_csv.c Wed Feb 24 00:10:33 2010
@@ -31,8 +31,6 @@
 #include "asterisk.h"
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <time.h>
 
 #include "asterisk/paths.h"	/* use ast_config_AST_LOG_DIR */
 #include "asterisk/config.h"
@@ -133,7 +131,7 @@
 	return 1;
 }
 
-static int append_string(char *buf, char *s, size_t bufsize)
+static int append_string(char *buf, const char *s, size_t bufsize)
 {
 	int pos = strlen(buf), spos = 0, error = -1;
 

Modified: team/russell/cdr-q/cdr/cdr_custom.c
URL: http://svnview.digium.com/svn/asterisk/team/russell/cdr-q/cdr/cdr_custom.c?view=diff&rev=248532&r1=248531&r2=248532
==============================================================================
--- team/russell/cdr-q/cdr/cdr_custom.c (original)
+++ team/russell/cdr-q/cdr/cdr_custom.c Wed Feb 24 00:10:33 2010
@@ -1,7 +1,7 @@
 /*
  * Asterisk -- An open source telephony toolkit.
  *
- * Copyright (C) 1999 - 2009, Digium, Inc.
+ * Copyright (C) 1999 - 2010, Digium, Inc.
  *
  * Mark Spencer <markster at digium.com>
  *
@@ -18,8 +18,8 @@
  * at the top of the source tree.
  */
 
-/*! \file
- *
+/*!
+ * \file
  * \brief Custom Comma Separated Value CDR records.
  *
  * \author Mark Spencer <markster at digium.com>
@@ -27,14 +27,13 @@
  * \arg See also \ref AstCDR
  *
  * Logs in LOG_DIR/cdr_custom
+ *
  * \ingroup cdr_drivers
  */
 
 #include "asterisk.h"
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <time.h>
 
 #include "asterisk/paths.h"	/* use ast_config_AST_LOG_DIR */
 #include "asterisk/channel.h"
@@ -58,22 +57,43 @@
 	AST_DECLARE_STRING_FIELDS(
 		AST_STRING_FIELD(filename);
 		AST_STRING_FIELD(format);
-		);
+	);
+	struct ast_cdr_backend *backend;
 	ast_mutex_t lock;
 	AST_RWLIST_ENTRY(cdr_config) list;
 };
 
 static AST_RWLIST_HEAD_STATIC(sinks, cdr_config);
 
+static enum ast_cdr_sink_res cdr_sink_cb(struct ast_cdr *cdr, void *data);
+
+static void cdr_config_destroy(struct cdr_config *sink)
+{
+	ast_cdr_unregister_sink(sink->backend);
+	sink->backend = NULL;
+
+	ast_mutex_destroy(&sink->lock);
+
+	ast_free(sink);
+}
+
+/*!
+ * \internal
+ * \pre sinks list is locked
+ */
 static void free_config(void)
 {
 	struct cdr_config *sink;
+
 	while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) {
-		ast_mutex_destroy(&sink->lock);
-		ast_free(sink);
-	}
-}
-
+		cdr_config_destroy(sink);
+	}
+}
+
+/*!
+ * \internal
+ * \pre sinks list is locked
+ */
 static int load_config(void)
 {
 	struct ast_config *cfg;
@@ -87,101 +107,103 @@
 		return -1;
 	}
 
-	var = ast_variable_browse(cfg, "mappings");
-	while (var) {
-		if (!ast_strlen_zero(var->name) && !ast_strlen_zero(var->value)) {
-			struct cdr_config *sink = ast_calloc_with_stringfields(1, struct cdr_config, 1024);
-
-			if (!sink) {
-				ast_log(LOG_ERROR, "Unable to allocate memory for configuration settings.\n");
-				res = -2;
-				break;
-			}
-
-			ast_string_field_build(sink, format, "%s\n", var->value);
-			ast_string_field_build(sink, filename, "%s/%s/%s", ast_config_AST_LOG_DIR, name, var->name);
-			ast_mutex_init(&sink->lock);
-
-			AST_RWLIST_INSERT_TAIL(&sinks, sink, list);
-		} else {
+	for (var = ast_variable_browse(cfg, "mappings"); var; var = var->next) {
+		struct cdr_config *sink;
+
+		if (ast_strlen_zero(var->name) || ast_strlen_zero(var->value)) {
 			ast_log(LOG_NOTICE, "Mapping must have both a filename and a format at line %d\n", var->lineno);
+			continue;
 		}
-		var = var->next;
-	}
+
+		if (!(sink = ast_calloc_with_stringfields(1, struct cdr_config, 1024))) {
+			res = -2;
+			break;
+		}
+
+		ast_string_field_build(sink, format, "%s\n", var->value);
+		ast_string_field_build(sink, filename, "%s/%s/%s", ast_config_AST_LOG_DIR, name, var->name);
+		ast_mutex_init(&sink->lock);
+
+		sink->backend = ast_cdr_register_sink(name, ast_module_info->description, sink->filename,
+					cdr_sink_cb, sink);
+
+		AST_RWLIST_INSERT_TAIL(&sinks, sink, list);
+	}
+
 	ast_config_destroy(cfg);
 
 	return res;
 }
 
-static int custom_log(struct ast_cdr *cdr)
+static enum ast_cdr_sink_res cdr_sink_cb(struct ast_cdr *cdr, void *data)
 {
 	struct ast_channel *dummy;
 	struct ast_str *str;
-	struct cdr_config *config;
+	struct cdr_config *config = data;
+	FILE *out;
+	enum ast_cdr_sink_res sink_res = AST_CDR_SINK_SUCCESS;
 
 	/* Batching saves memory management here.  Otherwise, it's the same as doing an allocation and free each time. */
 	if (!(str = ast_str_thread_get(&custom_buf, 16))) {
-		return -1;
+		return AST_CDR_SINK_FAILURE;
 	}
 
 	dummy = ast_dummy_channel_alloc();
 
 	if (!dummy) {
 		ast_log(LOG_ERROR, "Unable to allocate channel for variable subsitution.\n");
-		return -1;
-	}
-
-	/* We need to dup here since the cdr actually belongs to the other channel,
-	   so when we release this channel we don't want the CDR getting cleaned
-	   up prematurely. */
+		return AST_CDR_SINK_FAILURE;
+	}
+
+	/* 
+	 * We need to dup here since the cdr actually belongs to the other channel,
+	 * so when we release this channel we don't want the CDR getting cleaned
+	 * up prematurely.
+	 */
 	dummy->cdr = ast_cdr_dup(cdr);
 
-	AST_RWLIST_RDLOCK(&sinks);
-
-	AST_LIST_TRAVERSE(&sinks, config, list) {
-		FILE *out;
-
-		ast_str_substitute_variables(&str, 0, dummy, config->format);
-
-		/* Even though we have a lock on the list, we could be being chased by
-		   another thread and this lock ensures that we won't step on anyone's
-		   toes.  Once each CDR backend gets it's own thread, this lock can be
-		   removed. */
-		ast_mutex_lock(&config->lock);
-
-		/* Because of the absolutely unconditional need for the
-		   highest reliability possible in writing billing records,
-		   we open write and close the log file each time */
-		if ((out = fopen(config->filename, "a"))) {
-			fputs(ast_str_buffer(str), out);
-			fflush(out); /* be particularly anal here */
-			fclose(out);
-		} else {
-			ast_log(LOG_ERROR, "Unable to re-open master file %s : %s\n", config->filename, strerror(errno));
-		}
-
-		ast_mutex_unlock(&config->lock);
-	}
-
-	AST_RWLIST_UNLOCK(&sinks);
+	ast_str_substitute_variables(&str, 0, dummy, config->format);
+
+	/* 
+	 * Even though we have a lock on the list, we could be being chased by
+	 * another thread and this lock ensures that we won't step on anyone's
+	 * toes.  Once each CDR backend gets it's own thread, this lock can be
+	 * removed.
+	 */
+	ast_mutex_lock(&config->lock);
+
+	/* 
+	 * Because of the absolutely unconditional need for the
+	 * highest reliability possible in writing billing records,
+	 * we open write and close the log file each time.
+	 */
+	if ((out = fopen(config->filename, "a"))) {
+		fputs(ast_str_buffer(str), out);
+		fflush(out); /* be particularly anal here */
+		fclose(out);
+	} else {
+		ast_log(LOG_ERROR, "Unable to re-open master file %s : %s\n", config->filename, strerror(errno));
+		sink_res = AST_CDR_SINK_FAILURE;
+	}
+
+	ast_mutex_unlock(&config->lock);
 
 	ast_channel_release(dummy);
 
-	return 0;
+	return sink_res;
 }
 
 static int unload_module(void)
 {
-	ast_cdr_unregister(name);
-
 	if (AST_RWLIST_WRLOCK(&sinks)) {
-		ast_cdr_register(name, ast_module_info->description, custom_log);
 		ast_log(LOG_ERROR, "Unable to lock sink list.  Unload failed.\n");
 		return -1;
 	}
 
 	free_config();
+
 	AST_RWLIST_UNLOCK(&sinks);
+
 	return 0;
 }
 
@@ -193,8 +215,9 @@
 	}
 
 	load_config();
+
 	AST_RWLIST_UNLOCK(&sinks);
-	ast_cdr_register(name, ast_module_info->description, custom_log);
+
 	return AST_MODULE_LOAD_SUCCESS;
 }
 
@@ -202,18 +225,20 @@
 {
 	if (AST_RWLIST_WRLOCK(&sinks)) {
 		ast_log(LOG_ERROR, "Unable to lock sink list.  Load failed.\n");
-		return AST_MODULE_LOAD_FAILURE;
+		return -1;
 	}
 
 	free_config();
 	load_config();
+
 	AST_RWLIST_UNLOCK(&sinks);
-	return AST_MODULE_LOAD_SUCCESS;
+
+	return 0;
 }
 
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Customizable Comma Separated Values CDR Backend",
-		.load = load_module,
-		.unload = unload_module,
-		.reload = reload,
-	       );
-
+	.load = load_module,
+	.unload = unload_module,
+	.reload = reload,
+);
+

Modified: team/russell/cdr-q/cdr/cdr_odbc.c
URL: http://svnview.digium.com/svn/asterisk/team/russell/cdr-q/cdr/cdr_odbc.c?view=diff&rev=248532&r1=248531&r2=248532
==============================================================================
--- team/russell/cdr-q/cdr/cdr_odbc.c (original)
+++ team/russell/cdr-q/cdr/cdr_odbc.c Wed Feb 24 00:10:33 2010
@@ -1,7 +1,7 @@
 /*
  * Asterisk -- An open source telephony toolkit.
  *
- * Copyright (C) 2003-2005, Digium, Inc.
+ * Copyright (C) 2003-2010, Digium, Inc.
  *
  * Brian K. West <brian at bkw.org>
  *
@@ -37,8 +37,6 @@
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
-#include <time.h>
-
 #include "asterisk/config.h"
 #include "asterisk/channel.h"
 #include "asterisk/cdr.h"
@@ -47,8 +45,8 @@
 
 #define DATE_FORMAT "%Y-%m-%d %T"
 
-static char *name = "ODBC";
-static char *config_file = "cdr_odbc.conf";
+static const char name[] = "ODBC";
+static const char config_file[] = "cdr_odbc.conf";
 static char *dsn = NULL, *table = NULL;
 
 enum {
@@ -101,7 +99,7 @@
 	SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->duration, 0, NULL);
 	SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->billsec, 0, NULL);
 	if (ast_test_flag(&config, CONFIG_DISPOSITIONSTRING))
-		SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ast_cdr_disp2str(cdr->disposition)) + 1, 0, ast_cdr_disp2str(cdr->disposition), 0, NULL);
+		SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ast_cdr_disp2str(cdr->disposition)) + 1, 0, (char *) ast_cdr_disp2str(cdr->disposition), 0, NULL);
 	else
 		SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->disposition, 0, NULL);
 	SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->amaflags, 0, NULL);
@@ -128,6 +126,7 @@
 {
 	struct odbc_obj *obj = ast_odbc_request_obj(dsn, 0);
 	SQLHSTMT stmt;
+	int res = 0;
 
 	if (!obj) {
 		ast_log(LOG_ERROR, "Unable to retrieve database handle.  CDR failed.\n");
@@ -143,9 +142,13 @@
 
 		if (rows == 0)
 			ast_log(LOG_WARNING, "CDR successfully ran, but inserted 0 rows?\n");
-	} else
+	} else {
 		ast_log(LOG_ERROR, "CDR direct execute failed\n");
+		res = -1;
+	}
+
 	ast_odbc_release_obj(obj);
+
 	return 0;
 }
 

Modified: team/russell/cdr-q/cdr/cdr_pgsql.c
URL: http://svnview.digium.com/svn/asterisk/team/russell/cdr-q/cdr/cdr_pgsql.c?view=diff&rev=248532&r1=248531&r2=248532
==============================================================================
--- team/russell/cdr-q/cdr/cdr_pgsql.c (original)
+++ team/russell/cdr-q/cdr/cdr_pgsql.c Wed Feb 24 00:10:33 2010
@@ -41,8 +41,6 @@
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
-#include <time.h>
-
 #include <libpq-fe.h>
 
 #include "asterisk/config.h"
@@ -52,8 +50,8 @@
 
 #define DATE_FORMAT "'%Y-%m-%d %T'"
 

[... 1540 lines stripped ...]



More information about the asterisk-commits mailing list