[asterisk-commits] tilghman: branch tilghman/adaptive_realtime r115544 - in /team/tilghman/adapt...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed May 7 22:40:34 CDT 2008


Author: tilghman
Date: Wed May  7 22:40:33 2008
New Revision: 115544

URL: http://svn.digium.com/view/asterisk?view=rev&rev=115544
Log:
First pass

Modified:
    team/tilghman/adaptive_realtime/include/asterisk/config.h
    team/tilghman/adaptive_realtime/include/asterisk/res_odbc.h
    team/tilghman/adaptive_realtime/main/config.c
    team/tilghman/adaptive_realtime/res/res_config_odbc.c

Modified: team/tilghman/adaptive_realtime/include/asterisk/config.h
URL: http://svn.digium.com/view/asterisk/team/tilghman/adaptive_realtime/include/asterisk/config.h?view=diff&rev=115544&r1=115543&r2=115544
==============================================================================
--- team/tilghman/adaptive_realtime/include/asterisk/config.h (original)
+++ team/tilghman/adaptive_realtime/include/asterisk/config.h Wed May  7 22:40:33 2008
@@ -70,6 +70,7 @@
 typedef int realtime_update(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap);
 typedef int realtime_store(const char *database, const char *table, va_list ap);
 typedef int realtime_destroy(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap);
+typedef int realtime_require(const char *database, const char *table, va_list ap);
 
 /*! \brief Configuration engine structure, used to define realtime drivers */
 struct ast_config_engine {
@@ -80,6 +81,7 @@
 	realtime_update *update_func;
 	realtime_store *store_func;
 	realtime_destroy *destroy_func;
+	realtime_require *require_func;
 	struct ast_config_engine *next;
 };
 
@@ -185,6 +187,18 @@
 struct ast_variable *ast_load_realtime(const char *family, ...) attribute_sentinel;
 struct ast_variable *ast_load_realtime_all(const char *family, ...) attribute_sentinel;
 
+/*!
+ * \brief Inform realtime what fields that may be stored
+ * \param family which family/config is referenced
+ * This will inform builtin configuration backends that particular fields
+ * may be updated during the use of that configuration section.  This is
+ * mainly to be used during startup routines, to ensure that various fields
+ * exist in the backend.  The backends may take various actions, such as
+ * creating new fields in the data store or warning the administrator that
+ * new fields may need to be created, in order to ensure proper function.
+ */
+int ast_require_realtime_fields(const char *family, ...) attribute_sentinel;
+
 /*! 
  * \brief Retrieve realtime configuration 
  * \param family which family/config to lookup
@@ -231,6 +245,8 @@
  * \return 1 if family is configured in realtime and engine exists
 */
 int ast_check_realtime(const char *family);
+
+int ast_realtime_require_field(const char *family, ...) attribute_sentinel;
 
 /*! \brief Check if there's any realtime engines loaded */
 int ast_realtime_enabled(void);

Modified: team/tilghman/adaptive_realtime/include/asterisk/res_odbc.h
URL: http://svn.digium.com/view/asterisk/team/tilghman/adaptive_realtime/include/asterisk/res_odbc.h?view=diff&rev=115544&r1=115543&r2=115544
==============================================================================
--- team/tilghman/adaptive_realtime/include/asterisk/res_odbc.h (original)
+++ team/tilghman/adaptive_realtime/include/asterisk/res_odbc.h Wed May  7 22:40:33 2008
@@ -30,8 +30,17 @@
 #include <sql.h>
 #include <sqlext.h>
 #include <sqltypes.h>
+#include "asterisk/linkedlists.h"
 
 typedef enum { ODBC_SUCCESS=0, ODBC_FAIL=-1} odbc_status;
+
+typedef enum {
+	RQ_INTEGER,
+	RQ_CHAR,
+	RQ_FLOAT,
+	RQ_DATE,
+	RQ_DATETIME,
+} require_type;
 
 /*! \brief ODBC container */
 struct odbc_obj {
@@ -42,6 +51,27 @@
 	unsigned int used:1;
 	unsigned int up:1;
 	AST_LIST_ENTRY(odbc_obj) list;
+};
+
+/*!\brief These aren't used in any API calls, but they are kept in a common
+ * location, simply for convenience and to avoid duplication.
+ */
+struct odbc_cache_columns {
+	char *name;
+	SQLSMALLINT type;
+	SQLINTEGER size;
+	SQLSMALLINT decimals;
+	SQLSMALLINT radix;
+	SQLSMALLINT nullable;
+	SQLINTEGER octetlen;
+	AST_RWLIST_ENTRY(odbc_cache_columns) list;
+};
+
+struct odbc_cache_tables {
+	char *connection;
+	char *table;
+	AST_RWLIST_HEAD(_columns, odbc_cache_columns) columns;
+	AST_RWLIST_ENTRY(odbc_cache_tables) list;
 };
 
 /* functions */

