2003-10-01 15:03:30 +00:00
/*
2005-09-14 20:46:50 +00:00
* Asterisk - - An open source telephony toolkit .
2003-10-01 15:03:30 +00:00
*
2012-02-07 15:29:14 +00:00
* Copyright ( C ) 2003 - 2012
2005-09-15 15:44:26 +00:00
*
2008-11-20 17:48:58 +00:00
* Matthew D . Hardeman < mhardemn @ papersoft . com >
* Adapted from the MySQL CDR logger originally by James Sharp
2003-10-01 15:03:30 +00:00
*
* Modified September 2003
* Matthew D . Hardeman < mhardemn @ papersoft . com >
*
2005-09-14 20:46:50 +00:00
* See http : //www.asterisk.org for more information about
* the Asterisk project . Please do not directly contact
* any of the maintainers of this project for assistance ;
* the project provides a web site , mailing lists and IRC
* channels for your use .
*
2003-10-01 15:03:30 +00:00
* This program is free software , distributed under the terms of
2005-09-14 20:46:50 +00:00
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree .
*/
2010-02-26 08:45:11 +00:00
/*!
* \ file
2008-11-20 17:48:58 +00:00
* \ brief PostgreSQL CDR logger
*
* \ author Matthew D . Hardeman < mhardemn @ papersoft . com >
2012-09-21 17:14:59 +00:00
* PostgreSQL http : //www.postgresql.org/
2005-11-06 15:09:47 +00:00
* \ ingroup cdr_drivers
2003-10-01 15:03:30 +00:00
*/
2012-10-14 21:46:45 +00:00
/*! \li \ref cdr_pgsql.c uses the configuration file \ref cdr_pgsql.conf
* \ addtogroup configuration_file Configuration Files
*/
/*!
* \ page cdr_pgsql . conf cdr_pgsql . conf
* \ verbinclude cdr_pgsql . conf . sample
*/
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"
2003-10-01 15:03:30 +00:00
# include <libpq-fe.h>
2005-06-06 21:09:59 +00:00
# include "asterisk/config.h"
# include "asterisk/channel.h"
# include "asterisk/cdr.h"
2011-09-29 12:03:23 +00:00
# include "asterisk/cli.h"
2005-06-06 21:09:59 +00:00
# include "asterisk/module.h"
2008-02-25 23:04:20 +00:00
# define DATE_FORMAT "'%Y-%m-%d %T'"
2003-10-01 15:03:30 +00:00
2017-09-09 02:19:28 +00:00
# define PGSQL_MIN_VERSION_SCHEMA 70300
2010-02-26 08:04:07 +00:00
static const char name [ ] = " pgsql " ;
static const char config [ ] = " cdr_pgsql.conf " ;
2014-07-16 13:55:36 +00:00
static char * pghostname ;
static char * pgdbname ;
static char * pgdbuser ;
static char * pgpassword ;
static char * pgappname ;
static char * pgdbport ;
static char * table ;
static char * encoding ;
static char * tz ;
2003-10-01 15:03:30 +00:00
static int connected = 0 ;
2015-05-01 17:22:17 +00:00
/* Optimization to reduce number of memory allocations */
2008-02-25 23:04:20 +00:00
static int maxsize = 512 , maxsize2 = 512 ;
2011-09-29 12:03:23 +00:00
static time_t connect_time = 0 ;
static int totalrecords = 0 ;
static int records ;
static char * handle_cdr_pgsql_status ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a ) ;
static struct ast_cli_entry cdr_pgsql_status_cli [ ] = {
AST_CLI_DEFINE ( handle_cdr_pgsql_status , " Show connection status of the PostgreSQL CDR driver (cdr_pgsql) " ) ,
} ;
2003-10-01 15:03:30 +00:00
2004-06-09 01:45:08 +00:00
AST_MUTEX_DEFINE_STATIC ( pgsql_lock ) ;
2003-10-01 15:03:30 +00:00
2006-11-13 21:32:13 +00:00
static PGconn * conn = NULL ;
2003-10-01 15:03:30 +00:00
2008-02-25 23:04:20 +00:00
struct columns {
char * name ;
char * type ;
int len ;
2008-04-11 23:26:56 +00:00
unsigned int notnull : 1 ;
unsigned int hasdefault : 1 ;
2008-02-25 23:04:20 +00:00
AST_RWLIST_ENTRY ( columns ) list ;
} ;
static AST_RWLIST_HEAD_STATIC ( psql_columns , columns ) ;
2016-01-23 17:41:38 +00:00
# define LENGTHEN_BUF(size, var_sql) \
do { \
/* Lengthen buffer, if necessary */ \
if ( ast_str_strlen ( var_sql ) + size + 1 > ast_str_size ( var_sql ) ) { \
if ( ast_str_make_space ( & var_sql , ( ( ast_str_size ( var_sql ) + size + 3 ) / 512 + 1 ) * 512 ) ! = 0 ) { \
ast_log ( LOG_ERROR , " Unable to allocate sufficient memory. Insert CDR '%s:%s' failed. \n " , pghostname , table ) ; \
ast_free ( sql ) ; \
ast_free ( sql2 ) ; \
2008-12-13 08:36:35 +00:00
AST_RWLIST_UNLOCK ( & psql_columns ) ; \
2016-01-23 17:41:38 +00:00
ast_mutex_unlock ( & pgsql_lock ) ; \
return - 1 ; \
} \
} \
2008-02-25 23:04:20 +00:00
} while ( 0 )
2016-01-23 17:41:38 +00:00
# define LENGTHEN_BUF1(size) \
LENGTHEN_BUF ( size , sql ) ;
# define LENGTHEN_BUF2(size) \
LENGTHEN_BUF ( size , sql2 ) ;
2011-09-29 12:03:23 +00:00
/*! \brief Handle the CLI command cdr show pgsql status */
static char * handle_cdr_pgsql_status ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
{
switch ( cmd ) {
case CLI_INIT :
e - > command = " cdr show pgsql status " ;
e - > usage =
" Usage: cdr show pgsql status \n "
" Shows current connection status for cdr_pgsql \n " ;
return NULL ;
case CLI_GENERATE :
return NULL ;
}
2015-04-12 14:08:30 +00:00
if ( a - > argc ! = e - > args )
2011-09-29 12:03:23 +00:00
return CLI_SHOWUSAGE ;
if ( connected ) {
2016-02-18 04:58:01 +00:00
char status [ 256 ] ;
char status2 [ 100 ] = " " ;
char buf [ 362 ] ; /* 256+100+" for "+NULL */
2011-09-29 12:03:23 +00:00
int ctime = time ( NULL ) - connect_time ;
if ( pgdbport ) {
snprintf ( status , 255 , " Connected to %s@%s, port %s " , pgdbname , pghostname , pgdbport ) ;
} else {
snprintf ( status , 255 , " Connected to %s@%s " , pgdbname , pghostname ) ;
}
if ( pgdbuser & & * pgdbuser ) {
snprintf ( status2 , 99 , " with username %s " , pgdbuser ) ;
}
if ( table & & * table ) {
snprintf ( status2 , 99 , " using table %s " , table ) ;
}
2016-02-18 04:58:01 +00:00
snprintf ( buf , sizeof ( buf ) , " %s%s for " , status , status2 ) ;
ast_cli_print_timestr_fromseconds ( a - > fd , ctime , buf ) ;
2011-09-29 12:03:23 +00:00
if ( records = = totalrecords ) {
ast_cli ( a - > fd , " Wrote %d records since last restart. \n " , totalrecords ) ;
} else {
ast_cli ( a - > fd , " Wrote %d records since last restart and %d records since last reconnect. \n " , totalrecords , records ) ;
}
} else {
ast_cli ( a - > fd , " Not currently connected to a PgSQL server. \n " ) ;
}
return CLI_SUCCESS ;
}
2014-07-16 13:55:36 +00:00
static void pgsql_reconnect ( void )
{
struct ast_str * conn_info = ast_str_create ( 128 ) ;
if ( ! conn_info ) {
ast_log ( LOG_ERROR , " Failed to allocate memory for connection string. \n " ) ;
return ;
}
if ( conn ) {
PQfinish ( conn ) ;
conn = NULL ;
}
2019-06-04 17:41:33 +00:00
if ( ! ast_strlen_zero ( pghostname ) ) {
ast_str_append ( & conn_info , 0 , " host=%s " , pghostname ) ;
}
if ( ! ast_strlen_zero ( pgdbport ) ) {
ast_str_append ( & conn_info , 0 , " port=%s " , pgdbport ) ;
}
if ( ! ast_strlen_zero ( pgdbname ) ) {
ast_str_append ( & conn_info , 0 , " dbname=%s " , pgdbname ) ;
}
if ( ! ast_strlen_zero ( pgdbuser ) ) {
ast_str_append ( & conn_info , 0 , " user=%s " , pgdbuser ) ;
}
2014-07-16 13:55:36 +00:00
if ( ! ast_strlen_zero ( pgappname ) ) {
2019-06-04 17:41:33 +00:00
ast_str_append ( & conn_info , 0 , " application_name=%s " , pgappname ) ;
2014-07-16 13:55:36 +00:00
}
if ( ! ast_strlen_zero ( pgpassword ) ) {
2019-06-04 17:41:33 +00:00
ast_str_append ( & conn_info , 0 , " password=%s " , pgpassword ) ;
}
if ( ast_str_strlen ( conn_info ) = = 0 ) {
ast_log ( LOG_ERROR , " Connection string is blank. \n " ) ;
return ;
2014-07-16 13:55:36 +00:00
}
conn = PQconnectdb ( ast_str_buffer ( conn_info ) ) ;
ast_free ( conn_info ) ;
}
2003-10-01 15:03:30 +00:00
static int pgsql_log ( struct ast_cdr * cdr )
{
2007-07-18 19:47:20 +00:00
struct ast_tm tm ;
2004-07-09 16:19:00 +00:00
char * pgerror ;
2007-06-12 19:40:41 +00:00
PGresult * result ;
2017-03-30 13:11:46 +00:00
int res = - 1 ;
2003-10-01 15:03:30 +00:00
ast_mutex_lock ( & pgsql_lock ) ;
if ( ( ! connected ) & & pghostname & & pgdbuser & & pgpassword & & pgdbname ) {
2014-07-16 13:55:36 +00:00
pgsql_reconnect ( ) ;
2003-10-01 15:03:30 +00:00
if ( PQstatus ( conn ) ! = CONNECTION_BAD ) {
connected = 1 ;
2011-09-29 12:03:23 +00:00
connect_time = time ( NULL ) ;
records = 0 ;
2010-09-22 15:18:49 +00:00
if ( PQsetClientEncoding ( conn , encoding ) ) {
2010-09-24 03:41:02 +00:00
# ifdef HAVE_PGSQL_pg_encoding_to_char
2010-09-22 15:18:49 +00:00
ast_log ( LOG_WARNING , " Failed to set encoding to '%s'. Encoding set to default '%s' \n " , encoding , pg_encoding_to_char ( PQclientEncoding ( conn ) ) ) ;
2010-09-24 03:41:02 +00:00
# else
ast_log ( LOG_WARNING , " Failed to set encoding to '%s'. Encoding set to default. \n " , encoding ) ;
# endif
2010-09-22 15:18:49 +00:00
}
2003-10-01 15:03:30 +00:00
} else {
2003-11-05 05:56:27 +00:00
pgerror = PQerrorMessage ( conn ) ;
2008-04-20 14:52:07 +00:00
ast_log ( LOG_ERROR , " Unable to connect to database server %s. Calls will not be logged! \n " , pghostname ) ;
ast_log ( LOG_ERROR , " Reason: %s \n " , pgerror ) ;
2007-07-27 17:05:18 +00:00
PQfinish ( conn ) ;
2007-07-26 18:31:28 +00:00
conn = NULL ;
2003-10-01 15:03:30 +00:00
}
}
if ( connected ) {
2008-02-25 23:04:20 +00:00
struct columns * cur ;
2008-08-24 16:23:15 +00:00
struct ast_str * sql = ast_str_create ( maxsize ) , * sql2 = ast_str_create ( maxsize2 ) ;
2017-03-30 13:11:46 +00:00
char buf [ 257 ] ;
char * escapebuf = NULL , * value ;
2015-05-07 02:18:28 +00:00
char * separator = " " ;
2017-03-30 13:11:46 +00:00
size_t bufsize = 513 ;
2012-02-07 15:29:14 +00:00
2017-03-30 13:11:46 +00:00
escapebuf = ast_malloc ( bufsize ) ;
if ( ! escapebuf | | ! sql | | ! sql2 ) {
goto ast_log_cleanup ;
2008-08-24 16:23:15 +00:00
}
ast_str_set ( & sql , 0 , " INSERT INTO %s ( " , table ) ;
ast_str_set ( & sql2 , 0 , " VALUES ( " ) ;
2008-11-20 17:48:58 +00:00
2008-02-25 23:04:20 +00:00
AST_RWLIST_RDLOCK ( & psql_columns ) ;
AST_RWLIST_TRAVERSE ( & psql_columns , cur , list ) {
/* For fields not set, simply skip them */
2013-06-17 03:00:38 +00:00
ast_cdr_format_var ( cdr , cur - > name , & value , buf , sizeof ( buf ) , 0 ) ;
2008-05-10 14:19:41 +00:00
if ( strcmp ( cur - > name , " calldate " ) = = 0 & & ! value ) {
2013-06-17 03:00:38 +00:00
ast_cdr_format_var ( cdr , " start " , & value , buf , sizeof ( buf ) , 0 ) ;
2008-05-10 14:19:41 +00:00
}
2008-04-11 23:26:56 +00:00
if ( ! value ) {
if ( cur - > notnull & & ! cur - > hasdefault ) {
/* Field is NOT NULL (but no default), must include it anyway */
2008-06-29 12:06:46 +00:00
LENGTHEN_BUF1 ( strlen ( cur - > name ) + 2 ) ;
2015-05-07 02:18:28 +00:00
ast_str_append ( & sql , 0 , " %s \" %s \" " , separator , cur - > name ) ;
2008-04-11 23:26:56 +00:00
LENGTHEN_BUF2 ( 3 ) ;
2015-05-07 02:18:28 +00:00
ast_str_append ( & sql2 , 0 , " %s'' " , separator ) ;
separator = " , " ;
2008-04-11 23:26:56 +00:00
}
2008-02-25 23:04:20 +00:00
continue ;
2008-04-11 23:26:56 +00:00
}
2008-11-20 17:48:58 +00:00
2008-06-29 12:06:46 +00:00
LENGTHEN_BUF1 ( strlen ( cur - > name ) + 2 ) ;
2015-05-07 02:18:28 +00:00
ast_str_append ( & sql , 0 , " %s \" %s \" " , separator , cur - > name ) ;
2008-02-25 23:04:20 +00:00
if ( strcmp ( cur - > name , " start " ) = = 0 | | strcmp ( cur - > name , " calldate " ) = = 0 ) {
if ( strncmp ( cur - > type , " int " , 3 ) = = 0 ) {
2008-12-13 08:36:35 +00:00
LENGTHEN_BUF2 ( 13 ) ;
2015-05-07 02:18:28 +00:00
ast_str_append ( & sql2 , 0 , " %s%ld " , separator , ( long ) cdr - > start . tv_sec ) ;
2008-02-25 23:04:20 +00:00
} else if ( strncmp ( cur - > type , " float " , 5 ) = = 0 ) {
2008-12-13 08:36:35 +00:00
LENGTHEN_BUF2 ( 31 ) ;
2015-05-07 02:18:28 +00:00
ast_str_append ( & sql2 , 0 , " %s%f " , separator , ( double ) cdr - > start . tv_sec + ( double ) cdr - > start . tv_usec / 1000000.0 ) ;
2008-02-25 23:04:20 +00:00
} else {
/* char, hopefully */
2008-12-13 08:36:35 +00:00
LENGTHEN_BUF2 ( 31 ) ;
2010-10-11 03:20:17 +00:00
ast_localtime ( & cdr - > start , & tm , tz ) ;
2008-08-24 16:23:15 +00:00
ast_strftime ( buf , sizeof ( buf ) , DATE_FORMAT , & tm ) ;
2015-05-07 02:18:28 +00:00
ast_str_append ( & sql2 , 0 , " %s%s " , separator , buf ) ;
2008-02-25 23:04:20 +00:00
}
} else if ( strcmp ( cur - > name , " answer " ) = = 0 ) {
if ( strncmp ( cur - > type , " int " , 3 ) = = 0 ) {
2008-12-13 08:36:35 +00:00
LENGTHEN_BUF2 ( 13 ) ;
2015-05-07 02:18:28 +00:00
ast_str_append ( & sql2 , 0 , " %s%ld " , separator , ( long ) cdr - > answer . tv_sec ) ;
2008-02-25 23:04:20 +00:00
} else if ( strncmp ( cur - > type , " float " , 5 ) = = 0 ) {
2008-12-13 08:36:35 +00:00
LENGTHEN_BUF2 ( 31 ) ;
2015-05-07 02:18:28 +00:00
ast_str_append ( & sql2 , 0 , " %s%f " , separator , ( double ) cdr - > answer . tv_sec + ( double ) cdr - > answer . tv_usec / 1000000.0 ) ;
2008-02-25 23:04:20 +00:00
} else {
/* char, hopefully */
2008-12-13 08:36:35 +00:00
LENGTHEN_BUF2 ( 31 ) ;
2010-12-20 17:48:09 +00:00
ast_localtime ( & cdr - > answer , & tm , tz ) ;
2008-08-24 16:23:15 +00:00
ast_strftime ( buf , sizeof ( buf ) , DATE_FORMAT , & tm ) ;
2015-05-07 02:18:28 +00:00
ast_str_append ( & sql2 , 0 , " %s%s " , separator , buf ) ;
2008-02-25 23:04:20 +00:00
}
} else if ( strcmp ( cur - > name , " end " ) = = 0 ) {
if ( strncmp ( cur - > type , " int " , 3 ) = = 0 ) {
2008-12-13 08:36:35 +00:00
LENGTHEN_BUF2 ( 13 ) ;
2015-05-07 02:18:28 +00:00
ast_str_append ( & sql2 , 0 , " %s%ld " , separator , ( long ) cdr - > end . tv_sec ) ;
2008-02-25 23:04:20 +00:00
} else if ( strncmp ( cur - > type , " float " , 5 ) = = 0 ) {
2008-12-13 08:36:35 +00:00
LENGTHEN_BUF2 ( 31 ) ;
2015-05-07 02:18:28 +00:00
ast_str_append ( & sql2 , 0 , " %s%f " , separator , ( double ) cdr - > end . tv_sec + ( double ) cdr - > end . tv_usec / 1000000.0 ) ;
2008-02-25 23:04:20 +00:00
} else {
/* char, hopefully */
2008-12-13 08:36:35 +00:00
LENGTHEN_BUF2 ( 31 ) ;
2010-10-11 03:20:17 +00:00
ast_localtime ( & cdr - > end , & tm , tz ) ;
2008-08-24 16:23:15 +00:00
ast_strftime ( buf , sizeof ( buf ) , DATE_FORMAT , & tm ) ;
2015-05-07 02:18:28 +00:00
ast_str_append ( & sql2 , 0 , " %s%s " , separator , buf ) ;
2008-02-25 23:04:20 +00:00
}
} else if ( strcmp ( cur - > name , " duration " ) = = 0 | | strcmp ( cur - > name , " billsec " ) = = 0 ) {
if ( cur - > type [ 0 ] = = ' i ' ) {
/* Get integer, no need to escape anything */
2013-06-17 03:00:38 +00:00
ast_cdr_format_var ( cdr , cur - > name , & value , buf , sizeof ( buf ) , 0 ) ;
2008-12-13 08:36:35 +00:00
LENGTHEN_BUF2 ( 13 ) ;
2015-05-07 02:18:28 +00:00
ast_str_append ( & sql2 , 0 , " %s%s " , separator , value ) ;
2008-02-25 23:04:20 +00:00
} else if ( strncmp ( cur - > type , " float " , 5 ) = = 0 ) {
2010-12-20 17:48:09 +00:00
struct timeval * when = cur - > name [ 0 ] = = ' d ' ? & cdr - > start : ast_tvzero ( cdr - > answer ) ? & cdr - > end : & cdr - > answer ;
2008-12-13 08:36:35 +00:00
LENGTHEN_BUF2 ( 31 ) ;
2015-05-07 02:18:28 +00:00
ast_str_append ( & sql2 , 0 , " %s%f " , separator , ( double ) ( ast_tvdiff_us ( cdr - > end , * when ) / 1000000.0 ) ) ;
2008-02-25 23:04:20 +00:00
} else {
/* Char field, probably */
2010-12-20 17:48:09 +00:00
struct timeval * when = cur - > name [ 0 ] = = ' d ' ? & cdr - > start : ast_tvzero ( cdr - > answer ) ? & cdr - > end : & cdr - > answer ;
2008-12-13 08:36:35 +00:00
LENGTHEN_BUF2 ( 31 ) ;
2015-05-07 02:18:28 +00:00
ast_str_append ( & sql2 , 0 , " %s'%f' " , separator , ( double ) ( ast_tvdiff_us ( cdr - > end , * when ) / 1000000.0 ) ) ;
2008-02-25 23:04:20 +00:00
}
} else if ( strcmp ( cur - > name , " disposition " ) = = 0 | | strcmp ( cur - > name , " amaflags " ) = = 0 ) {
if ( strncmp ( cur - > type , " int " , 3 ) = = 0 ) {
/* Integer, no need to escape anything */
2013-06-17 03:00:38 +00:00
ast_cdr_format_var ( cdr , cur - > name , & value , buf , sizeof ( buf ) , 1 ) ;
2008-12-13 08:36:35 +00:00
LENGTHEN_BUF2 ( 13 ) ;
2015-05-07 02:18:28 +00:00
ast_str_append ( & sql2 , 0 , " %s%s " , separator , value ) ;
2008-02-25 23:04:20 +00:00
} else {
/* Although this is a char field, there are no special characters in the values for these fields */
2013-06-17 03:00:38 +00:00
ast_cdr_format_var ( cdr , cur - > name , & value , buf , sizeof ( buf ) , 0 ) ;
2008-12-13 08:36:35 +00:00
LENGTHEN_BUF2 ( 31 ) ;
2015-05-07 02:18:28 +00:00
ast_str_append ( & sql2 , 0 , " %s'%s' " , separator , value ) ;
2008-02-25 23:04:20 +00:00
}
} else {
/* Arbitrary field, could be anything */
2013-06-17 03:00:38 +00:00
ast_cdr_format_var ( cdr , cur - > name , & value , buf , sizeof ( buf ) , 0 ) ;
2008-02-25 23:04:20 +00:00
if ( strncmp ( cur - > type , " int " , 3 ) = = 0 ) {
long long whatever ;
2009-08-10 19:20:57 +00:00
if ( value & & sscanf ( value , " %30lld " , & whatever ) = = 1 ) {
2008-12-13 08:36:35 +00:00
LENGTHEN_BUF2 ( 26 ) ;
2015-05-07 02:18:28 +00:00
ast_str_append ( & sql2 , 0 , " %s%lld " , separator , whatever ) ;
2008-02-25 23:04:20 +00:00
} else {
2008-12-13 08:36:35 +00:00
LENGTHEN_BUF2 ( 2 ) ;
2015-05-07 02:18:28 +00:00
ast_str_append ( & sql2 , 0 , " %s0 " , separator ) ;
2008-02-25 23:04:20 +00:00
}
} else if ( strncmp ( cur - > type , " float " , 5 ) = = 0 ) {
long double whatever ;
2009-08-10 19:20:57 +00:00
if ( value & & sscanf ( value , " %30Lf " , & whatever ) = = 1 ) {
2008-12-13 08:36:35 +00:00
LENGTHEN_BUF2 ( 51 ) ;
2015-05-07 02:18:28 +00:00
ast_str_append ( & sql2 , 0 , " %s%30Lf " , separator , whatever ) ;
2008-02-25 23:04:20 +00:00
} else {
2008-12-13 08:36:35 +00:00
LENGTHEN_BUF2 ( 2 ) ;
2015-05-07 02:18:28 +00:00
ast_str_append ( & sql2 , 0 , " %s0 " , separator ) ;
2008-02-25 23:04:20 +00:00
}
/* XXX Might want to handle dates, times, and other misc fields here XXX */
} else {
2017-03-30 13:11:46 +00:00
if ( value ) {
size_t required_size = strlen ( value ) * 2 + 1 ;
/* If our argument size exceeds our buffer, grow it,
* as PQescapeStringConn ( ) expects the buffer to be
* adequitely sized and does * NOT * do size checking .
*/
if ( required_size > bufsize ) {
char * tmpbuf = ast_realloc ( escapebuf , required_size ) ;
if ( ! tmpbuf ) {
AST_RWLIST_UNLOCK ( & psql_columns ) ;
goto ast_log_cleanup ;
}
escapebuf = tmpbuf ;
bufsize = required_size ;
}
2008-02-25 23:04:20 +00:00
PQescapeStringConn ( conn , escapebuf , value , strlen ( value ) , NULL ) ;
2017-03-30 13:11:46 +00:00
} else {
2008-02-25 23:04:20 +00:00
escapebuf [ 0 ] = ' \0 ' ;
2017-03-30 13:11:46 +00:00
}
2008-12-13 08:36:35 +00:00
LENGTHEN_BUF2 ( strlen ( escapebuf ) + 3 ) ;
2015-05-07 02:18:28 +00:00
ast_str_append ( & sql2 , 0 , " %s'%s' " , separator , escapebuf ) ;
2008-02-25 23:04:20 +00:00
}
}
2015-05-07 02:18:28 +00:00
separator = " , " ;
2012-02-07 15:29:14 +00:00
}
2008-12-13 08:36:35 +00:00
LENGTHEN_BUF1 ( ast_str_strlen ( sql2 ) + 2 ) ;
2012-02-07 15:29:14 +00:00
AST_RWLIST_UNLOCK ( & psql_columns ) ;
2008-12-13 08:36:35 +00:00
ast_str_append ( & sql , 0 , " )%s) " , ast_str_buffer ( sql2 ) ) ;
2003-10-01 15:03:30 +00:00
2014-05-28 22:54:12 +00:00
ast_debug ( 3 , " Inserting a CDR record: [%s] \n " , ast_str_buffer ( sql ) ) ;
2003-10-01 15:03:30 +00:00
2003-11-05 05:56:27 +00:00
/* Test to be sure we're still connected... */
/* If we're connected, and connection is working, good. */
/* Otherwise, attempt reconnect. If it fails... sorry... */
if ( PQstatus ( conn ) = = CONNECTION_OK ) {
connected = 1 ;
} else {
2008-04-20 14:52:07 +00:00
ast_log ( LOG_ERROR , " Connection was lost... attempting to reconnect. \n " ) ;
2003-11-05 05:56:27 +00:00
PQreset ( conn ) ;
if ( PQstatus ( conn ) = = CONNECTION_OK ) {
2008-04-20 14:52:07 +00:00
ast_log ( LOG_ERROR , " Connection reestablished. \n " ) ;
2003-11-05 05:56:27 +00:00
connected = 1 ;
2011-09-29 12:03:23 +00:00
connect_time = time ( NULL ) ;
records = 0 ;
2003-11-05 05:56:27 +00:00
} else {
pgerror = PQerrorMessage ( conn ) ;
2008-04-20 14:52:07 +00:00
ast_log ( LOG_ERROR , " Unable to reconnect to database server %s. Calls will not be logged! \n " , pghostname ) ;
ast_log ( LOG_ERROR , " Reason: %s \n " , pgerror ) ;
2007-07-27 17:05:18 +00:00
PQfinish ( conn ) ;
2007-07-26 18:31:28 +00:00
conn = NULL ;
2003-11-05 05:56:27 +00:00
connected = 0 ;
2017-03-30 13:11:46 +00:00
goto ast_log_cleanup ;
2003-11-05 05:56:27 +00:00
}
}
2008-12-13 08:36:35 +00:00
result = PQexec ( conn , ast_str_buffer ( sql ) ) ;
2007-06-12 19:40:41 +00:00
if ( PQresultStatus ( result ) ! = PGRES_COMMAND_OK ) {
pgerror = PQresultErrorMessage ( result ) ;
2008-04-20 14:52:07 +00:00
ast_log ( LOG_ERROR , " Failed to insert call detail record into database! \n " ) ;
ast_log ( LOG_ERROR , " Reason: %s \n " , pgerror ) ;
ast_log ( LOG_ERROR , " Connection may have been lost... attempting to reconnect. \n " ) ;
2005-02-19 20:10:52 +00:00
PQreset ( conn ) ;
if ( PQstatus ( conn ) = = CONNECTION_OK ) {
2008-04-20 14:52:07 +00:00
ast_log ( LOG_ERROR , " Connection reestablished. \n " ) ;
2005-02-19 20:10:52 +00:00
connected = 1 ;
2011-09-29 12:03:23 +00:00
connect_time = time ( NULL ) ;
records = 0 ;
2007-06-20 23:33:49 +00:00
PQclear ( result ) ;
2008-12-13 08:36:35 +00:00
result = PQexec ( conn , ast_str_buffer ( sql ) ) ;
2007-06-12 19:40:41 +00:00
if ( PQresultStatus ( result ) ! = PGRES_COMMAND_OK ) {
2005-02-19 20:10:52 +00:00
pgerror = PQresultErrorMessage ( result ) ;
2008-04-20 14:52:07 +00:00
ast_log ( LOG_ERROR , " HARD ERROR! Attempted reconnection failed. DROPPING CALL RECORD! \n " ) ;
ast_log ( LOG_ERROR , " Reason: %s \n " , pgerror ) ;
2017-03-30 13:11:46 +00:00
} else {
2011-09-29 12:03:23 +00:00
/* Second try worked out ok */
totalrecords + + ;
records + + ;
2017-03-30 13:11:46 +00:00
res = 0 ;
2005-02-19 20:10:52 +00:00
}
}
2011-09-29 12:03:23 +00:00
} else {
totalrecords + + ;
records + + ;
2017-03-30 13:11:46 +00:00
res = 0 ;
2003-10-01 15:03:30 +00:00
}
2007-06-12 19:40:41 +00:00
PQclear ( result ) ;
2015-05-01 17:22:17 +00:00
/* Next time, just allocate buffers that are that big to start with. */
if ( ast_str_strlen ( sql ) > maxsize ) {
maxsize = ast_str_strlen ( sql ) ;
}
if ( ast_str_strlen ( sql2 ) > maxsize2 ) {
maxsize2 = ast_str_strlen ( sql2 ) ;
}
2017-03-30 13:11:46 +00:00
ast_log_cleanup :
ast_free ( escapebuf ) ;
2008-08-24 16:23:15 +00:00
ast_free ( sql ) ;
ast_free ( sql2 ) ;
2003-10-01 15:03:30 +00:00
}
2017-03-30 13:11:46 +00:00
2003-10-01 15:03:30 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2017-03-30 13:11:46 +00:00
return res ;
2003-10-01 15:03:30 +00:00
}
2012-02-07 15:29:14 +00:00
/* This function should be called without holding the pgsql_columns lock */
static void empty_columns ( void )
2008-02-25 23:04:20 +00:00
{
2008-08-07 00:52:23 +00:00
struct columns * current ;
2012-02-07 15:29:14 +00:00
AST_RWLIST_WRLOCK ( & psql_columns ) ;
while ( ( current = AST_RWLIST_REMOVE_HEAD ( & psql_columns , list ) ) ) {
ast_free ( current ) ;
}
AST_RWLIST_UNLOCK ( & psql_columns ) ;
2009-06-20 14:09:40 +00:00
2012-02-07 15:29:14 +00:00
}
static int unload_module ( void )
{
2013-10-27 20:04:17 +00:00
if ( ast_cdr_unregister ( name ) ) {
return - 1 ;
}
2011-09-29 12:03:23 +00:00
ast_cli_unregister_multiple ( cdr_pgsql_status_cli , ARRAY_LEN ( cdr_pgsql_status_cli ) ) ;
2008-02-25 23:04:20 +00:00
2014-07-16 13:55:36 +00:00
if ( conn ) {
PQfinish ( conn ) ;
conn = NULL ;
}
2012-02-07 15:29:14 +00:00
ast_free ( pghostname ) ;
ast_free ( pgdbname ) ;
ast_free ( pgdbuser ) ;
ast_free ( pgpassword ) ;
2014-07-16 13:55:36 +00:00
ast_free ( pgappname ) ;
2012-02-07 15:29:14 +00:00
ast_free ( pgdbport ) ;
ast_free ( table ) ;
ast_free ( encoding ) ;
ast_free ( tz ) ;
2008-02-25 23:04:20 +00:00
2012-02-07 15:29:14 +00:00
empty_columns ( ) ;
2008-02-25 23:04:20 +00:00
2003-10-01 15:03:30 +00:00
return 0 ;
}
2007-08-16 21:09:46 +00:00
static int config_module ( int reload )
2003-10-01 15:03:30 +00:00
{
2007-08-16 21:09:46 +00:00
char * pgerror ;
2008-02-25 23:04:20 +00:00
struct columns * cur ;
PGresult * result ;
2006-09-20 21:03:37 +00:00
const char * tmp ;
2007-08-16 21:09:46 +00:00
struct ast_config * cfg ;
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 } ;
2008-11-19 19:25:14 +00:00
if ( ( cfg = ast_config_load ( config , config_flags ) ) = = NULL | | cfg = = CONFIG_STATUS_FILEINVALID ) {
2007-08-16 21:09:46 +00:00
ast_log ( LOG_WARNING , " Unable to load config for PostgreSQL CDR's: %s \n " , config ) ;
return - 1 ;
2010-09-27 19:30:18 +00:00
} else if ( cfg = = CONFIG_STATUS_FILEUNCHANGED ) {
2007-08-16 21:09:46 +00:00
return 0 ;
2010-09-27 19:30:18 +00:00
}
2003-10-01 15:03:30 +00:00
2012-02-07 15:29:14 +00:00
ast_mutex_lock ( & pgsql_lock ) ;
2012-04-17 18:57:40 +00:00
if ( ! ast_variable_browse ( cfg , " global " ) ) {
2007-09-20 16:10:57 +00:00
ast_config_destroy ( cfg ) ;
2012-02-07 15:29:14 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
ast_log ( LOG_NOTICE , " cdr_pgsql configuration contains no global section, skipping module %s. \n " ,
reload ? " reload " : " load " ) ;
return - 1 ;
2007-09-20 16:10:57 +00:00
}
2003-10-01 15:03:30 +00:00
2007-08-16 21:09:46 +00:00
if ( ! ( tmp = ast_variable_retrieve ( cfg , " global " , " hostname " ) ) ) {
ast_log ( LOG_WARNING , " PostgreSQL server hostname not specified. Assuming unix socket connection \n " ) ;
2006-05-20 00:57:04 +00:00
tmp = " " ; /* connect via UNIX-socket by default */
2005-07-11 21:06:27 +00:00
}
2007-08-16 21:09:46 +00:00
2012-02-07 15:29:14 +00:00
ast_free ( pghostname ) ;
2007-09-20 16:10:57 +00:00
if ( ! ( pghostname = ast_strdup ( tmp ) ) ) {
ast_config_destroy ( cfg ) ;
2012-02-07 15:29:14 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2005-07-11 21:06:27 +00:00
return - 1 ;
2007-09-20 16:10:57 +00:00
}
2003-10-01 15:03:30 +00:00
2006-05-20 01:29:08 +00:00
if ( ! ( tmp = ast_variable_retrieve ( cfg , " global " , " dbname " ) ) ) {
2008-04-20 14:52:07 +00:00
ast_log ( LOG_WARNING , " PostgreSQL database not specified. Assuming asterisk \n " ) ;
2005-07-11 21:06:27 +00:00
tmp = " asteriskcdrdb " ;
}
2006-05-20 01:29:08 +00:00
2012-02-07 15:29:14 +00:00
ast_free ( pgdbname ) ;
2007-09-20 16:10:57 +00:00
if ( ! ( pgdbname = ast_strdup ( tmp ) ) ) {
ast_config_destroy ( cfg ) ;
2012-02-07 15:29:14 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2005-07-11 21:06:27 +00:00
return - 1 ;
2007-09-20 16:10:57 +00:00
}
2003-10-01 15:03:30 +00:00
2006-05-20 01:29:08 +00:00
if ( ! ( tmp = ast_variable_retrieve ( cfg , " global " , " user " ) ) ) {
2008-04-20 14:52:07 +00:00
ast_log ( LOG_WARNING , " PostgreSQL database user not specified. Assuming asterisk \n " ) ;
2006-05-20 01:35:12 +00:00
tmp = " asterisk " ;
2005-07-11 21:06:27 +00:00
}
2006-05-20 01:29:08 +00:00
2012-02-07 15:29:14 +00:00
ast_free ( pgdbuser ) ;
2007-09-20 16:10:57 +00:00
if ( ! ( pgdbuser = ast_strdup ( tmp ) ) ) {
ast_config_destroy ( cfg ) ;
2012-02-07 15:29:14 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2005-07-11 21:06:27 +00:00
return - 1 ;
2007-09-20 16:10:57 +00:00
}
2003-10-01 15:03:30 +00:00
2014-07-16 13:55:36 +00:00
if ( ! ( tmp = ast_variable_retrieve ( cfg , " global " , " appname " ) ) ) {
tmp = " " ;
}
ast_free ( pgappname ) ;
if ( ! ( pgappname = ast_strdup ( tmp ) ) ) {
ast_config_destroy ( cfg ) ;
ast_mutex_unlock ( & pgsql_lock ) ;
return - 1 ;
}
2006-05-20 01:29:08 +00:00
if ( ! ( tmp = ast_variable_retrieve ( cfg , " global " , " password " ) ) ) {
2008-04-20 14:52:07 +00:00
ast_log ( LOG_WARNING , " PostgreSQL database password not specified. Assuming blank \n " ) ;
2005-07-11 21:06:27 +00:00
tmp = " " ;
}
2006-05-20 01:29:08 +00:00
2012-02-07 15:29:14 +00:00
ast_free ( pgpassword ) ;
2007-09-20 16:10:57 +00:00
if ( ! ( pgpassword = ast_strdup ( tmp ) ) ) {
ast_config_destroy ( cfg ) ;
2012-02-07 15:29:14 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2005-07-11 21:06:27 +00:00
return - 1 ;
2007-09-20 16:10:57 +00:00
}
2003-10-01 15:03:30 +00:00
2008-04-20 14:52:07 +00:00
if ( ! ( tmp = ast_variable_retrieve ( cfg , " global " , " port " ) ) ) {
ast_log ( LOG_WARNING , " PostgreSQL database port not specified. Using default 5432. \n " ) ;
2005-07-11 21:06:27 +00:00
tmp = " 5432 " ;
}
2006-05-20 01:29:08 +00:00
2012-02-07 15:29:14 +00:00
ast_free ( pgdbport ) ;
2007-09-20 16:10:57 +00:00
if ( ! ( pgdbport = ast_strdup ( tmp ) ) ) {
ast_config_destroy ( cfg ) ;
2012-02-07 15:29:14 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2005-07-11 21:06:27 +00:00
return - 1 ;
2007-09-20 16:10:57 +00:00
}
2005-07-11 21:06:27 +00:00
2006-05-20 01:29:08 +00:00
if ( ! ( tmp = ast_variable_retrieve ( cfg , " global " , " table " ) ) ) {
2008-04-20 14:52:07 +00:00
ast_log ( LOG_WARNING , " CDR table not specified. Assuming cdr \n " ) ;
2005-07-11 21:06:27 +00:00
tmp = " cdr " ;
}
2006-05-20 01:29:08 +00:00
2012-02-07 15:29:14 +00:00
ast_free ( table ) ;
2007-09-20 16:10:57 +00:00
if ( ! ( table = ast_strdup ( tmp ) ) ) {
ast_config_destroy ( cfg ) ;
2012-02-07 15:29:14 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2005-07-11 21:06:27 +00:00
return - 1 ;
2007-09-20 16:10:57 +00:00
}
2003-10-01 15:03:30 +00:00
2010-09-22 15:18:49 +00:00
if ( ! ( tmp = ast_variable_retrieve ( cfg , " global " , " encoding " ) ) ) {
ast_log ( LOG_WARNING , " Encoding not specified. Assuming LATIN9 \n " ) ;
tmp = " LATIN9 " ;
}
2012-02-07 15:29:14 +00:00
ast_free ( encoding ) ;
2010-09-22 15:18:49 +00:00
if ( ! ( encoding = ast_strdup ( tmp ) ) ) {
ast_config_destroy ( cfg ) ;
2012-02-07 15:29:14 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2010-09-22 15:18:49 +00:00
return - 1 ;
}
2010-10-11 03:20:17 +00:00
if ( ! ( tmp = ast_variable_retrieve ( cfg , " global " , " timezone " ) ) ) {
tmp = " " ;
}
2012-02-07 15:29:14 +00:00
ast_free ( tz ) ;
tz = NULL ;
2010-10-11 03:20:17 +00:00
if ( ! ast_strlen_zero ( tmp ) & & ! ( tz = ast_strdup ( tmp ) ) ) {
ast_config_destroy ( cfg ) ;
2012-02-07 15:29:14 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2010-10-11 03:20:17 +00:00
return - 1 ;
}
2018-03-07 20:36:17 +00:00
if ( DEBUG_ATLEAST ( 1 ) ) {
2008-03-11 16:28:03 +00:00
if ( ast_strlen_zero ( pghostname ) ) {
2018-03-07 20:36:17 +00:00
ast_log ( LOG_DEBUG , " using default unix socket \n " ) ;
2008-03-11 16:28:03 +00:00
} else {
2018-03-07 20:36:17 +00:00
ast_log ( LOG_DEBUG , " got hostname of %s \n " , pghostname ) ;
2008-03-11 16:28:03 +00:00
}
2018-03-07 20:36:17 +00:00
ast_log ( LOG_DEBUG , " got port of %s \n " , pgdbport ) ;
ast_log ( LOG_DEBUG , " got user of %s \n " , pgdbuser ) ;
ast_log ( LOG_DEBUG , " got dbname of %s \n " , pgdbname ) ;
ast_log ( LOG_DEBUG , " got password of %s \n " , pgpassword ) ;
ast_log ( LOG_DEBUG , " got application name of %s \n " , pgappname ) ;
ast_log ( LOG_DEBUG , " got sql table name of %s \n " , table ) ;
ast_log ( LOG_DEBUG , " got encoding of %s \n " , encoding ) ;
ast_log ( LOG_DEBUG , " got timezone of %s \n " , tz ) ;
2006-01-24 11:46:29 +00:00
}
2008-03-11 16:28:03 +00:00
2014-07-16 13:55:36 +00:00
pgsql_reconnect ( ) ;
2003-10-01 15:03:30 +00:00
if ( PQstatus ( conn ) ! = CONNECTION_BAD ) {
2009-10-06 19:31:39 +00:00
char sqlcmd [ 768 ] ;
2008-04-11 23:26:56 +00:00
char * fname , * ftype , * flen , * fnotnull , * fdef ;
2009-10-06 19:31:39 +00:00
int i , rows , version ;
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Successfully connected to PostgreSQL database. \n " ) ;
2003-10-01 15:03:30 +00:00
connected = 1 ;
2011-09-29 12:03:23 +00:00
connect_time = time ( NULL ) ;
records = 0 ;
2010-09-22 15:18:49 +00:00
if ( PQsetClientEncoding ( conn , encoding ) ) {
2010-09-24 03:41:02 +00:00
# ifdef HAVE_PGSQL_pg_encoding_to_char
2010-09-22 15:18:49 +00:00
ast_log ( LOG_WARNING , " Failed to set encoding to '%s'. Encoding set to default '%s' \n " , encoding , pg_encoding_to_char ( PQclientEncoding ( conn ) ) ) ;
2010-09-24 03:41:02 +00:00
# else
ast_log ( LOG_WARNING , " Failed to set encoding to '%s'. Encoding set to default. \n " , encoding ) ;
# endif
2010-09-22 15:18:49 +00:00
}
2009-10-06 19:31:39 +00:00
version = PQserverVersion ( conn ) ;
2017-09-09 02:19:28 +00:00
if ( version > = PGSQL_MIN_VERSION_SCHEMA ) {
2015-05-02 04:43:22 +00:00
char * schemaname , * tablename , * tmp_schemaname , * tmp_tablename ;
2009-10-06 19:31:39 +00:00
if ( strchr ( table , ' . ' ) ) {
2015-05-02 04:43:22 +00:00
tmp_schemaname = ast_strdupa ( table ) ;
tmp_tablename = strchr ( tmp_schemaname , ' . ' ) ;
* tmp_tablename + + = ' \0 ' ;
2009-10-06 19:31:39 +00:00
} else {
2015-05-02 04:43:22 +00:00
tmp_schemaname = " " ;
tmp_tablename = table ;
2009-10-06 19:31:39 +00:00
}
2015-05-02 04:43:22 +00:00
tablename = ast_alloca ( strlen ( tmp_tablename ) * 2 + 1 ) ;
PQescapeStringConn ( conn , tablename , tmp_tablename , strlen ( tmp_tablename ) , NULL ) ;
2009-10-06 19:31:39 +00:00
2015-05-02 04:43:22 +00:00
schemaname = ast_alloca ( strlen ( tmp_schemaname ) * 2 + 1 ) ;
PQescapeStringConn ( conn , schemaname , tmp_schemaname , strlen ( tmp_schemaname ) , NULL ) ;
2008-02-25 23:04:20 +00:00
2019-10-08 18:40:30 +00:00
snprintf ( sqlcmd , sizeof ( sqlcmd ) , " 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 ) ? " " : " ' " ) ;
2008-12-15 18:09:58 +00:00
} else {
2009-10-06 19:31:39 +00:00
snprintf ( sqlcmd , sizeof ( sqlcmd ) , " 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 " , table ) ;
2008-12-15 18:09:58 +00:00
}
2008-02-25 23:04:20 +00:00
/* Query the columns */
result = PQexec ( conn , sqlcmd ) ;
if ( PQresultStatus ( result ) ! = PGRES_TUPLES_OK ) {
pgerror = PQresultErrorMessage ( result ) ;
2008-04-20 14:52:07 +00:00
ast_log ( LOG_ERROR , " Failed to query database columns: %s \n " , pgerror ) ;
2008-02-25 23:04:20 +00:00
PQclear ( result ) ;
unload_module ( ) ;
2012-02-07 15:29:14 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2008-02-25 23:04:20 +00:00
return AST_MODULE_LOAD_DECLINE ;
}
rows = PQntuples ( result ) ;
2010-07-12 15:37:01 +00:00
if ( rows = = 0 ) {
ast_log ( LOG_ERROR , " cdr_pgsql: Failed to query database columns. No columns found, does the table exist? \n " ) ;
PQclear ( result ) ;
unload_module ( ) ;
2012-02-07 15:29:14 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
2010-07-12 15:37:01 +00:00
return AST_MODULE_LOAD_DECLINE ;
}
2012-02-07 15:29:14 +00:00
/* Clear out the columns list. */
empty_columns ( ) ;
2008-02-25 23:04:20 +00:00
for ( i = 0 ; i < rows ; i + + ) {
fname = PQgetvalue ( result , i , 0 ) ;
ftype = PQgetvalue ( result , i , 1 ) ;
flen = PQgetvalue ( result , i , 2 ) ;
2008-04-11 23:26:56 +00:00
fnotnull = PQgetvalue ( result , i , 3 ) ;
fdef = PQgetvalue ( result , i , 4 ) ;
2009-10-06 19:31:39 +00:00
if ( atoi ( flen ) = = - 1 ) {
/* For varchar columns, the maximum length is encoded in a different field */
flen = PQgetvalue ( result , i , 5 ) ;
}
2014-05-28 22:54:12 +00:00
2008-02-25 23:04:20 +00:00
cur = ast_calloc ( 1 , sizeof ( * cur ) + strlen ( fname ) + strlen ( ftype ) + 2 ) ;
if ( cur ) {
2009-08-10 19:20:57 +00:00
sscanf ( flen , " %30d " , & cur - > len ) ;
2008-02-25 23:04:20 +00:00
cur - > name = ( char * ) cur + sizeof ( * cur ) ;
cur - > type = ( char * ) cur + sizeof ( * cur ) + strlen ( fname ) + 1 ;
strcpy ( cur - > name , fname ) ;
strcpy ( cur - > type , ftype ) ;
2008-04-11 23:26:56 +00:00
if ( * fnotnull = = ' t ' ) {
cur - > notnull = 1 ;
} else {
cur - > notnull = 0 ;
}
if ( ! ast_strlen_zero ( fdef ) ) {
cur - > hasdefault = 1 ;
} else {
cur - > hasdefault = 0 ;
}
2012-02-07 15:29:14 +00:00
AST_RWLIST_WRLOCK ( & psql_columns ) ;
2008-02-25 23:04:20 +00:00
AST_RWLIST_INSERT_TAIL ( & psql_columns , cur , list ) ;
2012-02-07 15:29:14 +00:00
AST_RWLIST_UNLOCK ( & psql_columns ) ;
2008-02-25 23:04:20 +00:00
}
}
PQclear ( result ) ;
2003-10-01 15:03:30 +00:00
} else {
2007-08-16 21:09:46 +00:00
pgerror = PQerrorMessage ( conn ) ;
2008-04-20 14:52:07 +00:00
ast_log ( LOG_ERROR , " Unable to connect to database server %s. CALLS WILL NOT BE LOGGED!! \n " , pghostname ) ;
ast_log ( LOG_ERROR , " Reason: %s \n " , pgerror ) ;
2003-10-01 15:03:30 +00:00
connected = 0 ;
2014-07-16 13:55:36 +00:00
PQfinish ( conn ) ;
conn = NULL ;
2003-10-01 15:03:30 +00:00
}
2007-09-20 16:10:57 +00:00
ast_config_destroy ( cfg ) ;
2012-02-07 15:29:14 +00:00
ast_mutex_unlock ( & pgsql_lock ) ;
return 0 ;
2003-10-01 15:03:30 +00:00
}
2006-08-21 02:11:39 +00:00
static int load_module ( void )
2003-10-01 15:03:30 +00:00
{
2011-09-29 12:03:23 +00:00
ast_cli_register_multiple ( cdr_pgsql_status_cli , sizeof ( cdr_pgsql_status_cli ) / sizeof ( struct ast_cli_entry ) ) ;
2012-02-07 15:29:14 +00:00
if ( config_module ( 0 ) ) {
return AST_MODULE_LOAD_DECLINE ;
}
return ast_cdr_register ( name , ast_module_info - > description , pgsql_log )
? AST_MODULE_LOAD_DECLINE : 0 ;
2003-10-01 15:03:30 +00:00
}
2006-08-21 02:11:39 +00:00
static int reload ( void )
2003-10-01 15:03:30 +00:00
{
2007-08-16 21:09:46 +00:00
return config_module ( 1 ) ;
2003-10-01 15:03:30 +00:00
}
2010-07-20 19:35:02 +00:00
AST_MODULE_INFO ( ASTERISK_GPL_KEY , AST_MODFLAG_LOAD_ORDER , " PostgreSQL CDR Backend " ,
2015-05-05 03:57:57 +00:00
. support_level = AST_MODULE_SUPPORT_EXTENDED ,
. load = load_module ,
. unload = unload_module ,
. reload = reload ,
. load_pri = AST_MODPRI_CDR_DRIVER ,
2018-02-17 03:11:42 +00:00
. requires = " cdr " ,
2015-05-05 03:57:57 +00:00
) ;