Asterisk - The Open Source Telephony Project  18.5.0
Data Structures | Macros | Enumerations | Functions | Variables
res_config_pgsql.c File Reference

PostgreSQL plugin for Asterisk RealTime Architecture. More...

#include "asterisk.h"
#include <libpq-fe.h>
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/config.h"
#include "asterisk/module.h"
#include "asterisk/lock.h"
#include "asterisk/utils.h"
#include "asterisk/cli.h"
Include dependency graph for res_config_pgsql.c:

Go to the source code of this file.

Data Structures

struct  columns
 
struct  tables::psql_columns
 
struct  psql_tables
 
struct  tables
 

Macros

#define ESCAPE_CLAUSE   (USE_BACKSLASH_AS_STRING ? " ESCAPE '\\'" : " ESCAPE '\\\\'")
 
#define ESCAPE_STRING(buffer, stringname)
 
#define has_schema_support   (version > 70300 ? 1 : 0)
 
#define IS_SQL_LIKE_CLAUSE(x)   ((x) && ast_ends_with(x, " LIKE"))
 
#define MAX_DB_OPTION_SIZE   64
 
#define release_table(table)   ast_rwlock_unlock(&(table)->lock);
 
#define RES_CONFIG_PGSQL_CONF   "res_pgsql.conf"
 
#define USE_BACKSLASH_AS_STRING   (version >= 90100 ? 1 : 0)
 

Enumerations

enum  { RQ_WARN, RQ_CREATECLOSE, RQ_CREATECHAR }
 

Functions

static void __init_escapebuf_buf (void)
 
static void __init_findtable_buf (void)
 
static void __init_semibuf_buf (void)
 
static void __init_sql_buf (void)
 
static void __init_where_buf (void)
 
static void __reg_module (void)
 
static void __unreg_module (void)
 
static int _pgsql_exec (const char *database, const char *tablename, const char *sql, PGresult **result)
 Helper function for pgsql_exec. For running querys, use pgsql_exec() More...
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
static struct ast_configconfig_pgsql (const char *database, const char *table, const char *file, struct ast_config *cfg, struct ast_flags flags, const char *suggested_incl, const char *who_asked)
 
static int destroy_pgsql (const char *database, const char *table, const char *keyfield, const char *lookup, const struct ast_variable *fields)
 
static void destroy_table (struct tables *table)
 
static struct columnsfind_column (struct tables *t, const char *colname)
 
static struct tablesfind_table (const char *database, const char *orig_tablename)
 
static char * handle_cli_realtime_pgsql_cache (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 
static char * handle_cli_realtime_pgsql_status (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 
static int load_module (void)
 
static int parse_config (int reload)
 
static int pgsql_exec (const char *database, const char *tablename, const char *sql, PGresult **result)
 Do a postgres query, with reconnection support. More...
 
static int pgsql_reconnect (const char *database)
 
static struct ast_configrealtime_multi_pgsql (const char *database, const char *table, const struct ast_variable *fields)
 
static struct ast_variablerealtime_pgsql (const char *database, const char *tablename, const struct ast_variable *fields)
 
static int reload (void)
 
static int require_pgsql (const char *database, const char *tablename, va_list ap)
 
static int store_pgsql (const char *database, const char *table, const struct ast_variable *fields)
 
static int unload_module (void)
 
static int unload_pgsql (const char *database, const char *tablename)
 
static int update2_pgsql (const char *database, const char *tablename, const struct ast_variable *lookup_fields, const struct ast_variable *update_fields)
 
static int update_pgsql (const char *database, const char *tablename, const char *keyfield, const char *lookup, const struct ast_variable *fields)
 

Variables

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

Detailed Description

PostgreSQL plugin for Asterisk RealTime Architecture.

Author
Mark Spencer marks.nosp@m.ter@.nosp@m.digiu.nosp@m.m.co.nosp@m.m
Manuel Guesdon mgues.nosp@m.don@.nosp@m.oxymi.nosp@m.um.n.nosp@m.et - PostgreSQL RealTime Driver Author/Adaptor

PostgreSQL http://www.postgresql.org

Definition in file res_config_pgsql.c.

Macro Definition Documentation

◆ ESCAPE_CLAUSE

#define ESCAPE_CLAUSE   (USE_BACKSLASH_AS_STRING ? " ESCAPE '\\'" : " ESCAPE '\\\\'")

Definition at line 388 of file res_config_pgsql.c.

Referenced by realtime_multi_pgsql(), and realtime_pgsql().

◆ ESCAPE_STRING

#define ESCAPE_STRING (   buffer,
  stringname 
)

◆ has_schema_support

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

Definition at line 54 of file res_config_pgsql.c.

Referenced by find_table().

◆ IS_SQL_LIKE_CLAUSE

#define IS_SQL_LIKE_CLAUSE (   x)    ((x) && ast_ends_with(x, " LIKE"))

Definition at line 387 of file res_config_pgsql.c.

Referenced by realtime_multi_pgsql(), and realtime_pgsql().

◆ MAX_DB_OPTION_SIZE

#define MAX_DB_OPTION_SIZE   64

Definition at line 57 of file res_config_pgsql.c.

◆ release_table

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

◆ RES_CONFIG_PGSQL_CONF

#define RES_CONFIG_PGSQL_CONF   "res_pgsql.conf"

Definition at line 50 of file res_config_pgsql.c.

Referenced by config_pgsql(), and parse_config().

◆ USE_BACKSLASH_AS_STRING

#define USE_BACKSLASH_AS_STRING   (version >= 90100 ? 1 : 0)

Definition at line 55 of file res_config_pgsql.c.

Enumeration Type Documentation

◆ anonymous enum

anonymous enum
Enumerator
RQ_WARN 
RQ_CREATECLOSE 
RQ_CREATECHAR 

Definition at line 91 of file res_config_pgsql.c.

Function Documentation

◆ __init_escapebuf_buf()

static void __init_escapebuf_buf ( void  )
static

Definition at line 47 of file res_config_pgsql.c.

54 : 0)

◆ __init_findtable_buf()

static void __init_findtable_buf ( void  )
static

Definition at line 45 of file res_config_pgsql.c.

54 : 0)

◆ __init_semibuf_buf()

static void __init_semibuf_buf ( void  )
static

Definition at line 48 of file res_config_pgsql.c.

54 : 0)

◆ __init_sql_buf()

static void __init_sql_buf ( void  )
static

Definition at line 44 of file res_config_pgsql.c.

54 : 0)

◆ __init_where_buf()

static void __init_where_buf ( void  )
static

Definition at line 46 of file res_config_pgsql.c.

54 : 0)

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 1722 of file res_config_pgsql.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 1722 of file res_config_pgsql.c.

◆ _pgsql_exec()

static int _pgsql_exec ( const char *  database,
const char *  tablename,
const char *  sql,
PGresult **  result 
)
static

Helper function for pgsql_exec. For running querys, use pgsql_exec()

Connect if not currently connected. Run the given query.

Parameters
databasedatabase name we are connected to (used for error logging)
tablenametable name we are connected to (used for error logging)
sqlsql query string to execute
resultpointer for where to store the result handle
Returns
-1 on fatal query error
-2 on query failure that resulted in disconnection
0 on success
Note
see pgsql_exec for full example

Definition at line 144 of file res_config_pgsql.c.

References ast_debug, ast_log, LOG_ERROR, LOG_NOTICE, NULL, pgsql_reconnect(), and pgsqlConn.

Referenced by pgsql_exec().

145 {
146  ExecStatusType result_status;
147 
148  if (!pgsqlConn) {
149  ast_debug(1, "PostgreSQL connection not defined, connecting\n");
150 
151  if (pgsql_reconnect(database) != 1) {
152  ast_log(LOG_NOTICE, "reconnect failed\n");
153  *result = NULL;
154  return -1;
155  }
156 
157  ast_debug(1, "PostgreSQL connection successful\n");
158  }
159 
160  *result = PQexec(pgsqlConn, sql);
161  result_status = PQresultStatus(*result);
162  if (result_status != PGRES_COMMAND_OK
163  && result_status != PGRES_TUPLES_OK
164  && result_status != PGRES_NONFATAL_ERROR) {
165 
166  ast_log(LOG_ERROR, "PostgreSQL RealTime: Failed to query '%s@%s'.\n", tablename, database);
167  ast_log(LOG_ERROR, "PostgreSQL RealTime: Query Failed: %s\n", sql);
168  ast_log(LOG_ERROR, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
169  PQresultErrorMessage(*result),
170  PQresStatus(result_status));
171 
172  /* we may have tried to run a command on a disconnected/disconnecting handle */
173  /* are we no longer connected to the database... if not try again */
174  if (PQstatus(pgsqlConn) != CONNECTION_OK) {
175  PQfinish(pgsqlConn);
176  pgsqlConn = NULL;
177  return -2;
178  }
179 
180  /* connection still okay, which means the query is just plain bad */
181  return -1;
182  }
183 
184  ast_debug(1, "PostgreSQL query successful: %s\n", sql);
185  return 0;
186 }
static PGconn * pgsqlConn
#define NULL
Definition: resample.c:96
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
static int pgsql_reconnect(const char *database)
#define LOG_ERROR
Definition: logger.h:285
#define LOG_NOTICE
Definition: logger.h:263
static PGresult * result
Definition: cel_pgsql.c:88

◆ AST_MODULE_SELF_SYM()

struct ast_module* AST_MODULE_SELF_SYM ( void  )

Definition at line 1722 of file res_config_pgsql.c.

◆ config_pgsql()

static struct ast_config* config_pgsql ( const char *  database,
const char *  table,
const char *  file,
struct ast_config cfg,
struct ast_flags  flags,
const char *  suggested_incl,
const char *  who_asked 
)
static

Definition at line 1132 of file res_config_pgsql.c.

References ast_category_append(), ast_category_new_dynamic, ast_config_internal_load(), ast_copy_string(), ast_debug, ast_log, ast_mutex_lock, ast_mutex_unlock, ast_str_buffer(), ast_str_set(), ast_str_thread_get(), ast_variable_append(), ast_variable_new, dbname, columns::last, LOG_WARNING, NULL, pgsql_exec(), pgsql_lock, RAII_VAR, RES_CONFIG_PGSQL_CONF, result, and sql_buf.

