Sat Mar 10 01:55:40 2012

Asterisk developer's documentation


res_config_pgsql.c File Reference

PostgreSQL plugin for Asterisk RealTime Architecture. More...

#include "asterisk.h"
#include <libpq-fe.h>
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/config.h"
#include "asterisk/module.h"
#include "asterisk/lock.h"
#include "asterisk/utils.h"
#include "asterisk/cli.h"

Go to the source code of this file.

Data Structures

struct  columns
struct  psql_tables
struct  tables
struct  tables::psql_columns

Defines

#define ESCAPE_STRING(buffer, stringname)
#define has_schema_support   (version > 70300 ? 1 : 0)
#define MAX_DB_OPTION_SIZE   64
#define release_table(table)   ast_rwlock_unlock(&(table)->lock);
#define RES_CONFIG_PGSQL_CONF   "res_pgsql.conf"

Enumerations

enum  { RQ_WARN, RQ_CREATECLOSE, RQ_CREATECHAR }

Functions

static void __init_escapebuf_buf (void)
static void __init_findtable_buf (void)
static void __init_semibuf_buf (void)
static void __init_sql_buf (void)
static void __init_where_buf (void)
static void __reg_module (void)
static void __unreg_module (void)
static struct ast_configconfig_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)
static int destroy_pgsql (const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
static void destroy_table (struct tables *table)
static struct columnsfind_column (struct tables *t, const char *colname)
static struct tablesfind_table (const char *database, const char *orig_tablename)
static char * handle_cli_realtime_pgsql_cache (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_cli_realtime_pgsql_status (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int load_module (void)
static int parse_config (int reload)
static int pgsql_reconnect (const char *database)
static struct ast_configrealtime_multi_pgsql (const char *database, const char *table, va_list ap)
static struct ast_variablerealtime_pgsql (const char *database, const char *tablename, va_list ap)
static int reload (void)
static int require_pgsql (const char *database, const char *tablename, va_list ap)
static int store_pgsql (const char *database, const char *table, va_list ap)
static int unload_module (void)
static int unload_pgsql (const char *database, const char *tablename)
static int update2_pgsql (const char *database, const char *tablename, va_list ap)
static int update_pgsql (const char *database, const char *tablename, const char *keyfield, const char *lookup, va_list ap)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "PostgreSQL RealTime Configuration Driver" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "88eaa8f5c1bd988bedd71113385e0886" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_REALTIME_DRIVER, }
static struct ast_module_infoast_module_info = &__mod_info
static struct ast_cli_entry cli_realtime []
static time_t connect_time = 0
static char dbhost [MAX_DB_OPTION_SIZE] = ""
static char dbname [MAX_DB_OPTION_SIZE] = ""
static char dbpass [MAX_DB_OPTION_SIZE] = ""
static int dbport = 5432
static char dbsock [MAX_DB_OPTION_SIZE] = ""
static char dbuser [MAX_DB_OPTION_SIZE] = ""
static struct ast_threadstorage escapebuf_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_escapebuf_buf , .custom_init = NULL , }
static struct ast_threadstorage findtable_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_findtable_buf , .custom_init = NULL , }
static struct ast_config_engine pgsql_engine
static ast_mutex_t pgsql_lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 }
static PGconn * pgsqlConn = NULL
static enum { ... }  requirements
static struct ast_threadstorage semibuf_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_semibuf_buf , .custom_init = NULL , }
static struct ast_threadstorage sql_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_sql_buf , .custom_init = NULL , }
static int version
static struct ast_threadstorage where_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_where_buf , .custom_init = NULL , }


Detailed Description

PostgreSQL plugin for Asterisk RealTime Architecture.

Author:
Mark Spencer <markster@digium.com>

Manuel Guesdon <mguesdon@oxymium.net> - PostgreSQL RealTime Driver Author/Adaptor

ExtRef:
PostgreSQL http://www.postgresql.org

Definition in file res_config_pgsql.c.


Define Documentation

#define ESCAPE_STRING ( buffer,
stringname   ) 

Definition at line 98 of file res_config_pgsql.c.

Referenced by destroy_pgsql(), realtime_multi_pgsql(), realtime_pgsql(), store_pgsql(), update2_pgsql(), and update_pgsql().

#define has_schema_support   (version > 70300 ? 1 : 0)

Definition at line 56 of file res_config_pgsql.c.

Referenced by find_table().

#define MAX_DB_OPTION_SIZE   64

Definition at line 58 of file res_config_pgsql.c.

#define release_table ( table   )     ast_rwlock_unlock(&(table)->lock);

Definition at line 293 of file res_config_pgsql.c.

Referenced by cdr_handler(), handle_cli_realtime_pgsql_cache(), require_pgsql(), update2_pgsql(), and update_pgsql().

#define RES_CONFIG_PGSQL_CONF   "res_pgsql.conf"

Definition at line 52 of file res_config_pgsql.c.

Referenced by config_pgsql(), and parse_config().


Enumeration Type Documentation

anonymous enum

Enumerator:
RQ_WARN 
RQ_CREATECLOSE 
RQ_CREATECHAR 

Definition at line 91 of file res_config_pgsql.c.


Function Documentation

static void __init_escapebuf_buf ( void   )  [static]

Definition at line 49 of file res_config_pgsql.c.

00056 : 0)

static void __init_findtable_buf ( void   )  [static]

Definition at line 47 of file res_config_pgsql.c.

00056 : 0)

static void __init_semibuf_buf ( void   )  [static]

Definition at line 50 of file res_config_pgsql.c.

00056 : 0)

static void __init_sql_buf ( void   )  [static]

Definition at line 46 of file res_config_pgsql.c.

00056 : 0)

static void __init_where_buf ( void   )  [static]

Definition at line 48 of file res_config_pgsql.c.

00056 : 0)

static void __reg_module ( void   )  [static]

Definition at line 1639 of file res_config_pgsql.c.

static void __unreg_module ( void   )  [static]

Definition at line 1639 of file res_config_pgsql.c.

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 
) [static]

Definition at line 1065 of file res_config_pgsql.c.

References ast_category_append(), ast_category_new(), ast_config_internal_load(), ast_debug, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_str_buffer(), ast_str_set(), ast_str_thread_get(), ast_variable_append(), ast_variable_new(), last, LOG_WARNING, pgsql_lock, pgsql_reconnect(), RES_CONFIG_PGSQL_CONF, and sql_buf.

01068 {
01069    PGresult *result = NULL;
01070    long num_rows;
01071    struct ast_variable *new_v;
01072    struct ast_category *cur_cat = NULL;
01073    struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
01074    char last[80] = "";
01075    int last_cat_metric = 0;
01076 
01077    last[0] = '\0';
01078 
01079    if (!file || !strcmp(file, RES_CONFIG_PGSQL_CONF)) {
01080       ast_log(LOG_WARNING, "PostgreSQL RealTime: Cannot configure myself.\n");
01081       return NULL;
01082    }
01083 
01084    ast_str_set(&sql, 0, "SELECT category, var_name, var_val, cat_metric FROM %s "
01085          "WHERE filename='%s' and commented=0"
01086          "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ", table, file);
01087 
01088    ast_debug(1, "PostgreSQL RealTime: Static SQL: %s\n", ast_str_buffer(sql));
01089 
01090    /* We now have our complete statement; Lets connect to the server and execute it. */
01091    ast_mutex_lock(&pgsql_lock);
01092    if (!pgsql_reconnect(database)) {
01093       ast_mutex_unlock(&pgsql_lock);
01094       return NULL;
01095    }
01096 
01097    if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
01098       ast_log(LOG_WARNING,
01099             "PostgreSQL RealTime: Failed to query '%s@%s'. Check debug for more info.\n", table, database);
01100       ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
01101       ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
01102       ast_mutex_unlock(&pgsql_lock);
01103       return NULL;
01104    } else {
01105       ExecStatusType result_status = PQresultStatus(result);
01106       if (result_status != PGRES_COMMAND_OK
01107          && result_status != PGRES_TUPLES_OK
01108          && result_status != PGRES_NONFATAL_ERROR) {
01109          ast_log(LOG_WARNING,
01110                "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
01111          ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
01112          ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
01113                   PQresultErrorMessage(result), PQresStatus(result_status));
01114          ast_mutex_unlock(&pgsql_lock);
01115          return NULL;
01116       }
01117    }
01118 
01119    if ((num_rows = PQntuples(result)) > 0) {
01120       int rowIndex = 0;
01121 
01122       ast_debug(1, "PostgreSQL RealTime: Found %ld rows.\n", num_rows);
01123 
01124       for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
01125          char *field_category = PQgetvalue(result, rowIndex, 0);
01126          char *field_var_name = PQgetvalue(result, rowIndex, 1);
01127          char *field_var_val = PQgetvalue(result, rowIndex, 2);
01128          char *field_cat_metric = PQgetvalue(result, rowIndex, 3);
01129          if (!strcmp(field_var_name, "#include")) {
01130             if (!ast_config_internal_load(field_var_val, cfg, flags, "", who_asked)) {
01131                PQclear(result);
01132                ast_mutex_unlock(&pgsql_lock);
01133                return NULL;
01134             }
01135             continue;
01136          }
01137 
01138          if (strcmp(last, field_category) || last_cat_metric != atoi(field_cat_metric)) {
01139             cur_cat = ast_category_new(field_category, "", 99999);
01140             if (!cur_cat)
01141                break;
01142             strcpy(last, field_category);
01143             last_cat_metric = atoi(field_cat_metric);
01144             ast_category_append(cfg, cur_cat);
01145          }
01146          new_v = ast_variable_new(field_var_name, field_var_val, "");
01147          ast_variable_append(cur_cat, new_v);
01148       }
01149    } else {
01150       ast_log(LOG_WARNING,
01151             "PostgreSQL RealTime: Could not find config '%s' in database.\n", file);
01152    }
01153 
01154    PQclear(result);
01155    ast_mutex_unlock(&pgsql_lock);
01156 
01157    return cfg;
01158 }

