Merge "res_pjsip: Ignore empty TLS configuration"
[asterisk/asterisk.git] / res / res_config_pgsql.c
index f8de865..e436e2f 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Asterisk -- A telephony toolkit for Linux.
+ * Asterisk -- An open source telephony toolkit.
  *
- * Copyright (C) 1999-2010, Digium, Inc.
+ * Copyright (C) 1999 - 2017, Digium, Inc.
  *
  * Manuel Guesdon <mguesdon@oxymium.net> - PostgreSQL RealTime Driver Author/Adaptor
  * Mark Spencer <markster@digium.com>  - Asterisk Author
@@ -19,7 +19,7 @@
  * \author Mark Spencer <markster@digium.com>
  * \author Manuel Guesdon <mguesdon@oxymium.net> - PostgreSQL RealTime Driver Author/Adaptor
  *
- * \extref PostgreSQL http://www.postgresql.org
+ * PostgreSQL http://www.postgresql.org
  */
 
 /*** MODULEINFO
@@ -29,8 +29,6 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
 #include <libpq-fe.h>                  /* PostgreSQL */
 
 #include "asterisk/file.h"
@@ -54,6 +52,7 @@ AST_THREADSTORAGE(semibuf_buf);
 static PGconn *pgsqlConn = NULL;
 static int version;
 #define has_schema_support     (version > 70300 ? 1 : 0)
+#define USE_BACKSLASH_AS_STRING        (version >= 90100 ? 1 : 0)
 
 #define MAX_DB_OPTION_SIZE 64
 
@@ -79,6 +78,7 @@ static char dbhost[MAX_DB_OPTION_SIZE] = "";
 static char dbuser[MAX_DB_OPTION_SIZE] = "";
 static char dbpass[MAX_DB_OPTION_SIZE] = "";
 static char dbname[MAX_DB_OPTION_SIZE] = "";
+static char dbappname[MAX_DB_OPTION_SIZE] = "";
 static char dbsock[MAX_DB_OPTION_SIZE] = "";
 static int dbport = 5432;
 static time_t connect_time = 0;
@@ -139,7 +139,7 @@ static void destroy_table(struct tables *table)
  *  \return -2 on query failure that resulted in disconnection
  *  \return 0 on success
  *