1135 {
1136  RAII_VAR(PGresult *, result, NULL, PQclear);
1137  long num_rows;
1138  struct ast_variable *new_v;
1139  struct ast_category *cur_cat = NULL;
1140  struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
1141  char last[80];
1142  int last_cat_metric = 0;
1143 
1144  last[0] = '\0';
1145 
1146  /*
1147  * Ignore database from the extconfig.conf since it is
1148  * configured by res_pgsql.conf.
1149  */
1150  database = dbname;
1151 
1152  if (!file || !strcmp(file, RES_CONFIG_PGSQL_CONF)) {
1153  ast_log(LOG_WARNING, "PostgreSQL RealTime: Cannot configure myself.\n");
1154  return NULL;
1155  }
1156 
1157  ast_str_set(&sql, 0, "SELECT category, var_name, var_val, cat_metric FROM %s "
1158  "WHERE filename='%s' and commented=0 "
1159  "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ", table, file);
1160 
1161  ast_debug(1, "PostgreSQL RealTime: Static SQL: %s\n", ast_str_buffer(sql));
1162 
1164 
1165  /* We now have our complete statement; Lets connect to the server and execute it. */
1166  if (pgsql_exec(database, table, ast_str_buffer(sql), &result) != 0) {
1168  return NULL;
1169  }
1170 
1171  if ((num_rows = PQntuples(result)) > 0) {
1172  int rowIndex = 0;
1173 
1174  ast_debug(1, "PostgreSQL RealTime: Found %ld rows.\n", num_rows);
1175 
1176  for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
1177  char *field_category = PQgetvalue(result, rowIndex, 0);
1178  char *field_var_name = PQgetvalue(result, rowIndex, 1);
1179  char *field_var_val = PQgetvalue(result, rowIndex, 2);
1180  char *field_cat_metric = PQgetvalue(result, rowIndex, 3);
1181  if (!strcmp(field_var_name, "#include")) {
1182  if (!ast_config_internal_load(field_var_val, cfg, flags, "", who_asked)) {
1184  return NULL;
1185  }
1186  continue;
1187  }
1188 
1189  if (strcmp(last, field_category) || last_cat_metric != atoi(field_cat_metric)) {
1190  cur_cat = ast_category_new_dynamic(field_category);
1191  if (!cur_cat) {
1192  break;
1193  }
1194  ast_copy_string(last, field_category, sizeof(last));
1195  last_cat_metric = atoi(field_cat_metric);
1196  ast_category_append(cfg, cur_cat);
1197  }
1198  new_v = ast_variable_new(field_var_name, field_var_val, "");
1199  ast_variable_append(cur_cat, new_v);
1200  }
1201  } else {
1203  "PostgreSQL RealTime: Could not find config '%s' in database.\n", file);
1204  }
1205 
1207 
1208  return cfg;
1209 }
static char dbname[MAX_DB_OPTION_SIZE]
#define LOG_WARNING
Definition: logger.h:274
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
Structure for variables, used for configurations and for channel variables.
#define ast_mutex_lock(a)
Definition: lock.h:187
#define NULL
Definition: resample.c:96
static ast_mutex_t pgsql_lock
struct ast_config * ast_config_internal_load(const char *configfile, struct ast_config *cfg, struct ast_flags flags, const char *suggested_incl_file, const char *who_asked)
Definition: main/config.c:3112
static char * table
Definition: cdr_odbc.c:58
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1065
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
#define RES_CONFIG_PGSQL_CONF
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:911
struct sla_ringing_trunk * last
Definition: app_meetme.c:1092
#define ast_variable_new(name, value, filename)
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
#define ast_category_new_dynamic(name)
Create a category that is not backed by a file.
static int pgsql_exec(const char *database, const char *tablename, const char *sql, PGresult **result)
Do a postgres query, with reconnection support.
static struct ast_threadstorage sql_buf
void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
Definition: extconf.c:1178
void ast_category_append(struct ast_config *config, struct ast_category *cat)
Appends a category to a config.
Definition: extconf.c:2835
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
static PGresult * result
Definition: cel_pgsql.c:88
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:861
#define ast_mutex_unlock(a)
Definition: lock.h:188

◆ destroy_pgsql()

static int destroy_pgsql ( const char *  database,
const char *  table,
const char *  keyfield,
const char *  lookup,
const struct ast_variable fields 
)
static

Definition at line 1052 of file res_config_pgsql.c.

References ast_debug, ast_log, ast_mutex_lock, ast_mutex_unlock, ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_thread_get(), ast_strlen_zero, buf1, buf2, dbname, ESCAPE_STRING, escapebuf_buf, LOG_WARNING, ast_variable::name, ast_variable::next, NULL, pgsql_exec(), pgsql_lock, pgsql_reconnect(), pgsqlConn, RAII_VAR, result, sql_buf, ast_variable::value, and where_buf.

1053 {
1054  RAII_VAR(PGresult *, result, NULL, PQclear);
1055  int numrows = 0;
1056  int pgresult;
1057  struct ast_str *sql = ast_str_thread_get(&sql_buf, 256);
1059  const struct ast_variable *field;
1060 
1061  /*
1062  * Ignore database from the extconfig.conf since it was
1063  * configured by res_pgsql.conf.
1064  */
1065  database = dbname;
1066 
1067  if (!table) {
1068  ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
1069  return -1;
1070  }
1071 
1072  /*
1073  * Must connect to the server before anything else as ESCAPE_STRING()
1074  * uses pgsqlConn
1075  */
1077  if (!pgsql_reconnect(database)) {
1079  return -1;
1080  }
1081 
1082  /* Get the first parameter and first value in our list of passed paramater/value pairs */
1083  if (ast_strlen_zero(keyfield) || ast_strlen_zero(lookup)) {
1085  "PostgreSQL RealTime: Realtime destroy requires at least 1 parameter and 1 value to search on.\n");
1086  if (pgsqlConn) {
1087  PQfinish(pgsqlConn);
1088  pgsqlConn = NULL;
1089  }
1091  return -1;
1092  }
1093 
1094  /* Create the first part of the query using the first parameter/value pairs we just extracted
1095  If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
1096 
1097  ESCAPE_STRING(buf1, keyfield);
1098  ESCAPE_STRING(buf2, lookup);
1099  ast_str_set(&sql, 0, "DELETE FROM %s WHERE %s = '%s'", table, ast_str_buffer(buf1), ast_str_buffer(buf2));
1100  for (field = fields; field; field = field->next) {
1101  ESCAPE_STRING(buf1, field->name);
1102  ESCAPE_STRING(buf2, field->value);
1103  ast_str_append(&sql, 0, " AND %s = '%s'", ast_str_buffer(buf1), ast_str_buffer(buf2));
1104  }
1105 
1106  ast_debug(1, "PostgreSQL RealTime: Delete SQL: %s\n", ast_str_buffer(sql));
1107 
1108  /* We now have our complete statement; Lets connect to the server and execute it. */
1109  if (pgsql_exec(database, table, ast_str_buffer(sql), &result) != 0) {
1111  return -1;
1112  }
1113 
1114  numrows = atoi(PQcmdTuples(result));
1116 
1117  ast_debug(1, "PostgreSQL RealTime: Deleted %d rows on table: %s\n", numrows, table);
1118 
1119  /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
1120  * An integer greater than zero indicates the number of rows affected
1121  * Zero indicates that no records were updated
1122  * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
1123  */
1124 
1125  if (numrows >= 0)
1126  return (int) numrows;
1127 
1128  return -1;
1129 }
struct ast_variable * next
static PGconn * pgsqlConn
static struct ast_threadstorage escapebuf_buf
static char dbname[MAX_DB_OPTION_SIZE]
#define LOG_WARNING
Definition: logger.h:274
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
Structure for variables, used for configurations and for channel variables.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1091
static struct ast_threadstorage buf2
#define ast_mutex_lock(a)
Definition: lock.h:187
#define NULL
Definition: resample.c:96
static ast_mutex_t pgsql_lock
#define ESCAPE_STRING(buffer, stringname)
#define ast_strlen_zero(foo)
Definition: strings.h:52
static char * table
Definition: cdr_odbc.c:58
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1065
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:911
static struct ast_threadstorage where_buf
static int pgsql_reconnect(const char *database)
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
static struct ast_threadstorage buf1
static int pgsql_exec(const char *database, const char *tablename, const char *sql, PGresult **result)
Do a postgres query, with reconnection support.
static struct ast_threadstorage sql_buf
static PGresult * result
Definition: cel_pgsql.c:88
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:861
#define ast_mutex_unlock(a)
Definition: lock.h:188

◆ destroy_table()

static void destroy_table ( struct tables table)
static

Definition at line 117 of file res_config_pgsql.c.

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

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

118 {
119  struct columns *column;
120  ast_rwlock_wrlock(&table->lock);
121  while ((column = AST_LIST_REMOVE_HEAD(&table->columns, list))) {
122  ast_free(column);
123  }
124  ast_rwlock_unlock(&table->lock);
125  ast_rwlock_destroy(&table->lock);
126  ast_free(table);
127 }
struct columns::@8 list
#define ast_rwlock_destroy(rwlock)
Definition: lock.h:231
#define ast_rwlock_unlock(a)
Definition: lock.h:232
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:832
#define ast_free(a)
Definition: astmm.h:182
#define ast_rwlock_wrlock(a)
Definition: lock.h:234
ast_mutex_t lock
struct tables::mysql_columns columns

◆ find_column()

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

Definition at line 374 of file res_config_pgsql.c.

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

Referenced by update2_pgsql(), and update_pgsql().

375 {
376  struct columns *column;
377 
378  /* Check that the column exists in the table */
379  AST_LIST_TRAVERSE(&t->columns, column, list) {
380  if (strcmp(column->name, colname) == 0) {
381  return column;
382  }
383  }
384  return NULL;
385 }
struct columns::@8 list
#define NULL
Definition: resample.c:96
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
struct tables::mysql_columns columns

◆ find_table()

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

Definition at line 250 of file res_config_pgsql.c.

References ast_alloca, ast_calloc, ast_debug, AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log, ast_mutex_lock, ast_mutex_unlock, ast_rwlock_init, ast_rwlock_rdlock, ast_str_buffer(), ast_str_set(), ast_str_thread_get(), ast_strdupa, ast_strlen_zero, ast_verb, tables::columns, destroy_table(), findtable_buf, has_schema_support, columns::hasdefault, columns::len, columns::list, tables::lock, LOG_ERROR, columns::name, tables::name, columns::notnull, NULL, pgsql_exec(), pgsql_lock, pgsqlConn, RAII_VAR, result, table, and columns::type.

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