static int destroy_pgsql ( const char *  database,
const char *  table,
const char *  keyfield,
const char *  lookup,
va_list  ap 
) [static]

Definition at line 973 of file res_config_pgsql.c.

References ast_debug, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_thread_get(), ast_strlen_zero(), buf1, buf2, ESCAPE_STRING, escapebuf_buf, LOG_WARNING, pgsql_lock, pgsql_reconnect(), sql_buf, and where_buf.

00974 {
00975    PGresult *result = NULL;
00976    int numrows = 0;
00977    int pgresult;
00978    struct ast_str *sql = ast_str_thread_get(&sql_buf, 256);
00979    struct ast_str *buf1 = ast_str_thread_get(&where_buf, 60), *buf2 = ast_str_thread_get(&escapebuf_buf, 60);
00980    const char *newparam, *newval;
00981 
00982    if (!table) {
00983       ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00984       return -1;
00985    }
00986 
00987    /* Get the first parameter and first value in our list of passed paramater/value pairs */
00988    /*newparam = va_arg(ap, const char *);
00989    newval = va_arg(ap, const char *);
00990    if (!newparam || !newval) {*/
00991    if (ast_strlen_zero(keyfield) || ast_strlen_zero(lookup))  {
00992       ast_log(LOG_WARNING,
00993             "PostgreSQL RealTime: Realtime destroy requires at least 1 parameter and 1 value to search on.\n");
00994       if (pgsqlConn) {
00995          PQfinish(pgsqlConn);
00996          pgsqlConn = NULL;
00997       };
00998       return -1;
00999    }
01000 
01001    /* Must connect to the server before anything else, as the escape function requires the connection handle.. */
01002    ast_mutex_lock(&pgsql_lock);
01003    if (!pgsql_reconnect(database)) {
01004       ast_mutex_unlock(&pgsql_lock);
01005       return -1;
01006    }
01007 
01008 
01009    /* Create the first part of the query using the first parameter/value pairs we just extracted
01010       If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
01011 
01012    ESCAPE_STRING(buf1, keyfield);
01013    ESCAPE_STRING(buf2, lookup);
01014    ast_str_set(&sql, 0, "DELETE FROM %s WHERE %s = '%s'", table, ast_str_buffer(buf1), ast_str_buffer(buf2));
01015    while ((newparam = va_arg(ap, const char *))) {
01016       newval = va_arg(ap, const char *);
01017       ESCAPE_STRING(buf1, newparam);
01018       ESCAPE_STRING(buf2, newval);
01019       ast_str_append(&sql, 0, " AND %s = '%s'", ast_str_buffer(buf1), ast_str_buffer(buf2));
01020    }
01021    va_end(ap);
01022 
01023    ast_debug(1, "PostgreSQL RealTime: Delete SQL: %s\n", ast_str_buffer(sql));
01024 
01025    if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
01026       ast_log(LOG_WARNING,
01027             "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
01028       ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
01029       ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
01030       ast_mutex_unlock(&pgsql_lock);
01031       return -1;
01032    } else {
01033       ExecStatusType result_status = PQresultStatus(result);
01034       if (result_status != PGRES_COMMAND_OK
01035          && result_status != PGRES_TUPLES_OK
01036          && result_status != PGRES_NONFATAL_ERROR) {
01037          ast_log(LOG_WARNING,
01038                "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
01039          ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
01040          ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
01041                   PQresultErrorMessage(result), PQresStatus(result_status));
01042          ast_mutex_unlock(&pgsql_lock);
01043          return -1;
01044       }
01045    }
01046 
01047    numrows = atoi(PQcmdTuples(result));
01048    ast_mutex_unlock(&pgsql_lock);
01049 
01050    ast_debug(1, "PostgreSQL RealTime: Deleted %d rows on table: %s\n", numrows, table);
01051 
01052    /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
01053     * An integer greater than zero indicates the number of rows affected
01054     * Zero indicates that no records were updated
01055     * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
01056     */
01057 
01058    if (numrows >= 0)
01059       return (int) numrows;
01060 
01061    return -1;
01062 }

static void destroy_table ( struct tables table  )  [static]

Definition at line 117 of file res_config_pgsql.c.

References ast_free, AST_LIST_REMOVE_HEAD, ast_rwlock_destroy, ast_rwlock_unlock, ast_rwlock_wrlock, columns::list, and table.

Referenced by find_table(), unload_module(), and unload_pgsql().

00118 {
00119    struct columns *column;
00120    ast_rwlock_wrlock(&table->lock);
00121    while ((column = AST_LIST_REMOVE_HEAD(&table->columns, list))) {
00122       ast_free(column);
00123    }
00124    ast_rwlock_unlock(&table->lock);
00125    ast_rwlock_destroy(&table->lock);
00126    ast_free(table);
00127 }

static struct columns* find_column ( struct tables t,
const char *  colname 
) [static]

Definition at line 295 of file res_config_pgsql.c.

References AST_LIST_TRAVERSE, tables::columns, columns::list, and columns::name.

Referenced by update2_pgsql(), and update_pgsql().

00296 {
00297    struct columns *column;
00298 
00299    /* Check that the column exists in the table */
00300    AST_LIST_TRAVERSE(&t->columns, column, list) {
00301       if (strcmp(column->name, colname) == 0) {
00302          return column;
00303       }
00304    }
00305    return NULL;
00306 }

static struct tables* find_table ( const char *  database,
const char *  orig_tablename 
) [static]

Definition at line 129 of file res_config_pgsql.c.

References ast_calloc, ast_debug, AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_rwlock_init, ast_rwlock_rdlock, ast_str_buffer(), ast_str_set(), ast_str_thread_get(), ast_strdupa, ast_strlen_zero(), ast_verb, destroy_table(), findtable_buf, has_schema_support, columns::list, LOG_ERROR, pgsql_lock, pgsql_reconnect(), and table.

Referenced by cdr_handler(), handle_cli_realtime_pgsql_cache(), realtime_require_handler(), require_pgsql(), update2_pgsql(), and update_pgsql().

