[asterisk-commits] tilghman: trunk r121683 - in /trunk: include/asterisk/ res/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Jun 10 16:14:58 CDT 2008


Author: tilghman
Date: Tue Jun 10 16:14:58 2008
New Revision: 121683

URL: http://svn.digium.com/view/asterisk?view=rev&rev=121683
Log:
Move the table cache routines to res_odbc, so they can be used from other
places (app_voicemail, for example).
(Related to bug #11678)

Modified:
    trunk/include/asterisk/res_odbc.h
    trunk/res/res_config_odbc.c
    trunk/res/res_odbc.c

Modified: trunk/include/asterisk/res_odbc.h
URL: http://svn.digium.com/view/asterisk/trunk/include/asterisk/res_odbc.h?view=diff&rev=121683&r1=121682&r2=121683
==============================================================================
--- trunk/include/asterisk/res_odbc.h (original)
+++ trunk/include/asterisk/res_odbc.h Tue Jun 10 16:14:58 2008
@@ -142,4 +142,35 @@
  */
 SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT (*prepare_cb)(struct odbc_obj *obj, void *data), void *data);
 
+/*!
+ * \brief Find or create an entry describing the table specified.
+ * \param database Name of an ODBC class 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.
+ */
+struct odbc_cache_tables *ast_odbc_find_table(const char *database, const char *tablename);
+
+/*!
+ * \brief Find a column entry within a cached table structure
+ * \param table Cached table structure, as returned from ast_odbc_find_table()
+ * \param colname The column name requested
+ * \retval A structure describing the column type, or NULL, if the column is not found.
+ */
+struct odbc_cache_columns *ast_odbc_find_column(struct odbc_cache_tables *table, const char *colname);
+
+/*!
+ * \brief Remove a cache entry from memory
+ * \param database Name of an ODBC class (used to ensure like-named tables in different databases are not confused)
+ * \param table Tablename for which a cached record should be removed
+ * \retval 0 if the cache entry was removed, or -1 if no matching entry was found.
+ */
+int ast_odbc_clear_cache(const char *database, const char *tablename);
+
+/*!
+ * \brief Release a table returned from ast_odbc_find_table
+ */
+#define ast_odbc_release_table(ptr) if (ptr) { AST_RWLIST_UNLOCK(&(ptr)->columns); }
+
 #endif /* _ASTERISK_RES_ODBC_H */

Modified: trunk/res/res_config_odbc.c
URL: http://svn.digium.com/view/asterisk/trunk/res/res_config_odbc.c?view=diff&rev=121683&r1=121682&r2=121683
==============================================================================
--- trunk/res/res_config_odbc.c (original)
+++ trunk/res/res_config_odbc.c Tue Jun 10 16:14:58 2008
@@ -53,153 +53,6 @@
 	va_list ap;
 	unsigned long long skip;
 };