251 {
252  struct columns *column;
253  struct tables *table;
254  struct ast_str *sql = ast_str_thread_get(&findtable_buf, 330);
255  RAII_VAR(PGresult *, result, NULL, PQclear);
256  int exec_result;
257  char *fname, *ftype, *flen, *fnotnull, *fdef;
258  int i, rows;
259 
261  AST_LIST_TRAVERSE(&psql_tables, table, list) {
262  if (!strcasecmp(table->name, orig_tablename)) {
263  ast_debug(1, "Found table in cache; now locking\n");
264  ast_rwlock_rdlock(&table->lock);
265  ast_debug(1, "Lock cached table; now returning\n");
267  return table;
268  }
269  }
270 
271  if (database == NULL) {
273  return NULL;
274  }
275 
276  ast_debug(1, "Table '%s' not found in cache, querying now\n", orig_tablename);
277 
278  /* Not found, scan the table */
279  if (has_schema_support) {
280  char *schemaname, *tablename, *tmp_schemaname, *tmp_tablename;
281  if (strchr(orig_tablename, '.')) {
282  tmp_schemaname = ast_strdupa(orig_tablename);
283  tmp_tablename = strchr(tmp_schemaname, '.');
284  *tmp_tablename++ = '\0';
285  } else {
286  tmp_schemaname = "";
287  tmp_tablename = ast_strdupa(orig_tablename);
288  }
289 
290  tablename = ast_alloca(strlen(tmp_tablename) * 2 + 1);
291  PQescapeStringConn(pgsqlConn, tablename, tmp_tablename, strlen(tmp_tablename), NULL);
292  schemaname = ast_alloca(strlen(tmp_schemaname) * 2 + 1);
293  PQescapeStringConn(pgsqlConn, schemaname, tmp_schemaname, strlen(tmp_schemaname), NULL);
294 
295  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",
296  tablename,
297  ast_strlen_zero(schemaname) ? "" : "'", ast_strlen_zero(schemaname) ? "current_schema()" : schemaname, ast_strlen_zero(schemaname) ? "" : "'");
298  } else {
299  char *tablename;
300  tablename = ast_alloca(strlen(orig_tablename) * 2 + 1);
301  PQescapeStringConn(pgsqlConn, tablename, orig_tablename, strlen(orig_tablename), NULL);
302 
303  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);
304  }
305 
307  exec_result = pgsql_exec(database, orig_tablename, ast_str_buffer(sql), &result);
309  ast_debug(1, "Query of table structure complete. Now retrieving results.\n");
310  if (exec_result != 0) {
311  ast_log(LOG_ERROR, "Failed to query database columns for table %s\n", orig_tablename);
313  return NULL;
314  }
315 
316  if (!(table = ast_calloc(1, sizeof(*table) + strlen(orig_tablename) + 1))) {
317  ast_log(LOG_ERROR, "Unable to allocate memory for new table structure\n");
319  return NULL;
320  }
321  strcpy(table->name, orig_tablename); /* SAFE */
322  ast_rwlock_init(&table->lock);
324 
325  rows = PQntuples(result);
326  for (i = 0; i < rows; i++) {
327  fname = PQgetvalue(result, i, 0);
328  ftype = PQgetvalue(result, i, 1);
329  flen = PQgetvalue(result, i, 2);
330  fnotnull = PQgetvalue(result, i, 3);
331  fdef = PQgetvalue(result, i, 4);
332  ast_verb(4, "Found column '%s' of type '%s'\n", fname, ftype);
333 
334  if (!(column = ast_calloc(1, sizeof(*column) + strlen(fname) + strlen(ftype) + 2))) {
335  ast_log(LOG_ERROR, "Unable to allocate column element for %s, %s\n", orig_tablename, fname);
336  destroy_table(table);
338  return NULL;
339  }
340 
341  if (strcmp(flen, "-1") == 0) {
342  /* Some types, like chars, have the length stored in a different field */
343  flen = PQgetvalue(result, i, 5);
344  sscanf(flen, "%30d", &column->len);
345  column->len -= 4;
346  } else {
347  sscanf(flen, "%30d", &column->len);
348  }
349  column->name = (char *)column + sizeof(*column);
350  column->type = (char *)column + sizeof(*column) + strlen(fname) + 1;
351  strcpy(column->name, fname);
352  strcpy(column->type, ftype);
353  if (*fnotnull == 't') {
354  column->notnull = 1;
355  } else {
356  column->notnull = 0;
357  }
358  if (!ast_strlen_zero(fdef)) {
359  column->hasdefault = 1;
360  } else {
361  column->hasdefault = 0;
362  }
363  AST_LIST_INSERT_TAIL(&table->columns, column, list);
364  }
365 
366  AST_LIST_INSERT_TAIL(&psql_tables, table, list);
367  ast_rwlock_rdlock(&table->lock);
369  return table;
370 }
unsigned int hasdefault
Definition: cdr_pgsql.c:98
#define has_schema_support
#define ast_rwlock_rdlock(a)
Definition: lock.h:233
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
static void destroy_table(struct tables *table)
static PGconn * pgsqlConn
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
#define ast_mutex_lock(a)
Definition: lock.h:187
#define NULL
Definition: resample.c:96
static ast_mutex_t pgsql_lock
#define ast_verb(level,...)
Definition: logger.h:463
#define ast_strlen_zero(foo)
Definition: strings.h:52
static char * table
Definition: cdr_odbc.c:58
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1065
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
char name[0]
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:911
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:290
#define LOG_ERROR
Definition: logger.h:285
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:730
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
#define ast_rwlock_init(rwlock)
wrapper for rwlock with tracking enabled
Definition: lock.h:222
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
static struct ast_threadstorage findtable_buf
static int pgsql_exec(const char *database, const char *tablename, const char *sql, PGresult **result)
Do a postgres query, with reconnection support.
ast_mutex_t lock
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:680
unsigned int notnull
Definition: cdr_pgsql.c:97
static PGresult * result
Definition: cel_pgsql.c:88
struct tables::mysql_columns columns
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:861
#define ast_mutex_unlock(a)
Definition: lock.h:188

◆ handle_cli_realtime_pgsql_cache()

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

Definition at line 1615 of file res_config_pgsql.c.

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

1616 {
1617  struct tables *cur;
1618  int l, which;
1619  char *ret = NULL;
1620 
1621  switch (cmd) {
1622  case CLI_INIT:
1623  e->command = "realtime show pgsql cache";
1624  e->usage =
1625  "Usage: realtime show pgsql cache [<table>]\n"
1626  " Shows table cache for the PostgreSQL RealTime driver\n";
1627  return NULL;
1628  case CLI_GENERATE:
1629  if (a->argc != 4) {
1630  return NULL;
1631  }
1632  l = strlen(a->word);
1633  which = 0;
1636  if (!strncasecmp(a->word, cur->name, l) && ++which > a->n) {
1637  ret = ast_strdup(cur->name);
1638  break;
1639  }
1640  }
1642  return ret;
1643  }
1644 
1645  if (a->argc == 4) {
1646  /* List of tables */
1649  ast_cli(a->fd, "%s\n", cur->name);
1650  }
1652  } else if (a->argc == 5) {
1653  /* List of columns */
1654  if ((cur = find_table(NULL, a->argv[4]))) {
1655  struct columns *col;
1656  ast_cli(a->fd, "Columns for Table Cache '%s':\n", a->argv[4]);
1657  ast_cli(a->fd, "%-20.20s %-20.20s %-3.3s %-8.8s\n", "Name", "Type", "Len", "Nullable");
1658  AST_LIST_TRAVERSE(&cur->columns, col, list) {
1659  ast_cli(a->fd, "%-20.20s %-20.20s %3d %-8.8s\n", col->name, col->type, col->len, col->notnull ? "NOT NULL" : "");
1660  }
1661  release_table(cur);
1662  } else {
1663  ast_cli(a->fd, "No such table '%s'\n", a->argv[4]);
1664  }
1665  }
1666  return 0;
1667 }
struct columns::@8 list
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
#define release_table(table)
const int argc
Definition: cli.h:160
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
Definition: cli.h:152
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
#define NULL
Definition: resample.c:96
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
static struct tables * find_table(const char *database, const char *orig_tablename)
char name[0]
const int fd
Definition: cli.h:159
const int n
Definition: cli.h:165
const char *const * argv
Definition: cli.h:161
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
struct tables::@9 list
char * command
Definition: cli.h:186
const char * word
Definition: cli.h:163
const char * usage
Definition: cli.h:177
unsigned int notnull
Definition: cdr_pgsql.c:97
struct tables::mysql_columns columns

◆ handle_cli_realtime_pgsql_status()

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

Definition at line 1669 of file res_config_pgsql.c.

References ast_cli_args::argc, ast_cli(), ast_cli_print_timestr_fromseconds(), AST_MODFLAG_LOAD_ORDER, AST_MODPRI_REALTIME_DRIVER, AST_MODULE_INFO(), AST_MODULE_SUPPORT_EXTENDED, ast_mutex_lock, ast_mutex_unlock, ast_strlen_zero, ASTERISK_GPL_KEY, buf, CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, connect_time, dbhost, dbname, dbport, dbsock, dbuser, ast_cli_args::fd, load_module(), NULL, pgsql_lock, pgsqlConn, reload(), unload_module(), and ast_cli_entry::usage.

1670 {
1671  char connection_info[256];
1672  char credentials[100] = "";
1673  char buf[376]; /* 256+100+"Connected to "+" for "+NULL */
1674  int is_connected = 0, ctimesec = time(NULL) - connect_time;
1675 
1676  switch (cmd) {
1677  case CLI_INIT:
1678  e->command = "realtime show pgsql status";
1679  e->usage =
1680  "Usage: realtime show pgsql status\n"
1681  " Shows connection information for the PostgreSQL RealTime driver\n";
1682  return NULL;
1683  case CLI_GENERATE:
1684  return NULL;
1685  }
1686 
1687  if (a->argc != 4)
1688  return CLI_SHOWUSAGE;
1689 
1690  if (!ast_strlen_zero(dbhost))
1691  snprintf(connection_info, sizeof(connection_info), "%s@%s, port %d", dbname, dbhost, dbport);
1692  else if (!ast_strlen_zero(dbsock))
1693  snprintf(connection_info, sizeof(connection_info), "%s on socket file %s", dbname, dbsock);
1694  else
1695  snprintf(connection_info, sizeof(connection_info), "%s@%s", dbname, dbhost);
1696 
1697  if (!ast_strlen_zero(dbuser))
1698  snprintf(credentials, sizeof(credentials), " with username %s", dbuser);
1699 
1701  is_connected = (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK);
1703 
1704  if (is_connected) {
1705  snprintf(buf, sizeof(buf), "Connected to %s%s for ", connection_info, credentials);
1706  ast_cli_print_timestr_fromseconds(a->fd, ctimesec, buf);
1707  return CLI_SUCCESS;
1708  } else {
1709  ast_cli(a->fd, "Unable to connect %s%s\n", connection_info, credentials);
1710  return CLI_FAILURE;
1711  }
1712 }
static int dbport
static PGconn * pgsqlConn
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static char dbname[MAX_DB_OPTION_SIZE]
const int argc
Definition: cli.h:160
Definition: cli.h:152
#define ast_mutex_lock(a)
Definition: lock.h:187
#define NULL
Definition: resample.c:96
static ast_mutex_t pgsql_lock
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
#define ast_strlen_zero(foo)
Definition: strings.h:52
const int fd
Definition: cli.h:159
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define CLI_FAILURE
Definition: cli.h:46
char * command
Definition: cli.h:186
static char dbuser[MAX_DB_OPTION_SIZE]
const char * usage
Definition: cli.h:177
void ast_cli_print_timestr_fromseconds(int fd, int seconds, const char *prefix)
Print on cli a duration in seconds in format s year(s), s week(s), s day(s), s hour(s), s second(s)
Definition: main/cli.c:3021
#define CLI_SUCCESS
Definition: cli.h:44
static char dbsock[MAX_DB_OPTION_SIZE]
static time_t connect_time
static char dbhost[MAX_DB_OPTION_SIZE]
#define ast_mutex_unlock(a)
Definition: lock.h:188

