[svn-commits] tilghman: trunk r148570 - in /trunk: apps/ include/asterisk/ main/ res/

SVN commits to the Digium repositories svn-commits at lists.digium.com
Mon Oct 13 19:08:53 CDT 2008


Author: tilghman
Date: Mon Oct 13 19:08:52 2008
New Revision: 148570

URL: http://svn.digium.com/view/asterisk?view=rev&rev=148570
Log:
Merge realtime_update2 branch, which adds a new realtime API call named
'update2', which permits updates which match across multiple columns, instead
of requiring all tables to have a single unique identifier.  All of the other
API calls with the exception of 'update' already had the ability to match on
multiple fields, so it was a missing and very desireable feature that an API
call implementing an update should have this, too.

This does not change any outward performance of Asterisk, but it should make
life easier for application developers who use the RealTime framework.

Modified:
    trunk/apps/app_voicemail.c
    trunk/include/asterisk/config.h
    trunk/include/asterisk/strings.h
    trunk/main/config.c
    trunk/res/res_config_curl.c
    trunk/res/res_config_ldap.c
    trunk/res/res_config_odbc.c
    trunk/res/res_config_pgsql.c
    trunk/res/res_config_sqlite.c
    trunk/res/res_realtime.c

Modified: trunk/apps/app_voicemail.c
URL: http://svn.digium.com/view/asterisk/trunk/apps/app_voicemail.c?view=diff&rev=148570&r1=148569&r2=148570
==============================================================================
--- trunk/apps/app_voicemail.c (original)
+++ trunk/apps/app_voicemail.c Mon Oct 13 19:08:52 2008
@@ -943,7 +943,7 @@
 		if (strlen(password) > 10) {
 			ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
 		}
-		res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, SENTINEL);
+		res = ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL);
 		if (res > 0) {
 			ast_copy_string(vmu->password, password, sizeof(vmu->password));
 			res = 0;
@@ -4638,8 +4638,6 @@
 	int ausemacro = 0;
 	int ousemacro = 0;
 	int ouseexten = 0;
-	int rtmsgid = 0;
-	char tmpid[16];
 	char tmpdur[16];
 	char priority[16];
 	char origtime[16];
@@ -4932,7 +4930,7 @@
 			snprintf(priority, sizeof(priority), "%d", chan->priority);
 			snprintf(origtime, sizeof(origtime), "%ld", (long)time(NULL));
 			get_date(date, sizeof(date));
-			rtmsgid = ast_store_realtime("voicemail_data", "origmailbox", ext, "context", chan->context, "macrocontext", chan->macrocontext, "exten", chan->exten, "priority", priority, "callerchan", chan->name, "callerid", ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"), "origdate", date, "origtime", origtime, "category", S_OR(category,""), SENTINEL);
+			ast_store_realtime("voicemail_data", "origmailbox", ext, "context", chan->context, "macrocontext", chan->macrocontext, "exten", chan->exten, "priority", priority, "callerchan", chan->name, "callerid", ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"), "origdate", date, "origtime", origtime, "category", S_OR(category,""), "filename", tmptxtfile, SENTINEL);
 		}
 
 		/* Store information */
@@ -4976,8 +4974,7 @@
 				ast_filedelete(tmptxtfile, NULL);
 				unlink(tmptxtfile);
 				if (ast_check_realtime("voicemail_data")) {
-					snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
-					ast_destroy_realtime("voicemail_data", "id", tmpid, SENTINEL);
+					ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
 				}
 			} else {
 				fprintf(txt, "duration=%d\n", duration);
@@ -4992,8 +4989,7 @@
 					unlink(tmptxtfile);
 					ast_unlock_path(dir);
 					if (ast_check_realtime("voicemail_data")) {
-						snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
-						ast_destroy_realtime("voicemail_data", "id", tmpid, SENTINEL);
+						ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
 					}
 				} else {
 #ifndef IMAP_STORAGE
@@ -5019,9 +5015,8 @@
 
 					ast_unlock_path(dir);
 					if (ast_check_realtime("voicemail_data")) {
-						snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
 						snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
-						ast_update_realtime("voicemail_data", "id", tmpid, "filename", fn, "duration", tmpdur, SENTINEL);
+						ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
 					}
 					/* We must store the file first, before copying the message, because
 					 * ODBC storage does the entire copy with SQL.

Modified: trunk/include/asterisk/config.h
URL: http://svn.digium.com/view/asterisk/trunk/include/asterisk/config.h?view=diff&rev=148570&r1=148569&r2=148570
==============================================================================
--- trunk/include/asterisk/config.h (original)
+++ trunk/include/asterisk/config.h Mon Oct 13 19:08:52 2008
@@ -91,6 +91,7 @@
 typedef struct ast_variable *realtime_var_get(const char *database, const char *table, va_list ap);
 typedef struct ast_config *realtime_multi_get(const char *database, const char *table, va_list ap);
 typedef int realtime_update(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap);
+typedef int realtime_update2(const char *database, const char *table, 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);
@@ -103,6 +104,7 @@
 	realtime_var_get *realtime_func;
 	realtime_multi_get *realtime_multi_func;
 	realtime_update *update_func;
+	realtime_update2 *update2_func;
 	realtime_store *store_func;
 	realtime_destroy *destroy_func;
 	realtime_require *require_func;
@@ -208,6 +210,9 @@
  * entity in realtime and return a variable list of its parameters.  Note
  * that unlike the variables in ast_config, the resulting list of variables
  * MUST be freed with ast_variables_destroy() as there is no container.
+ *
+ * Note that you should use the constant SENTINEL to terminate arguments, in
+ * order to preserve cross-platform compatibility.
  */
 struct ast_variable *ast_load_realtime(const char *family, ...) attribute_sentinel;
 struct ast_variable *ast_load_realtime_all(const char *family, ...) attribute_sentinel;
@@ -243,6 +248,9 @@
  * a timeout value may reasonably be specified as an INTEGER2, with size 5.
  * Even though values above 32767 seconds are possible, they are unlikely
  * to be useful, and we should not complain about that size).
+ *
+ * Note that you should use the constant SENTINEL to terminate arguments, in
+ * order to preserve cross-platform compatibility.
  */
 int ast_realtime_require_field(const char *family, ...) attribute_sentinel;
 
@@ -254,6 +262,9 @@
  * the ast_load_realtime, this function can return more than one entry and
  * is thus stored inside a taditional ast_config structure rather than 
  * just returning a linked list of variables.
+ *
+ * Note that you should use the constant SENTINEL to terminate arguments, in
+ * order to preserve cross-platform compatibility.
  */
 struct ast_config *ast_load_realtime_multientry(const char *family, ...) attribute_sentinel;
 
@@ -264,14 +275,31 @@
  * \param lookup which value to look for in the key field to match the entry.
  * This function is used to update a parameter in realtime configuration space.
  *
+ * Note that you should use the constant SENTINEL to terminate arguments, in
+ * order to preserve cross-platform compatibility.
  */
 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...) attribute_sentinel;
