Asterisk - The Open Source Telephony Project GIT-master-3dae2cf
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 queries, 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 = AST_BUILDOPT_SUM, .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 int order_multi_row_results_by_initial_column = 1
 
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 389 of file res_config_pgsql.c.

◆ ESCAPE_STRING

#define ESCAPE_STRING (   buffer,
  stringname 
)

Definition at line 99 of file res_config_pgsql.c.

◆ has_schema_support

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

Definition at line 54 of file res_config_pgsql.c.

◆ IS_SQL_LIKE_CLAUSE

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

Definition at line 388 of file res_config_pgsql.c.

◆ 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);

Definition at line 373 of file res_config_pgsql.c.

◆ RES_CONFIG_PGSQL_CONF

#define RES_CONFIG_PGSQL_CONF   "res_pgsql.conf"

Definition at line 50 of file res_config_pgsql.c.

◆ 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 92 of file res_config_pgsql.c.

static enum @427 requirements
@ RQ_CREATECHAR
@ RQ_WARN
@ RQ_CREATECLOSE

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 1727 of file res_config_pgsql.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 1727 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 queries, 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 145 of file res_config_pgsql.c.

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

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

Referenced by pgsql_exec().

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 1727 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 1133 of file res_config_pgsql.c.

1136{
1137 RAII_VAR(PGresult *, result, NULL, PQclear);
1138 long num_rows;
1139 struct ast_variable *new_v;
1140 struct ast_category *cur_cat = NULL;
1141 struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
1142 char last[80];
1143 int last_cat_metric = 0;
1144
1145 last[0] = '\0';
1146
1147 /*
1148 * Ignore database from the extconfig.conf since it is
1149 * configured by res_pgsql.conf.
1150 */
1151 database = dbname;
1152
1153 if (!file || !strcmp(file, RES_CONFIG_PGSQL_CONF)) {
1154 ast_log(LOG_WARNING, "PostgreSQL RealTime: Cannot configure myself.\n");
1155 return NULL;
1156 }
1157
1158 ast_str_set(&sql, 0, "SELECT category, var_name, var_val, cat_metric FROM %s "
1159 "WHERE filename='%s' and commented=0 "
1160 "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ", table, file);
1161
1162 ast_debug(1, "PostgreSQL RealTime: Static SQL: %s\n", ast_str_buffer(sql));
1163
1165
1166 /* We now have our complete statement; Lets connect to the server and execute it. */
1167 if (pgsql_exec(database, table, ast_str_buffer(sql), &result) != 0) {
1169 return NULL;
1170 }
1171
1172 if ((num_rows = PQntuples(result)) > 0) {
1173 int rowIndex = 0;
1174
1175 ast_debug(1, "PostgreSQL RealTime: Found %ld rows.\n", num_rows);
1176
1177 for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
1178 char *field_category = PQgetvalue(result, rowIndex, 0);
1179 char *field_var_name = PQgetvalue(result, rowIndex, 1);
1180 char *field_var_val = PQgetvalue(result, rowIndex, 2);
1181 char *field_cat_metric = PQgetvalue(result, rowIndex, 3);
1182 if (!strcmp(field_var_name, "#include")) {
1183 if (!ast_config_internal_load(field_var_val, cfg, flags, "", who_asked)) {
1185 return NULL;
1186 }
1187 continue;
1188 }
1189
1190 if (strcmp(last, field_category) || last_cat_metric != atoi(field_cat_metric)) {
1191 cur_cat = ast_category_new_dynamic(field_category);
1192 if (!cur_cat) {
1193 break;
1194 }
1195 ast_copy_string(last, field_category, sizeof(last));
1196 last_cat_metric = atoi(field_cat_metric);
1197 ast_category_append(cfg, cur_cat);
1198 }
1199 new_v = ast_variable_new(field_var_name, field_var_val, "");
1200 ast_variable_append(cur_cat, new_v);
1201 }
1202 } else {
1204 "PostgreSQL RealTime: Could not find config '%s' in database.\n", file);
1205 }
1206
1208
1209 return cfg;
1210}
struct sla_ringing_trunk * last
Definition: app_sla.c:332
static char * table
Definition: cdr_odbc.c:55
void ast_category_append(struct ast_config *config, struct ast_category *category)
Appends a category to a config.
Definition: extconf.c:2833
void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
Definition: extconf.c:1177
#define ast_variable_new(name, value, filename)
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:3286
#define ast_category_new_dynamic(name)
Create a category that is not backed by a file.
#define LOG_WARNING
#define ast_mutex_unlock(a)
Definition: lock.h:190
#define ast_mutex_lock(a)
Definition: lock.h:189
static int pgsql_exec(const char *database, const char *tablename, const char *sql, PGresult **result)
Do a postgres query, with reconnection support.
static char dbname[MAX_DB_OPTION_SIZE]
#define RES_CONFIG_PGSQL_CONF
static ast_mutex_t pgsql_lock
static struct ast_threadstorage sql_buf
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
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:1113
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
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:909
Support for dynamic strings.
Definition: strings.h:623
Structure for variables, used for configurations and for channel variables.
#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:941

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, make_ari_stubs::file, last, LOG_WARNING, NULL, pgsql_exec(), pgsql_lock, RAII_VAR, RES_CONFIG_PGSQL_CONF, result, sql_buf, and table.

◆ 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 1053 of file res_config_pgsql.c.