00130 {
00131    struct columns *column;
00132    struct tables *table;
00133    struct ast_str *sql = ast_str_thread_get(&findtable_buf, 330);
00134    char *pgerror;
00135    PGresult *result;
00136    char *fname, *ftype, *flen, *fnotnull, *fdef;
00137    int i, rows;
00138 
00139    AST_LIST_LOCK(&psql_tables);
00140    AST_LIST_TRAVERSE(&psql_tables, table, list) {
00141       if (!strcasecmp(table->name, orig_tablename)) {
00142          ast_debug(1, "Found table in cache; now locking\n");
00143          ast_rwlock_rdlock(&table->lock);
00144          ast_debug(1, "Lock cached table; now returning\n");
00145          AST_LIST_UNLOCK(&psql_tables);
00146          return table;
00147       }
00148    }
00149 
00150    ast_debug(1, "Table '%s' not found in cache, querying now\n", orig_tablename);
00151 
00152    /* Not found, scan the table */
00153    if (has_schema_support) {
00154       char *schemaname, *tablename;
00155       if (strchr(orig_tablename, '.')) {
00156          schemaname = ast_strdupa(orig_tablename);
00157          tablename = strchr(schemaname, '.');
00158          *tablename++ = '\0';
00159       } else {
00160          schemaname = "";
00161          tablename = ast_strdupa(orig_tablename);
00162       }
00163 
00164       /* Escape special characters in schemaname */
00165       if (strchr(schemaname, '\\') || strchr(schemaname, '\'')) {
00166          char *tmp = schemaname, *ptr;
00167 
00168          ptr = schemaname = alloca(strlen(tmp) * 2 + 1);
00169          for (; *tmp; tmp++) {
00170             if (strchr("\\'", *tmp)) {
00171                *ptr++ = *tmp;
00172             }
00173             *ptr++ = *tmp;
00174          }
00175          *ptr = '\0';
00176       }
00177       /* Escape special characters in tablename */
00178       if (strchr(tablename, '\\') || strchr(tablename, '\'')) {
00179          char *tmp = tablename, *ptr;
00180 
00181          ptr = tablename = alloca(strlen(tmp) * 2 + 1);
00182          for (; *tmp; tmp++) {
00183             if (strchr("\\'", *tmp)) {
00184                *ptr++ = *tmp;
00185             }
00186             *ptr++ = *tmp;
00187          }
00188          *ptr = '\0';
00189       }
00190 
00191       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",
00192          tablename,
00193          ast_strlen_zero(schemaname) ? "" : "'", ast_strlen_zero(schemaname) ? "current_schema()" : schemaname, ast_strlen_zero(schemaname) ? "" : "'");
00194    } else {
00195       /* Escape special characters in tablename */
00196       if (strchr(orig_tablename, '\\') || strchr(orig_tablename, '\'')) {
00197          const char *tmp = orig_tablename;
00198          char *ptr;
00199 
00200          orig_tablename = ptr = alloca(strlen(tmp) * 2 + 1);
00201          for (; *tmp; tmp++) {
00202             if (strchr("\\'", *tmp)) {
00203                *ptr++ = *tmp;
00204             }
00205             *ptr++ = *tmp;
00206          }
00207          *ptr = '\0';
00208       }
00209 
00210       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);
00211    }
00212 
00213    ast_mutex_lock(&pgsql_lock);
00214    if (!pgsql_reconnect(database)) {
00215       AST_LIST_UNLOCK(&psql_tables);
00216       ast_mutex_unlock(&pgsql_lock);
00217       return NULL;
00218    }
00219 
00220    result = PQexec(pgsqlConn, ast_str_buffer(sql));
00221    ast_debug(1, "Query of table structure complete.  Now retrieving results.\n");
00222    if (PQresultStatus(result) != PGRES_TUPLES_OK) {
00223       pgerror = PQresultErrorMessage(result);
00224       ast_log(LOG_ERROR, "Failed to query database columns: %s\n", pgerror);
00225       PQclear(result);
00226       AST_LIST_UNLOCK(&psql_tables);
00227       ast_mutex_unlock(&pgsql_lock);
00228       return NULL;
00229    }
00230 
00231    if (!(table = ast_calloc(1, sizeof(*table) + strlen(orig_tablename) + 1))) {
00232       ast_log(LOG_ERROR, "Unable to allocate memory for new table structure\n");
00233       PQclear(result);
00234       AST_LIST_UNLOCK(&psql_tables);
00235       ast_mutex_unlock(&pgsql_lock);
00236       return NULL;
00237    }
00238    strcpy(table->name, orig_tablename); /* SAFE */
00239    ast_rwlock_init(&table->lock);
00240    AST_LIST_HEAD_INIT_NOLOCK(&table->columns);
00241 
00242    rows = PQntuples(result);
00243    for (i = 0; i < rows; i++) {
00244       fname = PQgetvalue(result, i, 0);
00245       ftype = PQgetvalue(result, i, 1);
00246       flen = PQgetvalue(result, i, 2);
00247       fnotnull = PQgetvalue(result, i, 3);
00248       fdef = PQgetvalue(result, i, 4);
00249       ast_verb(4, "Found column '%s' of type '%s'\n", fname, ftype);
00250 
00251       if (!(column = ast_calloc(1, sizeof(*column) + strlen(fname) + strlen(ftype) + 2))) {
00252          ast_log(LOG_ERROR, "Unable to allocate column element for %s, %s\n", orig_tablename, fname);
00253          PQclear(result);
00254          destroy_table(table);
00255          AST_LIST_UNLOCK(&psql_tables);
00256          ast_mutex_unlock(&pgsql_lock);
00257          return NULL;
00258       }
00259 
00260       if (strcmp(flen, "-1") == 0) {
00261          /* Some types, like chars, have the length stored in a different field */
00262          flen = PQgetvalue(result, i, 5);
00263          sscanf(flen, "%30d", &column->len);
00264          column->len -= 4;
00265       } else {
00266          sscanf(flen, "%30d", &column->len);
00267       }
00268       column->name = (char *)column + sizeof(*column);
00269       column->type = (char *)column + sizeof(*column) + strlen(fname) + 1;
00270       strcpy(column->name, fname);
00271       strcpy(column->type, ftype);
00272       if (*fnotnull == 't') {
00273          column->notnull = 1;
00274       } else {
00275          column->notnull = 0;
00276       }
00277       if (!ast_strlen_zero(fdef)) {
00278          column->hasdefault = 1;
00279       } else {
00280          column->hasdefault = 0;
00281       }
00282       AST_LIST_INSERT_TAIL(&table->columns, column, list);
00283    }
00284    PQclear(result);
00285 
00286    AST_LIST_INSERT_TAIL(&psql_tables, table, list);
00287    ast_rwlock_rdlock(&table->lock);
00288    AST_LIST_UNLOCK(&psql_tables);
00289    ast_mutex_unlock(&pgsql_lock);
00290    return table;
00291 }

static char * handle_cli_realtime_pgsql_cache ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 1526 of file res_config_pgsql.c.

References ast_cli_args::argc, ast_cli_args::argv, ast_cli(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strdup, CLI_GENERATE, CLI_INIT, tables::columns, ast_cli_entry::command, ast_cli_args::fd, find_table(), columns::len, columns::list, ast_cli_args::n, columns::name, tables::name, columns::notnull, release_table, columns::type, ast_cli_entry::usage, and ast_cli_args::word.

01527 {
01528    struct tables *cur;
01529    int l, which;
01530    char *ret = NULL;
01531 
01532    switch (cmd) {
01533    case CLI_INIT:
01534       e->command = "realtime show pgsql cache";
01535       e->usage =
01536          "Usage: realtime show pgsql cache [<table>]\n"
01537          "       Shows table cache for the PostgreSQL RealTime driver\n";
01538       return NULL;
01539    case CLI_GENERATE:
01540       if (a->argc != 4) {
01541          return NULL;
01542       }
01543       l = strlen(a->word);
01544       which = 0;
01545       AST_LIST_LOCK(&psql_tables);
01546       AST_LIST_TRAVERSE(&psql_tables, cur, list) {
01547          if (!strncasecmp(a->word, cur->name, l) && ++which > a->n) {
01548             ret = ast_strdup(cur->name);
01549             break;
01550          }
01551       }
01552       AST_LIST_UNLOCK(&psql_tables);
01553       return ret;
01554    }
01555 
01556    if (a->argc == 4) {
01557       /* List of tables */
01558       AST_LIST_LOCK(&psql_tables);
01559       AST_LIST_TRAVERSE(&psql_tables, cur, list) {
01560          ast_cli(a->fd, "%s\n", cur->name);
01561       }
01562       AST_LIST_UNLOCK(&psql_tables);
01563    } else if (a->argc == 5) {
01564       /* List of columns */
01565       if ((cur = find_table(NULL, a->argv[4]))) {
01566          struct columns *col;
01567          ast_cli(a->fd, "Columns for Table Cache '%s':\n", a->argv[4]);
01568          ast_cli(a->fd, "%-20.20s %-20.20s %-3.3s %-8.8s\n", "Name", "Type", "Len", "Nullable");
01569          AST_LIST_TRAVERSE(&cur->columns, col, list) {
01570             ast_cli(a->fd, "%-20.20s %-20.20s %3d %-8.8s\n", col->name, col->type, col->len, col->notnull ? "NOT NULL" : "");
01571          }
01572          release_table(cur);
01573       } else {
01574          ast_cli(a->fd, "No such table '%s'\n", a->argv[4]);
01575       }
01576    }
01577    return 0;
01578 }

static char * handle_cli_realtime_pgsql_status ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 1580 of file res_config_pgsql.c.

References ast_cli_args::argc, ast_cli(), ast_strlen_zero(), CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, connect_time, dbhost, dbname, dbport, dbsock, dbuser, ast_cli_args::fd, status, and ast_cli_entry::usage.

01581 {
01582    char status[256], credentials[100] = "";
01583    int ctimesec = time(NULL) - connect_time;
01584 
01585    switch (cmd) {
01586    case CLI_INIT:
01587       e->command = "realtime show pgsql status";
01588       e->usage =
01589          "Usage: realtime show pgsql status\n"
01590          "       Shows connection information for the PostgreSQL RealTime driver\n";
01591       return NULL;
01592    case CLI_GENERATE:
01593       return NULL;
01594    }
01595 
01596    if (a->argc != 4)
01597       return CLI_SHOWUSAGE;
01598 
01599    if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
01600       if (!ast_strlen_zero(dbhost))
01601          snprintf(status, sizeof(status), "Connected to %s@%s, port %d", dbname, dbhost, dbport);
01602       else if (!ast_strlen_zero(dbsock))
01603          snprintf(status, sizeof(status), "Connected to %s on socket file %s", dbname, dbsock);
01604       else
01605          snprintf(status, sizeof(status), "Connected to %s@%s", dbname, dbhost);
01606 
01607       if (!ast_strlen_zero(dbuser))
01608          snprintf(credentials, sizeof(credentials), " with username %s", dbuser);
01609 
01610       if (ctimesec > 31536000)
01611          ast_cli(a->fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n",
01612                status, credentials, ctimesec / 31536000, (ctimesec % 31536000) / 86400,
01613                (ctimesec % 86400) / 3600, (ctimesec % 3600) / 60, ctimesec % 60);
01614       else if (ctimesec > 86400)
01615          ast_cli(a->fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status,
01616                credentials, ctimesec / 86400, (ctimesec % 86400) / 3600, (ctimesec % 3600) / 60,
01617                ctimesec % 60);
01618       else if (ctimesec > 3600)
01619          ast_cli(a->fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, credentials,
01620                ctimesec / 3600, (ctimesec % 3600) / 60, ctimesec % 60);
01621       else if (ctimesec > 60)
01622          ast_cli(a->fd, "%s%s for %d minutes, %d seconds.\n", status, credentials, ctimesec / 60,
01623                ctimesec % 60);
01624       else
01625          ast_cli(a->fd, "%s%s for %d seconds.\n", status, credentials, ctimesec);
01626 
01627       return CLI_SUCCESS;
01628    } else {
01629       return CLI_FAILURE;
01630    }
01631 }

static int load_module ( void   )  [static]

Definition at line 1323 of file res_config_pgsql.c.

References ARRAY_LEN, ast_cli_register_multiple(), ast_config_engine_register(), AST_MODULE_LOAD_DECLINE, ast_verb, cli_realtime, parse_config(), and pgsql_engine.

01324 {
01325    if(!parse_config(0))
01326       return AST_MODULE_LOAD_DECLINE;
01327 
01328    ast_config_engine_register(&pgsql_engine);
01329    ast_verb(1, "PostgreSQL RealTime driver loaded.\n");
01330    ast_cli_register_multiple(cli_realtime, ARRAY_LEN(cli_realtime));
01331 
01332    return 0;
01333 }