-
-/*!\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_debug(1, "Destroying table cache for %s\n", table->table);
-	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);
-}
-
-#define release_table(ptr) if (ptr) { AST_RWLIST_UNLOCK(&(ptr)->columns); }
-
-/*!
- * \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 *tablename)
-{
-	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, tablename) == 0) {
-			break;
-		}
-	}
-	if (tableptr) {
-		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", tablename, 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 *)tablename, 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(*tableptr) + strlen(database) + 1 + strlen(tablename) + 1))) {
-			ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", tablename, 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, tablename); /* 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, tablename, 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);
-		tableptr = NULL;
-	}
-	if (obj) {
-		ast_odbc_release_obj(obj);
-	}
-	return tableptr;
-}
-
-static struct odbc_cache_columns *find_column(struct odbc_cache_tables *table, const char *colname)
-{
-	struct odbc_cache_columns *col;
-	AST_RWLIST_TRAVERSE(&table->columns, col, list) {
-		if (strcasecmp(col->name, colname) == 0) {
-			return col;
-		}
-	}
-	return NULL;
-}
 
 static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data)
 {
@@ -552,38 +405,38 @@
 	int res, count = 0;
 	va_list aq;
 	struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
-	struct odbc_cache_tables *tableptr = find_table(database, table);
+	struct odbc_cache_tables *tableptr = ast_odbc_find_table(database, table);
 	struct odbc_cache_columns *column;
 
 	va_copy(cps.ap, ap);
 	va_copy(aq, ap);
 	
 	if (!table) {
-		release_table(tableptr);
+		ast_odbc_release_table(tableptr);
 		return -1;
 	}
 
 	obj = ast_odbc_request_obj(database, 0);
 	if (!obj) {
-		release_table(tableptr);
+		ast_odbc_release_table(tableptr);
 		return -1;
 	}
 
 	newparam = va_arg(aq, const char *);
 	if (!newparam)  {
 		ast_odbc_release_obj(obj);
-		release_table(tableptr);
+		ast_odbc_release_table(tableptr);
 		return -1;
 	}
 	newval = va_arg(aq, const char *);
 
-	if (tableptr && !(column = find_column(tableptr, newparam))) {
+	if (tableptr && !(column = ast_odbc_find_column(tableptr, newparam))) {
 		ast_log(LOG_WARNING, "Key field '%s' does not exist in table '%s@%s'.  Update will fail\n", newparam, table, database);
 	}
 
 	snprintf(sql, sizeof(sql), "UPDATE %s SET %s=?", table, newparam);
 	while((newparam = va_arg(aq, const char *))) {
-		if ((tableptr && (column = find_column(tableptr, newparam))) || count > 63) {
+		if ((tableptr && (column = ast_odbc_find_column(tableptr, newparam))) || count > 63) {
 			snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s=?", newparam);
 			newval = va_arg(aq, const char *);
 		} else { /* the column does not exist in the table OR we've exceeded the space in our flag field */
@@ -593,7 +446,7 @@
 	}
 	va_end(aq);
 	snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield);
-	release_table(tableptr);
+	ast_odbc_release_table(tableptr);
 
 	stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
 
@@ -883,7 +736,7 @@
 
 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_tables *tableptr = ast_odbc_find_table(database, table);
 	struct odbc_cache_columns *col;
 	char *elm;
 	int type, size;
@@ -1034,23 +887,6 @@
 #undef warn_length
 #undef warn_type
 
-static int unload_odbc(const char *database, const char *tablename)
-{
-	struct odbc_cache_tables *tableptr;
-
-	AST_RWLIST_RDLOCK(&odbc_tables);
-	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&odbc_tables, tableptr, list) {
-		if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) {
-			AST_LIST_REMOVE_CURRENT(list);
-			destroy_table_cache(tableptr);
-			break;
-		}
-	}
-	AST_RWLIST_TRAVERSE_SAFE_END
-	AST_RWLIST_UNLOCK(&odbc_tables);
-	return tableptr ? 0 : -1;
-}
-
 static struct ast_config_engine odbc_engine = {
 	.name = "odbc",
 	.load_func = config_odbc,
@@ -1060,21 +896,12 @@
 	.destroy_func = destroy_odbc,
 	.update_func = update_odbc,
 	.require_func = require_odbc,
-	.unload_func = unload_odbc,
+	.unload_func = ast_odbc_clear_cache,
 };
 
 static int unload_module (void)
 {
-	struct odbc_cache_tables *table;
-
 	ast_config_engine_deregister(&odbc_engine);
-
-	/* Empty the cache */
-	AST_RWLIST_WRLOCK(&odbc_tables);
-	while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
-		destroy_table_cache(table);
-	}
-	AST_RWLIST_UNLOCK(&odbc_tables);
 
 	ast_verb(1, "res_config_odbc unloaded.\n");
 	return 0;