◆ load_module()

static int load_module ( void  )
static

Definition at line 1389 of file res_config_pgsql.c.

References ARRAY_LEN, ast_cli_register_multiple, ast_config_engine_register(), AST_MODULE_LOAD_DECLINE, and parse_config().

Referenced by handle_cli_realtime_pgsql_status().

1390 {
1391  if(!parse_config(0))
1392  return AST_MODULE_LOAD_DECLINE;
1393 
1395 
1397 
1398  return 0;
1399 }
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
int ast_config_engine_register(struct ast_config_engine *newconfig)
Register config engine.
Definition: main/config.c:2990
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
static struct ast_cli_entry cli_realtime[]
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
static int parse_config(int reload)
static struct ast_config_engine pgsql_engine

◆ parse_config()

static int parse_config ( int  reload)
static

Definition at line 1434 of file res_config_pgsql.c.

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

Referenced by load_module(), and reload().

1435 {
1436  struct ast_config *config;
1437  const char *s;
1438  struct ast_flags config_flags = { is_reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1439 
1440  config = ast_config_load(RES_CONFIG_PGSQL_CONF, config_flags);
1441  if (config == CONFIG_STATUS_FILEUNCHANGED) {
1442  if (is_reload && pgsqlConn && PQstatus(pgsqlConn) != CONNECTION_OK) {
1443  ast_log(LOG_WARNING, "PostgreSQL RealTime: Not connected\n");
1444  }
1445  return 0;
1446  }
1447 
1448  if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
1449  ast_log(LOG_WARNING, "Unable to load config %s\n", RES_CONFIG_PGSQL_CONF);
1450  return 0;
1451  }
1452 
1454 
1455  /* XXX: Why would we do this before we're ready to establish a new connection? */
1456  if (pgsqlConn) {
1457  PQfinish(pgsqlConn);
1458  pgsqlConn = NULL;
1459  }
1460 
1461  if (!(s = ast_variable_retrieve(config, "general", "dbuser"))) {
1463  "PostgreSQL RealTime: No database user found, using 'asterisk' as default.\n");
1464  strcpy(dbuser, "asterisk");
1465  } else {
1466  ast_copy_string(dbuser, s, sizeof(dbuser));
1467  }
1468 
1469  if (!(s = ast_variable_retrieve(config, "general", "dbpass"))) {
1471  "PostgreSQL RealTime: No database password found, using 'asterisk' as default.\n");
1472  strcpy(dbpass, "asterisk");
1473  } else {
1474  ast_copy_string(dbpass, s, sizeof(dbpass));
1475  }
1476 
1477  if (!(s = ast_variable_retrieve(config, "general", "dbhost"))) {
1479  "PostgreSQL RealTime: No database host found, using localhost via socket.\n");
1480  dbhost[0] = '\0';
1481  } else {
1482  ast_copy_string(dbhost, s, sizeof(dbhost));
1483  }
1484 
1485  if (!(s = ast_variable_retrieve(config, "general", "dbname"))) {
1487  "PostgreSQL RealTime: No database name found, using 'asterisk' as default.\n");
1488  strcpy(dbname, "asterisk");
1489  } else {
1490  ast_copy_string(dbname, s, sizeof(dbname));
1491  }
1492 
1493  if (!(s = ast_variable_retrieve(config, "general", "dbport"))) {
1495  "PostgreSQL RealTime: No database port found, using 5432 as default.\n");
1496  dbport = 5432;
1497  } else {
1498  dbport = atoi(s);
1499  }
1500 
1501  if (!(s = ast_variable_retrieve(config, "general", "dbappname"))) {
1502  dbappname[0] = '\0';
1503  } else {
1504  ast_copy_string(dbappname, s, sizeof(dbappname));
1505  }
1506 
1507  if (!ast_strlen_zero(dbhost)) {
1508  /* No socket needed */
1509  } else if (!(s = ast_variable_retrieve(config, "general", "dbsock"))) {
1511  "PostgreSQL RealTime: No database socket found, using '/tmp/.s.PGSQL.%d' as default.\n", dbport);
1512  strcpy(dbsock, "/tmp");
1513  } else {
1514  ast_copy_string(dbsock, s, sizeof(dbsock));
1515  }
1516 
1517  if (!(s = ast_variable_retrieve(config, "general", "requirements"))) {
1519  "PostgreSQL RealTime: no requirements setting found, using 'warn' as default.\n");
1521  } else if (!strcasecmp(s, "createclose")) {
1523  } else if (!strcasecmp(s, "createchar")) {
1525  }
1526 
1527  ast_config_destroy(config);
1528 
1529  if (DEBUG_ATLEAST(1)) {
1530  if (!ast_strlen_zero(dbhost)) {
1531  ast_log(LOG_DEBUG, "PostgreSQL RealTime Host: %s\n", dbhost);
1532  ast_log(LOG_DEBUG, "PostgreSQL RealTime Port: %i\n", dbport);
1533  } else {
1534  ast_log(LOG_DEBUG, "PostgreSQL RealTime Socket: %s\n", dbsock);
1535  }
1536  ast_log(LOG_DEBUG, "PostgreSQL RealTime User: %s\n", dbuser);
1537  ast_log(LOG_DEBUG, "PostgreSQL RealTime Password: %s\n", dbpass);
1538  ast_log(LOG_DEBUG, "PostgreSQL RealTime DBName: %s\n", dbname);
1539  }
1540 
1541  if (!pgsql_reconnect(NULL)) {
1543  "PostgreSQL RealTime: Couldn't establish connection. Check debug.\n");
1544  ast_debug(1, "PostgreSQL RealTime: Cannot Connect: %s\n", PQerrorMessage(pgsqlConn));
1545  }
1546 
1547  ast_verb(2, "PostgreSQL RealTime reloaded.\n");
1548 
1549  /* Done reloading. Release lock so others can now use driver. */
1551 
1552  return 1;
1553 }
char * config
Definition: conf2ael.c:66
static int dbport
static PGconn * pgsqlConn
static char dbname[MAX_DB_OPTION_SIZE]
#define LOG_WARNING
Definition: logger.h:274
#define CONFIG_STATUS_FILEINVALID
#define ast_mutex_lock(a)
Definition: lock.h:187
#define NULL
Definition: resample.c:96
static ast_mutex_t pgsql_lock
#define LOG_DEBUG
Definition: logger.h:241
static char dbpass[MAX_DB_OPTION_SIZE]
#define ast_verb(level,...)
Definition: logger.h:463
#define ast_strlen_zero(foo)
Definition: strings.h:52
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
#define ast_config_load(filename, flags)
Load a config file.
#define RES_CONFIG_PGSQL_CONF
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: extconf.c:1290
static char dbappname[MAX_DB_OPTION_SIZE]
static int pgsql_reconnect(const char *database)
#define CONFIG_STATUS_FILEUNCHANGED
requirements
Structure used to handle boolean flags.
Definition: utils.h:199
static char dbuser[MAX_DB_OPTION_SIZE]
#define CONFIG_STATUS_FILEMISSING
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:694
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
static char dbsock[MAX_DB_OPTION_SIZE]
#define DEBUG_ATLEAST(level)
Definition: logger.h:441
static char dbhost[MAX_DB_OPTION_SIZE]
#define ast_mutex_unlock(a)
Definition: lock.h:188

◆ pgsql_exec()

static int pgsql_exec ( const char *  database,
const char *  tablename,
const char *  sql,
PGresult **  result 
)
static

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.

Parameters
databasedatabase name we are connected to (used for error logging)
tablenametable name we are connected to (used for error logging)
sqlsql query string to execute
resultpointer for where to store the result handle
Returns
-1 on query failure
0 on success
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);
}

Definition at line 218 of file res_config_pgsql.c.

References _pgsql_exec(), ast_debug, ast_log, and LOG_NOTICE.

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

219 {
220  int attempts = 0;
221  int res;
222 
223  /* Try the query, note failure if any */
224  /* On first failure, reconnect and try again (_pgsql_exec handles reconnect) */
225  /* On second failure, treat as fatal query error */
226 
227  while (attempts++ < 2) {
228  ast_debug(1, "PostgreSQL query attempt %d\n", attempts);
229  res = _pgsql_exec(database, tablename, sql, result);
230 
231  if (res == 0) {
232  if (attempts > 1) {
233  ast_log(LOG_NOTICE, "PostgreSQL RealTime: Query finally succeeded: %s\n", sql);
234  }
235 
236  return 0;
237  }
238 
239  if (res == -1) {
240  return -1; /* Still connected to db, but could not process query (fatal error) */
241  }
242 
243  /* res == -2 (query on a disconnected handle) */
244  ast_debug(1, "PostgreSQL query attempt %d failed, trying again\n", attempts);
245  }
246 
247  return -1;
248 }
static int _pgsql_exec(const char *database, const char *tablename, const char *sql, PGresult **result)
Helper function for pgsql_exec. For running querys, use pgsql_exec()
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
#define LOG_NOTICE
Definition: logger.h:263
static PGresult * result
Definition: cel_pgsql.c:88

◆ pgsql_reconnect()

static int pgsql_reconnect ( const char *  database)
static

Definition at line 1555 of file res_config_pgsql.c.

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

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