static int parse_config ( int  reload  )  [static]

Definition at line 1369 of file res_config_pgsql.c.

References ast_config_destroy(), ast_config_load, ast_copy_string(), ast_debug, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_strlen_zero(), ast_variable_retrieve(), ast_verb, config, CONFIG_FLAG_FILEUNCHANGED, config_flags, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEMISSING, CONFIG_STATUS_FILEUNCHANGED, dbhost, dbname, dbpass, dbport, dbsock, dbuser, LOG_WARNING, option_debug, pgsql_lock, pgsql_reconnect(), requirements, RES_CONFIG_PGSQL_CONF, RQ_CREATECHAR, RQ_CREATECLOSE, and RQ_WARN.

01370 {
01371    struct ast_config *config;
01372    const char *s;
01373    struct ast_flags config_flags = { is_reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01374 
01375    config = ast_config_load(RES_CONFIG_PGSQL_CONF, config_flags);
01376    if (config == CONFIG_STATUS_FILEUNCHANGED) {
01377       return 0;
01378    }
01379 
01380    if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
01381       ast_log(LOG_WARNING, "Unable to load config %s\n", RES_CONFIG_PGSQL_CONF);
01382       return 0;
01383    }
01384 
01385    ast_mutex_lock(&pgsql_lock);
01386 
01387    if (pgsqlConn) {
01388       PQfinish(pgsqlConn);
01389       pgsqlConn = NULL;
01390    }
01391 
01392    if (!(s = ast_variable_retrieve(config, "general", "dbuser"))) {
01393       ast_log(LOG_WARNING,
01394             "PostgreSQL RealTime: No database user found, using 'asterisk' as default.\n");
01395       strcpy(dbuser, "asterisk");
01396    } else {
01397       ast_copy_string(dbuser, s, sizeof(dbuser));
01398    }
01399 
01400    if (!(s = ast_variable_retrieve(config, "general", "dbpass"))) {
01401       ast_log(LOG_WARNING,
01402             "PostgreSQL RealTime: No database password found, using 'asterisk' as default.\n");
01403       strcpy(dbpass, "asterisk");
01404    } else {
01405       ast_copy_string(dbpass, s, sizeof(dbpass));
01406    }
01407 
01408    if (!(s = ast_variable_retrieve(config, "general", "dbhost"))) {
01409       ast_log(LOG_WARNING,
01410             "PostgreSQL RealTime: No database host found, using localhost via socket.\n");
01411       dbhost[0] = '\0';
01412    } else {
01413       ast_copy_string(dbhost, s, sizeof(dbhost));
01414    }
01415 
01416    if (!(s = ast_variable_retrieve(config, "general", "dbname"))) {
01417       ast_log(LOG_WARNING,
01418             "PostgreSQL RealTime: No database name found, using 'asterisk' as default.\n");
01419       strcpy(dbname, "asterisk");
01420    } else {
01421       ast_copy_string(dbname, s, sizeof(dbname));
01422    }
01423 
01424    if (!(s = ast_variable_retrieve(config, "general", "dbport"))) {
01425       ast_log(LOG_WARNING,
01426             "PostgreSQL RealTime: No database port found, using 5432 as default.\n");
01427       dbport = 5432;
01428    } else {
01429       dbport = atoi(s);
01430    }
01431 
01432    if (!ast_strlen_zero(dbhost)) {
01433       /* No socket needed */
01434    } else if (!(s = ast_variable_retrieve(config, "general", "dbsock"))) {
01435       ast_log(LOG_WARNING,
01436             "PostgreSQL RealTime: No database socket found, using '/tmp/.s.PGSQL.%d' as default.\n", dbport);
01437       strcpy(dbsock, "/tmp");
01438    } else {
01439       ast_copy_string(dbsock, s, sizeof(dbsock));
01440    }
01441 
01442    if (!(s = ast_variable_retrieve(config, "general", "requirements"))) {
01443       ast_log(LOG_WARNING,
01444             "PostgreSQL RealTime: no requirements setting found, using 'warn' as default.\n");
01445       requirements = RQ_WARN;
01446    } else if (!strcasecmp(s, "createclose")) {
01447       requirements = RQ_CREATECLOSE;
01448    } else if (!strcasecmp(s, "createchar")) {
01449       requirements = RQ_CREATECHAR;
01450    }
01451 
01452    ast_config_destroy(config);
01453 
01454    if (option_debug) {
01455       if (!ast_strlen_zero(dbhost)) {
01456          ast_debug(1, "PostgreSQL RealTime Host: %s\n", dbhost);
01457          ast_debug(1, "PostgreSQL RealTime Port: %i\n", dbport);
01458       } else {
01459          ast_debug(1, "PostgreSQL RealTime Socket: %s\n", dbsock);
01460       }
01461       ast_debug(1, "PostgreSQL RealTime User: %s\n", dbuser);
01462       ast_debug(1, "PostgreSQL RealTime Password: %s\n", dbpass);
01463       ast_debug(1, "PostgreSQL RealTime DBName: %s\n", dbname);
01464    }
01465 
01466    if (!pgsql_reconnect(NULL)) {
01467       ast_log(LOG_WARNING,
01468             "PostgreSQL RealTime: Couldn't establish connection. Check debug.\n");
01469       ast_debug(1, "PostgreSQL RealTime: Cannot Connect: %s\n", PQerrorMessage(pgsqlConn));
01470    }
01471 
01472    ast_verb(2, "PostgreSQL RealTime reloaded.\n");
01473 
01474    /* Done reloading. Release lock so others can now use driver. */
01475    ast_mutex_unlock(&pgsql_lock);
01476 
01477    return 1;
01478 }

static int pgsql_reconnect ( const char *  database  )  [static]

Definition at line 1480 of file res_config_pgsql.c.

References ast_copy_string(), ast_debug, ast_free, ast_log(), ast_str_append(), ast_str_buffer(), ast_str_create(), ast_str_set(), ast_str_size(), ast_strlen_zero(), connect_time, dbhost, dbname, dbpass, dbport, dbsock, dbuser, LOG_ERROR, and S_OR.

Referenced by config_pgsql(), destroy_pgsql(), find_table(), parse_config(), realtime_multi_pgsql(), realtime_pgsql(), require_pgsql(), store_pgsql(), update2_pgsql(), and update_pgsql().

01481 {
01482    char my_database[50];
01483 
01484    ast_copy_string(my_database, S_OR(database, dbname), sizeof(my_database));
01485 
01486    /* mutex lock should have been locked before calling this function. */
01487 
01488    if (pgsqlConn && PQstatus(pgsqlConn) != CONNECTION_OK) {
01489       PQfinish(pgsqlConn);
01490       pgsqlConn = NULL;
01491    }
01492 
01493    /* DB password can legitimately be 0-length */
01494    if ((!pgsqlConn) && (!ast_strlen_zero(dbhost) || !ast_strlen_zero(dbsock)) && !ast_strlen_zero(dbuser) && !ast_strlen_zero(my_database)) {
01495       struct ast_str *connInfo = ast_str_create(32);
01496 
01497       ast_str_set(&connInfo, 0, "host=%s port=%d dbname=%s user=%s",
01498          S_OR(dbhost, dbsock), dbport, my_database, dbuser);
01499       if (!ast_strlen_zero(dbpass))
01500          ast_str_append(&connInfo, 0, " password=%s", dbpass);
01501 
01502       ast_debug(1, "%u connInfo=%s\n", (unsigned int)ast_str_size(connInfo), ast_str_buffer(connInfo));
01503       pgsqlConn = PQconnectdb(ast_str_buffer(connInfo));
01504       ast_debug(1, "%u connInfo=%s\n", (unsigned int)ast_str_size(connInfo), ast_str_buffer(connInfo));
01505       ast_free(connInfo);
01506       connInfo = NULL;
01507 
01508       ast_debug(1, "pgsqlConn=%p\n", pgsqlConn);
01509       if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
01510          ast_debug(1, "PostgreSQL RealTime: Successfully connected to database.\n");
01511          connect_time = time(NULL);
01512          version = PQserverVersion(pgsqlConn);
01513          return 1;
01514       } else {
01515          ast_log(LOG_ERROR,
01516                "PostgreSQL RealTime: Failed to connect database %s on %s: %s\n",
01517                dbname, dbhost, PQresultErrorMessage(NULL));
01518          return 0;
01519       }
01520    } else {
01521       ast_debug(1, "PostgreSQL RealTime: One or more of the parameters in the config does not pass our validity checks.\n");
01522       return 1;
01523    }
01524 }

static struct ast_config* realtime_multi_pgsql ( const char *  database,
const char *  table,
va_list  ap 
) [static]

Definition at line 444 of file res_config_pgsql.c.

References ast_calloc, ast_category_append(), ast_category_new(), ast_category_rename(), ast_config_destroy(), ast_config_new(), ast_debug, ast_free, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_realtime_decode_chunk(), ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_thread_get(), ast_strdupa, ast_strip(), ast_strlen_zero(), ast_variable_append(), ast_variable_new(), ESCAPE_STRING, escapebuf_buf, LOG_ERROR, LOG_WARNING, pgsql_lock, pgsql_reconnect(), sql_buf, strsep(), and var.