+
+/*! 
+ * \brief Update realtime configuration 
+ * \param family which family/config to be updated
+ * This function is used to update a parameter in realtime configuration space.
+ * It includes the ability to lookup a row based upon multiple key criteria.
+ * As a result, this function includes two sentinel values, one to terminate
+ * lookup values and the other to terminate the listing of fields to update.
+ *
+ * Note that you should use the constant SENTINEL to terminate arguments, in
+ * order to preserve cross-platform compatibility.
+ */
+int ast_update2_realtime(const char *family, ...) attribute_sentinel;
 
 /*! 
  * \brief Create realtime configuration 
  * \param family which family/config to be created
  * This function is used to create a parameter in realtime configuration space.
  *
+ * Note that you should use the constant SENTINEL to terminate arguments, in
+ * order to preserve cross-platform compatibility.
  */
 int ast_store_realtime(const char *family, ...) attribute_sentinel;
 
@@ -283,6 +311,8 @@
  * This function is used to destroy an entry in realtime configuration space.
  * Additional params are used as keys.
  *
+ * Note that you should use the constant SENTINEL to terminate arguments, in
+ * order to preserve cross-platform compatibility.
  */
 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...) attribute_sentinel;
 

Modified: trunk/include/asterisk/strings.h
URL: http://svn.digium.com/view/asterisk/trunk/include/asterisk/strings.h?view=diff&rev=148570&r1=148569&r2=148570
==============================================================================
--- trunk/include/asterisk/strings.h (original)
+++ trunk/include/asterisk/strings.h Mon Oct 13 19:08:52 2008
@@ -421,8 +421,8 @@
 		_DB1(__ast_threadstorage_object_replace(old_buf, *buf, new_len + sizeof(struct ast_str));)
 	}
 
-        (*buf)->len = new_len;
-        return 0;
+	(*buf)->len = new_len;
+	return 0;
 }
 )
 