1054{
1055 RAII_VAR(PGresult *, result, NULL, PQclear);
1056 int numrows = 0;
1057 int pgresult;
1058 struct ast_str *sql = ast_str_thread_get(&sql_buf, 256);
1060 const struct ast_variable *field;
1061
1062 /*
1063 * Ignore database from the extconfig.conf since it was
1064 * configured by res_pgsql.conf.
1065 */
1066 database = dbname;
1067
1068 if (!table) {
1069 ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
1070 return -1;
1071 }
1072
1073 /*
1074 * Must connect to the server before anything else as ESCAPE_STRING()
1075 * uses pgsqlConn
1076 */
1078 if (!pgsql_reconnect(database)) {
1080 return -1;
1081 }
1082
1083 /* Get the first parameter and first value in our list of passed paramater/value pairs */
1084 if (ast_strlen_zero(keyfield) || ast_strlen_zero(lookup)) {
1086 "PostgreSQL RealTime: Realtime destroy requires at least 1 parameter and 1 value to search on.\n");
1087 if (pgsqlConn) {
1088 PQfinish(pgsqlConn);
1089 pgsqlConn = NULL;
1090 }
1092 return -1;
1093 }
1094
1095 /* Create the first part of the query using the first parameter/value pairs we just extracted
1096 If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
1097
1098 ESCAPE_STRING(buf1, keyfield);
1099 ESCAPE_STRING(buf2, lookup);
1100 ast_str_set(&sql, 0, "DELETE FROM %s WHERE %s = '%s'", table, ast_str_buffer(buf1), ast_str_buffer(buf2));
1101 for (field = fields; field; field = field->next) {
1102 ESCAPE_STRING(buf1, field->name);
1103 ESCAPE_STRING(buf2, field->value);
1104 ast_str_append(&sql, 0, " AND %s = '%s'", ast_str_buffer(buf1), ast_str_buffer(buf2));
1105 }
1106
1107 ast_debug(1, "PostgreSQL RealTime: Delete SQL: %s\n", ast_str_buffer(sql));
1108
1109 /* We now have our complete statement; Lets connect to the server and execute it. */
1110 if (pgsql_exec(database, table, ast_str_buffer(sql), &result) != 0) {
1112 return -1;
1113 }
1114
1115 numrows = atoi(PQcmdTuples(result));
1117
1118 ast_debug(1, "PostgreSQL RealTime: Deleted %d rows on table: %s\n", numrows, table);
1119
1120 /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
1121 * An integer greater than zero indicates the number of rows affected
1122 * Zero indicates that no records were updated
1123 * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
1124 */
1125
1126 if (numrows >= 0)
1127 return (int) numrows;
1128
1129 return -1;
1130}
static struct ast_threadstorage buf2
static struct ast_threadstorage buf1
static struct ast_threadstorage where_buf
static struct ast_threadstorage escapebuf_buf
#define ESCAPE_STRING(buffer, stringname)
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:1139
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
struct ast_variable * next

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, table, ast_variable::value, and where_buf.

◆ destroy_table()

static void destroy_table ( struct tables table)
static

Definition at line 118 of file res_config_pgsql.c.

119{
120 struct columns *column;
121 ast_rwlock_wrlock(&table->lock);
122 while ((column = AST_LIST_REMOVE_HEAD(&table->columns, list))) {
123 ast_free(column);
124 }
125 ast_rwlock_unlock(&table->lock);
128}
#define ast_free(a)
Definition: astmm.h:180
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define ast_rwlock_wrlock(a)
Definition: lock.h:236
#define ast_rwlock_destroy(rwlock)
Definition: lock.h:233
#define ast_rwlock_unlock(a)
Definition: lock.h:234
struct columns::@4 list

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

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

◆ find_column()

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

Definition at line 375 of file res_config_pgsql.c.

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

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

Referenced by update2_pgsql(), and update_pgsql().

◆ find_table()

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

Definition at line 251 of file res_config_pgsql.c.

252{
253 struct columns *column;
254 struct tables *table;
255 struct ast_str *sql = ast_str_thread_get(&findtable_buf, 330);
256 RAII_VAR(PGresult *, result, NULL, PQclear);
257 int exec_result;
258 char *fname, *ftype, *flen, *fnotnull, *fdef;
259 int i, rows;
260
263 if (!strcasecmp(table->name, orig_tablename)) {
264 ast_debug(1, "Found table in cache; now locking\n");
265 ast_rwlock_rdlock(&table->lock);
266 ast_debug(1, "Lock cached table; now returning\n");
268 return table;
269 }
270 }
271
272 if (database == NULL) {
274 return NULL;
275 }
276
277 ast_debug(1, "Table '%s' not found in cache, querying now\n", orig_tablename);
278
279 /* Not found, scan the table */
280 if (has_schema_support) {
281 char *schemaname, *tablename, *tmp_schemaname, *tmp_tablename;
282 if (strchr(orig_tablename, '.')) {
283 tmp_schemaname = ast_strdupa(orig_tablename);
284 tmp_tablename = strchr(tmp_schemaname, '.');
285 *tmp_tablename++ = '\0';
286 } else {
287 tmp_schemaname = "";
288 tmp_tablename = ast_strdupa(orig_tablename);
289 }
290
291 tablename = ast_alloca(strlen(tmp_tablename) * 2 + 1);
292 PQescapeStringConn(pgsqlConn, tablename, tmp_tablename, strlen(tmp_tablename), NULL);
293 schemaname = ast_alloca(strlen(tmp_schemaname) * 2 + 1);
294 PQescapeStringConn(pgsqlConn, schemaname, tmp_schemaname, strlen(tmp_schemaname), NULL);
295
296 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",
297 tablename,
298 ast_strlen_zero(schemaname) ? "" : "'", ast_strlen_zero(schemaname) ? "current_schema()" : schemaname, ast_strlen_zero(schemaname) ? "" : "'");
299 } else {
300 char *tablename;
301 tablename = ast_alloca(strlen(orig_tablename) * 2 + 1);
302 PQescapeStringConn(pgsqlConn, tablename, orig_tablename, strlen(orig_tablename), NULL);
303
304 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);
305 }
306
308 exec_result = pgsql_exec(database, orig_tablename, ast_str_buffer(sql), &result);
310 ast_debug(1, "Query of table structure complete. Now retrieving results.\n");
311 if (exec_result != 0) {
312 ast_log(LOG_ERROR, "Failed to query database columns for table %s\n", orig_tablename);
314 return NULL;
315 }
316
317 if (!(table = ast_calloc(1, sizeof(*table) + strlen(orig_tablename) + 1))) {
318 ast_log(LOG_ERROR, "Unable to allocate memory for new table structure\n");
320 return NULL;
321 }
322 strcpy(table->name, orig_tablename); /* SAFE */
323 ast_rwlock_init(&table->lock);
325
326 rows = PQntuples(result);
327 for (i = 0; i < rows; i++) {
328 fname = PQgetvalue(result, i, 0);
329 ftype = PQgetvalue(result, i, 1);
330 flen = PQgetvalue(result, i, 2);
331 fnotnull = PQgetvalue(result, i, 3);
332 fdef = PQgetvalue(result, i, 4);
333 ast_verb(4, "Found column '%s' of type '%s'\n", fname, ftype);
334
335 if (!(column = ast_calloc(1, sizeof(*column) + strlen(fname) + strlen(ftype) + 2))) {
336 ast_log(LOG_ERROR, "Unable to allocate column element for %s, %s\n", orig_tablename, fname);
339 return NULL;
340 }
341
342 if (strcmp(flen, "-1") == 0) {
343 /* Some types, like chars, have the length stored in a different field */
344 flen = PQgetvalue(result, i, 5);
345 sscanf(flen, "%30d", &column->len);
346 column->len -= 4;
347 } else {
348 sscanf(flen, "%30d", &column->len);
349 }
350 column->name = (char *)column + sizeof(*column);
351 column->type = (char *)column + sizeof(*column) + strlen(fname) + 1;
352 strcpy(column->name, fname);
353 strcpy(column->type, ftype);
354 if (*fnotnull == 't') {
355 column->notnull = 1;
356 } else {
357 column->notnull = 0;
358 }
359 if (!ast_strlen_zero(fdef)) {
360 column->hasdefault = 1;
361 } else {
362 column->hasdefault = 0;
363 }
364 AST_LIST_INSERT_TAIL(&table->columns, column, list);
365 }
366
368 ast_rwlock_rdlock(&table->lock);
370 return table;
371}
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_verb(level,...)
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:681
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:40
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:140
#define ast_rwlock_rdlock(a)
Definition: lock.h:235
#define ast_rwlock_init(rwlock)
wrapper for rwlock with tracking enabled
Definition: lock.h:224
static void destroy_table(struct tables *table)
#define has_schema_support
static struct ast_threadstorage findtable_buf
unsigned int hasdefault
Definition: cdr_pgsql.c:94
unsigned int notnull
Definition: cdr_pgsql.c:93

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, destroy_table(), findtable_buf, has_schema_support, columns::hasdefault, columns::len, LOG_ERROR, columns::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().