00445 {
00446    PGresult *result = NULL;
00447    int num_rows = 0, pgresult;
00448    struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
00449    struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
00450    const char *initfield = NULL;
00451    char *stringp;
00452    char *chunk;
00453    char *op;
00454    const char *newparam, *newval;
00455    struct ast_variable *var = NULL;
00456    struct ast_config *cfg = NULL;
00457    struct ast_category *cat = NULL;
00458 
00459    if (!table) {
00460       ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00461       return NULL;
00462    }
00463 
00464    if (!(cfg = ast_config_new()))
00465       return NULL;
00466 
00467    /* Get the first parameter and first value in our list of passed paramater/value pairs */
00468    newparam = va_arg(ap, const char *);
00469    newval = va_arg(ap, const char *);
00470    if (!newparam || !newval) {
00471       ast_log(LOG_WARNING,
00472             "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00473       if (pgsqlConn) {
00474          PQfinish(pgsqlConn);
00475          pgsqlConn = NULL;
00476       }
00477       ast_config_destroy(cfg);
00478       return NULL;
00479    }
00480 
00481    initfield = ast_strdupa(newparam);
00482    if ((op = strchr(initfield, ' '))) {
00483       *op = '\0';
00484    }
00485 
00486    /* Create the first part of the query using the first parameter/value pairs we just extracted
00487       If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
00488 
00489    if (!strchr(newparam, ' '))
00490       op = " =";
00491    else
00492       op = "";
00493 
00494    ESCAPE_STRING(escapebuf, newval);
00495    if (pgresult) {
00496       ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00497       va_end(ap);
00498       ast_config_destroy(cfg);
00499       return NULL;
00500    }
00501 
00502    ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, ast_str_buffer(escapebuf));
00503    while ((newparam = va_arg(ap, const char *))) {
00504       newval = va_arg(ap, const char *);
00505       if (!strchr(newparam, ' '))
00506          op = " =";
00507       else
00508          op = "";
00509 
00510       ESCAPE_STRING(escapebuf, newval);
00511       if (pgresult) {
00512          ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00513          va_end(ap);
00514          ast_config_destroy(cfg);
00515          return NULL;
00516       }
00517 
00518       ast_str_append(&sql, 0, " AND %s%s '%s'", newparam, op, ast_str_buffer(escapebuf));
00519    }
00520 
00521    if (initfield) {
00522       ast_str_append(&sql, 0, " ORDER BY %s", initfield);
00523    }
00524 
00525    va_end(ap);
00526 
00527    /* We now have our complete statement; Lets connect to the server and execute it. */
00528    ast_mutex_lock(&pgsql_lock);
00529    if (!pgsql_reconnect(database)) {
00530       ast_mutex_unlock(&pgsql_lock);
00531       ast_config_destroy(cfg);
00532       return NULL;
00533    }
00534 
00535    if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
00536       ast_log(LOG_WARNING,
00537             "PostgreSQL RealTime: Failed to query %s@%s. Check debug for more info.\n", table, database);
00538       ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00539       ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
00540       ast_mutex_unlock(&pgsql_lock);
00541       ast_config_destroy(cfg);
00542       return NULL;
00543    } else {
00544       ExecStatusType result_status = PQresultStatus(result);
00545       if (result_status != PGRES_COMMAND_OK
00546          && result_status != PGRES_TUPLES_OK
00547          && result_status != PGRES_NONFATAL_ERROR) {
00548          ast_log(LOG_WARNING,
00549                "PostgreSQL RealTime: Failed to query %s@%s. Check debug for more info.\n", table, database);
00550          ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00551          ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
00552                   PQresultErrorMessage(result), PQresStatus(result_status));
00553          PQclear(result);
00554          ast_mutex_unlock(&pgsql_lock);
00555          ast_config_destroy(cfg);
00556          return NULL;
00557       }
00558    }
00559 
00560    ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, ast_str_buffer(sql));
00561 
00562    if ((num_rows = PQntuples(result)) > 0) {
00563       int numFields = PQnfields(result);
00564       int i = 0;
00565       int rowIndex = 0;
00566       char **fieldnames = NULL;
00567 
00568       ast_debug(1, "PostgreSQL RealTime: Found %d rows.\n", num_rows);
00569 
00570       if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
00571          PQclear(result);
00572          ast_mutex_unlock(&pgsql_lock);
00573          ast_config_destroy(cfg);
00574          return NULL;
00575       }
00576       for (i = 0; i < numFields; i++)
00577          fieldnames[i] = PQfname(result, i);
00578 
00579       for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
00580          var = NULL;
00581          if (!(cat = ast_category_new("","",99999)))
00582             continue;
00583          for (i = 0; i < numFields; i++) {
00584             stringp = PQgetvalue(result, rowIndex, i);
00585             while (stringp) {
00586                chunk = strsep(&stringp, ";");
00587                if (chunk && !ast_strlen_zero(ast_realtime_decode_chunk(ast_strip(chunk)))) {
00588                   if (initfield && !strcmp(initfield, fieldnames[i])) {
00589                      ast_category_rename(cat, chunk);
00590                   }
00591                   var = ast_variable_new(fieldnames[i], chunk, "");
00592                   ast_variable_append(cat, var);
00593                }
00594             }
00595          }
00596          ast_category_append(cfg, cat);
00597       }
00598       ast_free(fieldnames);
00599    } else {
00600       ast_debug(1, "PostgreSQL RealTime: Could not find any rows in table %s.\n", table);
00601    }
00602 
00603    PQclear(result);
00604    ast_mutex_unlock(&pgsql_lock);
00605 
00606    return cfg;
00607 }

static struct ast_variable* realtime_pgsql ( const char *  database,
const char *  tablename,
va_list  ap 
) [static]

Definition at line 308 of file res_config_pgsql.c.

References ast_calloc, ast_debug, ast_free, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_realtime_decode_chunk(), ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_thread_get(), ast_strip(), ast_strlen_zero(), ast_variable_new(), ESCAPE_STRING, escapebuf_buf, LOG_ERROR, LOG_WARNING, ast_variable::next, pgsql_lock, pgsql_reconnect(), sql_buf, strsep(), and var.

00309 {
00310    PGresult *result = NULL;
00311    int num_rows = 0, pgresult;
00312    struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
00313    struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
00314    char *stringp;
00315    char *chunk;
00316    char *op;
00317    const char *newparam, *newval;
00318    struct ast_variable *var = NULL, *prev = NULL;
00319 
00320    if (!tablename) {
00321       ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00322       return NULL;
00323    }
00324 
00325    /* Get the first parameter and first value in our list of passed paramater/value pairs */
00326    newparam = va_arg(ap, const char *);
00327    newval = va_arg(ap, const char *);
00328    if (!newparam || !newval) {
00329       ast_log(LOG_WARNING,
00330             "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00331       if (pgsqlConn) {
00332          PQfinish(pgsqlConn);
00333          pgsqlConn = NULL;
00334       }
00335       return NULL;
00336    }
00337 
00338    /* Create the first part of the query using the first parameter/value pairs we just extracted
00339       If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
00340    op = strchr(newparam, ' ') ? "" : " =";
00341 
00342    ESCAPE_STRING(escapebuf, newval);
00343    if (pgresult) {
00344       ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00345       va_end(ap);
00346       return NULL;
00347    }
00348 
00349    ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", tablename, newparam, op, ast_str_buffer(escapebuf));
00350    while ((newparam = va_arg(ap, const char *))) {
00351       newval = va_arg(ap, const char *);
00352       if (!strchr(newparam, ' '))
00353          op = " =";
00354       else
00355          op = "";
00356 
00357       ESCAPE_STRING(escapebuf, newval);
00358       if (pgresult) {
00359          ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00360          va_end(ap);
00361          return NULL;
00362       }
00363 
00364       ast_str_append(&sql, 0, " AND %s%s '%s'", newparam, op, ast_str_buffer(escapebuf));
00365    }
00366    va_end(ap);
00367 
00368    /* We now have our complete statement; Lets connect to the server and execute it. */
00369    ast_mutex_lock(&pgsql_lock);
00370    if (!pgsql_reconnect(database)) {
00371       ast_mutex_unlock(&pgsql_lock);
00372       return NULL;
00373    }
00374 
00375    if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
00376       ast_log(LOG_WARNING,
00377             "PostgreSQL RealTime: Failed to query '%s@%s'. Check debug for more info.\n", tablename, database);
00378       ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00379       ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
00380       PQclear(result);
00381       ast_mutex_unlock(&pgsql_lock);
00382       return NULL;
00383    } else {
00384       ExecStatusType result_status = PQresultStatus(result);
00385       if (result_status != PGRES_COMMAND_OK
00386          && result_status != PGRES_TUPLES_OK
00387          && result_status != PGRES_NONFATAL_ERROR) {
00388          ast_log(LOG_WARNING,
00389                "PostgreSQL RealTime: Failed to query '%s@%s'. Check debug for more info.\n", tablename, database);
00390          ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00391          ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
00392                   PQresultErrorMessage(result), PQresStatus(result_status));
00393          ast_mutex_unlock(&pgsql_lock);
00394          return NULL;
00395       }
00396    }
00397 
00398    ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, ast_str_buffer(sql));
00399 
00400    if ((num_rows = PQntuples(result)) > 0) {
00401       int i = 0;
00402       int rowIndex = 0;
00403       int numFields = PQnfields(result);
00404       char **fieldnames = NULL;
00405 
00406       ast_debug(1, "PostgreSQL RealTime: Found %d rows.\n", num_rows);
00407 
00408       if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
00409          PQclear(result);
00410          ast_mutex_unlock(&pgsql_lock);
00411          return NULL;
00412       }
00413       for (i = 0; i < numFields; i++)
00414          fieldnames[i] = PQfname(result, i);
00415       for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
00416          for (i = 0; i < numFields; i++) {
00417             stringp = PQgetvalue(result, rowIndex, i);
00418             while (stringp) {
00419                chunk = strsep(&stringp, ";");
00420                if (chunk && !ast_strlen_zero(ast_realtime_decode_chunk(ast_strip(chunk)))) {
00421                   if (prev) {
00422                      prev->next = ast_variable_new(fieldnames[i], chunk, "");
00423                      if (prev->next) {
00424                         prev = prev->next;
00425                      }
00426                   } else {
00427                      prev = var = ast_variable_new(fieldnames[i], chunk, "");
00428                   }
00429                }
00430             }
00431          }
00432       }
00433       ast_free(fieldnames);
00434    } else {
00435       ast_debug(1, "Postgresql RealTime: Could not find any rows in table %s@%s.\n", tablename, database);
00436    }
00437 
00438    PQclear(result);
00439    ast_mutex_unlock(&pgsql_lock);
00440 
00441    return var;
00442 }

