[asterisk-commits] tilghman: branch tilghman/realtime_update2 r147589 - in /team/tilghman/realti...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Wed Oct 8 12:34:01 CDT 2008
Author: tilghman
Date: Wed Oct 8 12:34:01 2008
New Revision: 147589
URL: http://svn.digium.com/view/asterisk?view=rev&rev=147589
Log:
Initial batch of changes
Modified:
team/tilghman/realtime_update2/include/asterisk/config.h
team/tilghman/realtime_update2/include/asterisk/strings.h
team/tilghman/realtime_update2/main/config.c
team/tilghman/realtime_update2/res/res_config_curl.c
team/tilghman/realtime_update2/res/res_config_ldap.c
team/tilghman/realtime_update2/res/res_config_odbc.c
team/tilghman/realtime_update2/res/res_config_pgsql.c
team/tilghman/realtime_update2/res/res_config_sqlite.c
Modified: team/tilghman/realtime_update2/include/asterisk/config.h
URL: http://svn.digium.com/view/asterisk/team/tilghman/realtime_update2/include/asterisk/config.h?view=diff&rev=147589&r1=147588&r2=147589
==============================================================================
--- team/tilghman/realtime_update2/include/asterisk/config.h (original)
+++ team/tilghman/realtime_update2/include/asterisk/config.h Wed Oct 8 12:34:01 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: team/tilghman/realtime_update2/include/asterisk/strings.h
URL: http://svn.digium.com/view/asterisk/team/tilghman/realtime_update2/include/asterisk/strings.h?view=diff&rev=147589&r1=147588&r2=147589
==============================================================================
--- team/tilghman/realtime_update2/include/asterisk/strings.h (original)
+++ team/tilghman/realtime_update2/include/asterisk/strings.h Wed Oct 8 12:34:01 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: team/tilghman/realtime_update2/main/config.c
URL: http://svn.digium.com/view/asterisk/team/tilghman/realtime_update2/main/config.c?view=diff&rev=147589&r1=147588&r2=147589
==============================================================================
--- team/tilghman/realtime_update2/main/config.c (original)
+++ team/tilghman/realtime_update2/main/config.c Wed Oct 8 12:34:01 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: team/tilghman/realtime_update2/res/res_config_curl.c
URL: http://svn.digium.com/view/asterisk/team/tilghman/realtime_update2/res/res_config_curl.c?view=diff&rev=147589&r1=147588&r2=147589
==============================================================================
--- team/tilghman/realtime_update2/res/res_config_curl.c (original)
+++ team/tilghman/realtime_update2/res/res_config_curl.c Wed Oct 8 12:34:01 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 i, 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: team/tilghman/realtime_update2/res/res_config_ldap.c
URL: http://svn.digium.com/view/asterisk/team/tilghman/realtime_update2/res/res_config_ldap.c?view=diff&rev=147589&r1=147588&r2=147589
==============================================================================
--- team/tilghman/realtime_update2/res/res_config_ldap.c (original)
+++ team/tilghman/realtime_update2/res/res_config_ldap.c Wed Oct 8 12:34:01 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,200 @@
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) {
+ ast_debug(3, "LINE(%d) Modifying %s=%s hits: %d\n", __LINE__, attribute, lookup, num_entries);
+ 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: team/tilghman/realtime_update2/res/res_config_odbc.c
URL: http://svn.digium.com/view/asterisk/team/tilghman/realtime_update2/res/res_config_odbc.c?view=diff&rev=147589&r1=147588&r2=147589
==============================================================================
--- team/tilghman/realtime_update2/res/res_config_odbc.c (original)
+++ team/tilghman/realtime_update2/res/res_config_odbc.c Wed Oct 8 12:34:01 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,145 @@
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, count = 0, 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->description);
+ 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, "%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, table, 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, "%s%s ? ", first ? "" : ", ", 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;
+
+ 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 +1040,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: team/tilghman/realtime_update2/res/res_config_pgsql.c
URL: http://svn.digium.com/view/asterisk/team/tilghman/realtime_update2/res/res_config_pgsql.c?view=diff&rev=147589&r1=147588&r2=147589
==============================================================================
--- team/tilghman/realtime_update2/res/res_config_pgsql.c (original)
+++ team/tilghman/realtime_update2/res/res_config_pgsql.c Wed Oct 8 12:34:01 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,20 +556,18 @@
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);
+ release_table(table);
return -1;
}
ast_str_set(&sql, 0, "UPDATE %s SET %s = '%s'", tablename, newparam, escapebuf);
@@ -554,37 +575,28 @@
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, column)) {
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;
}
ast_str_append(&sql, 0, ", %s = '%s'", newparam, escapebuf);
}
va_end(ap);
- ast_mutex_unlock(&table->lock);
-
- PQescapeStringConn(pgsqlConn, escapebuf, lookup, (sizeof(escapebuf) - 1) / 2, &pgerror);
- if (pgerror) {
+ release_table(table);
+
+ ESCAPE_STRING(escapebuf, lookup);
+ if (pgresult) {
ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", lookup);
va_end(ap);
- ast_free(sql);
return -1;
}
@@ -596,7 +608,6 @@
ast_mutex_lock(&pgsql_lock);
if (!pgsql_reconnect(database)) {
ast_mutex_unlock(&pgsql_lock);
- ast_free(sql);
return -1;
}
@@ -642,22 +653,152 @@
return -1;
}
-#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 int update2_pgsql(const char *database, const char *tablename, va_list ap)
+{
+ PGresult *result = NULL;
+ int numrows = 0, pgresult, first = 1;
+ struct ast_str *escapebuf = ast_str_thread_get(escapebuf_buf, 16);
+ const char *newparam, *newval;
+ struct ast_str *sql = ast_str_thread_get(sql_buf, 100);
+ struct ast_str *where = ast_str_thread_get(where_buf, 100);
+ struct tables *table;
+ struct columns *column = NULL;
+
+ if (!tablename) {
+ ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
+ return -1;
+ }
+
+ if (!escapebuf || !sql || !where) {
+ /* Memory error, already handled */
+ return -1;
+ }
+
+ if (!(table = find_table(tablename))) {
+ ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
+ return -1;
+ }
+
+ ast_str_set(&sql, 0, "UPDATE %s SET ");
+ ast_set_set(&where, 0, "WHERE");
+
+ va_start(ap);
+ while ((newparam = va_arg(ap, const char *))) {
+ if (!find_column(table, newparam)) {
+ ast_log(LOG_ERROR, "Attempted to update based on criteria column '%s' (%s@%s), but that column does not exist!\n", newparam, tablename, database);
+ release_table(table);
+ va_end(ap);
+ return -1;
+ }
+
+ newval = va_arg(ap, const char *);
+ ESCAPE_STRING(escapebuf, newval);
+ if (pgresult) {
+ ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
+ va_end(ap);
+ release_table(table);
+ ast_free(sql);
+ return -1;
+ }
+ ast_str_append(&where, 0, "%s %s='%s'", first ? "" : ",", newparam, escapebuf->str);
+ first = 0;
+ }
+
+ if (first) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: Realtime update requires at least 1 parameter and 1 value to search on.\n");
+ if (pgsqlConn) {
+ PQfinish(pgsqlConn);
+ pgsqlConn = NULL;
+ }
+ release_table(table);
+ va_end(ap);
+ return -1;
+ }
+
+ /* Now retrieve the columns to update */
+ first = 1;
+ while ((newparam = va_arg(ap, const char *))) {
+ newval = va_arg(ap, const char *);
+
+ /* If the column is not within the table, then skip it */
+ if (!find_column(column)) {
+ ast_log(LOG_NOTICE, "Attempted to update column '%s' in table '%s@%s', but column does not exist!\n", newparam, tablename, database);
+ continue;
+ }
+
[... 321 lines stripped ...]
More information about the asterisk-commits
mailing list