Modified: trunk/main/config.c
URL: http://svn.digium.com/view/asterisk/trunk/main/config.c?view=diff&rev=148570&r1=148569&r2=148570
==============================================================================
--- trunk/main/config.c (original)
+++ trunk/main/config.c Mon Oct 13 19:08:52 2008
@@ -2069,8 +2069,8 @@
 static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
 {
 	struct ast_config_engine *eng;
-	char db[256]="";
-	char table[256]="";
+	char db[256];
+	char table[256];
 	struct ast_variable *res=NULL;
 
 	eng = find_engine(family, db, sizeof(db), table, sizeof(table));
@@ -2141,8 +2141,8 @@
 int ast_realtime_require_field(const char *family, ...)
 {
 	struct ast_config_engine *eng;
-	char db[256] = "";
-	char table[256] = "";
+	char db[256];
+	char table[256];
 	va_list ap;
 	int res = -1;
 
@@ -2159,8 +2159,8 @@
 int ast_unload_realtime(const char *family)
 {
 	struct ast_config_engine *eng;
-	char db[256] = "";
-	char table[256] = "";
+	char db[256];
+	char table[256];
 	int res = -1;
 
 	eng = find_engine(family, db, sizeof(db), table, sizeof(table));
@@ -2173,9 +2173,9 @@
 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
 {
 	struct ast_config_engine *eng;
-	char db[256]="";
-	char table[256]="";
-	struct ast_config *res=NULL;
+	char db[256];
+	char table[256];
+	struct ast_config *res = NULL;
 	va_list ap;
 
 	va_start(ap, family);
@@ -2191,8 +2191,8 @@
 {
 	struct ast_config_engine *eng;
 	int res = -1;
-	char db[256]="";
-	char table[256]="";
+	char db[256];
+	char table[256];
 	va_list ap;
 
 	va_start(ap, lookup);
@@ -2204,12 +2204,29 @@
 	return res;
 }
 
-int ast_store_realtime(const char *family, ...)
+int ast_update2_realtime(const char *family, ...)
 {
 	struct ast_config_engine *eng;
 	int res = -1;
-	char db[256]="";
-	char table[256]="";
+	char db[256];
+	char table[256];
+	va_list ap;
+
+	va_start(ap, family);
+	eng = find_engine(family, db, sizeof(db), table, sizeof(table));
+	if (eng && eng->update2_func) 
+		res = eng->update2_func(db, table, ap);
+	va_end(ap);
+
+	return res;
+}
+
+int ast_store_realtime(const char *family, ...)
+{
+	struct ast_config_engine *eng;
+	int res = -1;
+	char db[256];
+	char table[256];
 	va_list ap;
 
 	va_start(ap, family);
@@ -2225,8 +2242,8 @@
 {
 	struct ast_config_engine *eng;
 	int res = -1;
-	char db[256]="";
-	char table[256]="";
+	char db[256];
+	char table[256];
 	va_list ap;
 
 	va_start(ap, lookup);

Modified: trunk/res/res_config_curl.c
URL: http://svn.digium.com/view/asterisk/trunk/res/res_config_curl.c?view=diff&rev=148570&r1=148569&r2=148570
==============================================================================
--- trunk/res/res_config_curl.c (original)
+++ trunk/res/res_config_curl.c Mon Oct 13 19:08:52 2008
@@ -258,6 +258,69 @@
 	va_end(ap);
 
 	ast_str_append(&query, 0, ")}");
+	pbx_substitute_variables_helper(NULL, query->str, buffer, bufsize);
+
+	/* Line oriented output */
+	stringp = buffer;
+	while (*stringp <= ' ')
+		stringp++;
+	sscanf(stringp, "%d", &rowcount);
+
+	ast_free(buffer);
+	ast_free(query);
+
+	if (rowcount >= 0)
+		return (int)rowcount;
+
+	return -1;
+}
+
+static int update2_curl(const char *url, const char *unused, va_list ap)
+{
+	struct ast_str *query;
+	char buf1[200], buf2[200];
+	const char *newparam, *newval;
+	char *stringp;
+	int rowcount = -1, lookup = 1, first = 1;
+	const int EncodeSpecialChars = 1, bufsize = 100;
+	char *buffer;
+
+	if (!ast_custom_function_find("CURL")) {
+		ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
+		return -1;
+	}
+
+	if (!(query = ast_str_create(1000)))
+		return -1;
+
+	if (!(buffer = ast_malloc(bufsize))) {
+		ast_free(query);
+		return -1;
+	}
+
+	ast_str_set(&query, 0, "${CURL(%s/update?", url);
+
+	for (;;) {
+		if ((newparam = va_arg(ap, const char *)) == SENTINEL) {
+			if (lookup) {
+				lookup = 0;
+				ast_str_append(&query, 0, ",");
+				/* Back to the first parameter; we don't need a starting '&' */
+				first = 1;
+				continue;
+			} else {
+				break;
+			}
+		}
+		newval = va_arg(ap, const char *);
+		ast_uri_encode(newparam, buf1, sizeof(buf1), EncodeSpecialChars);
+		ast_uri_encode(newval, buf2, sizeof(buf2), EncodeSpecialChars);
+		ast_str_append(&query, 0, "%s%s=%s", first ? "" : "&", buf1, buf2);
+	}
+	va_end(ap);
+
+	ast_str_append(&query, 0, ")}");
+	/* TODO: Make proxies work */
 	pbx_substitute_variables_helper(NULL, query->str, buffer, bufsize);
 
 	/* Line oriented output */
@@ -535,6 +598,7 @@
 	.store_func = store_curl,
 	.destroy_func = destroy_curl,
 	.update_func = update_curl,
+	.update2_func = update2_curl,
 	.require_func = require_curl,
 };
 

Modified: trunk/res/res_config_ldap.c
URL: http://svn.digium.com/view/asterisk/trunk/res/res_config_ldap.c?view=diff&rev=148570&r1=148569&r2=148570
==============================================================================
--- trunk/res/res_config_ldap.c (original)
+++ trunk/res/res_config_ldap.c Mon Oct 13 19:08:52 2008
@@ -89,6 +89,7 @@
 	struct ast_variable *attributes;  /*!< attribute names conversion */
 	struct ast_variable *delimiters;  /*!< the current delimiter is semicolon, so we are not using this variable */
 	AST_LIST_ENTRY(ldap_table_config) entry;
+	/* TODO: Make proxies work */
 };
 
 /*! \brief Should be locked before using it */
@@ -1305,12 +1306,199 @@
 	return num_entries;
 }
 
+static int update2_ldap(const char *basedn, const char *table_name, va_list ap)
+{
+	int error = 0;
+	LDAPMessage *ldap_entry = NULL;
+	LDAPMod **ldap_mods;
+	const char *newparam = NULL;
+	const char *newval = NULL;
+	char *dn;
+	int num_entries = 0;
+	int i = 0;
+	int mods_size = 0;
+	int mod_exists = 0;
+	struct ldap_table_config *table_config = NULL;
+	char *clean_basedn = NULL;
+	struct ast_str *filter = NULL;
+	int tries = 0;
+	int result = 0;
+	LDAPMessage *ldap_result_msg = NULL;
+
+	if (!table_name) {
+		ast_log(LOG_WARNING, "No table_name specified.\n");
+		return -1;
+	} 
+
+	if (!(filter = ast_str_create(80)))
+		return -1;
+
+	ast_mutex_lock(&ldap_lock);
+
+	/* We now have our complete statement; Lets connect to the server and execute it.  */
+	if (!ldap_reconnect()) {
+		ast_mutex_unlock(&ldap_lock);
+		ast_free(filter);
+		return -1;
+	}
+
+	table_config = table_config_for_table_name(table_name);
+	if (!table_config) {
+		ast_log(LOG_WARNING, "No table named '%s'.\n", table_name);
+		ast_mutex_unlock(&ldap_lock);
+		ast_free(filter);
+		return -1;
+	}
+
+	clean_basedn = cleaned_basedn(NULL, basedn);
+
+	/* Create the filter with the table additional filter and the parameter/value pairs we were given */
+	ast_str_append(&filter, 0, "(&");
+	if (table_config && table_config->additional_filter) {
+		ast_str_append(&filter, 0, "%s", table_config->additional_filter);
+	}
+	if (table_config != base_table_config && base_table_config
+		&& base_table_config->additional_filter) {
+		ast_str_append(&filter, 0, "%s", base_table_config->additional_filter);
+	}
+
+	/* Get multiple lookup keyfields and values */
+	while ((newparam = va_arg(ap, const char *))) {
+		newval = va_arg(ap, const char *);
+		append_var_and_value_to_filter(&filter, table_config, newparam, newval);
+	}
+	ast_str_append(&filter, 0, ")");
+
+	/* Create the modification array with the parameter/value pairs we were given, 
+	 * if there are several parameters with the same name, we collect them into 
+	 * one parameter/value pair and delimit them with a semicolon */
+	newparam = va_arg(ap, const char *);
+	newparam = convert_attribute_name_to_ldap(table_config, newparam);
+	newval = va_arg(ap, const char *);
+	if (!newparam || !newval) {
+		ast_log(LOG_WARNING,
+				"LINE(%d): need at least one parameter to modify.\n", __LINE__);
+		ast_free(filter);
+		ast_free(clean_basedn);
+		return -1;
+	}
+
+	mods_size = 2; /* one for the first param/value pair and one for the the terminating NULL */
+	ldap_mods = ast_calloc(sizeof(LDAPMod *), mods_size);
+	ldap_mods[0] = ast_calloc(1, sizeof(LDAPMod));
+
+	ldap_mods[0]->mod_op = LDAP_MOD_REPLACE;
+	ldap_mods[0]->mod_type = ast_calloc(sizeof(char), strlen(newparam) + 1);
+	strcpy(ldap_mods[0]->mod_type, newparam);
+
+	ldap_mods[0]->mod_values = ast_calloc(sizeof(char), 2);
+	ldap_mods[0]->mod_values[0] = ast_calloc(sizeof(char), strlen(newval) + 1);
+	strcpy(ldap_mods[0]->mod_values[0], newval);
+
+	while ((newparam = va_arg(ap, const char *))) {
+		newparam = convert_attribute_name_to_ldap(table_config, newparam);
+		newval = va_arg(ap, const char *);
+		mod_exists = 0;
+
+		for (i = 0; i < mods_size - 1; i++) {
+			if (ldap_mods[i]&& !strcmp(ldap_mods[i]->mod_type, newparam)) {
+				/* We have the parameter allready, adding the value as a semicolon delimited value */
+				ldap_mods[i]->mod_values[0] = ast_realloc(ldap_mods[i]->mod_values[0], sizeof(char) * (strlen(ldap_mods[i]->mod_values[0]) + strlen(newval) + 2));
+				strcat(ldap_mods[i]->mod_values[0], ";");
+				strcat(ldap_mods[i]->mod_values[0], newval);
+				mod_exists = 1;	
+				break;
+			}
+		}
+
+		/* create new mod */
+		if (!mod_exists) {
+			mods_size++;
+			ldap_mods = ast_realloc(ldap_mods, sizeof(LDAPMod *) * mods_size);
+			ldap_mods[mods_size - 1] = NULL;
+			ldap_mods[mods_size - 2] = ast_calloc(1, sizeof(LDAPMod));
+
+			ldap_mods[mods_size - 2]->mod_op = LDAP_MOD_REPLACE;
+
+			ldap_mods[mods_size - 2]->mod_type = ast_calloc(sizeof(char), strlen(newparam) + 1);
+			strcpy(ldap_mods[mods_size - 2]->mod_type, newparam);
+
+			ldap_mods[mods_size - 2]->mod_values = ast_calloc(sizeof(char *), 2);
+			ldap_mods[mods_size - 2]->mod_values[0] = ast_calloc(sizeof(char), strlen(newval) + 1);
+			strcpy(ldap_mods[mods_size - 2]->mod_values[0], newval);
+		}
+	}
+	/* freeing ldap_mods further down */
+
+	do {
+		/* freeing ldap_result further down */
+		result = ldap_search_ext_s(ldapConn, clean_basedn,
+				  LDAP_SCOPE_SUBTREE, filter->str, NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT,
+				  &ldap_result_msg);
+		if (result != LDAP_SUCCESS && is_ldap_connect_error(result)) {
+			ast_log(LOG_WARNING, "Failed to query database. Try %d/3\n",
+				tries + 1);
+			tries++;
+			if (tries < 3) {
+				usleep(500000L * tries);
+				if (ldapConn) {
+					ldap_unbind_ext_s(ldapConn, NULL, NULL);
+					ldapConn = NULL;
+				}
+				if (!ldap_reconnect())
+					break;
+			}
+		}
+	} while (result != LDAP_SUCCESS && tries < 3 && is_ldap_connect_error(result));
+
+	if (result != LDAP_SUCCESS) {
+		ast_log(LOG_WARNING, "Failed to query directory. Check debug for more info.\n");
+		ast_log(LOG_WARNING, "Query: %s\n", filter->str);
+		ast_log(LOG_WARNING, "Query Failed because: %s\n",
+			ldap_err2string(result));
+
+		ast_mutex_unlock(&ldap_lock);
+		if (filter)
+			free(filter);
+		if (clean_basedn)
+			free(clean_basedn);
+		ldap_msgfree(ldap_result_msg);
+		ldap_mods_free(ldap_mods, 0);
+		return -1;
+	}
+	/* Ready to update */
+	if ((num_entries = ldap_count_entries(ldapConn, ldap_result_msg)) > 0) {
+		for (i = 0; option_debug > 2 && i < mods_size - 1; i++)
+			ast_debug(3, "LINE(%d) %s=%s \n", __LINE__, ldap_mods[i]->mod_type, ldap_mods[i]->mod_values[0]);
+
+		ldap_entry = ldap_first_entry(ldapConn, ldap_result_msg);
+
+		for (i = 0; ldap_entry; i++) { 
+			dn = ldap_get_dn(ldapConn, ldap_entry);
+			if ((error = ldap_modify_ext_s(ldapConn, dn, ldap_mods, NULL, NULL)) != LDAP_SUCCESS) 
+				ast_log(LOG_ERROR, "Couldn't modify dn:%s because %s", dn, ldap_err2string(error));
+
+			ldap_entry = ldap_next_entry(ldapConn, ldap_entry);
+		}
+	}
+
+	ast_mutex_unlock(&ldap_lock);
+	if (filter)
+		free(filter);
+	if (clean_basedn)
+		free(clean_basedn);
+	ldap_msgfree(ldap_result_msg);
+	ldap_mods_free(ldap_mods, 0);
+	return num_entries;
+}
+
 static struct ast_config_engine ldap_engine = {
 	.name = "ldap",
 	.load_func = config_ldap,
 	.realtime_func = realtime_ldap,
 	.realtime_multi_func = realtime_multi_ldap,
-	.update_func = update_ldap
+	.update_func = update_ldap,
+	.update2_func = update2_ldap,
 };
 
 static int load_module(void)

Modified: trunk/res/res_config_odbc.c
URL: http://svn.digium.com/view/asterisk/trunk/res/res_config_odbc.c?view=diff&rev=148570&r1=148569&r2=148570
==============================================================================
--- trunk/res/res_config_odbc.c (original)
+++ trunk/res/res_config_odbc.c Mon Oct 13 19:08:52 2008
@@ -48,6 +48,8 @@
 #include "asterisk/lock.h"
 #include "asterisk/res_odbc.h"
 #include "asterisk/utils.h"
+
+AST_THREADSTORAGE(sql_buf);
 
 struct custom_prepare_struct {
 	const char *sql;
@@ -470,6 +472,147 @@
 
 	if (rowcount >= 0)
 		return (int)rowcount;
+
+	return -1;
+}
+
+struct update2_prepare_struct {
+	const char *database;
+	const char *table;
+	va_list ap;
+};
+
+static SQLHSTMT update2_prepare(struct odbc_obj *obj, void *data)
+{
+	int res, x = 1, first = 1;
+	struct update2_prepare_struct *ups = data;
+	const char *newparam, *newval;
+	struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
+	SQLHSTMT stmt;
+	va_list ap;
+	struct odbc_cache_tables *tableptr = ast_odbc_find_table(ups->database, ups->table);
+	struct odbc_cache_columns *column;
+
+	if (!sql) {
+		if (tableptr) {
+			ast_odbc_release_table(tableptr);
+		}
+		return NULL;
+	}
+
+	if (!tableptr) {
+		ast_log(LOG_ERROR, "Could not retrieve metadata for table '%s@%s'.  Update will fail!\n", ups->table, ups->database);
+		return NULL;
+	}
+
+	res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+		ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+		ast_odbc_release_table(tableptr);
+		return NULL;
+	}
+
+	ast_str_set(&sql, 0, "UPDATE %s SET ", ups->table);
+
+	/* Start by finding the second set of parameters */
+	va_copy(ap, ups->ap);
+
+	while ((newparam = va_arg(ap, const char *))) {
+		newval = va_arg(ap, const char *);
+	}
+
+	while ((newparam = va_arg(ap, const char *))) {
+		newval = va_arg(ap, const char *);
+		if ((column = ast_odbc_find_column(tableptr, newparam))) {
+			ast_str_append(&sql, 0, "%s%s=? ", first ? "" : ", ", newparam);
+			SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
+			first = 0;
+		} else {
+			ast_log(LOG_NOTICE, "Not updating column '%s' in '%s@%s' because that column does not exist!\n", newparam, ups->table, ups->database);
+		}
+	}
+	va_end(ap);
+
+	/* Restart search, because we need to add the search parameters */
+	va_copy(ap, ups->ap);
+	ast_str_append(&sql, 0, "WHERE");
+	first = 1;
+
+	while ((newparam = va_arg(ap, const char *))) {
+		newval = va_arg(ap, const char *);
+		if (!(column = ast_odbc_find_column(tableptr, newparam))) {
+			ast_log(LOG_ERROR, "One or more of the criteria columns '%s' on '%s@%s' for this update does not exist!\n", newparam, ups->table, ups->database);
+			ast_odbc_release_table(tableptr);
+			SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+			return NULL;
+		}
+		ast_str_append(&sql, 0, "%s %s=?", first ? "" : " AND", newparam);
+		SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
+		first = 0;
+	}
+	va_end(ap);
+
+	/* Done with the table metadata */
+	ast_odbc_release_table(tableptr);
+
+	res = SQLPrepare(stmt, (unsigned char *)sql->str, SQL_NTS);
+	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+		ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql->str);
+		SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+		return NULL;
+	}
+
+	return stmt;
+}
+
+/*!
+ * \brief Execute an UPDATE query
+ * \param database
+ * \param table
+ * \param ap list containing one or more field/value set(s).
+ *
+ * Update a database table, preparing the sql statement from a list of
+ * key/value pairs specified in ap.  The lookup pairs are specified first
+ * and are separated from the update pairs by a sentinel value.
+ * Sub-in the values to the prepared statement and execute it.
+ *
+ * \retval number of rows affected
+ * \retval -1 on failure
+*/
+static int update2_odbc(const char *database, const char *table, va_list ap)
+{
+	struct odbc_obj *obj;
+	SQLHSTMT stmt;
+	struct update2_prepare_struct ups = { .database = database, .table = table, };
+	struct ast_str *sql;
+	int res;
+	SQLLEN rowcount = 0;
+
+	va_copy(ups.ap, ap);
+
+	if (!(obj = ast_odbc_request_obj(database, 0))) {
+		return -1;
+	}
+
+	if (!(stmt = ast_odbc_prepare_and_execute(obj, update2_prepare, &ups))) {
+		ast_odbc_release_obj(obj);
+		return -1;
+	}
+
+	res = SQLRowCount(stmt, &rowcount);
+	SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+	ast_odbc_release_obj(obj);
+
+	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+		/* Since only a single thread can access this memory, we can retrieve what would otherwise be lost. */
+		sql = ast_str_thread_get(&sql_buf, 16);
+		ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n", sql->str);
+		return -1;
+	}
+
+	if (rowcount >= 0) {
+		return (int)rowcount;
+	}
 
 	return -1;
 }
