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 08:34:28 -03: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 07:22:50 -03: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 15:48:53 -05: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 12:18:34 -04:00
char * schemaname , * tablename , * tmp_schemaname , * tmp_tablename ;
2009-10-06 19:31:39 +00:00
if ( strchr ( orig_tablename , '.' )) {
2015-05-07 12:18:34 -04: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 12:18:34 -04:00
tmp_schemaname = "" ;
tmp_tablename = ast_strdupa ( orig_tablename );
2009-10-06 19:31:39 +00:00
}
2015-05-07 12:18:34 -04: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 20:40:30 +02: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 12:18:34 -04: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 12:18:34 -04: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 14:15:53 -05: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 14:15:53 -05: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 09:38:06 -05:00
#define IS_SQL_LIKE_CLAUSE(x) ((x) && ast_ends_with(x, " LIKE"))
2017-09-19 07:22:50 -03:00
#define ESCAPE_CLAUSE (USE_BACKSLASH_AS_STRING ? " ESCAPE '\\'" : " ESCAPE '\\\\'")
2017-02-16 09:38:06 -05: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 09:38:06 -05: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 15:48:53 -05: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 15:48:53 -05: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 09:38:06 -05: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 15:48:53 -05:00
ast_mutex_unlock ( & pgsql_lock );
2007-11-29 19:35:49 +00:00
return NULL ;
}
2017-02-16 09:38:06 -05: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 09:38:06 -05:00
escape = "" ;
if ( ! strchr ( field -> name , ' ' )) {
2006-04-10 02:01:39 +00:00
op = " =" ;
2017-02-16 09:38:06 -05:00
} else {
2006-04-10 02:01:39 +00:00
op = "" ;
2017-02-16 09:38:06 -05: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 15:48:53 -05:00
ast_mutex_unlock ( & pgsql_lock );
2007-11-29 19:35:49 +00:00
return NULL ;
}
2017-02-16 09:38:06 -05: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 09:38:06 -05: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 15:48:53 -05: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 15:48:53 -05: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 09:38:06 -05:00
if ( ! strchr ( field -> name , ' ' )) {
2006-04-10 02:01:39 +00:00
op = " =" ;
2017-02-16 09:38:06 -05:00
escape = "" ;
} else {
2006-04-10 02:01:39 +00:00
op = "" ;
2017-02-16 09:38:06 -05: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 15:48:53 -05: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 09:38:06 -05: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 09:38:06 -05:00
escape = "" ;
if ( ! strchr ( field -> name , ' ' )) {
2006-04-10 02:01:39 +00:00
op = " =" ;
2017-02-16 09:38:06 -05:00
escape = "" ;
} else {
2006-04-10 02:01:39 +00:00
op = "" ;
2017-02-16 09:38:06 -05: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 15:48:53 -05: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 09:38:06 -05: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 09:56:54 -05:00
cat = ast_category_new_anonymous ();
if ( ! cat ) {
2006-04-10 02:01:39 +00:00
continue ;
2017-02-21 09:56:54 -05: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 15:48:53 -05: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 10:41:45 -05:00
release_table ( table );
2017-02-23 15:48:53 -05: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 15:48:53 -05: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 15:48:53 -05: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 15:48:53 -05: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 15:48:53 -05: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 15:48:53 -05: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 15:48:53 -05: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 10:41:45 -05:00
release_table ( table );
2017-02-23 15:48:53 -05: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 15:48:53 -05: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 15:48:53 -05: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 15:48:53 -05: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 15:48:53 -05: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 15:48:53 -05: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 );
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 15:48:53 -05: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 15:48:53 -05: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
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 08:34:28 -03: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.)
*/
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 15:48:53 -05: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 15:48:53 -05: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 15:48:53 -05: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 15:48:53 -05: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 09:56:54 -05:00
cur_cat = ast_category_new_dynamic ( field_category );
if ( ! cur_cat ) {
2006-04-05 17:46:09 +00:00
break ;
2017-02-21 09:56:54 -05: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 10:50:32 +01: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 10:50:32 +01: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 08:23:55 -05: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 10:50:32 +01: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 10:50:32 +01: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 15:48:53 -05:00
ast_mutex_unlock ( & pgsql_lock );
2017-02-28 10:41:45 -05: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 14:15:53 -05: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-09 23:13:07 -03: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 15:48:53 -05: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 15:36:17 -05:00
if ( DEBUG_ATLEAST ( 1 )) {
2007-09-21 14:40:10 +00:00
if ( ! ast_strlen_zero ( dbhost )) {
2018-03-07 15:36:17 -05: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 15:36:17 -05:00
ast_log ( LOG_DEBUG , "PostgreSQL RealTime Socket: %s \n " , dbsock );
2006-10-03 15:53:07 +00:00
}
2018-03-07 15:36:17 -05: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 15:48:53 -05: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 15:48:53 -05: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 01:58:01 -03:00
char connection_info [ 256 ];
char credentials [ 100 ] = "" ;
char buf [ 376 ]; /* 256+100+"Connected to "+" for "+NULL */
2017-02-23 15:48:53 -05: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 16:00:24 -03: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 16:00:24 -03: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 15:48:53 -05: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 15:48:53 -05:00
if ( is_connected ) {
2016-02-18 01:58:01 -03: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 16:00:24 -03: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-05 20:49:04 -04:00
. support_level = AST_MODULE_SUPPORT_EXTENDED ,
. load = load_module ,
. unload = unload_module ,
. reload = reload ,
. load_pri = AST_MODPRI_REALTIME_DRIVER ,
2018-02-16 22:11:42 -05:00
. requires = "extconfig" ,
2015-05-05 20:49:04 -04:00
);