◆ 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 1620 of file res_config_pgsql.c.

1621{
1622 struct tables *cur;
1623 int l, which;
1624 char *ret = NULL;
1625
1626 switch (cmd) {
1627 case CLI_INIT:
1628 e->command = "realtime show pgsql cache";
1629 e->usage =
1630 "Usage: realtime show pgsql cache [<table>]\n"
1631 " Shows table cache for the PostgreSQL RealTime driver\n";
1632 return NULL;
1633 case CLI_GENERATE:
1634 if (a->argc != 4) {
1635 return NULL;
1636 }
1637 l = strlen(a->word);
1638 which = 0;
1641 if (!strncasecmp(a->word, cur->name, l) && ++which > a->n) {
1642 ret = ast_strdup(cur->name);
1643 break;
1644 }
1645 }
1647 return ret;
1648 }
1649
1650 if (a->argc == 4) {
1651 /* List of tables */
1654 ast_cli(a->fd, "%s\n", cur->name);
1655 }
1657 } else if (a->argc == 5) {
1658 /* List of columns */
1659 if ((cur = find_table(NULL, a->argv[4]))) {
1660 struct columns *col;
1661 ast_cli(a->fd, "Columns for Table Cache '%s':\n", a->argv[4]);
1662 ast_cli(a->fd, "%-20.20s %-20.20s %-3.3s %-8.8s\n", "Name", "Type", "Len", "Nullable");
1663 AST_LIST_TRAVERSE(&cur->columns, col, list) {
1664 ast_cli(a->fd, "%-20.20s %-20.20s %3d %-8.8s\n", col->name, col->type, col->len, col->notnull ? "NOT NULL" : "");
1665 }
1666 release_table(cur);
1667 } else {
1668 ast_cli(a->fd, "No such table '%s'\n", a->argv[4]);
1669 }
1670 }
1671 return 0;
1672}
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
@ CLI_INIT
Definition: cli.h:152
@ CLI_GENERATE
Definition: cli.h:153
#define release_table(table)
static struct tables * find_table(const char *database, const char *orig_tablename)
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
char name[0]
struct tables::@5 list
static struct test_val a

References a, ast_cli(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strdup, CLI_GENERATE, CLI_INIT, tables::columns, ast_cli_entry::command, find_table(), columns::len, columns::list, tables::list, columns::name, tables::name, columns::notnull, NULL, release_table, columns::type, and ast_cli_entry::usage.

◆ 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 1674 of file res_config_pgsql.c.

1675{
1676 char connection_info[256];
1677 char credentials[100] = "";
1678 char buf[376]; /* 256+100+"Connected to "+" for "+NULL */
1679 int is_connected = 0, ctimesec = time(NULL) - connect_time;
1680
1681 switch (cmd) {
1682 case CLI_INIT:
1683 e->command = "realtime show pgsql status";
1684 e->usage =
1685 "Usage: realtime show pgsql status\n"
1686 " Shows connection information for the PostgreSQL RealTime driver\n";
1687 return NULL;
1688 case CLI_GENERATE:
1689 return NULL;
1690 }
1691
1692 if (a->argc != 4)
1693 return CLI_SHOWUSAGE;
1694
1695 if (!ast_strlen_zero(dbhost))
1696 snprintf(connection_info, sizeof(connection_info), "%s@%s, port %d", dbname, dbhost, dbport);
1697 else if (!ast_strlen_zero(dbsock))
1698 snprintf(connection_info, sizeof(connection_info), "%s on socket file %s", dbname, dbsock);
1699 else
1700 snprintf(connection_info, sizeof(connection_info), "%s@%s", dbname, dbhost);
1701
1702 if (!ast_strlen_zero(dbuser))
1703 snprintf(credentials, sizeof(credentials), " with username %s", dbuser);
1704
1706 is_connected = (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK);
1708
1709 if (is_connected) {
1710 snprintf(buf, sizeof(buf), "Connected to %s%s for ", connection_info, credentials);
1711 ast_cli_print_timestr_fromseconds(a->fd, ctimesec, buf);
1712 return CLI_SUCCESS;
1713 } else {
1714 ast_cli(a->fd, "Unable to connect %s%s\n", connection_info, credentials);
1715 return CLI_FAILURE;
1716 }
1717}
#define CLI_SHOWUSAGE
Definition: cli.h:45
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),...
Definition: main/cli.c:3063
#define CLI_SUCCESS
Definition: cli.h:44
#define CLI_FAILURE
Definition: cli.h:46
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static char dbuser[MAX_DB_OPTION_SIZE]
static char dbhost[MAX_DB_OPTION_SIZE]
static time_t connect_time
static char dbsock[MAX_DB_OPTION_SIZE]
static int dbport