1556 {
1557  char my_database[50];
1558 
1559  ast_copy_string(my_database, S_OR(database, dbname), sizeof(my_database));
1560 
1561  /* mutex lock should have been locked before calling this function. */
1562 
1563  if (pgsqlConn) {
1564  if (PQstatus(pgsqlConn) == CONNECTION_OK) {
1565  /* We're good? */
1566  return 1;
1567  }
1568 
1569  PQfinish(pgsqlConn);
1570  pgsqlConn = NULL;
1571  }
1572 
1573  /* DB password can legitimately be 0-length */
1574  if ((!ast_strlen_zero(dbhost) || !ast_strlen_zero(dbsock)) && !ast_strlen_zero(dbuser) && !ast_strlen_zero(my_database)) {
1575  struct ast_str *conn_info = ast_str_create(128);
1576 
1577  if (!conn_info) {
1578  ast_log(LOG_ERROR, "PostgreSQL RealTime: Failed to allocate memory for connection string.\n");
1579  return 0;
1580  }
1581 
1582  ast_str_set(&conn_info, 0, "host=%s port=%d dbname=%s user=%s",
1583  S_OR(dbhost, dbsock), dbport, my_database, dbuser);
1584 
1585  if (!ast_strlen_zero(dbappname)) {
1586  ast_str_append(&conn_info, 0, " application_name=%s", dbappname);
1587  }
1588 
1589  if (!ast_strlen_zero(dbpass)) {
1590  ast_str_append(&conn_info, 0, " password=%s", dbpass);
1591  }
1592 
1593  pgsqlConn = PQconnectdb(ast_str_buffer(conn_info));
1594  ast_free(conn_info);
1595  conn_info = NULL;
1596 
1597  ast_debug(1, "pgsqlConn=%p\n", pgsqlConn);
1598  if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
1599  ast_debug(1, "PostgreSQL RealTime: Successfully connected to database.\n");
1600  connect_time = time(NULL);
1601  version = PQserverVersion(pgsqlConn);
1602  return 1;
1603  } else {
1605  "PostgreSQL RealTime: Failed to connect database %s on %s: %s\n",
1606  my_database, dbhost, PQresultErrorMessage(NULL));
1607  return 0;
1608  }
1609  } else {
1610  ast_debug(1, "PostgreSQL RealTime: One or more of the parameters in the config does not pass our validity checks.\n");
1611  return 1;
1612  }
1613 }
static int dbport
static PGconn * pgsqlConn
static char dbname[MAX_DB_OPTION_SIZE]
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1091
#define NULL
Definition: resample.c:96
static char dbpass[MAX_DB_OPTION_SIZE]
#define ast_strlen_zero(foo)
Definition: strings.h:52
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1065
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
static char dbappname[MAX_DB_OPTION_SIZE]
#define LOG_ERROR
Definition: logger.h:285
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
#define ast_free(a)
Definition: astmm.h:182
static int version
static char dbuser[MAX_DB_OPTION_SIZE]
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:79
static char dbsock[MAX_DB_OPTION_SIZE]
static time_t connect_time
static char dbhost[MAX_DB_OPTION_SIZE]
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620

◆ realtime_multi_pgsql()

static struct ast_config* realtime_multi_pgsql ( const char *  database,
const char *  table,
const struct ast_variable fields 
)
static

Definition at line 524 of file res_config_pgsql.c.

References ast_calloc, ast_category_append(), ast_category_new_anonymous, ast_category_rename(), ast_config_destroy(), ast_config_new(), ast_debug, ast_free, ast_log, ast_mutex_lock, ast_mutex_unlock, ast_realtime_decode_chunk(), ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_thread_get(), ast_strdupa, ast_strip(), ast_strlen_zero, ast_variable_append(), ast_variable_new, dbname, ESCAPE_CLAUSE, ESCAPE_STRING, escapebuf_buf, IS_SQL_LIKE_CLAUSE, LOG_ERROR, LOG_WARNING, ast_variable::name, ast_variable::next, NULL, pgsql_exec(), pgsql_lock, pgsql_reconnect(), pgsqlConn, RAII_VAR, result, sql_buf, strsep(), ast_variable::value, and var.

525 {
526  RAII_VAR(PGresult *, result, NULL, PQclear);
527  int num_rows = 0, pgresult;
528  struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
529  struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
530  const struct ast_variable *field = fields;
531  const char *initfield = NULL;
532  char *stringp;
533  char *chunk;
534  char *op;
535  char *escape = "";
536  struct ast_variable *var = NULL;
537  struct ast_config *cfg = NULL;
538  struct ast_category *cat = NULL;
539 
540  /*
541  * Ignore database from the extconfig.conf since it was
542  * configured by res_pgsql.conf.
543  */
544  database = dbname;
545 
546  if (!table) {
547  ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
548  return NULL;
549  }
550 
551  if (!(cfg = ast_config_new()))
552  return NULL;
553 
554  /*
555  * Must connect to the server before anything else as ESCAPE_STRING()
556  * uses pgsqlConn
557  */
559  if (!pgsql_reconnect(database)) {
561  return NULL;
562  }
563 
564  /* Get the first parameter and first value in our list of passed paramater/value pairs */
565  if (!field) {
567  "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
568  if (pgsqlConn) {
569  PQfinish(pgsqlConn);
570  pgsqlConn = NULL;
571  }
573  ast_config_destroy(cfg);
574  return NULL;
575  }
576 
577  initfield = ast_strdupa(field->name);
578  if ((op = strchr(initfield, ' '))) {
579  *op = '\0';
580  }
581 
582  /* Create the first part of the query using the first parameter/value pairs we just extracted
583  If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
584 
585  if (!strchr(field->name, ' ')) {
586  op = " =";
587  escape = "";
588  } else {
589  op = "";
590  if (IS_SQL_LIKE_CLAUSE(field->name)) {
591  escape = ESCAPE_CLAUSE;
592  }
593  }
594 
595  ESCAPE_STRING(escapebuf, field->value);
596  if (pgresult) {
597  ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
599  ast_config_destroy(cfg);
600  return NULL;
601  }
602 
603  ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'%s", table, field->name, op, ast_str_buffer(escapebuf), escape);
604  while ((field = field->next)) {
605  escape = "";
606  if (!strchr(field->name, ' ')) {
607  op = " =";
608  escape = "";
609  } else {
610  op = "";
611  if (IS_SQL_LIKE_CLAUSE(field->name)) {
612  escape = ESCAPE_CLAUSE;
613  }
614  }
615 
616  ESCAPE_STRING(escapebuf, field->value);
617  if (pgresult) {
618  ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
620  ast_config_destroy(cfg);
621  return NULL;
622  }
623 
624  ast_str_append(&sql, 0, " AND %s%s '%s'%s", field->name, op, ast_str_buffer(escapebuf), escape);
625  }
626 
627  if (initfield) {
628  ast_str_append(&sql, 0, " ORDER BY %s", initfield);
629  }
630 
631  /* We now have our complete statement; Lets connect to the server and execute it. */
632  if (pgsql_exec(database, table, ast_str_buffer(sql), &result) != 0) {
634  ast_config_destroy(cfg);
635  return NULL;
636  } else {
637  ExecStatusType result_status = PQresultStatus(result);
638  if (result_status != PGRES_COMMAND_OK
639  && result_status != PGRES_TUPLES_OK
640  && result_status != PGRES_NONFATAL_ERROR) {
642  "PostgreSQL RealTime: Failed to query %s@%s. Check debug for more info.\n", table, database);
643  ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
644  ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
645  PQresultErrorMessage(result), PQresStatus(result_status));
647  ast_config_destroy(cfg);
648  return NULL;
649  }
650  }
651 
652  ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, ast_str_buffer(sql));
653 
654  if ((num_rows = PQntuples(result)) > 0) {
655  int numFields = PQnfields(result);
656  int i = 0;
657  int rowIndex = 0;
658  char **fieldnames = NULL;
659 
660  ast_debug(1, "PostgreSQL RealTime: Found %d rows.\n", num_rows);
661 
662  if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
664  ast_config_destroy(cfg);
665  return NULL;
666  }
667  for (i = 0; i < numFields; i++)
668  fieldnames[i] = PQfname(result, i);
669 
670  for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
671  var = NULL;
673  if (!cat) {
674  continue;
675  }
676  for (i = 0; i < numFields; i++) {
677  stringp = PQgetvalue(result, rowIndex, i);
678  while (stringp) {
679  chunk = strsep(&stringp, ";");
680  if (chunk && !ast_strlen_zero(ast_realtime_decode_chunk(ast_strip(chunk)))) {
681  if (initfield && !strcmp(initfield, fieldnames[i])) {
682  ast_category_rename(cat, chunk);
683  }
684  var = ast_variable_new(fieldnames[i], chunk, "");
685  ast_variable_append(cat, var);
686  }
687  }
688  }
689  ast_category_append(cfg, cat);
690  }
691  ast_free(fieldnames);
692  } else {
693  ast_debug(1, "PostgreSQL RealTime: Could not find any rows in table %s.\n", table);
694  }
695 
697 
698  return cfg;
699 }
struct ast_variable * next
char * ast_realtime_decode_chunk(char *chunk)
Remove standard encoding from realtime values, which ensures that a semicolon embedded within a singl...
Definition: main/config.c:3625
#define ESCAPE_CLAUSE
static PGconn * pgsqlConn
static struct ast_threadstorage escapebuf_buf
static char dbname[MAX_DB_OPTION_SIZE]
#define LOG_WARNING
Definition: logger.h:274
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
Structure for variables, used for configurations and for channel variables.
#define var
Definition: ast_expr2f.c:614
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1091
#define ast_mutex_lock(a)
Definition: lock.h:187
#define NULL
Definition: resample.c:96
static ast_mutex_t pgsql_lock
#define ESCAPE_STRING(buffer, stringname)
#define ast_strlen_zero(foo)
Definition: strings.h:52
#define ast_category_new_anonymous()
Create a nameless category that is not backed by a file.
static char * table
Definition: cdr_odbc.c:58
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1065
void ast_category_rename(struct ast_category *cat, const char *name)
Definition: main/config.c:1362
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:911
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:219
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: extconf.c:1290
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
#define ast_variable_new(name, value, filename)
static int pgsql_reconnect(const char *database)
struct ast_config * ast_config_new(void)
Create a new base configuration structure.
Definition: extconf.c:3276
#define LOG_ERROR
Definition: logger.h:285
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
static int pgsql_exec(const char *database, const char *tablename, const char *sql, PGresult **result)
Do a postgres query, with reconnection support.
static struct ast_threadstorage sql_buf
void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
Definition: extconf.c:1178
void ast_category_append(struct ast_config *config, struct ast_category *cat)
Appends a category to a config.
Definition: extconf.c:2835
char * strsep(char **str, const char *delims)
static PGresult * result
Definition: cel_pgsql.c:88
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:861
#define IS_SQL_LIKE_CLAUSE(x)
#define ast_mutex_unlock(a)
Definition: lock.h:188

◆ realtime_pgsql()

static struct ast_variable* realtime_pgsql ( const char *  database,
const char *  tablename,
const struct ast_variable fields 
)
static

Definition at line 390 of file res_config_pgsql.c.

References ast_calloc, ast_debug, ast_free, ast_log, ast_mutex_lock, ast_mutex_unlock, ast_realtime_decode_chunk(), ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_thread_get(), ast_strip(), ast_strlen_zero, ast_variable_new, dbname, ESCAPE_CLAUSE, ESCAPE_STRING, escapebuf_buf, IS_SQL_LIKE_CLAUSE, LOG_ERROR, LOG_WARNING, ast_variable::name, ast_variable::next, NULL, pgsql_exec(), pgsql_lock, pgsql_reconnect(), pgsqlConn, RAII_VAR, result, sql_buf, strsep(), ast_variable::value, and var.