@@ -899,6 +1042,7 @@
 	.store_func = store_odbc,
 	.destroy_func = destroy_odbc,
 	.update_func = update_odbc,
+	.update2_func = update2_odbc,
 	.require_func = require_odbc,
 	.unload_func = ast_odbc_clear_cache,
 };

Modified: trunk/res/res_config_pgsql.c
URL: http://svn.digium.com/view/asterisk/trunk/res/res_config_pgsql.c?view=diff&rev=148570&r1=148569&r2=148570
==============================================================================
--- trunk/res/res_config_pgsql.c (original)
+++ trunk/res/res_config_pgsql.c Mon Oct 13 19:08:52 2008
@@ -42,6 +42,10 @@
 #include "asterisk/cli.h"
 
 AST_MUTEX_DEFINE_STATIC(pgsql_lock);
+AST_THREADSTORAGE(sql_buf);
+AST_THREADSTORAGE(findtable_buf);
+AST_THREADSTORAGE(where_buf);
+AST_THREADSTORAGE(escapebuf_buf);
 
 #define RES_CONFIG_PGSQL_CONF "res_pgsql.conf"
 
@@ -59,7 +63,7 @@
 };
 
 struct tables {
-	ast_mutex_t lock;
+	ast_rwlock_t lock;
 	AST_LIST_HEAD_NOLOCK(psql_columns, columns) columns;
 	AST_LIST_ENTRY(tables) list;
 	char name[0];
@@ -87,15 +91,24 @@
 	AST_CLI_DEFINE(handle_cli_realtime_pgsql_cache, "Shows cached tables within the PostgreSQL realtime driver"),
 };
 