Modified: team/tilghman/adaptive_realtime/main/config.c
URL: http://svn.digium.com/view/asterisk/team/tilghman/adaptive_realtime/main/config.c?view=diff&rev=115544&r1=115543&r2=115544
==============================================================================
--- team/tilghman/adaptive_realtime/main/config.c (original)
+++ team/tilghman/adaptive_realtime/main/config.c Wed May  7 22:40:33 2008
@@ -2097,6 +2097,24 @@
 	return config_maps ? 1 : 0;
 }
 
+int ast_realtime_require_field(const char *family, ...)
+{
+	struct ast_config_engine *eng;
+	char db[256] = "";
+	char table[256] = "";
+	va_list ap;
+	int res = -1;
+
+	va_start(ap, family);
+	eng = find_engine(family, db, sizeof(db), table, sizeof(table));
+	if (eng && eng->require_func) {
+		res = eng->require_func(db, table, ap);
+	}
+	va_end(ap);
+
+	return res;
+}
+
 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
 {
 	struct ast_config_engine *eng;

Modified: team/tilghman/adaptive_realtime/res/res_config_odbc.c
URL: http://svn.digium.com/view/asterisk/team/tilghman/adaptive_realtime/res/res_config_odbc.c?view=diff&rev=115544&r1=115543&r2=115544
==============================================================================
--- team/tilghman/adaptive_realtime/res/res_config_odbc.c (original)
+++ team/tilghman/adaptive_realtime/res/res_config_odbc.c Wed May  7 22:40:33 2008
@@ -52,6 +52,139 @@
 	const char *extra;
 	va_list ap;
 };
+
+/*!\brief The structures referenced are in include/asterisk/res_odbc.h */
+static AST_RWLIST_HEAD_STATIC(odbc_tables, odbc_cache_tables);
+
+static void destroy_table_cache(struct odbc_cache_tables *table) {
+	struct odbc_cache_columns *col;
+	AST_RWLIST_WRLOCK(&table->columns);
+	while ((col = AST_RWLIST_REMOVE_HEAD(&table->columns, list))) {
+		ast_free(col);
+	}
+	AST_RWLIST_UNLOCK(&table->columns);
+	AST_RWLIST_HEAD_DESTROY(&table->columns);
+	ast_free(table);
+}
+
+/*!
+ * \brief Find or create an entry describing the table specified.
+ * \param obj An active ODBC handle on which to query the table
+ * \param table Tablename to describe
+ * \retval A structure describing the table layout, or NULL, if the table is not found or another error occurs.
+ * When a structure is returned, the contained columns list will be
+ * rdlock'ed, to ensure that it will be retained in memory.
+ */
+static struct odbc_cache_tables *find_table(const char *database, const char *table)
+{
+	struct odbc_cache_tables *tableptr;
+	struct odbc_cache_columns *entry;
+	char columnname[80];
+	SQLLEN sqlptr;
+	SQLHSTMT stmt = NULL;
+	int res = 0, error = 0, try = 0;
+	struct odbc_obj *obj = ast_odbc_request_obj(database, 0);
+
+	AST_RWLIST_RDLOCK(&odbc_tables);
+	AST_RWLIST_TRAVERSE(&odbc_tables, tableptr, list) {
+		if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, table) == 0) {
+			break;
+		}
+	}
+	if (table) {
+		AST_RWLIST_RDLOCK(&tableptr->columns);
+		AST_RWLIST_UNLOCK(&odbc_tables);
+		return tableptr;
+	}
+
+	if (!obj) {
+		ast_log(LOG_WARNING, "Unable to retrieve database handle for table description '%s@%s'\n", table, database);
+		return NULL;
+	}
+
+	/* Table structure not already cached; build it now. */
+	do {
+retry:
+		res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+		if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+			if (try == 0) {
+				try = 1;
+				ast_odbc_sanity_check(obj);
+				goto retry;
+			}
+			ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", database);
+			break;
+		}
+
+		res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)table, SQL_NTS, (unsigned char *)"%", SQL_NTS);
+		if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+			if (try == 0) {
+				try = 1;
+				SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+				ast_odbc_sanity_check(obj);
+				goto retry;
+			}
+			ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'.\n", database);
+			break;
+		}
+
+		if (!(tableptr = ast_calloc(sizeof(char), sizeof(*table) + strlen(database) + 1 + strlen(table) + 1))) {
+			ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", table, database);
+			break;
+		}
+
+		tableptr->connection = (char *)tableptr + sizeof(*tableptr);
+		tableptr->table = (char *)tableptr + sizeof(*tableptr) + strlen(database) + 1;
+		strcpy(tableptr->connection, database); /* SAFE */
+		strcpy(tableptr->table, table); /* SAFE */
+		AST_RWLIST_HEAD_INIT(&(tableptr->columns));
+
+		while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) {
+			SQLGetData(stmt,  4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr);
+
+			if (!(entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1))) {
+				ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, table, database);
+				error = 1;
+				break;
+			}
+			entry->name = (char *)entry + sizeof(*entry);
+			strcpy(entry->name, columnname);
+
+			SQLGetData(stmt,  5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL);
+			SQLGetData(stmt,  7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL);
+			SQLGetData(stmt,  9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL);
+			SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL);
+			SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL);
+			SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL);
+
+			/* Specification states that the octenlen should be the maximum number of bytes
+			 * returned in a char or binary column, but it seems that some drivers just set
+			 * it to NULL. (Bad Postgres! No biscuit!) */
+			if (entry->octetlen == 0) {
+				entry->octetlen = entry->size;
+			}
+
+			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);
+			/* Insert column info into column list */
+			AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
+		}
+		SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+
+		AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list);
+		AST_RWLIST_RDLOCK(&(tableptr->columns));
+	} while (0);
+
+	AST_RWLIST_UNLOCK(&odbc_tables);
+
+	if (error) {
+		destroy_table_cache(tableptr);
+		table = NULL;
+	}
+	if (obj) {
+		ast_odbc_release_obj(obj);
+	}
+	return tableptr;
+}
 
 static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data)
 {
@@ -709,6 +842,83 @@
 	return cfg;
 }
 