static int reload ( void   )  [static]

Definition at line 1362 of file res_config_pgsql.c.

References parse_config().

01363 {
01364    parse_config(1);
01365 
01366    return 0;
01367 }

static int require_pgsql ( const char *  database,
const char *  tablename,
va_list  ap 
) [static]

Definition at line 1160 of file res_config_pgsql.c.

References ast_debug, ast_free, AST_LIST_TRAVERSE, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_rq_is_int(), ast_str_buffer(), ast_str_create(), ast_str_set(), find_table(), columns::len, columns::list, LOG_ERROR, LOG_WARNING, columns::name, pgsql_lock, pgsql_reconnect(), release_table, requirements, RQ_CHAR, RQ_CREATECHAR, RQ_DATE, RQ_DATETIME, RQ_FLOAT, RQ_INTEGER1, RQ_INTEGER2, RQ_INTEGER3, RQ_INTEGER4, RQ_INTEGER8, RQ_UINTEGER1, RQ_UINTEGER2, RQ_UINTEGER3, RQ_UINTEGER4, RQ_UINTEGER8, RQ_WARN, columns::size, table, and columns::type.

01161 {
01162    struct columns *column;
01163    struct tables *table = find_table(database, tablename);
01164    char *elm;
01165    int type, size, res = 0;
01166 
01167    if (!table) {
01168       ast_log(LOG_WARNING, "Table %s not found in database.  This table should exist if you're using realtime.\n", tablename);
01169       return -1;
01170    }
01171 
01172    while ((elm = va_arg(ap, char *))) {
01173       type = va_arg(ap, require_type);
01174       size = va_arg(ap, int);
01175       AST_LIST_TRAVERSE(&table->columns, column, list) {
01176          if (strcmp(column->name, elm) == 0) {
01177             /* Char can hold anything, as long as it is large enough */
01178             if ((strncmp(column->type, "char", 4) == 0 || strncmp(column->type, "varchar", 7) == 0 || strcmp(column->type, "bpchar") == 0)) {
01179                if ((size > column->len) && column->len != -1) {
01180                   ast_log(LOG_WARNING, "Column '%s' should be at least %d long, but is only %d long.\n", column->name, size, column->len);
01181                   res = -1;
01182                }
01183             } else if (strncmp(column->type, "int", 3) == 0) {
01184                int typesize = atoi(column->type + 3);
01185                /* Integers can hold only other integers */
01186                if ((type == RQ_INTEGER8 || type == RQ_UINTEGER8 ||
01187                   type == RQ_INTEGER4 || type == RQ_UINTEGER4 ||
01188                   type == RQ_INTEGER3 || type == RQ_UINTEGER3 ||
01189                   type == RQ_UINTEGER2) && typesize == 2) {
01190                   ast_log(LOG_WARNING, "Column '%s' may not be large enough for the required data length: %d\n", column->name, size);
01191                   res = -1;
01192                } else if ((type == RQ_INTEGER8 || type == RQ_UINTEGER8 ||
01193                   type == RQ_UINTEGER4) && typesize == 4) {
01194                   ast_log(LOG_WARNING, "Column '%s' may not be large enough for the required data length: %d\n", column->name, size);
01195                   res = -1;
01196                } else if (type == RQ_CHAR || type == RQ_DATETIME || type == RQ_FLOAT || type == RQ_DATE) {
01197                   ast_log(LOG_WARNING, "Column '%s' is of the incorrect type: (need %s(%d) but saw %s)\n",
01198                      column->name,
01199                         type == RQ_CHAR ? "char" :
01200                         type == RQ_DATETIME ? "datetime" :
01201                         type == RQ_DATE ? "date" :
01202                         type == RQ_FLOAT ? "float" :
01203                         "a rather stiff drink ",
01204                      size, column->type);
01205                   res = -1;
01206                }
01207             } else if (strncmp(column->type, "float", 5) == 0) {
01208                if (!ast_rq_is_int(type) && type != RQ_FLOAT) {
01209                   ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type);
01210                   res = -1;
01211                }
01212             } else if (strncmp(column->type, "timestamp", 9) == 0) {
01213                if (type != RQ_DATETIME && type != RQ_DATE) {
01214                   ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type);
01215                   res = -1;
01216                }
01217             } else { /* There are other types that no module implements yet */
01218                ast_log(LOG_WARNING, "Possibly unsupported column type '%s' on column '%s'\n", column->type, column->name);
01219                res = -1;
01220             }
01221             break;
01222          }
01223       }
01224 
01225       if (!column) {
01226          if (requirements == RQ_WARN) {
01227             ast_log(LOG_WARNING, "Table %s requires a column '%s' of size '%d', but no such column exists.\n", tablename, elm, size);
01228          } else {
01229             struct ast_str *sql = ast_str_create(100);
01230             char fieldtype[15];
01231             PGresult *result;
01232 
01233             if (requirements == RQ_CREATECHAR || type == RQ_CHAR) {
01234                /* Size is minimum length; make it at least 50% greater,
01235                 * just to be sure, because PostgreSQL doesn't support
01236                 * resizing columns. */
01237                snprintf(fieldtype, sizeof(fieldtype), "CHAR(%d)",
01238                   size < 15 ? size * 2 :
01239                   (size * 3 / 2 > 255) ? 255 : size * 3 / 2);
01240             } else if (type == RQ_INTEGER1 || type == RQ_UINTEGER1 || type == RQ_INTEGER2) {
01241                snprintf(fieldtype, sizeof(fieldtype), "INT2");
01242             } else if (type == RQ_UINTEGER2 || type == RQ_INTEGER3 || type == RQ_UINTEGER3 || type == RQ_INTEGER4) {
01243                snprintf(fieldtype, sizeof(fieldtype), "INT4");
01244             } else if (type == RQ_UINTEGER4 || type == RQ_INTEGER8) {
01245                snprintf(fieldtype, sizeof(fieldtype), "INT8");
01246             } else if (type == RQ_UINTEGER8) {
01247                /* No such type on PostgreSQL */
01248                snprintf(fieldtype, sizeof(fieldtype), "CHAR(20)");
01249             } else if (type == RQ_FLOAT) {
01250                snprintf(fieldtype, sizeof(fieldtype), "FLOAT8");
01251             } else if (type == RQ_DATE) {
01252                snprintf(fieldtype, sizeof(fieldtype), "DATE");
01253             } else if (type == RQ_DATETIME) {
01254                snprintf(fieldtype, sizeof(fieldtype), "TIMESTAMP");
01255             } else {
01256                ast_log(LOG_ERROR, "Unrecognized request type %d\n", type);
01257                ast_free(sql);
01258                continue;
01259             }
01260             ast_str_set(&sql, 0, "ALTER TABLE %s ADD COLUMN %s %s", tablename, elm, fieldtype);
01261             ast_debug(1, "About to lock pgsql_lock (running alter on table '%s' to add column '%s')\n", tablename, elm);
01262 
01263             ast_mutex_lock(&pgsql_lock);
01264             if (!pgsql_reconnect(database)) {
01265                ast_mutex_unlock(&pgsql_lock);
01266                ast_log(LOG_ERROR, "Unable to add column: %s\n", ast_str_buffer(sql));
01267                ast_free(sql);
01268                continue;
01269             }
01270 
01271             ast_debug(1, "About to run ALTER query on table '%s' to add column '%s'\n", tablename, elm);
01272             result = PQexec(pgsqlConn, ast_str_buffer(sql));
01273             ast_debug(1, "Finished running ALTER query on table '%s'\n", tablename);
01274             if (PQresultStatus(result) != PGRES_COMMAND_OK) {
01275                ast_log(LOG_ERROR, "Unable to add column: %s\n", ast_str_buffer(sql));
01276             }
01277             PQclear(result);
01278             ast_mutex_unlock(&pgsql_lock);
01279 
01280             ast_free(sql);
01281          }
01282       }
01283    }
01284    release_table(table);
01285    return res;
01286 }

static int store_pgsql ( const char *  database,
const char *  table,
va_list  ap 
) [static]

Definition at line 880 of file res_config_pgsql.c.

References ast_debug, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_thread_get(), ESCAPE_STRING, escapebuf_buf, LOG_WARNING, pgsql_lock, pgsql_reconnect(), sql_buf, and where_buf.