391 {
392  RAII_VAR(PGresult *, result, NULL, PQclear);
393  int pgresult;
394  struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
395  struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
396  char *stringp;
397  char *chunk;
398  char *op;
399  char *escape = "";
400  const struct ast_variable *field = fields;
401  struct ast_variable *var = NULL, *prev = NULL;
402 
403  /*
404  * Ignore database from the extconfig.conf since it was
405  * configured by res_pgsql.conf.
406  */
407  database = dbname;
408 
409  if (!tablename) {
410  ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
411  return NULL;
412  }
413 
414  /*
415  * Must connect to the server before anything else as ESCAPE_STRING()
416  * uses pgsqlConn
417  */
419  if (!pgsql_reconnect(database)) {
421  return NULL;
422  }
423 
424  /* Get the first parameter and first value in our list of passed paramater/value pairs */
425  if (!field) {
427  "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
428  if (pgsqlConn) {
429  PQfinish(pgsqlConn);
430  pgsqlConn = NULL;
431  }
433  return NULL;
434  }
435 
436  /* Create the first part of the query using the first parameter/value pairs we just extracted
437  If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
438  if (!strchr(field->name, ' ')) {
439  op = " =";
440  } else {
441  op = "";
442  if (IS_SQL_LIKE_CLAUSE(field->name)) {
443  escape = ESCAPE_CLAUSE;
444  }
445  }
446 
447  ESCAPE_STRING(escapebuf, field->value);
448  if (pgresult) {
449  ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
451  return NULL;
452  }
453 
454  ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'%s", tablename, field->name, op, ast_str_buffer(escapebuf), escape);
455  while ((field = field->next)) {
456  escape = "";
457  if (!strchr(field->name, ' ')) {
458  op = " =";
459  } else {
460  op = "";
461  if (IS_SQL_LIKE_CLAUSE(field->name)) {
462  escape = ESCAPE_CLAUSE;
463  }
464  }
465 
466  ESCAPE_STRING(escapebuf, field->value);
467  if (pgresult) {
468  ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
470  return NULL;
471  }
472 
473  ast_str_append(&sql, 0, " AND %s%s '%s'%s", field->name, op, ast_str_buffer(escapebuf), escape);
474  }
475  ast_str_append(&sql, 0, " LIMIT 1");
476 
477  /* We now have our complete statement; Lets connect to the server and execute it. */
478  if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
480  return NULL;
481  }
482 
483  ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, ast_str_buffer(sql));
484 
485  if (PQntuples(result) > 0) {
486  int i = 0;
487  int numFields = PQnfields(result);
488  char **fieldnames = NULL;
489 
490  ast_debug(1, "PostgreSQL RealTime: Found a row.\n");
491 
492  if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
494  return NULL;
495  }
496  for (i = 0; i < numFields; i++)
497  fieldnames[i] = PQfname(result, i);
498  for (i = 0; i < numFields; i++) {
499  stringp = PQgetvalue(result, 0, i);
500  while (stringp) {
501  chunk = strsep(&stringp, ";");
502  if (chunk && !ast_strlen_zero(ast_realtime_decode_chunk(ast_strip(chunk)))) {
503  if (prev) {
504  prev->next = ast_variable_new(fieldnames[i], chunk, "");
505  if (prev->next) {
506  prev = prev->next;
507  }
508  } else {
509  prev = var = ast_variable_new(fieldnames[i], chunk, "");
510  }
511  }
512  }
513  }
514  ast_free(fieldnames);
515  } else {
516  ast_debug(1, "Postgresql RealTime: Could not find any rows in table %s@%s.\n", tablename, database);
517  }
518 
520 
521  return var;
522 }
struct ast_variable * next
char * ast_realtime_decode_chunk(char *chunk)
Remove standard encoding from realtime values, which ensures that a semicolon embedded within a singl...
Definition: main/config.c:3625
#define ESCAPE_CLAUSE
static PGconn * pgsqlConn
static struct ast_threadstorage escapebuf_buf
static char dbname[MAX_DB_OPTION_SIZE]
#define LOG_WARNING
Definition: logger.h:274
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
Structure for variables, used for configurations and for channel variables.
#define var
Definition: ast_expr2f.c:614
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1091
#define ast_mutex_lock(a)
Definition: lock.h:187
#define NULL
Definition: resample.c:96
static ast_mutex_t pgsql_lock
#define ESCAPE_STRING(buffer, stringname)
#define ast_strlen_zero(foo)
Definition: strings.h:52
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1065
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:911
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:219
#define ast_variable_new(name, value, filename)
static int pgsql_reconnect(const char *database)
#define LOG_ERROR
Definition: logger.h:285
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
static int pgsql_exec(const char *database, const char *tablename, const char *sql, PGresult **result)
Do a postgres query, with reconnection support.
static struct ast_threadstorage sql_buf
char * strsep(char **str, const char *delims)
static PGresult * result
Definition: cel_pgsql.c:88
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:861
#define IS_SQL_LIKE_CLAUSE(x)
#define ast_mutex_unlock(a)
Definition: lock.h:188

◆ reload()

static int reload ( void  )
static

Definition at line 1427 of file res_config_pgsql.c.

References parse_config().

Referenced by handle_cli_realtime_pgsql_status().

1428 {
1429  parse_config(1);
1430 
1431  return 0;
1432 }
static int parse_config(int reload)

◆ require_pgsql()

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

Definition at line 1211 of file res_config_pgsql.c.

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

1212 {
1213  struct columns *column;
1214  struct tables *table;
1215  char *elm;
1216  int type, res = 0;
1217  unsigned int size;
1218 
1219  /*
1220  * Ignore database from the extconfig.conf since it was
1221  * configured by res_pgsql.conf.
1222  */
1223  database = dbname;
1224 
1225  table = find_table(database, tablename);
1226  if (!table) {
1227  ast_log(LOG_WARNING, "Table %s not found in database. This table should exist if you're using realtime.\n", tablename);
1228  return -1;
1229  }
1230 
1231  while ((elm = va_arg(ap, char *))) {
1232  type = va_arg(ap, require_type);
1233  size = va_arg(ap, unsigned int);
1234  AST_LIST_TRAVERSE(&table->columns, column, list) {
1235  if (strcmp(column->name, elm) == 0) {
1236  /* Char can hold anything, as long as it is large enough */
1237  if ((strncmp(column->type, "char", 4) == 0 || strncmp(column->type, "varchar", 7) == 0 || strcmp(column->type, "bpchar") == 0)) {
1238  if ((size > column->len) && column->len != -1) {
1239  ast_log(LOG_WARNING, "Column '%s' should be at least %d long, but is only %d long.\n", column->name, size, column->len);
1240  res = -1;
1241  }
1242  } else if (strncmp(column->type, "int", 3) == 0) {
1243  int typesize = atoi(column->type + 3);
1244  /* Integers can hold only other integers */
1245  if ((type == RQ_INTEGER8 || type == RQ_UINTEGER8 ||
1246  type == RQ_INTEGER4 || type == RQ_UINTEGER4 ||
1247  type == RQ_INTEGER3 || type == RQ_UINTEGER3 ||
1248  type == RQ_UINTEGER2) && typesize == 2) {
1249  ast_log(LOG_WARNING, "Column '%s' may not be large enough for the required data length: %d\n", column->name, size);
1250  res = -1;
1251  } else if ((type == RQ_INTEGER8 || type == RQ_UINTEGER8 ||
1252  type == RQ_UINTEGER4) && typesize == 4) {
1253  ast_log(LOG_WARNING, "Column '%s' may not be large enough for the required data length: %d\n", column->name, size);
1254  res = -1;
1255  } else if (type == RQ_CHAR || type == RQ_DATETIME || type == RQ_FLOAT || type == RQ_DATE) {
1256  ast_log(LOG_WARNING, "Column '%s' is of the incorrect type: (need %s(%d) but saw %s)\n",
1257  column->name,
1258  type == RQ_CHAR ? "char" :
1259  type == RQ_DATETIME ? "datetime" :
1260  type == RQ_DATE ? "date" :
1261  type == RQ_FLOAT ? "float" :
1262  "a rather stiff drink ",
1263  size, column->type);
1264  res = -1;
1265  }
1266  } else if (strncmp(column->type, "float", 5) == 0) {
1267  if (!ast_rq_is_int(type) && type != RQ_FLOAT) {
1268  ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type);
1269  res = -1;
1270  }
1271  } else if (strncmp(column->type, "timestamp", 9) == 0) {
1272  if (type != RQ_DATETIME && type != RQ_DATE) {
1273  ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type);
1274  res = -1;
1275  }
1276  } else { /* There are other types that no module implements yet */
1277  ast_log(LOG_WARNING, "Possibly unsupported column type '%s' on column '%s'\n", column->type, column->name);
1278  res = -1;
1279  }
1280  break;
1281  }
1282  }
1283 
1284  if (!column) {
1285  if (requirements == RQ_WARN) {
1286  ast_log(LOG_WARNING, "Table %s requires a column '%s' of size '%d', but no such column exists.\n", tablename, elm, size);
1287  res = -1;
1288  } else {
1289  struct ast_str *sql = ast_str_create(100);
1290  char fieldtype[10];
1291  PGresult *result;
1292 
1293  if (requirements == RQ_CREATECHAR || type == RQ_CHAR) {
1294  /* Size is minimum length; make it at least 50% greater,
1295  * just to be sure, because PostgreSQL doesn't support
1296  * resizing columns. */
1297  snprintf(fieldtype, sizeof(fieldtype), "CHAR(%u)",
1298  size < 15 ? size * 2 :
1299  (size * 3 / 2 > 255) ? 255 : size * 3 / 2);
1300  } else if (type == RQ_INTEGER1 || type == RQ_UINTEGER1 || type == RQ_INTEGER2) {
1301  snprintf(fieldtype, sizeof(fieldtype), "INT2");
1302  } else if (type == RQ_UINTEGER2 || type == RQ_INTEGER3 || type == RQ_UINTEGER3 || type == RQ_INTEGER4) {
1303  snprintf(fieldtype, sizeof(fieldtype), "INT4");
1304  } else if (type == RQ_UINTEGER4 || type == RQ_INTEGER8) {
1305  snprintf(fieldtype, sizeof(fieldtype), "INT8");
1306  } else if (type == RQ_UINTEGER8) {
1307  /* No such type on PostgreSQL */
1308  snprintf(fieldtype, sizeof(fieldtype), "CHAR(20)");
1309  } else if (type == RQ_FLOAT) {
1310  snprintf(fieldtype, sizeof(fieldtype), "FLOAT8");
1311  } else if (type == RQ_DATE) {
1312  snprintf(fieldtype, sizeof(fieldtype), "DATE");
1313  } else if (type == RQ_DATETIME) {
1314  snprintf(fieldtype, sizeof(fieldtype), "TIMESTAMP");
1315  } else {
1316  ast_log(LOG_ERROR, "Unrecognized request type %d\n", type);
1317  ast_free(sql);
1318  continue;
1319  }
1320  ast_str_set(&sql, 0, "ALTER TABLE %s ADD COLUMN %s %s", tablename, elm, fieldtype);
1321  ast_debug(1, "About to lock pgsql_lock (running alter on table '%s' to add column '%s')\n", tablename, elm);
1322 
1324  ast_debug(1, "About to run ALTER query on table '%s' to add column '%s'\n", tablename, elm);
1325 
1326  if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
1328  release_table(table);
1329  return -1;
1330  }
1331 
1332  ast_debug(1, "Finished running ALTER query on table '%s'\n", tablename);
1333  if (PQresultStatus(result) != PGRES_COMMAND_OK) {
1334  ast_log(LOG_ERROR, "Unable to add column: %s\n", ast_str_buffer(sql));
1335  }
1336  PQclear(result);
1338 
1339  ast_free(sql);
1340  }
1341  }
1342  }
1343  release_table(table);
1344  return res;
1345 }
require_type
Types used in ast_realtime_require_field.
static const char type[]
Definition: chan_ooh323.c:109
static char dbname[MAX_DB_OPTION_SIZE]
#define release_table(table)
#define LOG_WARNING
Definition: logger.h:274
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
#define ast_mutex_lock(a)
Definition: lock.h:187
static ast_mutex_t pgsql_lock
static struct tables * find_table(const char *database, const char *orig_tablename)
static char * table
Definition: cdr_odbc.c:58
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1065
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
#define LOG_ERROR
Definition: logger.h:285
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
struct tables::@9 list
struct mysql_conn * database
#define ast_free(a)
Definition: astmm.h:182
requirements
static int pgsql_exec(const char *database, const char *tablename, const char *sql, PGresult **result)
Do a postgres query, with reconnection support.
static PGresult * result
Definition: cel_pgsql.c:88
struct tables::mysql_columns columns
int ast_rq_is_int(require_type type)
Check if require type is an integer type.
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620
#define ast_mutex_unlock(a)
Definition: lock.h:188

