Index: res/res_config_pgsql.c
===================================================================
--- res/res_config_pgsql.c	(revision 78145)
+++ res/res_config_pgsql.c	(working copy)
@@ -429,6 +429,191 @@
 	return -1;
 }
 
+static int store_pgsql(const char *database, const char *table, va_list ap)
+{
+	PGresult *result = NULL;
+	Oid insertid;
+	char sql[256];
+	char params[256];
+	char vals[256];
+	char buf[256];
+	int pgresult;
+	const char *newparam, *newval;
+
+	if (!table) {
+		ast_log(LOG_WARNING, "Postgresql RealTime: No table specified.\n");
+		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) {
+		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 */
+	PQescapeStringConn(pgsqlConn, buf, newparam, sizeof(newparam), &pgresult);
+	snprintf(params, sizeof(params), "%s", buf);
+	PQescapeStringConn(pgsqlConn, buf, newval, sizeof(newval), &pgresult);
+	snprintf(vals, sizeof(vals), "'%s'", buf);
+	while ((newparam = va_arg(ap, const char *))) {
+		newval = va_arg(ap, const char *);
+		PQescapeStringConn(pgsqlConn, buf, newparam, sizeof(newparam), &pgresult);
+		snprintf(params + strlen(params), sizeof(params) - strlen(params), ", %s", buf);
+		PQescapeStringConn(pgsqlConn, buf, newval, sizeof(newval), &pgresult);
+		snprintf(vals + strlen(vals), sizeof(vals) - strlen(vals), ", '%s'", buf);
+	}
+	va_end(ap);
+	snprintf(sql, sizeof(sql), "INSERT INTO (%s) VALUES (%s)", params, vals);
+
+	ast_debug(1, "Postgresql RealTime: Insert SQL: %s\n", sql);
+
+	if (!(result = PQexec(pgsqlConn, sql))) {
+		ast_log(LOG_WARNING,
+				"Postgresql RealTime: Failed to query database. Check debug for more info.\n");
+		ast_debug(1, "Postgresql RealTime: Query: %s\n", sql);
+		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);
+			ast_debug(1, "Postgresql RealTime: Query Failed because: %s (%s)\n",
+						PQresultErrorMessage(result), PQresStatus(result_status));
+			ast_mutex_unlock(&pgsql_lock);
+			return -1;
+		}
+	}
+
+	insertid = PQoidValue(result);
+	ast_mutex_unlock(&pgsql_lock);
+
+	ast_debug(1, "Postgresql RealTime: row inserted on table: %s, id: %u\n", table, insertid);
+
+	/* 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 (insertid >= 0)
+		return (int) insertid;
+
+	return -1;
+}
+
+static int destroy_pgsql(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
+{
+	PGresult *result = NULL;
+	int numrows = 0;
+	int pgresult;
+	char sql[256];
+	char buf[256], buf2[256];
+	const char *newparam, *newval;
+
+	if (!table) {
+		ast_log(LOG_WARNING, "Postgresql RealTime: No table specified.\n");
+		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 */
+
+	PQescapeStringConn(pgsqlConn, buf, keyfield, sizeof(keyfield), &pgresult);
+	PQescapeStringConn(pgsqlConn, buf2, lookup, sizeof(lookup), &pgresult);
+	snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE %s = '%s'", table, buf, buf2);
+	while ((newparam = va_arg(ap, const char *))) {
+		newval = va_arg(ap, const char *);
+		PQescapeStringConn(pgsqlConn, buf, newparam, sizeof(newparam), &pgresult);
+		PQescapeStringConn(pgsqlConn, buf2, newval, sizeof(newval), &pgresult);
+		snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s = '%s'", buf, buf2);
+	}
+	va_end(ap);
+
+	ast_debug(1, "Postgresql RealTime: Delete SQL: %s\n", sql);
+
+	if (!(result = PQexec(pgsqlConn, sql))) {
+		ast_log(LOG_WARNING,
+				"Postgresql RealTime: Failed to query database. Check debug for more info.\n");
+		ast_debug(1, "Postgresql RealTime: Query: %s\n", sql);
+		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);
+			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: Deleted %d rows on table: %s\n", numrows, table);
+
+	/* 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 struct ast_config *config_pgsql(const char *database, const char *table,
 					   const char *file, struct ast_config *cfg,
 					   int withcomments)
@@ -531,6 +716,8 @@
 	.load_func = config_pgsql,
 	.realtime_func = realtime_pgsql,
 	.realtime_multi_func = realtime_multi_pgsql,
+	.store_func = store_pgsql,
+	.destroy_func = destroy_pgsql,
 	.update_func = update_pgsql
 };
 