@@ -1089,15 +916,6 @@
 
 static int reload_module(void)
 {
-	struct odbc_cache_tables *table;
-
-	/* Empty the cache; it will get rebuilt the next time the tables are needed. */
-	AST_RWLIST_WRLOCK(&odbc_tables);
-	while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
-		destroy_table_cache(table);
-	}
-	AST_RWLIST_UNLOCK(&odbc_tables);
-
 	return 0;
 }
 

Modified: trunk/res/res_odbc.c
URL: http://svn.digium.com/view/asterisk/trunk/res/res_odbc.c?view=diff&rev=121683&r1=121682&r2=121683
==============================================================================
--- trunk/res/res_odbc.c (original)
+++ trunk/res/res_odbc.c Tue Jun 10 16:14:58 2008
@@ -69,6 +69,8 @@
 
 struct ao2_container *class_container;
 
+static AST_RWLIST_HEAD_STATIC(odbc_tables, odbc_cache_tables);
+
 static odbc_status odbc_obj_connect(struct odbc_obj *obj);
 static odbc_status odbc_obj_disconnect(struct odbc_obj *obj);
 static int odbc_register_class(struct odbc_class *class, int connect);
@@ -100,6 +102,165 @@
 	odbc_obj_disconnect(obj);
 	ast_mutex_destroy(&obj->lock);
 	ao2_ref(obj->parent, -1);
+}
+
+static void destroy_table_cache(struct odbc_cache_tables *table) {
+	struct odbc_cache_columns *col;
+	ast_debug(1, "Destroying table cache for %s\n", table->table);
+	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.
+ */
+struct odbc_cache_tables *ast_odbc_find_table(const char *database, const char *tablename)
+{
+	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, tablename) == 0) {
+			break;
+		}
+	}
+	if (tableptr) {
+		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", tablename, 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 *)tablename, 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(*tableptr) + strlen(database) + 1 + strlen(tablename) + 1))) {
+			ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", tablename, 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, tablename); /* 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, tablename, 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);
+		tableptr = NULL;
+	}
+	if (obj) {
+		ast_odbc_release_obj(obj);
+	}
+	return tableptr;
+}
+
+struct odbc_cache_columns *ast_odbc_find_column(struct odbc_cache_tables *table, const char *colname)
+{
+	struct odbc_cache_columns *col;
+	AST_RWLIST_TRAVERSE(&table->columns, col, list) {
+		if (strcasecmp(col->name, colname) == 0) {
+			return col;
+		}
+	}
+	return NULL;
+}
+
+int ast_odbc_clear_cache(const char *database, const char *tablename)
+{
+	struct odbc_cache_tables *tableptr;
+
+	AST_RWLIST_WRLOCK(&odbc_tables);
+	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&odbc_tables, tableptr, list) {
+		if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) {
+			AST_LIST_REMOVE_CURRENT(list);
+			destroy_table_cache(tableptr);
+			break;
+		}
+	}
+	AST_RWLIST_TRAVERSE_SAFE_END
+	AST_RWLIST_UNLOCK(&odbc_tables);
+	return tableptr ? 0 : -1;
 }
 
 SQLHSTMT ast_odbc_direct_execute(struct odbc_obj *obj, SQLHSTMT (*exec_cb)(struct odbc_obj *obj, void *data), void *data)
@@ -636,6 +797,7 @@
 
 static int reload(void)
 {
+	struct odbc_cache_tables *table;
 	struct odbc_class *class;
 	struct odbc_obj *current;
 	struct ao2_iterator aoi = ao2_iterator_init(class_container, 0);
@@ -694,6 +856,13 @@
 		ao2_ref(class, -1); /* C-ref-- (by iterator) */
 	}
 
+	/* Empty the cache; it will get rebuilt the next time the tables are needed. */
+	AST_RWLIST_WRLOCK(&odbc_tables);
+	while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
+		destroy_table_cache(table);
+	}
+	AST_RWLIST_UNLOCK(&odbc_tables);
+
 	return 0;
 }
 




More information about the asterisk-commits mailing list