2006-04-05 17:46:09 +00:00
/*
2012-10-14 21:44:27 +00:00
* Asterisk - - An open source telephony toolkit .
2006-04-05 17:46:09 +00:00
*
2017-09-19 11:34:28 +00:00
* Copyright ( C ) 1999 - 2017 , Digium , Inc .
2010-07-17 17:39:28 +00:00
*
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
*
2012-09-21 17:14:59 +00:00
* 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 >
2011-07-14 20:28:54 +00:00
< support_level > extended < / support_level >
2006-04-24 17:11:45 +00:00
* * */
2006-06-07 18:54:56 +00:00
# include "asterisk.h"
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 ) ;
2010-07-17 17:39:28 +00:00
AST_THREADSTORAGE ( semibuf_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)
2017-09-19 10:22:50 +00:00
# define USE_BACKSLASH_AS_STRING (version >= 90100 ? 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 ] = " " ;
2014-07-16 13:55:36 +00:00
static char dbappname [ MAX_DB_OPTION_SIZE ] = " " ;
2006-04-10 02:01:39 +00:00
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 { \
2010-07-17 17:39:28 +00:00
int len = strlen ( stringname ) ; \
struct ast_str * semi = ast_str_thread_get ( & semibuf_buf , len * 3 + 1 ) ; \
const char * chunk = stringname ; \
ast_str_reset ( semi ) ; \
for ( ; * chunk ; chunk + + ) { \
if ( strchr ( " ;^ " , * chunk ) ) { \
ast_str_append ( & semi , 0 , " ^%02hhX " , * chunk ) ; \
} else { \
ast_str_append ( & semi , 0 , " %c " , * chunk ) ; \
} \
} \
if ( ast_str_strlen ( semi ) > ( ast_str_size ( buffer ) - 1 ) / 2 ) { \
ast_str_make_space ( & buffer , ast_str_strlen ( semi ) * 2 + 1 ) ; \
2008-10-14 00:08:52 +00:00
} \
2010-07-17 17:39:28 +00:00
PQescapeStringConn ( pgsqlConn , ast_str_buffer ( buffer ) , ast_str_buffer ( semi ) , ast_str_size ( buffer ) , & 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 ) ;
}
2011-01-07 07:47:36 +00:00
/*! \brief Helper function for pgsql_exec. For running querys, use pgsql_exec()
*
* Connect if not currently connected . Run the given query .
*
* \ param database database name we are connected to ( used for error logging )
* \ param tablename table name we are connected to ( used for error logging )
* \ param sql sql query string to execute
* \ param result pointer for where to store the result handle
*
* \ return - 1 on fatal query error
* \ return - 2 on query failure that resulted in disconnection
* \ return 0 on success
*
2012-09-22 20:43:30 +00:00
* \ note see pgsql_exec for full example
2011-01-07 07:47:36 +00:00
*/
static int _pgsql_exec ( const char * database , const char * tablename , const char * sql , PGresult * * result )
{
ExecStatusType result_status ;
if ( ! pgsqlConn ) {
ast_debug ( 1 , " PostgreSQL connection not defined, connecting \n " ) ;
if ( pgsql_reconnect ( database ) ! = 1 ) {
ast_log ( LOG_NOTICE , " reconnect failed \n " ) ;
* result = NULL ;
return - 1 ;
}
ast_debug ( 1 , " PostgreSQL connection successful \n " ) ;
}
* result = PQexec ( pgsqlConn , sql ) ;
result_status = PQresultStatus ( * result ) ;
if ( result_status ! = PGRES_COMMAND_OK
& & result_status ! = PGRES_TUPLES_OK
& & result_status ! = PGRES_NONFATAL_ERROR ) {
ast_log ( LOG_ERROR , " PostgreSQL RealTime: Failed to query '%s@%s'. \n " , tablename , database ) ;
ast_log ( LOG_ERROR , " PostgreSQL RealTime: Query Failed: %s \n " , sql ) ;
ast_log ( LOG_ERROR , " PostgreSQL RealTime: Query Failed because: %s (%s) \n " ,
PQresultErrorMessage ( * result ) ,
PQresStatus ( result_status ) ) ;
/* we may have tried to run a command on a disconnected/disconnecting handle */
/* are we no longer connected to the database... if not try again */
if ( PQstatus ( pgsqlConn ) ! = CONNECTION_OK ) {
PQfinish ( pgsqlConn ) ;
pgsqlConn = NULL ;
return - 2 ;
}
/* connection still okay, which means the query is just plain bad */
return - 1 ;
}
ast_debug ( 1 , " PostgreSQL query successful: %s \n " , sql ) ;
return 0 ;
}
/*! \brief Do a postgres query, with reconnection support
*
* Connect if not currently connected . Run the given query
* and if we ' re disconnected afterwards , reconnect and query again .
*
* \ param database database name we are connected to ( used for error logging )
* \ param tablename table name we are connected to ( used for error logging )
* \ param sql sql query string to execute
* \ param result pointer for where to store the result handle
*
* \ return - 1 on query failure
* \ return 0 on success
*
2013-05-07 18:30:55 +00:00
* \ code
2011-01-07 07:47:36 +00:00
* int i , rows ;
* PGresult * result ;
* char * field_name , * field_type , * field_len , * field_notnull , * field_default ;
*
* pgsql_exec ( " db " , " table " , " SELECT 1 " , & result )
*
* rows = PQntuples ( result ) ;
* for ( i = 0 ; i < rows ; i + + ) {
* field_name = PQgetvalue ( result , i , 0 ) ;
* field_type = PQgetvalue ( result , i , 1 ) ;
* field_len = PQgetvalue ( result , i , 2 ) ;
* field_notnull = PQgetvalue ( result , i , 3 ) ;
* field_default = PQgetvalue ( result , i , 4 ) ;
* }
2013-05-07 18:30:55 +00:00
* \ endcode
2011-01-07 07:47:36 +00:00
*/
static int pgsql_exec ( const char * database , const char * tablename , const char * sql , PGresult * * result )
{
int attempts = 0 ;
int res ;
/* Try the query, note failure if any */
/* On first failure, reconnect and try again (_pgsql_exec handles reconnect) */
/* On second failure, treat as fatal query error */
while ( attempts + + < 2 ) {
ast_debug ( 1 , " PostgreSQL query attempt %d \n " , attempts ) ;
res = _pgsql_exec ( database , tablename , sql , result ) ;
if ( res = = 0 ) {
if ( attempts > 1 ) {
ast_log ( LOG_NOTICE , " PostgreSQL RealTime: Query finally succeeded: %s \n " , sql ) ;
}
return 0 ;
}
if ( res = = - 1 ) {
return - 1 ; /* Still connected to db, but could not process query (fatal error) */
}
/* res == -2 (query on a disconnected handle) */
ast_debug ( 1 , " PostgreSQL query attempt %d failed, trying again \n " , attempts ) ;
}
return - 1 ;
}
static struct tables * find_table ( const char * database , 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 ) ;
2014-01-24 21:46:54 +00:00
RAII_VAR ( PGresult * , result , NULL , PQclear ) ;
int exec_result ;
2008-06-05 19:07:27 +00:00
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 ;
}
}
2011-01-27 20:09:33 +00:00
if ( database = = NULL ) {
2017-02-23 20:48:53 +00:00
AST_LIST_UNLOCK ( & psql_tables ) ;
2011-01-27 20:09:33 +00:00
return NULL ;
}
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 ) {
2015-05-07 16:18:34 +00:00
char * schemaname , * tablename , * tmp_schemaname , * tmp_tablename ;
2009-10-06 19:31:39 +00:00
if ( strchr ( orig_tablename , ' . ' ) ) {
2015-05-07 16:18:34 +00:00
tmp_schemaname = ast_strdupa ( orig_tablename ) ;
tmp_tablename = strchr ( tmp_schemaname , ' . ' ) ;
* tmp_tablename + + = ' \0 ' ;
2009-10-06 19:31:39 +00:00
} else {
2015-05-07 16:18:34 +00:00
tmp_schemaname = " " ;
tmp_tablename = ast_strdupa ( orig_tablename ) ;
2009-10-06 19:31:39 +00:00
}
2015-05-07 16:18:34 +00:00
tablename = ast_alloca ( strlen ( tmp_tablename ) * 2 + 1 ) ;
PQescapeStringConn ( pgsqlConn , tablename , tmp_tablename , strlen ( tmp_tablename ) , NULL ) ;
schemaname = ast_alloca ( strlen ( tmp_schemaname ) * 2 + 1 ) ;
PQescapeStringConn ( pgsqlConn , schemaname , tmp_schemaname , strlen ( tmp_schemaname ) , NULL ) ;
2009-10-06 19:31:39 +00:00
2019-10-08 18:40:30 +00:00
ast_str_set ( & sql , 0 , " SELECT a.attname, t.typname, a.attlen, a.attnotnull, pg_catalog.pg_get_expr(d.adbin, d.adrelid) 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 " ,
2009-10-06 19:31:39 +00:00
tablename ,
ast_strlen_zero ( schemaname ) ? " " : " ' " , ast_strlen_zero ( schemaname ) ? " current_schema() " : schemaname , ast_strlen_zero ( schemaname ) ? " " : " ' " ) ;
} else {
2015-05-07 16:18:34 +00:00
char * tablename ;
tablename = ast_alloca ( strlen ( orig_tablename ) * 2 + 1 ) ;
PQescapeStringConn ( pgsqlConn , tablename , orig_tablename , strlen ( orig_tablename ) , NULL ) ;
2009-10-06 19:31:39 +00:00
2015-05-07 16:18:34 +00:00
ast_str_set ( & sql , 0 , " SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod FROM pg_class c, pg_type t, pg_attribute a LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid AND d.adnum = a.attnum WHERE c.oid = a.attrelid AND a.atttypid = t.oid AND (a.attnum > 0) AND c.relname = '%s' ORDER BY c.relname, attnum " , tablename ) ;
2009-10-06 19:31:39 +00:00
}
2015-10-14 19:15:53 +00:00
ast_mutex_lock ( & pgsql_lock ) ;
2011-01-07 07:47:36 +00:00
exec_result = pgsql_exec ( database , orig_tablename , ast_str_buffer ( sql ) , & result ) ;
2015-10-14 19:15:53 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2008-06-05 19:07:27 +00:00
ast_debug ( 1 , " Query of table structure complete. Now retrieving results. \n " ) ;
2011-01-07 07:47:36 +00:00
if ( exec_result ! = 0 ) {
ast_log ( LOG_ERROR , " Failed to query database columns for table %s \n " , orig_tablename ) ;
2008-06-05 19:07:27 +00:00
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 ) ;
}
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 ;
}
2017-02-16 14:38:06 +00:00
# define IS_SQL_LIKE_CLAUSE(x) ((x) && ast_ends_with(x, " LIKE"))
2017-09-19 10:22:50 +00:00
# define ESCAPE_CLAUSE (USE_BACKSLASH_AS_STRING ? " ESCAPE '\\'" : " ESCAPE '\\\\'")
2017-02-16 14:38:06 +00:00
2013-04-27 12:01:29 +00:00
static struct ast_variable * realtime_pgsql ( const char * database , const char * tablename , const struct ast_variable * fields )
2006-04-05 17:46:09 +00:00
{
2014-01-24 21:46:54 +00:00
RAII_VAR ( PGresult * , result , NULL , PQclear ) ;
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 ;
2017-02-16 14:38:06 +00:00
char * escape = " " ;
2013-04-27 12:01:29 +00:00
const struct ast_variable * field = fields ;
2006-04-10 02:01:39 +00:00
struct ast_variable * var = NULL , * prev = NULL ;
2006-04-05 17:46:09 +00:00
2012-02-13 17:25:41 +00:00
/*
* Ignore database from the extconfig . conf since it was
* configured by res_pgsql . conf .
*/
database = dbname ;
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 ;
}
2017-02-23 20:48:53 +00:00
/*
* Must connect to the server before anything else as ESCAPE_STRING ( )
* uses pgsqlConn
*/
ast_mutex_lock ( & pgsql_lock ) ;
if ( ! pgsql_reconnect ( database ) ) {
ast_mutex_unlock ( & pgsql_lock ) ;
return NULL ;
}
2006-04-05 17:46:09 +00:00
/* Get the first parameter and first value in our list of passed paramater/value pairs */
2013-04-27 12:01:29 +00:00
if ( ! field ) {
2006-04-10 02:01:39 +00:00
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
}
2017-02-23 20:48:53 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
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 */
2017-02-16 14:38:06 +00:00
if ( ! strchr ( field - > name , ' ' ) ) {
op = " = " ;
} else {
op = " " ;
if ( IS_SQL_LIKE_CLAUSE ( field - > name ) ) {
escape = ESCAPE_CLAUSE ;
}
}
2006-04-05 17:46:09 +00:00
2013-04-27 12:01:29 +00:00
ESCAPE_STRING ( escapebuf , field - > value ) ;
2008-10-14 00:08:52 +00:00
if ( pgresult ) {
2013-04-27 12:01:29 +00:00
ast_log ( LOG_ERROR , " PostgreSQL RealTime: detected invalid input: '%s' \n " , field - > value ) ;
2017-02-23 20:48:53 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2007-11-29 19:35:49 +00:00
return NULL ;
}
2017-02-16 14:38:06 +00:00
ast_str_set ( & sql , 0 , " SELECT * FROM %s WHERE %s%s '%s'%s " , tablename , field - > name , op , ast_str_buffer ( escapebuf ) , escape ) ;
2013-04-27 12:01:29 +00:00
while ( ( field = field - > next ) ) {
2017-02-16 14:38:06 +00:00
escape = " " ;
if ( ! strchr ( field - > name , ' ' ) ) {
2006-04-10 02:01:39 +00:00
op = " = " ;
2017-02-16 14:38:06 +00:00
} else {
2006-04-10 02:01:39 +00:00
op = " " ;
2017-02-16 14:38:06 +00:00
if ( IS_SQL_LIKE_CLAUSE ( field - > name ) ) {
escape = ESCAPE_CLAUSE ;
}
}
2007-11-29 19:35:49 +00:00
2013-04-27 12:01:29 +00:00
ESCAPE_STRING ( escapebuf , field - > value ) ;
2008-10-14 00:08:52 +00:00
if ( pgresult ) {
2013-04-27 12:01:29 +00:00
ast_log ( LOG_ERROR , " PostgreSQL RealTime: detected invalid input: '%s' \n " , field - > value ) ;
2017-02-23 20:48:53 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2007-11-29 19:35:49 +00:00
return NULL ;
}
2017-02-16 14:38:06 +00:00
ast_str_append ( & sql , 0 , " AND %s%s '%s'%s " , field - > name , op , ast_str_buffer ( escapebuf ) , escape ) ;
2006-04-05 17:46:09 +00:00
}
/* We now have our complete statement; Lets connect to the server and execute it. */
2011-01-07 07:47:36 +00:00
if ( pgsql_exec ( database , tablename , ast_str_buffer ( sql ) , & result ) ! = 0 ) {
2006-04-05 17:46:09 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
return NULL ;
2011-01-07 07:47:36 +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 * ) ) ) ) {
2011-06-15 16:19:38 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2006-04-10 02:01:39 +00:00
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 , " ; " ) ;
2010-07-17 17:39:28 +00:00
if ( chunk & & ! ast_strlen_zero ( ast_realtime_decode_chunk ( 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
}
2011-06-15 16:19:38 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2006-04-05 17:46:09 +00:00
return var ;
}
2013-04-27 12:01:29 +00:00
static struct ast_config * realtime_multi_pgsql ( const char * database , const char * table , const struct ast_variable * fields )
2006-04-05 17:46:09 +00:00
{
2014-01-24 21:46:54 +00:00
RAII_VAR ( PGresult * , result , NULL , PQclear ) ;
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 ) ;
2013-04-27 12:01:29 +00:00
const struct ast_variable * field = fields ;
2006-04-05 17:46:09 +00:00
const char * initfield = NULL ;
char * stringp ;
char * chunk ;
char * op ;
2017-02-16 14:38:06 +00:00
char * escape = " " ;
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 ;
2012-02-13 17:25:41 +00:00
/*
* Ignore database from the extconfig . conf since it was
* configured by res_pgsql . conf .
*/
database = dbname ;
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 ;
2017-02-23 20:48:53 +00:00
/*
* Must connect to the server before anything else as ESCAPE_STRING ( )
* uses pgsqlConn
*/
ast_mutex_lock ( & pgsql_lock ) ;
if ( ! pgsql_reconnect ( database ) ) {
ast_mutex_unlock ( & pgsql_lock ) ;
return NULL ;
}
2006-04-05 17:46:09 +00:00
/* Get the first parameter and first value in our list of passed paramater/value pairs */
2013-04-27 12:01:29 +00:00
if ( ! field ) {
2006-04-10 02:01:39 +00:00
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
}
2017-02-23 20:48:53 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2011-08-22 18:33:27 +00:00
ast_config_destroy ( cfg ) ;
2006-04-05 17:46:09 +00:00
return NULL ;
}
2013-04-27 12:01:29 +00:00
initfield = ast_strdupa ( field - > name ) ;
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 */
2017-02-16 14:38:06 +00:00
if ( ! strchr ( field - > name , ' ' ) ) {
2006-04-10 02:01:39 +00:00
op = " = " ;
2017-02-16 14:38:06 +00:00
escape = " " ;
} else {
2006-04-10 02:01:39 +00:00
op = " " ;
2017-02-16 14:38:06 +00:00
if ( IS_SQL_LIKE_CLAUSE ( field - > name ) ) {
escape = ESCAPE_CLAUSE ;
}
}
2006-04-05 17:46:09 +00:00
2013-04-27 12:01:29 +00:00
ESCAPE_STRING ( escapebuf , field - > value ) ;
2008-10-14 00:08:52 +00:00
if ( pgresult ) {
2013-04-27 12:01:29 +00:00
ast_log ( LOG_ERROR , " PostgreSQL RealTime: detected invalid input: '%s' \n " , field - > value ) ;
2017-02-23 20:48:53 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2011-08-22 18:33:27 +00:00
ast_config_destroy ( cfg ) ;
2007-11-29 19:35:49 +00:00
return NULL ;
}
2017-02-16 14:38:06 +00:00
ast_str_set ( & sql , 0 , " SELECT * FROM %s WHERE %s%s '%s'%s " , table , field - > name , op , ast_str_buffer ( escapebuf ) , escape ) ;
2013-04-27 12:01:29 +00:00
while ( ( field = field - > next ) ) {
2017-02-16 14:38:06 +00:00
escape = " " ;
if ( ! strchr ( field - > name , ' ' ) ) {
2006-04-10 02:01:39 +00:00
op = " = " ;
2017-02-16 14:38:06 +00:00
escape = " " ;
} else {
2006-04-10 02:01:39 +00:00
op = " " ;
2017-02-16 14:38:06 +00:00
if ( IS_SQL_LIKE_CLAUSE ( field - > name ) ) {
escape = ESCAPE_CLAUSE ;
}
}
2007-11-29 19:35:49 +00:00
2013-04-27 12:01:29 +00:00
ESCAPE_STRING ( escapebuf , field - > value ) ;
2008-10-14 00:08:52 +00:00
if ( pgresult ) {
2013-04-27 12:01:29 +00:00
ast_log ( LOG_ERROR , " PostgreSQL RealTime: detected invalid input: '%s' \n " , field - > value ) ;
2017-02-23 20:48:53 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2011-08-22 18:33:27 +00:00
ast_config_destroy ( cfg ) ;
2007-11-29 19:35:49 +00:00
return NULL ;
}
2017-02-16 14:38:06 +00:00
ast_str_append ( & sql , 0 , " AND %s%s '%s'%s " , field - > name , op , ast_str_buffer ( escapebuf ) , escape ) ;
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
}
/* We now have our complete statement; Lets connect to the server and execute it. */
2011-08-22 18:33:27 +00:00
if ( pgsql_exec ( database , table , ast_str_buffer ( sql ) , & result ) ! = 0 ) {
2006-04-05 17:46:09 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2011-08-22 18:33:27 +00:00
ast_config_destroy ( cfg ) ;
2011-06-15 16:19:38 +00:00
return NULL ;
} 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 %s@%s. Check debug for more info. \n " , table , database ) ;
ast_debug ( 1 , " PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ) ) ;
ast_debug ( 1 , " PostgreSQL RealTime: Query Failed because: %s (%s) \n " ,
PQresultErrorMessage ( result ) , PQresStatus ( result_status ) ) ;
ast_mutex_unlock ( & pgsql_lock ) ;
2011-08-22 18:33:27 +00:00
ast_config_destroy ( cfg ) ;
2011-06-15 16:19:38 +00:00
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 * ) ) ) ) {
2011-06-15 16:19:38 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2011-08-22 18:33:27 +00:00
ast_config_destroy ( cfg ) ;
2006-04-10 02:01:39 +00:00
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 ;
2017-02-21 14:56:54 +00:00
cat = ast_category_new_anonymous ( ) ;
if ( ! cat ) {
2006-04-10 02:01:39 +00:00
continue ;
2017-02-21 14:56:54 +00:00
}
2006-04-10 02:01:39 +00:00
for ( i = 0 ; i < numFields ; i + + ) {
stringp = PQgetvalue ( result , rowIndex , i ) ;
while ( stringp ) {
chunk = strsep ( & stringp , " ; " ) ;
2010-07-17 17:39:28 +00:00
if ( chunk & & ! ast_strlen_zero ( ast_realtime_decode_chunk ( 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
}
2011-06-15 16:19:38 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2006-04-05 17:46:09 +00:00
return cfg ;
}
2008-06-05 19:07:27 +00:00
static int update_pgsql ( const char * database , const char * tablename , const char * keyfield ,
2013-04-27 12:01:29 +00:00
const char * lookup , const struct ast_variable * fields )
2006-04-05 17:46:09 +00:00
{
2014-01-24 21:46:54 +00:00
RAII_VAR ( PGresult * , result , NULL , PQclear ) ;
2008-10-14 00:08:52 +00:00
int numrows = 0 , pgresult ;
2013-04-27 12:01:29 +00:00
const struct ast_variable * field = fields ;
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
2012-02-13 17:25:41 +00:00
/*
* Ignore database from the extconfig . conf since it was
* configured by res_pgsql . conf .
*/
database = dbname ;
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 ;
}
2011-01-07 07:47:36 +00:00
if ( ! ( table = find_table ( database , tablename ) ) ) {
2008-06-05 19:07:27 +00:00
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
}
2017-02-23 20:48:53 +00:00
/*
* Must connect to the server before anything else as ESCAPE_STRING ( )
* uses pgsqlConn
*/
ast_mutex_lock ( & pgsql_lock ) ;
if ( ! pgsql_reconnect ( database ) ) {
ast_mutex_unlock ( & pgsql_lock ) ;
2017-02-28 15:41:45 +00:00
release_table ( table ) ;
2017-02-23 20:48:53 +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 */
2013-04-27 12:01:29 +00:00
if ( ! field ) {
2006-04-10 02:01:39 +00:00
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
}
2017-02-23 20:48:53 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
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 ) {
2013-04-27 12:01:29 +00:00
if ( strcmp ( column - > name , field - > name ) = = 0 ) {
2008-06-05 19:07:27 +00:00
break ;
}
}
if ( ! column ) {
2013-04-27 12:01:29 +00:00
ast_log ( LOG_ERROR , " PostgreSQL RealTime: Updating on column '%s', but that column does not exist within the table '%s'! \n " , field - > name , tablename ) ;
2017-02-23 20:48:53 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
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 */
2013-04-27 12:01:29 +00:00
ESCAPE_STRING ( escapebuf , field - > value ) ;
2008-10-14 00:08:52 +00:00
if ( pgresult ) {
2013-04-27 12:01:29 +00:00
ast_log ( LOG_ERROR , " PostgreSQL RealTime: detected invalid input: '%s' \n " , field - > value ) ;
2017-02-23 20:48:53 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2008-10-14 00:08:52 +00:00
release_table ( table ) ;
2007-11-29 19:35:49 +00:00
return - 1 ;
}
2013-04-27 12:01:29 +00:00
ast_str_set ( & sql , 0 , " UPDATE %s SET %s = '%s' " , tablename , field - > name , ast_str_buffer ( escapebuf ) ) ;
2007-11-29 19:35:49 +00:00
2013-04-27 12:01:29 +00:00
while ( ( field = field - > next ) ) {
if ( ! find_column ( table , field - > name ) ) {
ast_log ( LOG_NOTICE , " Attempted to update column '%s' in table '%s', but column does not exist! \n " , field - > name , tablename ) ;
2008-06-05 19:07:27 +00:00
continue ;
}
2013-04-27 12:01:29 +00:00
ESCAPE_STRING ( escapebuf , field - > value ) ;
2008-10-14 00:08:52 +00:00
if ( pgresult ) {
2013-04-27 12:01:29 +00:00
ast_log ( LOG_ERROR , " PostgreSQL RealTime: detected invalid input: '%s' \n " , field - > value ) ;
2017-02-23 20:48:53 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2008-10-14 00:08:52 +00:00
release_table ( table ) ;
2007-11-29 19:35:49 +00:00
return - 1 ;
}
2013-04-27 12:01:29 +00:00
ast_str_append ( & sql , 0 , " , %s = '%s' " , field - > name , ast_str_buffer ( escapebuf ) ) ;
2006-04-05 17:46:09 +00:00
}
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 ) {
2011-01-07 07:47:36 +00:00
ast_log ( LOG_ERROR , " PostgreSQL RealTime: detected invalid input: '%s' \n " , lookup ) ;
2017-02-23 20:48:53 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2007-11-29 19:35:49 +00:00
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. */
2011-01-07 07:47:36 +00:00
if ( pgsql_exec ( database , tablename , ast_str_buffer ( sql ) , & result ) ! = 0 ) {
2006-04-05 17:46:09 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
return - 1 ;
2011-06-15 16:19:38 +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 ,
" PostgreSQL RealTime: Failed to query database. Check debug for more info. \n " ) ;
ast_debug ( 1 , " PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ) ) ;
ast_debug ( 1 , " PostgreSQL RealTime: Query Failed because: %s (%s) \n " ,
PQresultErrorMessage ( result ) , PQresStatus ( result_status ) ) ;
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 ;
}
2013-04-27 12:01:29 +00:00
static int update2_pgsql ( const char * database , const char * tablename , const struct ast_variable * lookup_fields , const struct ast_variable * update_fields )
2008-10-14 00:08:52 +00:00
{
2014-01-24 21:46:54 +00:00
RAII_VAR ( PGresult * , result , NULL , PQclear ) ;
2008-10-14 00:08:52 +00:00
int numrows = 0 , pgresult , first = 1 ;
struct ast_str * escapebuf = ast_str_thread_get ( & escapebuf_buf , 16 ) ;
2013-04-27 12:01:29 +00:00
const struct ast_variable * field ;
2008-10-14 00:08:52 +00:00
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 ;
2012-02-13 17:25:41 +00:00
/*
* Ignore database from the extconfig . conf since it was
* configured by res_pgsql . conf .
*/
database = dbname ;
2008-10-14 00:08:52 +00:00
if ( ! tablename ) {
ast_log ( LOG_WARNING , " PostgreSQL RealTime: No table specified. \n " ) ;
return - 1 ;
}
if ( ! escapebuf | | ! sql | | ! where ) {
/* Memory error, already handled */
return - 1 ;
}
2011-01-07 07:47:36 +00:00
if ( ! ( table = find_table ( database , tablename ) ) ) {
2008-10-14 00:08:52 +00:00
ast_log ( LOG_ERROR , " Table '%s' does not exist!! \n " , tablename ) ;
return - 1 ;
}
2017-02-23 20:48:53 +00:00
/*
* Must connect to the server before anything else as ESCAPE_STRING ( )
* uses pgsqlConn
*/
ast_mutex_lock ( & pgsql_lock ) ;
if ( ! pgsql_reconnect ( database ) ) {
ast_mutex_unlock ( & pgsql_lock ) ;
2017-02-28 15:41:45 +00:00
release_table ( table ) ;
2017-02-23 20:48:53 +00:00
return - 1 ;
}
2014-02-10 16:49:40 +00:00
ast_str_set ( & sql , 0 , " UPDATE %s SET " , tablename ) ;
ast_str_set ( & where , 0 , " WHERE " ) ;
2008-10-14 00:08:52 +00:00
2013-04-27 12:01:29 +00:00
for ( field = lookup_fields ; field ; field = field - > next ) {
if ( ! find_column ( table , field - > name ) ) {
ast_log ( LOG_ERROR , " Attempted to update based on criteria column '%s' (%s@%s), but that column does not exist! \n " , field - > name , tablename , database ) ;
2017-02-23 20:48:53 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2008-10-14 00:08:52 +00:00
release_table ( table ) ;
return - 1 ;
}
2010-07-17 17:39:28 +00:00
2013-04-27 12:01:29 +00:00
ESCAPE_STRING ( escapebuf , field - > value ) ;
2008-10-14 00:08:52 +00:00
if ( pgresult ) {
2013-04-27 12:01:29 +00:00
ast_log ( LOG_ERROR , " PostgreSQL RealTime: detected invalid input: '%s' \n " , field - > value ) ;
2017-02-23 20:48:53 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2008-10-14 00:08:52 +00:00
release_table ( table ) ;
return - 1 ;
}
2013-04-27 12:01:29 +00:00
ast_str_append ( & where , 0 , " %s %s='%s' " , first ? " " : " AND " , field - > name , 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 ;
}
2017-02-23 20:48:53 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2008-10-14 00:08:52 +00:00
release_table ( table ) ;
return - 1 ;
}
/* Now retrieve the columns to update */
first = 1 ;
2013-04-27 12:01:29 +00:00
for ( field = update_fields ; field ; field = field - > next ) {
2008-10-14 00:08:52 +00:00
/* If the column is not within the table, then skip it */
2013-04-27 12:01:29 +00:00
if ( ! find_column ( table , field - > name ) ) {
ast_log ( LOG_NOTICE , " Attempted to update column '%s' in table '%s@%s', but column does not exist! \n " , field - > name , tablename , database ) ;
2008-10-14 00:08:52 +00:00
continue ;
}
2013-04-27 12:01:29 +00:00
ESCAPE_STRING ( escapebuf , field - > value ) ;
2008-10-14 00:08:52 +00:00
if ( pgresult ) {
2013-04-27 12:01:29 +00:00
ast_log ( LOG_ERROR , " PostgreSQL RealTime: detected invalid input: '%s' \n " , field - > value ) ;
2017-02-23 20:48:53 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2008-10-14 00:08:52 +00:00
release_table ( table ) ;
return - 1 ;
}
2013-04-27 12:01:29 +00:00
ast_str_append ( & sql , 0 , " %s %s='%s' " , first ? " " : " , " , field - > name , ast_str_buffer ( escapebuf ) ) ;
2014-02-10 16:49:40 +00:00
first = 0 ;
2008-10-14 00:08:52 +00:00
}
release_table ( table ) ;
2014-02-10 16:49:40 +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
2017-02-23 20:48:53 +00:00
/* We now have our complete statement; Lets connect to the server and execute it. */
2011-01-07 07:47:36 +00:00
if ( pgsql_exec ( database , tablename , ast_str_buffer ( sql ) , & result ) ! = 0 ) {
2008-10-14 00:08:52 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2011-01-07 07:47:36 +00:00
return - 1 ;
}
2008-10-14 00:08:52 +00:00
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
2013-04-27 12:01:29 +00:00
static int store_pgsql ( const char * database , const char * table , const struct ast_variable * fields )
2007-08-17 13:40:11 +00:00
{
2014-01-24 21:46:54 +00:00
RAII_VAR ( PGresult * , result , NULL , PQclear ) ;
Return the number of rows affected by a SQL insert, rather than an object ID.
The realtime API specifies that the store callback is supposed to return the number
of rows affected. res_config_pgsql was instead returning an Oid cast as an int, which
during any nominal execution would be cast to 0. Returning 0 when more than 0 rows were
inserted causes problems to the function's callers.
To give an idea of how strange code can be, this is the necessary code change to fix
a device state issue reported against chan_pjsip in Asterisk 12+. The issue was that
the registrar would attempt to insert contacts into the database. Because of the 0
return from res_config_pgsql, the registrar would think that the contact was not successfully
inserted, even though it actually was. As such, even though the contact was query-able
and it was possible to call the endpoint, Asterisk would "think" the endpoint was unregistered,
meaning it would report the device state as UNAVAILABLE instead of NOT_INUSE.
The necessary fix applies to all versions of Asterisk, so even though the bug reported
only applies to Asterisk 12+, the code correction is being inserted into 1.8+.
Closes issue ASTERISK-23707
Reported by Mark Michelson
........
Merged revisions 413224 from http://svn.asterisk.org/svn/asterisk/branches/1.8
........
Merged revisions 413225 from http://svn.asterisk.org/svn/asterisk/branches/11
........
Merged revisions 413226 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@413227 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-05-02 20:07:08 +00:00
int numrows ;
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 ;
2013-04-27 12:01:29 +00:00
const struct ast_variable * field = fields ;
2007-08-17 13:40:11 +00:00
2012-02-13 17:25:41 +00:00
/*
* Ignore database from the extconfig . conf since it was
* configured by res_pgsql . conf .
*/
database = dbname ;
2007-08-17 13:40:11 +00:00
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 ;
}
2017-02-23 20:48:53 +00:00
/*
* Must connect to the server before anything else as ESCAPE_STRING ( )
* uses pgsqlConn
*/
ast_mutex_lock ( & pgsql_lock ) ;
if ( ! pgsql_reconnect ( database ) ) {
ast_mutex_unlock ( & pgsql_lock ) ;
return - 1 ;
}
2007-08-17 13:40:11 +00:00
/* Get the first parameter and first value in our list of passed paramater/value pairs */
2013-04-27 12:01:29 +00:00
if ( ! field ) {
2007-08-17 13:40:11 +00:00
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
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 */
2013-04-27 12:01:29 +00:00
ESCAPE_STRING ( buf , field - > name ) ;
2008-12-13 08:36:35 +00:00
ast_str_set ( & sql1 , 0 , " INSERT INTO %s (%s " , table , ast_str_buffer ( buf ) ) ;
2013-04-27 12:01:29 +00:00
ESCAPE_STRING ( buf , field - > value ) ;
2008-12-13 08:36:35 +00:00
ast_str_set ( & sql2 , 0 , " ) VALUES ('%s' " , ast_str_buffer ( buf ) ) ;
2013-04-27 12:01:29 +00:00
while ( ( field = field - > next ) ) {
ESCAPE_STRING ( buf , field - > name ) ;
2008-12-13 08:36:35 +00:00
ast_str_append ( & sql1 , 0 , " , %s " , ast_str_buffer ( buf ) ) ;
2013-04-27 12:01:29 +00:00
ESCAPE_STRING ( buf , field - > value ) ;
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
}
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
2017-02-23 20:48:53 +00:00
/* We now have our complete statement; Lets connect to the server and execute it. */
2011-01-07 07:47:36 +00:00
if ( pgsql_exec ( database , table , ast_str_buffer ( sql1 ) , & result ) ! = 0 ) {
2007-08-17 13:40:11 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2011-01-07 07:47:36 +00:00
return - 1 ;
}
2007-08-17 13:40:11 +00:00
Return the number of rows affected by a SQL insert, rather than an object ID.
The realtime API specifies that the store callback is supposed to return the number
of rows affected. res_config_pgsql was instead returning an Oid cast as an int, which
during any nominal execution would be cast to 0. Returning 0 when more than 0 rows were
inserted causes problems to the function's callers.
To give an idea of how strange code can be, this is the necessary code change to fix
a device state issue reported against chan_pjsip in Asterisk 12+. The issue was that
the registrar would attempt to insert contacts into the database. Because of the 0
return from res_config_pgsql, the registrar would think that the contact was not successfully
inserted, even though it actually was. As such, even though the contact was query-able
and it was possible to call the endpoint, Asterisk would "think" the endpoint was unregistered,
meaning it would report the device state as UNAVAILABLE instead of NOT_INUSE.
The necessary fix applies to all versions of Asterisk, so even though the bug reported
only applies to Asterisk 12+, the code correction is being inserted into 1.8+.
Closes issue ASTERISK-23707
Reported by Mark Michelson
........
Merged revisions 413224 from http://svn.asterisk.org/svn/asterisk/branches/1.8
........
Merged revisions 413225 from http://svn.asterisk.org/svn/asterisk/branches/11
........
Merged revisions 413226 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@413227 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-05-02 20:07:08 +00:00
numrows = atoi ( PQcmdTuples ( result ) ) ;
2007-08-17 13:40:11 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2017-09-19 11:34:28 +00:00
ast_debug ( 1 , " PostgreSQL RealTime: row inserted on table: %s. \n " , 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 . )
*/
Return the number of rows affected by a SQL insert, rather than an object ID.
The realtime API specifies that the store callback is supposed to return the number
of rows affected. res_config_pgsql was instead returning an Oid cast as an int, which
during any nominal execution would be cast to 0. Returning 0 when more than 0 rows were
inserted causes problems to the function's callers.
To give an idea of how strange code can be, this is the necessary code change to fix
a device state issue reported against chan_pjsip in Asterisk 12+. The issue was that
the registrar would attempt to insert contacts into the database. Because of the 0
return from res_config_pgsql, the registrar would think that the contact was not successfully
inserted, even though it actually was. As such, even though the contact was query-able
and it was possible to call the endpoint, Asterisk would "think" the endpoint was unregistered,
meaning it would report the device state as UNAVAILABLE instead of NOT_INUSE.
The necessary fix applies to all versions of Asterisk, so even though the bug reported
only applies to Asterisk 12+, the code correction is being inserted into 1.8+.
Closes issue ASTERISK-23707
Reported by Mark Michelson
........
Merged revisions 413224 from http://svn.asterisk.org/svn/asterisk/branches/1.8
........
Merged revisions 413225 from http://svn.asterisk.org/svn/asterisk/branches/11
........
Merged revisions 413226 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@413227 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-05-02 20:07:08 +00:00
if ( numrows > = 0 ) {
return numrows ;
}
2007-08-17 13:40:11 +00:00
return - 1 ;
}
2013-04-27 12:01:29 +00:00
static int destroy_pgsql ( const char * database , const char * table , const char * keyfield , const char * lookup , const struct ast_variable * fields )
2007-08-17 13:40:11 +00:00
{
2014-01-24 21:46:54 +00:00
RAII_VAR ( PGresult * , result , NULL , PQclear ) ;
2007-08-17 13:40:11 +00:00
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 ) ;
2013-04-27 12:01:29 +00:00
const struct ast_variable * field ;
2007-08-17 13:40:11 +00:00
2012-02-13 17:25:41 +00:00
/*
* Ignore database from the extconfig . conf since it was
* configured by res_pgsql . conf .
*/
database = dbname ;
2007-08-17 13:40:11 +00:00
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 ;
}
2017-02-23 20:48:53 +00:00
/*
* Must connect to the server before anything else as ESCAPE_STRING ( )
* uses pgsqlConn
*/
ast_mutex_lock ( & pgsql_lock ) ;
if ( ! pgsql_reconnect ( database ) ) {
ast_mutex_unlock ( & pgsql_lock ) ;
return - 1 ;
}
2007-08-17 13:40:11 +00:00
/* Get the first parameter and first value in our list of passed paramater/value pairs */
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 ;
2017-02-23 20:48:53 +00:00
}
2007-08-17 13:40:11 +00:00
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 ) ) ;
2013-04-27 12:01:29 +00:00
for ( field = fields ; field ; field = field - > next ) {
ESCAPE_STRING ( buf1 , field - > name ) ;
ESCAPE_STRING ( buf2 , field - > value ) ;
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
}
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
2017-02-23 20:48:53 +00:00
/* We now have our complete statement; Lets connect to the server and execute it. */
if ( pgsql_exec ( database , table , ast_str_buffer ( sql ) , & result ) ! = 0 ) {
2007-08-17 13:40:11 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2017-02-23 20:48:53 +00:00
return - 1 ;
}
2007-08-17 13:40:11 +00:00
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
{
2014-01-24 21:46:54 +00:00
RAII_VAR ( PGresult * , result , NULL , PQclear ) ;
2006-04-05 17:46:09 +00:00
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 ) ;
2012-02-13 17:25:41 +00:00
char last [ 80 ] ;
2006-04-05 17:46:09 +00:00
int last_cat_metric = 0 ;
last [ 0 ] = ' \0 ' ;
2012-02-13 17:25:41 +00:00
/*
* Ignore database from the extconfig . conf since it is
* configured by res_pgsql . conf .
*/
database = dbname ;
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 "
2011-01-07 07:47:36 +00:00
" WHERE filename='%s' and commented=0 "
2008-10-14 00:08:52 +00:00
" 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
ast_mutex_lock ( & pgsql_lock ) ;
2011-01-07 07:47:36 +00:00
/* We now have our complete statement; Lets connect to the server and execute it. */
if ( pgsql_exec ( database , table , ast_str_buffer ( sql ) , & result ) ! = 0 ) {
2006-04-05 17:46:09 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2011-01-07 07:47:36 +00:00
return NULL ;
}
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
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 ) ) {
2017-02-21 14:56:54 +00:00
cur_cat = ast_category_new_dynamic ( field_category ) ;
if ( ! cur_cat ) {
2006-04-05 17:46:09 +00:00
break ;
2017-02-21 14:56:54 +00:00
}
2012-02-13 17:25:41 +00:00
ast_copy_string ( last , field_category , sizeof ( last ) ) ;
2006-04-05 17:46:09 +00:00
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
}
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 ;
2012-02-13 17:25:41 +00:00
struct tables * table ;
2008-06-05 19:07:27 +00:00
char * elm ;
2018-01-12 09:50:32 +00:00
int type , res = 0 ;
unsigned int size ;
2008-06-05 19:07:27 +00:00
2012-02-13 17:25:41 +00:00
/*
* Ignore database from the extconfig . conf since it was
* configured by res_pgsql . conf .
*/
database = dbname ;
table = find_table ( database , tablename ) ;
2008-06-05 19:07:27 +00:00
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 ) ;
2018-01-12 09:50:32 +00:00
size = va_arg ( ap , unsigned int ) ;
2008-06-05 19:07:27 +00:00
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 ;
}
2010-09-01 18:19:12 +00:00
} else if ( strncmp ( column - > type , " float " , 5 ) = = 0 ) {
if ( ! ast_rq_is_int ( type ) & & type ! = RQ_FLOAT ) {
ast_log ( LOG_WARNING , " Column %s cannot be a %s \n " , column - > name , column - > type ) ;
res = - 1 ;
}
} else if ( strncmp ( column - > type , " timestamp " , 9 ) = = 0 ) {
if ( type ! = RQ_DATETIME & & type ! = RQ_DATE ) {
ast_log ( LOG_WARNING , " Column %s cannot be a %s \n " , column - > name , column - > type ) ;
res = - 1 ;
}
2008-06-05 19:07:27 +00:00
} 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 ) ;
2017-03-01 13:23:55 +00:00
res = - 1 ;
2008-06-05 19:07:27 +00:00
} else {
2008-06-09 22:51:59 +00:00
struct ast_str * sql = ast_str_create ( 100 ) ;
2018-01-12 09:50:32 +00:00
char fieldtype [ 10 ] ;
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 . */
2018-01-12 09:50:32 +00:00
snprintf ( fieldtype , sizeof ( fieldtype ) , " CHAR(%u) " ,
2008-07-31 20:10:39 +00:00
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 ) ;
2011-01-07 07:47:36 +00:00
ast_debug ( 1 , " About to run ALTER query on table '%s' to add column '%s' \n " , tablename , elm ) ;
if ( pgsql_exec ( database , tablename , ast_str_buffer ( sql ) , & result ) ! = 0 ) {
2017-02-23 20:48:53 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2017-02-28 15:41:45 +00:00
release_table ( table ) ;
2011-01-07 07:47:36 +00:00
return - 1 ;
}
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 ;
2012-02-13 17:25:41 +00:00
/*
* Ignore database from the extconfig . conf since it was
* configured by res_pgsql . conf .
*/
database = dbname ;
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 ) ;
2014-05-28 22:54:12 +00:00
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 ) ;
2015-10-14 19:15:53 +00:00
/* Unlock so something else can destroy the lock. */
ast_mutex_unlock ( & pgsql_lock ) ;
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
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 ) {
2016-02-10 02:13:07 +00:00
if ( is_reload & & pgsqlConn & & PQstatus ( pgsqlConn ) ! = CONNECTION_OK ) {
ast_log ( LOG_WARNING , " PostgreSQL RealTime: Not connected \n " ) ;
}
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 ) ;
2017-02-23 20:48:53 +00:00
/* XXX: Why would we do this before we're ready to establish a new connection? */
2007-08-16 21:09:46 +00:00
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
2014-07-16 13:55:36 +00:00
if ( ! ( s = ast_variable_retrieve ( config , " general " , " dbappname " ) ) ) {
dbappname [ 0 ] = ' \0 ' ;
} else {
ast_copy_string ( dbappname , s , sizeof ( dbappname ) ) ;
}
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 ) ;
2018-03-07 20:36:17 +00:00
if ( DEBUG_ATLEAST ( 1 ) ) {
2007-09-21 14:40:10 +00:00
if ( ! ast_strlen_zero ( dbhost ) ) {
2018-03-07 20:36:17 +00:00
ast_log ( LOG_DEBUG , " PostgreSQL RealTime Host: %s \n " , dbhost ) ;
ast_log ( LOG_DEBUG , " PostgreSQL RealTime Port: %i \n " , dbport ) ;
2006-10-03 15:53:07 +00:00
} else {
2018-03-07 20:36:17 +00:00
ast_log ( LOG_DEBUG , " PostgreSQL RealTime Socket: %s \n " , dbsock ) ;
2006-10-03 15:53:07 +00:00
}
2018-03-07 20:36:17 +00:00
ast_log ( LOG_DEBUG , " PostgreSQL RealTime User: %s \n " , dbuser ) ;
ast_log ( LOG_DEBUG , " PostgreSQL RealTime Password: %s \n " , dbpass ) ;
ast_log ( LOG_DEBUG , " 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. */
2017-02-23 20:48:53 +00:00
if ( pgsqlConn ) {
if ( PQstatus ( pgsqlConn ) = = CONNECTION_OK ) {
/* We're good? */
return 1 ;
}
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 */
2017-02-23 20:48:53 +00:00
if ( ( ! ast_strlen_zero ( dbhost ) | | ! ast_strlen_zero ( dbsock ) ) & & ! ast_strlen_zero ( dbuser ) & & ! ast_strlen_zero ( my_database ) ) {
2014-07-16 13:55:36 +00:00
struct ast_str * conn_info = ast_str_create ( 128 ) ;
if ( ! conn_info ) {
ast_log ( LOG_ERROR , " PostgreSQL RealTime: Failed to allocate memory for connection string. \n " ) ;
return 0 ;
}
2008-02-22 22:39:21 +00:00
2014-07-16 13:55:36 +00:00
ast_str_set ( & conn_info , 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 ) ;
2014-07-16 13:55:36 +00:00
if ( ! ast_strlen_zero ( dbappname ) ) {
ast_str_append ( & conn_info , 0 , " application_name=%s " , dbappname ) ;
}
if ( ! ast_strlen_zero ( dbpass ) ) {
ast_str_append ( & conn_info , 0 , " password=%s " , dbpass ) ;
}
pgsqlConn = PQconnectdb ( ast_str_buffer ( conn_info ) ) ;
ast_free ( conn_info ) ;
conn_info = NULL ;
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 " ,
2012-02-13 17:25:41 +00:00
my_database , 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 */
2011-01-27 20:09:33 +00:00
if ( ( cur = find_table ( NULL , 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
{
2016-02-18 04:58:01 +00:00
char connection_info [ 256 ] ;
char credentials [ 100 ] = " " ;
char buf [ 376 ] ; /* 256+100+"Connected to "+" for "+NULL */
2017-02-23 20:48:53 +00:00
int is_connected = 0 , 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 ;
2016-02-07 19:00:24 +00:00
if ( ! ast_strlen_zero ( dbhost ) )
snprintf ( connection_info , sizeof ( connection_info ) , " %s@%s, port %d " , dbname , dbhost , dbport ) ;
else if ( ! ast_strlen_zero ( dbsock ) )
snprintf ( connection_info , sizeof ( connection_info ) , " %s on socket file %s " , dbname , dbsock ) ;
else
snprintf ( connection_info , sizeof ( connection_info ) , " %s@%s " , dbname , dbhost ) ;
2006-04-05 17:46:09 +00:00
2016-02-07 19:00:24 +00:00
if ( ! ast_strlen_zero ( dbuser ) )
snprintf ( credentials , sizeof ( credentials ) , " with username %s " , dbuser ) ;
2006-04-05 17:46:09 +00:00
2017-02-23 20:48:53 +00:00
ast_mutex_lock ( & pgsql_lock ) ;
is_connected = ( pgsqlConn & & PQstatus ( pgsqlConn ) = = CONNECTION_OK ) ;
ast_mutex_unlock ( & pgsql_lock ) ;
2006-04-05 17:46:09 +00:00
2017-02-23 20:48:53 +00:00
if ( is_connected ) {
2016-02-18 04:58:01 +00:00
snprintf ( buf , sizeof ( buf ) , " Connected to %s%s for " , connection_info , credentials ) ;
ast_cli_print_timestr_fromseconds ( a - > fd , ctimesec , buf ) ;
2007-10-19 18:01:00 +00:00
return CLI_SUCCESS ;
2006-04-05 17:46:09 +00:00
} else {
2016-02-07 19:00:24 +00:00
ast_cli ( a - > fd , " Unable to connect %s%s \n " , connection_info , credentials ) ;
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 */
2010-07-20 19:35:02 +00:00
AST_MODULE_INFO ( ASTERISK_GPL_KEY , AST_MODFLAG_LOAD_ORDER , " PostgreSQL RealTime Configuration Driver " ,
2015-05-06 00:49:04 +00:00
. support_level = AST_MODULE_SUPPORT_EXTENDED ,
. load = load_module ,
. unload = unload_module ,
. reload = reload ,
. load_pri = AST_MODPRI_REALTIME_DRIVER ,
2018-02-17 03:11:42 +00:00
. requires = " extconfig " ,
2015-05-06 00:49:04 +00:00
) ;