◆ store_pgsql()

static int store_pgsql ( const char *  database,
const char *  table,
const struct ast_variable fields 
)
static

Definition at line 969 of file res_config_pgsql.c.

References ast_debug, ast_log, ast_mutex_lock, ast_mutex_unlock, ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_thread_get(), buf, dbname, ESCAPE_STRING, escapebuf_buf, LOG_WARNING, ast_variable::name, ast_variable::next, NULL, pgsql_exec(), pgsql_lock, pgsql_reconnect(), pgsqlConn, RAII_VAR, result, sql_buf, ast_variable::value, and where_buf.

970 {
971  RAII_VAR(PGresult *, result, NULL, PQclear);
972  int numrows;
973  struct ast_str *buf = ast_str_thread_get(&escapebuf_buf, 256);
974  struct ast_str *sql1 = ast_str_thread_get(&sql_buf, 256);
975  struct ast_str *sql2 = ast_str_thread_get(&where_buf, 256);
976  int pgresult;
977  const struct ast_variable *field = fields;
978 
979  /*
980  * Ignore database from the extconfig.conf since it was
981  * configured by res_pgsql.conf.
982  */
983  database = dbname;
984 
985  if (!table) {
986  ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
987  return -1;
988  }
989 
990  /*
991  * Must connect to the server before anything else as ESCAPE_STRING()
992  * uses pgsqlConn
993  */
995  if (!pgsql_reconnect(database)) {
997  return -1;
998  }
999 
1000  /* Get the first parameter and first value in our list of passed paramater/value pairs */
1001  if (!field) {
1003  "PostgreSQL RealTime: Realtime storage requires at least 1 parameter and 1 value to store.\n");
1004  if (pgsqlConn) {
1005  PQfinish(pgsqlConn);
1006  pgsqlConn = NULL;
1007  }
1009  return -1;
1010  }
1011 
1012  /* Create the first part of the query using the first parameter/value pairs we just extracted
1013  If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
1014  ESCAPE_STRING(buf, field->name);
1015  ast_str_set(&sql1, 0, "INSERT INTO %s (%s", table, ast_str_buffer(buf));
1016  ESCAPE_STRING(buf, field->value);
1017  ast_str_set(&sql2, 0, ") VALUES ('%s'", ast_str_buffer(buf));
1018  while ((field = field->next)) {
1019  ESCAPE_STRING(buf, field->name);
1020  ast_str_append(&sql1, 0, ", %s", ast_str_buffer(buf));
1021  ESCAPE_STRING(buf, field->value);
1022  ast_str_append(&sql2, 0, ", '%s'", ast_str_buffer(buf));
1023  }
1024  ast_str_append(&sql1, 0, "%s)", ast_str_buffer(sql2));
1025 
1026  ast_debug(1, "PostgreSQL RealTime: Insert SQL: %s\n", ast_str_buffer(sql1));
1027 
1028  /* We now have our complete statement; Lets connect to the server and execute it. */
1029  if (pgsql_exec(database, table, ast_str_buffer(sql1), &result) != 0) {
1031  return -1;
1032  }
1033 
1034  numrows = atoi(PQcmdTuples(result));
1036 
1037  ast_debug(1, "PostgreSQL RealTime: row inserted on table: %s.\n", table);
1038 
1039  /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
1040  * An integer greater than zero indicates the number of rows affected
1041  * Zero indicates that no records were updated
1042  * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
1043  */
1044 
1045  if (numrows >= 0) {
1046  return numrows;
1047  }
1048 
1049  return -1;
1050 }
struct ast_variable * next
static PGconn * pgsqlConn
static struct ast_threadstorage escapebuf_buf
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static char dbname[MAX_DB_OPTION_SIZE]
#define LOG_WARNING
Definition: logger.h:274
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
Structure for variables, used for configurations and for channel variables.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1091
#define ast_mutex_lock(a)
Definition: lock.h:187
#define NULL
Definition: resample.c:96
static ast_mutex_t pgsql_lock
#define ESCAPE_STRING(buffer, stringname)
static char * table
Definition: cdr_odbc.c:58
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1065
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:911
static struct ast_threadstorage where_buf
static int pgsql_reconnect(const char *database)
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
static int pgsql_exec(const char *database, const char *tablename, const char *sql, PGresult **result)
Do a postgres query, with reconnection support.
static struct ast_threadstorage sql_buf
static PGresult * result
Definition: cel_pgsql.c:88
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:861
#define ast_mutex_unlock(a)
Definition: lock.h:188

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 1401 of file res_config_pgsql.c.

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

Referenced by handle_cli_realtime_pgsql_status().

1402 {
1403  struct tables *table;
1404  /* Acquire control before doing anything to the module itself. */
1406 
1407  if (pgsqlConn) {
1408  PQfinish(pgsqlConn);
1409  pgsqlConn = NULL;
1410  }
1413 
1414  /* Unlock so something else can destroy the lock. */
1416 
1417  /* Destroy cached table info */
1419  while ((table = AST_LIST_REMOVE_HEAD(&psql_tables, list))) {
1420  destroy_table(table);
1421  }
1423 
1424  return 0;
1425 }
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
static void destroy_table(struct tables *table)
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
static PGconn * pgsqlConn
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
int ast_config_engine_deregister(struct ast_config_engine *del)
Deregister config engine.
Definition: main/config.c:3006
#define ast_mutex_lock(a)
Definition: lock.h:187
#define NULL
Definition: resample.c:96
static ast_mutex_t pgsql_lock
static char * table
Definition: cdr_odbc.c:58
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:832
static struct ast_cli_entry cli_realtime[]
struct tables::@9 list
static struct ast_config_engine pgsql_engine
#define ast_mutex_unlock(a)
Definition: lock.h:188

◆ unload_pgsql()

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

Definition at line 1347 of file res_config_pgsql.c.

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

1348 {
1349  struct tables *cur;
1350 
1351  /*
1352  * Ignore database from the extconfig.conf since it was
1353  * configured by res_pgsql.conf.
1354  */
1355  database = dbname;
1356 
1357  ast_debug(2, "About to lock table cache list\n");
1359  ast_debug(2, "About to traverse table cache list\n");
1361  if (strcmp(cur->name, tablename) == 0) {
1362  ast_debug(2, "About to remove matching cache entry\n");
1364  ast_debug(2, "About to destroy matching cache entry\n");
1365  destroy_table(cur);
1366  ast_debug(1, "Cache entry '%s@%s' destroyed\n", tablename, database);
1367  break;
1368  }
1369  }
1372  ast_debug(2, "About to return\n");
1373  return cur ? 0 : -1;
1374 }
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
static void destroy_table(struct tables *table)
static char dbname[MAX_DB_OPTION_SIZE]
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:614
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
char name[0]
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:556
struct tables::@9 list
struct mysql_conn * database
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:528

◆ update2_pgsql()

static int update2_pgsql ( const char *  database,
const char *  tablename,
const struct ast_variable lookup_fields,
const struct ast_variable update_fields 
)
static

Definition at line 843 of file res_config_pgsql.c.

References ast_debug, ast_log, ast_mutex_lock, ast_mutex_unlock, ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_thread_get(), dbname, ESCAPE_STRING, escapebuf_buf, find_column(), find_table(), columns::first, LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_variable::name, ast_variable::next, NULL, pgsql_exec(), pgsql_lock, pgsql_reconnect(), pgsqlConn, RAII_VAR, release_table, result, sql_buf, table, ast_variable::value, and where_buf.