References a, ast_cli(), ast_cli_print_timestr_fromseconds(), ast_mutex_lock, ast_mutex_unlock, ast_strlen_zero(), buf, CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, connect_time, dbhost, dbname, dbport, dbsock, dbuser, NULL, pgsql_lock, pgsqlConn, and ast_cli_entry::usage.

◆ load_module()

static int load_module ( void  )
static

Definition at line 1390 of file res_config_pgsql.c.

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

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

◆ parse_config()

static int parse_config ( int  reload)
static

Definition at line 1435 of file res_config_pgsql.c.

1436{
1437 struct ast_config *config;
1438 const char *s;
1439 struct ast_flags config_flags = { is_reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1440
1443 if (is_reload && pgsqlConn && PQstatus(pgsqlConn) != CONNECTION_OK) {
1444 ast_log(LOG_WARNING, "PostgreSQL RealTime: Not connected\n");
1445 }
1446 return 0;
1447 }
1448
1450 ast_log(LOG_WARNING, "Unable to load config %s\n", RES_CONFIG_PGSQL_CONF);
1451 return 0;
1452 }
1453
1455
1456 /* XXX: Why would we do this before we're ready to establish a new connection? */
1457 if (pgsqlConn) {
1458 PQfinish(pgsqlConn);
1459 pgsqlConn = NULL;
1460 }
1461
1462 if (!(s = ast_variable_retrieve(config, "general", "dbuser"))) {
1464 "PostgreSQL RealTime: No database user found, using 'asterisk' as default.\n");
1465 strcpy(dbuser, "asterisk");
1466 } else {
1467 ast_copy_string(dbuser, s, sizeof(dbuser));
1468 }
1469
1470 if (!(s = ast_variable_retrieve(config, "general", "dbpass"))) {
1472 "PostgreSQL RealTime: No database password found, using 'asterisk' as default.\n");
1473 strcpy(dbpass, "asterisk");
1474 } else {
1475 ast_copy_string(dbpass, s, sizeof(dbpass));
1476 }
1477
1478 if (!(s = ast_variable_retrieve(config, "general", "dbhost"))) {
1480 "PostgreSQL RealTime: No database host found, using localhost via socket.\n");
1481 dbhost[0] = '\0';
1482 } else {
1483 ast_copy_string(dbhost, s, sizeof(dbhost));
1484 }
1485
1486 if (!(s = ast_variable_retrieve(config, "general", "dbname"))) {
1488 "PostgreSQL RealTime: No database name found, using 'asterisk' as default.\n");
1489 strcpy(dbname, "asterisk");
1490 } else {
1491 ast_copy_string(dbname, s, sizeof(dbname));
1492 }
1493
1494 if (!(s = ast_variable_retrieve(config, "general", "dbport"))) {
1496 "PostgreSQL RealTime: No database port found, using 5432 as default.\n");
1497 dbport = 5432;
1498 } else {
1499 dbport = atoi(s);
1500 }
1501
1502 if (!(s = ast_variable_retrieve(config, "general", "dbappname"))) {
1503 dbappname[0] = '\0';
1504 } else {
1505 ast_copy_string(dbappname, s, sizeof(dbappname));
1506 }
1507
1508 if (!ast_strlen_zero(dbhost)) {
1509 /* No socket needed */
1510 } else if (!(s = ast_variable_retrieve(config, "general", "dbsock"))) {
1512 "PostgreSQL RealTime: No database socket found, using '/tmp/.s.PGSQL.%d' as default.\n", dbport);
1513 strcpy(dbsock, "/tmp");
1514 } else {
1515 ast_copy_string(dbsock, s, sizeof(dbsock));
1516 }
1517
1518 if (!(s = ast_variable_retrieve(config, "general", "requirements"))) {
1520 "PostgreSQL RealTime: no requirements setting found, using 'warn' as default.\n");
1522 } else if (!strcasecmp(s, "createclose")) {
1524 } else if (!strcasecmp(s, "createchar")) {
1526 }
1527
1528 /* Result set ordering is enabled by default */
1529 s = ast_variable_retrieve(config, "general", "order_multi_row_results_by_initial_column");
1531
1533
1534 if (DEBUG_ATLEAST(1)) {
1535 if (!ast_strlen_zero(dbhost)) {
1536 ast_log(LOG_DEBUG, "PostgreSQL RealTime Host: %s\n", dbhost);
1537 ast_log(LOG_DEBUG, "PostgreSQL RealTime Port: %i\n", dbport);
1538 } else {
1539 ast_log(LOG_DEBUG, "PostgreSQL RealTime Socket: %s\n", dbsock);
1540 }
1541 ast_log(LOG_DEBUG, "PostgreSQL RealTime User: %s\n", dbuser);
1542 ast_log(LOG_DEBUG, "PostgreSQL RealTime Password: %s\n", dbpass);
1543 ast_log(LOG_DEBUG, "PostgreSQL RealTime DBName: %s\n", dbname);
1544 }
1545
1546 if (!pgsql_reconnect(NULL)) {
1548 "PostgreSQL RealTime: Couldn't establish connection. Check debug.\n");
1549 ast_debug(1, "PostgreSQL RealTime: Cannot Connect: %s\n", PQerrorMessage(pgsqlConn));
1550 }
1551
1552 ast_verb(2, "PostgreSQL RealTime reloaded.\n");
1553
1554 /* Done reloading. Release lock so others can now use driver. */
1556
1557 return 1;
1558}
static const char config[]
Definition: chan_ooh323.c:111
#define ast_config_load(filename, flags)
Load a config file.
#define CONFIG_STATUS_FILEMISSING
#define CONFIG_STATUS_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:784
@ CONFIG_FLAG_FILEUNCHANGED
#define DEBUG_ATLEAST(level)
#define LOG_DEBUG
requirements
static char dbappname[MAX_DB_OPTION_SIZE]
static int order_multi_row_results_by_initial_column
static char dbpass[MAX_DB_OPTION_SIZE]
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: utils.c:2199
Structure used to handle boolean flags.
Definition: utils.h:199

