2006-04-05 17:46:09 +00:00
/*
* Asterisk - - A telephony toolkit for Linux .
*
* Copyright ( C ) 1999 - 2005 , Digium , Inc .
*
2007-10-19 18:01:00 +00:00
* Manuel Guesdon < mguesdon @ oxymium . net > - PostgreSQL RealTime Driver Author / Adaptor
2006-04-05 17:46:09 +00:00
* Mark Spencer < markster @ digium . com > - Asterisk Author
* Matthew Boehm < mboehm @ cytelcom . com > - MySQL RealTime Driver Author
*
2007-10-19 18:01:00 +00:00
* res_config_pgsql . c < PostgreSQL plugin for RealTime configuration engine >
2006-04-05 17:46:09 +00:00
*
* v1 .0 - ( 07 - 11 - 05 ) - Initial version based on res_config_mysql v2 .0
*/
/*! \file
*
2007-10-19 18:01:00 +00:00
* \ brief PostgreSQL plugin for Asterisk RealTime Architecture
2006-04-05 17:46:09 +00:00
*
* \ author Mark Spencer < markster @ digium . com >
2007-10-19 18:01:00 +00:00
* \ author Manuel Guesdon < mguesdon @ oxymium . net > - PostgreSQL RealTime Driver Author / Adaptor
2006-04-05 17:46:09 +00:00
*
2009-10-07 18:55:25 +00:00
* \ extref PostgreSQL http : //www.postgresql.org
2006-04-05 17:46:09 +00:00
*/
2006-04-24 17:11:45 +00:00
/*** MODULEINFO
< depend > pgsql < / depend >
* * */
2006-06-07 18:54:56 +00:00
# include "asterisk.h"
ASTERISK_FILE_VERSION ( __FILE__ , " $Revision$ " )
2006-04-10 02:01:39 +00:00
# include <libpq-fe.h> /* PostgreSQL */
2006-04-05 17:46:09 +00:00
# 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"
AST_MUTEX_DEFINE_STATIC ( pgsql_lock ) ;
2008-10-14 00:08:52 +00:00
AST_THREADSTORAGE ( sql_buf ) ;
AST_THREADSTORAGE ( findtable_buf ) ;
AST_THREADSTORAGE ( where_buf ) ;
AST_THREADSTORAGE ( escapebuf_buf ) ;
2006-04-10 02:01:39 +00:00
2006-04-05 17:46:09 +00:00
# define RES_CONFIG_PGSQL_CONF "res_pgsql.conf"
2006-04-10 02:01:39 +00:00
2009-06-15 17:34:30 +00:00
static PGconn * pgsqlConn = NULL ;
2009-10-06 19:31:39 +00:00
static int version ;
# define has_schema_support (version > 70300 ? 1 : 0)
2006-04-10 02:01:39 +00:00
2006-04-09 18:57:25 +00:00
# define MAX_DB_OPTION_SIZE 64
2006-04-10 02:01:39 +00:00
2008-06-05 19:07:27 +00:00
struct columns {
char * name ;
char * type ;
int len ;
unsigned int notnull : 1 ;
unsigned int hasdefault : 1 ;
AST_LIST_ENTRY ( columns ) list ;
} ;
struct tables {
2008-10-14 00:08:52 +00:00
ast_rwlock_t lock ;
2008-06-05 19:07:27 +00:00
AST_LIST_HEAD_NOLOCK ( psql_columns , columns ) columns ;
AST_LIST_ENTRY ( tables ) list ;
char name [ 0 ] ;
} ;
static AST_LIST_HEAD_STATIC ( psql_tables , tables ) ;
2006-04-10 02:01:39 +00:00
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 dbsock [ MAX_DB_OPTION_SIZE ] = " " ;
static int dbport = 5432 ;
static time_t connect_time = 0 ;
2006-04-05 17:46:09 +00:00
2007-08-16 21:09:46 +00:00
static int parse_config ( int reload ) ;
2006-04-05 17:46:09 +00:00
static int pgsql_reconnect ( const char * database ) ;
2007-10-19 18:01:00 +00:00
static char * handle_cli_realtime_pgsql_status ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a ) ;
2008-06-05 19:07:27 +00:00
static char * handle_cli_realtime_pgsql_cache ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a ) ;
2009-06-15 17:34:30 +00:00
static enum { RQ_WARN , RQ_CREATECLOSE , RQ_CREATECHAR } requirements ;
2006-04-05 17:46:09 +00:00
2006-09-18 19:54:18 +00:00
static struct ast_cli_entry cli_realtime [ ] = {
2007-10-22 20:05:18 +00:00
AST_CLI_DEFINE ( handle_cli_realtime_pgsql_status , " Shows connection information for the PostgreSQL RealTime driver " ) ,
2008-06-05 19:07:27 +00:00
AST_CLI_DEFINE ( handle_cli_realtime_pgsql_cache , " Shows cached tables within the PostgreSQL realtime driver " ) ,
2006-09-18 19:54:18 +00:00
} ;
2006-04-05 17:46:09 +00:00
2008-10-14 00:08:52 +00:00
# define ESCAPE_STRING(buffer, stringname) \
do { \
int len ; \
2008-12-13 08:36:35 +00:00
if ( ( len = strlen ( stringname ) ) > ( ast_str_size ( buffer ) - 1 ) / 2 ) { \
2008-10-14 00:08:52 +00:00
ast_str_make_space ( & buffer , len * 2 + 1 ) ; \
} \
2008-12-13 08:36:35 +00:00
PQescapeStringConn ( pgsqlConn , ast_str_buffer ( buffer ) , stringname , len , & pgresult ) ; \
2008-10-14 00:08:52 +00:00
} while ( 0 )
2008-06-05 19:07:27 +00:00
static void destroy_table ( struct tables * table )
{
struct columns * column ;
2008-10-14 00:08:52 +00:00
ast_rwlock_wrlock ( & table - > lock ) ;
2008-06-05 19:07:27 +00:00
while ( ( column = AST_LIST_REMOVE_HEAD ( & table - > columns , list ) ) ) {
ast_free ( column ) ;
}
2008-10-14 00:08:52 +00:00
ast_rwlock_unlock ( & table - > lock ) ;
ast_rwlock_destroy ( & table - > lock ) ;
2008-06-05 19:07:27 +00:00
ast_free ( table ) ;
}
2009-10-06 19:31:39 +00:00
static struct tables * find_table ( const char * orig_tablename )
2008-06-05 19:07:27 +00:00
{
struct columns * column ;
struct tables * table ;
2008-10-14 00:08:52 +00:00
struct ast_str * sql = ast_str_thread_get ( & findtable_buf , 330 ) ;
2008-06-05 19:07:27 +00:00
char * pgerror ;
PGresult * result ;
char * fname , * ftype , * flen , * fnotnull , * fdef ;
int i , rows ;
AST_LIST_LOCK ( & psql_tables ) ;
AST_LIST_TRAVERSE ( & psql_tables , table , list ) {
2009-10-06 19:31:39 +00:00
if ( ! strcasecmp ( table - > name , orig_tablename ) ) {
2008-06-05 19:07:27 +00:00
ast_debug ( 1 , " Found table in cache; now locking \n " ) ;
2008-10-14 00:08:52 +00:00
ast_rwlock_rdlock ( & table - > lock ) ;
2008-06-05 19:07:27 +00:00
ast_debug ( 1 , " Lock cached table; now returning \n " ) ;
AST_LIST_UNLOCK ( & psql_tables ) ;
return table ;
}
}
2009-10-06 19:31:39 +00:00
ast_debug ( 1 , " Table '%s' not found in cache, querying now \n " , orig_tablename ) ;
2008-06-05 19:07:27 +00:00
/* Not found, scan the table */
2009-10-06 19:31:39 +00:00
if ( has_schema_support ) {
char * schemaname , * tablename ;
if ( strchr ( orig_tablename , ' . ' ) ) {
schemaname = ast_strdupa ( orig_tablename ) ;
tablename = strchr ( schemaname , ' . ' ) ;
* tablename + + = ' \0 ' ;
} else {
schemaname = " " ;
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 ' ;
}
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 ' ;
}
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 ) ;
}
2008-12-13 08:36:35 +00:00
result = PQexec ( pgsqlConn , ast_str_buffer ( sql ) ) ;
2008-06-05 19:07:27 +00:00
ast_debug ( 1 , " Query of table structure complete. Now retrieving results. \n " ) ;
if ( PQresultStatus ( result ) ! = PGRES_TUPLES_OK ) {
pgerror = PQresultErrorMessage ( result ) ;
ast_log ( LOG_ERROR , " Failed to query database columns: %s \n " , pgerror ) ;
PQclear ( result ) ;
AST_LIST_UNLOCK ( & psql_tables ) ;
return NULL ;
}
2009-10-06 19:31:39 +00:00
if ( ! ( table = ast_calloc ( 1 , sizeof ( * table ) + strlen ( orig_tablename ) + 1 ) ) ) {
2008-06-05 19:07:27 +00:00
ast_log ( LOG_ERROR , " Unable to allocate memory for new table structure \n " ) ;
AST_LIST_UNLOCK ( & psql_tables ) ;
return NULL ;
}
2009-10-06 19:31:39 +00:00
strcpy ( table - > name , orig_tablename ) ; /* SAFE */
2008-10-14 00:08:52 +00:00
ast_rwlock_init ( & table - > lock ) ;
2008-06-05 19:07:27 +00:00
AST_LIST_HEAD_INIT_NOLOCK ( & table - > columns ) ;
2008-10-14 00:08:52 +00:00
2008-06-05 19:07:27 +00:00
rows = PQntuples ( result ) ;
for ( i = 0 ; i < rows ; i + + ) {
fname = PQgetvalue ( result , i , 0 ) ;
ftype = PQgetvalue ( result , i , 1 ) ;
flen = PQgetvalue ( result , i , 2 ) ;
fnotnull = PQgetvalue ( result , i , 3 ) ;
fdef = PQgetvalue ( result , i , 4 ) ;
ast_verb ( 4 , " Found column '%s' of type '%s' \n " , fname , ftype ) ;
if ( ! ( column = ast_calloc ( 1 , sizeof ( * column ) + strlen ( fname ) + strlen ( ftype ) + 2 ) ) ) {
2009-10-06 19:31:39 +00:00
ast_log ( LOG_ERROR , " Unable to allocate column element for %s, %s \n " , orig_tablename , fname ) ;
2008-06-05 19:07:27 +00:00
destroy_table ( table ) ;
AST_LIST_UNLOCK ( & psql_tables ) ;
return NULL ;
}
2008-06-13 21:50:28 +00:00
if ( strcmp ( flen , " -1 " ) = = 0 ) {
/* Some types, like chars, have the length stored in a different field */
flen = PQgetvalue ( result , i , 5 ) ;
2009-08-10 19:20:57 +00:00
sscanf ( flen , " %30d " , & column - > len ) ;
2008-06-13 21:50:28 +00:00
column - > len - = 4 ;
} else {
2009-08-10 19:20:57 +00:00
sscanf ( flen , " %30d " , & column - > len ) ;
2008-06-13 21:50:28 +00:00
}
2008-06-05 19:07:27 +00:00
column - > name = ( char * ) column + sizeof ( * column ) ;
column - > type = ( char * ) column + sizeof ( * column ) + strlen ( fname ) + 1 ;
strcpy ( column - > name , fname ) ;
strcpy ( column - > type , ftype ) ;
if ( * fnotnull = = ' t ' ) {
column - > notnull = 1 ;
} else {
column - > notnull = 0 ;
}
if ( ! ast_strlen_zero ( fdef ) ) {
column - > hasdefault = 1 ;
} else {
column - > hasdefault = 0 ;
}
AST_LIST_INSERT_TAIL ( & table - > columns , column , list ) ;
}
PQclear ( result ) ;
AST_LIST_INSERT_TAIL ( & psql_tables , table , list ) ;
2008-10-14 00:08:52 +00:00
ast_rwlock_rdlock ( & table - > lock ) ;
2008-06-05 19:07:27 +00:00
AST_LIST_UNLOCK ( & psql_tables ) ;
return table ;
}
2008-10-14 00:08:52 +00:00
# define release_table(table) ast_rwlock_unlock(&(table)->lock);
static struct columns * find_column ( struct tables * t , const char * colname )
{
struct columns * column ;
/* Check that the column exists in the table */
AST_LIST_TRAVERSE ( & t - > columns , column , list ) {
if ( strcmp ( column - > name , colname ) = = 0 ) {
return column ;
}
}
return NULL ;
}
static struct ast_variable * realtime_pgsql ( const char * database , const char * tablename , va_list ap )
2006-04-05 17:46:09 +00:00
{
PGresult * result = NULL ;
2008-10-14 00:08:52 +00:00
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 ) ;
2006-04-05 17:46:09 +00:00
char * stringp ;
char * chunk ;
char * op ;
const char * newparam , * newval ;
2006-04-10 02:01:39 +00:00
struct ast_variable * var = NULL , * prev = NULL ;
2006-04-05 17:46:09 +00:00
2008-10-14 00:08:52 +00:00
if ( ! tablename ) {
2007-10-19 18:01:00 +00:00
ast_log ( LOG_WARNING , " PostgreSQL RealTime: No table specified. \n " ) ;
2006-04-05 17:46:09 +00:00
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 * ) ;
2006-04-10 02:01:39 +00:00
if ( ! newparam | | ! newval ) {
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
" PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on. \n " ) ;
2006-04-10 02:01:39 +00:00
if ( pgsqlConn ) {
PQfinish ( pgsqlConn ) ;
pgsqlConn = NULL ;
2008-10-14 00:08:52 +00:00
}
2006-04-05 17:46:09 +00:00
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 */
2006-04-10 02:01:39 +00:00
op = strchr ( newparam , ' ' ) ? " " : " = " ;
2006-04-05 17:46:09 +00:00
2008-10-14 00:08:52 +00:00
ESCAPE_STRING ( escapebuf , newval ) ;
if ( pgresult ) {
2007-11-29 19:35:49 +00:00
ast_log ( LOG_ERROR , " Postgres detected invalid input: '%s' \n " , newval ) ;
va_end ( ap ) ;
return NULL ;
}
2008-12-13 08:36:35 +00:00
ast_str_set ( & sql , 0 , " SELECT * FROM %s WHERE %s%s '%s' " , tablename , newparam , op , ast_str_buffer ( escapebuf ) ) ;
2006-04-10 02:01:39 +00:00
while ( ( newparam = va_arg ( ap , const char * ) ) ) {
2006-04-05 17:46:09 +00:00
newval = va_arg ( ap , const char * ) ;
2006-04-10 02:01:39 +00:00
if ( ! strchr ( newparam , ' ' ) )
op = " = " ;
else
op = " " ;
2007-11-29 19:35:49 +00:00
2008-10-14 00:08:52 +00:00
ESCAPE_STRING ( escapebuf , newval ) ;
if ( pgresult ) {
2007-11-29 19:35:49 +00:00
ast_log ( LOG_ERROR , " Postgres detected invalid input: '%s' \n " , newval ) ;
va_end ( ap ) ;
return NULL ;
}
2008-12-13 08:36:35 +00:00
ast_str_append ( & sql , 0 , " AND %s%s '%s' " , newparam , op , ast_str_buffer ( escapebuf ) ) ;
2006-04-05 17:46:09 +00:00
}
va_end ( ap ) ;
/* We now have our complete statement; Lets connect to the server and execute it. */
ast_mutex_lock ( & pgsql_lock ) ;
2006-04-10 02:01:39 +00:00
if ( ! pgsql_reconnect ( database ) ) {
2006-04-05 17:46:09 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
return NULL ;
}
2006-04-10 02:01:39 +00:00
2008-12-13 08:36:35 +00:00
if ( ! ( result = PQexec ( pgsqlConn , ast_str_buffer ( sql ) ) ) ) {
2006-04-10 02:01:39 +00:00
ast_log ( LOG_WARNING ,
2008-10-14 00:08:52 +00:00
" PostgreSQL RealTime: Failed to query '%s@%s'. Check debug for more info. \n " , tablename , database ) ;
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ) ) ;
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query Failed because: %s \n " , PQerrorMessage ( pgsqlConn ) ) ;
2006-04-05 17:46:09 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
return NULL ;
2006-04-10 02:01:39 +00:00
} 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 ,
2008-10-14 00:08:52 +00:00
" PostgreSQL RealTime: Failed to query '%s@%s'. Check debug for more info. \n " , tablename , database ) ;
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ) ) ;
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query Failed because: %s (%s) \n " ,
2006-10-03 15:53:07 +00:00
PQresultErrorMessage ( result ) , PQresStatus ( result_status ) ) ;
2006-04-10 02:01:39 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
return NULL ;
}
2006-04-05 17:46:09 +00:00
}
2006-04-10 02:01:39 +00:00
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Result=%p Query: %s \n " , result , ast_str_buffer ( sql ) ) ;
2006-04-05 17:46:09 +00:00
2006-04-10 02:01:39 +00:00
if ( ( num_rows = PQntuples ( result ) ) > 0 ) {
int i = 0 ;
int rowIndex = 0 ;
int numFields = PQnfields ( result ) ;
char * * fieldnames = NULL ;
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Found %d rows. \n " , num_rows ) ;
2006-04-10 02:01:39 +00:00
2006-04-10 02:15:47 +00:00
if ( ! ( fieldnames = ast_calloc ( 1 , numFields * sizeof ( char * ) ) ) ) {
2006-04-10 02:01:39 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
PQclear ( result ) ;
return NULL ;
}
for ( i = 0 ; i < numFields ; i + + )
fieldnames [ i ] = PQfname ( result , i ) ;
for ( rowIndex = 0 ; rowIndex < num_rows ; rowIndex + + ) {
for ( i = 0 ; i < numFields ; i + + ) {
stringp = PQgetvalue ( result , rowIndex , i ) ;
while ( stringp ) {
chunk = strsep ( & stringp , " ; " ) ;
2007-06-13 17:37:06 +00:00
if ( ! ast_strlen_zero ( ast_strip ( chunk ) ) ) {
2006-04-10 02:01:39 +00:00
if ( prev ) {
2007-08-29 20:55:40 +00:00
prev - > next = ast_variable_new ( fieldnames [ i ] , chunk , " " ) ;
2006-04-10 02:01:39 +00:00
if ( prev - > next ) {
prev = prev - > next ;
}
} else {
2007-08-29 20:55:40 +00:00
prev = var = ast_variable_new ( fieldnames [ i ] , chunk , " " ) ;
2006-04-10 02:01:39 +00:00
}
}
}
}
2006-04-05 17:46:09 +00:00
}
2007-06-06 21:20:11 +00:00
ast_free ( fieldnames ) ;
2006-04-10 02:01:39 +00:00
} else {
2008-10-14 00:08:52 +00:00
ast_debug ( 1 , " Postgresql RealTime: Could not find any rows in table %s@%s. \n " , tablename , database ) ;
2006-04-05 17:46:09 +00:00
}
ast_mutex_unlock ( & pgsql_lock ) ;
PQclear ( result ) ;
return var ;
}
static struct ast_config * realtime_multi_pgsql ( const char * database , const char * table , va_list ap )
{
PGresult * result = NULL ;
2008-10-14 00:08:52 +00:00
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 ) ;
2006-04-05 17:46:09 +00:00
const char * initfield = NULL ;
char * stringp ;
char * chunk ;
char * op ;
const char * newparam , * newval ;
2006-04-10 02:01:39 +00:00
struct ast_variable * var = NULL ;
2006-04-05 17:46:09 +00:00
struct ast_config * cfg = NULL ;
struct ast_category * cat = NULL ;
2006-04-10 02:01:39 +00:00
if ( ! table ) {
2007-10-19 18:01:00 +00:00
ast_log ( LOG_WARNING , " PostgreSQL RealTime: No table specified. \n " ) ;
2006-04-05 17:46:09 +00:00
return NULL ;
}
2006-04-10 02:01:39 +00:00
2006-04-10 02:15:47 +00:00
if ( ! ( cfg = ast_config_new ( ) ) )
2006-04-05 17:46:09 +00:00
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 * ) ;
2006-04-10 02:01:39 +00:00
if ( ! newparam | | ! newval ) {
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
" PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on. \n " ) ;
2006-04-10 02:01:39 +00:00
if ( pgsqlConn ) {
PQfinish ( pgsqlConn ) ;
pgsqlConn = NULL ;
2008-10-14 00:08:52 +00:00
}
2006-04-05 17:46:09 +00:00
return NULL ;
}
initfield = ast_strdupa ( newparam ) ;
2006-05-10 13:22:15 +00:00
if ( ( op = strchr ( initfield , ' ' ) ) ) {
2006-04-05 17:46:09 +00:00
* op = ' \0 ' ;
}
/* 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 */
2006-04-10 02:01:39 +00:00
if ( ! strchr ( newparam , ' ' ) )
op = " = " ;
else
op = " " ;
2006-04-05 17:46:09 +00:00
2008-10-14 00:08:52 +00:00
ESCAPE_STRING ( escapebuf , newval ) ;
if ( pgresult ) {
2007-11-29 19:35:49 +00:00
ast_log ( LOG_ERROR , " Postgres detected invalid input: '%s' \n " , newval ) ;
va_end ( ap ) ;
return NULL ;
}
2008-12-13 08:36:35 +00:00
ast_str_set ( & sql , 0 , " SELECT * FROM %s WHERE %s%s '%s' " , table , newparam , op , ast_str_buffer ( escapebuf ) ) ;
2006-04-10 02:01:39 +00:00
while ( ( newparam = va_arg ( ap , const char * ) ) ) {
2006-04-05 17:46:09 +00:00
newval = va_arg ( ap , const char * ) ;
2006-04-10 02:01:39 +00:00
if ( ! strchr ( newparam , ' ' ) )
op = " = " ;
else
op = " " ;
2007-11-29 19:35:49 +00:00
2008-10-14 00:08:52 +00:00
ESCAPE_STRING ( escapebuf , newval ) ;
if ( pgresult ) {
2007-11-29 19:35:49 +00:00
ast_log ( LOG_ERROR , " Postgres detected invalid input: '%s' \n " , newval ) ;
va_end ( ap ) ;
return NULL ;
}
2008-12-13 08:36:35 +00:00
ast_str_append ( & sql , 0 , " AND %s%s '%s' " , newparam , op , ast_str_buffer ( escapebuf ) ) ;
2006-04-05 17:46:09 +00:00
}
2006-04-10 02:01:39 +00:00
if ( initfield ) {
2008-10-14 00:08:52 +00:00
ast_str_append ( & sql , 0 , " ORDER BY %s " , initfield ) ;
2006-04-05 17:46:09 +00:00
}
va_end ( ap ) ;
/* We now have our complete statement; Lets connect to the server and execute it. */
ast_mutex_lock ( & pgsql_lock ) ;
2006-04-10 02:01:39 +00:00
if ( ! pgsql_reconnect ( database ) ) {
2006-04-05 17:46:09 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
return NULL ;
}
2008-12-13 08:36:35 +00:00
if ( ! ( result = PQexec ( pgsqlConn , ast_str_buffer ( sql ) ) ) ) {
2006-04-10 02:01:39 +00:00
ast_log ( LOG_WARNING ,
2008-10-14 00:08:52 +00:00
" PostgreSQL RealTime: Failed to query %s@%s. Check debug for more info. \n " , table , database ) ;
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ) ) ;
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query Failed because: %s \n " , PQerrorMessage ( pgsqlConn ) ) ;
2006-04-05 17:46:09 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
return NULL ;
2006-04-10 02:01:39 +00:00
} 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 ,
2008-10-14 00:08:52 +00:00
" PostgreSQL RealTime: Failed to query %s@%s. Check debug for more info. \n " , table , database ) ;
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ) ) ;
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query Failed because: %s (%s) \n " ,
2006-10-03 15:53:07 +00:00
PQresultErrorMessage ( result ) , PQresStatus ( result_status ) ) ;
2006-04-10 02:01:39 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
return NULL ;
}
2006-04-05 17:46:09 +00:00
}
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Result=%p Query: %s \n " , result , ast_str_buffer ( sql ) ) ;
2006-04-05 17:46:09 +00:00
2006-04-10 02:01:39 +00:00
if ( ( num_rows = PQntuples ( result ) ) > 0 ) {
int numFields = PQnfields ( result ) ;
int i = 0 ;
int rowIndex = 0 ;
char * * fieldnames = NULL ;
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Found %d rows. \n " , num_rows ) ;
2006-04-10 02:01:39 +00:00
2006-04-10 02:15:47 +00:00
if ( ! ( fieldnames = ast_calloc ( 1 , numFields * sizeof ( char * ) ) ) ) {
2006-04-10 02:01:39 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
PQclear ( result ) ;
return NULL ;
2006-04-05 17:46:09 +00:00
}
2006-04-10 02:01:39 +00:00
for ( i = 0 ; i < numFields ; i + + )
fieldnames [ i ] = PQfname ( result , i ) ;
for ( rowIndex = 0 ; rowIndex < num_rows ; rowIndex + + ) {
var = NULL ;
2007-08-29 20:55:40 +00:00
if ( ! ( cat = ast_category_new ( " " , " " , 99999 ) ) )
2006-04-10 02:01:39 +00:00
continue ;
for ( i = 0 ; i < numFields ; i + + ) {
stringp = PQgetvalue ( result , rowIndex , i ) ;
while ( stringp ) {
chunk = strsep ( & stringp , " ; " ) ;
2007-06-13 17:37:06 +00:00
if ( ! ast_strlen_zero ( ast_strip ( chunk ) ) ) {
2006-04-10 02:01:39 +00:00
if ( initfield & & ! strcmp ( initfield , fieldnames [ i ] ) ) {
ast_category_rename ( cat , chunk ) ;
}
2007-08-29 20:55:40 +00:00
var = ast_variable_new ( fieldnames [ i ] , chunk , " " ) ;
2006-04-10 02:01:39 +00:00
ast_variable_append ( cat , var ) ;
}
}
}
ast_category_append ( cfg , cat ) ;
}
2007-06-06 21:20:11 +00:00
ast_free ( fieldnames ) ;
2006-04-05 17:46:09 +00:00
} else {
2010-05-26 16:23:28 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Could not find any rows in table %s. \n " , table ) ;
2006-04-05 17:46:09 +00:00
}
ast_mutex_unlock ( & pgsql_lock ) ;
PQclear ( result ) ;
return cfg ;
}
2008-06-05 19:07:27 +00:00
static int update_pgsql ( const char * database , const char * tablename , const char * keyfield ,
2006-04-10 02:01:39 +00:00
const char * lookup , va_list ap )
2006-04-05 17:46:09 +00:00
{
2006-04-10 02:01:39 +00:00
PGresult * result = NULL ;
2008-10-14 00:08:52 +00:00
int numrows = 0 , pgresult ;
2006-04-05 17:46:09 +00:00
const char * newparam , * newval ;
2008-10-14 00:08:52 +00:00
struct ast_str * sql = ast_str_thread_get ( & sql_buf , 100 ) ;
struct ast_str * escapebuf = ast_str_thread_get ( & escapebuf_buf , 100 ) ;
2008-06-05 19:07:27 +00:00
struct tables * table ;
struct columns * column = NULL ;
2006-04-05 17:46:09 +00:00
2008-06-05 19:07:27 +00:00
if ( ! tablename ) {
2007-10-19 18:01:00 +00:00
ast_log ( LOG_WARNING , " PostgreSQL RealTime: No table specified. \n " ) ;
2008-06-05 19:07:27 +00:00
return - 1 ;
}
if ( ! ( table = find_table ( tablename ) ) ) {
ast_log ( LOG_ERROR , " Table '%s' does not exist!! \n " , tablename ) ;
2006-04-10 02:01:39 +00:00
return - 1 ;
2006-04-05 17:46:09 +00:00
}
/* 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 * ) ;
2006-04-10 02:01:39 +00:00
if ( ! newparam | | ! newval ) {
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
" PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on. \n " ) ;
2006-04-10 02:01:39 +00:00
if ( pgsqlConn ) {
PQfinish ( pgsqlConn ) ;
pgsqlConn = NULL ;
2008-10-14 00:08:52 +00:00
}
release_table ( table ) ;
2008-06-05 19:07:27 +00:00
return - 1 ;
}
/* Check that the column exists in the table */
AST_LIST_TRAVERSE ( & table - > columns , column , list ) {
if ( strcmp ( column - > name , newparam ) = = 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 ) ;
2008-10-14 00:08:52 +00:00
release_table ( table ) ;
2006-04-10 02:01:39 +00:00
return - 1 ;
2006-04-05 17:46:09 +00:00
}
/* 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 */
2008-10-14 00:08:52 +00:00
ESCAPE_STRING ( escapebuf , newval ) ;
if ( pgresult ) {
2007-11-29 19:35:49 +00:00
ast_log ( LOG_ERROR , " Postgres detected invalid input: '%s' \n " , newval ) ;
va_end ( ap ) ;
2008-10-14 00:08:52 +00:00
release_table ( table ) ;
2007-11-29 19:35:49 +00:00
return - 1 ;
}
2008-12-13 08:36:35 +00:00
ast_str_set ( & sql , 0 , " UPDATE %s SET %s = '%s' " , tablename , newparam , ast_str_buffer ( escapebuf ) ) ;
2007-11-29 19:35:49 +00:00
2006-04-10 02:01:39 +00:00
while ( ( newparam = va_arg ( ap , const char * ) ) ) {
2006-04-05 17:46:09 +00:00
newval = va_arg ( ap , const char * ) ;
2007-11-29 19:35:49 +00:00
2008-10-14 00:08:52 +00:00
if ( ! find_column ( table , newparam ) ) {
2010-05-26 16:23:28 +00:00
ast_log ( LOG_NOTICE , " Attempted to update column '%s' in table '%s', but column does not exist! \n " , newparam , tablename ) ;
2008-06-05 19:07:27 +00:00
continue ;
}
2008-10-14 00:08:52 +00:00
ESCAPE_STRING ( escapebuf , newval ) ;
if ( pgresult ) {
2007-11-29 19:35:49 +00:00
ast_log ( LOG_ERROR , " Postgres detected invalid input: '%s' \n " , newval ) ;
va_end ( ap ) ;
2008-10-14 00:08:52 +00:00
release_table ( table ) ;
2007-11-29 19:35:49 +00:00
return - 1 ;
}
2008-12-13 08:36:35 +00:00
ast_str_append ( & sql , 0 , " , %s = '%s' " , newparam , ast_str_buffer ( escapebuf ) ) ;
2006-04-05 17:46:09 +00:00
}
va_end ( ap ) ;
2008-10-14 00:08:52 +00:00
release_table ( table ) ;
2007-11-29 19:35:49 +00:00
2008-10-14 00:08:52 +00:00
ESCAPE_STRING ( escapebuf , lookup ) ;
if ( pgresult ) {
2007-11-29 19:35:49 +00:00
ast_log ( LOG_ERROR , " Postgres detected invalid input: '%s' \n " , lookup ) ;
va_end ( ap ) ;
return - 1 ;
}
2008-12-13 08:36:35 +00:00
ast_str_append ( & sql , 0 , " WHERE %s = '%s' " , keyfield , ast_str_buffer ( escapebuf ) ) ;
2006-04-05 17:46:09 +00:00
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Update SQL: %s \n " , ast_str_buffer ( sql ) ) ;
2006-04-05 17:46:09 +00:00
/* We now have our complete statement; Lets connect to the server and execute it. */
ast_mutex_lock ( & pgsql_lock ) ;
2006-04-10 02:01:39 +00:00
if ( ! pgsql_reconnect ( database ) ) {
2006-04-05 17:46:09 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
return - 1 ;
}
2008-12-13 08:36:35 +00:00
if ( ! ( result = PQexec ( pgsqlConn , ast_str_buffer ( sql ) ) ) ) {
2006-04-10 02:01:39 +00:00
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
" PostgreSQL RealTime: Failed to query database. Check debug for more info. \n " ) ;
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ) ) ;
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query Failed because: %s \n " , PQerrorMessage ( pgsqlConn ) ) ;
2006-04-05 17:46:09 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
return - 1 ;
2006-04-10 02:01:39 +00:00
} 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 ,
2007-10-19 18:01:00 +00:00
" PostgreSQL RealTime: Failed to query database. Check debug for more info. \n " ) ;
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ) ) ;
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query Failed because: %s (%s) \n " ,
2006-10-03 15:53:07 +00:00
PQresultErrorMessage ( result ) , PQresStatus ( result_status ) ) ;
2006-04-10 02:01:39 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
return - 1 ;
}
2006-04-05 17:46:09 +00:00
}
numrows = atoi ( PQcmdTuples ( result ) ) ;
ast_mutex_unlock ( & pgsql_lock ) ;
2008-06-05 19:07:27 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Updated %d rows on table: %s \n " , numrows , tablename ) ;
2006-04-05 17:46:09 +00:00
/* 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 . )
2006-04-10 02:01:39 +00:00
*/
2006-04-05 17:46:09 +00:00
2006-04-10 02:01:39 +00:00
if ( numrows > = 0 )
return ( int ) numrows ;
2006-04-05 17:46:09 +00:00
return - 1 ;
}
2008-10-14 00:08:52 +00:00
static int update2_pgsql ( const char * database , const char * tablename , va_list ap )
{
PGresult * result = NULL ;
int numrows = 0 , pgresult , first = 1 ;
struct ast_str * escapebuf = ast_str_thread_get ( & escapebuf_buf , 16 ) ;
const char * newparam , * newval ;
struct ast_str * sql = ast_str_thread_get ( & sql_buf , 100 ) ;
struct ast_str * where = ast_str_thread_get ( & where_buf , 100 ) ;
struct tables * table ;
if ( ! tablename ) {
ast_log ( LOG_WARNING , " PostgreSQL RealTime: No table specified. \n " ) ;
return - 1 ;
}
if ( ! escapebuf | | ! sql | | ! where ) {
/* Memory error, already handled */
return - 1 ;
}
if ( ! ( table = find_table ( tablename ) ) ) {
ast_log ( LOG_ERROR , " Table '%s' does not exist!! \n " , tablename ) ;
return - 1 ;
}
ast_str_set ( & sql , 0 , " UPDATE %s SET " , tablename ) ;
ast_str_set ( & where , 0 , " WHERE " ) ;
while ( ( newparam = va_arg ( ap , const char * ) ) ) {
if ( ! find_column ( table , newparam ) ) {
ast_log ( LOG_ERROR , " Attempted to update based on criteria column '%s' (%s@%s), but that column does not exist! \n " , newparam , tablename , database ) ;
release_table ( table ) ;
return - 1 ;
}
newval = va_arg ( ap , const char * ) ;
ESCAPE_STRING ( escapebuf , newval ) ;
if ( pgresult ) {
ast_log ( LOG_ERROR , " Postgres detected invalid input: '%s' \n " , newval ) ;
release_table ( table ) ;
ast_free ( sql ) ;
return - 1 ;
}
2008-12-13 08:36:35 +00:00
ast_str_append ( & where , 0 , " %s %s='%s' " , first ? " " : " AND " , newparam , ast_str_buffer ( escapebuf ) ) ;
2008-10-14 00:08:52 +00:00
first = 0 ;
}
if ( first ) {
ast_log ( LOG_WARNING ,
" PostgreSQL RealTime: Realtime update requires at least 1 parameter and 1 value to search on. \n " ) ;
if ( pgsqlConn ) {
PQfinish ( pgsqlConn ) ;
pgsqlConn = NULL ;
}
release_table ( table ) ;
return - 1 ;
}
/* Now retrieve the columns to update */
first = 1 ;
while ( ( newparam = va_arg ( ap , const char * ) ) ) {
newval = va_arg ( ap , const char * ) ;
/* If the column is not within the table, then skip it */
if ( ! find_column ( table , newparam ) ) {
ast_log ( LOG_NOTICE , " Attempted to update column '%s' in table '%s@%s', but column does not exist! \n " , newparam , tablename , database ) ;
continue ;
}
ESCAPE_STRING ( escapebuf , newval ) ;
if ( pgresult ) {
ast_log ( LOG_ERROR , " Postgres detected invalid input: '%s' \n " , newval ) ;
release_table ( table ) ;
ast_free ( sql ) ;
return - 1 ;
}
2008-12-13 08:36:35 +00:00
ast_str_append ( & sql , 0 , " %s %s='%s' " , first ? " " : " , " , newparam , ast_str_buffer ( escapebuf ) ) ;
2008-10-14 00:08:52 +00:00
}
release_table ( table ) ;
2008-12-13 08:36:35 +00:00
ast_str_append ( & sql , 0 , " %s " , ast_str_buffer ( where ) ) ;
2008-10-14 00:08:52 +00:00
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Update SQL: %s \n " , ast_str_buffer ( sql ) ) ;
2008-10-14 00:08:52 +00:00
/* We now have our complete statement; connect to the server and execute it. */
ast_mutex_lock ( & pgsql_lock ) ;
if ( ! pgsql_reconnect ( database ) ) {
ast_mutex_unlock ( & pgsql_lock ) ;
return - 1 ;
}
2008-12-13 08:36:35 +00:00
if ( ! ( result = PQexec ( pgsqlConn , ast_str_buffer ( sql ) ) ) ) {
2008-10-14 00:08:52 +00:00
ast_log ( LOG_WARNING ,
" PostgreSQL RealTime: Failed to query database. Check debug for more info. \n " ) ;
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ) ) ;
2008-10-14 00:08:52 +00:00
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 " ) ;
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ) ) ;
2008-10-14 00:08:52 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query Failed because: %s (%s) \n " ,
PQresultErrorMessage ( result ) , PQresStatus ( result_status ) ) ;
ast_mutex_unlock ( & pgsql_lock ) ;
return - 1 ;
}
}
numrows = atoi ( PQcmdTuples ( result ) ) ;
ast_mutex_unlock ( & pgsql_lock ) ;
ast_debug ( 1 , " PostgreSQL RealTime: Updated %d rows on table: %s \n " , numrows , tablename ) ;
/* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
* An integer greater than zero indicates the number of rows affected
* Zero indicates that no records were updated
* - 1 indicates that the query returned an error ( although , if the query failed , it should have been caught above . )
*/
if ( numrows > = 0 ) {
return ( int ) numrows ;
}
return - 1 ;
}
2008-09-28 21:39:07 +00:00
2007-08-17 13:40:11 +00:00
static int store_pgsql ( const char * database , const char * table , va_list ap )
{
PGresult * result = NULL ;
Oid insertid ;
2008-10-14 00:08:52 +00:00
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 ) ;
2007-08-17 13:40:11 +00:00
int pgresult ;
const char * newparam , * newval ;
if ( ! table ) {
2007-10-19 18:01:00 +00:00
ast_log ( LOG_WARNING , " PostgreSQL RealTime: No table specified. \n " ) ;
2007-08-17 13:40:11 +00:00
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 ,
2007-10-19 18:01:00 +00:00
" PostgreSQL RealTime: Realtime storage requires at least 1 parameter and 1 value to store. \n " ) ;
2007-08-17 13:40:11 +00:00
if ( pgsqlConn ) {
PQfinish ( pgsqlConn ) ;
pgsqlConn = NULL ;
2008-09-28 21:39:07 +00:00
}
2007-08-17 13:40:11 +00:00
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 */
2008-09-28 21:39:07 +00:00
ESCAPE_STRING ( buf , newparam ) ;
2008-12-13 08:36:35 +00:00
ast_str_set ( & sql1 , 0 , " INSERT INTO %s (%s " , table , ast_str_buffer ( buf ) ) ;
2008-09-28 21:39:07 +00:00
ESCAPE_STRING ( buf , newval ) ;
2008-12-13 08:36:35 +00:00
ast_str_set ( & sql2 , 0 , " ) VALUES ('%s' " , ast_str_buffer ( buf ) ) ;
2007-08-17 13:40:11 +00:00
while ( ( newparam = va_arg ( ap , const char * ) ) ) {
newval = va_arg ( ap , const char * ) ;
2008-09-28 21:39:07 +00:00
ESCAPE_STRING ( buf , newparam ) ;
2008-12-13 08:36:35 +00:00
ast_str_append ( & sql1 , 0 , " , %s " , ast_str_buffer ( buf ) ) ;
2008-09-28 21:39:07 +00:00
ESCAPE_STRING ( buf , newval ) ;
2008-12-13 08:36:35 +00:00
ast_str_append ( & sql2 , 0 , " , '%s' " , ast_str_buffer ( buf ) ) ;
2007-08-17 13:40:11 +00:00
}
va_end ( ap ) ;
2008-12-13 08:36:35 +00:00
ast_str_append ( & sql1 , 0 , " %s) " , ast_str_buffer ( sql2 ) ) ;
2007-08-17 13:40:11 +00:00
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Insert SQL: %s \n " , ast_str_buffer ( sql1 ) ) ;
2007-08-17 13:40:11 +00:00
2008-12-13 08:36:35 +00:00
if ( ! ( result = PQexec ( pgsqlConn , ast_str_buffer ( sql1 ) ) ) ) {
2007-08-17 13:40:11 +00:00
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
" PostgreSQL RealTime: Failed to query database. Check debug for more info. \n " ) ;
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql1 ) ) ;
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query Failed because: %s \n " , PQerrorMessage ( pgsqlConn ) ) ;
2007-08-17 13:40:11 +00:00
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 ,
2007-10-19 18:01:00 +00:00
" PostgreSQL RealTime: Failed to query database. Check debug for more info. \n " ) ;
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql1 ) ) ;
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query Failed because: %s (%s) \n " ,
2007-08-17 13:40:11 +00:00
PQresultErrorMessage ( result ) , PQresStatus ( result_status ) ) ;
ast_mutex_unlock ( & pgsql_lock ) ;
return - 1 ;
}
}
insertid = PQoidValue ( result ) ;
ast_mutex_unlock ( & pgsql_lock ) ;
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: row inserted on table: %s, id: %u \n " , table , insertid ) ;
2007-08-17 13:40:11 +00:00
/* 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 ;
2008-10-14 00:08:52 +00:00
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 ) ;
2007-08-17 13:40:11 +00:00
const char * newparam , * newval ;
if ( ! table ) {
2007-10-19 18:01:00 +00:00
ast_log ( LOG_WARNING , " PostgreSQL RealTime: No table specified. \n " ) ;
2007-08-17 13:40:11 +00:00
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 ,
2007-10-19 18:01:00 +00:00
" PostgreSQL RealTime: Realtime destroy requires at least 1 parameter and 1 value to search on. \n " ) ;
2007-08-17 13:40:11 +00:00
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 */
2008-09-28 21:39:07 +00:00
ESCAPE_STRING ( buf1 , keyfield ) ;
ESCAPE_STRING ( buf2 , lookup ) ;
2008-12-13 08:36:35 +00:00
ast_str_set ( & sql , 0 , " DELETE FROM %s WHERE %s = '%s' " , table , ast_str_buffer ( buf1 ) , ast_str_buffer ( buf2 ) ) ;
2007-08-17 13:40:11 +00:00
while ( ( newparam = va_arg ( ap , const char * ) ) ) {
newval = va_arg ( ap , const char * ) ;
2008-09-28 21:39:07 +00:00
ESCAPE_STRING ( buf1 , newparam ) ;
ESCAPE_STRING ( buf2 , newval ) ;
2008-12-13 08:36:35 +00:00
ast_str_append ( & sql , 0 , " AND %s = '%s' " , ast_str_buffer ( buf1 ) , ast_str_buffer ( buf2 ) ) ;
2007-08-17 13:40:11 +00:00
}
va_end ( ap ) ;
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Delete SQL: %s \n " , ast_str_buffer ( sql ) ) ;
2007-08-17 13:40:11 +00:00
2008-12-13 08:36:35 +00:00
if ( ! ( result = PQexec ( pgsqlConn , ast_str_buffer ( sql ) ) ) ) {
2007-08-17 13:40:11 +00:00
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
" PostgreSQL RealTime: Failed to query database. Check debug for more info. \n " ) ;
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ) ) ;
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query Failed because: %s \n " , PQerrorMessage ( pgsqlConn ) ) ;
2007-08-17 13:40:11 +00:00
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 ,
2007-10-19 18:01:00 +00:00
" PostgreSQL RealTime: Failed to query database. Check debug for more info. \n " ) ;
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ) ) ;
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query Failed because: %s (%s) \n " ,
2007-08-17 13:40:11 +00:00
PQresultErrorMessage ( result ) , PQresStatus ( result_status ) ) ;
ast_mutex_unlock ( & pgsql_lock ) ;
return - 1 ;
}
}
numrows = atoi ( PQcmdTuples ( result ) ) ;
ast_mutex_unlock ( & pgsql_lock ) ;
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Deleted %d rows on table: %s \n " , numrows , table ) ;
2007-08-17 13:40:11 +00:00
/* 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 ;
}
2006-04-10 02:01:39 +00:00
static struct ast_config * config_pgsql ( const char * database , const char * table ,
2007-08-29 20:55:40 +00:00
const char * file , struct ast_config * cfg ,
2008-03-11 22:55:16 +00:00
struct ast_flags flags , const char * suggested_incl , const char * who_asked )
2006-04-05 17:46:09 +00:00
{
PGresult * result = NULL ;
long num_rows ;
2006-04-06 06:55:38 +00:00
struct ast_variable * new_v ;
struct ast_category * cur_cat = NULL ;
2008-10-14 00:08:52 +00:00
struct ast_str * sql = ast_str_thread_get ( & sql_buf , 100 ) ;
2006-04-05 17:46:09 +00:00
char last [ 80 ] = " " ;
int last_cat_metric = 0 ;
last [ 0 ] = ' \0 ' ;
2006-04-10 02:01:39 +00:00
if ( ! file | | ! strcmp ( file , RES_CONFIG_PGSQL_CONF ) ) {
2007-10-19 18:01:00 +00:00
ast_log ( LOG_WARNING , " PostgreSQL RealTime: Cannot configure myself. \n " ) ;
2006-04-05 17:46:09 +00:00
return NULL ;
}
2008-10-14 00:08:52 +00:00
ast_str_set ( & sql , 0 , " SELECT category, var_name, var_val, cat_metric FROM %s "
" WHERE filename='%s' and commented=0 "
" ORDER BY cat_metric DESC, var_metric ASC, category, var_name " , table , file ) ;
2006-04-05 17:46:09 +00:00
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Static SQL: %s \n " , ast_str_buffer ( sql ) ) ;
2006-04-05 17:46:09 +00:00
/* We now have our complete statement; Lets connect to the server and execute it. */
ast_mutex_lock ( & pgsql_lock ) ;
2006-04-10 02:01:39 +00:00
if ( ! pgsql_reconnect ( database ) ) {
2006-04-05 17:46:09 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
return NULL ;
}
2008-12-13 08:36:35 +00:00
if ( ! ( result = PQexec ( pgsqlConn , ast_str_buffer ( sql ) ) ) ) {
2006-04-10 02:01:39 +00:00
ast_log ( LOG_WARNING ,
2008-10-14 00:08:52 +00:00
" PostgreSQL RealTime: Failed to query '%s@%s'. Check debug for more info. \n " , table , database ) ;
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ) ) ;
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query Failed because: %s \n " , PQerrorMessage ( pgsqlConn ) ) ;
2006-04-05 17:46:09 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
return NULL ;
2006-04-10 02:01:39 +00:00
} 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 ,
2007-10-19 18:01:00 +00:00
" PostgreSQL RealTime: Failed to query database. Check debug for more info. \n " ) ;
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ) ) ;
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Query Failed because: %s (%s) \n " ,
2006-10-03 15:53:07 +00:00
PQresultErrorMessage ( result ) , PQresStatus ( result_status ) ) ;
2006-04-10 02:01:39 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
return NULL ;
}
2006-04-05 17:46:09 +00:00
}
2006-04-10 02:01:39 +00:00
if ( ( num_rows = PQntuples ( result ) ) > 0 ) {
int rowIndex = 0 ;
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Found %ld rows. \n " , num_rows ) ;
2006-04-10 02:01:39 +00:00
for ( rowIndex = 0 ; rowIndex < num_rows ; rowIndex + + ) {
char * field_category = PQgetvalue ( result , rowIndex , 0 ) ;
char * field_var_name = PQgetvalue ( result , rowIndex , 1 ) ;
char * field_var_val = PQgetvalue ( result , rowIndex , 2 ) ;
char * field_cat_metric = PQgetvalue ( result , rowIndex , 3 ) ;
if ( ! strcmp ( field_var_name , " #include " ) ) {
2008-03-11 22:55:16 +00:00
if ( ! ast_config_internal_load ( field_var_val , cfg , flags , " " , who_asked ) ) {
2006-04-05 17:46:09 +00:00
PQclear ( result ) ;
ast_mutex_unlock ( & pgsql_lock ) ;
return NULL ;
}
continue ;
}
2006-04-10 02:01:39 +00:00
if ( strcmp ( last , field_category ) | | last_cat_metric ! = atoi ( field_cat_metric ) ) {
2007-08-29 20:55:40 +00:00
cur_cat = ast_category_new ( field_category , " " , 99999 ) ;
2006-04-10 02:19:58 +00:00
if ( ! cur_cat )
2006-04-05 17:46:09 +00:00
break ;
strcpy ( last , field_category ) ;
last_cat_metric = atoi ( field_cat_metric ) ;
ast_category_append ( cfg , cur_cat ) ;
}
2007-08-29 20:55:40 +00:00
new_v = ast_variable_new ( field_var_name , field_var_val , " " ) ;
2006-04-05 17:46:09 +00:00
ast_variable_append ( cur_cat , new_v ) ;
}
} else {
2006-04-10 02:01:39 +00:00
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
" PostgreSQL RealTime: Could not find config '%s' in database. \n " , file ) ;
2006-04-05 17:46:09 +00:00
}
PQclear ( result ) ;
ast_mutex_unlock ( & pgsql_lock ) ;
return cfg ;
}
2008-06-05 19:07:27 +00:00
static int require_pgsql ( const char * database , const char * tablename , va_list ap )
{
struct columns * column ;
struct tables * table = find_table ( tablename ) ;
char * elm ;
int type , size , res = 0 ;
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 ;
}
while ( ( elm = va_arg ( ap , char * ) ) ) {
type = va_arg ( ap , require_type ) ;
size = va_arg ( ap , int ) ;
AST_LIST_TRAVERSE ( & table - > columns , column , list ) {
if ( strcmp ( column - > name , elm ) = = 0 ) {
/* Char can hold anything, as long as it is large enough */
if ( ( strncmp ( column - > type , " char " , 4 ) = = 0 | | strncmp ( column - > type , " varchar " , 7 ) = = 0 | | strcmp ( column - > type , " bpchar " ) = = 0 ) ) {
if ( ( size > column - > len ) & & column - > len ! = - 1 ) {
ast_log ( LOG_WARNING , " Column '%s' should be at least %d long, but is only %d long. \n " , column - > name , size , column - > len ) ;
res = - 1 ;
}
} else if ( strncmp ( column - > type , " int " , 3 ) = = 0 ) {
int typesize = atoi ( column - > type + 3 ) ;
/* Integers can hold only other integers */
2008-06-09 22:51:59 +00:00
if ( ( type = = RQ_INTEGER8 | | type = = RQ_UINTEGER8 | |
type = = RQ_INTEGER4 | | type = = RQ_UINTEGER4 | |
type = = RQ_INTEGER3 | | type = = RQ_UINTEGER3 | |
type = = RQ_UINTEGER2 ) & & typesize = = 2 ) {
2008-06-05 19:07:27 +00:00
ast_log ( LOG_WARNING , " Column '%s' may not be large enough for the required data length: %d \n " , column - > name , size ) ;
res = - 1 ;
2008-06-09 22:51:59 +00:00
} else if ( ( type = = RQ_INTEGER8 | | type = = RQ_UINTEGER8 | |
type = = RQ_UINTEGER4 ) & & typesize = = 4 ) {
ast_log ( LOG_WARNING , " Column '%s' may not be large enough for the required data length: %d \n " , column - > name , size ) ;
res = - 1 ;
} else if ( type = = RQ_CHAR | | type = = RQ_DATETIME | | type = = RQ_FLOAT | | type = = RQ_DATE ) {
2008-06-13 21:50:28 +00:00
ast_log ( LOG_WARNING , " Column '%s' is of the incorrect type: (need %s(%d) but saw %s) \n " ,
column - > name ,
type = = RQ_CHAR ? " char " :
type = = RQ_DATETIME ? " datetime " :
type = = RQ_DATE ? " date " :
type = = RQ_FLOAT ? " float " :
" a rather stiff drink " ,
size , column - > type ) ;
2008-06-05 19:07:27 +00:00
res = - 1 ;
}
2008-06-09 22:51:59 +00:00
} else if ( strncmp ( column - > type , " float " , 5 ) = = 0 & & ! ast_rq_is_int ( type ) & & type ! = RQ_FLOAT ) {
2008-06-05 19:07:27 +00:00
ast_log ( LOG_WARNING , " Column %s cannot be a %s \n " , column - > name , column - > type ) ;
res = - 1 ;
} else { /* There are other types that no module implements yet */
ast_log ( LOG_WARNING , " Possibly unsupported column type '%s' on column '%s' \n " , column - > type , column - > name ) ;
res = - 1 ;
}
break ;
}
}
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 ) ;
} else {
2008-06-09 22:51:59 +00:00
struct ast_str * sql = ast_str_create ( 100 ) ;
char fieldtype [ 15 ] ;
2008-08-10 00:47:56 +00:00
PGresult * result ;
2008-06-05 19:07:27 +00:00
if ( requirements = = RQ_CREATECHAR | | type = = RQ_CHAR ) {
2008-07-31 20:10:39 +00:00
/* 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) " ,
size < 15 ? size * 2 :
( size * 3 / 2 > 255 ) ? 255 : size * 3 / 2 ) ;
2008-06-09 22:51:59 +00:00
} else if ( type = = RQ_INTEGER1 | | type = = RQ_UINTEGER1 | | type = = RQ_INTEGER2 ) {
snprintf ( fieldtype , sizeof ( fieldtype ) , " INT2 " ) ;
} else if ( type = = RQ_UINTEGER2 | | type = = RQ_INTEGER3 | | type = = RQ_UINTEGER3 | | type = = RQ_INTEGER4 ) {
snprintf ( fieldtype , sizeof ( fieldtype ) , " INT4 " ) ;
} else if ( type = = RQ_UINTEGER4 | | type = = RQ_INTEGER8 ) {
snprintf ( fieldtype , sizeof ( fieldtype ) , " INT8 " ) ;
} else if ( type = = RQ_UINTEGER8 ) {
/* No such type on PostgreSQL */
snprintf ( fieldtype , sizeof ( fieldtype ) , " CHAR(20) " ) ;
2008-06-05 19:07:27 +00:00
} else if ( type = = RQ_FLOAT ) {
2008-06-09 22:51:59 +00:00
snprintf ( fieldtype , sizeof ( fieldtype ) , " FLOAT8 " ) ;
2008-06-05 19:07:27 +00:00
} else if ( type = = RQ_DATE ) {
2008-06-09 22:51:59 +00:00
snprintf ( fieldtype , sizeof ( fieldtype ) , " DATE " ) ;
2008-06-05 19:07:27 +00:00
} else if ( type = = RQ_DATETIME ) {
2008-06-09 22:51:59 +00:00
snprintf ( fieldtype , sizeof ( fieldtype ) , " TIMESTAMP " ) ;
2008-06-05 19:07:27 +00:00
} else {
2008-07-31 20:10:39 +00:00
ast_log ( LOG_ERROR , " Unrecognized request type %d \n " , type ) ;
2008-06-05 19:07:27 +00:00
ast_free ( sql ) ;
continue ;
}
2008-06-09 22:51:59 +00:00
ast_str_set ( & sql , 0 , " ALTER TABLE %s ADD COLUMN %s %s " , tablename , elm , fieldtype ) ;
2008-06-05 19:07:27 +00:00
ast_debug ( 1 , " About to lock pgsql_lock (running alter on table '%s' to add column '%s') \n " , tablename , elm ) ;
ast_mutex_lock ( & pgsql_lock ) ;
if ( ! pgsql_reconnect ( database ) ) {
ast_mutex_unlock ( & pgsql_lock ) ;
2008-12-13 08:36:35 +00:00
ast_log ( LOG_ERROR , " Unable to add column: %s \n " , ast_str_buffer ( sql ) ) ;
2008-06-05 19:07:27 +00:00
ast_free ( sql ) ;
continue ;
}
ast_debug ( 1 , " About to run ALTER query on table '%s' to add column '%s' \n " , tablename , elm ) ;
2008-12-13 08:36:35 +00:00
result = PQexec ( pgsqlConn , ast_str_buffer ( sql ) ) ;
2008-06-05 19:07:27 +00:00
ast_debug ( 1 , " Finished running ALTER query on table '%s' \n " , tablename ) ;
2008-08-10 00:47:56 +00:00
if ( PQresultStatus ( result ) ! = PGRES_COMMAND_OK ) {
2008-12-13 08:36:35 +00:00
ast_log ( LOG_ERROR , " Unable to add column: %s \n " , ast_str_buffer ( sql ) ) ;
2008-06-05 19:07:27 +00:00
}
2008-08-10 00:47:56 +00:00
PQclear ( result ) ;
2008-06-05 19:07:27 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
ast_free ( sql ) ;
}
}
}
2008-10-14 00:08:52 +00:00
release_table ( table ) ;
2008-06-05 19:07:27 +00:00
return res ;
}
static int unload_pgsql ( const char * database , const char * tablename )
{
struct tables * cur ;
2008-07-31 20:10:39 +00:00
ast_debug ( 2 , " About to lock table cache list \n " ) ;
2008-06-05 19:07:27 +00:00
AST_LIST_LOCK ( & psql_tables ) ;
2008-07-31 20:10:39 +00:00
ast_debug ( 2 , " About to traverse table cache list \n " ) ;
2008-06-05 19:07:27 +00:00
AST_LIST_TRAVERSE_SAFE_BEGIN ( & psql_tables , cur , list ) {
if ( strcmp ( cur - > name , tablename ) = = 0 ) {
2008-07-31 20:10:39 +00:00
ast_debug ( 2 , " About to remove matching cache entry \n " ) ;
2008-06-05 19:07:27 +00:00
AST_LIST_REMOVE_CURRENT ( list ) ;
2008-07-31 20:10:39 +00:00
ast_debug ( 2 , " About to destroy matching cache entry \n " ) ;
2008-06-05 19:07:27 +00:00
destroy_table ( cur ) ;
2008-07-31 20:10:39 +00:00
ast_debug ( 1 , " Cache entry '%s@%s' destroyed \n " , tablename , database ) ;
2008-06-05 19:07:27 +00:00
break ;
}
}
AST_LIST_TRAVERSE_SAFE_END
AST_LIST_UNLOCK ( & psql_tables ) ;
2008-07-31 20:10:39 +00:00
ast_debug ( 2 , " About to return \n " ) ;
2008-06-05 19:07:27 +00:00
return cur ? 0 : - 1 ;
}
2006-04-05 17:46:09 +00:00
static struct ast_config_engine pgsql_engine = {
. name = " pgsql " ,
. load_func = config_pgsql ,
. realtime_func = realtime_pgsql ,
. realtime_multi_func = realtime_multi_pgsql ,
2007-08-17 13:40:11 +00:00
. store_func = store_pgsql ,
. destroy_func = destroy_pgsql ,
2008-06-05 19:07:27 +00:00
. update_func = update_pgsql ,
2008-10-14 00:08:52 +00:00
. update2_func = update2_pgsql ,
2008-06-05 19:07:27 +00:00
. require_func = require_pgsql ,
. unload_func = unload_pgsql ,
2006-04-05 17:46:09 +00:00
} ;
2006-08-21 02:11:39 +00:00
static int load_module ( void )
2006-04-05 17:46:09 +00:00
{
2007-08-16 21:09:46 +00:00
if ( ! parse_config ( 0 ) )
2006-08-31 21:00:20 +00:00
return AST_MODULE_LOAD_DECLINE ;
2006-04-05 17:46:09 +00:00
ast_config_engine_register ( & pgsql_engine ) ;
2007-10-19 18:01:00 +00:00
ast_verb ( 1 , " PostgreSQL RealTime driver loaded. \n " ) ;
2008-12-05 10:31:25 +00:00
ast_cli_register_multiple ( cli_realtime , ARRAY_LEN ( cli_realtime ) ) ;
2006-04-05 17:46:09 +00:00
return 0 ;
}
2006-08-21 02:11:39 +00:00
static int unload_module ( void )
2006-04-05 17:46:09 +00:00
{
2008-06-05 19:07:27 +00:00
struct tables * table ;
2007-08-16 21:09:46 +00:00
/* Acquire control before doing anything to the module itself. */
2006-04-05 17:46:09 +00:00
ast_mutex_lock ( & pgsql_lock ) ;
2006-04-10 02:01:39 +00:00
if ( pgsqlConn ) {
PQfinish ( pgsqlConn ) ;
pgsqlConn = NULL ;
2007-08-16 21:09:46 +00:00
}
2008-12-05 10:31:25 +00:00
ast_cli_unregister_multiple ( cli_realtime , ARRAY_LEN ( cli_realtime ) ) ;
2006-04-05 17:46:09 +00:00
ast_config_engine_deregister ( & pgsql_engine ) ;
2007-10-19 18:01:00 +00:00
ast_verb ( 1 , " PostgreSQL RealTime unloaded. \n " ) ;
2006-04-05 17:46:09 +00:00
2008-06-05 19:07:27 +00:00
/* Destroy cached table info */
AST_LIST_LOCK ( & psql_tables ) ;
while ( ( table = AST_LIST_REMOVE_HEAD ( & psql_tables , list ) ) ) {
destroy_table ( table ) ;
}
AST_LIST_UNLOCK ( & psql_tables ) ;
2006-04-05 17:46:09 +00:00
/* Unlock so something else can destroy the lock. */
ast_mutex_unlock ( & pgsql_lock ) ;
return 0 ;
}
2006-08-21 02:11:39 +00:00
static int reload ( void )
2006-04-05 17:46:09 +00:00
{
2007-08-16 21:09:46 +00:00
parse_config ( 1 ) ;
2006-04-05 17:46:09 +00:00
return 0 ;
}
2008-08-10 00:47:56 +00:00
static int parse_config ( int is_reload )
2006-04-05 17:46:09 +00:00
{
struct ast_config * config ;
2006-09-20 21:03:37 +00:00
const char * s ;
2008-08-10 00:47:56 +00:00
struct ast_flags config_flags = { is_reload ? CONFIG_FLAG_FILEUNCHANGED : 0 } ;
2006-04-05 17:46:09 +00:00
2008-09-12 23:30:03 +00:00
config = ast_config_load ( RES_CONFIG_PGSQL_CONF , config_flags ) ;
if ( config = = CONFIG_STATUS_FILEUNCHANGED ) {
2007-08-16 21:09:46 +00:00
return 0 ;
2008-09-12 23:30:03 +00:00
}
2006-04-05 17:46:09 +00:00
2008-09-12 23:30:03 +00:00
if ( config = = CONFIG_STATUS_FILEMISSING | | config = = CONFIG_STATUS_FILEINVALID ) {
2007-08-16 21:09:46 +00:00
ast_log ( LOG_WARNING , " Unable to load config %s \n " , RES_CONFIG_PGSQL_CONF ) ;
2006-08-31 21:00:20 +00:00
return 0 ;
}
2007-08-16 21:09:46 +00:00
ast_mutex_lock ( & pgsql_lock ) ;
if ( pgsqlConn ) {
PQfinish ( pgsqlConn ) ;
pgsqlConn = NULL ;
}
2006-08-31 21:00:20 +00:00
if ( ! ( s = ast_variable_retrieve ( config , " general " , " dbuser " ) ) ) {
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
" PostgreSQL RealTime: No database user found, using 'asterisk' as default. \n " ) ;
2006-08-31 21:00:20 +00:00
strcpy ( dbuser , " asterisk " ) ;
} else {
ast_copy_string ( dbuser , s , sizeof ( dbuser ) ) ;
}
2006-04-05 17:46:09 +00:00
2006-08-31 21:00:20 +00:00
if ( ! ( s = ast_variable_retrieve ( config , " general " , " dbpass " ) ) ) {
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
" PostgreSQL RealTime: No database password found, using 'asterisk' as default. \n " ) ;
2006-08-31 21:00:20 +00:00
strcpy ( dbpass , " asterisk " ) ;
} else {
ast_copy_string ( dbpass , s , sizeof ( dbpass ) ) ;
}
2006-04-05 17:46:09 +00:00
2006-08-31 21:00:20 +00:00
if ( ! ( s = ast_variable_retrieve ( config , " general " , " dbhost " ) ) ) {
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
" PostgreSQL RealTime: No database host found, using localhost via socket. \n " ) ;
2006-08-31 21:00:20 +00:00
dbhost [ 0 ] = ' \0 ' ;
} else {
ast_copy_string ( dbhost , s , sizeof ( dbhost ) ) ;
}
2006-04-05 17:46:09 +00:00
2006-08-31 21:00:20 +00:00
if ( ! ( s = ast_variable_retrieve ( config , " general " , " dbname " ) ) ) {
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
" PostgreSQL RealTime: No database name found, using 'asterisk' as default. \n " ) ;
2006-08-31 21:00:20 +00:00
strcpy ( dbname , " asterisk " ) ;
} else {
ast_copy_string ( dbname , s , sizeof ( dbname ) ) ;
}
2006-04-05 17:46:09 +00:00
2006-08-31 21:00:20 +00:00
if ( ! ( s = ast_variable_retrieve ( config , " general " , " dbport " ) ) ) {
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
" PostgreSQL RealTime: No database port found, using 5432 as default. \n " ) ;
2006-08-31 21:00:20 +00:00
dbport = 5432 ;
} else {
dbport = atoi ( s ) ;
}
2006-04-05 17:46:09 +00:00
2007-12-03 23:29:57 +00:00
if ( ! ast_strlen_zero ( dbhost ) ) {
/* No socket needed */
} else if ( ! ( s = ast_variable_retrieve ( config , " general " , " dbsock " ) ) ) {
2006-08-31 21:00:20 +00:00
ast_log ( LOG_WARNING ,
2010-05-26 16:14:48 +00:00
" PostgreSQL RealTime: No database socket found, using '/tmp/.s.PGSQL.%d' as default. \n " , dbport ) ;
strcpy ( dbsock , " /tmp " ) ;
2006-08-31 21:00:20 +00:00
} else {
ast_copy_string ( dbsock , s , sizeof ( dbsock ) ) ;
2006-04-05 17:46:09 +00:00
}
2008-06-05 19:07:27 +00:00
if ( ! ( s = ast_variable_retrieve ( config , " general " , " requirements " ) ) ) {
ast_log ( LOG_WARNING ,
" PostgreSQL RealTime: no requirements setting found, using 'warn' as default. \n " ) ;
requirements = RQ_WARN ;
} else if ( ! strcasecmp ( s , " createclose " ) ) {
requirements = RQ_CREATECLOSE ;
} else if ( ! strcasecmp ( s , " createchar " ) ) {
requirements = RQ_CREATECHAR ;
}
2006-04-05 17:46:09 +00:00
ast_config_destroy ( config ) ;
2006-10-03 15:53:07 +00:00
if ( option_debug ) {
2007-09-21 14:40:10 +00:00
if ( ! ast_strlen_zero ( dbhost ) ) {
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , " PostgreSQL RealTime Host: %s \n " , dbhost ) ;
ast_debug ( 1 , " PostgreSQL RealTime Port: %i \n " , dbport ) ;
2006-10-03 15:53:07 +00:00
} else {
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , " PostgreSQL RealTime Socket: %s \n " , dbsock ) ;
2006-10-03 15:53:07 +00:00
}
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , " PostgreSQL RealTime User: %s \n " , dbuser ) ;
ast_debug ( 1 , " PostgreSQL RealTime Password: %s \n " , dbpass ) ;
ast_debug ( 1 , " PostgreSQL RealTime DBName: %s \n " , dbname ) ;
2006-04-05 17:46:09 +00:00
}
2007-08-16 21:09:46 +00:00
if ( ! pgsql_reconnect ( NULL ) ) {
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
" PostgreSQL RealTime: Couldn't establish connection. Check debug. \n " ) ;
ast_debug ( 1 , " PostgreSQL RealTime: Cannot Connect: %s \n " , PQerrorMessage ( pgsqlConn ) ) ;
2007-08-16 21:09:46 +00:00
}
2007-10-19 18:01:00 +00:00
ast_verb ( 2 , " PostgreSQL RealTime reloaded. \n " ) ;
2007-08-16 21:09:46 +00:00
/* Done reloading. Release lock so others can now use driver. */
ast_mutex_unlock ( & pgsql_lock ) ;
2006-04-05 17:46:09 +00:00
return 1 ;
}
static int pgsql_reconnect ( const char * database )
{
char my_database [ 50 ] ;
2006-04-10 02:01:39 +00:00
ast_copy_string ( my_database , S_OR ( database , dbname ) , sizeof ( my_database ) ) ;
2006-04-05 17:46:09 +00:00
/* mutex lock should have been locked before calling this function. */
if ( pgsqlConn & & PQstatus ( pgsqlConn ) ! = CONNECTION_OK ) {
2006-04-10 02:01:39 +00:00
PQfinish ( pgsqlConn ) ;
pgsqlConn = NULL ;
}
2008-02-22 22:39:21 +00:00
/* DB password can legitimately be 0-length */
2008-02-25 15:12:48 +00:00
if ( ( ! pgsqlConn ) & & ( ! ast_strlen_zero ( dbhost ) | | ! ast_strlen_zero ( dbsock ) ) & & ! ast_strlen_zero ( dbuser ) & & ! ast_strlen_zero ( my_database ) ) {
2008-02-22 22:39:21 +00:00
struct ast_str * connInfo = ast_str_create ( 32 ) ;
ast_str_set ( & connInfo , 0 , " host=%s port=%d dbname=%s user=%s " ,
2010-05-26 16:14:48 +00:00
S_OR ( dbhost , dbsock ) , dbport , my_database , dbuser ) ;
2008-02-22 22:39:21 +00:00
if ( ! ast_strlen_zero ( dbpass ) )
ast_str_append ( & connInfo , 0 , " password=%s " , dbpass ) ;
2008-12-13 08:36:35 +00:00
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 ) ) ;
2007-06-06 21:20:11 +00:00
ast_free ( connInfo ) ;
2006-04-10 02:15:47 +00:00
connInfo = NULL ;
2008-02-22 22:39:21 +00:00
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " pgsqlConn=%p \n " , pgsqlConn ) ;
2006-09-18 15:15:33 +00:00
if ( pgsqlConn & & PQstatus ( pgsqlConn ) = = CONNECTION_OK ) {
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: Successfully connected to database. \n " ) ;
2006-04-10 02:15:47 +00:00
connect_time = time ( NULL ) ;
2009-10-06 19:31:39 +00:00
version = PQserverVersion ( pgsqlConn ) ;
2006-04-10 02:15:47 +00:00
return 1 ;
} else {
ast_log ( LOG_ERROR ,
2008-02-22 22:39:21 +00:00
" PostgreSQL RealTime: Failed to connect database %s on %s: %s \n " ,
dbname , dbhost , PQresultErrorMessage ( NULL ) ) ;
2006-04-10 02:15:47 +00:00
return 0 ;
2006-04-10 02:01:39 +00:00
}
2006-04-05 17:46:09 +00:00
} else {
2008-02-22 22:39:21 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: One or more of the parameters in the config does not pass our validity checks. \n " ) ;
2006-04-10 02:01:39 +00:00
return 1 ;
2006-04-05 17:46:09 +00:00
}
}
2008-06-05 19:07:27 +00:00
static char * handle_cli_realtime_pgsql_cache ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
{
struct tables * cur ;
int l , which ;
char * ret = NULL ;
switch ( cmd ) {
case CLI_INIT :
2008-09-28 23:32:14 +00:00
e - > command = " realtime show pgsql cache " ;
2008-06-05 19:07:27 +00:00
e - > usage =
2008-09-28 23:32:14 +00:00
" Usage: realtime show pgsql cache [<table>] \n "
2008-06-05 19:07:27 +00:00
" Shows table cache for the PostgreSQL RealTime driver \n " ;
return NULL ;
case CLI_GENERATE :
2008-09-28 23:32:14 +00:00
if ( a - > argc ! = 4 ) {
2008-06-05 19:07:27 +00:00
return NULL ;
}
l = strlen ( a - > word ) ;
which = 0 ;
AST_LIST_LOCK ( & psql_tables ) ;
AST_LIST_TRAVERSE ( & psql_tables , cur , list ) {
if ( ! strncasecmp ( a - > word , cur - > name , l ) & & + + which > a - > n ) {
ret = ast_strdup ( cur - > name ) ;
break ;
}
}
AST_LIST_UNLOCK ( & psql_tables ) ;
return ret ;
}
2008-09-28 23:32:14 +00:00
if ( a - > argc = = 4 ) {
2008-06-05 19:07:27 +00:00
/* List of tables */
AST_LIST_LOCK ( & psql_tables ) ;
AST_LIST_TRAVERSE ( & psql_tables , cur , list ) {
ast_cli ( a - > fd , " %s \n " , cur - > name ) ;
}
AST_LIST_UNLOCK ( & psql_tables ) ;
2008-09-28 23:32:14 +00:00
} else if ( a - > argc = = 5 ) {
2008-06-05 19:07:27 +00:00
/* List of columns */
2008-09-28 23:32:14 +00:00
if ( ( cur = find_table ( a - > argv [ 4 ] ) ) ) {
2008-06-05 19:07:27 +00:00
struct columns * col ;
2008-09-28 23:32:14 +00:00
ast_cli ( a - > fd , " Columns for Table Cache '%s': \n " , a - > argv [ 4 ] ) ;
2008-06-05 19:07:27 +00:00
ast_cli ( a - > fd , " %-20.20s %-20.20s %-3.3s %-8.8s \n " , " Name " , " Type " , " Len " , " Nullable " ) ;
AST_LIST_TRAVERSE ( & cur - > columns , col , list ) {
ast_cli ( a - > fd , " %-20.20s %-20.20s %3d %-8.8s \n " , col - > name , col - > type , col - > len , col - > notnull ? " NOT NULL " : " " ) ;
}
2008-10-14 00:08:52 +00:00
release_table ( cur ) ;
2008-06-05 19:07:27 +00:00
} else {
2008-09-28 23:32:14 +00:00
ast_cli ( a - > fd , " No such table '%s' \n " , a - > argv [ 4 ] ) ;
2008-06-05 19:07:27 +00:00
}
}
return 0 ;
}
2007-10-19 18:01:00 +00:00
static char * handle_cli_realtime_pgsql_status ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
2006-04-05 17:46:09 +00:00
{
2008-07-11 00:55:06 +00:00
char status [ 256 ] , credentials [ 100 ] = " " ;
2008-08-10 00:47:56 +00:00
int ctimesec = time ( NULL ) - connect_time ;
2006-04-05 17:46:09 +00:00
2007-10-19 18:01:00 +00:00
switch ( cmd ) {
case CLI_INIT :
2008-09-28 23:32:14 +00:00
e - > command = " realtime show pgsql status " ;
2007-10-19 18:01:00 +00:00
e - > usage =
2008-09-28 23:32:14 +00:00
" Usage: realtime show pgsql status \n "
2007-10-19 18:01:00 +00:00
" Shows connection information for the PostgreSQL RealTime driver \n " ;
return NULL ;
case CLI_GENERATE :
return NULL ;
}
2008-09-28 23:32:14 +00:00
if ( a - > argc ! = 4 )
2007-10-19 18:01:00 +00:00
return CLI_SHOWUSAGE ;
2006-04-10 02:01:39 +00:00
if ( pgsqlConn & & PQstatus ( pgsqlConn ) = = CONNECTION_OK ) {
2007-10-19 18:01:00 +00:00
if ( ! ast_strlen_zero ( dbhost ) )
2008-07-11 00:55:06 +00:00
snprintf ( status , sizeof ( status ) , " Connected to %s@%s, port %d " , dbname , dbhost , dbport ) ;
2007-10-19 18:01:00 +00:00
else if ( ! ast_strlen_zero ( dbsock ) )
2008-07-11 00:55:06 +00:00
snprintf ( status , sizeof ( status ) , " Connected to %s on socket file %s " , dbname , dbsock ) ;
2007-10-19 18:01:00 +00:00
else
2008-07-11 00:55:06 +00:00
snprintf ( status , sizeof ( status ) , " Connected to %s@%s " , dbname , dbhost ) ;
2006-04-05 17:46:09 +00:00
2007-10-19 18:01:00 +00:00
if ( ! ast_strlen_zero ( dbuser ) )
2008-07-11 00:55:06 +00:00
snprintf ( credentials , sizeof ( credentials ) , " with username %s " , dbuser ) ;
2006-04-05 17:46:09 +00:00
2008-08-10 00:47:56 +00:00
if ( ctimesec > 31536000 )
2007-10-19 18:01:00 +00:00
ast_cli ( a - > fd , " %s%s for %d years, %d days, %d hours, %d minutes, %d seconds. \n " ,
2008-08-10 00:47:56 +00:00
status , credentials , ctimesec / 31536000 , ( ctimesec % 31536000 ) / 86400 ,
( ctimesec % 86400 ) / 3600 , ( ctimesec % 3600 ) / 60 , ctimesec % 60 ) ;
else if ( ctimesec > 86400 )
2007-10-19 18:01:00 +00:00
ast_cli ( a - > fd , " %s%s for %d days, %d hours, %d minutes, %d seconds. \n " , status ,
2008-08-10 00:47:56 +00:00
credentials , ctimesec / 86400 , ( ctimesec % 86400 ) / 3600 , ( ctimesec % 3600 ) / 60 ,
ctimesec % 60 ) ;
else if ( ctimesec > 3600 )
2008-07-11 00:55:06 +00:00
ast_cli ( a - > fd , " %s%s for %d hours, %d minutes, %d seconds. \n " , status , credentials ,
2008-08-10 00:47:56 +00:00
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 ) ;
2007-10-19 18:01:00 +00:00
else
2008-08-10 00:47:56 +00:00
ast_cli ( a - > fd , " %s%s for %d seconds. \n " , status , credentials , ctimesec ) ;
2006-04-05 17:46:09 +00:00
2007-10-19 18:01:00 +00:00
return CLI_SUCCESS ;
2006-04-05 17:46:09 +00:00
} else {
2007-10-19 18:01:00 +00:00
return CLI_FAILURE ;
2006-04-05 17:46:09 +00:00
}
}
2006-08-11 15:05:19 +00:00
2006-08-21 02:11:39 +00:00
/* needs usecount semantics defined */
2009-03-18 02:21:23 +00:00
AST_MODULE_INFO ( ASTERISK_GPL_KEY , AST_MODFLAG_DEFAULT , " PostgreSQL RealTime Configuration Driver " ,
2006-08-21 02:11:39 +00:00
. load = load_module ,
. unload = unload_module ,
. reload = reload
) ;