+#define warn_length(col, size)	ast_log(LOG_WARNING, "Column %s is not long enough to contain realtime data (needs %d)\n", col->name, size)
+#define warn_type(col, type)	ast_log(LOG_WARNING, "Column %s is of the incorrect type to contain realtime data\n", col->name)
+
+static int require_odbc(const char *database, const char *table, va_list ap)
+{
+	struct odbc_cache_tables *tableptr = find_table(database, table);
+	struct odbc_cache_columns *col;
+	char *elm;
+	int type, size;
+
+	if (!tableptr) {
+		return -1;
+	}
+
+	while ((elm = va_arg(ap, char *))) {
+		type = va_arg(ap, int);
+		size = va_arg(ap, require_type);
+		/* TODO Check if the field matches the criteria */
+		AST_RWLIST_TRAVERSE(&tableptr->columns, col, list) {
+			if (strcmp(col->name, elm) == 0) {
+				/* Type check, first.  Some fields are more particular than others */
+				switch (col->type) {
+				case SQL_CHAR:
+				case SQL_VARCHAR:
+				case SQL_LONGVARCHAR:
+				case SQL_BINARY:
+				case SQL_VARBINARY:
+				case SQL_LONGVARBINARY:
+				case SQL_GUID:
+					if ((type == RQ_INTEGER || type == RQ_CHAR) && col->size < size) {
+						warn_length(col, size);
+					} else if (type == RQ_DATE && col->size < 10) {
+						warn_length(col, 10);
+					} else if (type == RQ_DATETIME && col->size < 19) {
+						warn_length(col, 19);
+					} else if (type == RQ_FLOAT && col->size < 10) {
+						warn_length(col, 10);
+					}
+					break;
+				case SQL_TYPE_DATE:
+					if (type != RQ_DATE) {
+						warn_type(col, type);
+					}
+					break;
+				case SQL_TYPE_TIMESTAMP:
+				case SQL_TIMESTAMP:
+					if (type != RQ_DATE && type != RQ_DATETIME) {
+						warn_type(col, type);
+					}
+					break;
+				case SQL_INTEGER:
+				case SQL_BIGINT:
+				case SQL_SMALLINT:
+				case SQL_TINYINT:
+				case SQL_BIT:
+					if (type != RQ_INTEGER) {
+						warn_type(col, type);
+					}
+					break;
+				case SQL_NUMERIC:
+				case SQL_DECIMAL:
+				case SQL_FLOAT:
+				case SQL_REAL:
+				case SQL_DOUBLE:
+					if (type != RQ_INTEGER && type != RQ_FLOAT) {
+						warn_type(col, type);
+					}
+					break;
+				}
+				break;
+			}
+		}
+	}
+	va_end(ap);
+	return 0;
+}
+
 static struct ast_config_engine odbc_engine = {
 	.name = "odbc",
 	.load_func = config_odbc,
@@ -716,7 +926,8 @@
 	.realtime_multi_func = realtime_multi_odbc,
 	.store_func = store_odbc,
 	.destroy_func = destroy_odbc,
-	.update_func = update_odbc
+	.update_func = update_odbc,
+	.require_func = require_odbc,
 };
 
 static int unload_module (void)




More information about the asterisk-commits mailing list