References ast_config_destroy(), ast_config_load, ast_copy_string(), ast_debug, ast_log, ast_mutex_lock, ast_mutex_unlock, ast_strlen_zero(), ast_true(), 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, order_multi_row_results_by_initial_column, pgsql_lock, pgsql_reconnect(), pgsqlConn, RES_CONFIG_PGSQL_CONF, RQ_CREATECHAR, RQ_CREATECLOSE, and RQ_WARN.

Referenced by load_module(), and reload().

◆ 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 219 of file res_config_pgsql.c.

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

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

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

◆ pgsql_reconnect()

static int pgsql_reconnect ( const char *  database)
static

Definition at line 1560 of file res_config_pgsql.c.

1561{
1562 char my_database[50];
1563
1564 ast_copy_string(my_database, S_OR(database, dbname), sizeof(my_database));
1565
1566 /* mutex lock should have been locked before calling this function. */
1567
1568 if (pgsqlConn) {
1569 if (PQstatus(pgsqlConn) == CONNECTION_OK) {
1570 /* We're good? */
1571 return 1;
1572 }
1573
1574 PQfinish(pgsqlConn);
1575 pgsqlConn = NULL;
1576 }
1577
1578 /* DB password can legitimately be 0-length */
1580 struct ast_str *conn_info = ast_str_create(128);
1581
1582 if (!conn_info) {
1583 ast_log(LOG_ERROR, "PostgreSQL RealTime: Failed to allocate memory for connection string.\n");
1584 return 0;
1585 }
1586
1587 ast_str_set(&conn_info, 0, "host=%s port=%d dbname=%s user=%s",
1588 S_OR(dbhost, dbsock), dbport, my_database, dbuser);
1589
1590 if (!ast_strlen_zero(dbappname)) {
1591 ast_str_append(&conn_info, 0, " application_name=%s", dbappname);
1592 }
1593
1594 if (!ast_strlen_zero(dbpass)) {
1595 ast_str_append(&conn_info, 0, " password=%s", dbpass);
1596 }
1597
1598 pgsqlConn = PQconnectdb(ast_str_buffer(conn_info));
1599 ast_free(conn_info);
1600 conn_info = NULL;
1601
1602 ast_debug(1, "pgsqlConn=%p\n", pgsqlConn);
1603 if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
1604 ast_debug(1, "PostgreSQL RealTime: Successfully connected to database.\n");
1605 connect_time = time(NULL);
1606 version = PQserverVersion(pgsqlConn);
1607 return 1;
1608 } else {
1610 "PostgreSQL RealTime: Failed to connect database %s on %s: %s\n",
1611 my_database, dbhost, PQresultErrorMessage(NULL));
1612 return 0;
1613 }
1614 } else {
1615 ast_debug(1, "PostgreSQL RealTime: One or more of the parameters in the config does not pass our validity checks.\n");
1616 return 1;
1617 }
1618}
static int version
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition: strings.h:80
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659

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().

◆ 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 525 of file res_config_pgsql.c.

526{
527 RAII_VAR(PGresult *, result, NULL, PQclear);
528 int num_rows = 0, pgresult;
529 struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
530 struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
531 const struct ast_variable *field = fields;
532 const char *initfield = NULL;
533 char *stringp;
534 char *chunk;
535 char *op;
536 char *escape = "";
537 struct ast_variable *var = NULL;
538 struct ast_config *cfg = NULL;
539 struct ast_category *cat = NULL;
540
541 /*
542 * Ignore database from the extconfig.conf since it was
543 * configured by res_pgsql.conf.
544 */
545 database = dbname;
546
547 if (!table) {
548 ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
549 return NULL;
550 }
551
552 if (!(cfg = ast_config_new()))
553 return NULL;
554
555 /*
556 * Must connect to the server before anything else as ESCAPE_STRING()
557 * uses pgsqlConn
558 */
560 if (!pgsql_reconnect(database)) {
562 return NULL;
563 }
564
565 /* Get the first parameter and first value in our list of passed paramater/value pairs */
566 if (!field) {
568 "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
569 if (pgsqlConn) {
570 PQfinish(pgsqlConn);
571 pgsqlConn = NULL;
572 }
575 return NULL;
576 }
577
578 initfield = ast_strdupa(field->name);
579 if ((op = strchr(initfield, ' '))) {
580 *op = '\0';
581 }
582
583 /* Create the first part of the query using the first parameter/value pairs we just extracted
584 If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
585
586 if (!strchr(field->name, ' ')) {
587 op = " =";
588 escape = "";
589 } else {
590 op = "";
591 if (IS_SQL_LIKE_CLAUSE(field->name)) {
592 escape = ESCAPE_CLAUSE;
593 }
594 }
595
596 ESCAPE_STRING(escapebuf, field->value);
597 if (pgresult) {
598 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
601 return NULL;
602 }
603
604 ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'%s", table, field->name, op, ast_str_buffer(escapebuf), escape);
605 while ((field = field->next)) {
606 escape = "";
607 if (!strchr(field->name, ' ')) {
608 op = " =";
609 escape = "";
610 } else {
611 op = "";
612 if (IS_SQL_LIKE_CLAUSE(field->name)) {
613 escape = ESCAPE_CLAUSE;
614 }
615 }
616
617 ESCAPE_STRING(escapebuf, field->value);
618 if (pgresult) {
619 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
622 return NULL;
623 }
624
625 ast_str_append(&sql, 0, " AND %s%s '%s'%s", field->name, op, ast_str_buffer(escapebuf), escape);
626 }
627
629 ast_str_append(&sql, 0, " ORDER BY %s", initfield);
630 }
631
632 /* We now have our complete statement; Lets connect to the server and execute it. */
633 if (pgsql_exec(database, table, ast_str_buffer(sql), &result) != 0) {
636 return NULL;
637 } else {
638 ExecStatusType result_status = PQresultStatus(result);
639 if (result_status != PGRES_COMMAND_OK
640 && result_status != PGRES_TUPLES_OK
641 && result_status != PGRES_NONFATAL_ERROR) {
643 "PostgreSQL RealTime: Failed to query %s@%s. Check debug for more info.\n", table, database);
644 ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
645 ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
646 PQresultErrorMessage(result), PQresStatus(result_status));
649 return NULL;
650 }
651 }
652
653 ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, ast_str_buffer(sql));
654
655 if ((num_rows = PQntuples(result)) > 0) {
656 int numFields = PQnfields(result);
657 int i = 0;
658 int rowIndex = 0;
659 char **fieldnames = NULL;
660
661 ast_debug(1, "PostgreSQL RealTime: Found %d rows.\n", num_rows);
662
663 if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
666 return NULL;
667 }
668 for (i = 0; i < numFields; i++)
669 fieldnames[i] = PQfname(result, i);
670
671 for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
672 var = NULL;
674 if (!cat) {
675 continue;
676 }
677 for (i = 0; i < numFields; i++) {
678 stringp = PQgetvalue(result, rowIndex, i);
679 while (stringp) {
680 chunk = strsep(&stringp, ";");
681 if (chunk && !ast_strlen_zero(ast_realtime_decode_chunk(ast_strip(chunk)))) {
682 if (initfield && !strcmp(initfield, fieldnames[i])) {
683 ast_category_rename(cat, chunk);
684 }
685 var = ast_variable_new(fieldnames[i], chunk, "");
687 }
688 }
689 }
690 ast_category_append(cfg, cat);
691 }
692 ast_free(fieldnames);
693 } else {
694 ast_debug(1, "PostgreSQL RealTime: Could not find any rows in table %s.\n", table);
695 }
696
698
699 return cfg;
700}
#define var
Definition: ast_expr2f.c:605
char * strsep(char **str, const char *delims)
void ast_category_rename(struct ast_category *cat, const char *name)
Definition: main/config.c:1452
struct ast_config * ast_config_new(void)
Create a new base configuration structure.
Definition: extconf.c:3274
#define ast_category_new_anonymous()
Create a nameless category that is not backed by a file.
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:3802
#define IS_SQL_LIKE_CLAUSE(x)
#define ESCAPE_CLAUSE
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223

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, order_multi_row_results_by_initial_column, pgsql_exec(), pgsql_lock, pgsql_reconnect(), pgsqlConn, RAII_VAR, result, sql_buf, strsep(), table, ast_variable::value, and var.