+#define ESCAPE_STRING(buffer, stringname) \
+	do { \
+		int len; \
+		if ((len = strlen(stringname)) > (buffer->len - 1) / 2) { \
+			ast_str_make_space(&buffer, len * 2 + 1); \
+		} \
+		PQescapeStringConn(pgsqlConn, buffer->str, stringname, len, &pgresult); \
+	} while (0)
+
 static void destroy_table(struct tables *table)
 {
 	struct columns *column;
-	ast_mutex_lock(&table->lock);
+	ast_rwlock_wrlock(&table->lock);
 	while ((column = AST_LIST_REMOVE_HEAD(&table->columns, list))) {
 		ast_free(column);
 	}
-	ast_mutex_unlock(&table->lock);
-	ast_mutex_destroy(&table->lock);
+	ast_rwlock_unlock(&table->lock);
+	ast_rwlock_destroy(&table->lock);
 	ast_free(table);
 }
 
@@ -103,7 +116,7 @@
 {
 	struct columns *column;
 	struct tables *table;
-	struct ast_str *sql = ast_str_create(330);
+	struct ast_str *sql = ast_str_thread_get(&findtable_buf, 330);
 	char *pgerror;
 	PGresult *result;
 	char *fname, *ftype, *flen, *fnotnull, *fdef;
@@ -113,7 +126,7 @@
 	AST_LIST_TRAVERSE(&psql_tables, table, list) {
 		if (!strcasecmp(table->name, tablename)) {
 			ast_debug(1, "Found table in cache; now locking\n");
-			ast_mutex_lock(&table->lock);
+			ast_rwlock_rdlock(&table->lock);
 			ast_debug(1, "Lock cached table; now returning\n");
 			AST_LIST_UNLOCK(&psql_tables);
 			return table;
@@ -140,9 +153,9 @@
 		return NULL;
 	}
 	strcpy(table->name, tablename); /* SAFE */
-	ast_mutex_init(&table->lock);
+	ast_rwlock_init(&table->lock);
 	AST_LIST_HEAD_INIT_NOLOCK(&table->columns);
-	
+
 	rows = PQntuples(result);
 	for (i = 0; i < rows; i++) {
 		fname = PQgetvalue(result, i, 0);
@@ -186,23 +199,39 @@
 	PQclear(result);
 
 	AST_LIST_INSERT_TAIL(&psql_tables, table, list);
-	ast_mutex_lock(&table->lock);
+	ast_rwlock_rdlock(&table->lock);
 	AST_LIST_UNLOCK(&psql_tables);
 	return table;
 }
 
-static struct ast_variable *realtime_pgsql(const char *database, const char *table, va_list ap)
+#define release_table(table) ast_rwlock_unlock(&(table)->lock);
+
+static struct columns *find_column(struct tables *t, const char *colname)
+{
+	struct columns *column;
+
+	/* Check that the column exists in the table */
+	AST_LIST_TRAVERSE(&t->columns, column, list) {
+		if (strcmp(column->name, colname) == 0) {
+			return column;
+		}
+	}
+	return NULL;
+}
+
+static struct ast_variable *realtime_pgsql(const char *database, const char *tablename, va_list ap)
 {
 	PGresult *result = NULL;
-	int num_rows = 0, pgerror;
-	char sql[256], escapebuf[513];
+	int num_rows = 0, pgresult;
+	struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
+	struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
 	char *stringp;
 	char *chunk;
 	char *op;
 	const char *newparam, *newval;
 	struct ast_variable *var = NULL, *prev = NULL;
 
-	if (!table) {
+	if (!tablename) {
 		ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
 		return NULL;
 	}
@@ -216,7 +245,7 @@
 		if (pgsqlConn) {
 			PQfinish(pgsqlConn);
 			pgsqlConn = NULL;
-		};
+		}
 		return NULL;
 	}
 
@@ -224,15 +253,14 @@
 	   If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
 	op = strchr(newparam, ' ') ? "" : " =";
 
-	PQescapeStringConn(pgsqlConn, escapebuf, newval, (sizeof(escapebuf) - 1) / 2, &pgerror);
-	if (pgerror) {
+	ESCAPE_STRING(escapebuf, newval);
+	if (pgresult) {
 		ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
 		va_end(ap);
 		return NULL;
 	}
 
-	snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op,
-			 escapebuf);
+	ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", tablename, newparam, op, escapebuf->str);
 	while ((newparam = va_arg(ap, const char *))) {
 		newval = va_arg(ap, const char *);
 		if (!strchr(newparam, ' '))
@@ -240,15 +268,14 @@
 		else
 			op = "";
 
-		PQescapeStringConn(pgsqlConn, escapebuf, newval, (sizeof(escapebuf) - 1) / 2, &pgerror);
-		if (pgerror) {
+		ESCAPE_STRING(escapebuf, newval);
+		if (pgresult) {
 			ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
 			va_end(ap);
 			return NULL;
 		}
 
-		snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam,
-				 op, escapebuf);
+		ast_str_append(&sql, 0, " AND %s%s '%s'", newparam, op, escapebuf->str);
 	}
 	va_end(ap);
 
@@ -259,10 +286,10 @@
 		return NULL;
 	}
 
-	if (!(result = PQexec(pgsqlConn, sql))) {
-		ast_log(LOG_WARNING,
-				"PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
-		ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql);
+	if (!(result = PQexec(pgsqlConn, sql->str))) {
+		ast_log(LOG_WARNING,
+				"PostgreSQL RealTime: Failed to query '%s@%s'. Check debug for more info.\n", tablename, database);
+		ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql->str);
 		ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
 		ast_mutex_unlock(&pgsql_lock);
 		return NULL;
@@ -272,8 +299,8 @@
 			&& result_status != PGRES_TUPLES_OK
 			&& result_status != PGRES_NONFATAL_ERROR) {
 			ast_log(LOG_WARNING,
-					"PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
-			ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql);
+					"PostgreSQL RealTime: Failed to query '%s@%s'. Check debug for more info.\n", tablename, database);
+			ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql->str);
 			ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
 						PQresultErrorMessage(result), PQresStatus(result_status));
 			ast_mutex_unlock(&pgsql_lock);