- *  \example see pgsql_exec for full example
+ *  \note see pgsql_exec for full example
  */
 static int _pgsql_exec(const char *database, const char *tablename, const char *sql, PGresult **result)
 {
@@ -198,7 +198,7 @@ static int _pgsql_exec(const char *database, const char *tablename, const char *
  *  \return -1 on query failure
  *  \return 0 on success
  *
- *  \example
+ *  \code
  *     int i, rows;
  *     PGresult *result;
  *     char *field_name, *field_type, *field_len, *field_notnull, *field_default;
@@ -213,7 +213,7 @@ static int _pgsql_exec(const char *database, const char *tablename, const char *
  *             field_notnull = PQgetvalue(result, i, 3);
  *             field_default = PQgetvalue(result, i, 4);
  *     }
- *
+ *  \endcode
  */
 static int pgsql_exec(const char *database, const char *tablename, const char *sql, PGresult **result)
 {
@@ -252,8 +252,8 @@ static struct tables *find_table(const char *database, const char *orig_tablenam
        struct columns *column;
        struct tables *table;
        struct ast_str *sql = ast_str_thread_get(&findtable_buf, 330);
-        PGresult *result;
-        int exec_result;
+       RAII_VAR(PGresult *, result, NULL, PQclear);
+       int exec_result;
        char *fname, *ftype, *flen, *fnotnull, *fdef;
        int i, rows;
 
@@ -269,6 +269,7 @@ static struct tables *find_table(const char *database, const char *orig_tablenam
        }
 
        if (database == NULL) {
+               AST_LIST_UNLOCK(&psql_tables);
                return NULL;
        }
 
@@ -276,77 +277,44 @@ static struct tables *find_table(const char *database, const char *orig_tablenam
 
        /* Not found, scan the table */
        if (has_schema_support) {
-               char *schemaname, *tablename;
+               char *schemaname, *tablename, *tmp_schemaname, *tmp_tablename;
                if (strchr(orig_tablename, '.')) {
-                       schemaname = ast_strdupa(orig_tablename);
-                       tablename = strchr(schemaname, '.');
-                       *tablename++ = '\0';
+                       tmp_schemaname = ast_strdupa(orig_tablename);
+                       tmp_tablename = strchr(tmp_schemaname, '.');
+                       *tmp_tablename++ = '\0';
                } else {
-                       schemaname = "";
-                       tablename = ast_strdupa(orig_tablename);
+                       tmp_schemaname = "";
+                       tmp_tablename = ast_strdupa(orig_tablename);
                }
 
-               /* Escape special characters in schemaname */
-               if (strchr(schemaname, '\\') || strchr(schemaname, '\'')) {
-                       char *tmp = schemaname, *ptr;
-
-                       ptr = schemaname = alloca(strlen(tmp) * 2 + 1);
-                       for (; *tmp; tmp++) {
-                               if (strchr("\\'", *tmp)) {
-                                       *ptr++ = *tmp;
-                               }
-                               *ptr++ = *tmp;
-                       }
-                       *ptr = '\0';
-               }
-               /* Escape special characters in tablename */
-               if (strchr(tablename, '\\') || strchr(tablename, '\'')) {
-                       char *tmp = tablename, *ptr;
-
-                       ptr = tablename = alloca(strlen(tmp) * 2 + 1);
-                       for (; *tmp; tmp++) {
-                               if (strchr("\\'", *tmp)) {
-                                       *ptr++ = *tmp;
-                               }
-                               *ptr++ = *tmp;
-                       }
-                       *ptr = '\0';
-               }
+               tablename = ast_alloca(strlen(tmp_tablename) * 2 + 1);
+               PQescapeStringConn(pgsqlConn, tablename, tmp_tablename, strlen(tmp_tablename), NULL);
+               schemaname = ast_alloca(strlen(tmp_schemaname) * 2 + 1);
+               PQescapeStringConn(pgsqlConn, schemaname, tmp_schemaname, strlen(tmp_schemaname), NULL);
 
                ast_str_set(&sql, 0, "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod FROM (((pg_catalog.pg_class c INNER JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace AND c.relname = '%s' AND n.nspname = %s%s%s) INNER JOIN pg_catalog.pg_attribute a ON (NOT a.attisdropped) AND a.attnum > 0 AND a.attrelid = c.oid) INNER JOIN pg_catalog.pg_type t ON t.oid = a.atttypid) LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid AND d.adnum = a.attnum ORDER BY n.nspname, c.relname, attnum",
                        tablename,
                        ast_strlen_zero(schemaname) ? "" : "'", ast_strlen_zero(schemaname) ? "current_schema()" : schemaname, ast_strlen_zero(schemaname) ? "" : "'");
        } else {
-               /* Escape special characters in tablename */
-               if (strchr(orig_tablename, '\\') || strchr(orig_tablename, '\'')) {
-                       const char *tmp = orig_tablename;
-                       char *ptr;
-
-                       orig_tablename = ptr = alloca(strlen(tmp) * 2 + 1);
-                       for (; *tmp; tmp++) {
-                               if (strchr("\\'", *tmp)) {
-                                       *ptr++ = *tmp;
-                               }
-                               *ptr++ = *tmp;
-                       }
-                       *ptr = '\0';
-               }
+               char *tablename;
+               tablename = ast_alloca(strlen(orig_tablename) * 2 + 1);
+               PQescapeStringConn(pgsqlConn, tablename, orig_tablename, strlen(orig_tablename), NULL);
 
-               ast_str_set(&sql, 0, "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod FROM pg_class c, pg_type t, pg_attribute a LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid AND d.adnum = a.attnum WHERE c.oid = a.attrelid AND a.atttypid = t.oid AND (a.attnum > 0) AND c.relname = '%s' ORDER BY c.relname, attnum", orig_tablename);
+               ast_str_set(&sql, 0, "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod FROM pg_class c, pg_type t, pg_attribute a LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid AND d.adnum = a.attnum WHERE c.oid = a.attrelid AND a.atttypid = t.oid AND (a.attnum > 0) AND c.relname = '%s' ORDER BY c.relname, attnum", tablename);
        }
 
+       ast_mutex_lock(&pgsql_lock);
        exec_result = pgsql_exec(database, orig_tablename, ast_str_buffer(sql), &result);
+       ast_mutex_unlock(&pgsql_lock);
        ast_debug(1, "Query of table structure complete.  Now retrieving results.\n");
        if (exec_result != 0) {
                ast_log(LOG_ERROR, "Failed to query database columns for table %s\n", orig_tablename);
-               PQclear(result);
                AST_LIST_UNLOCK(&psql_tables);
                return NULL;
        }
 
        if (!(table = ast_calloc(1, sizeof(*table) + strlen(orig_tablename) + 1))) {
                ast_log(LOG_ERROR, "Unable to allocate memory for new table structure\n");
-               PQclear(result);
                AST_LIST_UNLOCK(&psql_tables);
                return NULL;
        }
@@ -365,7 +333,6 @@ static struct tables *find_table(const char *database, const char *orig_tablenam
 
                if (!(column = ast_calloc(1, sizeof(*column) + strlen(fname) + strlen(ftype) + 2))) {
                        ast_log(LOG_ERROR, "Unable to allocate column element for %s, %s\n", orig_tablename, fname);
-                       PQclear(result);
                        destroy_table(table);
                        AST_LIST_UNLOCK(&psql_tables);
                        return NULL;
@@ -395,7 +362,6 @@ static struct tables *find_table(const char *database, const char *orig_tablenam
                }
                AST_LIST_INSERT_TAIL(&table->columns, column, list);
        }
-       PQclear(result);
 
        AST_LIST_INSERT_TAIL(&psql_tables, table, list);
        ast_rwlock_rdlock(&table->lock);
@@ -418,71 +384,97 @@ static struct columns *find_column(struct tables *t, const char *colname)
        return NULL;
 }
 
-static struct ast_variable *realtime_pgsql(const char *database, const char *tablename, va_list ap)
+#define IS_SQL_LIKE_CLAUSE(x) ((x) && ast_ends_with(x, " LIKE"))
+#define ESCAPE_CLAUSE (USE_BACKSLASH_AS_STRING ? " ESCAPE '\\'" : " ESCAPE '\\\\'")
+
+static struct ast_variable *realtime_pgsql(const char *database, const char *tablename, const struct ast_variable *fields)
 {
-       PGresult *result = NULL;
+       RAII_VAR(PGresult *, result, NULL, PQclear);
        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;
+       char *escape = "";
+       const struct ast_variable *field = fields;
        struct ast_variable *var = NULL, *prev = NULL;
 
+       /*
+        * Ignore database from the extconfig.conf since it was
+        * configured by res_pgsql.conf.
+        */
+       database = dbname;
+
        if (!tablename) {
                ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
                return NULL;
        }
 
+       /*
+        * Must connect to the server before anything else as ESCAPE_STRING()
+        * uses pgsqlConn
+        */
+       ast_mutex_lock(&pgsql_lock);
+       if (!pgsql_reconnect(database)) {
+               ast_mutex_unlock(&pgsql_lock);
+               return NULL;
+       }
+
        /* Get the first parameter and first value in our list of passed paramater/value pairs */
-       newparam = va_arg(ap, const char *);
-       newval = va_arg(ap, const char *);
-       if (!newparam || !newval) {
+       if (!field) {
                ast_log(LOG_WARNING,
                                "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
                if (pgsqlConn) {
                        PQfinish(pgsqlConn);
                        pgsqlConn = NULL;
                }
+               ast_mutex_unlock(&pgsql_lock);
                return NULL;
        }
 
        /* 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 */
-       op = strchr(newparam, ' ') ? "" : " =";
+       if (!strchr(field->name, ' ')) {
+               op = " =";
+       } else {
+               op = "";
+               if (IS_SQL_LIKE_CLAUSE(field->name)) {
+                       escape = ESCAPE_CLAUSE;
+               }
+       }
 
-       ESCAPE_STRING(escapebuf, newval);
+       ESCAPE_STRING(escapebuf, field->value);
        if (pgresult) {
-               ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", newval);
-               va_end(ap);
+               ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
+               ast_mutex_unlock(&pgsql_lock);
                return NULL;
        }
 
-       ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", tablename, newparam, op, ast_str_buffer(escapebuf));
-       while ((newparam = va_arg(ap, const char *))) {
-               newval = va_arg(ap, const char *);
-               if (!strchr(newparam, ' '))
+       ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'%s", tablename, field->name, op, ast_str_buffer(escapebuf), escape);
+       while ((field = field->next)) {
+               escape = "";
+               if (!strchr(field->name, ' ')) {
                        op = " =";
-               else
+               } else {
                        op = "";
+                       if (IS_SQL_LIKE_CLAUSE(field->name)) {
+                               escape = ESCAPE_CLAUSE;
+                       }
+               }
 
-               ESCAPE_STRING(escapebuf, newval);
+               ESCAPE_STRING(escapebuf, field->value);
                if (pgresult) {
-                       ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", newval);
-                       va_end(ap);
+                       ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
+                       ast_mutex_unlock(&pgsql_lock);
                        return NULL;
                }
 
-               ast_str_append(&sql, 0, " AND %s%s '%s'", newparam, op, ast_str_buffer(escapebuf));
+               ast_str_append(&sql, 0, " AND %s%s '%s'%s", field->name, op, ast_str_buffer(escapebuf), escape);
        }
-       va_end(ap);
 
        /* We now have our complete statement; Lets connect to the server and execute it. */
-       ast_mutex_lock(&pgsql_lock);
-
         if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
-               PQclear(result);
                ast_mutex_unlock(&pgsql_lock);
                return NULL;
         }
@@ -498,7 +490,6 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab
                ast_debug(1, "PostgreSQL RealTime: Found %d rows.\n", num_rows);
 
                if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
-                       PQclear(result);
                        ast_mutex_unlock(&pgsql_lock);
                        return NULL;
                }
@@ -527,27 +518,33 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab
                ast_debug(1, "Postgresql RealTime: Could not find any rows in table %s@%s.\n", tablename, database);
        }
 
-       PQclear(result);
        ast_mutex_unlock(&pgsql_lock);
 
        return var;
 }
 
-static struct ast_config *realtime_multi_pgsql(const char *database, const char *table, va_list ap)
+static struct ast_config *realtime_multi_pgsql(const char *database, const char *table, const struct ast_variable *fields)
 {
-       PGresult *result = NULL;
+       RAII_VAR(PGresult *, result, NULL, PQclear);
        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 struct ast_variable *field = fields;
        const char *initfield = NULL;
        char *stringp;
        char *chunk;
        char *op;
-       const char *newparam, *newval;
+       char *escape = "";
        struct ast_variable *var = NULL;
        struct ast_config *cfg = NULL;
        struct ast_category *cat = NULL;
 
+       /*
+        * Ignore database from the extconfig.conf since it was
+        * configured by res_pgsql.conf.
+        */
+       database = dbname;
+
        if (!table) {
                ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
                return NULL;
@@ -556,21 +553,30 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char
        if (!(cfg = ast_config_new()))
                return NULL;
 
+       /*
+        * Must connect to the server before anything else as ESCAPE_STRING()
+        * uses pgsqlConn
+        */
+       ast_mutex_lock(&pgsql_lock);
+       if (!pgsql_reconnect(database)) {
+               ast_mutex_unlock(&pgsql_lock);
+               return NULL;
+       }
+
        /* Get the first parameter and first value in our list of passed paramater/value pairs */
-       newparam = va_arg(ap, const char *);
-       newval = va_arg(ap, const char *);
-       if (!newparam || !newval) {
+       if (!field) {
                ast_log(LOG_WARNING,
                                "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
                if (pgsqlConn) {
                        PQfinish(pgsqlConn);
                        pgsqlConn = NULL;
                }
+               ast_mutex_unlock(&pgsql_lock);
                ast_config_destroy(cfg);
                return NULL;
        }
 
-       initfield = ast_strdupa(newparam);
+       initfield = ast_strdupa(field->name);
        if ((op = strchr(initfield, ' '))) {
                *op = '\0';
        }
@@ -578,47 +584,53 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char
        /* 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 */
 
-       if (!strchr(newparam, ' '))
+       if (!strchr(field->name, ' ')) {
                op = " =";
-       else
+               escape = "";
+       } else {
                op = "";
+               if (IS_SQL_LIKE_CLAUSE(field->name)) {
+                       escape = ESCAPE_CLAUSE;
+               }
+       }
 
-       ESCAPE_STRING(escapebuf, newval);
+       ESCAPE_STRING(escapebuf, field->value);
        if (pgresult) {
-               ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", newval);
-               va_end(ap);
+               ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
+               ast_mutex_unlock(&pgsql_lock);
                ast_config_destroy(cfg);
                return NULL;
        }
 
-       ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, ast_str_buffer(escapebuf));
-       while ((newparam = va_arg(ap, const char *))) {
-               newval = va_arg(ap, const char *);
-               if (!strchr(newparam, ' '))
+       ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'%s", table, field->name, op, ast_str_buffer(escapebuf), escape);
+       while ((field = field->next)) {
+               escape = "";
+               if (!strchr(field->name, ' ')) {
                        op = " =";
-               else
+                       escape = "";
+               } else {
                        op = "";
+                       if (IS_SQL_LIKE_CLAUSE(field->name)) {
+                               escape = ESCAPE_CLAUSE;
+                       }
+               }
 
-               ESCAPE_STRING(escapebuf, newval);
+               ESCAPE_STRING(escapebuf, field->value);
                if (pgresult) {
-                       ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", newval);
-                       va_end(ap);
+                       ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
+                       ast_mutex_unlock(&pgsql_lock);
                        ast_config_destroy(cfg);
                        return NULL;
                }
 
-               ast_str_append(&sql, 0, " AND %s%s '%s'", newparam, op, ast_str_buffer(escapebuf));
+               ast_str_append(&sql, 0, " AND %s%s '%s'%s", field->name, op, ast_str_buffer(escapebuf), escape);
        }
 
        if (initfield) {
                ast_str_append(&sql, 0, " ORDER BY %s", initfield);
        }
 
-       va_end(ap);
-
        /* We now have our complete statement; Lets connect to the server and execute it. */
-       ast_mutex_lock(&pgsql_lock);
-
        if (pgsql_exec(database, table, ast_str_buffer(sql), &result) != 0) {
                ast_mutex_unlock(&pgsql_lock);
                ast_config_destroy(cfg);
@@ -633,7 +645,6 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char
                        ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
                        ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
                                                PQresultErrorMessage(result), PQresStatus(result_status));
-                       PQclear(result);
                        ast_mutex_unlock(&pgsql_lock);
                        ast_config_destroy(cfg);
                        return NULL;
@@ -651,7 +662,6 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char
                ast_debug(1, "PostgreSQL RealTime: Found %d rows.\n", num_rows);
 
                if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
-                       PQclear(result);
                        ast_mutex_unlock(&pgsql_lock);
                        ast_config_destroy(cfg);
                        return NULL;
@@ -661,8 +671,10 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char
 
                for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
                        var = NULL;
-                       if (!(cat = ast_category_new("","",99999)))
+                       cat = ast_category_new_anonymous();
+                       if (!cat) {
                                continue;
+                       }
                        for (i = 0; i < numFields; i++) {
                                stringp = PQgetvalue(result, rowIndex, i);
                                while (stringp) {
@@ -683,23 +695,28 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char
                ast_debug(1, "PostgreSQL RealTime: Could not find any rows in table %s.\n", table);
        }
 
-       PQclear(result);
        ast_mutex_unlock(&pgsql_lock);
 
        return cfg;
 }
 
 static int update_pgsql(const char *database, const char *tablename, const char *keyfield,
-                                               const char *lookup, va_list ap)
+                                               const char *lookup, const struct ast_variable *fields)
 {
-       PGresult *result = NULL;
+       RAII_VAR(PGresult *, result, NULL, PQclear);
        int numrows = 0, pgresult;
-       const char *newparam, *newval;
+       const struct ast_variable *field = fields;
        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;
 
+       /*
+        * Ignore database from the extconfig.conf since it was
+        * configured by res_pgsql.conf.
+        */
+       database = dbname;
+
        if (!tablename) {
                ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
                return -1;
@@ -710,29 +727,40 @@ static int update_pgsql(const char *database, const char *tablename, const char
                return -1;
        }
 
+       /*
+        * Must connect to the server before anything else as ESCAPE_STRING()
+        * uses pgsqlConn
+        */
+       ast_mutex_lock(&pgsql_lock);
+       if (!pgsql_reconnect(database)) {
+               ast_mutex_unlock(&pgsql_lock);
+               release_table(table);
+               return -1;
+       }
+
        /* Get the first parameter and first value in our list of passed paramater/value pairs */
-       newparam = va_arg(ap, const char *);
-       newval = va_arg(ap, const char *);
-       if (!newparam || !newval) {
+       if (!field) {
                ast_log(LOG_WARNING,
                                "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
                if (pgsqlConn) {
                        PQfinish(pgsqlConn);
                        pgsqlConn = NULL;
                }
+               ast_mutex_unlock(&pgsql_lock);
                release_table(table);
                return -1;
        }
 
        /* Check that the column exists in the table */
        AST_LIST_TRAVERSE(&table->columns, column, list) {
-               if (strcmp(column->name, newparam) == 0) {
+               if (strcmp(column->name, field->name) == 0) {
                        break;
                }
        }
 
        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_log(LOG_ERROR, "PostgreSQL RealTime: Updating on column '%s', but that column does not exist within the table '%s'!\n", field->name, tablename);
+               ast_mutex_unlock(&pgsql_lock);
                release_table(table);
                return -1;
        }
@@ -740,40 +768,37 @@ static int update_pgsql(const char *database, const char *tablename, const char
        /* 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 */
 
-       ESCAPE_STRING(escapebuf, newval);
+       ESCAPE_STRING(escapebuf, field->value);
        if (pgresult) {
-               ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", newval);
-               va_end(ap);
+               ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
+               ast_mutex_unlock(&pgsql_lock);
                release_table(table);
                return -1;
        }
-       ast_str_set(&sql, 0, "UPDATE %s SET %s = '%s'", tablename, newparam, ast_str_buffer(escapebuf));
-
-       while ((newparam = va_arg(ap, const char *))) {
-               newval = va_arg(ap, const char *);
+       ast_str_set(&sql, 0, "UPDATE %s SET %s = '%s'", tablename, field->name, ast_str_buffer(escapebuf));
 
-               if (!find_column(table, newparam)) {
-                       ast_log(LOG_NOTICE, "Attempted to update column '%s' in table '%s', but column does not exist!\n", newparam, tablename);
+       while ((field = field->next)) {
+               if (!find_column(table, field->name)) {
+                       ast_log(LOG_NOTICE, "Attempted to update column '%s' in table '%s', but column does not exist!\n", field->name, tablename);
                        continue;
                }
 
-               ESCAPE_STRING(escapebuf, newval);
+               ESCAPE_STRING(escapebuf, field->value);
                if (pgresult) {
-                       ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", newval);
-                       va_end(ap);
+                       ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
+                       ast_mutex_unlock(&pgsql_lock);
                        release_table(table);
                        return -1;
                }
 
-               ast_str_append(&sql, 0, ", %s = '%s'", newparam, ast_str_buffer(escapebuf));
+               ast_str_append(&sql, 0, ", %s = '%s'", field->name, ast_str_buffer(escapebuf));
        }
-       va_end(ap);
        release_table(table);
 
        ESCAPE_STRING(escapebuf, lookup);
        if (pgresult) {
                ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", lookup);
-               va_end(ap);
+               ast_mutex_unlock(&pgsql_lock);
                return -1;
        }
 
@@ -782,8 +807,6 @@ static int update_pgsql(const char *database, const char *tablename, const char
        ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", ast_str_buffer(sql));
 
        /* We now have our complete statement; Lets connect to the server and execute it. */
-       ast_mutex_lock(&pgsql_lock);
-
        if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
                ast_mutex_unlock(&pgsql_lock);
                return -1;
@@ -797,7 +820,6 @@ static int update_pgsql(const char *database, const char *tablename, const char
                        ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
                        ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
                                                PQresultErrorMessage(result), PQresStatus(result_status));
-                       PQclear(result);
                        ast_mutex_unlock(&pgsql_lock);
                        return -1;
                }
@@ -820,16 +842,22 @@ static int update_pgsql(const char *database, const char *tablename, const char
        return -1;
 }
 
-static int update2_pgsql(const char *database, const char *tablename, va_list ap)
+static int update2_pgsql(const char *database, const char *tablename, const struct ast_variable *lookup_fields, const struct ast_variable *update_fields)
 {
-       PGresult *result = NULL;
+       RAII_VAR(PGresult *, result, NULL, PQclear);
        int numrows = 0, pgresult, first = 1;
        struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 16);
-       const char *newparam, *newval;
+       const struct ast_variable *field;
        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;
 
+       /*
+        * Ignore database from the extconfig.conf since it was
+        * configured by res_pgsql.conf.
+        */
+       database = dbname;
+
        if (!tablename) {
                ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
                return -1;
@@ -845,25 +873,36 @@ static int update2_pgsql(const char *database, const char *tablename, va_list ap
                return -1;
        }
 
-       ast_str_set(&sql, 0, "UPDATE %s SET ", tablename);
-       ast_str_set(&where, 0, "WHERE");
+       /*
+        * Must connect to the server before anything else as ESCAPE_STRING()
+        * uses pgsqlConn
+        */
+       ast_mutex_lock(&pgsql_lock);
+       if (!pgsql_reconnect(database)) {
+               ast_mutex_unlock(&pgsql_lock);
+               release_table(table);
+               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);
+       for (field = lookup_fields; field; field = field->next) {
+               if (!find_column(table, field->name)) {
+                       ast_log(LOG_ERROR, "Attempted to update based on criteria column '%s' (%s@%s), but that column does not exist!\n", field->name, tablename, database);
+                       ast_mutex_unlock(&pgsql_lock);
                        release_table(table);
                        return -1;
                }
 
-               newval = va_arg(ap, const char *);
-               ESCAPE_STRING(escapebuf, newval);
+               ESCAPE_STRING(escapebuf, field->value);
                if (pgresult) {
-                       ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", newval);
+                       ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
+                       ast_mutex_unlock(&pgsql_lock);
                        release_table(table);
-                       ast_free(sql);
                        return -1;
                }
-               ast_str_append(&where, 0, "%s %s='%s'", first ? "" : " AND", newparam, ast_str_buffer(escapebuf));
+               ast_str_append(&where, 0, "%s %s='%s'", first ? "" : " AND", field->name, ast_str_buffer(escapebuf));
                first = 0;
        }
 
@@ -874,38 +913,38 @@ static int update2_pgsql(const char *database, const char *tablename, va_list ap
                        PQfinish(pgsqlConn);
                        pgsqlConn = NULL;
                }
+               ast_mutex_unlock(&pgsql_lock);
                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 *);
-
+       for (field = update_fields; field; field = field->next) {
                /* 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);
+               if (!find_column(table, field->name)) {
+                       ast_log(LOG_NOTICE, "Attempted to update column '%s' in table '%s@%s', but column does not exist!\n", field->name, tablename, database);
                        continue;
                }
 
-               ESCAPE_STRING(escapebuf, newval);
+               ESCAPE_STRING(escapebuf, field->value);
                if (pgresult) {
-                       ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", newval);
+                       ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
+                       ast_mutex_unlock(&pgsql_lock);
                        release_table(table);
-                       ast_free(sql);
                        return -1;
                }
 
-               ast_str_append(&sql, 0, "%s %s='%s'", first ? "" : ",", newparam, ast_str_buffer(escapebuf));
+               ast_str_append(&sql, 0, "%s %s='%s'", first ? "" : ",", field->name, ast_str_buffer(escapebuf));
+               first = 0;
        }
        release_table(table);
 
-       ast_str_append(&sql, 0, " %s", ast_str_buffer(where));
+       ast_str_append(&sql, 0, "%s", ast_str_buffer(where));
 
        ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", ast_str_buffer(sql));
 
-       /* We now have our complete statement; connect to the server and execute it. */
+       /* We now have our complete statement; Lets connect to the server and execute it. */
         if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
                ast_mutex_unlock(&pgsql_lock);
                return -1;
@@ -929,69 +968,75 @@ static int update2_pgsql(const char *database, const char *tablename, va_list ap
        return -1;
 }
 
-static int store_pgsql(const char *database, const char *table, va_list ap)
+static int store_pgsql(const char *database, const char *table, const struct ast_variable *fields)
 {
-       PGresult *result = NULL;
-       Oid insertid;
+       RAII_VAR(PGresult *, result, NULL, PQclear);
+       int numrows;
        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;
+       const struct ast_variable *field = fields;
+
+       /*
+        * Ignore database from the extconfig.conf since it was
+        * configured by res_pgsql.conf.
+        */
+       database = dbname;
 
        if (!table) {
                ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
                return -1;
        }
 
+       /*
+        * Must connect to the server before anything else as ESCAPE_STRING()
+        * uses pgsqlConn
+        */
+       ast_mutex_lock(&pgsql_lock);
+       if (!pgsql_reconnect(database)) {
+               ast_mutex_unlock(&pgsql_lock);
+               return -1;
+       }
+
        /* Get the first parameter and first value in our list of passed paramater/value pairs */
-       newparam = va_arg(ap, const char *);
-       newval = va_arg(ap, const char *);
-       if (!newparam || !newval) {
+       if (!field) {
                ast_log(LOG_WARNING,
                                "PostgreSQL RealTime: Realtime storage requires at least 1 parameter and 1 value to store.\n");
                if (pgsqlConn) {
                        PQfinish(pgsqlConn);
                        pgsqlConn = NULL;
                }
-               return -1;
-       }
-
-       /* Must connect to the server before anything else, as the escape function requires the connection handle.. */
-       ast_mutex_lock(&pgsql_lock);
-       if (!pgsql_reconnect(database)) {
                ast_mutex_unlock(&pgsql_lock);
                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 */
-       ESCAPE_STRING(buf, newparam);
+       ESCAPE_STRING(buf, field->name);
        ast_str_set(&sql1, 0, "INSERT INTO %s (%s", table, ast_str_buffer(buf));
-       ESCAPE_STRING(buf, newval);
+       ESCAPE_STRING(buf, field->value);
        ast_str_set(&sql2, 0, ") VALUES ('%s'", ast_str_buffer(buf));
-       while ((newparam = va_arg(ap, const char *))) {
-               newval = va_arg(ap, const char *);
-               ESCAPE_STRING(buf, newparam);
+       while ((field = field->next)) {
+               ESCAPE_STRING(buf, field->name);
                ast_str_append(&sql1, 0, ", %s", ast_str_buffer(buf));
-               ESCAPE_STRING(buf, newval);
+               ESCAPE_STRING(buf, field->value);
                ast_str_append(&sql2, 0, ", '%s'", ast_str_buffer(buf));
        }
-       va_end(ap);
        ast_str_append(&sql1, 0, "%s)", ast_str_buffer(sql2));
 
        ast_debug(1, "PostgreSQL RealTime: Insert SQL: %s\n", ast_str_buffer(sql1));
 
+       /* We now have our complete statement; Lets connect to the server and execute it. */
         if (pgsql_exec(database, table, ast_str_buffer(sql1), &result) != 0) {
                ast_mutex_unlock(&pgsql_lock);
                return -1;
         }
 
-       insertid = PQoidValue(result);
-       PQclear(result);
+       numrows = atoi(PQcmdTuples(result));
        ast_mutex_unlock(&pgsql_lock);
 
-       ast_debug(1, "PostgreSQL RealTime: row inserted on table: %s, id: %u\n", table, insertid);
+       ast_debug(1, "PostgreSQL RealTime: row inserted on table: %s.\n", table);
 
        /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
         * An integer greater than zero indicates the number of rows affected
@@ -999,68 +1044,74 @@ static int store_pgsql(const char *database, const char *table, va_list ap)
         * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
         */
 
-       if (insertid >= 0)
-               return (int) insertid;
+       if (numrows >= 0) {
+               return numrows;
+       }
 
        return -1;
 }
 
-static int destroy_pgsql(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
+static int destroy_pgsql(const char *database, const char *table, const char *keyfield, const char *lookup, const struct ast_variable *fields)
 {
-       PGresult *result = NULL;
+       RAII_VAR(PGresult *, result, NULL, PQclear);
        int numrows = 0;
        int pgresult;
        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;
+       const struct ast_variable *field;
+
+       /*
+        * Ignore database from the extconfig.conf since it was
+        * configured by res_pgsql.conf.
+        */
+       database = dbname;
 
        if (!table) {
                ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
                return -1;
        }
 
+       /*
+        * Must connect to the server before anything else as ESCAPE_STRING()
+        * uses pgsqlConn
+        */
+       ast_mutex_lock(&pgsql_lock);
+       if (!pgsql_reconnect(database)) {
+               ast_mutex_unlock(&pgsql_lock);
+               return -1;
+       }
+
        /* Get the first parameter and first value in our list of passed paramater/value pairs */
-       /*newparam = va_arg(ap, const char *);
-       newval = va_arg(ap, const char *);
-       if (!newparam || !newval) {*/
        if (ast_strlen_zero(keyfield) || ast_strlen_zero(lookup))  {
                ast_log(LOG_WARNING,
                                "PostgreSQL RealTime: Realtime destroy requires at least 1 parameter and 1 value to search on.\n");
                if (pgsqlConn) {
                        PQfinish(pgsqlConn);
                        pgsqlConn = NULL;
-               };
-               return -1;
-       }
-
-       /* Must connect to the server before anything else, as the escape function requires the connection handle.. */
-       ast_mutex_lock(&pgsql_lock);
-       if (!pgsql_reconnect(database)) {
+               }
                ast_mutex_unlock(&pgsql_lock);
                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 */
 
        ESCAPE_STRING(buf1, keyfield);
        ESCAPE_STRING(buf2, lookup);
        ast_str_set(&sql, 0, "DELETE FROM %s WHERE %s = '%s'", table, ast_str_buffer(buf1), ast_str_buffer(buf2));
-       while ((newparam = va_arg(ap, const char *))) {
-               newval = va_arg(ap, const char *);
-               ESCAPE_STRING(buf1, newparam);
-               ESCAPE_STRING(buf2, newval);
+       for (field = fields; field; field = field->next) {
+               ESCAPE_STRING(buf1, field->name);
+               ESCAPE_STRING(buf2, field->value);
                ast_str_append(&sql, 0, " AND %s = '%s'", ast_str_buffer(buf1), ast_str_buffer(buf2));
        }
-       va_end(ap);
 
        ast_debug(1, "PostgreSQL RealTime: Delete SQL: %s\n", ast_str_buffer(sql));
 
-        if (pgsql_exec(database, table, ast_str_buffer(sql), &result) != 0) {
+       /* We now have our complete statement; Lets connect to the server and execute it. */
+       if (pgsql_exec(database, table, ast_str_buffer(sql), &result) != 0) {
                ast_mutex_unlock(&pgsql_lock);
-               return -1;
-        }
+               return -1;
+       }
 
        numrows = atoi(PQcmdTuples(result));
        ast_mutex_unlock(&pgsql_lock);
@@ -1084,16 +1135,22 @@ static struct ast_config *config_pgsql(const char *database, const char *table,
                                                                           const char *file, struct ast_config *cfg,
                                                                           struct ast_flags flags, const char *suggested_incl, const char *who_asked)
 {
-       PGresult *result = NULL;
+       RAII_VAR(PGresult *, result, NULL, PQclear);
        long num_rows;
        struct ast_variable *new_v;
        struct ast_category *cur_cat = NULL;
        struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
-       char last[80] = "";
+       char last[80];
        int last_cat_metric = 0;
 
        last[0] = '\0';
 
+       /*
+        * Ignore database from the extconfig.conf since it is
+        * configured by res_pgsql.conf.
+        */
+       database = dbname;
+
        if (!file || !strcmp(file, RES_CONFIG_PGSQL_CONF)) {
                ast_log(LOG_WARNING, "PostgreSQL RealTime: Cannot configure myself.\n");
                return NULL;
@@ -1125,7 +1182,6 @@ static struct ast_config *config_pgsql(const char *database, const char *table,
                        char *field_cat_metric = PQgetvalue(result, rowIndex, 3);
                        if (!strcmp(field_var_name, "#include")) {
                                if (!ast_config_internal_load(field_var_val, cfg, flags, "", who_asked)) {
-                                       PQclear(result);
                                        ast_mutex_unlock(&pgsql_lock);
                                        return NULL;
                                }
@@ -1133,10 +1189,11 @@ static struct ast_config *config_pgsql(const char *database, const char *table,
                        }
 
                        if (strcmp(last, field_category) || last_cat_metric != atoi(field_cat_metric)) {
-                               cur_cat = ast_category_new(field_category, "", 99999);
-                               if (!cur_cat)
+                               cur_cat = ast_category_new_dynamic(field_category);
+                               if (!cur_cat) {
                                        break;
-                               strcpy(last, field_category);
+                               }
+                               ast_copy_string(last, field_category, sizeof(last));
                                last_cat_metric = atoi(field_cat_metric);
                                ast_category_append(cfg, cur_cat);
                        }
@@ -1148,7 +1205,6 @@ static struct ast_config *config_pgsql(const char *database, const char *table,
                                "PostgreSQL RealTime: Could not find config '%s' in database.\n", file);
        }
 
-       PQclear(result);
        ast_mutex_unlock(&pgsql_lock);
 
        return cfg;
@@ -1157,10 +1213,17 @@ static struct ast_config *config_pgsql(const char *database, const char *table,
 static int require_pgsql(const char *database, const char *tablename, va_list ap)
 {
        struct columns *column;
-       struct tables *table = find_table(database, tablename);
+       struct tables *table;
        char *elm;
        int type, size, res = 0;
 
+       /*
+        * Ignore database from the extconfig.conf since it was
+        * configured by res_pgsql.conf.
+        */
+       database = dbname;
+
+       table = find_table(database, tablename);
        if (!table) {
                ast_log(LOG_WARNING, "Table %s not found in database.  This table should exist if you're using realtime.\n", tablename);
                return -1;
@@ -1222,6 +1285,7 @@ static int require_pgsql(const char *database, const char *tablename, va_list ap
                if (!column) {
                        if (requirements == RQ_WARN) {
                                ast_log(LOG_WARNING, "Table %s requires a column '%s' of size '%d', but no such column exists.\n", tablename, elm, size);
+                               res = -1;
                        } else {
                                struct ast_str *sql = ast_str_create(100);
                                char fieldtype[15];
@@ -1231,7 +1295,7 @@ static int require_pgsql(const char *database, const char *tablename, va_list ap
                                        /* Size is minimum length; make it at least 50% greater,
                                         * just to be sure, because PostgreSQL doesn't support
                                         * resizing columns. */
-                                       snprintf(fieldtype, sizeof(fieldtype), "CHAR(%d)",
+                                       snprintf(fieldtype, sizeof(fieldtype), "CHAR(%hhu)",
                                                size < 15 ? size * 2 :
                                                (size * 3 / 2 > 255) ? 255 : size * 3 / 2);
                                } else if (type == RQ_INTEGER1 || type == RQ_UINTEGER1 || type == RQ_INTEGER2) {
@@ -1262,6 +1326,7 @@ static int require_pgsql(const char *database, const char *tablename, va_list ap
 
                                if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
                                        ast_mutex_unlock(&pgsql_lock);
+                                       release_table(table);
                                        return -1;
                                }
 
@@ -1283,6 +1348,13 @@ static int require_pgsql(const char *database, const char *tablename, va_list ap
 static int unload_pgsql(const char *database, const char *tablename)
 {
        struct tables *cur;
+
+       /*
+        * Ignore database from the extconfig.conf since it was
+        * configured by res_pgsql.conf.
+        */
+       database = dbname;
+
        ast_debug(2, "About to lock table cache list\n");
        AST_LIST_LOCK(&psql_tables);
        ast_debug(2, "About to traverse table cache list\n");
@@ -1321,7 +1393,7 @@ static int load_module(void)
                return AST_MODULE_LOAD_DECLINE;
 
        ast_config_engine_register(&pgsql_engine);
-       ast_verb(1, "PostgreSQL RealTime driver loaded.\n");
+
        ast_cli_register_multiple(cli_realtime, ARRAY_LEN(cli_realtime));
 
        return 0;
@@ -1339,7 +1411,9 @@ static int unload_module(void)
        }
        ast_cli_unregister_multiple(cli_realtime, ARRAY_LEN(cli_realtime));
        ast_config_engine_deregister(&pgsql_engine);
-       ast_verb(1, "PostgreSQL RealTime unloaded.\n");
+
+       /* Unlock so something else can destroy the lock. */
+       ast_mutex_unlock(&pgsql_lock);
 
        /* Destroy cached table info */
        AST_LIST_LOCK(&psql_tables);
@@ -1348,9 +1422,6 @@ static int unload_module(void)
        }
        AST_LIST_UNLOCK(&psql_tables);
 
-       /* Unlock so something else can destroy the lock. */
-       ast_mutex_unlock(&pgsql_lock);
-
        return 0;
 }
 
@@ -1369,6 +1440,9 @@ static int parse_config(int is_reload)
 
        config = ast_config_load(RES_CONFIG_PGSQL_CONF, config_flags);
        if (config == CONFIG_STATUS_FILEUNCHANGED) {
+               if (is_reload && pgsqlConn && PQstatus(pgsqlConn) != CONNECTION_OK) {
+                       ast_log(LOG_WARNING,  "PostgreSQL RealTime: Not connected\n");
+               }
                return 0;
        }
 
@@ -1379,6 +1453,7 @@ static int parse_config(int is_reload)
 
        ast_mutex_lock(&pgsql_lock);
 
+       /* XXX: Why would we do this before we're ready to establish a new connection? */
        if (pgsqlConn) {
                PQfinish(pgsqlConn);
                pgsqlConn = NULL;
@@ -1424,6 +1499,12 @@ static int parse_config(int is_reload)
                dbport = atoi(s);
        }
 
+       if (!(s = ast_variable_retrieve(config, "general", "dbappname"))) {
+               dbappname[0] = '\0';
+       } else {
+               ast_copy_string(dbappname, s, sizeof(dbappname));
+       }
+
        if (!ast_strlen_zero(dbhost)) {
                /* No socket needed */
        } else if (!(s = ast_variable_retrieve(config, "general", "dbsock"))) {
@@ -1480,25 +1561,39 @@ static int pgsql_reconnect(const char *database)
 
        /* mutex lock should have been locked before calling this function. */
 
-       if (pgsqlConn && PQstatus(pgsqlConn) != CONNECTION_OK) {
+       if (pgsqlConn) {
+               if (PQstatus(pgsqlConn) == CONNECTION_OK) {
+                       /* We're good? */
+                       return 1;
+               }
+
                PQfinish(pgsqlConn);
                pgsqlConn = NULL;
        }
 
        /* DB password can legitimately be 0-length */
-       if ((!pgsqlConn) && (!ast_strlen_zero(dbhost) || !ast_strlen_zero(dbsock)) && !ast_strlen_zero(dbuser) && !ast_strlen_zero(my_database)) {
-               struct ast_str *connInfo = ast_str_create(32);
+       if ((!ast_strlen_zero(dbhost) || !ast_strlen_zero(dbsock)) && !ast_strlen_zero(dbuser) && !ast_strlen_zero(my_database)) {
+               struct ast_str *conn_info = ast_str_create(128);
 
-               ast_str_set(&connInfo, 0, "host=%s port=%d dbname=%s user=%s",
+               if (!conn_info) {
+                       ast_log(LOG_ERROR, "PostgreSQL RealTime: Failed to allocate memory for connection string.\n");
+                       return 0;
+               }
+
+               ast_str_set(&conn_info, 0, "host=%s port=%d dbname=%s user=%s",
                        S_OR(dbhost, dbsock), dbport, my_database, dbuser);
-               if (!ast_strlen_zero(dbpass))
-                       ast_str_append(&connInfo, 0, " password=%s", dbpass);
 
-               ast_debug(1, "%u connInfo=%s\n", (unsigned int)ast_str_size(connInfo), ast_str_buffer(connInfo));
-               pgsqlConn = PQconnectdb(ast_str_buffer(connInfo));
-               ast_debug(1, "%u connInfo=%s\n", (unsigned int)ast_str_size(connInfo), ast_str_buffer(connInfo));
-               ast_free(connInfo);
-               connInfo = NULL;
+               if (!ast_strlen_zero(dbappname)) {
+                       ast_str_append(&conn_info, 0, " application_name=%s", dbappname);
+               }
+
+               if (!ast_strlen_zero(dbpass)) {
+                       ast_str_append(&conn_info, 0, " password=%s", dbpass);
+               }
+
+               pgsqlConn = PQconnectdb(ast_str_buffer(conn_info));
+               ast_free(conn_info);
+               conn_info = NULL;
 
                ast_debug(1, "pgsqlConn=%p\n", pgsqlConn);
                if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
@@ -1509,7 +1604,7 @@ static int pgsql_reconnect(const char *database)
                } else {
                        ast_log(LOG_ERROR,
                                        "PostgreSQL RealTime: Failed to connect database %s on %s: %s\n",
-                                       dbname, dbhost, PQresultErrorMessage(NULL));
+                                       my_database, dbhost, PQresultErrorMessage(NULL));
                        return 0;
                }
        } else {
@@ -1574,8 +1669,10 @@ static char *handle_cli_realtime_pgsql_cache(struct ast_cli_entry *e, int cmd, s
 
 static char *handle_cli_realtime_pgsql_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-       char status[256], credentials[100] = "";
-       int ctimesec = time(NULL) - connect_time;
+       char connection_info[256];
+       char credentials[100] = "";
+       char buf[376]; /* 256+100+"Connected to "+" for "+NULL */
+       int is_connected = 0, ctimesec = time(NULL) - connect_time;
 
        switch (cmd) {
        case CLI_INIT:
@@ -1591,44 +1688,35 @@ static char *handle_cli_realtime_pgsql_status(struct ast_cli_entry *e, int cmd,
        if (a->argc != 4)
                return CLI_SHOWUSAGE;
 
-       if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
-               if (!ast_strlen_zero(dbhost))
-                       snprintf(status, sizeof(status), "Connected to %s@%s, port %d", dbname, dbhost, dbport);
-               else if (!ast_strlen_zero(dbsock))
-                       snprintf(status, sizeof(status), "Connected to %s on socket file %s", dbname, dbsock);
-               else
-                       snprintf(status, sizeof(status), "Connected to %s@%s", dbname, dbhost);
-
-               if (!ast_strlen_zero(dbuser))
-                       snprintf(credentials, sizeof(credentials), " with username %s", dbuser);
-
-               if (ctimesec > 31536000)
-                       ast_cli(a->fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n",
-                                       status, credentials, ctimesec / 31536000, (ctimesec % 31536000) / 86400,
-                                       (ctimesec % 86400) / 3600, (ctimesec % 3600) / 60, ctimesec % 60);
-               else if (ctimesec > 86400)
-                       ast_cli(a->fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status,
-                                       credentials, ctimesec / 86400, (ctimesec % 86400) / 3600, (ctimesec % 3600) / 60,
-                                       ctimesec % 60);
-               else if (ctimesec > 3600)
-                       ast_cli(a->fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, credentials,
-                                       ctimesec / 3600, (ctimesec % 3600) / 60, ctimesec % 60);
-               else if (ctimesec > 60)
-                       ast_cli(a->fd, "%s%s for %d minutes, %d seconds.\n", status, credentials, ctimesec / 60,
-                                       ctimesec % 60);
-               else
-                       ast_cli(a->fd, "%s%s for %d seconds.\n", status, credentials, ctimesec);
+       if (!ast_strlen_zero(dbhost))
+               snprintf(connection_info, sizeof(connection_info), "%s@%s, port %d", dbname, dbhost, dbport);
+       else if (!ast_strlen_zero(dbsock))
+               snprintf(connection_info, sizeof(connection_info), "%s on socket file %s", dbname, dbsock);
+       else
+               snprintf(connection_info, sizeof(connection_info), "%s@%s", dbname, dbhost);
+
+       if (!ast_strlen_zero(dbuser))
+               snprintf(credentials, sizeof(credentials), " with username %s", dbuser);
+
+       ast_mutex_lock(&pgsql_lock);
+       is_connected = (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK);
+       ast_mutex_unlock(&pgsql_lock);
 
+       if (is_connected) {
+               snprintf(buf, sizeof(buf), "Connected to %s%s for ", connection_info, credentials);
+               ast_cli_print_timestr_fromseconds(a->fd, ctimesec, buf);
                return CLI_SUCCESS;
        } else {
+               ast_cli(a->fd, "Unable to connect %s%s\n", connection_info, credentials);
                return CLI_FAILURE;
        }
 }
 
 /* needs usecount semantics defined */
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PostgreSQL RealTime Configuration Driver",
-               .load = load_module,
-               .unload = unload_module,
-               .reload = reload,
-               .load_pri = AST_MODPRI_REALTIME_DRIVER,
-              );
+       .support_level = AST_MODULE_SUPPORT_EXTENDED,
+       .load = load_module,
+       .unload = unload_module,
+       .reload = reload,
+       .load_pri = AST_MODPRI_REALTIME_DRIVER,
+);