◆ realtime_pgsql()

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

Definition at line 391 of file res_config_pgsql.c.

392{
393 RAII_VAR(PGresult *, result, NULL, PQclear);
394 int pgresult;
395 struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
396 struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
397 char *stringp;
398 char *chunk;
399 char *op;
400 char *escape = "";
401 const struct ast_variable *field = fields;
402 struct ast_variable *var = NULL, *prev = NULL;
403
404 /*
405 * Ignore database from the extconfig.conf since it was
406 * configured by res_pgsql.conf.
407 */
408 database = dbname;
409
410 if (!tablename) {
411 ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
412 return NULL;
413 }
414
415 /*
416 * Must connect to the server before anything else as ESCAPE_STRING()
417 * uses pgsqlConn
418 */
420 if (!pgsql_reconnect(database)) {
422 return NULL;
423 }
424
425 /* Get the first parameter and first value in our list of passed paramater/value pairs */
426 if (!field) {
428 "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
429 if (pgsqlConn) {
430 PQfinish(pgsqlConn);
431 pgsqlConn = NULL;
432 }
434 return NULL;
435 }
436
437 /* Create the first part of the query using the first parameter/value pairs we just extracted
438 If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
439 if (!strchr(field->name, ' ')) {
440 op = " =";
441 } else {
442 op = "";
443 if (IS_SQL_LIKE_CLAUSE(field->name)) {
444 escape = ESCAPE_CLAUSE;
445 }
446 }
447
448 ESCAPE_STRING(escapebuf, field->value);
449 if (pgresult) {
450 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
452 return NULL;
453 }
454
455 ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'%s", tablename, field->name, op, ast_str_buffer(escapebuf), escape);
456 while ((field = field->next)) {
457 escape = "";
458 if (!strchr(field->name, ' ')) {
459 op = " =";
460 } else {
461 op = "";
462 if (IS_SQL_LIKE_CLAUSE(field->name)) {
463 escape = ESCAPE_CLAUSE;
464 }
465 }
466
467 ESCAPE_STRING(escapebuf, field->value);
468 if (pgresult) {
469 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
471 return NULL;
472 }
473
474 ast_str_append(&sql, 0, " AND %s%s '%s'%s", field->name, op, ast_str_buffer(escapebuf), escape);
475 }
476 ast_str_append(&sql, 0, " LIMIT 1");
477
478 /* We now have our complete statement; Lets connect to the server and execute it. */
479 if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
481 return NULL;
482 }
483
484 ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, ast_str_buffer(sql));
485
486 if (PQntuples(result) > 0) {
487 int i = 0;
488 int numFields = PQnfields(result);
489 char **fieldnames = NULL;
490
491 ast_debug(1, "PostgreSQL RealTime: Found a row.\n");
492
493 if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
495 return NULL;
496 }
497 for (i = 0; i < numFields; i++)
498 fieldnames[i] = PQfname(result, i);
499 for (i = 0; i < numFields; i++) {
500 stringp = PQgetvalue(result, 0, i);
501 while (stringp) {
502 chunk = strsep(&stringp, ";");
503 if (chunk && !ast_strlen_zero(ast_realtime_decode_chunk(ast_strip(chunk)))) {
504 if (prev) {
505 prev->next = ast_variable_new(fieldnames[i], chunk, "");
506 if (prev->next) {
507 prev = prev->next;
508 }
509 } else {
510 prev = var = ast_variable_new(fieldnames[i], chunk, "");
511 }
512 }
513 }
514 }
515 ast_free(fieldnames);
516 } else {
517 ast_debug(1, "Postgresql RealTime: Could not find any rows in table %s@%s.\n", tablename, database);
518 }
519
521
522 return var;
523}

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.

◆ reload()

static int reload ( void  )
static

Definition at line 1428 of file res_config_pgsql.c.

1429{
1430 parse_config(1);
1431
1432 return 0;
1433}

References parse_config().

◆ require_pgsql()

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

Definition at line 1212 of file res_config_pgsql.c.

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

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::database, dbname, find_table(), columns::len, tables::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, table, type, and columns::type.

◆ store_pgsql()

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

Definition at line 970 of file res_config_pgsql.c.

971{
972 RAII_VAR(PGresult *, result, NULL, PQclear);
973 int numrows;
975 struct ast_str *sql1 = ast_str_thread_get(&sql_buf, 256);
976 struct ast_str *sql2 = ast_str_thread_get(&where_buf, 256);
977 int pgresult;
978 const struct ast_variable *field = fields;
979
980 /*
981 * Ignore database from the extconfig.conf since it was
982 * configured by res_pgsql.conf.
983 */
984 database = dbname;
985
986 if (!table) {
987 ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
988 return -1;
989 }
990
991 /*
992 * Must connect to the server before anything else as ESCAPE_STRING()
993 * uses pgsqlConn
994 */
996 if (!pgsql_reconnect(database)) {
998 return -1;
999 }
1000
1001 /* Get the first parameter and first value in our list of passed paramater/value pairs */
1002 if (!field) {
1004 "PostgreSQL RealTime: Realtime storage requires at least 1 parameter and 1 value to store.\n");
1005 if (pgsqlConn) {
1006 PQfinish(pgsqlConn);
1007 pgsqlConn = NULL;
1008 }
1010 return -1;
1011 }
1012
1013 /* Create the first part of the query using the first parameter/value pairs we just extracted
1014 If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
1015 ESCAPE_STRING(buf, field->name);
1016 ast_str_set(&sql1, 0, "INSERT INTO %s (%s", table, ast_str_buffer(buf));
1017 ESCAPE_STRING(buf, field->value);
1018 ast_str_set(&sql2, 0, ") VALUES ('%s'", ast_str_buffer(buf));
1019 while ((field = field->next)) {
1020 ESCAPE_STRING(buf, field->name);
1021 ast_str_append(&sql1, 0, ", %s", ast_str_buffer(buf));
1022 ESCAPE_STRING(buf, field->value);
1023 ast_str_append(&sql2, 0, ", '%s'", ast_str_buffer(buf));
1024 }
1025 ast_str_append(&sql1, 0, "%s)", ast_str_buffer(sql2));
1026
1027 ast_debug(1, "PostgreSQL RealTime: Insert SQL: %s\n", ast_str_buffer(sql1));
1028
1029 /* We now have our complete statement; Lets connect to the server and execute it. */
1030 if (pgsql_exec(database, table, ast_str_buffer(sql1), &result) != 0) {
1032 return -1;
1033 }
1034
1035 numrows = atoi(PQcmdTuples(result));
1037
1038 ast_debug(1, "PostgreSQL RealTime: row inserted on table: %s.\n", table);
1039
1040 /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
1041 * An integer greater than zero indicates the number of rows affected
1042 * Zero indicates that no records were updated
1043 * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
1044 */
1045
1046 if (numrows >= 0) {
1047 return numrows;
1048 }
1049
1050 return -1;
1051}

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, table, ast_variable::value, and where_buf.

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 1402 of file res_config_pgsql.c.

1403{
1404 struct tables *table;
1405 /* Acquire control before doing anything to the module itself. */
1407
1408 if (pgsqlConn) {
1409 PQfinish(pgsqlConn);
1410 pgsqlConn = NULL;
1411 }
1414
1415 /* Unlock so something else can destroy the lock. */
1417
1418 /* Destroy cached table info */
1422 }
1424
1425 return 0;
1426}
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
int ast_config_engine_deregister(struct ast_config_engine *del)
Deregister config engine.
Definition: main/config.c:3180

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, cli_realtime, destroy_table(), tables::list, NULL, pgsql_engine, pgsql_lock, pgsqlConn, and table.

◆ unload_pgsql()

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

Definition at line 1348 of file res_config_pgsql.c.

1349{
1350 struct tables *cur;
1351
1352 /*
1353 * Ignore database from the extconfig.conf since it was
1354 * configured by res_pgsql.conf.
1355 */
1356 database = dbname;
1357
1358 ast_debug(2, "About to lock table cache list\n");
1360 ast_debug(2, "About to traverse table cache list\n");
1362 if (strcmp(cur->name, tablename) == 0) {
1363 ast_debug(2, "About to remove matching cache entry\n");
1365 ast_debug(2, "About to destroy matching cache entry\n");
1366 destroy_table(cur);
1367 ast_debug(1, "Cache entry '%s@%s' destroyed\n", tablename, database);
1368 break;
1369 }
1370 }
1373 ast_debug(2, "About to return\n");
1374 return cur ? 0 : -1;
1375}
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:557

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

◆ 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 844 of file res_config_pgsql.c.

845{
846 RAII_VAR(PGresult *, result, NULL, PQclear);
847 int numrows = 0, pgresult, first = 1;
848 struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 16);
849 const struct ast_variable *field;
850 struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
851 struct ast_str *where = ast_str_thread_get(&where_buf, 100);
852 struct tables *table;
853
854 /*
855 * Ignore database from the extconfig.conf since it was
856 * configured by res_pgsql.conf.
857 */
859
860 if (!tablename) {
861 ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
862 return -1;
863 }
864
865 if (!escapebuf || !sql || !where) {
866 /* Memory error, already handled */
867 return -1;
868 }
869
870 if (!(table = find_table(database, tablename))) {
871 ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
872 return -1;
873 }
874
875 /*
876 * Must connect to the server before anything else as ESCAPE_STRING()
877 * uses pgsqlConn
878 */
883 return -1;
884 }
885
886 ast_str_set(&sql, 0, "UPDATE %s SET", tablename);
887 ast_str_set(&where, 0, " WHERE");
888
889 for (field = lookup_fields; field; field = field->next) {
890 if (!find_column(table, field->name)) {
891 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);
894 return -1;
895 }
896
897 ESCAPE_STRING(escapebuf, field->value);
898 if (pgresult) {
899 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
902 return -1;
903 }
904 ast_str_append(&where, 0, "%s %s='%s'", first ? "" : " AND", field->name, ast_str_buffer(escapebuf));
905 first = 0;
906 }
907
908 if (first) {
910 "PostgreSQL RealTime: Realtime update requires at least 1 parameter and 1 value to search on.\n");
911 if (pgsqlConn) {
912 PQfinish(pgsqlConn);
913 pgsqlConn = NULL;
914 }
917 return -1;
918 }
919
920 /* Now retrieve the columns to update */
921 first = 1;
922 for (field = update_fields; field; field = field->next) {
923 /* If the column is not within the table, then skip it */
924 if (!find_column(table, field->name)) {
925 ast_log(LOG_NOTICE, "Attempted to update column '%s' in table '%s@%s', but column does not exist!\n", field->name, tablename, database);
926 continue;
927 }
928
929 ESCAPE_STRING(escapebuf, field->value);
930 if (pgresult) {
931 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
934 return -1;
935 }
936
937 ast_str_append(&sql, 0, "%s %s='%s'", first ? "" : ",", field->name, ast_str_buffer(escapebuf));
938 first = 0;
939 }
941
942 ast_str_append(&sql, 0, "%s", ast_str_buffer(where));
943
944 ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", ast_str_buffer(sql));
945
946 /* We now have our complete statement; Lets connect to the server and execute it. */
947 if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
949 return -1;
950 }
951
952 numrows = atoi(PQcmdTuples(result));
954
955 ast_debug(1, "PostgreSQL RealTime: Updated %d rows on table: %s\n", numrows, tablename);
956
957 /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
958 * An integer greater than zero indicates the number of rows affected
959 * Zero indicates that no records were updated
960 * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
961 */
962
963 if (numrows >= 0) {
964 return (int) numrows;
965 }
966
967 return -1;
968}
struct sla_ringing_trunk * first
Definition: app_sla.c:332
static struct columns * find_column(struct tables *t, const char *colname)