@@ -281,7 +308,7 @@
 		}
 	}
 
-	ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, sql);
+	ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, sql->str);
 
 	if ((num_rows = PQntuples(result)) > 0) {
 		int i = 0;
@@ -318,7 +345,7 @@
 		}
 		ast_free(fieldnames);
 	} else {
-		ast_debug(1, "Postgresql RealTime: Could not find any rows in table %s.\n", table);
+		ast_debug(1, "Postgresql RealTime: Could not find any rows in table %s@%s.\n", tablename, database);
 	}
 
 	ast_mutex_unlock(&pgsql_lock);
@@ -330,8 +357,9 @@
 static struct ast_config *realtime_multi_pgsql(const char *database, const char *table, va_list ap)
 {
 	PGresult *result = NULL;
-	int num_rows = 0, pgerror;
-	char sql[256], escapebuf[513];
+	int num_rows = 0, pgresult;
+	struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
+	struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
 	const char *initfield = NULL;
 	char *stringp;
 	char *chunk;
@@ -358,7 +386,7 @@
 		if (pgsqlConn) {
 			PQfinish(pgsqlConn);
 			pgsqlConn = NULL;
-		};
+		}
 		return NULL;
 	}
 
@@ -375,15 +403,14 @@
 	else
 		op = "";
 
