aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/app_voicemail.c15
-rw-r--r--include/asterisk/config.h30
-rw-r--r--include/asterisk/strings.h4
-rw-r--r--main/config.c47
-rw-r--r--res/res_config_curl.c64
-rw-r--r--res/res_config_ldap.c190
-rw-r--r--res/res_config_odbc.c144
-rw-r--r--res/res_config_pgsql.c365
-rw-r--r--res/res_config_sqlite.c113
-rw-r--r--res/res_realtime.c143
10 files changed, 942 insertions, 173 deletions
diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c
index 4134f19bf..0ddcf3b15 100644
--- a/apps/app_voicemail.c
+++ b/apps/app_voicemail.c
@@ -943,7 +943,7 @@ static int change_password_realtime(struct ast_vm_user *vmu, const char *passwor
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 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
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 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
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 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
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 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
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 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
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.
diff --git a/include/asterisk/config.h b/include/asterisk/config.h
index 8c9a1d3be..bd030a76e 100644
--- a/include/asterisk/config.h
+++ b/include/asterisk/config.h
@@ -91,6 +91,7 @@ typedef struct ast_config *config_load_func(const char *database, const char *ta
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 @@ struct ast_config_engine {
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 @@ int ast_category_exist(const struct ast_config *config, const char *category_nam
* 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 @@ int ast_unload_realtime(const char *family);
* 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 @@ int ast_realtime_require_field(const char *family, ...) attribute_sentinel;
* 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 @@ struct ast_config *ast_load_realtime_multientry(const char *family, ...) attribu
* \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 @@ int ast_store_realtime(const char *family, ...) attribute_sentinel;
* 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;
diff --git a/include/asterisk/strings.h b/include/asterisk/strings.h
index 514b3af6a..682524248 100644
--- a/include/asterisk/strings.h
+++ b/include/asterisk/strings.h
@@ -421,8 +421,8 @@ int ast_str_make_space(struct ast_str **buf, size_t new_len),
_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;
}
)
diff --git a/main/config.c b/main/config.c
index 5ea68a7a7..986c81571 100644
--- a/main/config.c
+++ b/main/config.c
@@ -2069,8 +2069,8 @@ struct ast_config *ast_config_load2(const char *filename, const char *who_asked,
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_enabled()
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_realtime_require_field(const char *family, ...)
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 @@ int ast_unload_realtime(const char *family)
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 @@ int ast_update_realtime(const char *family, const char *keyfield, const char *lo
{
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 @@ int ast_update_realtime(const char *family, const char *keyfield, const char *lo
return res;
}
+int ast_update2_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);
+ 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]="";
+ char db[256];
+ char table[256];
va_list ap;
va_start(ap, family);
@@ -2225,8 +2242,8 @@ int ast_destroy_realtime(const char *family, const char *keyfield, const char *l
{
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);
diff --git a/res/res_config_curl.c b/res/res_config_curl.c
index 37079adea..b159c768d 100644
--- a/res/res_config_curl.c
+++ b/res/res_config_curl.c
@@ -275,6 +275,69 @@ static int update_curl(const char *url, const char *unused, const char *keyfield
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 */
+ stringp = buffer;
+ while (*stringp <= ' ')
+ stringp++;
+ sscanf(stringp, "%d", &rowcount);
+
+ ast_free(buffer);
+ ast_free(query);
+
+ if (rowcount >= 0)
+ return (int)rowcount;
+
+ return -1;
+}
+
/*!
* \brief Execute an INSERT query
* \param url
@@ -535,6 +598,7 @@ static struct ast_config_engine curl_engine = {
.store_func = store_curl,
.destroy_func = destroy_curl,
.update_func = update_curl,
+ .update2_func = update2_curl,
.require_func = require_curl,
};
diff --git a/res/res_config_ldap.c b/res/res_config_ldap.c
index 966da9eea..b5690b7b4 100644
--- a/res/res_config_ldap.c
+++ b/res/res_config_ldap.c
@@ -89,6 +89,7 @@ struct ldap_table_config {
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 @@ static int update_ldap(const char *basedn, const char *table_name, const char *a
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)
diff --git a/res/res_config_odbc.c b/res/res_config_odbc.c
index 82d6c989d..ea83652b1 100644
--- a/res/res_config_odbc.c
+++ b/res/res_config_odbc.c
@@ -49,6 +49,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/res_odbc.h"
#include "asterisk/utils.h"
+AST_THREADSTORAGE(sql_buf);
+
struct custom_prepare_struct {
const char *sql;
const char *extra;
@@ -474,6 +476,147 @@ static int update_odbc(const char *database, const char *table, const char *keyf
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;
+}
+
/*!
* \brief Excute an INSERT query
* \param database
@@ -899,6 +1042,7 @@ static struct ast_config_engine odbc_engine = {
.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,
};
diff --git a/res/res_config_pgsql.c b/res/res_config_pgsql.c
index f7c1f3a6a..fe672bf62 100644
--- a/res/res_config_pgsql.c
+++ b/res/res_config_pgsql.c
@@ -42,6 +42,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#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 columns {
};
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 @@ static struct ast_cli_entry cli_realtime[] = {
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 @@ static struct tables *find_table(const char *tablename)
{
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 @@ static struct tables *find_table(const char *tablename)
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 @@ static struct tables *find_table(const char *tablename)
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 @@ static struct tables *find_table(const char *tablename)
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 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab
if (pgsqlConn) {
PQfinish(pgsqlConn);
pgsqlConn = NULL;
- };
+ }
return NULL;
}
@@ -224,15 +253,14 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab
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 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab
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 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab
return NULL;
}
- if (!(result = PQexec(pgsqlConn, sql))) {
+ if (!(result = PQexec(pgsqlConn, sql->str))) {
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\n", PQerrorMessage(pgsqlConn));
ast_mutex_unlock(&pgsql_lock);
return NULL;
@@ -272,8 +299,8 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab
&& 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 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab
}
}
- 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 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab
}
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_variable *realtime_pgsql(const char *database, const char *tab
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 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char
if (pgsqlConn) {
PQfinish(pgsqlConn);
pgsqlConn = NULL;
- };
+ }
return NULL;
}
@@ -375,15 +403,14 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char
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 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char
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 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char
return NULL;
}
- if (!(result = PQexec(pgsqlConn, sql))) {
+ if (!(result = PQexec(pgsqlConn, sql->str))) {
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\n", PQerrorMessage(pgsqlConn));
ast_mutex_unlock(&pgsql_lock);
return NULL;
@@ -428,8 +454,8 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char
&& 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 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char
}
}
- 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 @@ static int update_pgsql(const char *database, const char *tablename, const char
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 @@ static int update_pgsql(const char *database, const char *tablename, const char
if (pgsqlConn) {
PQfinish(pgsqlConn);
pgsqlConn = NULL;
- };
- ast_mutex_unlock(&table->lock);
- ast_free(sql);
+ }
+ release_table(table);
return -1;
}
@@ -533,62 +556,51 @@ static int update_pgsql(const char *database, const char *tablename, const char
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);
+ 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;
}
- ast_str_append(&sql, 0, ", %s = '%s'", newparam, escapebuf);
+ ast_str_append(&sql, 0, ", %s = '%s'", newparam, escapebuf->str);
}
va_end(ap);
- ast_mutex_unlock(&table->lock);
+ release_table(table);
- PQescapeStringConn(pgsqlConn, escapebuf, lookup, (sizeof(escapebuf) - 1) / 2, &pgerror);
- if (pgerror) {
+ 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;
}
- ast_str_append(&sql, 0, " WHERE %s = '%s'", keyfield, escapebuf);
+ ast_str_append(&sql, 0, " WHERE %s = '%s'", keyfield, escapebuf->str);
ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", sql->str);
@@ -596,7 +608,6 @@ static int update_pgsql(const char *database, const char *tablename, const char
ast_mutex_lock(&pgsql_lock);
if (!pgsql_reconnect(database)) {
ast_mutex_unlock(&pgsql_lock);
- ast_free(sql);
return -1;
}
@@ -642,22 +653,145 @@ static int update_pgsql(const char *database, const char *tablename, const char
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;
+
+ 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 ", tablename);
+ ast_str_set(&where, 0, "WHERE");
+
+ 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);
+ 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);
+ release_table(table);
+ ast_free(sql);
+ return -1;
+ }
+ ast_str_append(&where, 0, "%s %s='%s'", first ? "" : " AND", 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);
+ 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(table, newparam)) {
+ ast_log(LOG_NOTICE, "Attempted to update column '%s' in table '%s@%s', but column does not exist!\n", newparam, tablename, database);
+ continue;
+ }
+
+ ESCAPE_STRING(escapebuf, newval);
+ if (pgresult) {
+ ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
+ release_table(table);
+ ast_free(sql);
+ return -1;
+ }
+
+ ast_str_append(&sql, 0, "%s %s='%s'", first ? "" : ",", newparam, escapebuf->str);
+ }
+ release_table(table);
+
+ ast_str_append(&sql, 0, " %s", where->str);
+
+ ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", sql->str);
+
+ /* We now have our complete statement; connect to the server and execute it. */
+ ast_mutex_lock(&pgsql_lock);
+ if (!pgsql_reconnect(database)) {
+ ast_mutex_unlock(&pgsql_lock);
+ return -1;
+ }
+
+ if (!(result = PQexec(pgsqlConn, sql->str))) {
+ 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->str);
+ ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
+ ast_mutex_unlock(&pgsql_lock);
+ return -1;
+ } else {
+ ExecStatusType result_status = PQresultStatus(result);
+ if (result_status != PGRES_COMMAND_OK
+ && 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->str);
+ ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
+ PQresultErrorMessage(result), PQresStatus(result_status));
+ ast_mutex_unlock(&pgsql_lock);
+ return -1;
+ }
+ }
+
+ numrows = atoi(PQcmdTuples(result));
+ ast_mutex_unlock(&pgsql_lock);
+
+ ast_debug(1, "PostgreSQL RealTime: Updated %d rows on table: %s\n", numrows, tablename);
+
+ /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
+ * An integer greater than zero indicates the number of rows affected
+ * Zero indicates that no records were updated
+ * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
+ */
+
+ if (numrows >= 0) {
+ return (int) numrows;
+ }
+
+ return -1;
+}
static int store_pgsql(const char *database, const char *table, va_list ap)
{
PGresult *result = NULL;
Oid insertid;
- struct ast_str *buf = ast_str_create(256);
- struct ast_str *sql1 = ast_str_create(256);
- struct ast_str *sql2 = ast_str_create(256);
+ struct ast_str *buf = ast_str_thread_get(&escapebuf_buf, 256);
+ struct ast_str *sql1 = ast_str_thread_get(&sql_buf, 256);
+ struct ast_str *sql2 = ast_str_thread_get(&where_buf, 256);
int pgresult;
const char *newparam, *newval;
@@ -710,9 +844,6 @@ static int store_pgsql(const char *database, const char *table, va_list ap)
ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql1->str);
ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
ast_mutex_unlock(&pgsql_lock);
- ast_free(sql1);
- ast_free(sql2);
- ast_free(buf);
return -1;
} else {
ExecStatusType result_status = PQresultStatus(result);
@@ -725,18 +856,12 @@ static int store_pgsql(const char *database, const char *table, va_list ap)
ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
PQresultErrorMessage(result), PQresStatus(result_status));
ast_mutex_unlock(&pgsql_lock);
- ast_free(sql1);
- ast_free(sql2);
- ast_free(buf);
return -1;
}
}
insertid = PQoidValue(result);
ast_mutex_unlock(&pgsql_lock);
- ast_free(sql1);
- ast_free(sql2);
- ast_free(buf);
ast_debug(1, "PostgreSQL RealTime: row inserted on table: %s, id: %u\n", table, insertid);
@@ -757,8 +882,8 @@ static int destroy_pgsql(const char *database, const char *table, const char *ke
PGresult *result = NULL;
int numrows = 0;
int pgresult;
- struct ast_str *sql = ast_str_create(256);
- struct ast_str *buf1 = ast_str_create(60), *buf2 = ast_str_create(60);
+ struct ast_str *sql = ast_str_thread_get(&sql_buf, 256);
+ struct ast_str *buf1 = ast_str_thread_get(&where_buf, 60), *buf2 = ast_str_thread_get(&escapebuf_buf, 60);
const char *newparam, *newval;
if (!table) {
@@ -810,9 +935,6 @@ static int destroy_pgsql(const char *database, const char *table, const char *ke
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);
- ast_free(buf1);
- ast_free(buf2);
- ast_free(sql);
return -1;
} else {
ExecStatusType result_status = PQresultStatus(result);
@@ -825,18 +947,12 @@ static int destroy_pgsql(const char *database, const char *table, const char *ke
ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
PQresultErrorMessage(result), PQresStatus(result_status));
ast_mutex_unlock(&pgsql_lock);
- ast_free(buf1);
- ast_free(buf2);
- ast_free(sql);
return -1;
}
}
numrows = atoi(PQcmdTuples(result));
ast_mutex_unlock(&pgsql_lock);
- ast_free(buf1);
- ast_free(buf2);
- ast_free(sql);
ast_debug(1, "PostgreSQL RealTime: Deleted %d rows on table: %s\n", numrows, table);
@@ -861,9 +977,7 @@ static struct ast_config *config_pgsql(const char *database, const char *table,
long num_rows;
struct ast_variable *new_v;
struct ast_category *cur_cat = NULL;
- char sqlbuf[1024] = "";
- char *sql = sqlbuf;
- size_t sqlleft = sizeof(sqlbuf);
+ struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
char last[80] = "";
int last_cat_metric = 0;
@@ -874,11 +988,11 @@ static struct ast_config *config_pgsql(const char *database, const char *table,
return NULL;
}
- ast_build_string(&sql, &sqlleft, "SELECT category, var_name, var_val, cat_metric FROM %s ", table);
- ast_build_string(&sql, &sqlleft, "WHERE filename='%s' and commented=0", file);
- ast_build_string(&sql, &sqlleft, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ");
+ ast_str_set(&sql, 0, "SELECT category, var_name, var_val, cat_metric FROM %s "
+ "WHERE filename='%s' and commented=0"
+ "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ", table, file);
- ast_debug(1, "PostgreSQL RealTime: Static SQL: %s\n", sqlbuf);
+ ast_debug(1, "PostgreSQL RealTime: Static SQL: %s\n", sql->str);
/* We now have our complete statement; Lets connect to the server and execute it. */
ast_mutex_lock(&pgsql_lock);
@@ -887,10 +1001,10 @@ static struct ast_config *config_pgsql(const char *database, const char *table,
return NULL;
}
- if (!(result = PQexec(pgsqlConn, sqlbuf))) {
+ if (!(result = PQexec(pgsqlConn, sql->str))) {
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\n", PQerrorMessage(pgsqlConn));
ast_mutex_unlock(&pgsql_lock);
return NULL;
@@ -901,7 +1015,7 @@ static struct ast_config *config_pgsql(const char *database, const char *table,
&& 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);
+ 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);
@@ -1067,7 +1181,7 @@ static int require_pgsql(const char *database, const char *tablename, va_list ap
}
}
}
- ast_mutex_unlock(&table->lock);
+ release_table(table);
return res;
}
@@ -1101,6 +1215,7 @@ static struct ast_config_engine pgsql_engine = {
.store_func = store_pgsql,
.destroy_func = destroy_pgsql,
.update_func = update_pgsql,
+ .update2_func = update2_pgsql,
.require_func = require_pgsql,
.unload_func = unload_pgsql,
};
@@ -1353,7 +1468,7 @@ static char *handle_cli_realtime_pgsql_cache(struct ast_cli_entry *e, int cmd, s
AST_LIST_TRAVERSE(&cur->columns, col, list) {
ast_cli(a->fd, "%-20.20s %-20.20s %3d %-8.8s\n", col->name, col->type, col->len, col->notnull ? "NOT NULL" : "");
}
- ast_mutex_unlock(&cur->lock);
+ release_table(cur);
} else {
ast_cli(a->fd, "No such table '%s'\n", a->argv[4]);
}
diff --git a/res/res_config_sqlite.c b/res/res_config_sqlite.c
index 7a18d001c..c3ef1dd1a 100644
--- a/res/res_config_sqlite.c
+++ b/res/res_config_sqlite.c
@@ -123,6 +123,9 @@ MACRO_BEGIN \
} \
MACRO_END
+AST_THREADSTORAGE(sql_buf);
+AST_THREADSTORAGE(where_buf);
+
/*!
* Maximum number of loops before giving up executing a query. Calls to
* sqlite_xxx() functions which can return SQLITE_BUSY
@@ -299,7 +302,7 @@ static struct ast_config * config_handler(const char *database, const char *tabl
* \retval 0 if an error occurred.
*/
static size_t get_params(va_list ap, const char ***params_ptr,
- const char ***vals_ptr);
+ const char ***vals_ptr, int warn);
/*!
* \brief SQLite callback function for RealTime configuration.
@@ -396,6 +399,8 @@ static struct ast_config * realtime_multi_handler(const char *database,
*/
static int realtime_update_handler(const char *database, const char *table,
const char *keyfield, const char *entity, va_list ap);
+static int realtime_update2_handler(const char *database, const char *table,
+ va_list ap);
/*!
* \brief Asterisk callback function for RealTime configuration (variable
@@ -484,6 +489,7 @@ static struct ast_config_engine sqlite_engine =
.store_func = realtime_store_handler,
.destroy_func = realtime_destroy_handler,
.update_func = realtime_update_handler,
+ .update2_func = realtime_update2_handler,
.require_func = realtime_require_handler,
.unload_func = realtime_unload_handler,
};
@@ -949,7 +955,7 @@ static struct ast_config *config_handler(const char *database, const char *table
return cfg;
}
-static size_t get_params(va_list ap, const char ***params_ptr, const char ***vals_ptr)
+static size_t get_params(va_list ap, const char ***params_ptr, const char ***vals_ptr, int warn)
{
const char **tmp, *param, *val, **params, **vals;
size_t params_count;
@@ -981,8 +987,9 @@ static size_t get_params(va_list ap, const char ***params_ptr, const char ***val
if (params_count > 0) {
*params_ptr = params;
*vals_ptr = vals;
- } else
+ } else if (warn) {
ast_log(LOG_WARNING, "1 parameter and 1 value at least required\n");
+ }
return params_count;
}
@@ -1029,7 +1036,7 @@ static struct ast_variable * realtime_handler(const char *database, const char *
return NULL;
}
- params_count = get_params(ap, &params, &vals);
+ params_count = get_params(ap, &params, &vals, 1);
if (params_count == 0)
return NULL;
@@ -1038,10 +1045,10 @@ static struct ast_variable * realtime_handler(const char *database, const char *
/* \cond DOXYGEN_CAN_PARSE_THIS */
#undef QUERY
-#define QUERY "SELECT * FROM '%q' WHERE commented = 0 AND %q%s '%q'"
+#define QUERY "SELECT * FROM '%q' WHERE%s %q%s '%q'"
/* \endcond */
- query = sqlite_mprintf(QUERY, table, params[0], op, vals[0]);
+ query = sqlite_mprintf(QUERY, table, !strcmp(config_table, table) ? " commented = 0 AND" : "", params[0], op, vals[0]);
if (!query) {
ast_log(LOG_WARNING, "Unable to allocate SQL query\n");
@@ -1174,7 +1181,7 @@ static struct ast_config *realtime_multi_handler(const char *database,
return NULL;
}
- if (!(params_count = get_params(ap, &params, &vals))) {
+ if (!(params_count = get_params(ap, &params, &vals, 1))) {
ast_config_destroy(cfg);
return NULL;
}
@@ -1286,7 +1293,7 @@ static int realtime_update_handler(const char *database, const char *table,
return -1;
}
- if (!(params_count = get_params(ap, &params, &vals)))
+ if (!(params_count = get_params(ap, &params, &vals, 1)))
return -1;
/* \cond DOXYGEN_CAN_PARSE_THIS */
@@ -1355,6 +1362,80 @@ static int realtime_update_handler(const char *database, const char *table,
return rows_num;
}
+static int realtime_update2_handler(const char *database, const char *table,
+ va_list ap)
+{
+ char *errormsg = NULL, *tmp1, *tmp2;
+ int error, rows_num, first = 1;
+ struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
+ struct ast_str *where = ast_str_thread_get(&where_buf, 100);
+ const char *param, *value;
+
+ if (!table) {
+ ast_log(LOG_WARNING, "Table name unspecified\n");
+ return -1;
+ }
+
+ if (!sql) {
+ return -1;
+ }
+
+ ast_str_set(&sql, 0, "UPDATE %s SET", table);
+ ast_str_set(&where, 0, " WHERE");
+
+ while ((param = va_arg(ap, const char *))) {
+ value = va_arg(ap, const char *);
+ ast_str_append(&where, 0, "%s %s = %s",
+ first ? "" : " AND",
+ tmp1 = sqlite_mprintf("%q", param),
+ tmp2 = sqlite_mprintf("%Q", value));
+ sqlite_freemem(tmp1);
+ sqlite_freemem(tmp2);
+ first = 0;
+ }
+
+ if (first) {
+ ast_log(LOG_ERROR, "No criteria specified on update to '%s@%s'!\n", table, database);
+ return -1;
+ }
+
+ first = 1;
+ while ((param = va_arg(ap, const char *))) {
+ value = va_arg(ap, const char *);
+ ast_str_append(&sql, 0, "%s %s = %s",
+ first ? "" : ",",
+ tmp1 = sqlite_mprintf("%q", param),
+ tmp2 = sqlite_mprintf("%Q", value));
+ sqlite_freemem(tmp1);
+ sqlite_freemem(tmp2);
+ first = 0;
+ }
+
+ ast_str_append(&sql, 0, " %s", where->str);
+ ast_debug(1, "SQL query: %s\n", sql->str);
+
+ ast_mutex_lock(&mutex);
+
+ RES_CONFIG_SQLITE_BEGIN
+ error = sqlite_exec(db, sql->str, NULL, NULL, &errormsg);
+ RES_CONFIG_SQLITE_END(error)
+
+ if (!error) {
+ rows_num = sqlite_changes(db);
+ } else {
+ rows_num = -1;
+ }
+
+ ast_mutex_unlock(&mutex);
+
+ if (error) {
+ ast_log(LOG_WARNING, "%s\n", S_OR(errormsg, sqlite_error_string(error)));
+ }
+ sqlite_freemem(errormsg);
+
+ return rows_num;
+}
+
static int realtime_store_handler(const char *database, const char *table, va_list ap)
{
char *errormsg = NULL, *tmp_str, *tmp_keys = NULL, *tmp_keys2 = NULL, *tmp_vals = NULL, *tmp_vals2 = NULL;
@@ -1368,7 +1449,7 @@ static int realtime_store_handler(const char *database, const char *table, va_li
return -1;
}
- if (!(params_count = get_params(ap, &params, &vals)))
+ if (!(params_count = get_params(ap, &params, &vals, 1)))
return -1;
/* \cond DOXYGEN_CAN_PARSE_THIS */
@@ -1392,10 +1473,10 @@ static int realtime_store_handler(const char *database, const char *table, va_li
}
if ( tmp_vals2 ) {
- tmp_vals = sqlite_mprintf("%s, '%q'", tmp_vals2, params[i]);
+ tmp_vals = sqlite_mprintf("%s, '%q'", tmp_vals2, vals[i]);
sqlite_freemem(tmp_vals2);
} else {
- tmp_vals = sqlite_mprintf("'%q'", params[i]);
+ tmp_vals = sqlite_mprintf("'%q'", vals[i]);
}
if (!tmp_vals) {
ast_log(LOG_WARNING, "Unable to reallocate SQL query\n");
@@ -1453,7 +1534,7 @@ static int realtime_destroy_handler(const char *database, const char *table,
const char *keyfield, const char *entity, va_list ap)
{
char *query, *errormsg = NULL, *tmp_str;
- const char **params, **vals;
+ const char **params = NULL, **vals = NULL;
size_t params_count;
int error, rows_num;
size_t i;
@@ -1463,8 +1544,7 @@ static int realtime_destroy_handler(const char *database, const char *table,
return -1;
}
- if (!(params_count = get_params(ap, &params, &vals)))
- return -1;
+ params_count = get_params(ap, &params, &vals, 0);
/* \cond DOXYGEN_CAN_PARSE_THIS */
#undef QUERY
@@ -1509,10 +1589,11 @@ static int realtime_destroy_handler(const char *database, const char *table,
error = sqlite_exec(db, query, NULL, NULL, &errormsg);
RES_CONFIG_SQLITE_END(error)
- if (!error)
+ if (!error) {
rows_num = sqlite_changes(db);
- else
+ } else {
rows_num = -1;
+ }
ast_mutex_unlock(&mutex);
diff --git a/res/res_realtime.c b/res/res_realtime.c
index 5ecff8b94..15e25beec 100644
--- a/res/res_realtime.c
+++ b/res/res_realtime.c
@@ -76,7 +76,8 @@ static char *cli_realtime_load(struct ast_cli_entry *e, int cmd, struct ast_cli_
return CLI_SUCCESS;
}
-static char *cli_realtime_update(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) {
+static char *cli_realtime_update(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
int res = 0;
switch (cmd) {
@@ -93,18 +94,149 @@ static char *cli_realtime_update(struct ast_cli_entry *e, int cmd, struct ast_cl
return NULL;
}
-
if (a->argc < 7)
return CLI_SHOWUSAGE;
res = ast_update_realtime(a->argv[2], a->argv[3], a->argv[4], a->argv[5], a->argv[6], SENTINEL);
- if(res < 0) {
+ if (res < 0) {
+ ast_cli(a->fd, "Failed to update. Check the debug log for possible SQL related entries.\n");
+ return CLI_FAILURE;
+ }
+
+ ast_cli(a->fd, "Updated %d RealTime record%s.\n", res, ESS(res));
+
+ return CLI_SUCCESS;
+}
+
+static char *cli_realtime_update2(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int res = -1;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "realtime update2";
+ e->usage =
+ "Usage: realtime update2 <family> <colupdate> <newvalue> <colmatch> <valuematch> [... <colmatch5> <valuematch5>]\n"
+ " Update a single variable using the RealTime driver.\n"
+ " You must supply a family name, a column to update on, a new value, column to match, and value to match.\n"
+ " Ex: realtime update sipfriends name bobsphone port 4343\n"
+ " will execute SQL as UPDATE sipfriends SET port = 4343 WHERE name = bobsphone\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc < 7)
+ return CLI_SHOWUSAGE;
+
+ if (a->argc == 7) {
+ res = ast_update2_realtime(a->argv[2], a->argv[5], a->argv[6], SENTINEL, a->argv[3], a->argv[4], SENTINEL);
+ } else if (a->argc == 9) {
+ res = ast_update2_realtime(a->argv[2], a->argv[5], a->argv[6], a->argv[7], a->argv[8], SENTINEL, a->argv[3], a->argv[4], SENTINEL);
+ } else if (a->argc == 11) {
+ res = ast_update2_realtime(a->argv[2], a->argv[5], a->argv[6], a->argv[7], a->argv[8], a->argv[9], a->argv[10], SENTINEL, a->argv[3], a->argv[4], SENTINEL);
+ } else if (a->argc == 13) {
+ res = ast_update2_realtime(a->argv[2], a->argv[5], a->argv[6], a->argv[7], a->argv[8], a->argv[9], a->argv[10], a->argv[11], a->argv[12], SENTINEL, a->argv[3], a->argv[4], SENTINEL);
+ } else if (a->argc == 15) {
+ res = ast_update2_realtime(a->argv[2], a->argv[5], a->argv[6], a->argv[7], a->argv[8], a->argv[9], a->argv[10], a->argv[11], a->argv[12], a->argv[13], a->argv[14], SENTINEL, a->argv[3], a->argv[4], SENTINEL);
+ } else {
+ return CLI_SHOWUSAGE;
+ }
+
+ if (res < 0) {
ast_cli(a->fd, "Failed to update. Check the debug log for possible SQL related entries.\n");
return CLI_FAILURE;
}
- ast_cli(a->fd, "Updated %d RealTime record%s.\n", res, ESS(res));
+ ast_cli(a->fd, "Updated %d RealTime record%s.\n", res, ESS(res));
+
+ return CLI_SUCCESS;
+}
+
+static char *cli_realtime_store(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int res = -1;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "realtime store";
+ e->usage =
+ "Usage: realtime store <family> <colname1> <value1> [<colname2> <value2> [... <colmatch5> <valuematch5>]]\n"
+ " Create a stored row using the RealTime driver.\n"
+ " You must supply a family name and name/value pairs (up to 5). If\n"
+ " you need to store more than 5 key/value pairs, start with the first\n"
+ " five, then use 'realtime update' or 'realtime update2' to add\n"
+ " additional columns.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc < 5) {
+ return CLI_SHOWUSAGE;
+ } else if (a->argc == 5) {
+ res = ast_store_realtime(a->argv[2], a->argv[3], a->argv[4], SENTINEL);
+ } else if (a->argc == 7) {
+ res = ast_store_realtime(a->argv[2], a->argv[3], a->argv[4], a->argv[5], a->argv[6], SENTINEL);
+ } else if (a->argc == 9) {
+ res = ast_store_realtime(a->argv[2], a->argv[3], a->argv[4], a->argv[5], a->argv[6], a->argv[7], a->argv[8], SENTINEL);
+ } else if (a->argc == 11) {
+ res = ast_store_realtime(a->argv[2], a->argv[3], a->argv[4], a->argv[5], a->argv[6], a->argv[7], a->argv[8], a->argv[9], a->argv[10], SENTINEL);
+ } else if (a->argc == 13) {
+ res = ast_store_realtime(a->argv[2], a->argv[3], a->argv[4], a->argv[5], a->argv[6], a->argv[7], a->argv[8], a->argv[9], a->argv[10], a->argv[11], a->argv[12], SENTINEL);
+ } else {
+ return CLI_SHOWUSAGE;
+ }
+
+ if (res < 0) {
+ ast_cli(a->fd, "Failed to store record. Check the debug log for possible SQL related entries.\n");
+ return CLI_FAILURE;
+ }
+
+ ast_cli(a->fd, "Stored RealTime record.\n");
+
+ return CLI_SUCCESS;
+}
+
+static char *cli_realtime_destroy(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int res = -1;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "realtime destroy";
+ e->usage =
+ "Usage: realtime destroy <family> <colname1> <value1> [<colname2> <value2> [... <colmatch5> <valuematch5>]]\n"
+ " Remove a stored row using the RealTime driver.\n"
+ " You must supply a family name and name/value pairs (up to 5).\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc < 5) {
+ return CLI_SHOWUSAGE;
+ } else if (a->argc == 5) {
+ res = ast_destroy_realtime(a->argv[2], a->argv[3], a->argv[4], SENTINEL);
+ } else if (a->argc == 7) {
+ res = ast_destroy_realtime(a->argv[2], a->argv[3], a->argv[4], a->argv[5], a->argv[6], SENTINEL);
+ } else if (a->argc == 9) {
+ res = ast_destroy_realtime(a->argv[2], a->argv[3], a->argv[4], a->argv[5], a->argv[6], a->argv[7], a->argv[8], SENTINEL);
+ } else if (a->argc == 11) {
+ res = ast_destroy_realtime(a->argv[2], a->argv[3], a->argv[4], a->argv[5], a->argv[6], a->argv[7], a->argv[8], a->argv[9], a->argv[10], SENTINEL);
+ } else if (a->argc == 13) {
+ res = ast_destroy_realtime(a->argv[2], a->argv[3], a->argv[4], a->argv[5], a->argv[6], a->argv[7], a->argv[8], a->argv[9], a->argv[10], a->argv[11], a->argv[12], SENTINEL);
+ } else {
+ return CLI_SHOWUSAGE;
+ }
+
+ if (res < 0) {
+ ast_cli(a->fd, "Failed to remove record. Check the debug log for possible SQL related entries.\n");
+ return CLI_FAILURE;
+ }
+
+ ast_cli(a->fd, "Removed %d RealTime record%s.\n", res, ESS(res));
return CLI_SUCCESS;
}
@@ -112,6 +244,9 @@ static char *cli_realtime_update(struct ast_cli_entry *e, int cmd, struct ast_cl
static struct ast_cli_entry cli_realtime[] = {
AST_CLI_DEFINE(cli_realtime_load, "Used to print out RealTime variables."),
AST_CLI_DEFINE(cli_realtime_update, "Used to update RealTime variables."),
+ AST_CLI_DEFINE(cli_realtime_update2, "Used to test the RealTime update2 method"),
+ AST_CLI_DEFINE(cli_realtime_store, "Store a new row into a RealTime database"),
+ AST_CLI_DEFINE(cli_realtime_destroy, "Delete a row from a RealTime database"),
};
static int unload_module(void)