References ast_debug, ast_log, ast_mutex_lock, ast_mutex_unlock, ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_thread_get(), tables::database, dbname, ESCAPE_STRING, escapebuf_buf, find_column(), find_table(), 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.

◆ 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 702 of file res_config_pgsql.c.

704{
705 RAII_VAR(PGresult *, result, NULL, PQclear);
706 int numrows = 0, pgresult;
707 const struct ast_variable *field = fields;
708 struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
709 struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
710 struct tables *table;
711 struct columns *column = NULL;
712
713 /*
714 * Ignore database from the extconfig.conf since it was
715 * configured by res_pgsql.conf.
716 */
717 database = dbname;
718
719 if (!tablename) {
720 ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
721 return -1;
722 }
723
724 if (!(table = find_table(database, tablename))) {
725 ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
726 return -1;
727 }
728
729 /*
730 * Must connect to the server before anything else as ESCAPE_STRING()
731 * uses pgsqlConn
732 */
734 if (!pgsql_reconnect(database)) {
737 return -1;
738 }
739
740 /* Get the first parameter and first value in our list of passed paramater/value pairs */
741 if (!field) {
743 "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
744 if (pgsqlConn) {
745 PQfinish(pgsqlConn);
746 pgsqlConn = NULL;
747 }
750 return -1;
751 }
752
753 /* Check that the column exists in the table */
754 AST_LIST_TRAVERSE(&table->columns, column, list) {
755 if (strcmp(column->name, field->name) == 0) {
756 break;
757 }
758 }
759
760 if (!column) {
761 ast_log(LOG_ERROR, "PostgreSQL RealTime: Updating on column '%s', but that column does not exist within the table '%s'!\n", field->name, tablename);
764 return -1;
765 }
766
767 /* Create the first part of the query using the first parameter/value pairs we just extracted
768 If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
769
770 ESCAPE_STRING(escapebuf, field->value);
771 if (pgresult) {
772 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
775 return -1;
776 }
777 ast_str_set(&sql, 0, "UPDATE %s SET %s = '%s'", tablename, field->name, ast_str_buffer(escapebuf));
778
779 while ((field = field->next)) {
780 if (!find_column(table, field->name)) {
781 ast_log(LOG_NOTICE, "Attempted to update column '%s' in table '%s', but column does not exist!\n", field->name, tablename);
782 continue;
783 }
784
785 ESCAPE_STRING(escapebuf, field->value);
786 if (pgresult) {
787 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
790 return -1;
791 }
792
793 ast_str_append(&sql, 0, ", %s = '%s'", field->name, ast_str_buffer(escapebuf));
794 }
796
797 ESCAPE_STRING(escapebuf, lookup);
798 if (pgresult) {
799 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", lookup);
801 return -1;
802 }
803
804 ast_str_append(&sql, 0, " WHERE %s = '%s'", keyfield, ast_str_buffer(escapebuf));
805
806 ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", ast_str_buffer(sql));
807
808 /* We now have our complete statement; Lets connect to the server and execute it. */
809 if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
811 return -1;
812 } else {
813 ExecStatusType result_status = PQresultStatus(result);
814 if (result_status != PGRES_COMMAND_OK
815 && result_status != PGRES_TUPLES_OK
816 && result_status != PGRES_NONFATAL_ERROR) {
818 "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
819 ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
820 ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
821 PQresultErrorMessage(result), PQresStatus(result_status));
823 return -1;
824 }
825 }
826
827 numrows = atoi(PQcmdTuples(result));
829
830 ast_debug(1, "PostgreSQL RealTime: Updated %d rows on table: %s\n", numrows, tablename);
831
832 /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
833 * An integer greater than zero indicates the number of rows affected
834 * Zero indicates that no records were updated
835 * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
836 */
837
838 if (numrows >= 0)
839 return (int) numrows;
840
841 return -1;
842}

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(), dbname, ESCAPE_STRING, escapebuf_buf, find_column(), find_table(), columns::list, LOG_ERROR, LOG_NOTICE, LOG_WARNING, columns::name, ast_variable::name, ast_variable::next, NULL, pgsql_exec(), pgsql_lock, pgsql_reconnect(), pgsqlConn, RAII_VAR, release_table, result, sql_buf, table, and ast_variable::value.

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 = AST_BUILDOPT_SUM, .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 1727 of file res_config_pgsql.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 1727 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 94 of file res_config_pgsql.c.

Referenced by load_module(), and unload_module().

◆ 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 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().

◆ order_multi_row_results_by_initial_column

int order_multi_row_results_by_initial_column = 1
static

Definition at line 85 of file res_config_pgsql.c.

Referenced by parse_config(), and realtime_multi_pgsql().

◆ pgsql_engine

struct ast_config_engine pgsql_engine
static

Definition at line 1377 of file res_config_pgsql.c.

Referenced by load_module(), and unload_module().

◆ 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

◆ 

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().