-	PQescapeStringConn(pgsqlConn, escapebuf, newval, (sizeof(escapebuf) - 1) / 2, &pgerror);
-	if (pgerror) {
+	ESCAPE_STRING(escapebuf, newval);
+	if (pgresult) {
 		ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
 		va_end(ap);
 		return NULL;
 	}
 
-	snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op,
-			 escapebuf);
+	ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, escapebuf->str);
 	while ((newparam = va_arg(ap, const char *))) {
 		newval = va_arg(ap, const char *);
 		if (!strchr(newparam, ' '))
@@ -391,19 +418,18 @@
 		else
 			op = "";
 
-		PQescapeStringConn(pgsqlConn, escapebuf, newval, (sizeof(escapebuf) - 1) / 2, &pgerror);
-		if (pgerror) {
+		ESCAPE_STRING(escapebuf, newval);
+		if (pgresult) {
 			ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
 			va_end(ap);
 			return NULL;
 		}
 
-		snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam,
-				 op, escapebuf);
+		ast_str_append(&sql, 0, " AND %s%s '%s'", newparam, op, escapebuf->str);
 	}
 
 	if (initfield) {
-		snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
+		ast_str_append(&sql, 0, " ORDER BY %s", initfield);
 	}
 
 	va_end(ap);
@@ -415,10 +441,10 @@
 		return NULL;
 	}
 
