diff --git a/Makefile.am b/Makefile.am index adf412b205..f11f891aac 100644 --- a/Makefile.am +++ b/Makefile.am @@ -207,7 +207,8 @@ library_include_HEADERS = \ libs/libteletone/src/libteletone.h \ libs/libtpl-1.5/src/tpl.h \ src/include/switch_limit.h \ - src/include/switch_odbc.h + src/include/switch_odbc.h \ + src/include/switch_pgsql.h nodist_libfreeswitch_la_SOURCES = \ src/include/switch_frame.h \ @@ -262,6 +263,7 @@ libfreeswitch_la_SOURCES = \ src/switch_config.c \ src/switch_time.c \ src/switch_odbc.c \ + src/switch_pgsql.c \ src/switch_limit.c \ src/g711.c \ src/switch_pcm.c \ diff --git a/conf/vanilla/autoload_configs/switch.conf.xml b/conf/vanilla/autoload_configs/switch.conf.xml index 64f3d6fa71..34bdedcda9 100644 --- a/conf/vanilla/autoload_configs/switch.conf.xml +++ b/conf/vanilla/autoload_configs/switch.conf.xml @@ -144,6 +144,8 @@ + + + + + diff --git a/configure.in b/configure.in index 92a88048fd..4e036b0d81 100644 --- a/configure.in +++ b/configure.in @@ -395,7 +395,30 @@ if test "x$enable_core_odbc_support" != "xno"; then AC_CHECK_LIB([odbc], [SQLDisconnect],, AC_MSG_ERROR([no usable libodbc; please install unixodbc devel package or equivalent])) fi +AC_ARG_ENABLE(core-pgsql-support, + [AS_HELP_STRING([--enable-core-pgsql-support], [Compile with PGSQL Support])],,[enable_core_pgsql_support="no"]) +if test x"$enable_core_pgsql_support" = x"yes" ; then + +AC_PATH_PROG([PG_CONFIG], [pg_config], [no]) +if test "$PG_CONFIG" != "no"; then + AC_MSG_CHECKING([for PostgreSQL libraries]) + POSTGRESQL_CFLAGS="`$PG_CONFIG --cflags` -I`$PG_CONFIG --includedir`" + POSTGRESQL_CXXFLAGS="`$PG_CONFIG --cppflags` -I`$PG_CONFIG --includedir`" + POSTGRESQL_LDFLAGS="`$PG_CONFIG --ldflags` -L`$PG_CONFIG --libdir` -lpq" + POSTGRESQL_VERSION=`$PG_CONFIG --version | sed -e 's#PostgreSQL ##'` + AC_DEFINE([SWITCH_HAVE_PGSQL], [1], [Define to 1 if PostgreSQL libraries are available]) + AC_CHECK_LIB([pq], [PQgetvalue],, AC_MSG_ERROR([no usable libpq; please install PostgreSQL devel package or equivalent])) + AC_MSG_RESULT([yes]) + SWITCH_AM_CFLAGS="$POSTGRESQL_CFLAGS $SWITCH_AM_CFLAGS" + SWITCH_AM_CXXFLAGS="$POSTGRESQL_CXXFLAGS $SWITCH_AM_CXXFLAGS" + SWITCH_AM_LDFLAGS="$POSTGRESQL_LDFLAGS $SWITCH_AM_LDFLAGS" +else + AC_MSG_RESULT([no]) + AC_MSG_FAILURE([Unabled to find pg_config in PATH. Is PostgreSQL installed?]) +fi + +fi AC_ARG_ENABLE(timerfd-wrapper, [AC_HELP_STRING([--enable-timerfd-wrapper],[timerfd is in the kernel but not in your libc])],[enable_timer_fd_wrapper="$enableval"],[enable_timer_fd_wrapper="no"]) diff --git a/src/include/switch.h b/src/include/switch.h index 3e1bfbb77f..1eb45ae48a 100644 --- a/src/include/switch.h +++ b/src/include/switch.h @@ -135,6 +135,7 @@ #include "switch_config.h" #include "switch_nat.h" #include "switch_odbc.h" +#include "switch_pgsql.h" #include "switch_json.h" #include "switch_limit.h" diff --git a/src/include/switch_core.h b/src/include/switch_core.h index fa703eafc0..73607926fa 100644 --- a/src/include/switch_core.h +++ b/src/include/switch_core.h @@ -2210,12 +2210,14 @@ typedef enum { typedef enum { SCDB_TYPE_CORE_DB, - SCDB_TYPE_ODBC + SCDB_TYPE_ODBC, + SCDB_TYPE_PGSQL } switch_cache_db_handle_type_t; typedef union { switch_core_db_t *core_db_dbh; switch_odbc_handle_t *odbc_dbh; + switch_pgsql_handle_t *pgsql_dbh; } switch_cache_db_native_handle_t; typedef struct { @@ -2228,9 +2230,14 @@ typedef struct { char *pass; } switch_cache_db_odbc_options_t; +typedef struct { + char *dsn; +} switch_cache_db_pgsql_options_t; + typedef union { switch_cache_db_core_db_options_t core_db_options; switch_cache_db_odbc_options_t odbc_options; + switch_cache_db_pgsql_options_t pgsql_options; } switch_cache_db_connection_options_t; struct switch_cache_db_handle; @@ -2241,6 +2248,11 @@ static inline const char *switch_cache_db_type_name(switch_cache_db_handle_type_ const char *type_str = "INVALID"; switch (type) { + case SCDB_TYPE_PGSQL: + { + type_str = "PGSQL"; + } + break; case SCDB_TYPE_ODBC: { type_str = "ODBC"; diff --git a/src/include/switch_pgsql.h b/src/include/switch_pgsql.h new file mode 100644 index 0000000000..282172e646 --- /dev/null +++ b/src/include/switch_pgsql.h @@ -0,0 +1,162 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2012, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Eliot Gable + * + * switch_pgsql.h -- PGSQL Driver + * + */ + +#ifndef SWITCH_PGSQL_H +#define SWITCH_PGSQL_H + +#include + +#define DEFAULT_PGSQL_RETRIES 120 + +SWITCH_BEGIN_EXTERN_C + +struct switch_pgsql_handle; +struct switch_pgsql_result; + + +typedef enum { + SWITCH_PGSQL_STATE_INIT, + SWITCH_PGSQL_STATE_DOWN, + SWITCH_PGSQL_STATE_CONNECTED, + SWITCH_PGSQL_STATE_ERROR +} switch_pgsql_state_t; + +typedef enum { + SWITCH_PGSQL_SUCCESS = 0, + SWITCH_PGSQL_FAIL = -1 +} switch_pgsql_status_t; + +/*! + \brief Create a new handle for the PGSQL connection. + \param dsn The DSN of the database to connect to. See documentation for PQconnectdb() at + http://www.postgresql.org/docs/9.0/static/libpq-connect.html. The DSN *MUST* be + prefixed with 'pgsql;' to use the switch_cache_db* functionality. However, the DSN + passed to this function directly *MUST NOT* be prefixed with 'pgsql;'. + \return Returns a pointer to a newly allocated switch_pgsql_handle_t type or NULL on failure. + */ +SWITCH_DECLARE(switch_pgsql_handle_t *) switch_pgsql_handle_new(const char *dsn); + +/*! + \brief Sets the number of retries if the PGSQL connection fails. + \param handle A fully allocated switch_pgsql_handle_t returned from a call to switch_pgsql_handle_new(). + \param num_retries How many times to retry connecting to the database if this connection fails. + */ +SWITCH_DECLARE(void) switch_pgsql_set_num_retries(switch_pgsql_handle_t *handle, int num_retries); + +/*! + \brief Disconnects a PGSQL connection from the database. + \param handle The PGSQL database handle to disconnect. + \return Returns SWITCH_PGSQL_SUCCESS or SWITCH_PGSQL_FAIL. + */ +SWITCH_DECLARE(switch_pgsql_status_t ) switch_pgsql_handle_disconnect(switch_pgsql_handle_t *handle); +#if 0 + ) /* Emacs formatting issue */ +#endif +/*! + \brief Connect to the database specified by the DSN passed to the switch_pgsql_handle_new() call which + initialized this handle. + \param The database handle to connect to the database. + \return Returns SWITCH_PGSQL_SUCCESS or SWITCH_PGSQL_FAIL. + */ +SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_handle_connect(switch_pgsql_handle_t *handle); + +/*! + */ +SWITCH_DECLARE(void) switch_pgsql_handle_destroy(switch_pgsql_handle_t **handlep); + +SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_send_query(switch_pgsql_handle_t *handle, const char* sql); + +SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_cancel_real(const char *file, const char *func, int line, switch_pgsql_handle_t *handle); +#define switch_pgsql_cancel(handle) switch_pgsql_cancel_real(__FILE__, (char * )__SWITCH_FUNC__, __LINE__, handle) + +SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_next_result_timed(switch_pgsql_handle_t *handle, switch_pgsql_result_t **result_out, int seconds); +#define switch_pgsql_next_result(h, r) switch_pgsql_next_result_timed(h, r, 10000) + +SWITCH_DECLARE(void) switch_pgsql_free_result(switch_pgsql_result_t **result); + +SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_finish_results_real(const char* file, const char *func, int line, switch_pgsql_handle_t *handle); +#define switch_pgsql_finish_results(handle) switch_pgsql_finish_results_real(__FILE__, (char * )__SWITCH_FUNC__, __LINE__, handle) + +SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_handle_exec_base(switch_pgsql_handle_t *handle, const char *sql, char **err); + +SWITCH_DECLARE(switch_pgsql_state_t) switch_pgsql_handle_get_state(switch_pgsql_handle_t *handle); +SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_handle_exec(switch_pgsql_handle_t *handle, const char *sql, char **err); +SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_handle_exec_string(switch_pgsql_handle_t *handle, const char *sql, char *resbuf, size_t len, char **err); +SWITCH_DECLARE(switch_bool_t) switch_pgsql_available(void); +SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_SQLSetAutoCommitAttr(switch_pgsql_handle_t *handle, switch_bool_t on); +SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_SQLEndTran(switch_pgsql_handle_t *handle, switch_bool_t commit); + +/*! + \brief Execute the sql query and issue a callback for each row returned + \param file the file from which this function is called + \param func the function from which this function is called + \param line the line from which this function is called + \param handle the PGSQL handle + \param sql the sql string to execute + \param callback the callback function to execute + \param pdata the state data passed on each callback invocation + \return SWITCH_STATUS_SUCCESS if the operation was successful + \note none +*/ +SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_handle_callback_exec_detailed(const char *file, const char *func, int line, switch_pgsql_handle_t *handle, + const char *sql, switch_core_db_callback_func_t callback, void *pdata, + char **err); +/*! + \brief Execute the sql query and issue a callback for each row returned + \param handle the PGSQL handle + \param sql the sql string to execute + \param callback the callback function to execute + \param pdata the state data passed on each callback invocation + \return SWITCH_STATUS_SUCCESS if the operation was successful + \note none +*/ +#define switch_pgsql_handle_callback_exec(handle, sql, callback, pdata, err) \ + switch_pgsql_handle_callback_exec_detailed(__FILE__, (char * )__SWITCH_FUNC__, __LINE__, \ + handle, sql, callback, pdata, err) + + +SWITCH_DECLARE(char *) switch_pgsql_handle_get_error(switch_pgsql_handle_t *handle); + +SWITCH_DECLARE(int) switch_pgsql_handle_affected_rows(switch_pgsql_handle_t *handle); + +SWITCH_END_EXTERN_C +#endif +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */ diff --git a/src/include/switch_types.h b/src/include/switch_types.h index f9d8476454..6f77f32ef4 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -324,7 +324,7 @@ typedef enum { SCF_CLEAR_SQL = (1 << 17), SCF_THREADED_SYSTEM_EXEC = (1 << 18), SCF_SYNC_CLOCK_REQUESTED = (1 << 19), - SCF_CORE_ODBC_REQ = (1 << 20), + SCF_CORE_NON_SQLITE_DB_REQ = (1 << 20), SCF_DEBUG_SQL = (1 << 21), SCF_API_EXPANSION = (1 << 22), SCF_SESSION_THREAD_POOL = (1 << 23) @@ -1833,6 +1833,8 @@ typedef struct switch_buffer switch_buffer_t; typedef struct switch_codec_settings switch_codec_settings_t; typedef struct switch_codec_fmtp switch_codec_fmtp_t; typedef struct switch_odbc_handle switch_odbc_handle_t; +typedef struct switch_pgsql_handle switch_pgsql_handle_t; +typedef struct switch_pgsql_result switch_pgsql_result_t; typedef struct switch_io_routines switch_io_routines_t; typedef struct switch_speech_handle switch_speech_handle_t; diff --git a/src/mod/applications/mod_cidlookup/mod_cidlookup.c b/src/mod/applications/mod_cidlookup/mod_cidlookup.c index 562cea7f5f..d71278dd2f 100755 --- a/src/mod/applications/mod_cidlookup/mod_cidlookup.c +++ b/src/mod/applications/mod_cidlookup/mod_cidlookup.c @@ -94,12 +94,20 @@ static switch_cache_db_handle_t *cidlookup_get_db_handle(void) switch_cache_db_handle_t *dbh = NULL; if (!zstr(globals.odbc_dsn)) { - options.odbc_options.dsn = globals.odbc_dsn; - options.odbc_options.user = globals.odbc_user; - options.odbc_options.pass = globals.odbc_pass; + char *dsn; + if ((dsn = strstr(globals.odbc_dsn, "pgsql;")) != NULL) { + options.pgsql_options.dsn = (char*)(dsn + 6); - if (switch_cache_db_get_db_handle(&dbh, SCDB_TYPE_ODBC, &options) != SWITCH_STATUS_SUCCESS) - dbh = NULL; + if (switch_cache_db_get_db_handle(&dbh, SCDB_TYPE_PGSQL, &options) != SWITCH_STATUS_SUCCESS) + dbh = NULL; + } else { + options.odbc_options.dsn = globals.odbc_dsn; + options.odbc_options.user = globals.odbc_user; + options.odbc_options.pass = globals.odbc_pass; + + if (switch_cache_db_get_db_handle(&dbh, SCDB_TYPE_ODBC, &options) != SWITCH_STATUS_SUCCESS) + dbh = NULL; + } } return dbh; } @@ -112,8 +120,8 @@ static switch_status_t config_callback_dsn(switch_xml_config_item_t *data, const switch_cache_db_handle_t *dbh = NULL; - if (!switch_odbc_available()) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ODBC is not compiled in. Do not configure odbc-dsn parameter!\n"); + if (!switch_odbc_available() && !switch_pgsql_available()) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ODBC and PGSQL are not compiled in. Do not configure odbc-dsn parameter!\n"); return SWITCH_STATUS_FALSE; } @@ -134,7 +142,7 @@ static switch_status_t config_callback_dsn(switch_xml_config_item_t *data, const switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Connecting to dsn: %s\n", globals.odbc_dsn); if (!(dbh = cidlookup_get_db_handle())) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Cannot Open ODBC Database!\n"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Cannot Open Database!\n"); switch_goto_status(SWITCH_STATUS_FALSE, done); } } @@ -195,7 +203,7 @@ static switch_bool_t cidlookup_execute_sql_callback(char *sql, switch_core_db_ca retval = SWITCH_TRUE; } } else { - *err = switch_core_sprintf(cbt->pool, "Unable to get ODBC handle. dsn: %s, dbh is %s\n", globals.odbc_dsn, dbh ? "not null" : "null"); + *err = switch_core_sprintf(cbt->pool, "Unable to get database handle. dsn: %s, dbh is %s\n", globals.odbc_dsn, dbh ? "not null" : "null"); } switch_cache_db_release_db_handle(&dbh); @@ -564,7 +572,7 @@ static cid_data_t *do_lookup(switch_memory_pool_t *pool, switch_event_t *event, switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "caller_id_number", number); /* database always wins */ - if (switch_odbc_available() && globals.odbc_dsn && globals.sql) { + if ((switch_odbc_available() || switch_pgsql_available()) && globals.odbc_dsn && globals.sql) { name = do_db_lookup(pool, event, number, globals.sql); if (name) { cid->name = name; @@ -610,7 +618,7 @@ static cid_data_t *do_lookup(switch_memory_pool_t *pool, switch_event_t *event, switch_assert(cid); } /* append area if we can */ - if (!cid->area && !skipcitystate && strlen(number) == 11 && number[0] == '1' && switch_odbc_available() && globals.odbc_dsn && globals.citystate_sql) { + if (!cid->area && !skipcitystate && strlen(number) == 11 && number[0] == '1' && (switch_odbc_available() || switch_pgsql_available()) && globals.odbc_dsn && globals.citystate_sql) { /* yes, this is really area */ name = do_db_lookup(pool, event, number, globals.citystate_sql); @@ -768,7 +776,7 @@ SWITCH_STANDARD_API(cidlookup_function) stream->write_function(stream, " odbc-dsn: %s\n sql: %s\n citystate-sql: %s\n", globals.odbc_dsn ? globals.odbc_dsn : "(null)", globals.sql ? globals.sql : "(null)", globals.citystate_sql ? globals.citystate_sql : "(null)"); - stream->write_function(stream, " ODBC Compiled: %s\n", switch_odbc_available()? "true" : "false"); + stream->write_function(stream, " ODBC Compiled: %s; PGSQL Compiled: %s\n", switch_odbc_available() ? "true" : "false", switch_pgsql_available() ? "true" : "false"); switch_goto_status(SWITCH_STATUS_SUCCESS, done); } diff --git a/src/mod/applications/mod_db/mod_db.c b/src/mod/applications/mod_db/mod_db.c index 5c64b20c95..69e0522be7 100644 --- a/src/mod/applications/mod_db/mod_db.c +++ b/src/mod/applications/mod_db/mod_db.c @@ -92,12 +92,20 @@ switch_cache_db_handle_t *limit_get_db_handle(void) switch_cache_db_handle_t *dbh = NULL; if (!zstr(globals.odbc_dsn)) { - options.odbc_options.dsn = globals.odbc_dsn; - options.odbc_options.user = globals.odbc_user; - options.odbc_options.pass = globals.odbc_pass; + char *dsn; + if ((dsn = strstr(globals.odbc_dsn, "pgsql;")) != NULL) { + options.pgsql_options.dsn = (char*)(dsn + 6); - if (switch_cache_db_get_db_handle(&dbh, SCDB_TYPE_ODBC, &options) != SWITCH_STATUS_SUCCESS) - dbh = NULL; + if (switch_cache_db_get_db_handle(&dbh, SCDB_TYPE_PGSQL, &options) != SWITCH_STATUS_SUCCESS) + dbh = NULL; + } else { + options.odbc_options.dsn = globals.odbc_dsn; + options.odbc_options.user = globals.odbc_user; + options.odbc_options.pass = globals.odbc_pass; + + if (switch_cache_db_get_db_handle(&dbh, SCDB_TYPE_ODBC, &options) != SWITCH_STATUS_SUCCESS) + dbh = NULL; + } return dbh; } else { options.core_db_options.db_path = globals.dbname; @@ -275,7 +283,7 @@ SWITCH_LIMIT_STATUS(limit_status_db) /* INIT / Config */ -static switch_xml_config_string_options_t limit_config_dsn = { NULL, 0, "[^:]+:[^:]+:.+" }; +static switch_xml_config_string_options_t limit_config_dsn = { NULL, 0, "^pgsql;|[^:]+:[^:]+:.+" }; static switch_xml_config_item_t config_settings[] = { SWITCH_CONFIG_ITEM("odbc-dsn", SWITCH_CONFIG_STRING, 0, &globals.odbc_dsn, NULL, &limit_config_dsn, diff --git a/src/mod/applications/mod_fifo/mod_fifo.c b/src/mod/applications/mod_fifo/mod_fifo.c index f500c4683a..56383a2973 100644 --- a/src/mod/applications/mod_fifo/mod_fifo.c +++ b/src/mod/applications/mod_fifo/mod_fifo.c @@ -731,12 +731,20 @@ switch_cache_db_handle_t *fifo_get_db_handle(void) switch_cache_db_handle_t *dbh = NULL; if (!zstr(globals.odbc_dsn)) { - options.odbc_options.dsn = globals.odbc_dsn; - options.odbc_options.user = globals.odbc_user; - options.odbc_options.pass = globals.odbc_pass; + char *dsn; + if ((dsn = strstr(globals.odbc_dsn, "pgsql;")) != NULL) { + options.pgsql_options.dsn = (char*)(dsn + 6); - if (switch_cache_db_get_db_handle(&dbh, SCDB_TYPE_ODBC, &options) != SWITCH_STATUS_SUCCESS) { - dbh = NULL; + if (switch_cache_db_get_db_handle(&dbh, SCDB_TYPE_PGSQL, &options) != SWITCH_STATUS_SUCCESS) + dbh = NULL; + } else { + options.odbc_options.dsn = globals.odbc_dsn; + options.odbc_options.user = globals.odbc_user; + options.odbc_options.pass = globals.odbc_pass; + + if (switch_cache_db_get_db_handle(&dbh, SCDB_TYPE_ODBC, &options) != SWITCH_STATUS_SUCCESS) { + dbh = NULL; + } } return dbh; } else { @@ -4010,7 +4018,7 @@ static switch_status_t load_config(int reload, int del_all) } if (!strcasecmp(var, "odbc-dsn") && !zstr(val)) { - if (switch_odbc_available()) { + if (switch_odbc_available() || switch_pgsql_available()) { switch_set_string(globals.odbc_dsn, val); if ((globals.odbc_user = strchr(globals.odbc_dsn, ':'))) { *globals.odbc_user++ = '\0'; diff --git a/src/mod/applications/mod_voicemail/mod_voicemail.c b/src/mod/applications/mod_voicemail/mod_voicemail.c index 78eb57a20a..2e17e5dbf6 100644 --- a/src/mod/applications/mod_voicemail/mod_voicemail.c +++ b/src/mod/applications/mod_voicemail/mod_voicemail.c @@ -167,12 +167,20 @@ switch_cache_db_handle_t *vm_get_db_handle(vm_profile_t *profile) switch_cache_db_handle_t *dbh = NULL; if (!zstr(profile->odbc_dsn)) { - options.odbc_options.dsn = profile->odbc_dsn; - options.odbc_options.user = profile->odbc_user; - options.odbc_options.pass = profile->odbc_pass; + char *dsn; + if ((dsn = strstr(profile->odbc_dsn, "pgsql;")) != NULL) { + options.pgsql_options.dsn = (char*)(dsn + 6); - if (switch_cache_db_get_db_handle(&dbh, SCDB_TYPE_ODBC, &options) != SWITCH_STATUS_SUCCESS) - dbh = NULL; + if (switch_cache_db_get_db_handle(&dbh, SCDB_TYPE_PGSQL, &options) != SWITCH_STATUS_SUCCESS) + dbh = NULL; + } else { + options.odbc_options.dsn = profile->odbc_dsn; + options.odbc_options.user = profile->odbc_user; + options.odbc_options.pass = profile->odbc_pass; + + if (switch_cache_db_get_db_handle(&dbh, SCDB_TYPE_ODBC, &options) != SWITCH_STATUS_SUCCESS) + dbh = NULL; + } return dbh; } else { options.core_db_options.db_path = profile->dbname; diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index 26ff378ee4..e738cf2fb0 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -4414,7 +4414,7 @@ switch_status_t config_sofia(int reload, char *profile_name) sofia_set_flag(profile, TFLAG_CAPTURE); nua_set_params(profile->nua, TPTAG_CAPT(mod_sofia_globals.capture_server), TAG_END()); } else if (!strcasecmp(var, "odbc-dsn") && !zstr(val)) { - if (switch_odbc_available()) { + if (switch_odbc_available() || switch_pgsql_available()) { profile->odbc_dsn = switch_core_strdup(profile->pool, val); if ((profile->odbc_user = strchr(profile->odbc_dsn, ':'))) { *profile->odbc_user++ = '\0'; diff --git a/src/mod/endpoints/mod_sofia/sofia_glue.c b/src/mod/endpoints/mod_sofia/sofia_glue.c index a67bc97b36..a39a8e9e71 100644 --- a/src/mod/endpoints/mod_sofia/sofia_glue.c +++ b/src/mod/endpoints/mod_sofia/sofia_glue.c @@ -6367,12 +6367,20 @@ switch_cache_db_handle_t *sofia_glue_get_db_handle(sofia_profile_t *profile) switch_cache_db_handle_t *dbh = NULL; if (!zstr(profile->odbc_dsn)) { - options.odbc_options.dsn = profile->odbc_dsn; - options.odbc_options.user = profile->odbc_user; - options.odbc_options.pass = profile->odbc_pass; + char *dsn; + if ((dsn = strstr(profile->odbc_dsn, "pgsql;")) != NULL) { + options.pgsql_options.dsn = (char*)(dsn + 6); - if (switch_cache_db_get_db_handle(&dbh, SCDB_TYPE_ODBC, &options) != SWITCH_STATUS_SUCCESS) - dbh = NULL; + if (switch_cache_db_get_db_handle(&dbh, SCDB_TYPE_PGSQL, &options) != SWITCH_STATUS_SUCCESS) + dbh = NULL; + } else { + options.odbc_options.dsn = profile->odbc_dsn; + options.odbc_options.user = profile->odbc_user; + options.odbc_options.pass = profile->odbc_pass; + + if (switch_cache_db_get_db_handle(&dbh, SCDB_TYPE_ODBC, &options) != SWITCH_STATUS_SUCCESS) + dbh = NULL; + } return dbh; } else { options.core_db_options.db_path = profile->dbname; diff --git a/src/switch_core.c b/src/switch_core.c index 95a7b4dbed..8e1a6567d7 100644 --- a/src/switch_core.c +++ b/src/switch_core.c @@ -1910,7 +1910,7 @@ static void switch_load_core_config(const char *file) } else if (!strcasecmp(var, "core-db-name") && !zstr(val)) { runtime.dbname = switch_core_strdup(runtime.memory_pool, val); } else if (!strcasecmp(var, "core-db-dsn") && !zstr(val)) { - if (switch_odbc_available()) { + if (switch_odbc_available() || switch_pgsql_available()) { runtime.odbc_dsn = switch_core_strdup(runtime.memory_pool, val); if ((runtime.odbc_user = strchr(runtime.odbc_dsn, ':'))) { *runtime.odbc_user++ = '\0'; @@ -1919,10 +1919,10 @@ static void switch_load_core_config(const char *file) } } } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ODBC IS NOT AVAILABLE!\n"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ODBC AND PGSQL ARE NOT AVAILABLE!\n"); } } else if (!strcasecmp(var, "core-recovery-db-dsn") && !zstr(val)) { - if (switch_odbc_available()) { + if (switch_odbc_available() || switch_pgsql_available()) { runtime.recovery_odbc_dsn = switch_core_strdup(runtime.memory_pool, val); if ((runtime.recovery_odbc_user = strchr(runtime.recovery_odbc_dsn, ':'))) { *runtime.recovery_odbc_user++ = '\0'; @@ -1931,10 +1931,10 @@ static void switch_load_core_config(const char *file) } } } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ODBC IS NOT AVAILABLE!\n"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ODBC AND PGSQL ARE NOT AVAILABLE!\n"); } - } else if (!strcasecmp(var, "core-odbc-required") && !zstr(val)) { - switch_set_flag((&runtime), SCF_CORE_ODBC_REQ); + } else if (!strcasecmp(var, "core-non-sqlite-db-required") && !zstr(val)) { + switch_set_flag((&runtime), SCF_CORE_NON_SQLITE_DB_REQ); } else if (!strcasecmp(var, "core-dbtype") && !zstr(val)) { if (!strcasecmp(val, "MSSQL")) { runtime.odbc_dbtype = DBTYPE_MSSQL; diff --git a/src/switch_core_sqldb.c b/src/switch_core_sqldb.c index b4a58bbd9d..cbc7a611e3 100644 --- a/src/switch_core_sqldb.c +++ b/src/switch_core_sqldb.c @@ -190,7 +190,7 @@ SWITCH_DECLARE(switch_status_t) _switch_core_db_handle(switch_cache_db_handle_t } if (zstr(runtime.odbc_dsn)) { - if (switch_test_flag((&runtime), SCF_CORE_ODBC_REQ)) { + if (switch_test_flag((&runtime), SCF_CORE_NON_SQLITE_DB_REQ)) { return SWITCH_STATUS_FALSE; } @@ -202,11 +202,18 @@ SWITCH_DECLARE(switch_status_t) _switch_core_db_handle(switch_cache_db_handle_t r = _switch_cache_db_get_db_handle(dbh, SCDB_TYPE_CORE_DB, &options, file, func, line); } else { - options.odbc_options.dsn = runtime.odbc_dsn; - options.odbc_options.user = runtime.odbc_user; - options.odbc_options.pass = runtime.odbc_pass; + char *dsn; + if ((dsn = strstr(runtime.odbc_dsn, "pgsql;")) != NULL) { + options.pgsql_options.dsn = (char*)(dsn + 6); - r = _switch_cache_db_get_db_handle(dbh, SCDB_TYPE_ODBC, &options, file, func, line); + r = _switch_cache_db_get_db_handle(dbh, SCDB_TYPE_PGSQL, &options, file, func, line); + } else { + options.odbc_options.dsn = runtime.odbc_dsn; + options.odbc_options.user = runtime.odbc_user; + options.odbc_options.pass = runtime.odbc_pass; + + r = _switch_cache_db_get_db_handle(dbh, SCDB_TYPE_ODBC, &options, file, func, line); + } } /* I *think* we can do without this now, if not let me know @@ -218,6 +225,56 @@ SWITCH_DECLARE(switch_status_t) _switch_core_db_handle(switch_cache_db_handle_t return r; } +#define SWITCH_CORE_PERSIST_DB "core" +/*! + \brief Open the default system database +*/ +SWITCH_DECLARE(switch_status_t) _switch_core_persist_db_handle(switch_cache_db_handle_t **dbh, const char *file, const char *func, int line) +{ + switch_cache_db_connection_options_t options = { {0} }; + switch_status_t r; + + if (!sql_manager.manage) { + return SWITCH_STATUS_FALSE; + } + + if (zstr(runtime.odbc_dsn)) { + if (switch_test_flag((&runtime), SCF_CORE_NON_SQLITE_DB_REQ)) { + return SWITCH_STATUS_FALSE; + } + + if (runtime.dbname) { + options.core_db_options.db_path = runtime.dbname; + } else { + options.core_db_options.db_path = SWITCH_CORE_PERSIST_DB; + } + r = _switch_cache_db_get_db_handle(dbh, SCDB_TYPE_CORE_DB, &options, file, func, line); + + } else { + char *dsn; + if ((dsn = strstr(runtime.odbc_dsn, "pgsql;")) != NULL) { + options.pgsql_options.dsn = (char*)(dsn + 6); + + r = _switch_cache_db_get_db_handle(dbh, SCDB_TYPE_PGSQL, &options, file, func, line); + } else { + options.odbc_options.dsn = runtime.odbc_dsn; + options.odbc_options.user = runtime.odbc_user; + options.odbc_options.pass = runtime.odbc_pass; + + r = _switch_cache_db_get_db_handle(dbh, SCDB_TYPE_ODBC, &options, file, func, line); + } + } + + /* I *think* we can do without this now, if not let me know + if (r == SWITCH_STATUS_SUCCESS && !(*dbh)->io_mutex) { + (*dbh)->io_mutex = sql_manager.io_mutex; + } + */ + + return r; +} + + #define SWITCH_CORE_RECOVERY_DB "core_recovery" SWITCH_DECLARE(switch_status_t) _switch_core_recovery_db_handle(switch_cache_db_handle_t **dbh, const char *file, const char *func, int line) { @@ -229,7 +286,7 @@ SWITCH_DECLARE(switch_status_t) _switch_core_recovery_db_handle(switch_cache_db_ } if (zstr(runtime.recovery_odbc_dsn)) { - if (switch_test_flag((&runtime), SCF_CORE_ODBC_REQ)) { + if (switch_test_flag((&runtime), SCF_CORE_NON_SQLITE_DB_REQ)) { return SWITCH_STATUS_FALSE; } @@ -241,11 +298,18 @@ SWITCH_DECLARE(switch_status_t) _switch_core_recovery_db_handle(switch_cache_db_ r = _switch_cache_db_get_db_handle(dbh, SCDB_TYPE_CORE_DB, &options, file, func, line); } else { - options.odbc_options.dsn = runtime.recovery_odbc_dsn; - options.odbc_options.user = runtime.recovery_odbc_user; - options.odbc_options.pass = runtime.recovery_odbc_pass; + char *dsn; + if ((dsn = strstr(runtime.recovery_odbc_dsn, "pgsql;")) != NULL) { + options.pgsql_options.dsn = (char*)(dsn + 6); - r = _switch_cache_db_get_db_handle(dbh, SCDB_TYPE_ODBC, &options, file, func, line); + r = _switch_cache_db_get_db_handle(dbh, SCDB_TYPE_PGSQL, &options, file, func, line); + } else { + options.odbc_options.dsn = runtime.recovery_odbc_dsn; + options.odbc_options.user = runtime.recovery_odbc_user; + options.odbc_options.pass = runtime.recovery_odbc_pass; + + r = _switch_cache_db_get_db_handle(dbh, SCDB_TYPE_ODBC, &options, file, func, line); + } } /* I *think* we can do without this now, if not let me know @@ -286,6 +350,11 @@ static void sql_close(time_t prune) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG10, "Dropping idle DB connection %s\n", dbh->name); switch (dbh->type) { + case SCDB_TYPE_PGSQL: + { + switch_pgsql_handle_destroy(&dbh->native_handle.pgsql_dbh); + } + break; case SCDB_TYPE_ODBC: { switch_odbc_handle_destroy(&dbh->native_handle.odbc_dbh); @@ -373,8 +442,8 @@ SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle(switch_cache_db_h uint32_t yield_len = 100000, total_yield = 0; const char *db_name = NULL; - const char *db_user = NULL; - const char *db_pass = NULL; + const char *odbc_user = NULL; + const char *odbc_pass = NULL; while(runtime.max_db_handles && sql_manager.total_handles >= runtime.max_db_handles && sql_manager.total_used_handles >= sql_manager.total_handles) { if (!waiting++) { @@ -393,18 +462,24 @@ SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle(switch_cache_db_h } switch (type) { + case SCDB_TYPE_PGSQL: + { + db_name = connection_options->odbc_options.dsn; + odbc_user = NULL; + odbc_pass = NULL; + } case SCDB_TYPE_ODBC: { db_name = connection_options->odbc_options.dsn; - db_user = connection_options->odbc_options.user; - db_pass = connection_options->odbc_options.pass; + odbc_user = connection_options->odbc_options.user; + odbc_pass = connection_options->odbc_options.pass; } break; case SCDB_TYPE_CORE_DB: { db_name = connection_options->core_db_options.db_path; - db_user = ""; - db_pass = ""; + odbc_user = NULL; + odbc_pass = NULL; } break; } @@ -413,8 +488,11 @@ SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle(switch_cache_db_h return SWITCH_STATUS_FALSE; } - - snprintf(db_str, sizeof(db_str) - 1, "db=\"%s\";user=\"%s\";pass=\"%s\"", db_name, db_user, db_pass); + if (odbc_user || odbc_pass) { + snprintf(db_str, sizeof(db_str) - 1, "db=\"%s\";user=\"%s\";pass=\"%s\"", db_name, odbc_user, odbc_pass); + } else { + snprintf(db_str, sizeof(db_str) - 1, "db=\"%s\"", db_name); + } snprintf(db_callsite_str, sizeof(db_callsite_str) - 1, "%s:%d", file, line); snprintf(thread_str, sizeof(thread_str) - 1, "thread=\"%lu\"", (unsigned long) (intptr_t) self); @@ -424,8 +502,23 @@ SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle(switch_cache_db_h } else { switch_core_db_t *db = NULL; switch_odbc_handle_t *odbc_dbh = NULL; + switch_pgsql_handle_t *pgsql_dbh = NULL; switch (type) { + case SCDB_TYPE_PGSQL: + { + if (!switch_pgsql_available()) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failure! PGSQL NOT AVAILABLE! Can't connect to DSN %s\n", connection_options->pgsql_options.dsn); + goto end; + } + + if ((pgsql_dbh = switch_pgsql_handle_new(connection_options->pgsql_options.dsn))) { + if (switch_pgsql_handle_connect(pgsql_dbh) != SWITCH_PGSQL_SUCCESS) { + switch_pgsql_handle_destroy(&pgsql_dbh); + } + } + } + break; case SCDB_TYPE_ODBC: { @@ -454,8 +547,8 @@ SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle(switch_cache_db_h goto end; } - if (!db && !odbc_dbh) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failure to connect to %s %s!\n", db?"SQLITE":"ODBC", db?connection_options->core_db_options.db_path:connection_options->odbc_options.dsn); + if (!db && !odbc_dbh && !pgsql_dbh) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failure to connect to %s %s!\n", (db ? "SQLITE" : (odbc_dbh ? "ODBC" : "PGSQL")), (db ? connection_options->core_db_options.db_path : (odbc_dbh ? connection_options->odbc_options.dsn : connection_options->pgsql_options.dsn))); goto end; } @@ -466,8 +559,10 @@ SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle(switch_cache_db_h if (db) { new_dbh->native_handle.core_db_dbh = db; - } else { + } else if (odbc_dbh) { new_dbh->native_handle.odbc_dbh = odbc_dbh; + } else { + new_dbh->native_handle.pgsql_dbh = pgsql_dbh; } add_handle(new_dbh, db_str, db_callsite_str, thread_str); @@ -499,6 +594,11 @@ static switch_status_t switch_cache_db_execute_sql_real(switch_cache_db_handle_t } switch (dbh->type) { + case SCDB_TYPE_PGSQL: + { + status = switch_pgsql_handle_exec(dbh->native_handle.pgsql_dbh, sql, &errmsg); + } + break; case SCDB_TYPE_ODBC: { status = switch_odbc_handle_exec(dbh->native_handle.odbc_dbh, sql, NULL, &errmsg); @@ -650,6 +750,11 @@ SWITCH_DECLARE(int) switch_cache_db_affected_rows(switch_cache_db_handle_t *dbh) return switch_odbc_handle_affected_rows(dbh->native_handle.odbc_dbh); } break; + case SCDB_TYPE_PGSQL: + { + return switch_pgsql_handle_affected_rows(dbh->native_handle.pgsql_dbh); + } + break; } return 0; } @@ -707,6 +812,11 @@ SWITCH_DECLARE(char *) switch_cache_db_execute_sql2str(switch_cache_db_handle_t status = switch_odbc_handle_exec_string(dbh->native_handle.odbc_dbh, sql, str, len, err); } break; + case SCDB_TYPE_PGSQL: + { + status = switch_pgsql_handle_exec_string(dbh->native_handle.pgsql_dbh, sql, str, len, err); + } + break; } end: @@ -784,16 +894,34 @@ SWITCH_DECLARE(switch_status_t) switch_cache_db_persistant_execute_trans(switch_ while (begin_retries > 0) { again = 0; - if (runtime.odbc_dbtype == DBTYPE_DEFAULT) { - switch_cache_db_execute_sql_real(dbh, "BEGIN", &errmsg); - } else { - switch_odbc_status_t result; - - if ((result = switch_odbc_SQLSetAutoCommitAttr(dbh->native_handle.odbc_dbh, 0)) != SWITCH_ODBC_SUCCESS) { - char tmp[100]; - switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to Set AutoCommit Off", result); - errmsg = strdup(tmp); + switch(dbh->type) { + case SCDB_TYPE_CORE_DB: + { + switch_cache_db_execute_sql_real(dbh, "BEGIN", &errmsg); } + break; + case SCDB_TYPE_ODBC: + { + switch_odbc_status_t result; + + if ((result = switch_odbc_SQLSetAutoCommitAttr(dbh->native_handle.odbc_dbh, 0)) != SWITCH_ODBC_SUCCESS) { + char tmp[100]; + switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to Set AutoCommit Off", result); + errmsg = strdup(tmp); + } + } + break; + case SCDB_TYPE_PGSQL: + { + switch_pgsql_status_t result; + + if ((result = switch_pgsql_SQLSetAutoCommitAttr(dbh->native_handle.pgsql_dbh, 0)) != SWITCH_PGSQL_SUCCESS) { + char tmp[100]; + switch_snprintfv(tmp, sizeof(tmp), "%q-%i", "Unable to Set AutoCommit Off", result); + errmsg = strdup(tmp); + } + } + break; } if (errmsg) { @@ -807,11 +935,25 @@ SWITCH_DECLARE(switch_status_t) switch_cache_db_persistant_execute_trans(switch_ errmsg = NULL; if (again) { - if (runtime.odbc_dbtype == DBTYPE_DEFAULT) { - switch_cache_db_execute_sql_real(dbh, "COMMIT", NULL); - } else { - switch_odbc_SQLEndTran(dbh->native_handle.odbc_dbh, 1); - switch_odbc_SQLSetAutoCommitAttr(dbh->native_handle.odbc_dbh, 1); + switch(dbh->type) { + case SCDB_TYPE_CORE_DB: + { + switch_cache_db_execute_sql_real(dbh, "COMMIT", NULL); + } + break; + case SCDB_TYPE_ODBC: + { + switch_odbc_SQLEndTran(dbh->native_handle.odbc_dbh, 1); + switch_odbc_SQLSetAutoCommitAttr(dbh->native_handle.odbc_dbh, 1); + } + break; + case SCDB_TYPE_PGSQL: + { + switch_pgsql_SQLEndTran(dbh->native_handle.pgsql_dbh, 1); + switch_pgsql_SQLSetAutoCommitAttr(dbh->native_handle.pgsql_dbh, 1); + switch_pgsql_finish_results(dbh->native_handle.pgsql_dbh); + } + break; } goto again; @@ -868,11 +1010,25 @@ SWITCH_DECLARE(switch_status_t) switch_cache_db_persistant_execute_trans(switch_ done: - if (runtime.odbc_dbtype == DBTYPE_DEFAULT) { - switch_cache_db_execute_sql_real(dbh, "COMMIT", NULL); - } else { - switch_odbc_SQLEndTran(dbh->native_handle.odbc_dbh, 1); - switch_odbc_SQLSetAutoCommitAttr(dbh->native_handle.odbc_dbh, 1); + switch(dbh->type) { + case SCDB_TYPE_CORE_DB: + { + switch_cache_db_execute_sql_real(dbh, "COMMIT", NULL); + } + break; + case SCDB_TYPE_ODBC: + { + switch_odbc_SQLEndTran(dbh->native_handle.odbc_dbh, 1); + switch_odbc_SQLSetAutoCommitAttr(dbh->native_handle.odbc_dbh, 1); + } + break; + case SCDB_TYPE_PGSQL: + { + switch_pgsql_SQLEndTran(dbh->native_handle.pgsql_dbh, 1); + switch_pgsql_SQLSetAutoCommitAttr(dbh->native_handle.pgsql_dbh, 1); + switch_pgsql_finish_results(dbh->native_handle.pgsql_dbh); + } + break; } if (!zstr(runtime.core_db_post_trans_execute)) { @@ -903,6 +1059,11 @@ SWITCH_DECLARE(switch_status_t) switch_cache_db_execute_sql_callback(switch_cach switch (dbh->type) { + case SCDB_TYPE_PGSQL: + { + status = switch_pgsql_handle_callback_exec(dbh->native_handle.pgsql_dbh, sql, callback, pdata, err); + } + break; case SCDB_TYPE_ODBC: { status = switch_odbc_handle_callback_exec(dbh->native_handle.odbc_dbh, sql, callback, pdata, err); @@ -951,6 +1112,17 @@ SWITCH_DECLARE(switch_bool_t) switch_cache_db_test_reactive(switch_cache_db_hand if (io_mutex) switch_mutex_lock(io_mutex); switch (dbh->type) { + case SCDB_TYPE_PGSQL: + { + if (switch_pgsql_handle_exec(dbh->native_handle.pgsql_dbh, test_sql, NULL) != SWITCH_PGSQL_SUCCESS) { + r = SWITCH_FALSE; + if (drop_sql) { + switch_pgsql_handle_exec(dbh->native_handle.pgsql_dbh, drop_sql, NULL); + } + switch_pgsql_handle_exec(dbh->native_handle.pgsql_dbh, reactive_sql, NULL); + } + } + break; case SCDB_TYPE_ODBC: { if (switch_odbc_handle_exec(dbh->native_handle.odbc_dbh, test_sql, NULL, NULL) != SWITCH_ODBC_SUCCESS) { @@ -1057,6 +1229,8 @@ static void *SWITCH_THREAD_FUNC switch_core_sql_thread(switch_thread_t *thread, switch_mutex_lock(sql_manager.cond_mutex); switch (sql_manager.event_db->type) { + case SCDB_TYPE_PGSQL: + break; case SCDB_TYPE_ODBC: break; case SCDB_TYPE_CORE_DB: @@ -2313,7 +2487,7 @@ switch_status_t switch_core_sqldb_start(switch_memory_pool_t *pool, switch_bool_ if (switch_core_db_handle(&sql_manager.dbh) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB!\n"); - if (switch_test_flag((&runtime), SCF_CORE_ODBC_REQ)) { + if (switch_test_flag((&runtime), SCF_CORE_NON_SQLITE_DB_REQ)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failure! ODBC IS REQUIRED!\n"); return SWITCH_STATUS_FALSE; } @@ -2336,6 +2510,7 @@ switch_status_t switch_core_sqldb_start(switch_memory_pool_t *pool, switch_bool_ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Opening DB\n"); switch (sql_manager.dbh->type) { + case SCDB_TYPE_PGSQL: case SCDB_TYPE_ODBC: if (switch_test_flag((&runtime), SCF_CLEAR_SQL)) { char sql[512] = ""; @@ -2376,6 +2551,7 @@ switch_status_t switch_core_sqldb_start(switch_memory_pool_t *pool, switch_bool_ switch (sql_manager.dbh->type) { + case SCDB_TYPE_PGSQL: case SCDB_TYPE_ODBC: { char *err; @@ -2405,7 +2581,7 @@ switch_status_t switch_core_sqldb_start(switch_memory_pool_t *pool, switch_bool_ runtime.odbc_dsn = NULL; runtime.odbc_user = NULL; runtime.odbc_pass = NULL; - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Transactions not supported on your DB, disabling ODBC\n"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Transactions not supported on your DB, disabling non-SQLite support; using SQLite\n"); switch_cache_db_release_db_handle(&sql_manager.dbh); free(err); goto top; @@ -2536,9 +2712,9 @@ SWITCH_DECLARE(void) switch_core_sqldb_start_thread(void) if (switch_core_recovery_db_handle(&dbh) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB!\n"); - if (switch_test_flag((&runtime), SCF_CORE_ODBC_REQ)) { + if (switch_test_flag((&runtime), SCF_CORE_NON_SQLITE_DB_REQ)) { int arg = 1; - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failure! ODBC IS REQUIRED!\n"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failure! ODBC OR PGSQL IS REQUIRED!\n"); switch_core_session_ctl(SCSC_SHUTDOWN_NOW, &arg); } @@ -2562,8 +2738,8 @@ SWITCH_DECLARE(void) switch_core_sqldb_start_thread(void) if (switch_core_db_handle(&sql_manager.dbh) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB!\n"); - if (switch_test_flag((&runtime), SCF_CORE_ODBC_REQ)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failure! ODBC IS REQUIRED!\n"); + if (switch_test_flag((&runtime), SCF_CORE_NON_SQLITE_DB_REQ)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failure! ODBC OR PGSQL IS REQUIRED!\n"); goto end; } diff --git a/src/switch_pgsql.c b/src/switch_pgsql.c new file mode 100644 index 0000000000..ead39a7e9f --- /dev/null +++ b/src/switch_pgsql.c @@ -0,0 +1,770 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2012, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Eliot Gable + * + * switch_pgsql.c -- PGSQL Driver + * + */ + +#include +#include + +#ifdef SWITCH_HAVE_PGSQL +#include + + +struct switch_pgsql_handle { + char *dsn; + const char *sql; + PGconn* con; + int sock; + switch_pgsql_state_t state; + int affected_rows; + int num_retries; + switch_bool_t auto_commit; + switch_bool_t in_txn; +}; + +struct switch_pgsql_result { + PGresult *result; + ExecStatusType status; + char *err; + int rows; + int cols; +}; +#endif + +SWITCH_DECLARE(switch_pgsql_handle_t *) switch_pgsql_handle_new(const char *dsn) +{ +#ifdef SWITCH_HAVE_PGSQL + switch_pgsql_handle_t *new_handle; + + if (!(new_handle = malloc(sizeof(*new_handle)))) { + goto err; + } + + memset(new_handle, 0, sizeof(*new_handle)); + + if (!(new_handle->dsn = strdup(dsn))) { + goto err; + } + + new_handle->sock = 0; + new_handle->state = SWITCH_PGSQL_STATE_INIT; + new_handle->con = NULL; + new_handle->affected_rows = 0; + new_handle->num_retries = DEFAULT_PGSQL_RETRIES; + new_handle->auto_commit = SWITCH_TRUE; + new_handle->in_txn = SWITCH_FALSE; + + return new_handle; + + err: + if (new_handle) { + switch_safe_free(new_handle->dsn); + switch_safe_free(new_handle); + } +#endif + return NULL; +} + +SWITCH_DECLARE(void) switch_pgsql_set_num_retries(switch_pgsql_handle_t *handle, int num_retries) +{ +#ifdef SWITCH_HAVE_PGSQL + if (handle) { + handle->num_retries = num_retries; + } +#endif +} + +SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_handle_disconnect(switch_pgsql_handle_t *handle) +{ +#ifdef SWITCH_HAVE_PGSQL + + if (!handle) { + return SWITCH_PGSQL_FAIL; + } + + if (handle->state == SWITCH_PGSQL_STATE_CONNECTED) { + PQfinish(handle->con); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG10, "Disconnected from [%s]\n", handle->dsn); + } + + handle->state = SWITCH_PGSQL_STATE_DOWN; + + return SWITCH_PGSQL_SUCCESS; +#else + return SWITCH_PGSQL_FAIL; +#endif +} + + +#ifdef SWITCH_HAVE_PGSQL + +SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_send_query(switch_pgsql_handle_t *handle, const char* sql) +{ + char *err_str; + + if (!PQsendQuery(handle->con, sql)) { + err_str = switch_pgsql_handle_get_error(handle); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to send query (%s) to database: %s\n", sql, err_str); + switch_pgsql_finish_results(handle); + goto error; + } + handle->sql = sql; + + return SWITCH_PGSQL_SUCCESS; + error: + return SWITCH_PGSQL_FAIL; +} + +SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_cancel_real(const char *file, const char *func, int line, switch_pgsql_handle_t *handle) +{ + char err_buf[256]; + PGcancel *cancel = NULL; + switch_pgsql_status_t ret = SWITCH_PGSQL_SUCCESS; + + memset(err_buf, 0, 256); + cancel = PQgetCancel(handle->con); + if(!PQcancel(cancel, err_buf, 256)) { + switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL, SWITCH_LOG_CRIT, "Failed to cancel long-running query (%s): %s\n", handle->sql, err_buf); + ret = SWITCH_PGSQL_FAIL; + } + PQfreeCancel(cancel); + return ret; +} + + +SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_next_result_timed(switch_pgsql_handle_t *handle, switch_pgsql_result_t **result_out, int msec) +{ + switch_pgsql_result_t *res; + switch_time_t start; + switch_time_t ctime; + unsigned int usec = msec * 1000; + char *err_str; + fd_set pgset; + struct timeval timeout; + + if(!handle) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "**BUG** Null handle passed to switch_pgsql_next_result.\n"); + return SWITCH_PGSQL_FAIL; + } + + /* Try to consume input that might be waiting right away */ + if (PQconsumeInput(handle->con)) { + /* And check to see if we have a full result ready for reading */ + if (PQisBusy(handle->con)) { + + /* Wait for a result to become available, up to msec milliseconds */ + start = switch_time_now(); + while((ctime = switch_time_now()) - start <= usec) { + FD_ZERO(&pgset); + FD_SET(handle->sock, &pgset); + + timeout.tv_sec = 0; + timeout.tv_usec = 500; + + /* Wait for the PostgreSQL socket to be ready for data reads. */ + if (select(FD_SETSIZE, &pgset, NULL, NULL, &timeout) > 0) { + /* Then try to consume any input waiting. */ + if (PQconsumeInput(handle->con)) { + /* And check to see if we have a full result ready for reading */ + if (!PQisBusy(handle->con)) { + /* If we can pull a full result without blocking, then break this loop */ + break; + } + } else { + /* If we had an error trying to consume input, report it and cancel the query. */ + err_str = switch_pgsql_handle_get_error(handle); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "An error occurred trying to consume input for query (%s): %s\n", handle->sql, err_str); + switch_safe_free(err_str); + switch_pgsql_cancel(handle); + goto error; + } + } + } + + /* If we broke the loop above because of a timeout, report that and cancel the query. */ + if (ctime - start > usec) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Query (%s) took too long to complete or database not responding.\n", handle->sql); + switch_pgsql_cancel(handle); + goto error; + } + + + + } + } else { + /* If we had an error trying to consume input, report it and cancel the query. */ + err_str = switch_pgsql_handle_get_error(handle); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "An error occurred trying to consume input for query (%s): %s\n", handle->sql, err_str); + switch_safe_free(err_str); + switch_pgsql_cancel(handle); + goto error; + } + + + /* At this point, we know we can read a full result without blocking. */ + if(!(res = malloc(sizeof(switch_pgsql_result_t)))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Malloc failed!\n"); + goto error; + } + memset(res, 0, sizeof(switch_pgsql_result_t)); + + + res->result = PQgetResult(handle->con); + if (res->result) { + *result_out = res; + res->status = PQresultStatus(res->result); + switch(res->status) { + case PGRES_TUPLES_OK: + { + res->rows = PQntuples(res->result); + handle->affected_rows = res->rows; + res->cols = PQnfields(res->result); + } + break; + case PGRES_COPY_OUT: + case PGRES_COPY_IN: + case PGRES_COMMAND_OK: + break; + case PGRES_EMPTY_QUERY: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Query (%s) returned PGRES_EMPTY_QUERY\n", handle->sql); + case PGRES_BAD_RESPONSE: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Query (%s) returned PGRES_BAD_RESPONSE\n", handle->sql); + case PGRES_NONFATAL_ERROR: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Query (%s) returned PGRES_NONFATAL_ERROR\n", handle->sql); + case PGRES_FATAL_ERROR: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Query (%s) returned PGRES_FATAL_ERROR\n", handle->sql); + res->err = PQresultErrorMessage(res->result); + goto error; + break; + } + } else { + free(res); + res = NULL; + *result_out = NULL; + goto error; + } + + return SWITCH_PGSQL_SUCCESS; + error: + return SWITCH_PGSQL_FAIL; +} + +SWITCH_DECLARE(void) switch_pgsql_free_result(switch_pgsql_result_t **result) +{ + if (!*result) { + return; + } + + if ((*result)->result) { + PQclear((*result)->result); + } + free(*result); + *result = NULL; +} + +SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_finish_results_real(const char* file, const char* func, int line, switch_pgsql_handle_t *handle) +{ + switch_pgsql_result_t *res = NULL; + switch_pgsql_status_t final_status = SWITCH_PGSQL_SUCCESS; + int done = 0; + do { + switch_pgsql_next_result(handle, &res); + if (res && res->err) { + switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL, SWITCH_LOG_ERROR, "Error executing query:\n%s\n", res->err); + final_status = SWITCH_PGSQL_FAIL; + } + if (!res) done = 1; + switch_pgsql_free_result(&res); + } while (!done); + return final_status; +} + +static int db_is_up(switch_pgsql_handle_t *handle) +{ + int ret = 0; + switch_event_t *event; + char *err_str = NULL; + int max_tries = DEFAULT_PGSQL_RETRIES; + int code = 0, recon = 0; + + if (handle) { + max_tries = handle->num_retries; + if (max_tries < 1) + max_tries = DEFAULT_PGSQL_RETRIES; + } + + top: + + if (!handle) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "No DB Handle\n"); + goto done; + } + if (!handle->con) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "No DB Connection\n"); + goto done; + } + + if (PQstatus(handle->con) == CONNECTION_BAD) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "PQstatus returned bad connection; reconnecting...\n"); + handle->state = SWITCH_PGSQL_STATE_ERROR; + PQreset(handle->con); + if (PQstatus(handle->con) == CONNECTION_BAD) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "PQstatus returned bad connection -- reconnection failed!\n"); + goto error; + } + handle->state = SWITCH_PGSQL_STATE_CONNECTED; + } + +/* if (!PQsendQuery(handle->con, "SELECT 1")) { + code = __LINE__; + goto error; + } + + if(switch_pgsql_next_result(handle, &result) == SWITCH_PGSQL_FAIL) { + code = __LINE__; + goto error; + } + + if (!result || result->status != PGRES_COMMAND_OK) { + code = __LINE__; + goto error; + } + + switch_pgsql_free_result(&result); + switch_pgsql_finish_results(handle); +*/ + ret = 1; + goto done; + + error: + err_str = switch_pgsql_handle_get_error(handle); + + if (PQstatus(handle->con) == CONNECTION_BAD) { + handle->state = SWITCH_PGSQL_STATE_ERROR; + PQreset(handle->con); + if (PQstatus(handle->con) == CONNECTION_OK) { + handle->state = SWITCH_PGSQL_STATE_CONNECTED; + recon = SWITCH_PGSQL_SUCCESS; + } + } + + max_tries--; + + if (switch_event_create(&event, SWITCH_EVENT_TRAP) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Failure-Message", "The sql server is not responding for DSN %s [%s][%d]", + switch_str_nil(handle->dsn), switch_str_nil(err_str), code); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "The sql server is not responding for DSN %s [%s][%d]\n", + switch_str_nil(handle->dsn), switch_str_nil(err_str), code); + + if (recon == SWITCH_PGSQL_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Additional-Info", "The connection has been re-established"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "The connection has been re-established\n"); + } else { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Additional-Info", "The connection could not be re-established"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "The connection could not be re-established\n"); + } + if (!max_tries) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Additional-Info", "Giving up!"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Giving up!\n"); + } + + switch_event_fire(&event); + } + + if (!max_tries) { + goto done; + } + + switch_safe_free(err_str); + switch_yield(1000000); + goto top; + + done: + + switch_safe_free(err_str); + + return ret; +} +#endif + + +SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_handle_connect(switch_pgsql_handle_t *handle) +{ +#ifdef SWITCH_HAVE_PGSQL + if (handle->state == SWITCH_PGSQL_STATE_CONNECTED) { + switch_pgsql_handle_disconnect(handle); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Re-connecting %s\n", handle->dsn); + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Connecting %s\n", handle->dsn); + + handle->con = PQconnectdb(handle->dsn); + if (PQstatus(handle->con) != CONNECTION_OK) { + char *err_str; + if ((err_str = switch_pgsql_handle_get_error(handle))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s\n", err_str); + switch_safe_free(err_str); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to connect to the database [%s]\n", handle->dsn); + switch_pgsql_handle_disconnect(handle); + } + return SWITCH_PGSQL_FAIL; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Connected to [%s]\n", handle->dsn); + handle->state = SWITCH_PGSQL_STATE_CONNECTED; + handle->sock = PQsocket(handle->con); + return SWITCH_PGSQL_SUCCESS; +#else + return SWITCH_PGSQL_FAIL; +#endif +} + +SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_handle_exec_string(switch_pgsql_handle_t *handle, const char *sql, char *resbuf, size_t len, char **err) +{ +#ifdef SWITCH_HAVE_PGSQL + switch_pgsql_status_t sstatus = SWITCH_PGSQL_SUCCESS; + char *val = NULL; + switch_pgsql_result_t *result = NULL; + + handle->affected_rows = 0; + + if (switch_pgsql_handle_exec_base(handle, sql, err) == SWITCH_PGSQL_FAIL) { + goto error; + } + + if(switch_pgsql_next_result(handle, &result) == SWITCH_PGSQL_FAIL) { + goto error; + } + + if (!result || result->status != PGRES_COMMAND_OK) { + sstatus = SWITCH_PGSQL_FAIL; + goto done; + } + + if (handle->affected_rows <= 0) { + goto done; + } + + val = PQgetvalue(result->result, 0, 0); + strncpy(resbuf, val, len); + + done: + + switch_pgsql_free_result(&result); + if (switch_pgsql_finish_results(handle) != SWITCH_PGSQL_SUCCESS) { + sstatus = SWITCH_PGSQL_FAIL; + } + + return sstatus; + error: + return SWITCH_PGSQL_FAIL; +#else + return SWITCH_PGSQL_FAIL; +#endif +} + +SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_handle_exec_base(switch_pgsql_handle_t *handle, const char *sql, char **err) +{ +#ifdef SWITCH_HAVE_PGSQL + char *err_str = NULL; + + handle->affected_rows = 0; + + if (!db_is_up(handle)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Database is not up!\n"); + goto error; + } + + if (handle->auto_commit == SWITCH_FALSE && handle->in_txn == SWITCH_FALSE) { + if (switch_pgsql_send_query(handle, "BEGIN") != SWITCH_PGSQL_SUCCESS) { + switch_pgsql_finish_results(handle); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error sending BEGIN!\n"); + goto error; + } + + if (switch_pgsql_finish_results(handle) != SWITCH_PGSQL_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error sending BEGIN!\n"); + goto error; + } + handle->in_txn = SWITCH_TRUE; + } + + if (switch_pgsql_send_query(handle, sql) != SWITCH_PGSQL_SUCCESS) { + switch_pgsql_finish_results(handle); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error sending query!\n"); + goto error; + } + + return SWITCH_PGSQL_SUCCESS; + + error: + err_str = switch_pgsql_handle_get_error(handle); + + if (zstr(err_str)) { + err_str = strdup((char *)"SQL ERROR!"); + } + + if (err_str) { + if (!switch_stristr("already exists", err_str) && !switch_stristr("duplicate key name", err_str)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ERR: [%s]\n[%s]\n", sql, switch_str_nil(err_str)); + } + if (err) { + *err = err_str; + } else { + free(err_str); + } + } +#endif + return SWITCH_PGSQL_FAIL; +} + +SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_handle_exec(switch_pgsql_handle_t *handle, const char *sql, char **err) +{ +#ifdef SWITCH_HAVE_PGSQL + if (switch_pgsql_handle_exec_base(handle, sql, err) == SWITCH_PGSQL_FAIL) { + goto error; + } + + return switch_pgsql_finish_results(handle); + error: +#endif + return SWITCH_PGSQL_FAIL; +} + +SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_handle_callback_exec_detailed(const char *file, const char *func, int line, + switch_pgsql_handle_t *handle, + const char *sql, switch_core_db_callback_func_t callback, void *pdata, + char **err) +{ +#ifdef SWITCH_HAVE_PGSQL + char *err_str = NULL; + int row = 0, col = 0, err_cnt = 0; + switch_pgsql_result_t *result = NULL; + + handle->affected_rows = 0; + + switch_assert(callback != NULL); + + if (switch_pgsql_handle_exec_base(handle, sql, err) == SWITCH_PGSQL_FAIL) { + goto error; + } + + if (switch_pgsql_next_result(handle, &result) == SWITCH_PGSQL_FAIL) { + err_cnt++; + err_str = switch_pgsql_handle_get_error(handle); + if (result && !zstr(result->err)) { + switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL, SWITCH_LOG_ERROR, "ERR: [%s]\n[%s]\n", sql, switch_str_nil(result->err)); + } + if (!zstr(err_str)) { + switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL, SWITCH_LOG_ERROR, "ERR: [%s]\n[%s]\n", sql, switch_str_nil(err_str)); + } + switch_safe_free(err_str); + err_str = NULL; + } + + while (result != NULL) { + for (row = 0; row < result->rows; ++row) { + char **names; + char **vals; + + names = calloc(result->cols, sizeof(*names)); + vals = calloc(result->cols, sizeof(*vals)); + + switch_assert(names && vals); + + for (col = 0; col < result->cols; ++col) { + char * tmp; + int len; + + tmp = PQfname(result->result, col); + if (tmp) { + len = strlen(tmp); + names[col] = malloc(len+1); + strncpy(names[col], tmp, len); + + len = PQgetlength(result->result, row, col); + vals[col] = malloc(len+1); + tmp = PQgetvalue(result->result, row, col); + strncpy(vals[col], tmp, len); + } else { + switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL, SWITCH_LOG_ERROR, "ERR: Column number %d out of range\n", col); + } + } + + if (callback(pdata, row, vals, names)) { + break; + } + + for (col = 0; col < result->cols; ++col) { + free(names[col]); + free(vals[col]); + } + free(names); + free(vals); + } + if (switch_pgsql_next_result(handle, &result) == SWITCH_PGSQL_FAIL) { + err_cnt++; + err_str = switch_pgsql_handle_get_error(handle); + if (result && !zstr(result->err)) { + switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL, SWITCH_LOG_ERROR, "ERR: [%s]\n[%s]\n", sql, switch_str_nil(result->err)); + } + if (!zstr(err_str)) { + switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL, SWITCH_LOG_ERROR, "ERR: [%s]\n[%s]\n", sql, switch_str_nil(err_str)); + } + switch_safe_free(err_str); + err_str = NULL; + } + } + if (err_cnt) { + goto error; + } + + return SWITCH_PGSQL_SUCCESS; + error: +#endif + return SWITCH_PGSQL_FAIL; +} + +SWITCH_DECLARE(void) switch_pgsql_handle_destroy(switch_pgsql_handle_t **handlep) +{ +#ifdef SWITCH_HAVE_PGSQL + + switch_pgsql_handle_t *handle = NULL; + + if (!handlep) { + return; + } + handle = *handlep; + + if (handle) { + switch_pgsql_handle_disconnect(handle); + + switch_safe_free(handle->dsn); + free(handle); + } + *handlep = NULL; +#else + return; +#endif +} + +SWITCH_DECLARE(switch_pgsql_state_t) switch_pgsql_handle_get_state(switch_pgsql_handle_t *handle) +{ +#ifdef SWITCH_HAVE_PGSQL + return handle ? handle->state : SWITCH_PGSQL_STATE_INIT; +#else + return SWITCH_PGSQL_STATE_ERROR; +#endif +} + +SWITCH_DECLARE(char *) switch_pgsql_handle_get_error(switch_pgsql_handle_t *handle) +{ +#ifdef SWITCH_HAVE_PGSQL + char * err_str; + if (!handle) { + return NULL; + }; + switch_strdup(err_str, PQerrorMessage(handle->con)); + return err_str; +#else + return NULL; +#endif +} + +SWITCH_DECLARE(int) switch_pgsql_handle_affected_rows(switch_pgsql_handle_t *handle) +{ +#ifdef SWITCH_HAVE_PGSQL + return handle->affected_rows; +#else + return 0; +#endif +} + +SWITCH_DECLARE(switch_bool_t) switch_pgsql_available(void) +{ +#ifdef SWITCH_HAVE_PGSQL + return SWITCH_TRUE; +#else + return SWITCH_FALSE; +#endif +} + +SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_SQLSetAutoCommitAttr(switch_pgsql_handle_t *handle, switch_bool_t on) +{ +#ifdef SWITCH_HAVE_PGSQL + if (on) { + handle->auto_commit = SWITCH_TRUE; + } else { + handle->auto_commit = SWITCH_FALSE; + } + return SWITCH_PGSQL_SUCCESS; +#else + return (switch_pgsql_status_t) SWITCH_FALSE; +#endif +} + +SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_SQLEndTran(switch_pgsql_handle_t *handle, switch_bool_t commit) +{ +#ifdef SWITCH_HAVE_PGSQL + char * err_str = NULL; + if (commit) { + if(!PQsendQuery(handle->con, "COMMIT")) { + err_str = switch_pgsql_handle_get_error(handle); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Could not commit transaction: %s\n", err_str); + switch_safe_free(err_str); + return SWITCH_PGSQL_FAIL; + } + } else { + if(!PQsendQuery(handle->con, "ROLLBACK")) { + err_str = switch_pgsql_handle_get_error(handle); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Could not rollback transaction: %s\n", err_str); + switch_safe_free(err_str); + return SWITCH_PGSQL_FAIL; + } + } + handle->in_txn = SWITCH_FALSE; + return SWITCH_PGSQL_SUCCESS; +#else + return (switch_pgsql_status_t) SWITCH_FALSE; +#endif +} + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */