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
*
2009-02-19 00:26:01 +00:00
* Copyright ( C ) 1999 - 2008 , 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
*
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
*/
2006-04-24 17:11:45 +00:00
/*** MODULEINFO
2009-01-15 20:18:53 +00:00
< depend > generic_odbc < / depend >
2007-06-18 16:35:51 +00:00
< depend > ltdl < / depend >
2006-04-24 17:11:45 +00:00
* * */
2006-06-07 18:54:56 +00:00
# include "asterisk.h"
ASTERISK_FILE_VERSION ( __FILE__ , " $Revision$ " )
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"
2010-07-08 14:48:42 +00:00
# include "asterisk/data.h"
2009-02-19 00:26:01 +00:00
/*** DOCUMENTATION
< function name = " ODBC " language = " en_US " >
< synopsis >
Controls ODBC transaction properties .
< / synopsis >
< syntax >
< parameter name = " property " required = " true " >
< enumlist >
< enum name = " transaction " >
< para > Gets or sets the active transaction ID . If set , and the transaction ID does not
exist and a < replaceable > database name < / replaceable > is specified as an argument , it will be created . < / para >
< / enum >
< enum name = " forcecommit " >
< para > Controls whether a transaction will be automatically committed when the channel
hangs up . Defaults to false . If a < replaceable > transaction ID < / replaceable > is specified in the optional argument ,
the property will be applied to that ID , otherwise to the current active ID . < / para >
< / enum >
< enum name = " isolation " >
< para > Controls the data isolation on uncommitted transactions . May be one of the
following : < literal > read_committed < / literal > , < literal > read_uncommitted < / literal > ,
< literal > repeatable_read < / literal > , or < literal > serializable < / literal > . Defaults to the
database setting in < filename > res_odbc . conf < / filename > or < literal > read_committed < / literal >
if not specified . If a < replaceable > transaction ID < / replaceable > is specified as an optional argument , it will be
applied to that ID , otherwise the current active ID . < / para >
< / enum >
< / enumlist >
< / parameter >
< parameter name = " argument " required = " false " / >
< / syntax >
< description >
< para > The ODBC ( ) function allows setting several properties to influence how a connected
database processes transactions . < / para >
< / description >
< / function >
< application name = " ODBC_Commit " language = " en_US " >
< synopsis >
Commits a currently open database transaction .
< / synopsis >
< syntax >
< parameter name = " transaction ID " required = " no " / >
< / syntax >
< description >
< para > Commits the database transaction specified by < replaceable > transaction ID < / replaceable >
or the current active transaction , if not specified . < / para >
< / description >
< / application >
< application name = " ODBC_Rollback " language = " en_US " >
< synopsis >
Rollback a currently open database transaction .
< / synopsis >
< syntax >
< parameter name = " transaction ID " required = " no " / >
< / syntax >
< description >
< para > Rolls back the database transaction specified by < replaceable > transaction ID < / replaceable >
or the current active transaction , if not specified . < / para >
< / description >
< / application >
* * */
2004-06-11 00:12:35 +00:00
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 haspool : 1 ; /*!< Boolean - TDS databases need this */
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 */
unsigned int limit ; /*!< Maximum number of database handles we will allow */
int count ; /*!< Running count of pooled connections */
unsigned int idlecheck ; /*!< Recheck the connection if it is idle for this long (in seconds) */
2010-07-23 16:19:21 +00:00
unsigned int conntimeout ; /*!< Maximum time the connection process should take */
/*! 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 ;
/*! List of handles associated with this class */
2008-05-05 23:38:15 +00:00
struct ao2_container * obj_container ;
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 ) ;
static int odbc_register_class ( struct odbc_class * class , int connect ) ;
2009-02-19 00:26:01 +00:00
static void odbc_txn_free ( void * data ) ;
static void odbc_release_obj2 ( struct odbc_obj * obj , struct odbc_txn_frame * tx ) ;
AST_THREADSTORAGE ( errors_buf ) ;
static struct ast_datastore_info txn_info = {
. type = " ODBC_Transaction " ,
. destroy = odbc_txn_free ,
} ;
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 */
} ;
2010-07-08 14:48:42 +00:00
# define DATA_EXPORT_ODBC_CLASS(MEMBER) \
MEMBER ( odbc_class , name , AST_DATA_STRING ) \
MEMBER ( odbc_class , dsn , AST_DATA_STRING ) \
MEMBER ( odbc_class , username , AST_DATA_STRING ) \
MEMBER ( odbc_class , password , AST_DATA_PASSWORD ) \
MEMBER ( odbc_class , limit , AST_DATA_INTEGER ) \
MEMBER ( odbc_class , count , AST_DATA_INTEGER ) \
MEMBER ( odbc_class , forcecommit , AST_DATA_BOOLEAN )
AST_DATA_STRUCTURE ( odbc_class , DATA_EXPORT_ODBC_CLASS ) ;
2009-02-19 00:26:01 +00:00
static const char * isolation2text ( int iso )
{
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 " ;
}
}
static int text2isolation ( const char * txt )
{
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 ;
}
}
static struct odbc_txn_frame * find_transaction ( struct ast_channel * chan , struct odbc_obj * obj , const char * name , int active )
{
struct ast_datastore * txn_store ;
AST_LIST_HEAD ( , odbc_txn_frame ) * oldlist ;
struct odbc_txn_frame * txn = NULL ;
if ( ! chan & & obj & & obj - > txf & & obj - > txf - > owner ) {
chan = obj - > txf - > owner ;
} else if ( ! chan ) {
/* No channel == no transaction */
return NULL ;
}
ast_channel_lock ( chan ) ;
if ( ( txn_store = ast_channel_datastore_find ( chan , & txn_info , NULL ) ) ) {
oldlist = txn_store - > data ;
} else {
/* Need to create a new datastore */
if ( ! ( txn_store = ast_datastore_alloc ( & txn_info , NULL ) ) ) {
ast_log ( LOG_ERROR , " Unable to allocate a new datastore. Cannot create a new transaction. \n " ) ;
ast_channel_unlock ( chan ) ;
return NULL ;
}
if ( ! ( oldlist = ast_calloc ( 1 , sizeof ( * oldlist ) ) ) ) {
ast_log ( LOG_ERROR , " Unable to allocate datastore list head. Cannot create a new transaction. \n " ) ;
ast_datastore_free ( txn_store ) ;
ast_channel_unlock ( chan ) ;
return NULL ;
}
txn_store - > data = oldlist ;
AST_LIST_HEAD_INIT ( oldlist ) ;
ast_channel_datastore_add ( chan , txn_store ) ;
}
AST_LIST_LOCK ( oldlist ) ;
ast_channel_unlock ( chan ) ;
/* Scanning for an object is *fast*. Scanning for a name is much slower. */
if ( obj ! = NULL | | active = = 1 ) {
AST_LIST_TRAVERSE ( oldlist , txn , list ) {
if ( txn - > obj = = obj | | txn - > active ) {
AST_LIST_UNLOCK ( oldlist ) ;
return txn ;
}
}
}
if ( name ! = NULL ) {
AST_LIST_TRAVERSE ( oldlist , txn , list ) {
if ( ! strcasecmp ( txn - > name , name ) ) {
AST_LIST_UNLOCK ( oldlist ) ;
return txn ;
}
}
}
/* Nothing found, create one */
if ( name & & obj & & ( txn = ast_calloc ( 1 , sizeof ( * txn ) + strlen ( name ) + 1 ) ) ) {
struct odbc_txn_frame * otxn ;
strcpy ( txn - > name , name ) ; /* SAFE */
txn - > obj = obj ;
txn - > isolation = obj - > parent - > isolation ;
txn - > forcecommit = obj - > parent - > forcecommit ;
txn - > owner = chan ;
txn - > active = 1 ;
/* On creation, the txn becomes active, and all others inactive */
AST_LIST_TRAVERSE ( oldlist , otxn , list ) {
otxn - > active = 0 ;
}
AST_LIST_INSERT_TAIL ( oldlist , txn , list ) ;
obj - > txf = txn ;
obj - > tx = 1 ;
}
AST_LIST_UNLOCK ( oldlist ) ;
return txn ;
}
static struct odbc_txn_frame * release_transaction ( struct odbc_txn_frame * tx )
{
if ( ! tx ) {
return NULL ;
}
ast_debug ( 2 , " release_transaction(%p) called (tx->obj = %p, tx->obj->txf = %p) \n " , tx , tx - > obj , tx - > obj ? tx - > obj - > txf : NULL ) ;
/* If we have an owner, disassociate */
if ( tx - > owner ) {
struct ast_datastore * txn_store ;
AST_LIST_HEAD ( , odbc_txn_frame ) * oldlist ;
ast_channel_lock ( tx - > owner ) ;
if ( ( txn_store = ast_channel_datastore_find ( tx - > owner , & txn_info , NULL ) ) ) {
oldlist = txn_store - > data ;
AST_LIST_LOCK ( oldlist ) ;
AST_LIST_REMOVE ( oldlist , tx , list ) ;
AST_LIST_UNLOCK ( oldlist ) ;
}
ast_channel_unlock ( tx - > owner ) ;
tx - > owner = NULL ;
}
if ( tx - > obj ) {
/* If we have any uncommitted transactions, they are handled when we release the object */
struct odbc_obj * obj = tx - > obj ;
/* Prevent recursion during destruction */
tx - > obj - > txf = NULL ;
tx - > obj = NULL ;
odbc_release_obj2 ( obj , tx ) ;
}
ast_free ( tx ) ;
return NULL ;
}
static void odbc_txn_free ( void * vdata )
{
struct odbc_txn_frame * tx ;
AST_LIST_HEAD ( , odbc_txn_frame ) * oldlist = vdata ;
ast_debug ( 2 , " odbc_txn_free(%p) called \n " , vdata ) ;
AST_LIST_LOCK ( oldlist ) ;
while ( ( tx = AST_LIST_REMOVE_HEAD ( oldlist , list ) ) ) {
release_transaction ( tx ) ;
}
AST_LIST_UNLOCK ( oldlist ) ;
AST_LIST_HEAD_DESTROY ( oldlist ) ;
ast_free ( oldlist ) ;
}
static int mark_transaction_active ( struct ast_channel * chan , struct odbc_txn_frame * tx )
{
struct ast_datastore * txn_store ;
AST_LIST_HEAD ( , odbc_txn_frame ) * oldlist ;
struct odbc_txn_frame * active = NULL , * txn ;
if ( ! chan & & tx & & tx - > owner ) {
chan = tx - > owner ;
}
ast_channel_lock ( chan ) ;
if ( ! ( txn_store = ast_channel_datastore_find ( chan , & txn_info , NULL ) ) ) {
ast_channel_unlock ( chan ) ;
return - 1 ;
}
oldlist = txn_store - > data ;
AST_LIST_LOCK ( oldlist ) ;
AST_LIST_TRAVERSE ( oldlist , txn , list ) {
if ( txn = = tx ) {
txn - > active = 1 ;
active = txn ;
} else {
txn - > active = 0 ;
}
}
AST_LIST_UNLOCK ( oldlist ) ;
ast_channel_unlock ( chan ) ;
return active ? 0 : - 1 ;
}
2005-02-17 16:31:08 +00:00
2008-05-05 23:38:15 +00:00
static void odbc_class_destructor ( void * data )
{
struct odbc_class * class = data ;
/* 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
}
2008-05-05 23:38:15 +00:00
ao2_ref ( class - > obj_container , - 1 ) ;
SQLFreeHandle ( SQL_HANDLE_ENV , class - > env ) ;
}
static int null_hash_fn ( const void * obj , const int flags )
{
return 0 ;
}
static void odbc_obj_destructor ( void * data )
{
struct odbc_obj * obj = data ;
2009-02-17 18:49:20 +00:00
struct odbc_class * class = obj - > parent ;
obj - > parent = NULL ;
2008-05-05 23:38:15 +00:00
odbc_obj_disconnect ( obj ) ;
ast_mutex_destroy ( & obj - > lock ) ;
2009-02-17 18:49:20 +00:00
ao2_ref ( class , - 1 ) ;
2008-05-05 23:38:15 +00:00
}
2004-06-11 00:12:35 +00:00
2008-06-10 21:14:58 +00:00
static void destroy_table_cache ( struct odbc_cache_tables * table ) {
struct odbc_cache_columns * col ;
ast_debug ( 1 , " Destroying table cache for %s \n " , table - > table ) ;
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 ) ;
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 .
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 ;
int res = 0 , error = 0 , try = 0 ;
struct odbc_obj * obj = ast_odbc_request_obj ( database , 0 ) ;
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 ) ;
2008-07-11 19:53:38 +00:00
if ( obj ) {
ast_odbc_release_obj ( obj ) ;
}
2008-06-10 21:14:58 +00:00
return tableptr ;
}
if ( ! obj ) {
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 ) ) {
if ( try = = 0 ) {
try = 1 ;
ast_odbc_sanity_check ( obj ) ;
2009-02-19 00:26:01 +00:00
continue ;
2008-06-10 21:14:58 +00:00
}
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 ) ) {
if ( try = = 0 ) {
try = 1 ;
SQLFreeHandle ( SQL_HANDLE_STMT , stmt ) ;
ast_odbc_sanity_check ( obj ) ;
2009-02-19 00:26:01 +00:00
continue ;
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 ;
}
ast_verb ( 10 , " 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 ) ;
/* 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 ;
}
if ( obj ) {
ast_odbc_release_obj ( obj ) ;
}
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 )
{
int attempt ;
SQLHSTMT stmt ;
for ( attempt = 0 ; attempt < 2 ; attempt + + ) {
stmt = exec_cb ( obj , data ) ;
if ( stmt ) {
break ;
2009-02-19 00:26:01 +00:00
} else if ( obj - > tx ) {
ast_log ( LOG_WARNING , " Failed to execute, but unable to reconnect, as we're transactional. \n " ) ;
break ;
2007-09-14 17:29:23 +00:00
} else {
obj - > up = 0 ;
ast_log ( LOG_WARNING , " SQL Exec Direct failed. Attempting a reconnect... \n " ) ;
odbc_obj_disconnect ( obj ) ;
odbc_obj_connect ( obj ) ;
}
}
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
{
int res = 0 , i , attempt ;
SQLINTEGER nativeerror = 0 , numfields = 0 ;
SQLSMALLINT diagbytes = 0 ;
unsigned char state [ 10 ] , diagnostic [ 256 ] ;
SQLHSTMT stmt ;
for ( attempt = 0 ; attempt < 2 ; attempt + + ) {
/* 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 ) ;
if ( stmt ) {
res = SQLExecute ( stmt ) ;
if ( ( res ! = SQL_SUCCESS ) & & ( res ! = SQL_SUCCESS_WITH_INFO ) & & ( res ! = SQL_NO_DATA ) ) {
if ( res = = SQL_ERROR ) {
SQLGetDiagField ( SQL_HANDLE_STMT , stmt , 1 , SQL_DIAG_NUMBER , & numfields , SQL_IS_INTEGER , & diagbytes ) ;
2007-03-08 21:34:40 +00:00
for ( i = 0 ; i < numfields ; i + + ) {
2005-11-09 02:01:12 +00:00
SQLGetDiagRec ( SQL_HANDLE_STMT , stmt , i + 1 , state , & nativeerror , diagnostic , sizeof ( diagnostic ) , & diagbytes ) ;
ast_log ( LOG_WARNING , " SQL Execute returned an error %d: %s: %s (%d) \n " , res , state , diagnostic , diagbytes ) ;
if ( i > 10 ) {
ast_log ( LOG_WARNING , " Oh, that was good. There are really %d diagnostics? \n " , ( int ) numfields ) ;
break ;
}
}
}
2009-02-19 00:26:01 +00:00
if ( obj - > tx ) {
ast_log ( LOG_WARNING , " SQL Execute error, but unable to reconnect, as we're transactional. \n " ) ;
break ;
} else {
ast_log ( LOG_WARNING , " SQL Execute error %d! Attempting a reconnect... \n " , res ) ;
SQLFreeHandle ( SQL_HANDLE_STMT , stmt ) ;
stmt = NULL ;
2005-11-09 02:01:12 +00:00
2009-02-19 00:26:01 +00:00
obj - > up = 0 ;
/*
* While this isn ' t the best way to try to correct an error , this won ' t automatically
* fail when the statement handle invalidates .
*/
ast_odbc_sanity_check ( obj ) ;
continue ;
}
} else {
2008-01-21 18:15:57 +00:00
obj - > last_used = ast_tvnow ( ) ;
2009-02-19 00:26:01 +00:00
}
2005-11-09 02:01:12 +00:00
break ;
2009-02-19 00:26:01 +00:00
} else if ( attempt = = 0 ) {
2006-11-13 05:58:14 +00:00
ast_odbc_sanity_check ( obj ) ;
2009-02-19 00:26:01 +00:00
}
2005-11-09 02:01:12 +00:00
}
return stmt ;
}
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
{
2005-11-09 02:01:12 +00:00
int res = 0 , i ;
SQLINTEGER nativeerror = 0 , numfields = 0 ;
SQLSMALLINT diagbytes = 0 ;
unsigned char state [ 10 ] , diagnostic [ 256 ] ;
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 ) {
SQLGetDiagField ( SQL_HANDLE_STMT , stmt , 1 , SQL_DIAG_NUMBER , & numfields , SQL_IS_INTEGER , & diagbytes ) ;
2007-03-28 03:40:19 +00:00
for ( i = 0 ; i < numfields ; i + + ) {
2005-11-09 02:01:12 +00:00
SQLGetDiagRec ( SQL_HANDLE_STMT , stmt , i + 1 , state , & nativeerror , diagnostic , sizeof ( diagnostic ) , & diagbytes ) ;
ast_log ( LOG_WARNING , " SQL Execute returned an error %d: %s: %s (%d) \n " , res , state , diagnostic , diagbytes ) ;
if ( i > 10 ) {
ast_log ( LOG_WARNING , " Oh, that was good. There are really %d diagnostics? \n " , ( int ) numfields ) ;
break ;
}
}
}
2010-07-23 16:19:21 +00:00
} else {
2008-01-21 18:15:57 +00:00
obj - > last_used = ast_tvnow ( ) ;
2010-07-23 16:19:21 +00:00
}
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
2006-09-20 04:57:20 +00:00
int ast_odbc_sanity_check ( struct odbc_obj * obj )
2005-02-17 16:31:08 +00:00
{
char * test_sql = " select 1 " ;
SQLHSTMT stmt ;
int res = 0 ;
2007-08-13 21:44:22 +00:00
if ( ! ast_strlen_zero ( obj - > parent - > sanitysql ) )
2006-11-13 05:58:14 +00:00
test_sql = obj - > parent - > sanitysql ;
2006-04-18 18:16:32 +00:00
if ( obj - > up ) {
res = SQLAllocHandle ( SQL_HANDLE_STMT , obj - > con , & stmt ) ;
2005-02-17 16:31:08 +00:00
if ( ( res ! = SQL_SUCCESS ) & & ( res ! = SQL_SUCCESS_WITH_INFO ) ) {
2006-04-18 18:16:32 +00:00
obj - > up = 0 ;
2005-02-17 16:31:08 +00:00
} else {
2005-11-09 02:01:12 +00:00
res = SQLPrepare ( stmt , ( unsigned char * ) test_sql , SQL_NTS ) ;
2005-02-17 16:31:08 +00:00
if ( ( res ! = SQL_SUCCESS ) & & ( res ! = SQL_SUCCESS_WITH_INFO ) ) {
2006-04-18 18:16:32 +00:00
obj - > up = 0 ;
2005-02-17 16:31:08 +00:00
} else {
res = SQLExecute ( stmt ) ;
if ( ( res ! = SQL_SUCCESS ) & & ( res ! = SQL_SUCCESS_WITH_INFO ) ) {
2006-04-18 18:16:32 +00:00
obj - > up = 0 ;
2005-02-17 16:31:08 +00:00
}
}
}
SQLFreeHandle ( SQL_HANDLE_STMT , stmt ) ;
}
2009-02-19 00:26:01 +00:00
if ( ! obj - > up & & ! obj - > tx ) { /* Try to reconnect! */
2005-02-17 16:31:08 +00:00
ast_log ( LOG_WARNING , " Connection is down attempting to reconnect... \n " ) ;
odbc_obj_disconnect ( obj ) ;
odbc_obj_connect ( obj ) ;
}
return obj - > up ;
}
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 ;
2010-07-23 16:19:21 +00:00
int enabled , pooling , limit , bse , conntimeout , forcecommit , isolation ;
struct timeval ncache = { 0 , 0 } ;
2008-01-21 18:15:57 +00:00
unsigned int idlecheck ;
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 ;
2008-08-10 00:47:56 +00:00
preconnect = idlecheck = 0 ;
2006-08-31 21:00:20 +00:00
pooling = 0 ;
limit = 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 ;
2006-08-31 21:00:20 +00:00
for ( v = ast_variable_browse ( config , cat ) ; v ; v = v - > next ) {
if ( ! strcasecmp ( v - > name , " pooling " ) ) {
2006-09-21 23:29:32 +00:00
if ( ast_true ( v - > value ) )
pooling = 1 ;
2008-02-01 18:08:44 +00:00
} else if ( ! strncasecmp ( v - > name , " share " , 5 ) ) {
/* "shareconnections" is a little clearer in meaning than "pooling" */
if ( ast_false ( v - > value ) )
pooling = 1 ;
2006-08-31 21:00:20 +00:00
} else if ( ! strcasecmp ( v - > name , " limit " ) ) {
2009-08-10 19:20:57 +00:00
sscanf ( v - > value , " %30d " , & limit ) ;
2006-08-31 21:00:20 +00:00
if ( ast_true ( v - > value ) & & ! limit ) {
ast_log ( LOG_WARNING , " Limit should be a number, not a boolean: '%s'. Setting limit to 1023 for ODBC class '%s'. \n " , v - > value , cat ) ;
limit = 1023 ;
} else if ( ast_false ( v - > value ) ) {
ast_log ( LOG_WARNING , " Limit should be a number, not a boolean: '%s'. Disabling ODBC class '%s'. \n " , v - > value , cat ) ;
enabled = 0 ;
break ;
2004-10-23 07:03:01 +00:00
}
2008-01-21 18:15:57 +00:00
} else if ( ! strcasecmp ( v - > name , " idlecheck " ) ) {
2009-08-10 19:20:57 +00:00
sscanf ( v - > value , " %30u " , & idlecheck ) ;
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 " ) ) {
if ( ( isolation = text2isolation ( v - > value ) ) = = 0 ) {
ast_log ( LOG_ERROR , " Unrecognized value for 'isolation': '%s' in section '%s' \n " , v - > value , cat ) ;
isolation = SQL_TXN_READ_COMMITTED ;
}
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
2008-05-05 23:38:15 +00:00
new - > obj_container = ao2_container_alloc ( 1 , null_hash_fn , ao2_match_by_addr ) ;
2006-08-31 21:00:20 +00:00
if ( pooling ) {
new - > haspool = pooling ;
if ( limit ) {
new - > limit = limit ;
} else {
ast_log ( LOG_WARNING , " Pooling without also setting a limit is pointless. Changing limit from 0 to 5. \n " ) ;
new - > limit = 5 ;
2004-07-08 19:58:26 +00:00
}
2006-04-18 18:16:32 +00:00
}
2006-08-31 21:00:20 +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 ;
2008-01-21 18:15:57 +00:00
new - > idlecheck = idlecheck ;
2010-07-23 16:19:21 +00:00
new - > conntimeout = conntimeout ;
new - > negative_connection_cache = ncache ;
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 ;
}
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
{
2008-05-05 23:38:15 +00:00
struct ao2_iterator aoi = ao2_iterator_init ( class_container , 0 ) ;
2006-04-18 18:16:32 +00:00
struct odbc_class * class ;
struct odbc_obj * current ;
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 ) ;
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 ) ) ) {
2006-09-18 19:54:18 +00:00
int count = 0 ;
2010-07-23 16:19:21 +00:00
char timestr [ 80 ] ;
struct ast_tm tm ;
ast_localtime ( & class - > last_negative_connect , & tm , NULL ) ;
ast_strftime ( timestr , sizeof ( timestr ) , " %Y-%m-%d %T " , & tm ) ;
2007-09-18 22:43:45 +00:00
ast_cli ( a - > fd , " Name: %s \n DSN: %s \n " , class - > name , class - > dsn ) ;
2010-07-23 16:19:21 +00:00
ast_cli ( a - > fd , " Last connection attempt: %s \n " , timestr ) ;
2005-01-07 06:36:02 +00:00
2006-09-18 19:54:18 +00:00
if ( class - > haspool ) {
2008-05-05 23:38:15 +00:00
struct ao2_iterator aoi2 = ao2_iterator_init ( class - > obj_container , 0 ) ;
2007-09-18 22:43:45 +00:00
ast_cli ( a - > fd , " Pooled: Yes \n Limit: %d \n Connections in use: %d \n " , class - > limit , class - > count ) ;
2005-01-07 06:36:02 +00:00
2008-05-05 23:38:15 +00:00
while ( ( current = ao2_iterator_next ( & aoi2 ) ) ) {
2008-05-23 18:09:14 +00:00
ast_mutex_lock ( & current - > lock ) ;
2008-07-11 19:53:38 +00:00
# ifdef DEBUG_THREADS
ast_cli ( a - > fd , " - Connection %d: %s (%s:%d %s) \n " , + + count ,
current - > used ? " in use " :
current - > up & & ast_odbc_sanity_check ( current ) ? " connected " : " disconnected " ,
current - > file , current - > lineno , current - > function ) ;
# else
2008-05-05 23:38:15 +00:00
ast_cli ( a - > fd , " - Connection %d: %s \n " , + + count ,
current - > used ? " in use " :
current - > up & & ast_odbc_sanity_check ( current ) ? " connected " : " disconnected " ) ;
2008-07-11 19:53:38 +00:00
# endif
2008-05-23 18:09:14 +00:00
ast_mutex_unlock ( & current - > lock ) ;
2008-05-05 23:38:15 +00:00
ao2_ref ( current , - 1 ) ;
2006-09-18 19:54:18 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & aoi2 ) ;
2006-09-18 19:54:18 +00:00
} else {
2009-02-19 00:26:01 +00:00
/* Should only ever be one of these (unless there are transactions) */
2008-05-05 23:38:15 +00:00
struct ao2_iterator aoi2 = ao2_iterator_init ( class - > obj_container , 0 ) ;
while ( ( current = ao2_iterator_next ( & aoi2 ) ) ) {
2009-02-19 00:26:01 +00:00
ast_cli ( a - > fd , " Pooled: No \n Connected: %s \n " , current - > used ? " In use " :
current - > up & & ast_odbc_sanity_check ( current ) ? " Yes " : " No " ) ;
2008-05-05 23:38:15 +00:00
ao2_ref ( current , - 1 ) ;
2006-04-18 18:16:32 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & aoi2 ) ;
2006-09-18 19:54:18 +00:00
}
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
2008-08-10 00:47:56 +00:00
static int 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 ;
if ( class ) {
2008-05-05 23:38:15 +00:00
ao2_link ( class_container , class ) ;
/* I still have a reference in the caller, so a deref is NOT missing here. */
2006-04-18 18:16:32 +00:00
2008-08-10 00:47:56 +00:00
if ( preconnect ) {
2006-04-18 18:16:32 +00:00
/* Request and release builds a connection */
2006-09-20 04:57:20 +00:00
obj = ast_odbc_request_obj ( class - > name , 0 ) ;
2009-02-19 00:26:01 +00:00
if ( obj ) {
2007-01-30 09:10:09 +00:00
ast_odbc_release_obj ( obj ) ;
2009-02-19 00:26:01 +00:00
}
2006-04-18 18:16:32 +00:00
}
2004-06-11 00:12:35 +00:00
2006-04-18 18:16:32 +00:00
return 0 ;
} else {
ast_log ( LOG_WARNING , " Attempted to register a NULL class? \n " ) ;
return - 1 ;
2005-02-17 16:31:08 +00:00
}
2004-06-11 00:12:35 +00:00
}
2009-02-19 00:26:01 +00:00
static void odbc_release_obj2 ( struct odbc_obj * obj , struct odbc_txn_frame * tx )
2004-07-08 19:58:26 +00:00
{
2009-02-19 00:26:01 +00:00
SQLINTEGER nativeerror = 0 , numfields = 0 ;
SQLSMALLINT diagbytes = 0 , i ;
unsigned char state [ 10 ] , diagnostic [ 256 ] ;
ast_debug ( 2 , " odbc_release_obj2(%p) called (obj->txf = %p) \n " , obj , obj - > txf ) ;
if ( tx ) {
ast_debug ( 1 , " called on a transactional handle with %s \n " , tx - > forcecommit ? " COMMIT " : " ROLLBACK " ) ;
if ( SQLEndTran ( SQL_HANDLE_DBC , obj - > con , tx - > forcecommit ? SQL_COMMIT : SQL_ROLLBACK ) = = SQL_ERROR ) {
/* Handle possible transaction commit failure */
SQLGetDiagField ( SQL_HANDLE_DBC , obj - > con , 1 , SQL_DIAG_NUMBER , & numfields , SQL_IS_INTEGER , & diagbytes ) ;
for ( i = 0 ; i < numfields ; i + + ) {
SQLGetDiagRec ( SQL_HANDLE_DBC , obj - > con , i + 1 , state , & nativeerror , diagnostic , sizeof ( diagnostic ) , & diagbytes ) ;
ast_log ( LOG_WARNING , " SQLEndTran returned an error: %s: %s \n " , state , diagnostic ) ;
if ( ! strcmp ( ( char * ) state , " 25S02 " ) | | ! strcmp ( ( char * ) state , " 08007 " ) ) {
/* These codes mean that a commit failed and a transaction
* is still active . We must rollback , or things will get
* very , very weird for anybody using the handle next . */
SQLEndTran ( SQL_HANDLE_DBC , obj - > con , SQL_ROLLBACK ) ;
}
if ( i > 10 ) {
ast_log ( LOG_WARNING , " Oh, that was good. There are really %d diagnostics? \n " , ( int ) numfields ) ;
break ;
}
}
}
/* Transaction is done, reset autocommit */
if ( SQLSetConnectAttr ( obj - > con , SQL_ATTR_AUTOCOMMIT , ( void * ) SQL_AUTOCOMMIT_ON , 0 ) = = SQL_ERROR ) {
SQLGetDiagField ( SQL_HANDLE_DBC , obj - > con , 1 , SQL_DIAG_NUMBER , & numfields , SQL_IS_INTEGER , & diagbytes ) ;
for ( i = 0 ; i < numfields ; i + + ) {
SQLGetDiagRec ( SQL_HANDLE_DBC , obj - > con , i + 1 , state , & nativeerror , diagnostic , sizeof ( diagnostic ) , & diagbytes ) ;
ast_log ( LOG_WARNING , " SetConnectAttr (Autocommit) returned an error: %s: %s \n " , state , diagnostic ) ;
if ( i > 10 ) {
ast_log ( LOG_WARNING , " Oh, that was good. There are really %d diagnostics? \n " , ( int ) numfields ) ;
break ;
}
}
}
}
2008-07-11 19:53:38 +00:00
# ifdef DEBUG_THREADS
obj - > file [ 0 ] = ' \0 ' ;
obj - > function [ 0 ] = ' \0 ' ;
obj - > lineno = 0 ;
# endif
2009-02-19 00:26:01 +00:00
/* For pooled connections, this frees the connection to be
* reused . For non - pooled connections , it does nothing . */
obj - > used = 0 ;
if ( obj - > txf ) {
/* Prevent recursion -- transaction is already closed out. */
obj - > txf - > obj = NULL ;
obj - > txf = release_transaction ( obj - > txf ) ;
}
2008-05-05 23:38:15 +00:00
ao2_ref ( obj , - 1 ) ;
2006-04-18 18:16:32 +00:00
}
2004-07-08 19:58:26 +00:00
2009-02-19 00:26:01 +00:00
void ast_odbc_release_obj ( struct odbc_obj * obj )
{
struct odbc_txn_frame * tx = find_transaction ( NULL , obj , NULL , 0 ) ;
odbc_release_obj2 ( obj , tx ) ;
}
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-05-21 21:13:09 +00:00
static int commit_exec ( struct ast_channel * chan , const char * data )
2009-02-19 00:26:01 +00:00
{
struct odbc_txn_frame * tx ;
SQLINTEGER nativeerror = 0 , numfields = 0 ;
SQLSMALLINT diagbytes = 0 , i ;
unsigned char state [ 10 ] , diagnostic [ 256 ] ;
if ( ast_strlen_zero ( data ) ) {
tx = find_transaction ( chan , NULL , NULL , 1 ) ;
} else {
tx = find_transaction ( chan , NULL , data , 0 ) ;
}
pbx_builtin_setvar_helper ( chan , " COMMIT_RESULT " , " OK " ) ;
if ( tx ) {
if ( SQLEndTran ( SQL_HANDLE_DBC , tx - > obj - > con , SQL_COMMIT ) = = SQL_ERROR ) {
struct ast_str * errors = ast_str_thread_get ( & errors_buf , 16 ) ;
ast_str_reset ( errors ) ;
/* Handle possible transaction commit failure */
SQLGetDiagField ( SQL_HANDLE_DBC , tx - > obj - > con , 1 , SQL_DIAG_NUMBER , & numfields , SQL_IS_INTEGER , & diagbytes ) ;
for ( i = 0 ; i < numfields ; i + + ) {
SQLGetDiagRec ( SQL_HANDLE_DBC , tx - > obj - > con , i + 1 , state , & nativeerror , diagnostic , sizeof ( diagnostic ) , & diagbytes ) ;
ast_str_append ( & errors , 0 , " %s%s " , ast_str_strlen ( errors ) ? " , " : " " , state ) ;
ast_log ( LOG_WARNING , " SQLEndTran returned an error: %s: %s \n " , state , diagnostic ) ;
if ( i > 10 ) {
ast_log ( LOG_WARNING , " Oh, that was good. There are really %d diagnostics? \n " , ( int ) numfields ) ;
break ;
}
}
pbx_builtin_setvar_helper ( chan , " COMMIT_RESULT " , ast_str_buffer ( errors ) ) ;
}
}
return 0 ;
}
2009-05-21 21:13:09 +00:00
static int rollback_exec ( struct ast_channel * chan , const char * data )
2009-02-19 00:26:01 +00:00
{
struct odbc_txn_frame * tx ;
SQLINTEGER nativeerror = 0 , numfields = 0 ;
SQLSMALLINT diagbytes = 0 , i ;
unsigned char state [ 10 ] , diagnostic [ 256 ] ;
if ( ast_strlen_zero ( data ) ) {
tx = find_transaction ( chan , NULL , NULL , 1 ) ;
} else {
tx = find_transaction ( chan , NULL , data , 0 ) ;
}
pbx_builtin_setvar_helper ( chan , " ROLLBACK_RESULT " , " OK " ) ;
if ( tx ) {
if ( SQLEndTran ( SQL_HANDLE_DBC , tx - > obj - > con , SQL_ROLLBACK ) = = SQL_ERROR ) {
struct ast_str * errors = ast_str_thread_get ( & errors_buf , 16 ) ;
ast_str_reset ( errors ) ;
/* Handle possible transaction commit failure */
SQLGetDiagField ( SQL_HANDLE_DBC , tx - > obj - > con , 1 , SQL_DIAG_NUMBER , & numfields , SQL_IS_INTEGER , & diagbytes ) ;
for ( i = 0 ; i < numfields ; i + + ) {
SQLGetDiagRec ( SQL_HANDLE_DBC , tx - > obj - > con , i + 1 , state , & nativeerror , diagnostic , sizeof ( diagnostic ) , & diagbytes ) ;
ast_str_append ( & errors , 0 , " %s%s " , ast_str_strlen ( errors ) ? " , " : " " , state ) ;
ast_log ( LOG_WARNING , " SQLEndTran returned an error: %s: %s \n " , state , diagnostic ) ;
if ( i > 10 ) {
ast_log ( LOG_WARNING , " Oh, that was good. There are really %d diagnostics? \n " , ( int ) numfields ) ;
break ;
}
}
pbx_builtin_setvar_helper ( chan , " ROLLBACK_RESULT " , ast_str_buffer ( errors ) ) ;
}
}
return 0 ;
}
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 ;
}
# define USE_TX (void *)(long)1
# define NO_TX (void *)(long)2
# define EOR_TX (void *)(long)3
static int aoro2_obj_cb ( void * vobj , void * arg , int flags )
{
struct odbc_obj * obj = vobj ;
ast_mutex_lock ( & obj - > lock ) ;
if ( ( arg = = NO_TX & & ! obj - > tx ) | | ( arg = = EOR_TX & & ! obj - > used ) | | ( arg = = USE_TX & & obj - > tx & & ! obj - > used ) ) {
obj - > used = 1 ;
ast_mutex_unlock ( & obj - > lock ) ;
return CMP_MATCH | CMP_STOP ;
}
ast_mutex_unlock ( & obj - > lock ) ;
return 0 ;
}
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
SQLINTEGER nativeerror = 0 , numfields = 0 ;
SQLSMALLINT diagbytes = 0 , i ;
unsigned char state [ 10 ] , diagnostic [ 256 ] ;
2006-04-18 18:16:32 +00:00
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
2009-02-17 18:49:20 +00:00
ast_assert ( ao2_ref ( class , 0 ) > 1 ) ;
2006-04-18 18:16:32 +00:00
if ( class - > haspool ) {
/* Recycle connections before building another */
2009-02-19 00:26:01 +00:00
obj = ao2_callback ( class - > obj_container , 0 , aoro2_obj_cb , EOR_TX ) ;
2004-07-08 19:58:26 +00:00
2009-02-17 18:49:20 +00:00
if ( obj ) {
ast_assert ( ao2_ref ( obj , 0 ) > 1 ) ;
}
2010-07-23 16:19:21 +00:00
if ( ! obj & & ( class - > count < class - > limit ) & &
( time ( NULL ) > class - > last_negative_connect . tv_sec + class - > negative_connection_cache . tv_sec ) ) {
2008-05-05 23:38:15 +00:00
obj = ao2_alloc ( sizeof ( * obj ) , odbc_obj_destructor ) ;
2006-04-18 18:16:32 +00:00
if ( ! obj ) {
2010-07-23 16:19:21 +00:00
class - > count - - ;
2008-05-05 23:38:15 +00:00
ao2_ref ( class , - 1 ) ;
2010-07-23 16:19:21 +00:00
ast_debug ( 3 , " Unable to allocate object \n " ) ;
2006-04-18 18:16:32 +00:00
return NULL ;
}
2009-02-17 18:49:20 +00:00
ast_assert ( ao2_ref ( obj , 0 ) = = 1 ) ;
2006-04-18 18:16:32 +00:00
ast_mutex_init ( & obj - > lock ) ;
2008-05-05 23:38:15 +00:00
/* obj inherits the outstanding reference to class */
2006-04-18 18:16:32 +00:00
obj - > parent = class ;
2009-02-19 00:26:01 +00:00
class = NULL ;
2007-08-11 05:28:08 +00:00
if ( odbc_obj_connect ( obj ) = = ODBC_FAIL ) {
ast_log ( LOG_WARNING , " Failed to connect to %s \n " , name ) ;
2008-05-05 23:38:15 +00:00
ao2_ref ( obj , - 1 ) ;
2009-02-17 18:49:20 +00:00
ast_assert ( ao2_ref ( class , 0 ) > 0 ) ;
2007-08-11 05:28:08 +00:00
obj = NULL ;
} else {
obj - > used = 1 ;
2009-02-19 00:26:01 +00:00
ao2_link ( obj - > parent - > obj_container , obj ) ;
ast_atomic_fetchadd_int ( & obj - > parent - > count , + 1 ) ;
2007-08-11 05:28:08 +00:00
}
2008-12-18 16:36:48 +00:00
} else {
/* Object is not constructed, so delete outstanding reference to class. */
ao2_ref ( class , - 1 ) ;
class = NULL ;
2006-04-18 18:16:32 +00:00
}
2009-02-19 00:26:01 +00:00
if ( obj & & ast_test_flag ( & flags , RES_ODBC_INDEPENDENT_CONNECTION ) ) {
/* Ensure this connection has autocommit turned off. */
if ( SQLSetConnectAttr ( obj - > con , SQL_ATTR_AUTOCOMMIT , ( void * ) SQL_AUTOCOMMIT_OFF , 0 ) = = SQL_ERROR ) {
SQLGetDiagField ( SQL_HANDLE_DBC , obj - > con , 1 , SQL_DIAG_NUMBER , & numfields , SQL_IS_INTEGER , & diagbytes ) ;
for ( i = 0 ; i < numfields ; i + + ) {
SQLGetDiagRec ( SQL_HANDLE_DBC , obj - > con , i + 1 , state , & nativeerror , diagnostic , sizeof ( diagnostic ) , & diagbytes ) ;
ast_log ( LOG_WARNING , " SQLSetConnectAttr (Autocommit) returned an error: %s: %s \n " , state , diagnostic ) ;
if ( i > 10 ) {
ast_log ( LOG_WARNING , " Oh, that was good. There are really %d diagnostics? \n " , ( int ) numfields ) ;
break ;
}
}
}
}
} else if ( ast_test_flag ( & flags , RES_ODBC_INDEPENDENT_CONNECTION ) ) {
/* Non-pooled connections -- but must use a separate connection handle */
if ( ! ( obj = ao2_callback ( class - > obj_container , 0 , aoro2_obj_cb , USE_TX ) ) ) {
2010-07-23 16:19:21 +00:00
ast_debug ( 1 , " Object not found \n " ) ;
2009-02-19 00:26:01 +00:00
obj = ao2_alloc ( sizeof ( * obj ) , odbc_obj_destructor ) ;
if ( ! obj ) {
ao2_ref ( class , - 1 ) ;
2010-07-23 16:19:21 +00:00
ast_debug ( 3 , " Unable to allocate object \n " ) ;
2009-02-19 00:26:01 +00:00
return NULL ;
}
ast_mutex_init ( & obj - > lock ) ;
/* obj inherits the outstanding reference to class */
obj - > parent = class ;
class = NULL ;
if ( odbc_obj_connect ( obj ) = = ODBC_FAIL ) {
ast_log ( LOG_WARNING , " Failed to connect to %s \n " , name ) ;
ao2_ref ( obj , - 1 ) ;
obj = NULL ;
} else {
obj - > used = 1 ;
ao2_link ( obj - > parent - > obj_container , obj ) ;
ast_atomic_fetchadd_int ( & obj - > parent - > count , + 1 ) ;
}
2006-04-18 18:16:32 +00:00
}
2004-07-08 19:58:26 +00:00
2009-02-19 00:26:01 +00:00
if ( obj & & SQLSetConnectAttr ( obj - > con , SQL_ATTR_AUTOCOMMIT , ( void * ) SQL_AUTOCOMMIT_OFF , 0 ) = = SQL_ERROR ) {
SQLGetDiagField ( SQL_HANDLE_DBC , obj - > con , 1 , SQL_DIAG_NUMBER , & numfields , SQL_IS_INTEGER , & diagbytes ) ;
for ( i = 0 ; i < numfields ; i + + ) {
SQLGetDiagRec ( SQL_HANDLE_DBC , obj - > con , i + 1 , state , & nativeerror , diagnostic , sizeof ( diagnostic ) , & diagbytes ) ;
ast_log ( LOG_WARNING , " SetConnectAttr (Autocommit) returned an error: %s: %s \n " , state , diagnostic ) ;
if ( i > 10 ) {
ast_log ( LOG_WARNING , " Oh, that was good. There are really %d diagnostics? \n " , ( int ) numfields ) ;
break ;
}
}
}
} else {
/* Non-pooled connection: multiple modules can use the same connection. */
2009-02-25 19:03:35 +00:00
if ( ( obj = ao2_callback ( class - > obj_container , 0 , aoro2_obj_cb , NO_TX ) ) ) {
2008-12-18 16:36:48 +00:00
/* Object is not constructed, so delete outstanding reference to class. */
2009-02-17 18:49:20 +00:00
ast_assert ( ao2_ref ( class , 0 ) > 1 ) ;
2008-12-18 16:36:48 +00:00
ao2_ref ( class , - 1 ) ;
class = NULL ;
} else {
2006-04-18 18:16:32 +00:00
/* No entry: build one */
2009-02-19 00:26:01 +00:00
if ( ! ( obj = ao2_alloc ( sizeof ( * obj ) , odbc_obj_destructor ) ) ) {
2009-02-17 18:49:20 +00:00
ast_assert ( ao2_ref ( class , 0 ) > 1 ) ;
2008-05-05 23:38:15 +00:00
ao2_ref ( class , - 1 ) ;
2010-07-23 16:19:21 +00:00
ast_debug ( 3 , " Unable to allocate object \n " ) ;
2006-04-18 18:16:32 +00:00
return NULL ;
}
ast_mutex_init ( & obj - > lock ) ;
2008-05-05 23:38:15 +00:00
/* obj inherits the outstanding reference to class */
2006-04-18 18:16:32 +00:00
obj - > parent = class ;
2009-02-19 00:26:01 +00:00
class = NULL ;
2006-04-18 18:16:32 +00:00
if ( odbc_obj_connect ( obj ) = = ODBC_FAIL ) {
2007-01-30 09:10:09 +00:00
ast_log ( LOG_WARNING , " Failed to connect to %s \n " , name ) ;
2008-05-05 23:38:15 +00:00
ao2_ref ( obj , - 1 ) ;
2007-01-30 09:10:09 +00:00
obj = NULL ;
2006-04-18 18:16:32 +00:00
} else {
2009-02-19 00:26:01 +00:00
ao2_link ( obj - > parent - > obj_container , obj ) ;
2009-02-17 18:49:20 +00:00
ast_assert ( ao2_ref ( obj , 0 ) > 1 ) ;
2006-04-18 18:16:32 +00:00
}
2009-02-19 00:26:01 +00:00
}
if ( obj & & SQLSetConnectAttr ( obj - > con , SQL_ATTR_AUTOCOMMIT , ( void * ) SQL_AUTOCOMMIT_ON , 0 ) = = SQL_ERROR ) {
SQLGetDiagField ( SQL_HANDLE_DBC , obj - > con , 1 , SQL_DIAG_NUMBER , & numfields , SQL_IS_INTEGER , & diagbytes ) ;
for ( i = 0 ; i < numfields ; i + + ) {
SQLGetDiagRec ( SQL_HANDLE_DBC , obj - > con , i + 1 , state , & nativeerror , diagnostic , sizeof ( diagnostic ) , & diagbytes ) ;
ast_log ( LOG_WARNING , " SetConnectAttr (Autocommit) returned an error: %s: %s \n " , state , diagnostic ) ;
if ( i > 10 ) {
ast_log ( LOG_WARNING , " Oh, that was good. There are really %d diagnostics? \n " , ( int ) numfields ) ;
break ;
}
}
2006-04-18 18:16:32 +00:00
}
2006-02-02 16:09:01 +00:00
}
2009-02-19 00:26:01 +00:00
/* Set the isolation property */
if ( obj & & SQLSetConnectAttr ( obj - > con , SQL_ATTR_TXN_ISOLATION , ( void * ) ( long ) obj - > parent - > isolation , 0 ) = = SQL_ERROR ) {
SQLGetDiagField ( SQL_HANDLE_DBC , obj - > con , 1 , SQL_DIAG_NUMBER , & numfields , SQL_IS_INTEGER , & diagbytes ) ;
for ( i = 0 ; i < numfields ; i + + ) {
SQLGetDiagRec ( SQL_HANDLE_DBC , obj - > con , i + 1 , state , & nativeerror , diagnostic , sizeof ( diagnostic ) , & diagbytes ) ;
ast_log ( LOG_WARNING , " SetConnectAttr (Txn isolation) returned an error: %s: %s \n " , state , diagnostic ) ;
if ( i > 10 ) {
ast_log ( LOG_WARNING , " Oh, that was good. There are really %d diagnostics? \n " , ( int ) numfields ) ;
break ;
}
}
}
2010-07-23 16:19:21 +00:00
if ( obj & & ast_test_flag ( & flags , RES_ODBC_CONNECTED ) & & ! obj - > up ) {
/* Check if this connection qualifies for reconnection, with negative connection cache time */
if ( time ( NULL ) > class - > last_negative_connect . tv_sec + class - > negative_connection_cache . tv_sec ) {
odbc_obj_connect ( obj ) ;
}
} else if ( obj & & ast_test_flag ( & flags , RES_ODBC_SANITY_CHECK ) ) {
2006-09-20 04:57:20 +00:00
ast_odbc_sanity_check ( obj ) ;
2010-07-23 16:19:21 +00:00
} else if ( obj & & obj - > parent - > idlecheck > 0 & & ast_tvdiff_sec ( ast_tvnow ( ) , obj - > last_used ) > obj - > parent - > idlecheck ) {
2008-01-21 18:15:57 +00:00
odbc_obj_connect ( obj ) ;
2010-07-23 16:19:21 +00:00
}
2008-01-21 18:15:57 +00:00
2008-07-11 21:50:21 +00:00
# ifdef DEBUG_THREADS
2008-07-11 19:56:34 +00:00
if ( obj ) {
ast_copy_string ( obj - > file , file , sizeof ( obj - > file ) ) ;
ast_copy_string ( obj - > function , function , sizeof ( obj - > function ) ) ;
obj - > lineno = lineno ;
}
2008-07-11 19:53:38 +00:00
# endif
2008-12-18 16:36:48 +00:00
ast_assert ( class = = NULL ) ;
2008-07-11 19:53:38 +00:00
2009-02-17 18:49:20 +00:00
if ( obj ) {
ast_assert ( ao2_ref ( obj , 0 ) > 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 } ;
return _ast_odbc_request_obj2 ( name , flags , file , function , lineno ) ;
}
struct odbc_obj * ast_odbc_retrieve_transaction_obj ( struct ast_channel * chan , const char * objname )
{
struct ast_datastore * txn_store ;
AST_LIST_HEAD ( , odbc_txn_frame ) * oldlist ;
struct odbc_txn_frame * txn = NULL ;
if ( ! chan ) {
/* No channel == no transaction */
return NULL ;
}
ast_channel_lock ( chan ) ;
if ( ( txn_store = ast_channel_datastore_find ( chan , & txn_info , NULL ) ) ) {
oldlist = txn_store - > data ;
} else {
ast_channel_unlock ( chan ) ;
return NULL ;
}
AST_LIST_LOCK ( oldlist ) ;
ast_channel_unlock ( chan ) ;
AST_LIST_TRAVERSE ( oldlist , txn , list ) {
if ( txn - > obj & & txn - > obj - > parent & & ! strcmp ( txn - > obj - > parent - > name , objname ) ) {
AST_LIST_UNLOCK ( oldlist ) ;
return txn - > obj ;
}
}
AST_LIST_UNLOCK ( oldlist ) ;
return NULL ;
}
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 ] ;
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 ;
}
2004-07-08 19:58:26 +00:00
ast_mutex_lock ( & obj - > lock ) ;
2004-06-11 00:12:35 +00:00
2005-02-17 16:31:08 +00:00
res = SQLDisconnect ( obj - > con ) ;
2009-02-17 18:49:20 +00:00
if ( obj - > parent ) {
if ( res = = SQL_SUCCESS | | res = = SQL_SUCCESS_WITH_INFO ) {
ast_log ( LOG_DEBUG , " Disconnected %d from %s [%s] \n " , res , obj - > parent - > name , obj - > parent - > dsn ) ;
} else {
ast_log ( LOG_DEBUG , " res_odbc: %s [%s] already disconnected \n " , obj - > parent - > name , obj - > parent - > dsn ) ;
}
2004-07-08 19:58:26 +00:00
}
2008-08-11 00:25:28 +00:00
2008-10-02 15:17:16 +00:00
if ( ( res = SQLFreeHandle ( SQL_HANDLE_DBC , obj - > con ) = = SQL_SUCCESS ) ) {
obj - > con = NULL ;
2008-08-11 00:25:28 +00:00
ast_log ( LOG_DEBUG , " Database handle deallocated \n " ) ;
} else {
2008-10-08 12:15:06 +00:00
SQLGetDiagRec ( SQL_HANDLE_DBC , obj - > con , 1 , state , & err , msg , 100 , & mlen ) ;
2008-08-11 00:25:28 +00:00
ast_log ( LOG_WARNING , " Unable to deallocate database handle? %d errno=%d %s \n " , res , ( int ) err , msg ) ;
}
2005-02-17 16:31:08 +00:00
obj - > up = 0 ;
2004-07-08 19:58:26 +00:00
ast_mutex_unlock ( & obj - > lock ) ;
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
2004-07-08 19:58:26 +00:00
ast_mutex_lock ( & obj - > lock ) ;
2004-06-11 00:12:35 +00:00
2008-10-02 15:17:16 +00:00
if ( obj - > up ) {
odbc_obj_disconnect ( obj ) ;
ast_log ( LOG_NOTICE , " Re-connecting %s \n " , obj - > parent - > name ) ;
} else {
ast_log ( LOG_NOTICE , " Connecting %s \n " , obj - > parent - > name ) ;
}
2006-04-18 18:16:32 +00:00
res = SQLAllocHandle ( SQL_HANDLE_DBC , obj - > parent - > env , & obj - > 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
ast_mutex_unlock ( & obj - > lock ) ;
return ODBC_FAIL ;
2004-07-08 19:58:26 +00:00
}
2010-07-23 18:56:59 +00:00
SQLSetConnectAttr ( obj - > con , SQL_LOGIN_TIMEOUT , ( SQLPOINTER * ) ( long ) obj - > parent - > conntimeout , 0 ) ;
SQLSetConnectAttr ( obj - > con , SQL_ATTR_CONNECTION_TIMEOUT , ( SQLPOINTER * ) ( long ) obj - > parent - > conntimeout , 0 ) ;
2006-04-18 18:16:32 +00:00
# ifdef NEEDTRACE
SQLSetConnectAttr ( obj - > con , SQL_ATTR_TRACE , & enable , SQL_IS_INTEGER ) ;
SQLSetConnectAttr ( obj - > con , SQL_ATTR_TRACEFILE , tracefile , strlen ( tracefile ) ) ;
# endif
2004-07-08 19:58:26 +00:00
res = SQLConnect ( obj - > 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 ) ) {
2008-08-10 00:47:56 +00:00
SQLGetDiagRec ( SQL_HANDLE_DBC , obj - > con , 1 , state , & err , msg , 100 , & mlen ) ;
2010-07-23 16:19:21 +00:00
obj - > parent - > last_negative_connect = ast_tvnow ( ) ;
2004-10-31 23:17:11 +00:00
ast_mutex_unlock ( & obj - > lock ) ;
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 ) ;
2004-07-08 19:58:26 +00:00
return ODBC_FAIL ;
} else {
2006-04-18 18:16:32 +00:00
ast_log ( LOG_NOTICE , " res_odbc: Connected to %s [%s] \n " , obj - > parent - > name , obj - > parent - > dsn ) ;
2004-07-08 19:58:26 +00:00
obj - > up = 1 ;
2008-01-21 18:15:57 +00:00
obj - > last_used = ast_tvnow ( ) ;
2004-07-08 19:58:26 +00:00
}
2004-06-11 00:12:35 +00:00
2004-07-08 19:58:26 +00:00
ast_mutex_unlock ( & obj - > lock ) ;
return ODBC_SUCCESS ;
2004-06-11 00:12:35 +00:00
}
2009-02-19 00:26:01 +00:00
static int acf_transaction_read ( struct ast_channel * chan , const char * cmd , char * data , char * buf , size_t len )
{
AST_DECLARE_APP_ARGS ( args ,
AST_APP_ARG ( property ) ;
AST_APP_ARG ( opt ) ;
) ;
struct odbc_txn_frame * tx ;
AST_STANDARD_APP_ARGS ( args , data ) ;
if ( strcasecmp ( args . property , " transaction " ) = = 0 ) {
if ( ( tx = find_transaction ( chan , NULL , NULL , 1 ) ) ) {
ast_copy_string ( buf , tx - > name , len ) ;
return 0 ;
}
} else if ( strcasecmp ( args . property , " isolation " ) = = 0 ) {
if ( ! ast_strlen_zero ( args . opt ) ) {
tx = find_transaction ( chan , NULL , args . opt , 0 ) ;
} else {
tx = find_transaction ( chan , NULL , NULL , 1 ) ;
}
if ( tx ) {
ast_copy_string ( buf , isolation2text ( tx - > isolation ) , len ) ;
return 0 ;
}
} else if ( strcasecmp ( args . property , " forcecommit " ) = = 0 ) {
if ( ! ast_strlen_zero ( args . opt ) ) {
tx = find_transaction ( chan , NULL , args . opt , 0 ) ;
} else {
tx = find_transaction ( chan , NULL , NULL , 1 ) ;
}
if ( tx ) {
ast_copy_string ( buf , tx - > forcecommit ? " 1 " : " 0 " , len ) ;
return 0 ;
}
}
return - 1 ;
}
static int acf_transaction_write ( struct ast_channel * chan , const char * cmd , char * s , const char * value )
{
AST_DECLARE_APP_ARGS ( args ,
AST_APP_ARG ( property ) ;
AST_APP_ARG ( opt ) ;
) ;
struct odbc_txn_frame * tx ;
SQLINTEGER nativeerror = 0 , numfields = 0 ;
SQLSMALLINT diagbytes = 0 , i ;
unsigned char state [ 10 ] , diagnostic [ 256 ] ;
AST_STANDARD_APP_ARGS ( args , s ) ;
if ( strcasecmp ( args . property , " transaction " ) = = 0 ) {
/* Set active transaction */
struct odbc_obj * obj ;
if ( ( tx = find_transaction ( chan , NULL , value , 0 ) ) ) {
mark_transaction_active ( chan , tx ) ;
} else {
/* No such transaction, create one */
struct ast_flags flags = { RES_ODBC_INDEPENDENT_CONNECTION } ;
if ( ast_strlen_zero ( args . opt ) | | ! ( obj = ast_odbc_request_obj2 ( args . opt , flags ) ) ) {
ast_log ( LOG_ERROR , " Could not create transaction: invalid database specification '%s' \n " , S_OR ( args . opt , " " ) ) ;
pbx_builtin_setvar_helper ( chan , " ODBC_RESULT " , " INVALID_DB " ) ;
return - 1 ;
}
if ( ! ( tx = find_transaction ( chan , obj , value , 0 ) ) ) {
pbx_builtin_setvar_helper ( chan , " ODBC_RESULT " , " FAILED_TO_CREATE " ) ;
return - 1 ;
}
obj - > tx = 1 ;
}
pbx_builtin_setvar_helper ( chan , " ODBC_RESULT " , " OK " ) ;
return 0 ;
} else if ( strcasecmp ( args . property , " forcecommit " ) = = 0 ) {
/* Set what happens when an uncommitted transaction ends without explicit Commit or Rollback */
if ( ast_strlen_zero ( args . opt ) ) {
tx = find_transaction ( chan , NULL , NULL , 1 ) ;
} else {
tx = find_transaction ( chan , NULL , args . opt , 0 ) ;
}
if ( ! tx ) {
pbx_builtin_setvar_helper ( chan , " ODBC_RESULT " , " FAILED_TO_CREATE " ) ;
return - 1 ;
}
if ( ast_true ( value ) ) {
tx - > forcecommit = 1 ;
} else if ( ast_false ( value ) ) {
tx - > forcecommit = 0 ;
} else {
ast_log ( LOG_ERROR , " Invalid value for forcecommit: '%s' \n " , S_OR ( value , " " ) ) ;
pbx_builtin_setvar_helper ( chan , " ODBC_RESULT " , " INVALID_VALUE " ) ;
return - 1 ;
}
pbx_builtin_setvar_helper ( chan , " ODBC_RESULT " , " OK " ) ;
return 0 ;
} else if ( strcasecmp ( args . property , " isolation " ) = = 0 ) {
/* How do uncommitted transactions affect reads? */
int isolation = text2isolation ( value ) ;
if ( ast_strlen_zero ( args . opt ) ) {
tx = find_transaction ( chan , NULL , NULL , 1 ) ;
} else {
tx = find_transaction ( chan , NULL , args . opt , 0 ) ;
}
if ( ! tx ) {
pbx_builtin_setvar_helper ( chan , " ODBC_RESULT " , " FAILED_TO_CREATE " ) ;
return - 1 ;
}
if ( isolation = = 0 ) {
pbx_builtin_setvar_helper ( chan , " ODBC_RESULT " , " INVALID_VALUE " ) ;
ast_log ( LOG_ERROR , " Invalid isolation specification: '%s' \n " , S_OR ( value , " " ) ) ;
} else if ( SQLSetConnectAttr ( tx - > obj - > con , SQL_ATTR_TXN_ISOLATION , ( void * ) ( long ) isolation , 0 ) = = SQL_ERROR ) {
pbx_builtin_setvar_helper ( chan , " ODBC_RESULT " , " SQL_ERROR " ) ;
SQLGetDiagField ( SQL_HANDLE_DBC , tx - > obj - > con , 1 , SQL_DIAG_NUMBER , & numfields , SQL_IS_INTEGER , & diagbytes ) ;
for ( i = 0 ; i < numfields ; i + + ) {
SQLGetDiagRec ( SQL_HANDLE_DBC , tx - > obj - > con , i + 1 , state , & nativeerror , diagnostic , sizeof ( diagnostic ) , & diagbytes ) ;
ast_log ( LOG_WARNING , " SetConnectAttr (Txn isolation) returned an error: %s: %s \n " , state , diagnostic ) ;
if ( i > 10 ) {
ast_log ( LOG_WARNING , " Oh, that was good. There are really %d diagnostics? \n " , ( int ) numfields ) ;
break ;
}
}
} else {
pbx_builtin_setvar_helper ( chan , " ODBC_RESULT " , " OK " ) ;
tx - > isolation = isolation ;
}
return 0 ;
} else {
ast_log ( LOG_ERROR , " Unknown property: '%s' \n " , args . property ) ;
return - 1 ;
}
}
static struct ast_custom_function odbc_function = {
. name = " ODBC " ,
. read = acf_transaction_read ,
. write = acf_transaction_write ,
} ;
2009-05-12 13:59:35 +00:00
static const char * const app_commit = " ODBC_Commit " ;
static const char * const app_rollback = " ODBC_Rollback " ;
2009-02-19 00:26:01 +00:00
2010-07-08 14:48:42 +00:00
/*!
* \ internal
* \ brief Implements the channels provider .
*/
static int data_odbc_provider_handler ( const struct ast_data_search * search ,
struct ast_data * root )
{
struct ao2_iterator aoi , aoi2 ;
struct odbc_class * class ;
struct odbc_obj * current ;
struct ast_data * data_odbc_class , * data_odbc_connections , * data_odbc_connection ;
struct ast_data * enum_node ;
int count ;
aoi = ao2_iterator_init ( class_container , 0 ) ;
while ( ( class = ao2_iterator_next ( & aoi ) ) ) {
data_odbc_class = ast_data_add_node ( root , " class " ) ;
if ( ! data_odbc_class ) {
ao2_ref ( class , - 1 ) ;
continue ;
}
ast_data_add_structure ( odbc_class , data_odbc_class , class ) ;
if ( ! ao2_container_count ( class - > obj_container ) ) {
ao2_ref ( class , - 1 ) ;
continue ;
}
data_odbc_connections = ast_data_add_node ( data_odbc_class , " connections " ) ;
if ( ! data_odbc_connections ) {
ao2_ref ( class , - 1 ) ;
continue ;
}
ast_data_add_bool ( data_odbc_class , " shared " , ! class - > haspool ) ;
/* isolation */
enum_node = ast_data_add_node ( data_odbc_class , " isolation " ) ;
if ( ! enum_node ) {
ao2_ref ( class , - 1 ) ;
continue ;
}
ast_data_add_int ( enum_node , " value " , class - > isolation ) ;
ast_data_add_str ( enum_node , " text " , isolation2text ( class - > isolation ) ) ;
count = 0 ;
aoi2 = ao2_iterator_init ( class - > obj_container , 0 ) ;
while ( ( current = ao2_iterator_next ( & aoi2 ) ) ) {
data_odbc_connection = ast_data_add_node ( data_odbc_connections , " connection " ) ;
if ( ! data_odbc_connection ) {
ao2_ref ( current , - 1 ) ;
continue ;
}
ast_mutex_lock ( & current - > lock ) ;
ast_data_add_str ( data_odbc_connection , " status " , current - > used ? " in use " :
current - > up & & ast_odbc_sanity_check ( current ) ? " connected " : " disconnected " ) ;
ast_data_add_bool ( data_odbc_connection , " transactional " , current - > tx ) ;
ast_mutex_unlock ( & current - > lock ) ;
if ( class - > haspool ) {
ast_data_add_int ( data_odbc_connection , " number " , + + count ) ;
}
ao2_ref ( current , - 1 ) ;
}
ao2_ref ( class , - 1 ) ;
if ( ! ast_data_search_match ( search , data_odbc_class ) ) {
ast_data_remove_node ( root , data_odbc_class ) ;
}
}
return 0 ;
}
/*!
* \ internal
* \ brief / asterisk / res / odbc / listprovider .
*/
static const struct ast_data_handler odbc_provider = {
. version = AST_DATA_HANDLER_VERSION ,
. get = data_odbc_provider_handler
} ;
static const struct ast_data_entry odbc_providers [ ] = {
AST_DATA_ENTRY ( " /asterisk/res/odbc " , & odbc_provider ) ,
} ;
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 ;
2008-05-20 16:13:48 +00:00
struct odbc_obj * current ;
2008-05-05 23:38:15 +00:00
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-05 23:38:15 +00:00
/* Purge remaining classes */
2008-05-20 16:13:48 +00:00
/* Note on how this works; this is a case of circular references, so we
* explicitly do NOT want to use a callback here ( or we wind up in
* recursive hell ) .
*
* 1. Iterate through all the classes . Note that the classes will currently
* contain two classes of the same name , one of which is marked delme and
* will be purged when all remaining objects of the class are released , and
* the other , which was created above when we re - parsed the config file .
* 2. On each class , there is a reference held by the master container and
* a reference held by each connection object . There are two cases for
* destruction of the class , noted below . However , in all cases , all O - refs
* ( references to objects ) will first be freed , which will cause the C - refs
* ( references to classes ) to be decremented ( but never to 0 , because the
* class container still has a reference ) .
* a ) If the class has outstanding objects , the C - ref by the class
* container will then be freed , which leaves only C - refs by any
* outstanding objects . When the final outstanding object is released
* ( O - refs held by applications and dialplan functions ) , it will in turn
* free the final C - ref , causing class destruction .
* b ) If the class has no outstanding objects , when the class container
* removes the final C - ref , the class will be destroyed .
*/
aoi = ao2_iterator_init ( class_container , 0 ) ;
while ( ( class = ao2_iterator_next ( & aoi ) ) ) { /* C-ref++ (by iterator) */
if ( class - > delme ) {
struct ao2_iterator aoi2 = ao2_iterator_init ( class - > obj_container , 0 ) ;
while ( ( current = ao2_iterator_next ( & aoi2 ) ) ) { /* O-ref++ (by iterator) */
ao2_unlink ( class - > obj_container , current ) ; /* unlink O-ref from class (reference handled implicitly) */
ao2_ref ( current , - 1 ) ; /* O-ref-- (by iterator) */
/* At this point, either
* a ) there ' s an outstanding O - ref , or
* b ) the object has already been destroyed .
*/
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & aoi2 ) ;
2008-05-20 16:13:48 +00:00
ao2_unlink ( class_container , class ) ; /* unlink C-ref from container (reference handled implicitly) */
/* At this point, either
* a ) there ' s an outstanding O - ref , which holds an outstanding C - ref , or
* b ) the last remaining C - ref is held by the iterator , which will be
* destroyed in the next step .
*/
}
ao2_ref ( class , - 1 ) ; /* C-ref-- (by iterator) */
}
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
{
/* Prohibit unloading */
return - 1 ;
}
2006-08-21 02:11:39 +00:00
static int load_module ( void )
2004-07-08 19:58:26 +00:00
{
2008-05-05 23:38:15 +00:00
if ( ! ( class_container = ao2_container_alloc ( 1 , null_hash_fn , ao2_match_by_addr ) ) )
return AST_MODULE_LOAD_DECLINE ;
2008-01-18 06:52:18 +00:00
if ( load_odbc_config ( ) = = - 1 )
2006-08-31 21:00:20 +00:00
return AST_MODULE_LOAD_DECLINE ;
2008-12-05 10:31:25 +00:00
ast_cli_register_multiple ( cli_odbc , ARRAY_LEN ( cli_odbc ) ) ;
2010-07-08 14:48:42 +00:00
ast_data_register_multiple ( odbc_providers , ARRAY_LEN ( odbc_providers ) ) ;
2009-02-19 00:26:01 +00:00
ast_register_application_xml ( app_commit , commit_exec ) ;
ast_register_application_xml ( app_rollback , rollback_exec ) ;
ast_custom_function_register ( & odbc_function ) ;
2004-07-08 19:58:26 +00:00
ast_log ( LOG_NOTICE , " res_odbc loaded. \n " ) ;
return 0 ;
}
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 " ,
2006-08-21 02:11:39 +00:00
. load = load_module ,
. unload = unload_module ,
. reload = reload ,
2010-07-20 19:35:02 +00:00
. load_pri = AST_MODPRI_REALTIME_DEPEND ,
2006-08-21 02:11:39 +00:00
) ;