-	if (!(result = PQexec(pgsqlConn, sql))) {
-		ast_log(LOG_WARNING,
-				"PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
-		ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql);
+	if (!(result = PQexec(pgsqlConn, sql->str))) {
+		ast_log(LOG_WARNING,
+				"PostgreSQL RealTime: Failed to query %s@%s. Check debug for more info.\n", table, database);
+		ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql->str);
 		ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
 		ast_mutex_unlock(&pgsql_lock);
 		return NULL;
@@ -428,8 +454,8 @@
 			&& result_status != PGRES_TUPLES_OK
 			&& result_status != PGRES_NONFATAL_ERROR) {
 			ast_log(LOG_WARNING,
-					"PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
-			ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql);
+					"PostgreSQL RealTime: Failed to query %s@%s. Check debug for more info.\n", table, database);
+			ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql->str);
 			ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
 						PQresultErrorMessage(result), PQresStatus(result_status));
 			ast_mutex_unlock(&pgsql_lock);
@@ -437,7 +463,7 @@
 		}
 	}
 
-	ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, sql);
+	ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, sql->str);
 
 	if ((num_rows = PQntuples(result)) > 0) {
 		int numFields = PQnfields(result);
@@ -490,22 +516,20 @@
 						const char *lookup, va_list ap)
 {
 	PGresult *result = NULL;
-	int numrows = 0, pgerror;
-	char escapebuf[513];
+	int numrows = 0, pgresult;
 	const char *newparam, *newval;
-	struct ast_str *sql = ast_str_create(100);
+	struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
+	struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
 	struct tables *table;
 	struct columns *column = NULL;
 
 	if (!tablename) {
 		ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
-		ast_free(sql);
 		return -1;
 	}
 
 	if (!(table = find_table(tablename))) {
 		ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
-		ast_free(sql);
 		return -1;
 	}
 
@@ -518,9 +542,8 @@
 		if (pgsqlConn) {
 			PQfinish(pgsqlConn);
 			pgsqlConn = NULL;
-		};
-		ast_mutex_unlock(&table->lock);
-		ast_free(sql);
+		}
+		release_table(table);
 		return -1;
 	}
 
@@ -533,62 +556,51 @@
 
 	if (!column) {
 		ast_log(LOG_ERROR, "PostgreSQL RealTime: Updating on column '%s', but that column does not exist within the table '%s'!\n", newparam, tablename);
-		ast_mutex_unlock(&table->lock);
-		ast_free(sql);
+		release_table(table);
 		return -1;
 	}
 
 	/* Create the first part of the query using the first parameter/value pairs we just extracted
 	   If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
 
-	PQescapeStringConn(pgsqlConn, escapebuf, newval, (sizeof(escapebuf) - 1) / 2, &pgerror);
-	if (pgerror) {
+	ESCAPE_STRING(escapebuf, newval);
+	if (pgresult) {
 		ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
 		va_end(ap);
-		ast_mutex_unlock(&table->lock);
-		ast_free(sql);
-		return -1;
-	}
-	ast_str_set(&sql, 0, "UPDATE %s SET %s = '%s'", tablename, newparam, escapebuf);
+		release_table(table);
+		return -1;
+	}
+	ast_str_set(&sql, 0, "UPDATE %s SET %s = '%s'", tablename, newparam, escapebuf->str);
 
 	while ((newparam = va_arg(ap, const char *))) {
 		newval = va_arg(ap, const char *);
 
-		/* If the column is not within the table, then skip it */
-		AST_LIST_TRAVERSE(&table->columns, column, list) {
-			if (strcmp(column->name, newparam) == 0) {
-				break;
-			}
-		}
-
-		if (!column) {
+		if (!find_column(table, newparam)) {
 			ast_log(LOG_WARNING, "Attempted to update column '%s' in table '%s', but column does not exist!\n", newparam, tablename);
 			continue;
 		}
 
-		PQescapeStringConn(pgsqlConn, escapebuf, newval, (sizeof(escapebuf) - 1) / 2, &pgerror);
-		if (pgerror) {
+		ESCAPE_STRING(escapebuf, newval);
+		if (pgresult) {
 			ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
 			va_end(ap);
-			ast_mutex_unlock(&table->lock);
-			ast_free(sql);
+			release_table(table);
 			return -1;
 		}
 

[... 756 lines stripped ...]



More information about the svn-commits mailing list