844 {
845  RAII_VAR(PGresult *, result, NULL, PQclear);
846  int numrows = 0, pgresult, first = 1;
847  struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 16);
848  const struct ast_variable *field;
849  struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
850  struct ast_str *where = ast_str_thread_get(&where_buf, 100);
851  struct tables *table;
852 
853  /*
854  * Ignore database from the extconfig.conf since it was
855  * configured by res_pgsql.conf.
856  */
857  database = dbname;
858 
859  if (!tablename) {
860  ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
861  return -1;
862  }
863 
864  if (!escapebuf || !sql || !where) {
865  /* Memory error, already handled */
866  return -1;
867  }
868 
869  if (!(table = find_table(database, tablename))) {
870  ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
871  return -1;
872  }
873 
874  /*
875  * Must connect to the server before anything else as ESCAPE_STRING()
876  * uses pgsqlConn
877  */
879  if (!pgsql_reconnect(database)) {
881  release_table(table);
882  return -1;
883  }
884 
885  ast_str_set(&sql, 0, "UPDATE %s SET", tablename);
886  ast_str_set(&where, 0, " WHERE");
887 
888  for (field = lookup_fields; field; field = field->next) {
889  if (!find_column(table, field->name)) {
890  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);
892  release_table(table);
893  return -1;
894  }
895 
896  ESCAPE_STRING(escapebuf, field->value);
897  if (pgresult) {
898  ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
900  release_table(table);
901  return -1;
902  }
903  ast_str_append(&where, 0, "%s %s='%s'", first ? "" : " AND", field->name, ast_str_buffer(escapebuf));
904  first = 0;
905  }
906 
907  if (first) {
909  "PostgreSQL RealTime: Realtime update requires at least 1 parameter and 1 value to search on.\n");
910  if (pgsqlConn) {
911  PQfinish(pgsqlConn);
912  pgsqlConn = NULL;
913  }
915  release_table(table);
916  return -1;
917  }
918 
919  /* Now retrieve the columns to update */
920  first = 1;
921  for (field = update_fields; field; field = field->next) {
922  /* If the column is not within the table, then skip it */
923  if (!find_column(table, field->name)) {
924  ast_log(LOG_NOTICE, "Attempted to update column '%s' in table '%s@%s', but column does not exist!\n", field->name, tablename, database);
925  continue;
926  }
927 
928  ESCAPE_STRING(escapebuf, field->value);
929  if (pgresult) {
930  ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
932  release_table(table);
933  return -1;
934  }
935 
936  ast_str_append(&sql, 0, "%s %s='%s'", first ? "" : ",", field->name, ast_str_buffer(escapebuf));
937  first = 0;
938  }
939  release_table(table);
940 
941  ast_str_append(&sql, 0, "%s", ast_str_buffer(where));
942 
943  ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", ast_str_buffer(sql));
944 
945  /* We now have our complete statement; Lets connect to the server and execute it. */
946  if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
948  return -1;
949  }
950 
951  numrows = atoi(PQcmdTuples(result));
953 
954  ast_debug(1, "PostgreSQL RealTime: Updated %d rows on table: %s\n", numrows, tablename);
955 
956  /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
957  * An integer greater than zero indicates the number of rows affected
958  * Zero indicates that no records were updated
959  * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
960  */
961 
962  if (numrows >= 0) {
963  return (int) numrows;
964  }
965 
966  return -1;
967 }
struct ast_variable * next
static PGconn * pgsqlConn
static struct ast_threadstorage escapebuf_buf
static char dbname[MAX_DB_OPTION_SIZE]
#define release_table(table)
#define LOG_WARNING
Definition: logger.h:274
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
Structure for variables, used for configurations and for channel variables.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1091
#define ast_mutex_lock(a)
Definition: lock.h:187
#define NULL
Definition: resample.c:96
static ast_mutex_t pgsql_lock
static struct tables * find_table(const char *database, const char *orig_tablename)
#define ESCAPE_STRING(buffer, stringname)
static char * table
Definition: cdr_odbc.c:58
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1065
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:911
static struct ast_threadstorage where_buf
static int pgsql_reconnect(const char *database)
#define LOG_ERROR
Definition: logger.h:285
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
struct sla_ringing_trunk * first
Definition: app_meetme.c:1092
#define LOG_NOTICE
Definition: logger.h:263
struct mysql_conn * database
static int pgsql_exec(const char *database, const char *tablename, const char *sql, PGresult **result)
Do a postgres query, with reconnection support.
static struct ast_threadstorage sql_buf
static PGresult * result
Definition: cel_pgsql.c:88
static struct columns * find_column(struct tables *t, const char *colname)
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:861
#define ast_mutex_unlock(a)
Definition: lock.h:188

◆ update_pgsql()

static int update_pgsql ( const char *  database,
const char *  tablename,
const char *  keyfield,
const char *  lookup,
const struct ast_variable fields 
)
static

Definition at line 701 of file res_config_pgsql.c.

References ast_debug, AST_LIST_TRAVERSE, ast_log, ast_mutex_lock, ast_mutex_unlock, ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_thread_get(), tables::columns, dbname, ESCAPE_STRING, escapebuf_buf, find_column(), find_table(), columns::list, LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_variable::name, columns::name, ast_variable::next, NULL, pgsql_exec(), pgsql_lock, pgsql_reconnect(), pgsqlConn, RAII_VAR, release_table, result, sql_buf, table, and ast_variable::value.

703 {
704  RAII_VAR(PGresult *, result, NULL, PQclear);
705  int numrows = 0, pgresult;
706  const struct ast_variable *field = fields;
707  struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
708  struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
709  struct tables *table;
710  struct columns *column = NULL;
711 
712  /*
713  * Ignore database from the extconfig.conf since it was
714  * configured by res_pgsql.conf.
715  */
716  database = dbname;
717 
718  if (!tablename) {
719  ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
720  return -1;
721  }
722 
723  if (!(table = find_table(database, tablename))) {
724  ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
725  return -1;
726  }
727 
728  /*
729  * Must connect to the server before anything else as ESCAPE_STRING()
730  * uses pgsqlConn
731  */
733  if (!pgsql_reconnect(database)) {
735  release_table(table);
736  return -1;
737  }
738 
739  /* Get the first parameter and first value in our list of passed paramater/value pairs */
740  if (!field) {
742  "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
743  if (pgsqlConn) {
744  PQfinish(pgsqlConn);
745  pgsqlConn = NULL;
746  }
748  release_table(table);
749  return -1;
750  }
751 
752  /* Check that the column exists in the table */
753  AST_LIST_TRAVERSE(&table->columns, column, list) {
754  if (strcmp(column->name, field->name) == 0) {
755  break;
756  }
757  }
758 
759  if (!column) {
760  ast_log(LOG_ERROR, "PostgreSQL RealTime: Updating on column '%s', but that column does not exist within the table '%s'!\n", field->name, tablename);
762  release_table(table);
763  return -1;
764  }
765 
766  /* Create the first part of the query using the first parameter/value pairs we just extracted
767  If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
768 
769  ESCAPE_STRING(escapebuf, field->value);
770  if (pgresult) {
771  ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
773  release_table(table);
774  return -1;
775  }
776  ast_str_set(&sql, 0, "UPDATE %s SET %s = '%s'", tablename, field->name, ast_str_buffer(escapebuf));
777 
778  while ((field = field->next)) {
779  if (!find_column(table, field->name)) {
780  ast_log(LOG_NOTICE, "Attempted to update column '%s' in table '%s', but column does not exist!\n", field->name, tablename);
781  continue;
782  }
783 
784  ESCAPE_STRING(escapebuf, field->value);
785  if (pgresult) {
786  ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
788  release_table(table);
789  return -1;
790  }
791 
792  ast_str_append(&sql, 0, ", %s = '%s'", field->name, ast_str_buffer(escapebuf));
793  }
794  release_table(table);
795 
796  ESCAPE_STRING(escapebuf, lookup);
797  if (pgresult) {
798  ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", lookup);
800  return -1;
801  }
802 
803  ast_str_append(&sql, 0, " WHERE %s = '%s'", keyfield, ast_str_buffer(escapebuf));
804 
805  ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", ast_str_buffer(sql));
806 
807  /* We now have our complete statement; Lets connect to the server and execute it. */
808  if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
810  return -1;
811  } else {
812  ExecStatusType result_status = PQresultStatus(result);
813  if (result_status != PGRES_COMMAND_OK
814  && result_status != PGRES_TUPLES_OK
815  && result_status != PGRES_NONFATAL_ERROR) {
817  "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
818  ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
819  ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
820  PQresultErrorMessage(result), PQresStatus(result_status));
822  return -1;
823  }
824  }
825 
826  numrows = atoi(PQcmdTuples(result));
828 
829  ast_debug(1, "PostgreSQL RealTime: Updated %d rows on table: %s\n", numrows, tablename);
830 
831  /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
832  * An integer greater than zero indicates the number of rows affected
833  * Zero indicates that no records were updated
834  * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
835  */
836 
837  if (numrows >= 0)
838  return (int) numrows;
839 
840  return -1;
841 }
struct ast_variable * next
struct columns::@8 list
static PGconn * pgsqlConn
static struct ast_threadstorage escapebuf_buf
static char dbname[MAX_DB_OPTION_SIZE]
#define release_table(table)
#define LOG_WARNING
Definition: logger.h:274
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
Structure for variables, used for configurations and for channel variables.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1091
#define ast_mutex_lock(a)
Definition: lock.h:187
#define NULL
Definition: resample.c:96
static ast_mutex_t pgsql_lock
static struct tables * find_table(const char *database, const char *orig_tablename)
#define ESCAPE_STRING(buffer, stringname)
static char * table
Definition: cdr_odbc.c:58
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1065
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:911
static int pgsql_reconnect(const char *database)
#define LOG_ERROR
Definition: logger.h:285
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
#define LOG_NOTICE
Definition: logger.h:263
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
static int pgsql_exec(const char *database, const char *tablename, const char *sql, PGresult **result)
Do a postgres query, with reconnection support.
static struct ast_threadstorage sql_buf
static PGresult * result
Definition: cel_pgsql.c:88
struct tables::mysql_columns columns
static struct columns * find_column(struct tables *t, const char *colname)
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:861
#define ast_mutex_unlock(a)
Definition: lock.h:188

Variable Documentation

◆ __mod_info

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

Definition at line 1722 of file res_config_pgsql.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 1722 of file res_config_pgsql.c.

◆ cli_realtime

struct ast_cli_entry cli_realtime[]
static
Initial value:
= {
{ .handler = handle_cli_realtime_pgsql_status , .summary = "Shows connection information for the PostgreSQL RealTime driver" ,},
{ .handler = handle_cli_realtime_pgsql_cache , .summary = "Shows cached tables within the PostgreSQL realtime driver" ,},
}
static char * handle_cli_realtime_pgsql_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_cli_realtime_pgsql_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)

Definition at line 93 of file res_config_pgsql.c.

◆ connect_time

time_t connect_time = 0
static

Definition at line 84 of file res_config_pgsql.c.

Referenced by handle_cli_realtime_pgsql_status(), and pgsql_reconnect().

◆ dbappname

char dbappname[MAX_DB_OPTION_SIZE] = ""
static

Definition at line 81 of file res_config_pgsql.c.

Referenced by parse_config(), and pgsql_reconnect().

◆ dbhost

char dbhost[MAX_DB_OPTION_SIZE] = ""
static

◆ dbname

char dbname[MAX_DB_OPTION_SIZE] = ""
static

◆ dbpass

char dbpass[MAX_DB_OPTION_SIZE] = ""
static

Definition at line 79 of file res_config_pgsql.c.

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

◆ dbport

int dbport = 5432
static

◆ dbsock

char dbsock[MAX_DB_OPTION_SIZE] = ""
static

◆ dbuser

char dbuser[MAX_DB_OPTION_SIZE] = ""
static

◆ escapebuf_buf

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

◆ findtable_buf

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

Definition at line 45 of file res_config_pgsql.c.

Referenced by find_table().

◆ pgsql_engine

struct ast_config_engine pgsql_engine
static

Definition at line 1376 of file res_config_pgsql.c.

◆ pgsql_lock

ast_mutex_t pgsql_lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, {1, 0} }
static

◆ pgsqlConn

PGconn* pgsqlConn = NULL
static

◆ psql_tables

struct psql_tables psql_tables = { .first = NULL, .last = NULL, .lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, {1, 0} } , }
static

◆ requirements

enum { ... } requirements

◆ semibuf_buf

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

Definition at line 48 of file res_config_pgsql.c.

◆ sql_buf

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

◆ version

int version
static

Definition at line 53 of file res_config_pgsql.c.

Referenced by pgsql_reconnect().

◆ where_buf

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

Definition at line 46 of file res_config_pgsql.c.

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