psyclpc/src/pkg-odbc.c

1129 lines
28 KiB
C

#include <stdlib.h>
#include <sql.h>
#include <sqlext.h>
#include "pkg-odbc.h"
#include "svalue.h"
#include "array.h"
#include "mapping.h"
#include "xalloc.h"
#define DEBUG_FUNC 1
//#define ODBC_DEBUG (DEBUG_FUNC)
#define ODBC_DEBUG 0
#define MEM_CHECK( x ) if ( !x ) { \
errorf( "Out of memory.\n" ); \
return( NULL ); \
}
/* prototypes: */
void destruct_odbc_environment( void );
static void push_db_connection( hDBC * handle );
static hDBC * pop_db_connection( void );
static int dispose_db_connection( hDBC * handle );
static void dispose_column_meta_data( COL_META_DATA * column );
static char * extract_diagnostics_info( SQLSMALLINT type, SQLHANDLE handle );
static void raise_critical_sql_exception( hDBC * handle, char * msg );
static SQLSMALLINT map_column_type( SQLSMALLINT col_type, SQLSMALLINT digits );
/*************************************************************************
* handle to the ODBC Environment
*************************************************************************/
static hODBCENV * hODBCEnv;
static char *
init_odbc_environment( void )
{
#if ODBC_DEBUG & DEBUG_FUNC
printf( "call init_odbc_environment( )\n" );
#endif
SQLRETURN ret;
if ( hODBCEnv ) // already initialized
return( NULL );
hODBCEnv = pxalloc( sizeof( hODBCENV ) );
MEM_CHECK( hODBCEnv );
ret = SQLAllocHandle( SQL_HANDLE_ENV, SQL_NULL_HANDLE, &(hODBCEnv->hODBCEnv) );
if ( !SQL_SUCCEEDED( ret ) ) {
destruct_odbc_environment();
return( "Allocation of ODBC Environment failed.\n" );
}
ret = SQLSetEnvAttr( hODBCEnv->hODBCEnv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0 );
if ( !SQL_SUCCEEDED( ret ) ) {
destruct_odbc_environment();
return( "Could not set ODBC Environment Attributes.\n" );
}
hODBCEnv->next_hDBCon_ID = 1;
hODBCEnv->hDBCons = NULL;
#if ODBC_DEBUG & DEBUG_FUNC
printf( "return init_odbc_environment( )\n" );
#endif
return( NULL );
}
void
destruct_odbc_environment( void )
{
if ( !hODBCEnv )
return;
if ( hODBCEnv->hODBCEnv ) {
SQLFreeHandle( SQL_HANDLE_ENV, hODBCEnv->hODBCEnv );
hODBCEnv->hODBCEnv = NULL;
}
while ( hODBCEnv->hDBCons ) {
dispose_db_connection( pop_db_connection() );
}
pfree( hODBCEnv );
hODBCEnv = NULL;
}
/*************************************************************************
* allocate_db_connection( ) . allocates a hDBC struct with the given ID
* push_db_connection( ) ..... adds an allocated hDBC struct to the DB
* connections
* pop_db_connection( ) ...... removes an allocated hDBC struct from
* the DB connections
* get_db_connection_by_id( ) returns the requested handle and moves it on
* top of hODBCEnv->hDBCons
* dispose_db_connection( ) .. disposes the passed connection
*************************************************************************/
static hDBC *
allocate_db_connection( int ID )
{
hDBC * handle;
if ( !ID )
return( NULL );
handle = pxalloc( sizeof( hDBC ) );
MEM_CHECK( handle );
handle->ID = ID;
handle->name = NULL;
handle->prev = NULL;
handle->next = NULL;
handle->hDBCon = NULL;
handle->hStmt = NULL;
handle->colcnt = 0;
handle->rowcnt = 0;
handle->columns = NULL;
return( handle );
}
static void
push_db_connection( hDBC * handle )
{
if ( !handle || !hODBCEnv )
return;
if ( hODBCEnv->hDBCons ) {
hODBCEnv->hDBCons->prev = handle;
handle->next = hODBCEnv->hDBCons;
}
hODBCEnv->hDBCons = handle;
}
static hDBC *
pop_db_connection( void )
{
hDBC * tmp;
if ( !hODBCEnv || !hODBCEnv->hDBCons )
return( NULL );
tmp = hODBCEnv->hDBCons;
hODBCEnv->hDBCons = tmp->next;
tmp->next = NULL;
return( tmp );
}
static hDBC *
get_db_connection_by_id( int ID )
{
hDBC * handle,
* prev,
* next;
if ( !hODBCEnv->hDBCons )
return( NULL );
handle = hODBCEnv->hDBCons;
while( (handle->ID != ID ) &&
(handle = handle->next) );
if ( !handle )
return( NULL );
if ( handle == hODBCEnv->hDBCons )
return( handle );
prev = handle->prev;
next = handle->next;
prev->next = next;
if ( next )
next->prev = prev;
hODBCEnv->hDBCons->prev = handle;
handle->prev = NULL;
handle->next = hODBCEnv->hDBCons;
hODBCEnv->hDBCons = handle;
return( handle );
}
static int
dispose_db_connection( hDBC * handle )
{
int ret;
int i;
hDBC * prev,
* next;
if ( !handle )
return( 0 );
ret = handle->ID;
pfree( handle->name );
handle->name = NULL;
if ( handle->hDBCon ) {
SQLDisconnect( handle->hDBCon );
SQLFreeHandle( SQL_HANDLE_DBC, handle->hDBCon );
handle->hDBCon = NULL;
}
if ( handle->columns ) {
for ( i = 0; i < handle->colcnt; ++i ) {
dispose_column_meta_data( handle->columns[ i ] );
}
pfree( handle->columns );
handle->columns = NULL;
}
pfree( handle );
return ret;
}
/*************************************************************************
* allocate_column_meta_data( ) . allocates a COL_META_DATA struct
* dispose_column_meta_data( ) .. disposes a COL_META_DATA struct
*************************************************************************/
static COL_META_DATA *
allocate_column_meta_data( void )
{
COL_META_DATA * column;
column = pxalloc( sizeof( COL_META_DATA ) );
MEM_CHECK( column );
column->nr = 0;
column->name = NULL;
column->type = 0;
column->data.number_v = NULL;
column->data.double_v = NULL;
column->data.string_v = NULL;
return( column );
}
static void
dispose_column_meta_data( COL_META_DATA * column )
{
if ( !column )
return;
pfree( column->name );
column->name = NULL;
pfree( column->data.number_v );
column->data.number_v = NULL;
pfree( column->data.double_v );
column->data.double_v = NULL;
pfree( column->data.string_v );
column->data.string_v = NULL;
pfree( column );
}
/*************************************************************************
* f_sql_odbc_enabled( void )
*
* returns TRUE if ODBC was enabled and the ODBC Environment is valid.
*************************************************************************/
svalue_t *
f_sql_odbc_enabled( svalue_t * argv, int argc )
{
#if ODBC_DEBUG & DEBUG_FUNC
printf( "call f_sql_odbc_enabled( )\n" );
#endif
char * err;
err = init_odbc_environment();
argv++;
if ( err ) {
put_number( argv, 0 );
} else {
put_number( argv, 1 );
}
#if ODBC_DEBUG & DEBUG_FUNC
printf( "ret f_sql_odbc_enabled( )\n" );
#endif
return( argv );
}
/*************************************************************************
* f_sql_odbc_datasources( void )
*
* returns a mapping of all defined datasources.
* ([ name : description ])
*************************************************************************/
svalue_t *
f_sql_odbc_datasources( svalue_t * argv, int argc )
{
#if ODBC_DEBUG & DEBUG_FUNC
printf( "call f_sql_odbc_datasources( )\n" );
#endif
SQLRETURN ret;
char * err;
char dsName[ 256 ],
dsDescription[ 256 ];
SQLSMALLINT dsNameLen,
dsDescriptionLen;
mapping_t * map;
svalue_t * key;
svalue_t * value;
argv++;
err = init_odbc_environment();
if ( err ) {
errorf( err );
return( NULL );
}
map = allocate_mapping( 0, 1 );
MEM_CHECK( map );
do {
ret = SQLDataSources( hODBCEnv->hODBCEnv, SQL_FETCH_NEXT, dsName, 256, &dsNameLen,
dsDescription, 256, &dsDescriptionLen );
if ( SQL_SUCCEEDED( ret ) ) {
key = pxalloc( sizeof( svalue_t ) );
put_malloced_string( key, string_copy( dsName ) );
value = get_map_lvalue( map, key );
MEM_CHECK( value );
put_malloced_string( value, string_copy( dsDescription ) );
}
} while( SQL_SUCCEEDED( ret ) );
if ( ret != SQL_NO_DATA ) {
char * err;
err = extract_diagnostics_info( SQL_HANDLE_ENV, hODBCEnv->hODBCEnv );
free_mapping( map );
errorf( (err?err:"Failed to fetch datasource information.\n") );
return( NULL );
}
put_mapping( argv, map );
#if ODBC_DEBUG & DEBUG_FUNC
printf( "ret f_sql_odbc_datasources( )\n" );
#endif
return( argv );
}
/*************************************************************************
* f_sql_handles( void )
*
* returns an array containing the IDs of the currently opened connections
*************************************************************************/
svalue_t *
f_sql_handles( svalue_t * argv, int argc )
{
#if ODBC_DEBUG & DEBUG_FUNC
printf( "call f_sql_handles( )\n" );
#endif
int cnt, i;
hDBC * tmp;
vector_t * vec;
argv++;
if ( !hODBCEnv )
init_odbc_environment();
if ( !hODBCEnv ) {
put_number( argv, 0 );
return( argv );
}
if ( !hODBCEnv->hDBCons ) {
vec = allocate_array( 0 );
MEM_CHECK( vec );
put_array( argv, vec );
return( argv );
}
tmp = hODBCEnv->hDBCons;
cnt = 1;
while( (tmp = tmp->next ) )
cnt++;
vec = allocate_array( cnt );
MEM_CHECK( vec );
tmp = hODBCEnv->hDBCons;
for ( i = 0; i < cnt; ++i ) {
put_number( vec->item + i, tmp->ID );
tmp = tmp->next;
}
put_array( argv, vec );
#if ODBC_DEBUG & DEBUG_FUNC
printf( "ret f_sql_handles( )\n" );
#endif
return( argv );
}
/*************************************************************************
* f_sql_connect( string database, void|string user, void|string pwd )
*
* opens a connection to an ODBC database. returns the ID of the
* created handle
*************************************************************************/
svalue_t *
f_sql_connect ( svalue_t * argv, int argc )
{
#if ODBC_DEBUG & DEBUG_FUNC
printf( "call f_sql_connect ( )\n" );
#endif
SQLRETURN ret;
SQLCHAR * database,
* user,
* password;
int pos;
hDBC * handle;
char * err;
database = NULL;
user = NULL;
password = NULL;
switch( argc ) {
case 3 :
TYPE_TESTV3( argv, T_STRING );
password = argv->u.string;
case 2 :
pos = 2 - argc;
TYPE_TESTV2( argv + pos, T_STRING );
user = (argv + pos)->u.string;
case 1 :
pos = 1 - argc;
TYPE_TESTV1( argv + pos, T_STRING );
database = (argv + pos)->u.string;
break;
default:
errorf( "Too many arguments to sql_connect().\n" );
return( NULL );
}
if ( !hODBCEnv && (err = init_odbc_environment()) ) {
raise_critical_sql_exception( handle, err );
return( NULL );
}
handle = allocate_db_connection( hODBCEnv->next_hDBCon_ID++ );
MEM_CHECK( handle );
ret = SQLAllocHandle( SQL_HANDLE_DBC, hODBCEnv->hODBCEnv, &(handle->hDBCon) );
if ( !SQL_SUCCEEDED( ret ) ) {
raise_critical_sql_exception( handle, extract_diagnostics_info( SQL_HANDLE_ENV, hODBCEnv->hODBCEnv ) );
return( NULL );
}
SQLSetConnectAttr( handle->hDBCon, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0 );
SQLSetConnectAttr( handle->hDBCon, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER *)SQL_AUTOCOMMIT_ON, 0 );
ret = SQLConnect( handle->hDBCon, database, SQL_NTS,
(user ? user : (SQLCHAR *)"\0"), SQL_NTS,
(password ? password : (SQLCHAR *)"\0"), SQL_NTS);
if ( !SQL_SUCCEEDED( ret ) ) {
raise_critical_sql_exception( handle, extract_diagnostics_info( SQL_HANDLE_DBC, handle->hDBCon ) );
return( NULL );
}
handle->name = string_copy( database );
push_db_connection( handle );
switch( argc ) {
case 3:
free_string_svalue( argv );
argv--;
case 2:
free_string_svalue( argv );
argv--;
case 1:
free_string_svalue( argv );
}
put_number( argv, handle->ID );
#if ODBC_DEBUG & DEBUG_FUNC
printf( "ret f_sql_connect ( )\n" );
#endif
return( argv );
}
/*************************************************************************
* f_sql_close( int handle )
*
* colses the connection with the given ID
*************************************************************************/
svalue_t *
f_sql_close( svalue_t * argv, int argc )
{
#if ODBC_DEBUG & DEBUG_FUNC
printf( "call f_sql_close( )\n" );
#endif
int id;
hDBC * handle;
TYPE_TEST1( argv, T_NUMBER );
id = argv->u.number;
if ( !(handle = get_db_connection_by_id( id )) ) {
errorf( "Illegal handle for database.\n" );
return( NULL );
}
id = dispose_db_connection( pop_db_connection() );
if ( !hODBCEnv->hDBCons ) { //closing the last connection, close ODBC Environment
destruct_odbc_environment();
}
free_svalue( argv );
put_number( argv, id );
#if ODBC_DEBUG & DEBUG_FUNC
printf( "ret f_sql_close( )\n" );
#endif
return( argv );
}
/*************************************************************************
* f_sql_exec( int handle, string statement )
*
* executes an SQL statement
*************************************************************************/
svalue_t *
f_sql_exec( svalue_t * argv, int argc )
{
#if ODBC_DEBUG & DEBUG_FUNC
printf( "call f_sql_exec( )\n" );
#endif
int id, i;
SQLCHAR * statement;
hDBC * handle;
//SQLSMALLINT cols;
SQLRETURN ret;
TYPE_TEST2( argv, T_STRING );
statement = string_copy( argv->u.string );
free_string_svalue( argv );
argv--;
TYPE_TEST1( argv, T_NUMBER );
id = argv->u.number;
free_svalue( argv );
if ( !(handle = get_db_connection_by_id( id )) ) {
pfree( statement );
errorf( "Illegal handle for database.\n" );
return( NULL );
}
if ( handle->hStmt ) {
//printf( "freeing statement\n" );
ret = SQLFreeStmt( handle->hStmt, SQL_UNBIND );
if ( !SQL_SUCCEEDED( ret ) ) {
//printf( "SQLFreeStmt( handle->hStmt, SQL_UNBIND ) = %d\n", ret );
pfree( statement );
errorf( extract_diagnostics_info( SQL_HANDLE_STMT, handle->hStmt ) );
return( NULL );
}
ret = SQLFreeHandle( SQL_HANDLE_STMT, handle->hStmt );
if ( !SQL_SUCCEEDED( ret ) ) {
//printf( "SQLFreeHandle( SQL_HANDLE_STMT, handle->hStmt ) = %d\n", ret );
pfree( statement );
errorf( extract_diagnostics_info( SQL_HANDLE_STMT, handle->hStmt ) );
return( NULL );
}
}
if ( handle->columns ) {
for ( i = 0; i < handle->colcnt; ++i ) {
dispose_column_meta_data( handle->columns[ i ] );
}
pfree( handle->columns );
handle->columns = NULL;
}
//printf( "allocating statement \n" );
ret = SQLAllocHandle( SQL_HANDLE_STMT, handle->hDBCon, &handle->hStmt );
if ( !SQL_SUCCEEDED( ret ) ) {
pfree( statement );
errorf( extract_diagnostics_info( SQL_HANDLE_DBC, handle->hDBCon ) );
return( NULL );
}
handle->colcnt = 0;
handle->rowcnt = 0;
// printf( "executing\n" );
ret = SQLExecDirect( handle->hStmt, statement, SQL_NTS );
pfree( statement );
if ( !SQL_SUCCEEDED( ret ) ) {
//printf( "XXX: %s\n", extract_diagnostics_info( SQL_HANDLE_STMT, handle->hStmt ) );
put_number( argv, 0 );
return( argv );
}
/* getting number of columns. */
//ret = SQLNumResultCols( handle->hStmt, &cols );
ret = SQLNumResultCols( handle->hStmt, &handle->colcnt );
if ( !SQL_SUCCEEDED( ret ) ) {
put_number( argv, 0 );
return( argv );
}
ret = SQLRowCount( handle->hStmt, &handle->rowcnt );
if ( !SQL_SUCCEEDED( ret ) ) {
put_number( argv, 0 );
return( argv );
}
//handle->colcnt = cols;
if ( handle->colcnt ) {
handle->columns = pxalloc( handle->colcnt * sizeof( COL_META_DATA* ) );
MEM_CHECK( handle->columns );
}
/* fetching meta data */
COL_META_DATA * tmp;
SQLCHAR dColname[ 100 ];
SQLSMALLINT dColnameLen;
SQLSMALLINT dType;
SQLSMALLINT dDDigits;
SQLSMALLINT dNullable;
SQLUINTEGER dColSize;
for ( i = 1; i <= handle->colcnt; ++i ) {
ret = SQLDescribeCol( handle->hStmt, i, dColname, sizeof( dColname ), &dColnameLen, &dType, &dColSize, &dDDigits, &dNullable );
if ( !SQL_SUCCEEDED( ret ) ) {
put_number( argv, 0 );
return( argv );
}
tmp = allocate_column_meta_data();
MEM_CHECK( tmp );
tmp->nr = i;
tmp->name = string_copy( dColname );
tmp->type = map_column_type( dType, dDDigits );
// printf( "[%s] dColSize=%d dDDigits=%d\n", dColname, dColSize, dDDigits );
SQLLEN len;
switch( tmp->type ) {
case T_NUMBER:
tmp->data.number_v = pxalloc( sizeof( SQL_C_LONG ) );
*tmp->data.number_v = 0;
SQLBindCol( handle->hStmt, i, SQL_C_LONG, tmp->data.number_v, 100, &len );
break;
case T_FLOAT:
tmp->data.double_v = pxalloc( sizeof( SQL_C_DOUBLE ) );
*tmp->data.double_v = 0;
SQLBindCol( handle->hStmt, i, SQL_C_DOUBLE, tmp->data.double_v, 100, &len );
break;
default:
tmp->data.string_v = pxalloc( (dColSize + 1) * sizeof( SQLCHAR ) );
SQLBindCol( handle->hStmt, i, SQL_C_CHAR, tmp->data.string_v, (dColSize + 1), &len );
}
handle->columns[ i-1 ] = tmp;
}
put_number( argv, id );
#if ODBC_DEBUG & DEBUG_FUNC
printf( "ret f_sql_exec( )\n" );
#endif
return( argv );
}
/*************************************************************************
* f_sql_column_names( int handle )
*
* returns the returned column names of the last executed statement
*************************************************************************/
svalue_t *
f_sql_column_names( svalue_t * argv, int argc )
{
#if ODBC_DEBUG & DEBUG_FUNC
printf( "call f_sql_column_names( )\n" );
#endif
int id, cnt, i;
hDBC * handle;
vector_t * vec;
TYPE_TEST1( argv, T_NUMBER );
id = argv->u.number;
free_svalue( argv );
if ( !(handle = get_db_connection_by_id( id )) ) {
errorf( "Illegal handle for database.\n" );
return( NULL );
}
if ( !handle->columns ) {
vec = allocate_array( 0 );
MEM_CHECK( vec );
put_array( argv, vec );
return( argv );
}
cnt = handle->colcnt;
vec = allocate_array( cnt );
MEM_CHECK( vec );
for ( i = 0; i < cnt; ++i ) {
if ( handle->columns[ i ] )
put_malloced_string(vec->item + i, string_copy( handle->columns[ i ]->name ) );
}
put_array( argv, vec );
#if ODBC_DEBUG & DEBUG_FUNC
printf( "ret f_sql_column_names( )\n" );
#endif
return( argv );
}
/*************************************************************************
* f_sql_affected_rows( int handle )
*
* returns the number of rows in the resultset
*************************************************************************/
svalue_t *
f_sql_affected_rows( svalue_t * argv, int argc )
{
#if ODBC_DEBUG & DEBUG_FUNC
printf( "call f_sql_affected_rows( )\n" );
#endif
int id;
hDBC * handle;
TYPE_TEST1( argv, T_NUMBER );
id = argv->u.number;
free_svalue( argv );
if ( !(handle = get_db_connection_by_id( id )) ) {
errorf( "Illegal handle for database.\n" );
return( NULL );
}
if ( !handle->hStmt ) {
put_number( argv, 0 );
return( argv );
}
put_number( argv, handle->rowcnt );
#if ODBC_DEBUG & DEBUG_FUNC
printf( "ret f_sql_affected_rows( )\n" );
#endif
return( argv );
}
/*************************************************************************
* mixed f_sql_fetch( int handle, void|int method )
*
* returns one row of the result set either as an array (default)
* or a mapping ( ([ column_name : data ])
*************************************************************************/
static vector_t *
fetch_into_vector( hDBC * handle )
{
int i;
vector_t * vec;
STORE_DOUBLE_USED;
vec = allocate_array( handle->colcnt );
MEM_CHECK( vec );
for( i = 0; i < handle->colcnt; ++i ) {
if ( !handle->columns[ i ] ) continue;
//printf( " fetch_into_vector[%2d] ", i );
switch( handle->columns[ i ]->type ){
case T_FLOAT:
//printf( "float=%f\n", handle->columns[ i ]->data.double_v );
STORE_DOUBLE( vec->item + i, *handle->columns[ i ]->data.double_v );
( vec->item + i)->type = T_FLOAT;
break;
case T_NUMBER:
//printf( "number=%d\n", *handle->columns[ i ]->data.number_v );
put_number( vec->item + i, *handle->columns[ i ]->data.number_v );
break;
case T_STRING:
default :
//printf( "string=%s\n", handle->columns[ i ]->data.string_v );
put_malloced_string( vec->item + i, string_copy( handle->columns[ i ]->data.string_v ) );
break;
}
}
return( vec );
}
static mapping_t *
fetch_into_mapping( hDBC * handle )
{
int i;
mapping_t * map;
svalue_t * key,
* value;
STORE_DOUBLE_USED;
map = allocate_mapping( handle->colcnt, 1 );
MEM_CHECK( map );
for( i = 0; i < handle->colcnt; ++i ) {
if ( !handle->columns[ i ] ) continue;
//printf( " fetch_into_mapping[%2d] ", i );
key = pxalloc( sizeof( svalue_t ) );
MEM_CHECK( key );
put_malloced_string( key, string_copy( handle->columns[ i ]->name ) );
value = get_map_lvalue( map, key );
MEM_CHECK( value );
switch( handle->columns[ i ]->type ){
case T_FLOAT:
//printf( "float=%f\n", handle->columns[ i ]->data.double_v );
STORE_DOUBLE( value, *handle->columns[ i ]->data.double_v );
value->type = T_FLOAT;
break;
case T_NUMBER:
//printf( "number=%d\n", *handle->columns[ i ]->data.number_v );
put_number( value, *handle->columns[ i ]->data.number_v );
break;
case T_STRING:
default :
//printf( "string=%s\n", handle->columns[ i ]->data.string_v );
put_malloced_string( value, string_copy( handle->columns[ i ]->data.string_v ) );
break;
}
}
return( map );
}
svalue_t *
f_sql_fetch( svalue_t * argv, int argc )
{
#if ODBC_DEBUG & DEBUG_FUNC
printf( "call f_sql_fetch( )\n" );
#endif
int id, method = 0;
hDBC * handle;
SQLRETURN ret;
STORE_DOUBLE_USED;
switch( argc ) {
case 2 :
TYPE_TEST2( argv, T_NUMBER );
method = argv->u.number;
free_svalue( argv );
argv--;
case 1 :
TYPE_TEST1( argv, T_NUMBER );
id = argv->u.number;
free_svalue( argv );
break;
default:
errorf( "Too many arguments to sql_fetch().\n" );
return( NULL );
}
if ( !(handle = get_db_connection_by_id( id )) ) {
errorf( "Illegal handle for database.\n" );
return( NULL );
}
if ( !handle->hStmt ) {
put_number( argv, 0 );
return( argv );
}
//printf( "\nFetching ....\n" );
ret = SQLFetch( handle->hStmt );
if ( ret == SQL_NO_DATA ) {
//printf( "NO_DATA\n" );
put_number( argv, 0 );
return( argv );
} else if ( !SQL_SUCCEEDED( ret ) ) {
put_number( argv, 0 );
return( argv );
}
if ( !handle->columns ) {
put_number( argv, 0 );
return( argv );
}
if ( !method ) { //fetch as array
put_array( argv, fetch_into_vector( handle ) );
} else { //fetch as mapping
put_mapping( argv, fetch_into_mapping( handle ) );
}
#if ODBC_DEBUG & DEBUG_FUNC
printf( "ret f_sql_fetch( )\n" );
#endif
return( argv );
}
/*************************************************************************
* string f_sql_error( int handle )
*
* returns the last encountered error
*************************************************************************/
svalue_t *
f_sql_error( svalue_t * argv, int argc )
{
#if ODBC_DEBUG & DEBUG_FUNC
printf( "call f_sql_error( )\n" );
#endif
int id, cnt, i;
hDBC * handle;
char * err;
TYPE_TEST1( argv, T_NUMBER );
id = argv->u.number;
free_svalue( argv );
if ( !(handle = get_db_connection_by_id( id )) ) {
errorf( "Illegal handle for database.\n" );
return( NULL );
}
if ( handle->hStmt ) {
err = extract_diagnostics_info( SQL_HANDLE_STMT, handle->hStmt );
} else {
err = extract_diagnostics_info( SQL_HANDLE_DBC, handle->hDBCon );
}
if ( err ) {
put_malloced_string( argv, err );
} else {
put_number( argv, 0 );
}
#if ODBC_DEBUG & DEBUG_FUNC
printf( "ret f_sql_error( )\n" );
#endif
return( argv );
}
/*************************************************************************
* extracts the dignosticinfo of an handle.
*************************************************************************/
static char *
extract_diagnostics_info( SQLSMALLINT type, SQLHANDLE handle )
{
char ** tmp;
char * ret;
int i,
j = 0;
SQLINTEGER recnum;
SQLGetDiagField( type, handle, 0, SQL_DIAG_NUMBER, &recnum, SQL_IS_INTEGER, 0 );
if ( !recnum )
return( NULL );
tmp = pxalloc( recnum * sizeof( tmp ) );
SQLCHAR state[ 7 ];
SQLINTEGER native;
SQLCHAR text[1000],
buffer[1000];
SQLSMALLINT len;
for ( i = 0; i < recnum; i++ ) {
SQLGetDiagRec( type, handle, i+1, state, &native, text, sizeof( text ), &len );
sprintf( buffer, "[%5s]%s\n", state, text );
tmp[ i ] = string_copy( buffer );
j += strlen( tmp[ i ] );
}
ret = pxalloc( (j + 1) * sizeof( char ) );
for ( i = 0; i < recnum; i++ ) {
if( i ) {
strcat( ret, tmp[ i ] );
} else {
strcpy( ret, tmp[ i ] );
}
pfree( tmp[ i ] );
}
pfree( tmp );
return( ret );
}
/*************************************************************************
* raise_critical_sql_exception() closes the affected DB handle and
* aborts the execution
*************************************************************************/
static void
raise_critical_sql_exception( hDBC * handle, char * msg )
{
if ( handle )
dispose_db_connection( handle );
errorf( ( msg ? msg : "An unknown error occured during the current DB operation.\n" ) );
abort();
}
/*************************************************************************/
/* maps SQL datatypes to LDMud datatypes */
/*************************************************************************/
static SQLSMALLINT
map_column_type( SQLSMALLINT col_type, SQLSMALLINT digits )
{
switch( col_type ) {
case SQL_DECIMAL :
case SQL_REAL :
case SQL_FLOAT :
case SQL_DOUBLE :
//printf( "T_FLOAT\n" );
return( T_FLOAT );
case SQL_NUMERIC : if ( digits )
return( T_FLOAT );
else
return( T_NUMBER );
case SQL_SMALLINT:
case SQL_INTEGER :
case SQL_BIT :
case SQL_TINYINT :
case SQL_BIGINT :
//printf( "T_NUMBER\n" );
return( T_NUMBER );
default :
//printf( "T_STRING\n" );
return( T_STRING );
}
}