2004-06-11 00:12:35 +00:00
/*
2005-09-14 20:46:50 +00:00
* Asterisk -- An open source telephony toolkit.
2004-06-11 00:12:35 +00:00
*
2012-02-27 15:35:10 +00:00
* Copyright (C) 1999 - 2012, Digium, Inc.
2004-06-11 00:12:35 +00:00
*
2004-10-05 06:46:11 +00:00
* Mark Spencer <markster@digium.com>
2004-06-11 00:12:35 +00:00
*
* res_odbc.c <ODBC resource manager>
2005-01-21 07:06:25 +00:00
* Copyright (C) 2004 - 2005 Anthony Minessale II <anthmct@yahoo.com>
2005-09-14 20:46:50 +00:00
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
2005-10-24 20:12:06 +00:00
/*! \file
2005-09-14 20:46:50 +00:00
*
2005-10-24 20:12:06 +00:00
* \brief ODBC resource manager
2017-12-22 09:23:22 -05:00
*
2005-12-30 21:18:06 +00:00
* \author Mark Spencer <markster@digium.com>
* \author Anthony Minessale II <anthmct@yahoo.com>
2009-02-19 00:26:01 +00:00
* \author Tilghman Lesher <tilghman@digium.com>
2005-09-14 20:46:50 +00:00
*
2005-12-30 21:18:06 +00:00
* \arg See also: \ref cdr_odbc
2004-06-11 00:12:35 +00:00
*/
2012-10-14 21:44:27 +00:00
/*! \li \ref res_odbc.c uses the configuration file \ref res_odbc.conf
2012-10-01 23:24:10 +00:00
* \addtogroup configuration_file Configuration Files
*/
2017-12-22 09:23:22 -05:00
/*!
2012-10-01 23:24:10 +00:00
* \page res_odbc.conf res_odbc.conf
* \verbinclude res_odbc.conf.sample
*/
2006-04-24 17:11:45 +00:00
/*** MODULEINFO
2009-01-15 20:18:53 +00:00
<depend>generic_odbc</depend>
2016-08-18 14:15:46 -06:00
<depend>res_odbc_transaction</depend>
2011-07-14 20:28:54 +00:00
<support_level>core</support_level>
2006-04-24 17:11:45 +00:00
***/
2006-06-07 18:54:56 +00:00
#include "asterisk.h"
2005-04-21 06:02:45 +00:00
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/config.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/cli.h"
#include "asterisk/lock.h"
#include "asterisk/res_odbc.h"
2008-01-21 18:15:57 +00:00
#include "asterisk/time.h"
2008-05-05 23:38:15 +00:00
#include "asterisk/astobj2.h"
2009-02-19 00:26:01 +00:00
#include "asterisk/app.h"
2009-01-19 21:42:46 +00:00
#include "asterisk/strings.h"
2009-02-19 00:26:01 +00:00
#include "asterisk/threadstorage.h"
2006-04-18 18:16:32 +00:00
struct odbc_class
2004-07-08 19:58:26 +00:00
{
2006-04-18 18:16:32 +00:00
AST_LIST_ENTRY ( odbc_class ) list ;
2004-07-08 19:58:26 +00:00
char name [ 80 ];
2006-04-18 18:16:32 +00:00
char dsn [ 80 ];
2008-01-18 06:52:18 +00:00
char * username ;
char * password ;
2008-05-05 23:38:15 +00:00
char * sanitysql ;
2006-04-18 18:16:32 +00:00
SQLHENV env ;
2009-02-19 00:26:01 +00:00
unsigned int delme : 1 ; /*!< Purge the class */
unsigned int backslash_is_escape : 1 ; /*!< On this database, the backslash is a native escape sequence */
unsigned int forcecommit : 1 ; /*!< Should uncommitted transactions be auto-committed on handle release? */
unsigned int isolation ; /*!< Flags for how the DB should deal with data in other, uncommitted transactions */
2010-07-23 16:19:21 +00:00
unsigned int conntimeout ; /*!< Maximum time the connection process should take */
2016-06-02 14:04:45 -03:00
unsigned int maxconnections ; /*!< Maximum number of allowed connections */
2010-07-23 16:19:21 +00:00
/*! When a connection fails, cache that failure for how long? */
struct timeval negative_connection_cache ;
/*! When a connection fails, when did that last occur? */
struct timeval last_negative_connect ;
2016-06-02 14:04:45 -03:00
/*! A pool of available connections */
AST_LIST_HEAD_NOLOCK (, odbc_obj ) connections ;
/*! Lock to protect the connections */
ast_mutex_t lock ;
/*! Condition to notify any pending connection requesters */
ast_cond_t cond ;
/*! The total number of current connections */
size_t connection_cnt ;
2019-02-06 12:16:01 +00:00
/*! Whether logging is enabled on this class or not */
unsigned int logging ;
/*! The number of prepares executed on this class (total from all connections */
int prepares_executed ;
/*! The number of queries executed on this class (total from all connections) */
int queries_executed ;
/*! The longest execution time for a query executed on this class */
long longest_query_execution_time ;
/*! The SQL query that took the longest to execute */
char * sql_text ;
/*! Slow query limit (in milliseconds) */
unsigned int slowquerylimit ;
2004-06-11 00:12:35 +00:00
};
2009-03-18 02:39:36 +00:00
static struct ao2_container * class_container ;
2004-06-11 00:12:35 +00:00
2008-06-10 21:14:58 +00:00
static AST_RWLIST_HEAD_STATIC ( odbc_tables , odbc_cache_tables );
2006-04-18 18:16:32 +00:00
static odbc_status odbc_obj_connect ( struct odbc_obj * obj );
static odbc_status odbc_obj_disconnect ( struct odbc_obj * obj );
2016-06-02 14:04:45 -03:00
static void odbc_register_class ( struct odbc_class * class , int connect );
2009-02-19 00:26:01 +00:00
AST_THREADSTORAGE ( errors_buf );
struct odbc_txn_frame {
AST_LIST_ENTRY ( odbc_txn_frame ) list ;
struct ast_channel * owner ;
struct odbc_obj * obj ; /*!< Database handle within which transacted statements are run */
/*!\brief Is this record the current active transaction within the channel?
* Note that the active flag is really only necessary for statements which
* are triggered from the dialplan, as there isn't a direct correlation
* between multiple statements. Applications wishing to use transactions
* may simply perform each statement on the same odbc_obj, which keeps the
* transaction persistent.
*/
unsigned int active : 1 ;
unsigned int forcecommit : 1 ; /*!< Should uncommitted transactions be auto-committed on handle release? */
unsigned int isolation ; /*!< Flags for how the DB should deal with data in other, uncommitted transactions */
char name [ 0 ]; /*!< Name of this transaction ID */
};
2015-12-23 15:07:05 -06:00
const char * ast_odbc_isolation2text ( int iso )
2009-02-19 00:26:01 +00:00
{
if ( iso == SQL_TXN_READ_COMMITTED ) {
return "read_committed" ;
} else if ( iso == SQL_TXN_READ_UNCOMMITTED ) {
return "read_uncommitted" ;
} else if ( iso == SQL_TXN_SERIALIZABLE ) {
return "serializable" ;
} else if ( iso == SQL_TXN_REPEATABLE_READ ) {
return "repeatable_read" ;
} else {
return "unknown" ;
}
}
2015-12-23 15:07:05 -06:00
int ast_odbc_text2isolation ( const char * txt )
2009-02-19 00:26:01 +00:00
{
if ( strncasecmp ( txt , "read_" , 5 ) == 0 ) {
if ( strncasecmp ( txt + 5 , "c" , 1 ) == 0 ) {
return SQL_TXN_READ_COMMITTED ;
} else if ( strncasecmp ( txt + 5 , "u" , 1 ) == 0 ) {
return SQL_TXN_READ_UNCOMMITTED ;
} else {
return 0 ;
}
} else if ( strncasecmp ( txt , "ser" , 3 ) == 0 ) {
return SQL_TXN_SERIALIZABLE ;
} else if ( strncasecmp ( txt , "rep" , 3 ) == 0 ) {
return SQL_TXN_REPEATABLE_READ ;
} else {
return 0 ;
}
}
2008-05-05 23:38:15 +00:00
static void odbc_class_destructor ( void * data )
{
struct odbc_class * class = data ;
2016-06-02 14:04:45 -03:00
struct odbc_obj * obj ;
2008-05-05 23:38:15 +00:00
/* Due to refcounts, we can safely assume that any objects with a reference
* to us will prevent our destruction, so we don't need to worry about them.
*/
2009-02-19 00:26:01 +00:00
if ( class -> username ) {
2008-05-05 23:38:15 +00:00
ast_free ( class -> username );
2009-02-19 00:26:01 +00:00
}
if ( class -> password ) {
2008-05-05 23:38:15 +00:00
ast_free ( class -> password );
2009-02-19 00:26:01 +00:00
}
if ( class -> sanitysql ) {
2008-05-05 23:38:15 +00:00
ast_free ( class -> sanitysql );
2009-02-19 00:26:01 +00:00
}
2016-06-02 14:04:45 -03:00
while (( obj = AST_LIST_REMOVE_HEAD ( & class -> connections , list ))) {
ao2_ref ( obj , - 1 );
}
2008-05-05 23:38:15 +00:00
SQLFreeHandle ( SQL_HANDLE_ENV , class -> env );
2016-06-02 14:04:45 -03:00
ast_mutex_destroy ( & class -> lock );
ast_cond_destroy ( & class -> cond );
2019-02-06 12:16:01 +00:00
ast_free ( class -> sql_text );
2008-05-05 23:38:15 +00:00
}
static void odbc_obj_destructor ( void * data )
{
struct odbc_obj * obj = data ;
2016-06-02 14:04:45 -03:00
2008-05-05 23:38:15 +00:00
odbc_obj_disconnect ( obj );
}
2004-06-11 00:12:35 +00:00
2016-06-02 14:04:45 -03:00
static void destroy_table_cache ( struct odbc_cache_tables * table )
{
2008-06-10 21:14:58 +00:00
struct odbc_cache_columns * col ;
2016-06-02 14:04:45 -03:00
2008-06-10 21:14:58 +00:00
ast_debug ( 1 , "Destroying table cache for %s \n " , table -> table );
2016-06-02 14:04:45 -03:00
2008-06-10 21:14:58 +00:00
AST_RWLIST_WRLOCK ( & table -> columns );
while (( col = AST_RWLIST_REMOVE_HEAD ( & table -> columns , list ))) {
ast_free ( col );
}
AST_RWLIST_UNLOCK ( & table -> columns );
AST_RWLIST_HEAD_DESTROY ( & table -> columns );
2016-06-02 14:04:45 -03:00
2008-06-10 21:14:58 +00:00
ast_free ( table );
}
/*!
* \brief Find or create an entry describing the table specified.
2009-04-01 20:13:28 +00:00
* \param database Name of an ODBC class on which to query the table
* \param tablename Tablename to describe
2008-06-10 21:14:58 +00:00
* \retval A structure describing the table layout, or NULL, if the table is not found or another error occurs.
* When a structure is returned, the contained columns list will be
* rdlock'ed, to ensure that it will be retained in memory.
2015-12-23 15:07:05 -06:00
*
* XXX This creates a connection and disconnects it. In some situations, the caller of
* this function has its own connection and could donate it to this function instead of
* needing to create another one.
*
* XXX The automatic readlock of the columns is awkward. It's done because it's possible for
* multiple threads to have references to the table, and the table is not refcounted. Possible
* changes here would be
* * Eliminate the table cache entirely. The use of ast_odbc_find_table() is generally
* questionable. The only real good use right now is from ast_realtime_require_field() in
* order to make sure the DB has the expected columns in it. Since that is only used sparingly,
* the need to cache tables is questionable. Instead, the table structure can be fetched from
* the DB directly each time, resulting in a single owner of the data.
* * Make odbc_cache_tables a refcounted object.
*
2009-04-01 20:13:28 +00:00
* \since 1.6.1
2008-06-10 21:14:58 +00:00
*/
struct odbc_cache_tables * ast_odbc_find_table ( const char * database , const char * tablename )
{
struct odbc_cache_tables * tableptr ;
struct odbc_cache_columns * entry ;
char columnname [ 80 ];
SQLLEN sqlptr ;
SQLHSTMT stmt = NULL ;
2015-12-23 15:07:05 -06:00
int res = 0 , error = 0 ;
2015-02-09 02:35:31 +00:00
struct odbc_obj * obj ;
2008-06-10 21:14:58 +00:00
AST_RWLIST_RDLOCK ( & odbc_tables );
AST_RWLIST_TRAVERSE ( & odbc_tables , tableptr , list ) {
if ( strcmp ( tableptr -> connection , database ) == 0 && strcmp ( tableptr -> table , tablename ) == 0 ) {
break ;
}
}
if ( tableptr ) {
AST_RWLIST_RDLOCK ( & tableptr -> columns );
AST_RWLIST_UNLOCK ( & odbc_tables );
return tableptr ;
}
2015-02-09 02:35:31 +00:00
if ( ! ( obj = ast_odbc_request_obj ( database , 0 ))) {
2008-06-10 21:14:58 +00:00
ast_log ( LOG_WARNING , "Unable to retrieve database handle for table description '%s@%s' \n " , tablename , database );
2009-02-19 00:26:01 +00:00
AST_RWLIST_UNLOCK ( & odbc_tables );
2008-06-10 21:14:58 +00:00
return NULL ;
}
/* Table structure not already cached; build it now. */
do {
res = SQLAllocHandle ( SQL_HANDLE_STMT , obj -> con , & stmt );
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
ast_log ( LOG_WARNING , "SQL Alloc Handle failed on connection '%s'! \n " , database );
break ;
}
res = SQLColumns ( stmt , NULL , 0 , NULL , 0 , ( unsigned char * ) tablename , SQL_NTS , ( unsigned char * ) "%" , SQL_NTS );
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
2015-12-23 15:07:05 -06:00
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
2008-06-10 21:14:58 +00:00
ast_log ( LOG_ERROR , "Unable to query database columns on connection '%s'. \n " , database );
break ;
}
if ( ! ( tableptr = ast_calloc ( sizeof ( char ), sizeof ( * tableptr ) + strlen ( database ) + 1 + strlen ( tablename ) + 1 ))) {
ast_log ( LOG_ERROR , "Out of memory creating entry for table '%s' on connection '%s' \n " , tablename , database );
break ;
}
tableptr -> connection = ( char * ) tableptr + sizeof ( * tableptr );
tableptr -> table = ( char * ) tableptr + sizeof ( * tableptr ) + strlen ( database ) + 1 ;
strcpy ( tableptr -> connection , database ); /* SAFE */
strcpy ( tableptr -> table , tablename ); /* SAFE */
AST_RWLIST_HEAD_INIT ( & ( tableptr -> columns ));
while (( res = SQLFetch ( stmt )) != SQL_NO_DATA && res != SQL_ERROR ) {
SQLGetData ( stmt , 4 , SQL_C_CHAR , columnname , sizeof ( columnname ), & sqlptr );
if ( ! ( entry = ast_calloc ( sizeof ( char ), sizeof ( * entry ) + strlen ( columnname ) + 1 ))) {
ast_log ( LOG_ERROR , "Out of memory creating entry for column '%s' in table '%s' on connection '%s' \n " , columnname , tablename , database );
error = 1 ;
break ;
}
entry -> name = ( char * ) entry + sizeof ( * entry );
strcpy ( entry -> name , columnname );
SQLGetData ( stmt , 5 , SQL_C_SHORT , & entry -> type , sizeof ( entry -> type ), NULL );
SQLGetData ( stmt , 7 , SQL_C_LONG , & entry -> size , sizeof ( entry -> size ), NULL );
SQLGetData ( stmt , 9 , SQL_C_SHORT , & entry -> decimals , sizeof ( entry -> decimals ), NULL );
SQLGetData ( stmt , 10 , SQL_C_SHORT , & entry -> radix , sizeof ( entry -> radix ), NULL );
SQLGetData ( stmt , 11 , SQL_C_SHORT , & entry -> nullable , sizeof ( entry -> nullable ), NULL );
SQLGetData ( stmt , 16 , SQL_C_LONG , & entry -> octetlen , sizeof ( entry -> octetlen ), NULL );
/* Specification states that the octenlen should be the maximum number of bytes
* returned in a char or binary column, but it seems that some drivers just set
* it to NULL. (Bad Postgres! No biscuit!) */
if ( entry -> octetlen == 0 ) {
entry -> octetlen = entry -> size ;
}
2014-05-28 22:54:12 +00:00
ast_debug ( 3 , "Found %s column with type %hd with len %ld, octetlen %ld, and numlen (%hd,%hd) \n " , entry -> name , entry -> type , ( long ) entry -> size , ( long ) entry -> octetlen , entry -> decimals , entry -> radix );
2008-06-10 21:14:58 +00:00
/* Insert column info into column list */
AST_LIST_INSERT_TAIL ( & ( tableptr -> columns ), entry , list );
}
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
AST_RWLIST_INSERT_TAIL ( & odbc_tables , tableptr , list );
AST_RWLIST_RDLOCK ( & ( tableptr -> columns ));
2009-02-19 00:26:01 +00:00
break ;
} while ( 1 );
2008-06-10 21:14:58 +00:00
AST_RWLIST_UNLOCK ( & odbc_tables );
if ( error ) {
destroy_table_cache ( tableptr );
tableptr = NULL ;
}
2015-02-09 02:35:31 +00:00
ast_odbc_release_obj ( obj );
2008-06-10 21:14:58 +00:00
return tableptr ;
}
struct odbc_cache_columns * ast_odbc_find_column ( struct odbc_cache_tables * table , const char * colname )
{
struct odbc_cache_columns * col ;
AST_RWLIST_TRAVERSE ( & table -> columns , col , list ) {
if ( strcasecmp ( col -> name , colname ) == 0 ) {
return col ;
}
}
return NULL ;
}
int ast_odbc_clear_cache ( const char * database , const char * tablename )
{
struct odbc_cache_tables * tableptr ;
AST_RWLIST_WRLOCK ( & odbc_tables );
AST_RWLIST_TRAVERSE_SAFE_BEGIN ( & odbc_tables , tableptr , list ) {
if ( strcmp ( tableptr -> connection , database ) == 0 && strcmp ( tableptr -> table , tablename ) == 0 ) {
AST_LIST_REMOVE_CURRENT ( list );
destroy_table_cache ( tableptr );
break ;
}
}
AST_RWLIST_TRAVERSE_SAFE_END
AST_RWLIST_UNLOCK ( & odbc_tables );
return tableptr ? 0 : - 1 ;
}
2007-09-14 17:29:23 +00:00
SQLHSTMT ast_odbc_direct_execute ( struct odbc_obj * obj , SQLHSTMT ( * exec_cb )( struct odbc_obj * obj , void * data ), void * data )
{
2019-02-06 12:16:01 +00:00
struct timeval start ;
2007-09-14 17:29:23 +00:00
SQLHSTMT stmt ;
2019-02-06 12:16:01 +00:00
if ( obj -> parent -> logging ) {
start = ast_tvnow ();
}
2015-12-23 15:07:05 -06:00
stmt = exec_cb ( obj , data );
2012-02-27 15:35:10 +00:00
2019-02-06 12:16:01 +00:00
if ( obj -> parent -> logging ) {
long execution_time = ast_tvdiff_ms ( ast_tvnow (), start );
if ( obj -> parent -> slowquerylimit && execution_time > obj -> parent -> slowquerylimit ) {
ast_log ( LOG_WARNING , "SQL query '%s' took %ld milliseconds to execute on class '%s', this may indicate a database problem \n " ,
obj -> sql_text , execution_time , obj -> parent -> name );
}
ast_mutex_lock ( & obj -> parent -> lock );
if ( execution_time > obj -> parent -> longest_query_execution_time || ! obj -> parent -> sql_text ) {
obj -> parent -> longest_query_execution_time = execution_time ;
/* Due to the callback nature of the res_odbc API it's not possible to ensure that
* the SQL text is removed from the connection in all cases, so only if it becomes the
* new longest executing query do we steal the SQL text. In other cases what will happen
* is that the SQL text will be freed if the connection is released back to the class or
* if a new query is done on the connection.
*/
ast_free ( obj -> parent -> sql_text );
obj -> parent -> sql_text = obj -> sql_text ;
obj -> sql_text = NULL ;
}
ast_mutex_unlock ( & obj -> parent -> lock );
}
2007-09-14 17:29:23 +00:00
return stmt ;
}
2006-09-20 04:57:20 +00:00
SQLHSTMT ast_odbc_prepare_and_execute ( struct odbc_obj * obj , SQLHSTMT ( * prepare_cb )( struct odbc_obj * obj , void * data ), void * data )
2005-11-09 02:01:12 +00:00
{
2019-02-06 12:16:01 +00:00
struct timeval start ;
2015-12-23 15:07:05 -06:00
int res = 0 ;
2005-11-09 02:01:12 +00:00
SQLHSTMT stmt ;
2019-02-06 12:16:01 +00:00
if ( obj -> parent -> logging ) {
start = ast_tvnow ();
}
2015-12-23 15:07:05 -06:00
/* This prepare callback may do more than just prepare -- it may also
* bind parameters, bind results, etc. The real key, here, is that
* when we disconnect, all handles become invalid for most databases.
* We must therefore redo everything when we establish a new
* connection. */
stmt = prepare_cb ( obj , data );
2016-06-02 14:04:45 -03:00
if ( ! stmt ) {
return NULL ;
}
2005-11-09 02:01:12 +00:00
2016-06-02 14:04:45 -03:00
res = SQLExecute ( stmt );
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO ) && ( res != SQL_NO_DATA )) {
if ( res == SQL_ERROR ) {
ast_odbc_print_errors ( SQL_HANDLE_STMT , stmt , "SQL Execute" );
2009-02-19 00:26:01 +00:00
}
2016-06-02 14:04:45 -03:00
ast_log ( LOG_WARNING , "SQL Execute error %d! \n " , res );
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
stmt = NULL ;
2019-02-06 12:16:01 +00:00
} else if ( obj -> parent -> logging ) {
long execution_time = ast_tvdiff_ms ( ast_tvnow (), start );
if ( obj -> parent -> slowquerylimit && execution_time > obj -> parent -> slowquerylimit ) {
ast_log ( LOG_WARNING , "SQL query '%s' took %ld milliseconds to execute on class '%s', this may indicate a database problem \n " ,
obj -> sql_text , execution_time , obj -> parent -> name );
}
ast_mutex_lock ( & obj -> parent -> lock );
/* If this takes the record on longest query execution time, update the parent class
* with the information.
*/
if ( execution_time > obj -> parent -> longest_query_execution_time || ! obj -> parent -> sql_text ) {
obj -> parent -> longest_query_execution_time = execution_time ;
ast_free ( obj -> parent -> sql_text );
obj -> parent -> sql_text = obj -> sql_text ;
obj -> sql_text = NULL ;
}
ast_mutex_unlock ( & obj -> parent -> lock );
ast_atomic_fetchadd_int ( & obj -> parent -> queries_executed , + 1 );
2005-11-09 02:01:12 +00:00
}
return stmt ;
}
2019-02-06 12:16:01 +00:00
int ast_odbc_prepare ( struct odbc_obj * obj , SQLHSTMT * stmt , const char * sql )
{
if ( obj -> parent -> logging ) {
/* It is possible for this connection to be reused without being
* released back to the class, so we free what may already exist
* and place the new SQL in.
*/
ast_free ( obj -> sql_text );
obj -> sql_text = ast_strdup ( sql );
ast_atomic_fetchadd_int ( & obj -> parent -> prepares_executed , + 1 );
}
return SQLPrepare ( stmt , ( unsigned char * ) sql , SQL_NTS );
}
SQLRETURN ast_odbc_execute_sql ( struct odbc_obj * obj , SQLHSTMT * stmt , const char * sql )
{
if ( obj -> parent -> logging ) {
ast_free ( obj -> sql_text );
obj -> sql_text = ast_strdup ( sql );
ast_atomic_fetchadd_int ( & obj -> parent -> queries_executed , + 1 );
}
return SQLExecDirect ( stmt , ( unsigned char * ) sql , SQL_NTS );
}
2010-07-23 16:19:21 +00:00
int ast_odbc_smart_execute ( struct odbc_obj * obj , SQLHSTMT stmt )
2005-02-17 16:31:08 +00:00
{
2015-12-23 15:07:05 -06:00
int res = 0 ;
2012-02-27 15:35:10 +00:00
2005-02-17 16:31:08 +00:00
res = SQLExecute ( stmt );
2005-10-31 21:31:25 +00:00
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO ) && ( res != SQL_NO_DATA )) {
2005-11-09 02:01:12 +00:00
if ( res == SQL_ERROR ) {
2015-12-23 15:07:05 -06:00
ast_odbc_print_errors ( SQL_HANDLE_STMT , stmt , "SQL Execute" );
2005-11-09 02:01:12 +00:00
}
2010-07-23 16:19:21 +00:00
}
2019-02-06 12:16:01 +00:00
if ( obj -> parent -> logging ) {
ast_atomic_fetchadd_int ( & obj -> parent -> queries_executed , + 1 );
}
2005-02-17 16:31:08 +00:00
return res ;
}
2009-01-19 21:42:46 +00:00
SQLRETURN ast_odbc_ast_str_SQLGetData ( struct ast_str ** buf , int pmaxlen , SQLHSTMT StatementHandle , SQLUSMALLINT ColumnNumber , SQLSMALLINT TargetType , SQLLEN * StrLen_or_Ind )
{
SQLRETURN res ;
if ( pmaxlen == 0 ) {
if ( SQLGetData ( StatementHandle , ColumnNumber , TargetType , ast_str_buffer ( * buf ), 0 , StrLen_or_Ind ) == SQL_SUCCESS_WITH_INFO ) {
ast_str_make_space ( buf , * StrLen_or_Ind + 1 );
}
} else if ( pmaxlen > 0 ) {
ast_str_make_space ( buf , pmaxlen );
}
res = SQLGetData ( StatementHandle , ColumnNumber , TargetType , ast_str_buffer ( * buf ), ast_str_size ( * buf ), StrLen_or_Ind );
ast_str_update ( * buf );
return res ;
}
2005-02-17 16:31:08 +00:00
2015-12-23 15:07:05 -06:00
struct ast_str * ast_odbc_print_errors ( SQLSMALLINT handle_type , SQLHANDLE handle , const char * operation )
{
struct ast_str * errors = ast_str_thread_get ( & errors_buf , 16 );
SQLINTEGER nativeerror = 0 ;
SQLSMALLINT diagbytes = 0 ;
SQLSMALLINT i ;
unsigned char state [ 10 ];
unsigned char diagnostic [ 256 ];
ast_str_reset ( errors );
2018-09-20 14:59:54 -04:00
i = 0 ;
while ( SQLGetDiagRec ( handle_type , handle , ++ i , state , & nativeerror ,
diagnostic , sizeof ( diagnostic ), & diagbytes ) == SQL_SUCCESS ) {
2015-12-23 15:07:05 -06:00
ast_str_append ( & errors , 0 , "%s%s" , ast_str_strlen ( errors ) ? "," : "" , state );
ast_log ( LOG_WARNING , "%s returned an error: %s: %s \n " , operation , state , diagnostic );
/* XXX Why is this here? */
if ( i > 10 ) {
2018-09-20 14:59:54 -04:00
ast_log ( LOG_WARNING , "There are more than 10 diagnostic records! Ignore the rest. \n " );
2015-12-23 15:07:05 -06:00
break ;
2005-02-17 16:31:08 +00:00
}
}
2015-12-23 15:07:05 -06:00
return errors ;
}
unsigned int ast_odbc_class_get_isolation ( struct odbc_class * class )
{
return class -> isolation ;
}
unsigned int ast_odbc_class_get_forcecommit ( struct odbc_class * class )
{
return class -> forcecommit ;
}
const char * ast_odbc_class_get_name ( struct odbc_class * class )
{
return class -> name ;
2005-02-17 16:31:08 +00:00
}
2004-07-08 19:58:26 +00:00
static int load_odbc_config ( void )
{
static char * cfg = "res_odbc.conf" ;
struct ast_config * config ;
struct ast_variable * v ;
2007-11-14 15:13:22 +00:00
char * cat ;
const char * dsn , * username , * password , * sanitysql ;
2019-02-06 12:16:01 +00:00
int enabled , bse , conntimeout , forcecommit , isolation , maxconnections , logging , slowquerylimit ;
2010-07-23 16:19:21 +00:00
struct timeval ncache = { 0 , 0 };
2008-08-10 00:47:56 +00:00
int preconnect = 0 , res = 0 ;
2007-08-16 21:09:46 +00:00
struct ast_flags config_flags = { 0 };
2004-07-08 19:58:26 +00:00
2006-04-18 18:16:32 +00:00
struct odbc_class * new ;
2004-07-08 19:58:26 +00:00
2007-08-16 21:09:46 +00:00
config = ast_config_load ( cfg , config_flags );
2008-09-12 23:30:03 +00:00
if ( config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID ) {
2006-08-31 21:00:20 +00:00
ast_log ( LOG_WARNING , "Unable to load config file res_odbc.conf \n " );
return - 1 ;
}
for ( cat = ast_category_browse ( config , NULL ); cat ; cat = ast_category_browse ( config , cat )) {
if ( ! strcasecmp ( cat , "ENV" )) {
for ( v = ast_variable_browse ( config , cat ); v ; v = v -> next ) {
setenv ( v -> name , v -> value , 1 );
ast_log ( LOG_NOTICE , "Adding ENV var: %s=%s \n " , v -> name , v -> value );
}
} else {
/* Reset all to defaults for each class of odbc connections */
2006-11-13 05:58:14 +00:00
dsn = username = password = sanitysql = NULL ;
2006-08-31 21:00:20 +00:00
enabled = 1 ;
2015-12-23 15:07:05 -06:00
preconnect = 0 ;
2007-11-25 17:50:07 +00:00
bse = 1 ;
2010-08-25 16:14:11 +00:00
conntimeout = 10 ;
2009-02-19 00:26:01 +00:00
forcecommit = 0 ;
isolation = SQL_TXN_READ_COMMITTED ;
2016-06-02 14:04:45 -03:00
maxconnections = 1 ;
2019-02-06 12:16:01 +00:00
logging = 0 ;
slowquerylimit = 5000 ;
2006-08-31 21:00:20 +00:00
for ( v = ast_variable_browse ( config , cat ); v ; v = v -> next ) {
2015-12-23 15:07:05 -06:00
if ( ! strcasecmp ( v -> name , "pooling" ) ||
! strncasecmp ( v -> name , "share" , 5 ) ||
! strcasecmp ( v -> name , "limit" ) ||
! strcasecmp ( v -> name , "idlecheck" )) {
2016-09-21 15:48:47 +00:00
ast_log ( LOG_WARNING , "The 'pooling', 'shared_connections', 'limit', and 'idlecheck' options were replaced by 'max_connections'. See res_odbc.conf.sample. \n " );
2006-08-31 21:00:20 +00:00
} else if ( ! strcasecmp ( v -> name , "enabled" )) {
enabled = ast_true ( v -> value );
} else if ( ! strcasecmp ( v -> name , "pre-connect" )) {
2008-08-10 00:47:56 +00:00
preconnect = ast_true ( v -> value );
2006-08-31 21:00:20 +00:00
} else if ( ! strcasecmp ( v -> name , "dsn" )) {
dsn = v -> value ;
} else if ( ! strcasecmp ( v -> name , "username" )) {
username = v -> value ;
} else if ( ! strcasecmp ( v -> name , "password" )) {
password = v -> value ;
2006-11-13 05:58:14 +00:00
} else if ( ! strcasecmp ( v -> name , "sanitysql" )) {
sanitysql = v -> value ;
2007-11-25 17:50:07 +00:00
} else if ( ! strcasecmp ( v -> name , "backslash_is_escape" )) {
bse = ast_true ( v -> value );
2010-07-23 16:19:21 +00:00
} else if ( ! strcasecmp ( v -> name , "connect_timeout" )) {
if ( sscanf ( v -> value , "%d" , & conntimeout ) != 1 || conntimeout < 1 ) {
ast_log ( LOG_WARNING , "connect_timeout must be a positive integer \n " );
conntimeout = 10 ;
}
} else if ( ! strcasecmp ( v -> name , "negative_connection_cache" )) {
double dncache ;
if ( sscanf ( v -> value , "%lf" , & dncache ) != 1 || dncache < 0 ) {
ast_log ( LOG_WARNING , "negative_connection_cache must be a non-negative integer \n " );
/* 5 minutes sounds like a reasonable default */
ncache . tv_sec = 300 ;
ncache . tv_usec = 0 ;
} else {
ncache . tv_sec = ( int ) dncache ;
ncache . tv_usec = ( dncache - ncache . tv_sec ) * 1000000 ;
}
2009-02-19 00:26:01 +00:00
} else if ( ! strcasecmp ( v -> name , "forcecommit" )) {
forcecommit = ast_true ( v -> value );
} else if ( ! strcasecmp ( v -> name , "isolation" )) {
2015-12-23 15:07:05 -06:00
if (( isolation = ast_odbc_text2isolation ( v -> value )) == 0 ) {
2009-02-19 00:26:01 +00:00
ast_log ( LOG_ERROR , "Unrecognized value for 'isolation': '%s' in section '%s' \n " , v -> value , cat );
isolation = SQL_TXN_READ_COMMITTED ;
}
2016-06-02 14:04:45 -03:00
} else if ( ! strcasecmp ( v -> name , "max_connections" )) {
if ( sscanf ( v -> value , "%30d" , & maxconnections ) != 1 || maxconnections < 1 ) {
ast_log ( LOG_WARNING , "max_connections must be a positive integer \n " );
maxconnections = 1 ;
}
2019-02-06 12:16:01 +00:00
} else if ( ! strcasecmp ( v -> name , "logging" )) {
logging = ast_true ( v -> value );
} else if ( ! strcasecmp ( v -> name , "slow_query_limit" )) {
if ( sscanf ( v -> value , "%30d" , & slowquerylimit ) != 1 ) {
ast_log ( LOG_WARNING , "slow_query_limit must be a positive integer \n " );
slowquerylimit = 5000 ;
}
2004-07-08 19:58:26 +00:00
}
2006-08-31 21:00:20 +00:00
}
2004-07-08 19:58:26 +00:00
2006-08-31 21:00:20 +00:00
if ( enabled && ! ast_strlen_zero ( dsn )) {
2008-05-05 23:38:15 +00:00
new = ao2_alloc ( sizeof ( * new ), odbc_class_destructor );
2004-07-08 19:58:26 +00:00
2006-08-31 21:00:20 +00:00
if ( ! new ) {
res = - 1 ;
break ;
}
2004-07-08 19:58:26 +00:00
2006-08-31 21:00:20 +00:00
SQLAllocHandle ( SQL_HANDLE_ENV , SQL_NULL_HANDLE , & new -> env );
res = SQLSetEnvAttr ( new -> env , SQL_ATTR_ODBC_VERSION , ( void * ) SQL_OV_ODBC3 , 0 );
2006-04-18 18:16:32 +00:00
2006-08-31 21:00:20 +00:00
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
ast_log ( LOG_WARNING , "res_odbc: Error SetEnv \n " );
2008-05-05 23:38:15 +00:00
ao2_ref ( new , - 1 );
2006-08-31 21:00:20 +00:00
return res ;
}
2006-04-18 18:16:32 +00:00
2007-11-25 17:50:07 +00:00
new -> backslash_is_escape = bse ? 1 : 0 ;
2009-02-19 00:26:01 +00:00
new -> forcecommit = forcecommit ? 1 : 0 ;
new -> isolation = isolation ;
2010-07-23 16:19:21 +00:00
new -> conntimeout = conntimeout ;
new -> negative_connection_cache = ncache ;
2016-06-02 14:04:45 -03:00
new -> maxconnections = maxconnections ;
2019-02-06 12:16:01 +00:00
new -> logging = logging ;
new -> slowquerylimit = slowquerylimit ;
2007-11-25 17:50:07 +00:00
2008-05-05 23:38:15 +00:00
if ( cat )
ast_copy_string ( new -> name , cat , sizeof ( new -> name ));
if ( dsn )
ast_copy_string ( new -> dsn , dsn , sizeof ( new -> dsn ));
if ( username && ! ( new -> username = ast_strdup ( username ))) {
ao2_ref ( new , - 1 );
break ;
}
if ( password && ! ( new -> password = ast_strdup ( password ))) {
ao2_ref ( new , - 1 );
break ;
}
if ( sanitysql && ! ( new -> sanitysql = ast_strdup ( sanitysql ))) {
ao2_ref ( new , - 1 );
break ;
}
2016-06-02 14:04:45 -03:00
ast_mutex_init ( & new -> lock );
ast_cond_init ( & new -> cond , NULL );
2008-08-10 00:47:56 +00:00
odbc_register_class ( new , preconnect );
2006-08-31 21:00:20 +00:00
ast_log ( LOG_NOTICE , "Registered ODBC class '%s' dsn->[%s] \n " , cat , dsn );
2008-05-05 23:38:15 +00:00
ao2_ref ( new , - 1 );
new = NULL ;
2004-07-08 19:58:26 +00:00
}
}
2004-06-11 00:12:35 +00:00
}
2006-08-31 21:00:20 +00:00
ast_config_destroy ( config );
2006-04-18 18:16:32 +00:00
return res ;
2005-01-07 06:36:02 +00:00
}
2007-09-18 22:43:45 +00:00
static char * handle_cli_odbc_show ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
2004-07-08 19:58:26 +00:00
{
2012-01-27 18:47:16 +00:00
struct ao2_iterator aoi ;
2006-04-18 18:16:32 +00:00
struct odbc_class * class ;
2007-09-18 22:43:45 +00:00
int length = 0 ;
int which = 0 ;
char * ret = NULL ;
switch ( cmd ) {
case CLI_INIT :
e -> command = "odbc show" ;
e -> usage =
"Usage: odbc show [class] \n "
" List settings of a particular ODBC class or, \n "
" if not specified, all classes. \n " ;
return NULL ;
case CLI_GENERATE :
if ( a -> pos != 2 )
return NULL ;
length = strlen ( a -> word );
2012-01-27 18:47:16 +00:00
aoi = ao2_iterator_init ( class_container , 0 );
2008-05-05 23:38:15 +00:00
while (( class = ao2_iterator_next ( & aoi ))) {
2007-09-18 22:43:45 +00:00
if ( ! strncasecmp ( a -> word , class -> name , length ) && ++ which > a -> n ) {
ret = ast_strdup ( class -> name );
}
2008-05-05 23:38:15 +00:00
ao2_ref ( class , - 1 );
2009-02-17 18:49:20 +00:00
if ( ret ) {
break ;
}
2007-09-18 22:43:45 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & aoi );
2007-09-18 22:43:45 +00:00
if ( ! ret && ! strncasecmp ( a -> word , "all" , length ) && ++ which > a -> n ) {
ret = ast_strdup ( "all" );
}
return ret ;
}
2004-07-08 19:58:26 +00:00
2007-09-18 22:43:45 +00:00
ast_cli ( a -> fd , " \n ODBC DSN Settings \n " );
2008-05-05 23:38:15 +00:00
ast_cli ( a -> fd , "----------------- \n\n " );
aoi = ao2_iterator_init ( class_container , 0 );
while (( class = ao2_iterator_next ( & aoi ))) {
2007-09-18 22:43:45 +00:00
if (( a -> argc == 2 ) || ( a -> argc == 3 && ! strcmp ( a -> argv [ 2 ], "all" )) || ( ! strcmp ( a -> argv [ 2 ], class -> name ))) {
2010-07-23 16:19:21 +00:00
char timestr [ 80 ];
struct ast_tm tm ;
2007-09-18 22:43:45 +00:00
ast_cli ( a -> fd , " Name: %s \n DSN: %s \n " , class -> name , class -> dsn );
2016-08-06 02:37:35 -04:00
if ( class -> last_negative_connect . tv_sec > 0 ) {
ast_localtime ( & class -> last_negative_connect , & tm , NULL );
ast_strftime ( timestr , sizeof ( timestr ), "%Y-%m-%d %T" , & tm );
ast_cli ( a -> fd , " Last fail connection attempt: %s \n " , timestr );
}
2016-06-02 14:04:45 -03:00
ast_cli ( a -> fd , " Number of active connections: %zd (out of %d) \n " , class -> connection_cnt , class -> maxconnections );
2019-02-06 12:16:01 +00:00
ast_cli ( a -> fd , " Logging: %s \n " , class -> logging ? "Enabled" : "Disabled" );
if ( class -> logging ) {
ast_cli ( a -> fd , " Number of prepares executed: %d \n " , class -> prepares_executed );
ast_cli ( a -> fd , " Number of queries executed: %d \n " , class -> queries_executed );
ast_mutex_lock ( & class -> lock );
if ( class -> sql_text ) {
ast_cli ( a -> fd , " Longest running SQL query: %s (%ld milliseconds) \n " , class -> sql_text , class -> longest_query_execution_time );
}
ast_mutex_unlock ( & class -> lock );
}
2007-09-18 22:43:45 +00:00
ast_cli ( a -> fd , " \n " );
2005-01-07 06:36:02 +00:00
}
2008-05-05 23:38:15 +00:00
ao2_ref ( class , - 1 );
2004-07-08 19:58:26 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & aoi );
2006-09-18 19:54:18 +00:00
2007-09-18 22:43:45 +00:00
return CLI_SUCCESS ;
2004-06-11 00:12:35 +00:00
}
2006-09-18 19:54:18 +00:00
static struct ast_cli_entry cli_odbc [] = {
2007-10-22 20:05:18 +00:00
AST_CLI_DEFINE ( handle_cli_odbc_show , "List ODBC DSN(s)" )
2006-09-18 19:54:18 +00:00
};
2004-06-11 00:12:35 +00:00
2016-06-02 14:04:45 -03:00
static void odbc_register_class ( struct odbc_class * class , int preconnect )
2004-07-08 19:58:26 +00:00
{
2006-04-18 18:16:32 +00:00
struct odbc_obj * obj ;
2004-06-11 00:12:35 +00:00
2016-06-02 14:04:45 -03:00
ao2_link ( class_container , class );
/* I still have a reference in the caller, so a deref is NOT missing here. */
if ( ! preconnect ) {
return ;
2005-02-17 16:31:08 +00:00
}
2016-06-02 14:04:45 -03:00
/* Request and release builds a connection */
obj = ast_odbc_request_obj ( class -> name , 0 );
if ( obj ) {
ast_odbc_release_obj ( obj );
}
return ;
2004-06-11 00:12:35 +00:00
}
2015-12-23 15:07:05 -06:00
void ast_odbc_release_obj ( struct odbc_obj * obj )
2004-07-08 19:58:26 +00:00
{
2016-06-02 14:04:45 -03:00
struct odbc_class * class = obj -> parent ;
ast_debug ( 2 , "Releasing ODBC handle %p into pool \n " , obj );
/* The odbc_obj only holds a reference to the class when it is
* actively being used. This guarantees no circular reference
* between odbc_class and odbc_obj. Since it is being released
* we also release our class reference. If a reload occurred before
* the class will go away automatically once all odbc_obj are
* released back.
*/
obj -> parent = NULL ;
2019-02-06 12:16:01 +00:00
/* Free the SQL text so that the next user of this connection has
* a fresh start.
*/
ast_free ( obj -> sql_text );
obj -> sql_text = NULL ;
2016-06-02 14:04:45 -03:00
ast_mutex_lock ( & class -> lock );
AST_LIST_INSERT_HEAD ( & class -> connections , obj , list );
ast_cond_signal ( & class -> cond );
ast_mutex_unlock ( & class -> lock );
ao2_ref ( class , - 1 );
2006-04-18 18:16:32 +00:00
}
2004-07-08 19:58:26 +00:00
2007-11-25 17:50:07 +00:00
int ast_odbc_backslash_is_escape ( struct odbc_obj * obj )
{
return obj -> parent -> backslash_is_escape ;
}
2009-02-19 00:26:01 +00:00
static int aoro2_class_cb ( void * obj , void * arg , int flags )
{
struct odbc_class * class = obj ;
char * name = arg ;
if ( ! strcmp ( class -> name , name ) && ! class -> delme ) {
return CMP_MATCH | CMP_STOP ;
}
return 0 ;
}
2016-07-10 21:08:28 -03:00
unsigned int ast_odbc_get_max_connections ( const char * name )
{
struct odbc_class * class ;
unsigned int max_connections ;
class = ao2_callback ( class_container , 0 , aoro2_class_cb , ( char * ) name );
if ( ! class ) {
return 0 ;
}
max_connections = class -> maxconnections ;
ao2_ref ( class , - 1 );
return max_connections ;
}
2016-06-02 14:04:45 -03:00
/*
* \brief Determine if the connection has died.
*
* \param connection The connection to check
* \param class The ODBC class
* \retval 1 Yep, it's dead
* \retval 0 It's alive and well
*/
static int connection_dead ( struct odbc_obj * connection , struct odbc_class * class )
{
char * test_sql = "select 1" ;
SQLINTEGER dead ;
SQLRETURN res ;
SQLHSTMT stmt ;
res = SQLGetConnectAttr ( connection -> con , SQL_ATTR_CONNECTION_DEAD , & dead , 0 , 0 );
if ( SQL_SUCCEEDED ( res )) {
return dead == SQL_CD_TRUE ? 1 : 0 ;
}
/* If the Driver doesn't support SQL_ATTR_CONNECTION_DEAD do a
* probing query instead
*/
res = SQLAllocHandle ( SQL_HANDLE_STMT , connection -> con , & stmt );
if ( ! SQL_SUCCEEDED ( res )) {
return 1 ;
}
if ( ! ast_strlen_zero ( class -> sanitysql )) {
test_sql = class -> sanitysql ;
}
res = SQLPrepare ( stmt , ( unsigned char * ) test_sql , SQL_NTS );
if ( ! SQL_SUCCEEDED ( res )) {
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
return 1 ;
}
res = SQLExecute ( stmt );
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
return SQL_SUCCEEDED ( res ) ? 0 : 1 ;
}
2009-02-19 00:26:01 +00:00
struct odbc_obj * _ast_odbc_request_obj2 ( const char * name , struct ast_flags flags , const char * file , const char * function , int lineno )
2006-04-18 18:16:32 +00:00
{
struct odbc_obj * obj = NULL ;
struct odbc_class * class ;
2009-02-19 00:26:01 +00:00
if ( ! ( class = ao2_callback ( class_container , 0 , aoro2_class_cb , ( char * ) name ))) {
2010-08-26 13:28:05 +00:00
ast_debug ( 1 , "Class '%s' not found! \n " , name );
2006-04-18 18:16:32 +00:00
return NULL ;
2009-02-19 00:26:01 +00:00
}
2006-04-18 18:16:32 +00:00
2016-06-02 14:04:45 -03:00
ast_mutex_lock ( & class -> lock );
while ( ! obj ) {
obj = AST_LIST_REMOVE_HEAD ( & class -> connections , list );
if ( ! obj ) {
if ( class -> connection_cnt < class -> maxconnections ) {
/* If no connection is immediately available establish a new
* one if allowed. If we try and fail we give up completely as
* we could go into an infinite loop otherwise.
*/
obj = ao2_alloc ( sizeof ( * obj ), odbc_obj_destructor );
if ( ! obj ) {
break ;
}
obj -> parent = ao2_bump ( class );
if ( odbc_obj_connect ( obj ) == ODBC_FAIL ) {
ao2_ref ( obj -> parent , - 1 );
ao2_ref ( obj , - 1 );
obj = NULL ;
break ;
}
class -> connection_cnt ++ ;
ast_debug ( 2 , "Created ODBC handle %p on class '%s', new count is %zd \n " , obj ,
name , class -> connection_cnt );
} else {
/* Otherwise if we're not allowed to create a new one we
* wait for another thread to give up the connection they
* own.
*/
ast_cond_wait ( & class -> cond , & class -> lock );
}
} else if ( connection_dead ( obj , class )) {
/* If the connection is dead try to grab another functional one from the
* pool instead of trying to resurrect this one.
*/
ao2_ref ( obj , - 1 );
obj = NULL ;
class -> connection_cnt -- ;
ast_debug ( 2 , "ODBC handle %p dead - removing from class '%s', new count is %zd \n " ,
obj , name , class -> connection_cnt );
} else {
/* We successfully grabbed a connection from the pool and all is well!
*/
obj -> parent = ao2_bump ( class );
ast_debug ( 2 , "Reusing ODBC handle %p from class '%s' \n " , obj , name );
}
2010-07-23 16:19:21 +00:00
}
2008-01-21 18:15:57 +00:00
2016-06-02 14:04:45 -03:00
ast_mutex_unlock ( & class -> lock );
ao2_ref ( class , - 1 );
2006-04-18 18:16:32 +00:00
return obj ;
2004-06-11 00:12:35 +00:00
}
2009-02-19 00:26:01 +00:00
struct odbc_obj * _ast_odbc_request_obj ( const char * name , int check , const char * file , const char * function , int lineno )
{
struct ast_flags flags = { check ? RES_ODBC_SANITY_CHECK : 0 };
2015-12-23 15:07:05 -06:00
/* XXX New flow means that the "check" parameter doesn't do anything. We're requesting
* a connection from ODBC. We'll either get a new one, which obviously is already connected, or
* we'll get one from the ODBC connection pool. In that case, it will ensure to only give us a
* live connection
*/
2009-02-19 00:26:01 +00:00
return _ast_odbc_request_obj2 ( name , flags , file , function , lineno );
}
2006-04-18 18:16:32 +00:00
static odbc_status odbc_obj_disconnect ( struct odbc_obj * obj )
2004-07-08 19:58:26 +00:00
{
int res ;
2008-08-11 00:25:28 +00:00
SQLINTEGER err ;
short int mlen ;
2008-10-08 12:15:06 +00:00
unsigned char msg [ 200 ], state [ 10 ];
2012-02-27 15:35:10 +00:00
SQLHDBC con ;
2008-08-11 00:25:28 +00:00
2008-10-02 15:17:16 +00:00
/* Nothing to disconnect */
if ( ! obj -> con ) {
return ODBC_SUCCESS ;
}
2012-02-27 15:35:10 +00:00
con = obj -> con ;
obj -> con = NULL ;
res = SQLDisconnect ( con );
2005-02-17 16:31:08 +00:00
2012-04-17 18:57:40 +00:00
if (( res = SQLFreeHandle ( SQL_HANDLE_DBC , con )) == SQL_SUCCESS ) {
2015-12-23 15:07:05 -06:00
ast_debug ( 3 , "Database handle %p (connection %p) deallocated \n " , obj , con );
2008-08-11 00:25:28 +00:00
} else {
2012-02-27 15:35:10 +00:00
SQLGetDiagRec ( SQL_HANDLE_DBC , con , 1 , state , & err , msg , 100 , & mlen );
ast_log ( LOG_WARNING , "Unable to deallocate database handle %p? %d errno=%d %s \n " , con , res , ( int ) err , msg );
2008-08-11 00:25:28 +00:00
}
2004-07-08 19:58:26 +00:00
return ODBC_SUCCESS ;
2004-06-11 00:12:35 +00:00
}
2006-04-18 18:16:32 +00:00
static odbc_status odbc_obj_connect ( struct odbc_obj * obj )
2004-07-08 19:58:26 +00:00
{
int res ;
2005-09-02 19:29:18 +00:00
SQLINTEGER err ;
2004-07-08 19:58:26 +00:00
short int mlen ;
2008-08-10 00:47:56 +00:00
unsigned char msg [ 200 ], state [ 10 ];
2006-04-18 18:16:32 +00:00
#ifdef NEEDTRACE
SQLINTEGER enable = 1 ;
char * tracefile = "/tmp/odbc.trace" ;
#endif
2012-02-27 15:35:10 +00:00
SQLHDBC con ;
2015-04-21 18:52:22 +02:00
long int negative_cache_expiration ;
2004-06-11 00:12:35 +00:00
2015-12-23 15:07:05 -06:00
ast_assert ( obj -> con == NULL );
ast_debug ( 3 , "Connecting %s(%p) \n " , obj -> parent -> name , obj );
2008-10-02 15:17:16 +00:00
2015-04-21 18:52:22 +02:00
/* Dont connect while server is marked as unreachable via negative_connection_cache */
negative_cache_expiration = obj -> parent -> last_negative_connect . tv_sec + obj -> parent -> negative_connection_cache . tv_sec ;
if ( time ( NULL ) < negative_cache_expiration ) {
ast_log ( LOG_WARNING , "Not connecting to %s. Negative connection cache for %ld seconds \n " , obj -> parent -> name , negative_cache_expiration - time ( NULL ));
return ODBC_FAIL ;
}
2012-02-27 15:35:10 +00:00
res = SQLAllocHandle ( SQL_HANDLE_DBC , obj -> parent -> env , & con );
2004-06-11 00:12:35 +00:00
2006-04-18 18:16:32 +00:00
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
ast_log ( LOG_WARNING , "res_odbc: Error AllocHDB %d \n " , res );
2010-07-23 16:19:21 +00:00
obj -> parent -> last_negative_connect = ast_tvnow ();
2006-04-18 18:16:32 +00:00
return ODBC_FAIL ;
2004-07-08 19:58:26 +00:00
}
2012-02-27 15:35:10 +00:00
SQLSetConnectAttr ( con , SQL_LOGIN_TIMEOUT , ( SQLPOINTER * )( long ) obj -> parent -> conntimeout , 0 );
SQLSetConnectAttr ( con , SQL_ATTR_CONNECTION_TIMEOUT , ( SQLPOINTER * )( long ) obj -> parent -> conntimeout , 0 );
2006-04-18 18:16:32 +00:00
#ifdef NEEDTRACE
2012-02-27 15:35:10 +00:00
SQLSetConnectAttr ( con , SQL_ATTR_TRACE , & enable , SQL_IS_INTEGER );
SQLSetConnectAttr ( con , SQL_ATTR_TRACEFILE , tracefile , strlen ( tracefile ));
2006-04-18 18:16:32 +00:00
#endif
2012-02-27 15:35:10 +00:00
res = SQLConnect ( con ,
2006-04-18 18:16:32 +00:00
( SQLCHAR * ) obj -> parent -> dsn , SQL_NTS ,
( SQLCHAR * ) obj -> parent -> username , SQL_NTS ,
( SQLCHAR * ) obj -> parent -> password , SQL_NTS );
2004-07-08 19:58:26 +00:00
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
2012-02-27 15:35:10 +00:00
SQLGetDiagRec ( SQL_HANDLE_DBC , con , 1 , state , & err , msg , 100 , & mlen );
2010-07-23 16:19:21 +00:00
obj -> parent -> last_negative_connect = ast_tvnow ();
2005-09-02 19:29:18 +00:00
ast_log ( LOG_WARNING , "res_odbc: Error SQLConnect=%d errno=%d %s \n " , res , ( int ) err , msg );
2012-04-17 18:57:40 +00:00
if (( res = SQLFreeHandle ( SQL_HANDLE_DBC , con )) != SQL_SUCCESS ) {
2012-02-27 15:35:10 +00:00
SQLGetDiagRec ( SQL_HANDLE_DBC , con , 1 , state , & err , msg , 100 , & mlen );
ast_log ( LOG_WARNING , "Unable to deallocate database handle %p? %d errno=%d %s \n " , con , res , ( int ) err , msg );
}
2004-07-08 19:58:26 +00:00
return ODBC_FAIL ;
} else {
2015-12-23 15:07:05 -06:00
ast_debug ( 3 , "res_odbc: Connected to %s [%s (%p)] \n " , obj -> parent -> name , obj -> parent -> dsn , obj );
2004-07-08 19:58:26 +00:00
}
2004-06-11 00:12:35 +00:00
2012-02-27 15:35:10 +00:00
obj -> con = con ;
2004-07-08 19:58:26 +00:00
return ODBC_SUCCESS ;
2004-06-11 00:12:35 +00:00
}
2006-08-21 02:11:39 +00:00
static int reload ( void )
2004-07-08 19:58:26 +00:00
{
2008-06-10 21:14:58 +00:00
struct odbc_cache_tables * table ;
2008-05-05 23:38:15 +00:00
struct odbc_class * class ;
struct ao2_iterator aoi = ao2_iterator_init ( class_container , 0 );
2006-04-18 18:16:32 +00:00
/* First, mark all to be purged */
2008-05-05 23:38:15 +00:00
while (( class = ao2_iterator_next ( & aoi ))) {
2006-04-18 18:16:32 +00:00
class -> delme = 1 ;
2008-05-05 23:38:15 +00:00
ao2_ref ( class , - 1 );
2006-04-18 18:16:32 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & aoi );
2006-04-18 18:16:32 +00:00
2008-05-05 23:38:15 +00:00
load_odbc_config ();
2006-04-18 18:16:32 +00:00
2008-05-20 16:13:48 +00:00
aoi = ao2_iterator_init ( class_container , 0 );
2015-12-23 15:07:05 -06:00
while (( class = ao2_iterator_next ( & aoi ))) {
2008-05-20 16:13:48 +00:00
if ( class -> delme ) {
2015-12-23 15:07:05 -06:00
ao2_unlink ( class_container , class );
2008-05-20 16:13:48 +00:00
}
2015-12-23 15:07:05 -06:00
ao2_ref ( class , - 1 );
2008-05-20 16:13:48 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & aoi );
2006-04-18 18:16:32 +00:00
2008-06-10 21:14:58 +00:00
/* Empty the cache; it will get rebuilt the next time the tables are needed. */
AST_RWLIST_WRLOCK ( & odbc_tables );
while (( table = AST_RWLIST_REMOVE_HEAD ( & odbc_tables , list ))) {
destroy_table_cache ( table );
}
AST_RWLIST_UNLOCK ( & odbc_tables );
2004-07-08 19:58:26 +00:00
return 0 ;
2004-06-11 00:12:35 +00:00
}
2006-08-21 02:11:39 +00:00
static int unload_module ( void )
2006-04-18 18:16:32 +00:00
{
2018-08-15 12:31:00 -04:00
ao2_cleanup ( class_container );
ast_cli_unregister_multiple ( cli_odbc , ARRAY_LEN ( cli_odbc ));
return 0 ;
2006-04-18 18:16:32 +00:00
}
2006-08-21 02:11:39 +00:00
static int load_module ( void )
2004-07-08 19:58:26 +00:00
{
2018-11-19 15:10:15 -05:00
class_container = ao2_container_alloc_list ( AO2_ALLOC_OPT_LOCK_MUTEX , 0 , NULL , ao2_match_by_addr );
if ( ! class_container ) {
2008-05-05 23:38:15 +00:00
return AST_MODULE_LOAD_DECLINE ;
2018-08-15 12:31:00 -04:00
}
if ( load_odbc_config () == - 1 ) {
2006-08-31 21:00:20 +00:00
return AST_MODULE_LOAD_DECLINE ;
2018-08-15 12:31:00 -04:00
}
ast_module_shutdown_ref ( ast_module_info -> self );
2008-12-05 10:31:25 +00:00
ast_cli_register_multiple ( cli_odbc , ARRAY_LEN ( cli_odbc ));
2018-08-15 12:31:00 -04:00
2018-01-05 14:44:55 +01:00
return AST_MODULE_LOAD_SUCCESS ;
2004-07-08 19:58:26 +00:00
}
2004-06-11 00:12:35 +00:00
2010-07-20 19:35:02 +00:00
AST_MODULE_INFO ( ASTERISK_GPL_KEY , AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER , "ODBC resource" ,
2015-05-05 20:49:04 -04:00
. support_level = AST_MODULE_SUPPORT_CORE ,
. load = load_module ,
. unload = unload_module ,
. reload = reload ,
. load_pri = AST_MODPRI_REALTIME_DEPEND ,
);