00881 {
00882    PGresult *result = NULL;
00883    Oid insertid;
00884    struct ast_str *buf = ast_str_thread_get(&escapebuf_buf, 256);
00885    struct ast_str *sql1 = ast_str_thread_get(&sql_buf, 256);
00886    struct ast_str *sql2 = ast_str_thread_get(&where_buf, 256);
00887    int pgresult;
00888    const char *newparam, *newval;
00889 
00890    if (!table) {
00891       ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00892       return -1;
00893    }
00894 
00895    /* Get the first parameter and first value in our list of passed paramater/value pairs */
00896    newparam = va_arg(ap, const char *);
00897    newval = va_arg(ap, const char *);
00898    if (!newparam || !newval) {
00899       ast_log(LOG_WARNING,
00900             "PostgreSQL RealTime: Realtime storage requires at least 1 parameter and 1 value to store.\n");
00901       if (pgsqlConn) {
00902          PQfinish(pgsqlConn);
00903          pgsqlConn = NULL;
00904       }
00905       return -1;
00906    }
00907 
00908    /* Must connect to the server before anything else, as the escape function requires the connection handle.. */
00909    ast_mutex_lock(&pgsql_lock);
00910    if (!pgsql_reconnect(database)) {
00911       ast_mutex_unlock(&pgsql_lock);
00912       return -1;
00913    }
00914 
00915    /* Create the first part of the query using the first parameter/value pairs we just extracted
00916       If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
00917    ESCAPE_STRING(buf, newparam);
00918    ast_str_set(&sql1, 0, "INSERT INTO %s (%s", table, ast_str_buffer(buf));
00919    ESCAPE_STRING(buf, newval);
00920    ast_str_set(&sql2, 0, ") VALUES ('%s'", ast_str_buffer(buf));
00921    while ((newparam = va_arg(ap, const char *))) {
00922       newval = va_arg(ap, const char *);
00923       ESCAPE_STRING(buf, newparam);
00924       ast_str_append(&sql1, 0, ", %s", ast_str_buffer(buf));
00925       ESCAPE_STRING(buf, newval);
00926       ast_str_append(&sql2, 0, ", '%s'", ast_str_buffer(buf));
00927    }
00928    va_end(ap);
00929    ast_str_append(&sql1, 0, "%s)", ast_str_buffer(sql2));
00930 
00931    ast_debug(1, "PostgreSQL RealTime: Insert SQL: %s\n", ast_str_buffer(sql1));
00932 
00933    if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql1)))) {
00934       ast_log(LOG_WARNING,
00935             "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
00936       ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql1));
00937       ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
00938       ast_mutex_unlock(&pgsql_lock);
00939       return -1;
00940    } else {
00941       ExecStatusType result_status = PQresultStatus(result);
00942       if (result_status != PGRES_COMMAND_OK
00943          && result_status != PGRES_TUPLES_OK
00944          && result_status != PGRES_NONFATAL_ERROR) {
00945          ast_log(LOG_WARNING,
00946                "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
00947          ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql1));
00948          ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
00949                   PQresultErrorMessage(result), PQresStatus(result_status));
00950          ast_mutex_unlock(&pgsql_lock);
00951          return -1;
00952       }
00953    }
00954 
00955    insertid = PQoidValue(result);
00956    PQclear(result);
00957    ast_mutex_unlock(&pgsql_lock);
00958 
00959    ast_debug(1, "PostgreSQL RealTime: row inserted on table: %s, id: %u\n", table, insertid);
00960 
00961    /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
00962     * An integer greater than zero indicates the number of rows affected
00963     * Zero indicates that no records were updated
00964     * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
00965     */
00966 
00967    if (insertid >= 0)
00968       return (int) insertid;
00969 
00970    return -1;
00971 }

static int unload_module ( void   )  [static]

Definition at line 1335 of file res_config_pgsql.c.

References ARRAY_LEN, ast_cli_unregister_multiple(), ast_config_engine_deregister(), AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_mutex_lock, ast_mutex_unlock, ast_verb, cli_realtime, destroy_table(), columns::list, pgsql_engine, pgsql_lock, and table.

01336 {
01337    struct tables *table;
01338    /* Acquire control before doing anything to the module itself. */
01339    ast_mutex_lock(&pgsql_lock);
01340 
01341    if (pgsqlConn) {
01342       PQfinish(pgsqlConn);
01343       pgsqlConn = NULL;
01344    }
01345    ast_cli_unregister_multiple(cli_realtime, ARRAY_LEN(cli_realtime));
01346    ast_config_engine_deregister(&pgsql_engine);
01347    ast_verb(1, "PostgreSQL RealTime unloaded.\n");
01348 
01349    /* Destroy cached table info */
01350    AST_LIST_LOCK(&psql_tables);
01351    while ((table = AST_LIST_REMOVE_HEAD(&psql_tables, list))) {
01352       destroy_table(table);
01353    }
01354    AST_LIST_UNLOCK(&psql_tables);
01355 
01356    /* Unlock so something else can destroy the lock. */
01357    ast_mutex_unlock(&pgsql_lock);
01358 
01359    return 0;
01360 }

static int unload_pgsql ( const char *  database,
const char *  tablename 
) [static]

Definition at line 1288 of file res_config_pgsql.c.

References ast_debug, AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, destroy_table(), columns::list, and tables::name.

01289 {
01290    struct tables *cur;
01291    ast_debug(2, "About to lock table cache list\n");
01292    AST_LIST_LOCK(&psql_tables);
01293    ast_debug(2, "About to traverse table cache list\n");
01294    AST_LIST_TRAVERSE_SAFE_BEGIN(&psql_tables, cur, list) {
01295       if (strcmp(cur->name, tablename) == 0) {
01296          ast_debug(2, "About to remove matching cache entry\n");
01297          AST_LIST_REMOVE_CURRENT(list);
01298          ast_debug(2, "About to destroy matching cache entry\n");
01299          destroy_table(cur);
01300          ast_debug(1, "Cache entry '%s@%s' destroyed\n", tablename, database);
01301          break;
01302       }
01303    }
01304    AST_LIST_TRAVERSE_SAFE_END
01305    AST_LIST_UNLOCK(&psql_tables);
01306    ast_debug(2, "About to return\n");
01307    return cur ? 0 : -1;
01308 }

static int update2_pgsql ( const char *  database,
const char *  tablename,
va_list  ap 
) [static]

Definition at line 748 of file res_config_pgsql.c.

References ast_debug, ast_free, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_thread_get(), ESCAPE_STRING, escapebuf_buf, find_column(), find_table(), first, LOG_ERROR, LOG_NOTICE, LOG_WARNING, pgsql_lock, pgsql_reconnect(), release_table, sql_buf, table, and where_buf.

00749 {
00750    PGresult *result = NULL;
00751    int numrows = 0, pgresult, first = 1;
00752    struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 16);
00753    const char *newparam, *newval;
00754    struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
00755    struct ast_str *where = ast_str_thread_get(&where_buf, 100);
00756    struct tables *table;
00757 
00758    if (!tablename) {
00759       ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00760       return -1;
00761    }
00762 
00763    if (!escapebuf || !sql || !where) {
00764       /* Memory error, already handled */
00765       return -1;
00766    }
00767 
00768    if (!(table = find_table(database, tablename))) {
00769       ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
00770       return -1;
00771    }
00772 
00773    ast_str_set(&sql, 0, "UPDATE %s SET ", tablename);
00774    ast_str_set(&where, 0, "WHERE");
00775 
00776    while ((newparam = va_arg(ap, const char *))) {
00777       if (!find_column(table, newparam)) {
00778          ast_log(LOG_ERROR, "Attempted to update based on criteria column '%s' (%s@%s), but that column does not exist!\n", newparam, tablename, database);
00779          release_table(table);
00780          return -1;
00781       }
00782 
00783       newval = va_arg(ap, const char *);
00784       ESCAPE_STRING(escapebuf, newval);
00785       if (pgresult) {
00786          ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00787          release_table(table);
00788          ast_free(sql);
00789          return -1;
00790       }
00791       ast_str_append(&where, 0, "%s %s='%s'", first ? "" : " AND", newparam, ast_str_buffer(escapebuf));
00792       first = 0;
00793    }
00794 
00795    if (first) {
00796       ast_log(LOG_WARNING,
00797             "PostgreSQL RealTime: Realtime update requires at least 1 parameter and 1 value to search on.\n");
00798       if (pgsqlConn) {
00799          PQfinish(pgsqlConn);
00800          pgsqlConn = NULL;
00801       }
00802       release_table(table);
00803       return -1;
00804    }
00805 
00806    /* Now retrieve the columns to update */
00807    first = 1;
00808    while ((newparam = va_arg(ap, const char *))) {
00809       newval = va_arg(ap, const char *);
00810 
00811       /* If the column is not within the table, then skip it */
00812       if (!find_column(table, newparam)) {
00813          ast_log(LOG_NOTICE, "Attempted to update column '%s' in table '%s@%s', but column does not exist!\n", newparam, tablename, database);
00814          continue;
00815       }
00816 
00817       ESCAPE_STRING(escapebuf, newval);
00818       if (pgresult) {
00819          ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00820          release_table(table);
00821          ast_free(sql);
00822          return -1;
00823       }
00824 
00825       ast_str_append(&sql, 0, "%s %s='%s'", first ? "" : ",", newparam, ast_str_buffer(escapebuf));
00826    }
00827    release_table(table);
00828 
00829    ast_str_append(&sql, 0, " %s", ast_str_buffer(where));
00830 
00831    ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", ast_str_buffer(sql));
00832 
00833    /* We now have our complete statement; connect to the server and execute it. */
00834    ast_mutex_lock(&pgsql_lock);
00835    if (!pgsql_reconnect(database)) {
00836       ast_mutex_unlock(&pgsql_lock);
00837       return -1;
00838    }
00839 
00840    if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
00841       ast_log(LOG_WARNING,
00842             "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
00843       ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00844       ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
00845       ast_mutex_unlock(&pgsql_lock);
00846       return -1;
00847    } else {
00848       ExecStatusType result_status = PQresultStatus(result);
00849       if (result_status != PGRES_COMMAND_OK
00850          && result_status != PGRES_TUPLES_OK
00851          && result_status != PGRES_NONFATAL_ERROR) {
00852          ast_log(LOG_WARNING,
00853                "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
00854          ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00855          ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
00856                   PQresultErrorMessage(result), PQresStatus(result_status));
00857          ast_mutex_unlock(&pgsql_lock);
00858          return -1;
00859       }
00860    }
00861 
00862    numrows = atoi(PQcmdTuples(result));
00863    ast_mutex_unlock(&pgsql_lock);
00864 
00865    ast_debug(1, "PostgreSQL RealTime: Updated %d rows on table: %s\n", numrows, tablename);
00866 
00867    /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
00868     * An integer greater than zero indicates the number of rows affected
00869     * Zero indicates that no records were updated
00870     * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
00871     */
00872 
00873    if (numrows >= 0) {
00874       return (int) numrows;
00875    }
00876 
00877    return -1;
00878 }

static int update_pgsql ( const char *  database,
const char *  tablename,
const char *  keyfield,
const char *  lookup,
va_list  ap 
) [static]

Definition at line 609 of file res_config_pgsql.c.

References ast_debug, AST_LIST_TRAVERSE, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_thread_get(), ESCAPE_STRING, escapebuf_buf, find_column(), find_table(), columns::list, LOG_ERROR, LOG_NOTICE, LOG_WARNING, columns::name, pgsql_lock, pgsql_reconnect(), release_table, sql_buf, and table.

00611 {
00612    PGresult *result = NULL;
00613    int numrows = 0, pgresult;
00614    const char *newparam, *newval;
00615    struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
00616    struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
00617    struct tables *table;
00618    struct columns *column = NULL;
00619 
00620    if (!tablename) {
00621       ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00622       return -1;
00623    }
00624 
00625    if (!(table = find_table(database, tablename))) {
00626       ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
00627       return -1;
00628    }
00629 
00630    /* Get the first parameter and first value in our list of passed paramater/value pairs */
00631    newparam = va_arg(ap, const char *);
00632    newval = va_arg(ap, const char *);
00633    if (!newparam || !newval) {
00634       ast_log(LOG_WARNING,
00635             "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00636       if (pgsqlConn) {
00637          PQfinish(pgsqlConn);
00638          pgsqlConn = NULL;
00639       }
00640       release_table(table);
00641       return -1;
00642    }
00643 
00644    /* Check that the column exists in the table */
00645    AST_LIST_TRAVERSE(&table->columns, column, list) {
00646       if (strcmp(column->name, newparam) == 0) {
00647          break;
00648       }
00649    }
00650 
00651    if (!column) {
00652       ast_log(LOG_ERROR, "PostgreSQL RealTime: Updating on column '%s', but that column does not exist within the table '%s'!\n", newparam, tablename);
00653       release_table(table);
00654       return -1;
00655    }
00656 
00657    /* Create the first part of the query using the first parameter/value pairs we just extracted
00658       If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
00659 
00660    ESCAPE_STRING(escapebuf, newval);
00661    if (pgresult) {
00662       ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00663       va_end(ap);
00664       release_table(table);
00665       return -1;
00666    }
00667    ast_str_set(&sql, 0, "UPDATE %s SET %s = '%s'", tablename, newparam, ast_str_buffer(escapebuf));
00668 
00669    while ((newparam = va_arg(ap, const char *))) {
00670       newval = va_arg(ap, const char *);
00671 
00672       if (!find_column(table, newparam)) {
00673          ast_log(LOG_NOTICE, "Attempted to update column '%s' in table '%s', but column does not exist!\n", newparam, tablename);
00674          continue;
00675       }
00676 
00677       ESCAPE_STRING(escapebuf, newval);
00678       if (pgresult) {
00679          ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00680          va_end(ap);
00681          release_table(table);
00682          return -1;
00683       }
00684 
00685       ast_str_append(&sql, 0, ", %s = '%s'", newparam, ast_str_buffer(escapebuf));
00686    }
00687    va_end(ap);
00688    release_table(table);
00689 
00690    ESCAPE_STRING(escapebuf, lookup);
00691    if (pgresult) {
00692       ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", lookup);
00693       va_end(ap);
00694       return -1;
00695    }
00696 
00697    ast_str_append(&sql, 0, " WHERE %s = '%s'", keyfield, ast_str_buffer(escapebuf));
00698 
00699    ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", ast_str_buffer(sql));
00700 
00701    /* We now have our complete statement; Lets connect to the server and execute it. */
00702    ast_mutex_lock(&pgsql_lock);
00703    if (!pgsql_reconnect(database)) {
00704       ast_mutex_unlock(&pgsql_lock);
00705       return -1;
00706    }
00707 
00708    if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
00709       ast_log(LOG_WARNING,
00710             "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
00711       ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00712       ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
00713       ast_mutex_unlock(&pgsql_lock);
00714       return -1;
00715    } else {
00716       ExecStatusType result_status = PQresultStatus(result);
00717       if (result_status != PGRES_COMMAND_OK
00718          && result_status != PGRES_TUPLES_OK
00719          && result_status != PGRES_NONFATAL_ERROR) {
00720          ast_log(LOG_WARNING,
00721                "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
00722          ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00723          ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
00724                   PQresultErrorMessage(result), PQresStatus(result_status));
00725          PQclear(result);
00726          ast_mutex_unlock(&pgsql_lock);
00727          return -1;
00728       }
00729    }
00730 
00731    numrows = atoi(PQcmdTuples(result));
00732    ast_mutex_unlock(&pgsql_lock);
00733 
00734    ast_debug(1, "PostgreSQL RealTime: Updated %d rows on table: %s\n", numrows, tablename);
00735 
00736    /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
00737     * An integer greater than zero indicates the number of rows affected
00738     * Zero indicates that no records were updated
00739     * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
00740     */
00741 
00742    if (numrows >= 0)
00743       return (int) numrows;
00744 
00745    return -1;
00746 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "PostgreSQL RealTime Configuration Driver" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "88eaa8f5c1bd988bedd71113385e0886" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_REALTIME_DRIVER, } [static]

Definition at line 1639 of file res_config_pgsql.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 1639 of file res_config_pgsql.c.

struct ast_cli_entry cli_realtime[] [static]

Initial value:

 {
   { .handler =  handle_cli_realtime_pgsql_status , .summary =  "Shows connection information for the PostgreSQL RealTime driver" ,__VA_ARGS__ },
   { .handler =  handle_cli_realtime_pgsql_cache , .summary =  "Shows cached tables within the PostgreSQL realtime driver" ,__VA_ARGS__ },
}

Definition at line 93 of file res_config_pgsql.c.

Referenced by load_module(), and unload_module().

time_t connect_time = 0 [static]

Definition at line 84 of file res_config_pgsql.c.

char dbhost[MAX_DB_OPTION_SIZE] = "" [static]

Definition at line 78 of file res_config_pgsql.c.

Referenced by handle_cli_realtime_pgsql_status(), parse_config(), and pgsql_reconnect().

char dbname[MAX_DB_OPTION_SIZE] = "" [static]

Definition at line 81 of file res_config_pgsql.c.

Referenced by handle_cli_realtime_pgsql_status(), parse_config(), and pgsql_reconnect().

char dbpass[MAX_DB_OPTION_SIZE] = "" [static]

Definition at line 80 of file res_config_pgsql.c.

Referenced by parse_config(), and pgsql_reconnect().

int dbport = 5432 [static]

Definition at line 83 of file res_config_pgsql.c.

Referenced by handle_cli_realtime_pgsql_status(), parse_config(), and pgsql_reconnect().

char dbsock[MAX_DB_OPTION_SIZE] = "" [static]

Definition at line 82 of file res_config_pgsql.c.

Referenced by handle_cli_realtime_pgsql_status(), parse_config(), and pgsql_reconnect().

char dbuser[MAX_DB_OPTION_SIZE] = "" [static]

Definition at line 79 of file res_config_pgsql.c.

Referenced by handle_cli_realtime_pgsql_status(), parse_config(), and pgsql_reconnect().

struct ast_threadstorage escapebuf_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_escapebuf_buf , .custom_init = NULL , } [static]

Definition at line 49 of file res_config_pgsql.c.

Referenced by destroy_pgsql(), realtime_multi_pgsql(), realtime_pgsql(), store_pgsql(), update2_pgsql(), and update_pgsql().

struct ast_threadstorage findtable_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_findtable_buf , .custom_init = NULL , } [static]

Definition at line 47 of file res_config_pgsql.c.

Referenced by find_table().

struct ast_config_engine pgsql_engine [static]

Definition at line 1310 of file res_config_pgsql.c.

Referenced by load_module(), and unload_module().

ast_mutex_t pgsql_lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 } [static]

Definition at line 45 of file res_config_pgsql.c.

PGconn* pgsqlConn = NULL [static]

Definition at line 54 of file res_config_pgsql.c.

enum { ... } requirements [static]

Referenced by parse_config(), and require_pgsql().

struct ast_threadstorage semibuf_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_semibuf_buf , .custom_init = NULL , } [static]

Definition at line 50 of file res_config_pgsql.c.

struct ast_threadstorage sql_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_sql_buf , .custom_init = NULL , } [static]

Definition at line 46 of file res_config_pgsql.c.

int version [static]

Definition at line 55 of file res_config_pgsql.c.

struct ast_threadstorage where_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_where_buf , .custom_init = NULL , } [static]

Definition at line 48 of file res_config_pgsql.c.

Referenced by destroy_pgsql(), realtime_update2_handler(), store_pgsql(), and update2_pgsql().


Generated on Sat Mar 10 01:55:40 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7