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
*
2010-07-17 17:39:28 +00:00
* Copyright (C) 1999 - 2010, 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
*
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+odbc plugin for portable configuration engine
2005-09-14 20:46:50 +00:00
*
2005-12-30 21:18:06 +00:00
* \author Mark Spencer <markster@digium.com>
* \author Anthony Minessale II <anthmct@yahoo.com>
*
* \arg http://www.unixodbc.org
2004-06-11 00:12:35 +00:00
*/
2006-04-24 17:11:45 +00:00
/*** MODULEINFO
2007-02-09 23:53:51 +00:00
<depend>res_odbc</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"
ASTERISK_FILE_VERSION ( __FILE__ , "$Revision$" )
2005-04-21 06:02:45 +00:00
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/config.h"
#include "asterisk/module.h"
#include "asterisk/lock.h"
#include "asterisk/res_odbc.h"
#include "asterisk/utils.h"
2010-07-17 17:39:28 +00:00
#include "asterisk/stringfields.h"
2004-06-11 00:12:35 +00:00
2008-10-14 00:08:52 +00:00
AST_THREADSTORAGE ( sql_buf );
2014-05-28 11:36:01 +00:00
AST_THREADSTORAGE ( rowdata_buf );
2008-10-14 00:08:52 +00:00
2007-08-07 19:14:45 +00:00
struct custom_prepare_struct {
const char * sql ;
const char * extra ;
2010-07-17 17:39:28 +00:00
AST_DECLARE_STRING_FIELDS (
AST_STRING_FIELD ( encoding )[ 256 ];
);
2007-08-07 21:00:10 +00:00
va_list ap ;
2008-06-05 19:07:27 +00:00
unsigned long long skip ;
2007-08-07 19:14:45 +00:00
};
2014-05-06 17:01:30 +00:00
#define ENCODE_CHUNK(buffer, s) \
do { \
char *eptr = buffer; \
const char *vptr = s; \
for (; *vptr && eptr < buffer + sizeof(buffer); vptr++) { \
if (strchr("^;", *vptr)) { \
/* We use ^XX, instead of %XX because '%' is a special character in SQL */ \
snprintf(eptr, buffer + sizeof(buffer) - eptr, "^%02hhX", *vptr); \
eptr += 3; \
} else { \
*eptr++ = *vptr; \
} \
} \
if (eptr < buffer + sizeof(buffer)) { \
*eptr = '\0'; \
} else { \
buffer[sizeof(buffer) - 1] = '\0'; \
} \
} while(0)
2010-07-17 17:39:28 +00:00
static void decode_chunk ( char * chunk )
{
for (; * chunk ; chunk ++ ) {
2010-12-16 09:06:20 +00:00
if ( * chunk == '^' && strchr ( "0123456789ABCDEF" , chunk [ 1 ]) && strchr ( "0123456789ABCDEF" , chunk [ 2 ])) {
2014-05-09 22:28:40 +00:00
sscanf ( chunk + 1 , "%02hhX" , ( unsigned char * ) chunk );
2010-07-17 17:39:28 +00:00
memmove ( chunk + 1 , chunk + 3 , strlen ( chunk + 3 ) + 1 );
}
}
}
2014-03-28 04:27:02 +00:00
static inline int is_text ( const struct odbc_cache_columns * column )
{
return column -> type == SQL_CHAR || column -> type == SQL_VARCHAR || column -> type == SQL_LONGVARCHAR
|| column -> type == SQL_WCHAR || column -> type == SQL_WVARCHAR || column -> type == SQL_WLONGVARCHAR ;
}
2007-08-07 19:14:45 +00:00
static SQLHSTMT custom_prepare ( struct odbc_obj * obj , void * data )
{
2008-06-05 19:07:27 +00:00
int res , x = 1 , count = 0 ;
2007-08-07 19:14:45 +00:00
struct custom_prepare_struct * cps = data ;
const char * newparam , * newval ;
2010-07-17 17:39:28 +00:00
char encodebuf [ 1024 ];
2007-08-07 19:14:45 +00:00
SQLHSTMT stmt ;
va_list ap ;
2007-08-07 21:00:10 +00:00
2007-08-07 19:14:45 +00:00
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! \n " );
return NULL ;
}
2014-05-09 22:28:40 +00:00
ast_debug ( 1 , "Skip: %llu; SQL: %s \n " , cps -> skip , cps -> sql );
2009-01-28 22:48:01 +00:00
2007-08-07 19:14:45 +00:00
res = SQLPrepare ( stmt , ( unsigned char * ) cps -> sql , SQL_NTS );
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
ast_log ( LOG_WARNING , "SQL Prepare failed![%s] \n " , cps -> sql );
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
return NULL ;
}
2012-04-17 21:10:50 +00:00
va_copy ( ap , cps -> ap );
2007-08-07 19:14:45 +00:00
while (( newparam = va_arg ( ap , const char * ))) {
newval = va_arg ( ap , const char * );
2009-01-28 22:48:01 +00:00
if (( 1LL << count ++ ) & cps -> skip ) {
2014-05-09 22:28:40 +00:00
ast_debug ( 1 , "Skipping field '%s'='%s' (%llo/%llo) \n " , newparam , newval , 1ULL << ( count - 1 ), cps -> skip );
2008-06-05 19:07:27 +00:00
continue ;
}
2009-01-28 22:48:01 +00:00
ast_debug ( 1 , "Parameter %d ('%s') = '%s' \n " , x , newparam , newval );
2010-07-17 17:39:28 +00:00
if ( strchr ( newval , ';' ) || strchr ( newval , '^' )) {
2014-05-06 17:01:30 +00:00
ENCODE_CHUNK ( encodebuf , newval );
2010-07-17 17:39:28 +00:00
ast_string_field_set ( cps , encoding [ x ], encodebuf );
newval = cps -> encoding [ x ];
}
2007-08-07 19:14:45 +00:00
SQLBindParameter ( stmt , x ++ , SQL_PARAM_INPUT , SQL_C_CHAR , SQL_CHAR , strlen ( newval ), 0 , ( void * ) newval , 0 , NULL );
}
va_end ( ap );
2014-05-06 17:01:30 +00:00
if ( ! ast_strlen_zero ( cps -> extra )) {
2014-05-07 17:48:55 +00:00
const char * newval = cps -> extra ;
if ( strchr ( newval , ';' ) || strchr ( newval , '^' )) {
ENCODE_CHUNK ( encodebuf , newval );
ast_string_field_set ( cps , encoding [ x ], encodebuf );
newval = cps -> encoding [ x ];
2014-05-06 17:01:30 +00:00
}
2014-05-07 17:48:55 +00:00
SQLBindParameter ( stmt , x ++ , SQL_PARAM_INPUT , SQL_C_CHAR , SQL_CHAR , strlen ( newval ), 0 , ( void * ) newval , 0 , NULL );
2014-05-06 17:01:30 +00:00
}
2007-08-07 19:14:45 +00:00
return stmt ;
}
2007-07-23 14:32:04 +00:00
/*!
* \brief Excute an SQL query and return ast_variable list
* \param database
* \param table
2007-09-05 16:31:39 +00:00
* \param ap list containing one or more field/operator/value set.
*
2007-07-23 14:32:04 +00:00
* Select database and preform query on table, prepare the sql statement
* Sub-in the values to the prepared statement and execute it. Return results
* as a ast_variable list.
*
* \retval var on success
* \retval NULL on failure
*/
2004-10-07 19:57:50 +00:00
static struct ast_variable * realtime_odbc ( const char * database , const char * table , va_list ap )
2004-07-08 20:16:28 +00:00
{
2006-04-18 18:16:32 +00:00
struct odbc_obj * obj ;
2004-10-05 06:46:11 +00:00
SQLHSTMT stmt ;
2004-11-22 05:03:03 +00:00
char sql [ 1024 ];
2004-10-05 06:46:11 +00:00
char coltitle [ 256 ];
2014-05-28 11:36:01 +00:00
struct ast_str * rowdata = ast_str_thread_get ( & rowdata_buf , 128 );
2004-11-22 05:03:03 +00:00
char * op ;
2011-05-05 22:44:52 +00:00
const char * newparam ;
2004-10-05 06:46:11 +00:00
char * stringp ;
char * chunk ;
SQLSMALLINT collen ;
int res ;
int x ;
struct ast_variable * var = NULL , * prev = NULL ;
SQLULEN colsize ;
SQLSMALLINT colcount = 0 ;
SQLSMALLINT datatype ;
SQLSMALLINT decimaldigits ;
SQLSMALLINT nullable ;
2006-12-19 14:57:45 +00:00
SQLLEN indicator ;
2004-10-07 19:57:50 +00:00
va_list aq ;
2007-08-07 21:00:10 +00:00
struct custom_prepare_struct cps = { . sql = sql };
2010-07-23 16:19:21 +00:00
struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
2007-08-07 21:00:10 +00:00
2010-07-17 17:39:28 +00:00
if ( ! table ) {
2004-10-05 06:46:11 +00:00
return NULL ;
2010-07-17 17:39:28 +00:00
}
2004-10-05 06:46:11 +00:00
2010-07-23 16:19:21 +00:00
obj = ast_odbc_request_obj2 ( database , connected_flag );
2004-10-05 06:46:11 +00:00
2007-08-07 19:14:45 +00:00
if ( ! obj ) {
ast_log ( LOG_ERROR , "No database handle available with the name of '%s' (check res_odbc.conf) \n " , database );
2004-10-05 06:46:11 +00:00
return NULL ;
}
2012-04-17 21:10:50 +00:00
va_copy ( aq , ap );
2004-10-07 19:57:50 +00:00
newparam = va_arg ( aq , const char * );
2008-07-10 21:23:23 +00:00
if ( ! newparam ) {
2012-04-17 21:10:50 +00:00
va_end ( aq );
2008-07-10 21:23:23 +00:00
ast_odbc_release_obj ( obj );
2004-10-07 19:57:50 +00:00
return NULL ;
2008-07-10 21:23:23 +00:00
}
2011-05-05 22:44:52 +00:00
va_arg ( aq , const char * );
2007-07-11 18:35:36 +00:00
op = ! strchr ( newparam , ' ' ) ? " =" : "" ;
2007-11-25 17:50:07 +00:00
snprintf ( sql , sizeof ( sql ), "SELECT * FROM %s WHERE %s%s ?%s" , table , newparam , op ,
strcasestr ( newparam , "LIKE" ) && ! ast_odbc_backslash_is_escape ( obj ) ? " ESCAPE ' \\ '" : "" );
2004-10-07 19:57:50 +00:00
while (( newparam = va_arg ( aq , const char * ))) {
2007-07-11 18:35:36 +00:00
op = ! strchr ( newparam , ' ' ) ? " =" : "" ;
snprintf ( sql + strlen ( sql ), sizeof ( sql ) - strlen ( sql ), " AND %s%s ?%s" , newparam , op ,
2007-11-25 17:50:07 +00:00
strcasestr ( newparam , "LIKE" ) && ! ast_odbc_backslash_is_escape ( obj ) ? " ESCAPE ' \\ '" : "" );
2011-05-05 22:44:52 +00:00
va_arg ( aq , const char * );
2004-10-07 19:57:50 +00:00
}
va_end ( aq );
2007-07-06 16:14:39 +00:00
2014-05-27 19:46:48 +00:00
if ( ast_string_field_init ( & cps , 256 )) {
ast_odbc_release_obj ( obj );
return NULL ;
}
2012-04-17 21:10:50 +00:00
va_copy ( cps . ap , ap );
2007-08-07 19:14:45 +00:00
stmt = ast_odbc_prepare_and_execute ( obj , custom_prepare , & cps );
2012-04-17 21:10:50 +00:00
va_end ( cps . ap );
2014-05-27 19:46:48 +00:00
ast_string_field_free_memory ( & cps );
2004-10-05 06:46:11 +00:00
2007-08-07 19:14:45 +00:00
if ( ! stmt ) {
2006-09-20 04:57:20 +00:00
ast_odbc_release_obj ( obj );
2004-10-05 06:46:11 +00:00
return NULL ;
}
2005-10-31 21:31:25 +00:00
res = SQLNumResultCols ( stmt , & colcount );
2004-10-05 06:46:11 +00:00
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
2005-10-31 21:31:25 +00:00
ast_log ( LOG_WARNING , "SQL Column Count error! \n [%s] \n\n " , sql );
2004-10-05 06:46:11 +00:00
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
2006-09-20 04:57:20 +00:00
ast_odbc_release_obj ( obj );
2004-10-05 06:46:11 +00:00
return NULL ;
}
2005-10-31 21:31:25 +00:00
res = SQLFetch ( stmt );
if ( res == SQL_NO_DATA ) {
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
2006-09-20 04:57:20 +00:00
ast_odbc_release_obj ( obj );
2007-08-07 19:14:45 +00:00
return NULL ;
2005-10-31 21:31:25 +00:00
}
2004-10-05 06:46:11 +00:00
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
2005-10-31 21:31:25 +00:00
ast_log ( LOG_WARNING , "SQL Fetch error! \n [%s] \n\n " , sql );
2004-10-05 06:46:11 +00:00
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
2006-09-20 04:57:20 +00:00
ast_odbc_release_obj ( obj );
2004-10-05 06:46:11 +00:00
return NULL ;
}
2006-04-18 18:16:32 +00:00
for ( x = 0 ; x < colcount ; x ++ ) {
2011-03-10 05:54:53 +00:00
colsize = 0 ;
2005-10-31 21:31:25 +00:00
collen = sizeof ( coltitle );
2006-03-01 17:53:05 +00:00
res = SQLDescribeCol ( stmt , x + 1 , ( unsigned char * ) coltitle , sizeof ( coltitle ), & collen ,
2005-10-31 21:31:25 +00:00
& datatype , & colsize , & decimaldigits , & nullable );
2004-10-05 06:46:11 +00:00
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
2005-10-31 21:31:25 +00:00
ast_log ( LOG_WARNING , "SQL Describe Column error! \n [%s] \n\n " , sql );
if ( var )
ast_variables_destroy ( var );
2006-09-20 04:57:20 +00:00
ast_odbc_release_obj ( obj );
2004-10-05 06:46:11 +00:00
return NULL ;
}
2005-04-22 01:59:53 +00:00
2014-05-28 11:36:01 +00:00
ast_str_reset ( rowdata );
2005-10-31 21:31:25 +00:00
indicator = 0 ;
2014-05-28 11:36:01 +00:00
res = SQLGetData ( stmt , x + 1 , SQL_CHAR , ast_str_buffer ( rowdata ), ast_str_size ( rowdata ), & indicator );
ast_str_update ( rowdata );
if ( indicator == SQL_NULL_DATA ) {
ast_str_reset ( rowdata );
} else if ( ! ast_str_strlen ( rowdata )) {
2007-01-07 16:21:12 +00:00
/* Because we encode the empty string for a NULL, we will encode
* actual empty strings as a string containing a single whitespace. */
2014-05-28 11:36:01 +00:00
ast_str_set ( & rowdata , - 1 , "%s" , " " );
} else if (( res == SQL_SUCCESS ) || ( res == SQL_SUCCESS_WITH_INFO )) {
if ( indicator != ast_str_strlen ( rowdata )) {
/* If the available space was not enough to contain the row data enlarge and read in the rest */
ast_str_make_space ( & rowdata , indicator + 1 );
res = SQLGetData ( stmt , x + 1 , SQL_CHAR , ast_str_buffer ( rowdata ) + ast_str_strlen ( rowdata ),
ast_str_size ( rowdata ) - ast_str_strlen ( rowdata ), & indicator );
ast_str_update ( rowdata );
}
2007-01-07 16:21:12 +00:00
}
2005-04-22 01:59:53 +00:00
2005-10-31 21:31:25 +00:00
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
ast_log ( LOG_WARNING , "SQL Get Data error! \n [%s] \n\n " , sql );
if ( var )
ast_variables_destroy ( var );
2006-09-20 04:57:20 +00:00
ast_odbc_release_obj ( obj );
2005-10-31 21:31:25 +00:00
return NULL ;
}
2014-05-28 11:36:01 +00:00
stringp = ast_str_buffer ( rowdata );
2010-07-17 17:39:28 +00:00
while ( stringp ) {
2005-10-31 21:31:25 +00:00
chunk = strsep ( & stringp , ";" );
2007-08-07 19:14:45 +00:00
if ( ! ast_strlen_zero ( ast_strip ( chunk ))) {
2010-07-17 17:39:28 +00:00
if ( strchr ( chunk , '^' )) {
decode_chunk ( chunk );
}
2007-08-07 19:14:45 +00:00
if ( prev ) {
2007-08-29 20:55:40 +00:00
prev -> next = ast_variable_new ( coltitle , chunk , "" );
2010-07-17 17:39:28 +00:00
if ( prev -> next ) {
2007-08-07 19:14:45 +00:00
prev = prev -> next ;
2010-07-17 17:39:28 +00:00
}
} else {
2007-08-29 20:55:40 +00:00
prev = var = ast_variable_new ( coltitle , chunk , "" );
2010-07-17 17:39:28 +00:00
}
2007-08-07 19:14:45 +00:00
}
2004-10-05 06:46:11 +00:00
}
}
2007-08-07 19:14:45 +00:00
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
2006-09-20 04:57:20 +00:00
ast_odbc_release_obj ( obj );
2004-10-05 06:46:11 +00:00
return var ;
}
2007-07-23 14:32:04 +00:00
/*!
* \brief Excute an Select query and return ast_config list
* \param database
* \param table
2007-09-05 16:31:39 +00:00
* \param ap list containing one or more field/operator/value set.
*
2007-07-23 14:32:04 +00:00
* Select database and preform query on table, prepare the sql statement
* Sub-in the values to the prepared statement and execute it.
* Execute this prepared query against several ODBC connected databases.
* Return results as an ast_config variable.
*
* \retval var on success
* \retval NULL on failure
*/
2004-11-23 17:58:59 +00:00
static struct ast_config * realtime_multi_odbc ( const char * database , const char * table , va_list ap )
{
2006-04-18 18:16:32 +00:00
struct odbc_obj * obj ;
2004-11-23 17:58:59 +00:00
SQLHSTMT stmt ;
char sql [ 1024 ];
char coltitle [ 256 ];
2014-05-28 11:36:01 +00:00
struct ast_str * rowdata = ast_str_thread_get ( & rowdata_buf , 128 );
2012-05-31 18:39:30 +00:00
const char * initfield ;
2004-11-23 17:58:59 +00:00
char * op ;
2011-05-05 22:44:52 +00:00
const char * newparam ;
2004-11-23 17:58:59 +00:00
char * stringp ;
char * chunk ;
SQLSMALLINT collen ;
int res ;
int x ;
2005-01-25 06:10:20 +00:00
struct ast_variable * var = NULL ;
2004-11-23 17:58:59 +00:00
struct ast_config * cfg = NULL ;
struct ast_category * cat = NULL ;
2010-07-23 16:19:21 +00:00
struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
2004-11-23 17:58:59 +00:00
SQLULEN colsize ;
SQLSMALLINT colcount = 0 ;
SQLSMALLINT datatype ;
SQLSMALLINT decimaldigits ;
SQLSMALLINT nullable ;
2006-12-19 14:57:45 +00:00
SQLLEN indicator ;
2007-08-07 21:00:10 +00:00
struct custom_prepare_struct cps = { . sql = sql };
2004-11-23 17:58:59 +00:00
va_list aq ;
2007-08-07 21:00:10 +00:00
2014-05-27 19:46:48 +00:00
if ( ! table ) {
2010-07-17 17:39:28 +00:00
return NULL ;
}
2007-06-28 19:41:18 +00:00
2010-07-23 16:19:21 +00:00
obj = ast_odbc_request_obj2 ( database , connected_flag );
2010-07-17 17:39:28 +00:00
if ( ! obj ) {
2004-11-23 17:58:59 +00:00
return NULL ;
2010-07-17 17:39:28 +00:00
}
2004-11-23 17:58:59 +00:00
2012-04-17 21:10:50 +00:00
va_copy ( aq , ap );
2004-11-23 17:58:59 +00:00
newparam = va_arg ( aq , const char * );
if ( ! newparam ) {
2012-04-17 21:10:50 +00:00
va_end ( aq );
2006-09-20 04:57:20 +00:00
ast_odbc_release_obj ( obj );
2004-11-23 17:58:59 +00:00
return NULL ;
}
2012-04-17 21:10:50 +00:00
2004-11-24 03:07:08 +00:00
initfield = ast_strdupa ( newparam );
2012-04-17 21:10:50 +00:00
if (( op = strchr ( initfield , ' ' ))) {
2004-11-24 03:07:08 +00:00
* op = '\0' ;
2012-04-17 21:10:50 +00:00
}
2011-05-05 22:44:52 +00:00
va_arg ( aq , const char * );
2007-07-11 18:35:36 +00:00
op = ! strchr ( newparam , ' ' ) ? " =" : "" ;
2007-11-23 17:09:06 +00:00
snprintf ( sql , sizeof ( sql ), "SELECT * FROM %s WHERE %s%s ?%s" , table , newparam , op ,
2007-11-25 17:50:07 +00:00
strcasestr ( newparam , "LIKE" ) && ! ast_odbc_backslash_is_escape ( obj ) ? " ESCAPE ' \\ '" : "" );
2004-11-23 17:58:59 +00:00
while (( newparam = va_arg ( aq , const char * ))) {
2007-07-11 18:35:36 +00:00
op = ! strchr ( newparam , ' ' ) ? " =" : "" ;
snprintf ( sql + strlen ( sql ), sizeof ( sql ) - strlen ( sql ), " AND %s%s ?%s" , newparam , op ,
2007-11-25 17:50:07 +00:00
strcasestr ( newparam , "LIKE" ) && ! ast_odbc_backslash_is_escape ( obj ) ? " ESCAPE ' \\ '" : "" );
2011-05-05 22:44:52 +00:00
va_arg ( aq , const char * );
2004-11-23 17:58:59 +00:00
}
va_end ( aq );
2007-07-06 16:14:39 +00:00
2012-05-31 18:39:30 +00:00
snprintf ( sql + strlen ( sql ), sizeof ( sql ) - strlen ( sql ), " ORDER BY %s" , initfield );
2012-04-17 21:10:50 +00:00
2014-05-27 19:46:48 +00:00
if ( ast_string_field_init ( & cps , 256 )) {
ast_odbc_release_obj ( obj );
return NULL ;
}
2012-04-17 21:10:50 +00:00
va_copy ( cps . ap , ap );
2007-08-07 19:14:45 +00:00
stmt = ast_odbc_prepare_and_execute ( obj , custom_prepare , & cps );
2012-04-17 21:10:50 +00:00
va_end ( cps . ap );
2014-05-27 19:46:48 +00:00
ast_string_field_free_memory ( & cps );
2004-11-23 17:58:59 +00:00
2007-08-07 19:14:45 +00:00
if ( ! stmt ) {
2006-09-20 04:57:20 +00:00
ast_odbc_release_obj ( obj );
2004-11-23 17:58:59 +00:00
return NULL ;
}
res = SQLNumResultCols ( stmt , & colcount );
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
ast_log ( LOG_WARNING , "SQL Column Count error! \n [%s] \n\n " , sql );
2006-04-18 18:16:32 +00:00
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
2006-09-20 04:57:20 +00:00
ast_odbc_release_obj ( obj );
2004-11-23 17:58:59 +00:00
return NULL ;
}
2005-01-25 06:10:20 +00:00
cfg = ast_config_new ();
if ( ! cfg ) {
ast_log ( LOG_WARNING , "Out of memory! \n " );
2006-04-18 18:16:32 +00:00
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
2006-09-20 04:57:20 +00:00
ast_odbc_release_obj ( obj );
2005-01-25 06:10:20 +00:00
return NULL ;
}
2005-10-31 21:31:25 +00:00
while (( res = SQLFetch ( stmt )) != SQL_NO_DATA ) {
2004-11-23 17:58:59 +00:00
var = NULL ;
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
ast_log ( LOG_WARNING , "SQL Fetch error! \n [%s] \n\n " , sql );
continue ;
}
2007-08-29 20:55:40 +00:00
cat = ast_category_new ( "" , "" , 99999 );
2005-01-25 06:10:20 +00:00
if ( ! cat ) {
ast_log ( LOG_WARNING , "Out of memory! \n " );
continue ;
}
2004-11-23 17:58:59 +00:00
for ( x = 0 ; x < colcount ; x ++ ) {
2011-03-10 05:54:53 +00:00
colsize = 0 ;
2004-11-23 17:58:59 +00:00
collen = sizeof ( coltitle );
2006-03-01 17:53:05 +00:00
res = SQLDescribeCol ( stmt , x + 1 , ( unsigned char * ) coltitle , sizeof ( coltitle ), & collen ,
2004-11-23 17:58:59 +00:00
& datatype , & colsize , & decimaldigits , & nullable );
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
ast_log ( LOG_WARNING , "SQL Describe Column error! \n [%s] \n\n " , sql );
2005-01-25 06:10:20 +00:00
ast_category_destroy ( cat );
2011-08-22 18:33:27 +00:00
goto next_sql_fetch ;
2004-11-23 17:58:59 +00:00
}
2005-04-22 01:59:53 +00:00
2014-05-28 11:36:01 +00:00
ast_str_reset ( rowdata );
2005-04-22 01:59:53 +00:00
indicator = 0 ;
2014-05-28 11:36:01 +00:00
res = SQLGetData ( stmt , x + 1 , SQL_CHAR , ast_str_buffer ( rowdata ), ast_str_size ( rowdata ), & indicator );
ast_str_update ( rowdata );
if ( indicator == SQL_NULL_DATA ) {
2005-04-22 01:59:53 +00:00
continue ;
2014-05-28 11:36:01 +00:00
}
if (( res == SQL_SUCCESS ) || ( res == SQL_SUCCESS_WITH_INFO )) {
if ( indicator != ast_str_strlen ( rowdata )) {
/* If the available space was not enough to contain the row data enlarge and read in the rest */
ast_str_make_space ( & rowdata , indicator + 1 );
res = SQLGetData ( stmt , x + 1 , SQL_CHAR , ast_str_buffer ( rowdata ) + ast_str_strlen ( rowdata ),
ast_str_size ( rowdata ) - ast_str_strlen ( rowdata ), & indicator );
ast_str_update ( rowdata );
}
}
2005-04-22 01:59:53 +00:00
2004-11-23 17:58:59 +00:00
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
ast_log ( LOG_WARNING , "SQL Get Data error! \n [%s] \n\n " , sql );
2005-01-25 06:10:20 +00:00
ast_category_destroy ( cat );
2011-08-22 18:33:27 +00:00
goto next_sql_fetch ;
2004-11-23 17:58:59 +00:00
}
2014-05-28 11:36:01 +00:00
stringp = ast_str_buffer ( rowdata );
2010-07-17 17:39:28 +00:00
while ( stringp ) {
2004-11-23 17:58:59 +00:00
chunk = strsep ( & stringp , ";" );
2005-11-08 01:55:31 +00:00
if ( ! ast_strlen_zero ( ast_strip ( chunk ))) {
2010-07-17 17:39:28 +00:00
if ( strchr ( chunk , '^' )) {
decode_chunk ( chunk );
}
2012-05-31 18:39:30 +00:00
if ( ! strcmp ( initfield , coltitle )) {
2005-01-25 06:10:20 +00:00
ast_category_rename ( cat , chunk );
2010-07-17 17:39:28 +00:00
}
2007-08-29 20:55:40 +00:00
var = ast_variable_new ( coltitle , chunk , "" );
2005-01-25 06:10:20 +00:00
ast_variable_append ( cat , var );
2004-11-23 17:58:59 +00:00
}
}
2004-11-24 03:07:08 +00:00
}
2005-01-25 06:10:20 +00:00
ast_category_append ( cfg , cat );
2011-08-22 18:33:27 +00:00
next_sql_fetch :;
2004-11-23 17:58:59 +00:00
}
2006-04-18 18:16:32 +00:00
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
2006-09-20 04:57:20 +00:00
ast_odbc_release_obj ( obj );
2004-11-23 17:58:59 +00:00
return cfg ;
}
2007-07-23 14:32:04 +00:00
/*!
* \brief Excute an UPDATE query
* \param database
* \param table
* \param keyfield where clause field
* \param lookup value of field for where clause
2007-09-05 16:31:39 +00:00
* \param ap list containing one or more field/value set(s).
*
2007-07-23 14:32:04 +00:00
* Update a database table, prepare the sql statement using keyfield and lookup
* control the number of records to change. All values to be changed are stored in ap list.
* Sub-in the values to the prepared statement and execute it.
*
* \retval number of rows affected
* \retval -1 on failure
*/
2004-10-05 06:46:11 +00:00
static int update_odbc ( const char * database , const char * table , const char * keyfield , const char * lookup , va_list ap )
{
2006-04-18 18:16:32 +00:00
struct odbc_obj * obj ;
2004-10-05 06:46:11 +00:00
SQLHSTMT stmt ;
char sql [ 256 ];
SQLLEN rowcount = 0 ;
2014-03-28 04:27:02 +00:00
const char * newparam , * newval ;
int res , count = 0 , paramcount = 0 ;
2004-10-05 06:46:11 +00:00
va_list aq ;
2007-08-07 21:00:10 +00:00
struct custom_prepare_struct cps = { . sql = sql , . extra = lookup };
2010-07-17 17:39:28 +00:00
struct odbc_cache_tables * tableptr ;
2011-07-07 16:18:18 +00:00
struct odbc_cache_columns * column = NULL ;
2010-07-23 16:19:21 +00:00
struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
2007-08-07 21:00:10 +00:00
2014-03-28 04:27:02 +00:00
if ( ! table || ! keyfield ) {
2010-07-17 17:39:28 +00:00
return - 1 ;
}
tableptr = ast_odbc_find_table ( database , table );
2010-07-23 16:19:21 +00:00
if ( ! ( obj = ast_odbc_request_obj2 ( database , connected_flag ))) {
2008-06-10 21:14:58 +00:00
ast_odbc_release_table ( tableptr );
2004-10-05 06:46:11 +00:00
return - 1 ;
2008-06-05 19:07:27 +00:00
}
2004-10-05 06:46:11 +00:00
2014-03-28 04:27:02 +00:00
if ( tableptr && ! ast_odbc_find_column ( tableptr , keyfield )) {
ast_log ( LOG_WARNING , "Key field '%s' does not exist in table '%s@%s'. Update will fail \n " , keyfield , table , database );
2004-10-05 06:46:11 +00:00
}
2008-06-05 19:07:27 +00:00
2014-03-28 04:27:02 +00:00
va_copy ( aq , ap );
2008-06-05 19:07:27 +00:00
2014-03-28 04:27:02 +00:00
snprintf ( sql , sizeof ( sql ), "UPDATE %s SET " , table );
2004-10-05 06:46:11 +00:00
while (( newparam = va_arg ( aq , const char * ))) {
2014-03-28 04:27:02 +00:00
newval = va_arg ( aq , const char * );
if (( tableptr && ( column = ast_odbc_find_column ( tableptr , newparam ))) || count >= 64 ) {
if ( paramcount ++ ) {
snprintf ( sql + strlen ( sql ), sizeof ( sql ) - strlen ( sql ), ", " );
}
/* NULL test for non-text columns */
if ( count < 64 && ast_strlen_zero ( newval ) && column -> nullable && ! is_text ( column ) && ! ast_odbc_allow_empty_string_in_nontext ( obj )) {
snprintf ( sql + strlen ( sql ), sizeof ( sql ) - strlen ( sql ), "%s=NULL" , newparam );
2011-07-07 16:18:18 +00:00
cps . skip |= ( 1LL << count );
} else {
2014-03-28 04:27:02 +00:00
/* Value is not an empty string, or column accepts empty strings, or we couldn't fit any more into cps.skip (count >= 64 ?!). */
snprintf ( sql + strlen ( sql ), sizeof ( sql ) - strlen ( sql ), "%s=?" , newparam );
2011-07-07 16:18:18 +00:00
}
2009-01-28 22:48:01 +00:00
} else { /* the column does not exist in the table */
cps . skip |= ( 1LL << count );
2008-06-05 19:07:27 +00:00
}
2014-03-28 04:27:02 +00:00
++ count ;
2004-10-05 06:46:11 +00:00
}
va_end ( aq );
snprintf ( sql + strlen ( sql ), sizeof ( sql ) - strlen ( sql ), " WHERE %s=?" , keyfield );
2008-06-10 21:14:58 +00:00
ast_odbc_release_table ( tableptr );
2004-10-05 06:46:11 +00:00
2014-05-27 19:46:48 +00:00
if ( ast_string_field_init ( & cps , 256 )) {
ast_odbc_release_obj ( obj );
return - 1 ;
}
2012-04-17 21:10:50 +00:00
va_copy ( cps . ap , ap );
2007-08-07 19:14:45 +00:00
stmt = ast_odbc_prepare_and_execute ( obj , custom_prepare , & cps );
2012-04-17 21:10:50 +00:00
va_end ( cps . ap );
2014-05-27 19:46:48 +00:00
ast_string_field_free_memory ( & cps );
2004-10-05 06:46:11 +00:00
2007-08-07 19:14:45 +00:00
if ( ! stmt ) {
2006-09-20 04:57:20 +00:00
ast_odbc_release_obj ( obj );
2004-10-05 06:46:11 +00:00
return - 1 ;
}
res = SQLRowCount ( stmt , & rowcount );
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
2006-09-20 04:57:20 +00:00
ast_odbc_release_obj ( obj );
2004-10-05 06:46:11 +00:00
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
ast_log ( LOG_WARNING , "SQL Row Count error! \n [%s] \n\n " , sql );
return - 1 ;
}
2010-07-17 17:39:28 +00:00
if ( rowcount >= 0 ) {
return ( int ) rowcount ;
}
2005-04-22 03:29:03 +00:00
2004-10-05 06:46:11 +00:00
return - 1 ;
}
2008-10-14 00:08:52 +00:00
struct update2_prepare_struct {
const char * database ;
const char * table ;
va_list ap ;
};
static SQLHSTMT update2_prepare ( struct odbc_obj * obj , void * data )
{
int res , x = 1 , first = 1 ;
struct update2_prepare_struct * ups = data ;
const char * newparam , * newval ;
struct ast_str * sql = ast_str_thread_get ( & sql_buf , 16 );
SQLHSTMT stmt ;
va_list ap ;
struct odbc_cache_tables * tableptr = ast_odbc_find_table ( ups -> database , ups -> table );
if ( ! sql ) {
if ( tableptr ) {
ast_odbc_release_table ( tableptr );
}
return NULL ;
}
if ( ! tableptr ) {
ast_log ( LOG_ERROR , "Could not retrieve metadata for table '%s@%s'. Update will fail! \n " , ups -> table , ups -> database );
return NULL ;
}
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! \n " );
ast_odbc_release_table ( tableptr );
return NULL ;
}
ast_str_set ( & sql , 0 , "UPDATE %s SET " , ups -> table );
/* Start by finding the second set of parameters */
va_copy ( ap , ups -> ap );
while (( newparam = va_arg ( ap , const char * ))) {
newval = va_arg ( ap , const char * );
}
while (( newparam = va_arg ( ap , const char * ))) {
newval = va_arg ( ap , const char * );
2012-06-11 15:23:30 +00:00
if ( ast_odbc_find_column ( tableptr , newparam )) {
2008-10-14 00:08:52 +00:00
ast_str_append ( & sql , 0 , "%s%s=? " , first ? "" : ", " , newparam );
SQLBindParameter ( stmt , x ++ , SQL_PARAM_INPUT , SQL_C_CHAR , SQL_CHAR , strlen ( newval ), 0 , ( void * ) newval , 0 , NULL );
first = 0 ;
} else {
ast_log ( LOG_NOTICE , "Not updating column '%s' in '%s@%s' because that column does not exist! \n " , newparam , ups -> table , ups -> database );
}
}
va_end ( ap );
ast_str_append ( & sql , 0 , "WHERE" );
first = 1 ;
2012-04-17 21:10:50 +00:00
/* Restart search, because we need to add the search parameters */
va_copy ( ap , ups -> ap );
2008-10-14 00:08:52 +00:00
while (( newparam = va_arg ( ap , const char * ))) {
newval = va_arg ( ap , const char * );
2012-06-11 15:23:30 +00:00
if ( ! ast_odbc_find_column ( tableptr , newparam )) {
2012-04-17 21:10:50 +00:00
va_end ( ap );
2008-10-14 00:08:52 +00:00
ast_log ( LOG_ERROR , "One or more of the criteria columns '%s' on '%s@%s' for this update does not exist! \n " , newparam , ups -> table , ups -> database );
ast_odbc_release_table ( tableptr );
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
return NULL ;
}
ast_str_append ( & sql , 0 , "%s %s=?" , first ? "" : " AND" , newparam );
SQLBindParameter ( stmt , x ++ , SQL_PARAM_INPUT , SQL_C_CHAR , SQL_CHAR , strlen ( newval ), 0 , ( void * ) newval , 0 , NULL );
first = 0 ;
}
va_end ( ap );
/* Done with the table metadata */
ast_odbc_release_table ( tableptr );
2008-12-13 08:36:35 +00:00
res = SQLPrepare ( stmt , ( unsigned char * ) ast_str_buffer ( sql ), SQL_NTS );
2008-10-14 00:08:52 +00:00
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
2008-12-13 08:36:35 +00:00
ast_log ( LOG_WARNING , "SQL Prepare failed![%s] \n " , ast_str_buffer ( sql ));
2008-10-14 00:08:52 +00:00
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
return NULL ;
}
return stmt ;
}
/*!
* \brief Execute an UPDATE query
* \param database
* \param table
* \param ap list containing one or more field/value set(s).
*
* Update a database table, preparing the sql statement from a list of
* key/value pairs specified in ap. The lookup pairs are specified first
* and are separated from the update pairs by a sentinel value.
* Sub-in the values to the prepared statement and execute it.
*
* \retval number of rows affected
* \retval -1 on failure
*/
static int update2_odbc ( const char * database , const char * table , va_list ap )
{
struct odbc_obj * obj ;
SQLHSTMT stmt ;
struct update2_prepare_struct ups = { . database = database , . table = table , };
struct ast_str * sql ;
int res ;
SQLLEN rowcount = 0 ;
if ( ! ( obj = ast_odbc_request_obj ( database , 0 ))) {
return - 1 ;
}
2012-04-17 21:10:50 +00:00
va_copy ( ups . ap , ap );
2008-10-14 00:08:52 +00:00
if ( ! ( stmt = ast_odbc_prepare_and_execute ( obj , update2_prepare , & ups ))) {
2012-04-17 21:10:50 +00:00
va_end ( ups . ap );
2008-10-14 00:08:52 +00:00
ast_odbc_release_obj ( obj );
return - 1 ;
}
2012-04-17 21:10:50 +00:00
va_end ( ups . ap );
2008-10-14 00:08:52 +00:00
res = SQLRowCount ( stmt , & rowcount );
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
ast_odbc_release_obj ( obj );
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
/* Since only a single thread can access this memory, we can retrieve what would otherwise be lost. */
sql = ast_str_thread_get ( & sql_buf , 16 );
2008-12-13 08:36:35 +00:00
ast_log ( LOG_WARNING , "SQL Row Count error! \n [%s] \n " , ast_str_buffer ( sql ));
2008-10-14 00:08:52 +00:00
return - 1 ;
}
if ( rowcount >= 0 ) {
return ( int ) rowcount ;
}
return - 1 ;
}
2007-08-17 13:45:44 +00:00
/*!
* \brief Excute an INSERT query
* \param database
* \param table
* \param ap list containing one or more field/value set(s)
2007-09-05 16:31:39 +00:00
*
2007-08-17 13:45:44 +00:00
* Insert a new record into database table, prepare the sql statement.
* All values to be changed are stored in ap list.
* Sub-in the values to the prepared statement and execute it.
*
* \retval number of rows affected
* \retval -1 on failure
*/
static int store_odbc ( const char * database , const char * table , va_list ap )
{
struct odbc_obj * obj ;
SQLHSTMT stmt ;
char sql [ 256 ];
char keys [ 256 ];
char vals [ 256 ];
SQLLEN rowcount = 0 ;
2011-05-05 22:44:52 +00:00
const char * newparam ;
2007-08-17 13:45:44 +00:00
int res ;
va_list aq ;
struct custom_prepare_struct cps = { . sql = sql , . extra = NULL };
2010-07-23 16:19:21 +00:00
struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
2007-08-17 13:45:44 +00:00
2012-04-17 21:10:50 +00:00
if ( ! table ) {
2007-08-17 13:45:44 +00:00
return - 1 ;
2012-04-17 21:10:50 +00:00
}
2007-08-17 13:45:44 +00:00
2010-07-23 16:19:21 +00:00
obj = ast_odbc_request_obj2 ( database , connected_flag );
2012-04-17 21:10:50 +00:00
if ( ! obj ) {
2007-08-17 13:45:44 +00:00
return - 1 ;
2012-04-17 21:10:50 +00:00
}
va_copy ( aq , ap );
2007-08-17 13:45:44 +00:00
newparam = va_arg ( aq , const char * );
if ( ! newparam ) {
2012-04-17 21:10:50 +00:00
va_end ( aq );
2007-08-17 13:45:44 +00:00
ast_odbc_release_obj ( obj );
return - 1 ;
}
2011-05-05 22:44:52 +00:00
va_arg ( aq , const char * );
2007-08-17 13:45:44 +00:00
snprintf ( keys , sizeof ( keys ), "%s" , newparam );
2007-10-01 15:23:19 +00:00
ast_copy_string ( vals , "?" , sizeof ( vals ));
2007-08-17 13:45:44 +00:00
while (( newparam = va_arg ( aq , const char * ))) {
snprintf ( keys + strlen ( keys ), sizeof ( keys ) - strlen ( keys ), ", %s" , newparam );
snprintf ( vals + strlen ( vals ), sizeof ( vals ) - strlen ( vals ), ", ?" );
2011-05-05 22:44:52 +00:00
va_arg ( aq , const char * );
2007-08-17 13:45:44 +00:00
}
va_end ( aq );
snprintf ( sql , sizeof ( sql ), "INSERT INTO %s (%s) VALUES (%s)" , table , keys , vals );
2014-05-27 19:46:48 +00:00
if ( ast_string_field_init ( & cps , 256 )) {
ast_odbc_release_obj ( obj );
return - 1 ;
}
2012-04-17 21:10:50 +00:00
va_copy ( cps . ap , ap );
2007-08-17 13:45:44 +00:00
stmt = ast_odbc_prepare_and_execute ( obj , custom_prepare , & cps );
2012-04-17 21:10:50 +00:00
va_end ( cps . ap );
2014-05-27 19:46:48 +00:00
ast_string_field_free_memory ( & cps );
2007-08-17 13:45:44 +00:00
if ( ! stmt ) {
ast_odbc_release_obj ( obj );
return - 1 ;
}
res = SQLRowCount ( stmt , & rowcount );
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
ast_odbc_release_obj ( obj );
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
ast_log ( LOG_WARNING , "SQL Row Count error! \n [%s] \n\n " , sql );
return - 1 ;
}
if ( rowcount >= 0 )
return ( int ) rowcount ;
return - 1 ;
}
/*!
* \brief Excute an DELETE query
* \param database
* \param table
* \param keyfield where clause field
* \param lookup value of field for where clause
* \param ap list containing one or more field/value set(s)
2007-09-05 16:31:39 +00:00
*
* Delete a row from a database table, prepare the sql statement using keyfield and lookup
2007-08-17 13:45:44 +00:00
* control the number of records to change. Additional params to match rows are stored in ap list.
* Sub-in the values to the prepared statement and execute it.
*
* \retval number of rows affected
* \retval -1 on failure
*/
static int destroy_odbc ( const char * database , const char * table , const char * keyfield , const char * lookup , va_list ap )
{
struct odbc_obj * obj ;
SQLHSTMT stmt ;
char sql [ 256 ];
SQLLEN rowcount = 0 ;
2011-05-05 22:44:52 +00:00
const char * newparam ;
2007-08-17 13:45:44 +00:00
int res ;
va_list aq ;
struct custom_prepare_struct cps = { . sql = sql , . extra = lookup };
2010-07-23 16:19:21 +00:00
struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
2007-08-17 13:45:44 +00:00
2012-04-17 21:10:50 +00:00
if ( ! table ) {
2007-08-17 13:45:44 +00:00
return - 1 ;
2012-04-17 21:10:50 +00:00
}
2007-08-17 13:45:44 +00:00
2010-07-23 16:19:21 +00:00
obj = ast_odbc_request_obj2 ( database , connected_flag );
2012-04-17 21:10:50 +00:00
if ( ! obj ) {
2007-08-17 13:45:44 +00:00
return - 1 ;
2012-04-17 21:10:50 +00:00
}
2007-08-17 13:45:44 +00:00
snprintf ( sql , sizeof ( sql ), "DELETE FROM %s WHERE " , table );
2012-04-17 21:10:50 +00:00
va_copy ( aq , ap );
2007-08-17 13:45:44 +00:00
while (( newparam = va_arg ( aq , const char * ))) {
snprintf ( sql + strlen ( sql ), sizeof ( sql ) - strlen ( sql ), "%s=? AND " , newparam );
2011-05-05 22:44:52 +00:00
va_arg ( aq , const char * );
2007-08-17 13:45:44 +00:00
}
va_end ( aq );
snprintf ( sql + strlen ( sql ), sizeof ( sql ) - strlen ( sql ), "%s=?" , keyfield );
2014-05-27 19:46:48 +00:00
if ( ast_string_field_init ( & cps , 256 )) {
ast_odbc_release_obj ( obj );
return - 1 ;
}
2012-04-17 21:10:50 +00:00
va_copy ( cps . ap , ap );
2007-08-17 13:45:44 +00:00
stmt = ast_odbc_prepare_and_execute ( obj , custom_prepare , & cps );
2012-04-17 21:10:50 +00:00
va_end ( cps . ap );
2014-05-27 19:46:48 +00:00
ast_string_field_free_memory ( & cps );
2007-08-17 13:45:44 +00:00
if ( ! stmt ) {
ast_odbc_release_obj ( obj );
return - 1 ;
}
res = SQLRowCount ( stmt , & rowcount );
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
ast_odbc_release_obj ( obj );
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
ast_log ( LOG_WARNING , "SQL Row Count error! \n [%s] \n\n " , sql );
return - 1 ;
}
if ( rowcount >= 0 )
return ( int ) rowcount ;
return - 1 ;
}
2006-04-18 18:16:32 +00:00
struct config_odbc_obj {
char * sql ;
unsigned long cat_metric ;
char category [ 128 ];
char var_name [ 128 ];
2014-05-28 11:36:01 +00:00
char * var_val ;
unsigned long var_val_size ;
2006-12-19 14:57:45 +00:00
SQLLEN err ;
2006-04-18 18:16:32 +00:00
};
2014-05-28 11:36:01 +00:00
static SQLHSTMT length_determination_odbc_prepare ( struct odbc_obj * obj , void * data )
{
struct config_odbc_obj * q = data ;
SQLHSTMT sth ;
int res ;
res = SQLAllocHandle ( SQL_HANDLE_STMT , obj -> con , & sth );
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
ast_verb ( 4 , "Failure in AllocStatement %d \n " , res );
return NULL ;
}
res = SQLPrepare ( sth , ( unsigned char * ) q -> sql , SQL_NTS );
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
ast_verb ( 4 , "Error in PREPARE %d \n " , res );
SQLFreeHandle ( SQL_HANDLE_STMT , sth );
return NULL ;
}
SQLBindCol ( sth , 1 , SQL_C_ULONG , & q -> var_val_size , sizeof ( q -> var_val_size ), & q -> err );
return sth ;
}
2006-04-18 18:16:32 +00:00
static SQLHSTMT config_odbc_prepare ( struct odbc_obj * obj , void * data )
{
struct config_odbc_obj * q = data ;
SQLHSTMT sth ;
int res ;
res = SQLAllocHandle ( SQL_HANDLE_STMT , obj -> con , & sth );
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
2007-07-26 15:49:18 +00:00
ast_verb ( 4 , "Failure in AllocStatement %d \n " , res );
2006-04-18 18:16:32 +00:00
return NULL ;
}
res = SQLPrepare ( sth , ( unsigned char * ) q -> sql , SQL_NTS );
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
2007-07-26 15:49:18 +00:00
ast_verb ( 4 , "Error in PREPARE %d \n " , res );
2006-04-18 18:16:32 +00:00
SQLFreeHandle ( SQL_HANDLE_STMT , sth );
return NULL ;
}
2007-05-03 00:10:38 +00:00
SQLBindCol ( sth , 1 , SQL_C_ULONG , & q -> cat_metric , sizeof ( q -> cat_metric ), & q -> err );
SQLBindCol ( sth , 2 , SQL_C_CHAR , q -> category , sizeof ( q -> category ), & q -> err );
SQLBindCol ( sth , 3 , SQL_C_CHAR , q -> var_name , sizeof ( q -> var_name ), & q -> err );
2014-05-28 11:36:01 +00:00
SQLBindCol ( sth , 4 , SQL_C_CHAR , q -> var_val , q -> var_val_size , & q -> err );
2006-04-18 18:16:32 +00:00
return sth ;
}
2008-03-11 22:55:16 +00:00
static struct ast_config * config_odbc ( const char * database , const char * table , const char * file , struct ast_config * cfg , struct ast_flags flags , const char * sugg_incl , const char * who_asked )
2004-10-05 06:46:11 +00:00
{
2005-01-25 06:10:20 +00:00
struct ast_variable * new_v ;
struct ast_category * cur_cat ;
2004-10-05 06:46:11 +00:00
int res = 0 ;
2006-04-18 18:16:32 +00:00
struct odbc_obj * obj ;
2007-05-03 00:10:38 +00:00
char sqlbuf [ 1024 ] = "" ;
2007-05-03 00:25:50 +00:00
char * sql = sqlbuf ;
2007-05-03 00:10:38 +00:00
size_t sqlleft = sizeof ( sqlbuf );
2006-04-18 18:16:32 +00:00
unsigned int last_cat_metric = 0 ;
2007-05-03 00:10:38 +00:00
SQLSMALLINT rowcount = 0 ;
2004-07-08 20:16:28 +00:00
SQLHSTMT stmt ;
2005-01-25 06:10:20 +00:00
char last [ 128 ] = "" ;
2006-04-18 18:16:32 +00:00
struct config_odbc_obj q ;
2007-08-16 21:09:46 +00:00
struct ast_flags loader_flags = { 0 };
2010-07-23 16:19:21 +00:00
struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
2006-04-18 18:16:32 +00:00
memset ( & q , 0 , sizeof ( q ));
2004-07-08 20:16:28 +00:00
if ( ! file || ! strcmp ( file , "res_config_odbc.conf" ))
2004-12-18 22:04:07 +00:00
return NULL ; /* cant configure myself with myself ! */
2004-07-08 20:16:28 +00:00
2010-07-23 16:19:21 +00:00
obj = ast_odbc_request_obj2 ( database , connected_flag );
2004-07-08 20:16:28 +00:00
if ( ! obj )
return NULL ;
2014-05-28 11:36:01 +00:00
q . sql = sqlbuf ;
ast_build_string ( & sql , & sqlleft , "SELECT MAX(LENGTH(var_val)) FROM %s WHERE filename='%s'" , table , file );
stmt = ast_odbc_prepare_and_execute ( obj , length_determination_odbc_prepare , & q );
if ( ! stmt ) {
ast_log ( LOG_WARNING , "SQL select error! \n [%s] \n\n " , sql );
ast_odbc_release_obj ( obj );
return NULL ;
}
res = SQLNumResultCols ( stmt , & rowcount );
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
ast_log ( LOG_WARNING , "SQL NumResultCols error! \n [%s] \n\n " , sql );
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
ast_odbc_release_obj ( obj );
return NULL ;
}
if ( ! rowcount ) {
ast_log ( LOG_NOTICE , "found nothing \n " );
ast_odbc_release_obj ( obj );
return cfg ;
}
/* There will be only one result for this, the maximum length of a variable value */
if ( SQLFetch ( stmt ) == SQL_NO_DATA ) {
ast_log ( LOG_NOTICE , "Failed to determine maximum length of a configuration value \n " );
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
ast_odbc_release_obj ( obj );
return NULL ;
}
/* Reset stuff to a fresh state for the actual query which will retrieve all configuration */
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
sql = sqlbuf ;
sqlleft = sizeof ( sqlbuf );
2007-05-03 00:10:38 +00:00
ast_build_string ( & sql , & sqlleft , "SELECT cat_metric, category, var_name, var_val FROM %s " , table );
ast_build_string ( & sql , & sqlleft , "WHERE filename='%s' AND commented=0 " , file );
ast_build_string ( & sql , & sqlleft , "ORDER BY cat_metric DESC, var_metric ASC, category, var_name " );
2014-05-28 11:36:01 +00:00
q . var_val_size += 1 ;
q . var_val = ast_malloc ( q . var_val_size );
if ( ! q . var_val ) {
ast_log ( LOG_WARNING , "Could not create buffer for reading in configuration values for '%s' \n " , file );
ast_odbc_release_obj ( obj );
return NULL ;
}
2004-07-08 20:16:28 +00:00
2006-09-20 04:57:20 +00:00
stmt = ast_odbc_prepare_and_execute ( obj , config_odbc_prepare , & q );
2006-04-18 18:16:32 +00:00
if ( ! stmt ) {
ast_log ( LOG_WARNING , "SQL select error! \n [%s] \n\n " , sql );
2006-09-20 04:57:20 +00:00
ast_odbc_release_obj ( obj );
2014-05-28 11:36:01 +00:00
ast_free ( q . var_val );
2004-07-08 20:16:28 +00:00
return NULL ;
2004-06-11 00:12:35 +00:00
}
2006-04-18 18:16:32 +00:00
res = SQLNumResultCols ( stmt , & rowcount );
2004-06-11 00:12:35 +00:00
2004-07-08 20:16:28 +00:00
if (( res != SQL_SUCCESS ) && ( res != SQL_SUCCESS_WITH_INFO )) {
2006-04-18 18:16:32 +00:00
ast_log ( LOG_WARNING , "SQL NumResultCols error! \n [%s] \n\n " , sql );
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
2006-09-20 04:57:20 +00:00
ast_odbc_release_obj ( obj );
2014-05-28 11:36:01 +00:00
ast_free ( q . var_val );
2004-10-05 06:46:11 +00:00
return NULL ;
}
2005-01-25 06:10:20 +00:00
if ( ! rowcount ) {
2006-04-18 18:16:32 +00:00
ast_log ( LOG_NOTICE , "found nothing \n " );
2006-09-20 04:57:20 +00:00
ast_odbc_release_obj ( obj );
2014-05-28 11:36:01 +00:00
ast_free ( q . var_val );
2005-01-25 06:10:20 +00:00
return cfg ;
}
2004-07-08 20:16:28 +00:00
2005-01-25 06:10:20 +00:00
cur_cat = ast_config_get_current_category ( cfg );
2004-07-08 20:16:28 +00:00
2005-01-25 06:10:20 +00:00
while (( res = SQLFetch ( stmt )) != SQL_NO_DATA ) {
2006-04-18 18:16:32 +00:00
if ( ! strcmp ( q . var_name , "#include" )) {
2008-03-11 22:55:16 +00:00
if ( ! ast_config_internal_load ( q . var_val , cfg , loader_flags , "" , who_asked )) {
2006-04-18 18:16:32 +00:00
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
2006-09-20 04:57:20 +00:00
ast_odbc_release_obj ( obj );
2014-05-28 11:36:01 +00:00
ast_free ( q . var_val );
2005-01-25 06:10:20 +00:00
return NULL ;
2004-07-08 20:16:28 +00:00
}
2005-01-25 06:10:20 +00:00
continue ;
}
2006-04-18 18:16:32 +00:00
if ( strcmp ( last , q . category ) || last_cat_metric != q . cat_metric ) {
2007-08-29 20:55:40 +00:00
cur_cat = ast_category_new ( q . category , "" , 99999 );
2005-01-25 06:10:20 +00:00
if ( ! cur_cat ) {
ast_log ( LOG_WARNING , "Out of memory! \n " );
break ;
}
2006-04-18 18:16:32 +00:00
strcpy ( last , q . category );
last_cat_metric = q . cat_metric ;
2005-01-25 06:10:20 +00:00
ast_category_append ( cfg , cur_cat );
2004-07-08 20:16:28 +00:00
}
2007-08-29 20:55:40 +00:00
new_v = ast_variable_new ( q . var_name , q . var_val , "" );
2005-01-25 06:10:20 +00:00
ast_variable_append ( cur_cat , new_v );
2004-07-08 20:16:28 +00:00
}
2004-06-11 00:12:35 +00:00
2006-04-18 18:16:32 +00:00
SQLFreeHandle ( SQL_HANDLE_STMT , stmt );
2006-09-20 04:57:20 +00:00
ast_odbc_release_obj ( obj );
2014-05-28 11:36:01 +00:00
ast_free ( q . var_val );
2005-01-25 06:10:20 +00:00
return cfg ;
2004-07-08 20:16:28 +00:00
}
2004-06-11 00:12:35 +00:00
2008-06-09 22:51:59 +00:00
#define warn_length(col, size) ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' is not long enough to contain realtime data (needs %d)\n", table, database, col->name, size)
#define warn_type(col, type) ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' is of the incorrect type (%d) to contain the required realtime data\n", table, database, col->name, col->type)
2008-06-05 19:07:27 +00:00
static int require_odbc ( const char * database , const char * table , va_list ap )
{
2008-06-10 21:14:58 +00:00
struct odbc_cache_tables * tableptr = ast_odbc_find_table ( database , table );
2008-06-05 19:07:27 +00:00
struct odbc_cache_columns * col ;
char * elm ;
int type , size ;
if ( ! tableptr ) {
return - 1 ;
}
while (( elm = va_arg ( ap , char * ))) {
type = va_arg ( ap , require_type );
size = va_arg ( ap , int );
/* Check if the field matches the criteria */
AST_RWLIST_TRAVERSE ( & tableptr -> columns , col , list ) {
if ( strcmp ( col -> name , elm ) == 0 ) {
/* Type check, first. Some fields are more particular than others */
switch ( col -> type ) {
case SQL_CHAR :
case SQL_VARCHAR :
case SQL_LONGVARCHAR :
2009-09-10 18:17:14 +00:00
#ifdef HAVE_ODBC_WCHAR
2009-06-19 15:47:55 +00:00
case SQL_WCHAR :
case SQL_WVARCHAR :
case SQL_WLONGVARCHAR :
2009-09-10 18:17:14 +00:00
#endif
2008-06-05 19:07:27 +00:00
case SQL_BINARY :
case SQL_VARBINARY :
case SQL_LONGVARBINARY :
case SQL_GUID :
2008-06-09 22:51:59 +00:00
#define CHECK_SIZE(n) \
if (col->size < n) { \
warn_length(col, n); \
} \
break;
switch ( type ) {
case RQ_UINTEGER1 : CHECK_SIZE ( 3 ) /* 255 */
case RQ_INTEGER1 : CHECK_SIZE ( 4 ) /* -128 */
case RQ_UINTEGER2 : CHECK_SIZE ( 5 ) /* 65535 */
case RQ_INTEGER2 : CHECK_SIZE ( 6 ) /* -32768 */
case RQ_UINTEGER3 : /* 16777215 */
case RQ_INTEGER3 : CHECK_SIZE ( 8 ) /* -8388608 */
case RQ_DATE : /* 2008-06-09 */
case RQ_UINTEGER4 : CHECK_SIZE ( 10 ) /* 4200000000 */
case RQ_INTEGER4 : CHECK_SIZE ( 11 ) /* -2100000000 */
case RQ_DATETIME : /* 2008-06-09 16:03:47 */
case RQ_UINTEGER8 : CHECK_SIZE ( 19 ) /* trust me */
case RQ_INTEGER8 : CHECK_SIZE ( 20 ) /* ditto */
case RQ_FLOAT :
case RQ_CHAR : CHECK_SIZE ( size )
2008-06-05 19:07:27 +00:00
}
2008-06-09 22:51:59 +00:00
#undef CHECK_SIZE
2008-06-05 19:07:27 +00:00
break ;
case SQL_TYPE_DATE :
if ( type != RQ_DATE ) {
warn_type ( col , type );
}
break ;
case SQL_TYPE_TIMESTAMP :
case SQL_TIMESTAMP :
if ( type != RQ_DATE && type != RQ_DATETIME ) {
warn_type ( col , type );
}
break ;
case SQL_BIT :
2008-06-09 22:51:59 +00:00
warn_length ( col , size );
break ;
#define WARN_TYPE_OR_LENGTH(n) \
if (!ast_rq_is_int(type)) { \
warn_type(col, type); \
} else { \
warn_length(col, n); \
}
case SQL_TINYINT :
if ( type != RQ_UINTEGER1 ) {
WARN_TYPE_OR_LENGTH ( size )
}
break ;
case SQL_C_STINYINT :
if ( type != RQ_INTEGER1 ) {
WARN_TYPE_OR_LENGTH ( size )
2008-06-05 19:07:27 +00:00
}
2008-06-09 22:51:59 +00:00
break ;
case SQL_C_USHORT :
if ( type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_UINTEGER2 ) {
WARN_TYPE_OR_LENGTH ( size )
}
break ;
case SQL_SMALLINT :
case SQL_C_SSHORT :
if ( type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_INTEGER2 ) {
WARN_TYPE_OR_LENGTH ( size )
}
break ;
case SQL_C_ULONG :
if ( type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
type != RQ_INTEGER4 ) {
WARN_TYPE_OR_LENGTH ( size )
}
break ;
case SQL_INTEGER :
case SQL_C_SLONG :
if ( type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
2009-06-19 15:47:55 +00:00
type != RQ_INTEGER4 ) {
2008-06-09 22:51:59 +00:00
WARN_TYPE_OR_LENGTH ( size )
}
break ;
case SQL_C_UBIGINT :
if ( type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
type != RQ_INTEGER8 ) {
WARN_TYPE_OR_LENGTH ( size )
}
break ;
case SQL_BIGINT :
case SQL_C_SBIGINT :
if ( type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
2009-06-19 15:47:55 +00:00
type != RQ_INTEGER8 ) {
2008-06-09 22:51:59 +00:00
WARN_TYPE_OR_LENGTH ( size )
2008-06-05 19:07:27 +00:00
}
break ;
2008-06-09 22:51:59 +00:00
#undef WARN_TYPE_OR_LENGTH
2008-06-05 19:07:27 +00:00
case SQL_NUMERIC :
case SQL_DECIMAL :
case SQL_FLOAT :
case SQL_REAL :
case SQL_DOUBLE :
2008-06-09 22:51:59 +00:00
if ( ! ast_rq_is_int ( type ) && type != RQ_FLOAT ) {
2008-06-05 19:07:27 +00:00
warn_type ( col , type );
}
break ;
default :
2008-06-09 22:51:59 +00:00
ast_log ( LOG_WARNING , "Realtime table %s@%s: column type (%d) unrecognized for column '%s' \n " , table , database , col -> type , elm );
2008-06-05 19:07:27 +00:00
}
break ;
}
}
if ( ! col ) {
2008-06-09 22:51:59 +00:00
ast_log ( LOG_WARNING , "Realtime table %s@%s requires column '%s', but that column does not exist! \n " , table , database , elm );
2008-06-05 19:07:27 +00:00
}
}
AST_RWLIST_UNLOCK ( & tableptr -> columns );
return 0 ;
}
2008-06-09 22:51:59 +00:00
#undef warn_length
#undef warn_type
2008-06-05 19:07:27 +00:00
2011-09-01 17:31:34 +00:00
static int unload_odbc ( const char * a , const char * b )
{
return ast_odbc_clear_cache ( a , b );
}
2005-01-25 06:10:20 +00:00
static struct ast_config_engine odbc_engine = {
. name = "odbc" ,
. load_func = config_odbc ,
. realtime_func = realtime_odbc ,
. realtime_multi_func = realtime_multi_odbc ,
2007-08-17 13:45:44 +00:00
. store_func = store_odbc ,
. destroy_func = destroy_odbc ,
2008-06-05 19:07:27 +00:00
. update_func = update_odbc ,
2008-10-14 00:08:52 +00:00
. update2_func = update2_odbc ,
2008-06-05 19:07:27 +00:00
. require_func = require_odbc ,
2011-09-01 17:31:34 +00:00
. unload_func = unload_odbc ,
2005-01-25 06:10:20 +00:00
};
2006-08-21 02:11:39 +00:00
static int unload_module ( void )
2004-06-11 00:12:35 +00:00
{
2005-01-25 06:10:20 +00:00
ast_config_engine_deregister ( & odbc_engine );
2008-06-05 19:07:27 +00:00
2007-07-26 15:49:18 +00:00
ast_verb ( 1 , "res_config_odbc unloaded. \n " );
2004-06-11 00:12:35 +00:00
return 0 ;
}
2006-08-21 02:11:39 +00:00
static int load_module ( void )
2004-06-11 00:12:35 +00:00
{
2005-01-25 06:10:20 +00:00
ast_config_engine_register ( & odbc_engine );
2007-07-26 15:49:18 +00:00
ast_verb ( 1 , "res_config_odbc loaded. \n " );
2004-06-11 00:12:35 +00:00
return 0 ;
}
2008-06-05 19:07:27 +00:00
static int reload_module ( void )
{
return 0 ;
}
2010-07-20 19:35:02 +00:00
AST_MODULE_INFO ( ASTERISK_GPL_KEY , AST_MODFLAG_LOAD_ORDER , "Realtime ODBC configuration" ,
2006-08-21 02:11:39 +00:00
. load = load_module ,
. unload = unload_module ,
2008-06-05 19:07:27 +00:00
. reload = reload_module ,
2010-07-20 19:35:02 +00:00
. load_pri = AST_MODPRI_REALTIME_DRIVER ,
2006-08-21 02:11:39 +00:00
);