#include "dbe_def.h"
/*
void _BOOT() { ***ue anchor*** }
*/
MODULE = DBE PACKAGE = DBE PREFIX = _DBE_
BOOT:
{
#ifdef DBE_DEBUG
_debug( "booting DBE v%s build %u\n", XS_VERSION, DBE_BUILD );
#if DBE_DEBUG > 1
debug_init();
#endif
#endif
Zero( &global, 1, global );
global.sv_trace_out = newRV( (SV *) PL_stderrgv );
#ifdef USE_ITHREADS
MUTEX_INIT( &global.thread_lock );
#endif
}
#/*****************************************************************************
# * _DBE_CLONE()
# *****************************************************************************/
#ifdef USE_ITHREADS
void
_DBE_CLONE( ... )
PREINIT:
dbe_con_t *con;
dbe_res_t *res;
dbe_stmt_t *stmt;
dbe_lob_stream_t *ls;
PPCODE:
GLOBAL_LOCK();
for( con = global.first_con; con != NULL; con = con->next ) {
#ifdef DBE_DEBUG
_debug( "DBE::CLONE con %lu, ref: %d\n", con->id, con->refcnt );
#endif
con->refcnt ++;
for( stmt = con->first_stmt; stmt != NULL; stmt = stmt->next ) {
#ifdef DBE_DEBUG
_debug( "DBE::CLONE stmt %lu, ref: %d\n", stmt->id, stmt->refcnt );
#endif
stmt->refcnt ++;
}
for( res = con->first_res; res != NULL; res = res->next ) {
#ifdef DBE_DEBUG
_debug( "DBE::CLONE res %lu, ref: %d\n", res->id, res->refcnt );
#endif
res->refcnt ++;
}
for( ls = con->first_lob; ls != NULL; ls = ls->next ) {
#ifdef DBE_DEBUG
_debug( "DBE::CLONE lob %lu, ref: %d\n", ls->id, ls->refcnt );
#endif
ls->refcnt ++;
}
}
GLOBAL_UNLOCK();
#endif
#/*****************************************************************************
# * _DBE_END()
# *****************************************************************************/
void
_DBE_END( ... )
CODE:
if( items ) {} /* avoid compiler warning */
#ifdef DBE_DEBUG
_debug( "DBE::END called\n" );
#endif
IF_TRACE_GLOBAL( DBE_TRACE_ALL )
TRACE( NULL, DBE_TRACE_ALL, FALSE, "END reached\n" );
dbe_free();
#ifdef USE_ITHREADS
MUTEX_DESTROY( &global.thread_lock );
#endif
#/*****************************************************************************
# * _DBE___cleanup__()
# *****************************************************************************/
void
_DBE___cleanup__( ... )
CODE:
if( items ); /* avoid compiler warning */
#ifdef DBE_DEBUG
_debug( "DBE::__cleanup__ called\n" );
#endif
dbe_cleanup();
void
_DBE_connect( ... )
PREINIT:
dbe_con_t *con;
drv_def_t *dd;
SV *sv;
HV *hv;
STRLEN lkey, lval, ldriver = 5;
char tmp[256], **args, *key, *val = NULL, *dsn = NULL;
char error[1024];
const char *driver = "Text";
int argc = 0, i;
unsigned int flags;
enum dbe_getinfo_type gi_type;
PPCODE:
Newxz( con, 1, dbe_con_t );
con->flags = CON_ONERRORDIE;
if( PL_dowarn )
con->flags |= CON_ONERRORWARN;
con->sv_trace_out = SvREFCNT_inc( global.sv_trace_out );
con->trace_level = global.trace_level;
if( items < 1 )
Perl_croak( aTHX_ "Usage: DBE->connect(...)" );
if( items == 2 ) {
/* use DSN */
key = SvPV( ST(1), lkey );
Newx( dsn, lkey + 1, char );
Copy( key, dsn, lkey + 1, char );
argc = dbe_dsn_parse( dsn, &args );
for( i = 0; i < argc; i += 2 ) {
if( my_stricmp( args[i], "driver" ) == 0 ||
my_stricmp( args[i], "provider" ) == 0
) {
driver = args[i + 1];
ldriver = strlen( driver );
}
else if( my_stricmp( args[i], "reconnect" ) == 0 ) {
con->reconnect_count = atol( args[i + 1] );
}
else if( my_stricmp( args[i], "croak" ) == 0 ) {
if( STR_TRUE( args[i + 1] ) )
con->flags |= CON_ONERRORDIE;
else
con->flags &= (~CON_ONERRORDIE);
}
else if( my_stricmp( args[i], "warn" ) == 0 ) {
if( STR_TRUE( args[i + 1] ) )
con->flags |= CON_ONERRORWARN;
else
con->flags &= (~CON_ONERRORWARN);
}
}
}
else {
Newx( args, items - 1, char * );
for( i = 1; i < items - 1; i += 2 ) {
if( ! SvPOK( ST(i) ) )
continue;
key = SvPV( ST(i), lkey );
args[i - 1] = key;
if( ! SvOK( ST(i + 1) ) )
args[i] = NULL;
else {
val = SvPV( ST(i + 1), lval );
args[i] = val;
}
argc += 2;
if( my_stricmp( key, "driver" ) == 0 ||
my_stricmp( key, "provider" ) == 0
) {
driver = val;
ldriver = lval;
}
else if( my_stricmp( key, "reconnect" ) == 0 ) {
con->reconnect_count = (u_int) SvUV( ST(i + 1) );
}
else if( my_stricmp( key, "croak" ) == 0 ) {
if( STR_TRUE( SvPV_nolen( ST(i + 1) ) ) )
con->flags |= CON_ONERRORDIE;
else
con->flags &= (~CON_ONERRORDIE);
}
else if( my_stricmp( key, "warn" ) == 0 ) {
if( STR_TRUE( SvPV_nolen( ST(i + 1) ) ) )
con->flags |= CON_ONERRORWARN;
else
con->flags &= (~CON_ONERRORWARN);
}
}
}
IF_TRACE_GLOBAL( DBE_TRACE_ALL )
TRACE( NULL, DBE_TRACE_ALL, TRUE, "-> connect(Driver.%s)", driver );
key = my_strcpy( tmp, "DBE::Driver::" );
key = my_strcpyu( key, driver );
key = my_strcpy( key, "::VERSION" );
if( get_sv( tmp, FALSE ) == NULL ) {
/* load driver module */
key = my_strcpy( tmp, "require DBE::Driver::" );
key = my_strcpyu( key, driver );
#ifdef DBE_DEBUG
_debug( "load driver module %s\n", tmp + 8 );
#endif
IF_TRACE_GLOBAL( DBE_TRACE_ALL )
TRACE( NULL, DBE_TRACE_ALL, FALSE,
" load driver module %s", tmp + 8 );
eval_sv(
sv_2mortal( newSVpvn( tmp, key - tmp ) ),
G_DISCARD
);
if( SvTRUE( ERRSV ) ) {
dbe_drv_set_error( NULL, -1, SvPV_nolen( ERRSV ) );
IF_TRACE_GLOBAL( DBE_TRACE_ALL )
TRACE( NULL, DBE_TRACE_ALL, FALSE,
" init driver %s: %s", driver, global.last_error );
sprintf( error,
"Init driver %s: %s", driver, global.last_error );
goto _error;
}
}
con->drv = dbe_init_driver( driver, ldriver );
if( con->drv == NULL ) {
#ifdef DBE_DEBUG
_debug( "init driver failed: %s\n", global.last_error );
#endif
IF_TRACE_GLOBAL( DBE_TRACE_ALL )
TRACE( NULL, DBE_TRACE_ALL, FALSE,
" init driver %s: %s", driver, global.last_error );
sprintf( error,
"Init driver %s: %s", driver, global.last_error );
goto _error;
}
dd = con->drv->def;
if( dd->drv_connect == NULL ) {
dbe_drv_set_error( NULL, -1, NOFUNCTION_ERROR, "connect" );
sprintf( error,
"[%s] Connect failed: %s", dd->description, global.last_error );
goto _error;
}
con->drv_data = dd->drv_connect( con, args, argc );
if( con->drv_data == NULL ) {
#ifdef DBE_DEBUG
_debug( "connection failed: %s\n", con->last_error );
#endif
IF_TRACE_GLOBAL( DBE_TRACE_ALL )
TRACE( NULL, DBE_TRACE_ALL, FALSE,
" <- connect failed: %s", con->last_error );
global.last_errno = con->last_errno;
my_strcpy( global.last_error, con->last_error );
sprintf( error,
"[%s] Connect failed: %s", dd->description, con->last_error );
goto _error;
}
if( dd->con_set_trace != NULL )
dd->con_set_trace( con->drv_data, global.trace_level );
/* get some info */
con->catalog_separator = '.';
con->quote_id_prefix = '"';
con->quote_id_suffix = '"';
con->escape_pattern = '\\';
con->catalog_location = SQL_CL_START;
if( dd->con_getinfo != NULL ) {
if( dd->con_getinfo(
con->drv_data, SQL_IDENTIFIER_QUOTE_CHAR,
tmp, sizeof(tmp), &i, &gi_type ) == DBE_OK
) {
IF_TRACE_GLOBAL( DBE_TRACE_ALL )
TRACE( NULL, DBE_TRACE_ALL, FALSE,
" getinfo(SQL_IDENTIFIER_QUOTE_CHAR)= '%c'", *tmp );
con->quote_id_prefix = *tmp;
con->quote_id_suffix = *tmp;
}
if( dd->con_getinfo(
con->drv_data, SQL_SEARCH_PATTERN_ESCAPE,
tmp, sizeof(tmp), &i, &gi_type ) == DBE_OK
) {
IF_TRACE_GLOBAL( DBE_TRACE_ALL )
TRACE( NULL, DBE_TRACE_ALL, FALSE,
" getinfo(SQL_SEARCH_PATTERN_ESCAPE)= '%c'", *tmp );
con->escape_pattern = *tmp;
}
if( dd->con_getinfo(
con->drv_data, SQL_CATALOG_NAME_SEPARATOR,
tmp, sizeof(tmp), &i, &gi_type ) == DBE_OK
) {
IF_TRACE_GLOBAL( DBE_TRACE_ALL )
TRACE( NULL, DBE_TRACE_ALL, FALSE,
" getinfo(SQL_CATALOG_NAME_SEPARATOR)= '%c'", *tmp );
con->catalog_separator = *tmp;
}
if( dd->con_getinfo(
con->drv_data, SQL_CATALOG_LOCATION,
tmp, sizeof(tmp), &i, &gi_type ) == DBE_OK
) {
IF_TRACE_GLOBAL( DBE_TRACE_ALL )
TRACE( NULL, DBE_TRACE_ALL, FALSE,
" getinfo(SQL_CATALOG_LOCATION)= %d", *((short *) tmp) );
con->catalog_location = (char) *((short *) tmp);
}
}
/* add connection */
dbe_con_add( con );
/* create the class */
hv = gv_stashpv( con->drv->class_con, TRUE );
#ifdef USE_ITHREADS
MUTEX_INIT( &con->thread_lock );
#endif
sv = sv_2mortal( newSViv( (IV) con->id ) );
IF_TRACE_GLOBAL( DBE_TRACE_ALL )
TRACE( NULL, DBE_TRACE_ALL, TRUE,
"<- connected= %s=SCALAR(0x%lx)",
con->drv->class_con, (size_t) sv
);
ST(0) = sv_2mortal( sv_bless( newRV( sv ), hv ) );
Safefree( args );
Safefree( dsn );
XSRETURN(1);
_error:
flags = con->flags;
Safefree( con );
Safefree( args );
Safefree( dsn );
if( flags & CON_ONERRORDIE )
Perl_croak( aTHX_ error );
if( flags & CON_ONERRORWARN )
Perl_warn( aTHX_ error );
XSRETURN_EMPTY;
#/*****************************************************************************
# * _DBE_open_drivers()
# *****************************************************************************/
void
_DBE_open_drivers( ... )
PREINIT:
dbe_drv_t *drv;
PPCODE:
GLOBAL_LOCK();
for( drv = global.first_drv; drv != NULL; drv = drv->next ) {
XPUSHs( sv_2mortal( newSVpv( drv->name, 0 ) ) );
XPUSHs( sv_2mortal( newSVpv( drv->def->description, 0 ) ) );
}
GLOBAL_UNLOCK();
#/*****************************************************************************
# * _DBE_installed_drivers()
# *****************************************************************************/
void
_DBE_installed_drivers( ... )
PREINIT:
dbe_drv_t *drv;
AV *inc;
SV **psv, **svb = NULL;
int il, i, svbl = 0;
char *skey, tmp[1024], module[256], *s1;
STRLEN lkey;
DIR *dh;
Direntry_t *de;
PPCODE:
if( (inc = get_av( "INC", 0 )) == NULL )
XSRETURN_EMPTY;
il = av_len( inc );
for( i = il; i >= 0; i -- ) {
if( (psv = av_fetch( inc, i, 0 )) == NULL || ! SvPOK( (*psv) ) )
continue;
skey = SvPVx( (*psv), lkey );
s1 = my_strcpy( tmp, skey );
s1 = my_strcpy( s1, "/DBE/Driver/" );
dh = PerlDir_open( tmp );
if( dh == NULL )
continue;
while( (de = PerlDir_read( dh )) != NULL ) {
if( de->d_name[0] == '.' )
continue;
my_strcpy( module, de->d_name );
skey = my_stristr( module, ".PM" );
if( skey == NULL )
continue;
*skey = '\0';
s1 = my_strcpy( tmp, "require DBE::Driver::" );
s1 = my_strcpy( s1, module );
#ifdef DBE_DEBUG
_debug( "eval '%s'\n", tmp );
#endif
IF_TRACE_GLOBAL( DBE_TRACE_ALL )
TRACE( NULL, DBE_TRACE_ALL, FALSE, "-> eval('%s')", tmp );
eval_sv(
sv_2mortal( newSVpvn( tmp, s1 - tmp ) ),
G_DISCARD | G_KEEPERR
);
if( SvTRUE( ERRSV ) ) {
dbe_drv_set_error( NULL, -1, SvPV_nolen( ERRSV ) );
continue;
}
drv = dbe_init_driver( module, skey - module );
if( drv == NULL )
continue;
if( (svbl % 8) == 0 )
Renew( svb, svbl + 8, SV * );
svb[svbl ++] = sv_2mortal( newSVpv( drv->name, 0 ) );
svb[svbl ++] = sv_2mortal( newSVpv( drv->def->description, 0 ) );
}
PerlDir_close( dh );
}
for( i = 0; i < svbl; i ++ )
XPUSHs( svb[i] );
Safefree( svb );
#/*****************************************************************************
# * _DBE_errno()
# *****************************************************************************/
void
_DBE_errno( ... )
PPCODE:
XSRETURN_IV( global.last_errno );
#/*****************************************************************************
# * _DBE_error()
# *****************************************************************************/
void
_DBE_error( ... )
PPCODE:
GLOBAL_LOCK();
XPUSHs( sv_2mortal( newSVpvn(
global.last_error, strlen( global.last_error ) ) ) );
GLOBAL_UNLOCK();
#/*****************************************************************************
# * _DBE_trace()
# *****************************************************************************/
void
_DBE_trace( ... )
PREINIT:
drv_def_t *dd;
dbe_con_t *con;
PPCODE:
/* Usage: DBE->trace( [level [, handle]] ) */
GLOBAL_LOCK();
if( items > 1 ) {
if( SvIOK( ST(1) ) ) {
global.trace_level = (int) SvIV( ST(1) );
}
else {
global.trace_level =
dbe_trace_level_by_name( SvPV_nolen( ST(1) ) );
}
}
else {
global.trace_level = DBE_TRACE_NONE;
}
for( con = global.first_con; con != NULL; con = con->next ) {
dd = con->drv->def;
if( ! con->trace_local && dd->con_set_trace != NULL )
dd->con_set_trace( con->drv_data, global.trace_level );
}
if( global.trace_level <= DBE_TRACE_NONE )
goto exit;
if( items > 2 && SvOK( ST(2) ) ) {
SvREFCNT_dec( global.sv_trace_out );
global.sv_trace_out = SvREFCNT_inc( ST(2) );
}
dbe_perl_print( global.sv_trace_out, "\n", 1 );
TRACE( NULL, 0, TRUE,
"DBE v%s build %u %s trace level set to %s [%d]",
XS_VERSION, DBE_BUILD,
#ifdef USE_ITHREADS
"thread-multi",
#else
"",
#endif
dbe_trace_name_by_level( global.trace_level ),
global.trace_level
);
if( ! PL_dowarn ) {
dbe_perl_print( global.sv_trace_out,
" Note: Perl is running without the -w option\n", 48
);
}
exit:
GLOBAL_UNLOCK();
XSRETURN_YES;
#/*****************************************************************************
# * _DBE_dump_var()
# *****************************************************************************/
void
_DBE_dump_var( ... )
PREINIT:
size_t rlen;
char *str, *name;
PPCODE:
if( items < 2 || items > 3 )
Perl_croak( aTHX_ "usage: DBE->dump_var($var,$name=0)" );
name = items > 2 && SvOK( ST(2) ) ? SvPV_nolen( ST(2) ) : NULL;
str = my_dump_var( ST(1), name, &rlen, 0 );
ST(0) = sv_2mortal( newSVpvn( str, rlen ) );
Safefree( str );
XSRETURN(1);
#/*****************************************************************************
# * _DBE_show_var()
# *****************************************************************************/
void
_DBE_show_var( ... )
PREINIT:
size_t rlen;
char *str;
PPCODE:
if( items != 2 )
Perl_croak( aTHX_ "usage: DBE->show_var($var)" );
str = my_dump_var( ST(1), NULL, &rlen, 1 );
ST(0) = sv_2mortal( newSVpvn( str, rlen ) );
Safefree( str );
XSRETURN(1);
#/*############################ CONNECTION CLASS #############################*/
MODULE = DBE PACKAGE = DBE::CON PREFIX = CON_
#/*****************************************************************************
# * CON_DESTROY()
# *****************************************************************************/
void
CON_DESTROY( ... )
PREINIT:
dbe_con_t *con;
CODE:
if( items < 1 || (con = dbe_con_find( ST(0) )) == NULL )
XSRETURN_EMPTY;
#ifdef DBE_DEBUG
_debug( "DESTROY called for con %lu, ref %d\n", con->id, con->refcnt );
#endif
con->refcnt --;
if( con->refcnt < 0 ) {
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- DESTROY= %s=SCALAR(0x%x)",
con->drv->class_con, SvRV( ST(0) )
);
dbe_con_rem( con );
}
#/*****************************************************************************
# * CON_close( this )
# *****************************************************************************/
void
CON_close( this )
SV *this;
PREINIT:
dbe_con_t *con;
CODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- DESTROY= %s=SCALAR(0x%x)",
con->drv->class_con, SvRV( this )
);
dbe_con_rem( con );
XSRETURN_YES;
#/*****************************************************************************
# * CON_reconnect( this )
# *****************************************************************************/
void
CON_reconnect( this )
SV *this;
PREINIT:
dbe_con_t *con;
int r;
unsigned int recon;
CODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
recon = con->reconnect_count;
con->reconnect_count = 1;
r = dbe_con_reconnect( this, "con", con );
con->reconnect_count = recon;
if( r != DBE_OK )
XSRETURN_EMPTY;
XSRETURN_YES;
#/*****************************************************************************
# * CON_ping( this )
# *****************************************************************************/
void
CON_ping( this )
SV *this;
PREINIT:
dbe_con_t *con;
drv_def_t *dd;
int r;
CODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
if( dd->con_ping == NULL )
NOFUNCTION_XS( this, "con", con, "ping" );
_retry:
r = dd->con_ping( con->drv_data );
switch( r ) {
case DBE_OK:
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE, "<- ping()= TRUE" );
XSRETURN_YES;
case DBE_CONN_LOST:
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE, "<- ping()= FALSE" );
if( dbe_con_reconnect( this, "con", con ) == DBE_OK )
goto _retry;
default:
HANDLE_ERROR_XS( this, "con", con, "Ping failed" );
}
#/*****************************************************************************
# * CON_query( this, query )
# *****************************************************************************/
void
CON_query( this, query, ... )
SV *this;
SV *query;
PREINIT:
dbe_con_t *con;
drv_def_t *dd;
dbe_res_t *res = NULL;
char *sql, *sql3, *sql2 = NULL, **params, *key;
STRLEN sql_len;
int i, r, param_count, key_len;
drv_stmt_t *stmt;
SV *sv;
HV *hv;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
sql = SvPV( query, sql_len );
for( ; isSPACE( *sql ); sql ++, sql_len -- );
if( *sql == '%' ) {
sql3 = sql2 = dbe_convert_query(
con, sql + 1, sql_len - 1, &sql_len, ¶ms, ¶m_count );
}
else {
sql3 = sql;
param_count = 0;
params = NULL;
}
#ifdef DBE_DEBUG
_debug( "SQL: %s\n", sql );
#endif
_retry:
if( param_count || items > 2 ) {
if( dd->con_prepare == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "prepare" );
r = DBE_ERROR;
goto _cleanup;
}
if( dd->stmt_execute == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "execute" );
r = DBE_ERROR;
goto _cleanup;
}
r = dd->con_prepare( con->drv_data, sql3, sql_len, &stmt );
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
goto _reconnect;
default:
goto _cleanup;
}
if( items > 2 && SvROK( ST(2) ) ) {
sv = SvRV( ST(2) );
if( SvTYPE( sv ) == SVt_PVHV ) {
hv = (HV *) sv, hv_iterinit( hv );
while( (sv = hv_iternextsv( hv, &key, &key_len )) != NULL ) {
r = dbe_bind_param_str(
this, "con", con, stmt, dd,
params, param_count, key, sv, 0
);
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
goto _reconnect;
default:
goto _cleanup;
}
}
goto _execute;
}
}
for( i = 2; i < items; i ++ ) {
r = dbe_bind_param_num(
this, "con", con, stmt, dd, i - 1, ST(i), 0, NULL );
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
goto _reconnect;
default:
goto _cleanup;
}
}
_execute:
r = dd->stmt_execute( stmt, &res );
if( dd->stmt_free != NULL )
dd->stmt_free( stmt );
}
else {
if( dd->con_query == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "query" );
r = DBE_ERROR;
goto _cleanup;
}
r = dd->con_query( con->drv_data, sql3, sql_len, &res );
}
switch( r ) {
case DBE_OK:
if( res == NULL ) {
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE, "<- do('%s')= TRUE", sql );
XPUSHs( sv_2mortal( newSViv( 1 ) ) );
goto _cleanup;
}
dbe_res_add( con, res );
sv = sv_2mortal( newSViv( (IV) res->id ) );
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- query('%s')= %s=SCALAR(0x%lx)",
sql, con->drv->class_res, (size_t) sv
);
hv = gv_stashpv( con->drv->class_res, TRUE );
XPUSHs( sv_2mortal( sv_bless( newRV( sv ), hv ) ) );
break;
case DBE_CONN_LOST:
_reconnect:
if( dbe_con_reconnect( this, "con", con ) == DBE_OK )
goto _retry;
}
_cleanup:
Safefree( sql2 );
if( params != NULL ) {
for( i = 0; i < param_count; i ++ )
Safefree( params[i] );
Safefree( params );
}
if( r != DBE_OK ) {
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- query('%s')= FALSE", sql );
HANDLE_ERROR_XS( this, "con", con, "Query failed" );
}
#/*****************************************************************************
# * CON_prepare( this, query )
# *****************************************************************************/
void
CON_prepare( this, query )
SV *this;
SV *query;
PREINIT:
dbe_con_t *con;
drv_def_t *dd;
dbe_stmt_t *stmt;
const char *sql, *sql3;
char *sql2 = NULL, **params;
STRLEN sql_len;
int i, r, param_count;
drv_stmt_t *p = NULL;
SV *sv;
HV *hv;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
if( dd->con_prepare == NULL )
NOFUNCTION_XS( this, "con", con, "prepare" );
sql = SvPV( query, sql_len );
for( ; isSPACE( *sql ); sql ++, sql_len -- );
if( *sql == '%' ) {
sql3 = sql2 = dbe_convert_query(
con, &sql[1], sql_len - 1, &sql_len, ¶ms, ¶m_count );
}
else {
sql3 = sql;
params = NULL;
param_count = 0;
}
#ifdef DBE_DEBUG
_debug( "SQL: %s\n", sql );
#endif
_retry:
r = dd->con_prepare( con->drv_data, sql3, sql_len, &p );
switch( r ) {
case DBE_OK:
if( p == NULL ) {
dbe_drv_set_error( con,
-1, "Driver did not return a statement object" );
goto _error;
}
Newxz( stmt, 1, dbe_stmt_t );
stmt->drv_data = p;
stmt->con = con;
if( param_count && params != NULL ) {
Newxz( stmt->params, param_count, dbe_param_t );
for( i = 0; i < param_count; i ++ ) {
stmt->params[i].name = params[i];
}
stmt->num_params = param_count;
stmt->param_names = params;
}
dbe_stmt_add( con, stmt );
hv = gv_stashpv( con->drv->class_stmt, FALSE );
sv = sv_2mortal( newSViv( (IV) stmt->id ) );
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- prepare('%s')= %s=SCALAR(0x%lx)",
sql, con->drv->class_stmt, (size_t) sv
);
Safefree( sql2 );
ST(0) = sv_2mortal( sv_bless( newRV( sv ), hv ) );
XSRETURN(1);
case DBE_CONN_LOST:
if( dbe_con_reconnect( this, "con", con ) == DBE_OK )
goto _retry;
}
_error:
Safefree( sql2 );
if( params != NULL ) {
for( i = 0; i < param_count; i ++ )
Safefree( params[i] );
Safefree( params );
}
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- prepare('%s')= FALSE", sql );
HANDLE_ERROR_XS( this, "con", con, "Prepare failed" );
#/*****************************************************************************
# * CON_selectrow_array( this, query )
# *****************************************************************************/
void
CON_selectrow_array( this, query, ... )
SV *this;
SV *query;
PREINIT:
dbe_con_t *con;
drv_def_t *dd;
char *sql, *sql2 = NULL, *sql3, **params, *key;
STRLEN sql_len;
drv_stmt_t *drv_stmt = NULL;
dbe_res_t *res = NULL;
int i, r, param_count, retcount = 0, key_len;
HV *hv;
SV *sv;
dbe_vrt_res_t *vres;
dbe_vrt_row_t *vrow;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
sql = SvPV( query, sql_len );
for( ; isSPACE( *sql ); sql ++, sql_len -- );
if( *sql == '%' ) {
sql3 = sql2 = dbe_convert_query(
con, &sql[1], sql_len - 1, &sql_len, ¶ms, ¶m_count );
}
else {
sql3 = sql;
params = NULL;
param_count = 0;
}
#ifdef DBE_DEBUG
_debug( "SQL: %s\n", sql );
#endif
_retry:
if( items > 2 || param_count ) {
if( dd->con_prepare == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "prepare" );
r = DBE_ERROR;
goto _cleanup;
}
if( dd->stmt_execute == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "execute" );
r = DBE_ERROR;
goto _cleanup;
}
r = dd->con_prepare( con->drv_data, sql3, sql_len, &drv_stmt );
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
goto _reconnect;
default:
goto _cleanup;
}
if( items > 2 && SvROK( ST(2) ) ) {
sv = SvRV( ST(2) );
if( SvTYPE( sv ) == SVt_PVHV ) {
hv = (HV *) sv, hv_iterinit( hv );
while( (sv = hv_iternextsv( hv, &key, &key_len )) != NULL ) {
r = dbe_bind_param_str(
this, "con", con, drv_stmt, dd,
params, param_count, key, sv, 0
);
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
goto _reconnect;
default:
goto _cleanup;
}
}
goto _execute;
}
}
for( i = 2; i < items; i ++ ) {
r = dbe_bind_param_num(
this, "con", con, drv_stmt, dd, i - 1, ST(i), 0, NULL );
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
goto _reconnect;
default:
goto _cleanup;
}
}
_execute:
r = dd->stmt_execute( drv_stmt, &res );
}
else {
if( dd->con_query == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "query" );
r = DBE_ERROR;
goto _cleanup;
}
r = dd->con_query( con->drv_data, sql3, sql_len, &res );
}
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
_reconnect:
if( dbe_con_reconnect( this, "con", con ) == DBE_OK )
goto _retry;
default:
goto _cleanup;
}
if( res == NULL ) {
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- selectrow_array('%s')= NULL", sql );
goto _cleanup;
}
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- selectrow_array('%s')= TRUE (columns: %u)",
sql, res->num_fields
);
if( res->type == RES_TYPE_DRV ) {
if( dd->res_fetch_row == NULL )
NOFUNCTION_XS( this, "con", con, "fetch_row" );
if( dbe_res_fetch_row( res, FALSE ) != DBE_OK )
goto _cleanup;
for( i = 0; i < (int) res->num_fields; i ++ ) {
if( res->sv_buffer[i] != NULL )
ST(i) = sv_2mortal( res->sv_buffer[i] );
else
ST(i) = sv_newmortal();
}
retcount = i;
}
else { /* RES_TYPE_VRT */
vres = res->vrt_res, vrow = vres->rows;
if( ! vres->row_count ) {
goto _cleanup;
}
for( i = 0; i < (int) vres->column_count; i ++ ) {
ST(i) = sv_2mortal( dbe_vrt_res_fetch(
vres->columns[i].type, vrow->data[i], vrow->lengths[i] ) );
}
retcount = i;
}
_cleanup:
Safefree( sql2 );
if( params != NULL ) {
for( i = 0; i < param_count; i ++ )
Safefree( params[i] );
Safefree( params );
}
if( res != NULL )
dbe_res_free( res );
if( drv_stmt != NULL && dd->stmt_free != NULL )
dd->stmt_free( drv_stmt );
if( r != DBE_OK ) {
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- selectrow_array('%s')= FALSE", sql );
HANDLE_ERROR_XS( this, "con", con, "Select row array" );
}
XSRETURN(retcount);
#/*****************************************************************************
# * CON_selectrow_arrayref( this, query )
# *****************************************************************************/
void
CON_selectrow_arrayref( this, query, ... )
SV *this;
SV *query;
PREINIT:
dbe_con_t *con;
drv_def_t *dd;
char *sql, *sql2 = NULL, *sql3, **params, *key;
STRLEN sql_len;
drv_stmt_t *drv_stmt = NULL;
dbe_res_t *res = NULL;
AV *av;
HV *hv;
SV *sv;
int i, r, param_count, key_len;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
sql = SvPV( query, sql_len );
for( ; isSPACE( *sql ); sql ++, sql_len -- );
if( *sql == '%' ) {
sql3 = sql2 = dbe_convert_query(
con, &sql[1], sql_len - 1, &sql_len, ¶ms, ¶m_count );
}
else {
sql3 = sql;
params = NULL;
param_count = 0;
}
#ifdef DBE_DEBUG
_debug( "SQL: %s\n", sql );
#endif
_retry:
if( items > 2 || param_count ) {
if( dd->con_prepare == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "prepare" );
r = DBE_ERROR;
goto _cleanup;
}
if( dd->stmt_execute == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "execute" );
r = DBE_ERROR;
goto _cleanup;
}
r = dd->con_prepare( con->drv_data, sql3, sql_len, &drv_stmt );
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
goto _reconnect;
default:
goto _cleanup;
}
if( items > 2 && SvROK( ST(2) ) ) {
sv = SvRV( ST(2) );
if( SvTYPE( sv ) == SVt_PVHV ) {
hv = (HV *) sv, hv_iterinit( hv );
while( (sv = hv_iternextsv( hv, &key, &key_len )) != NULL ) {
r = dbe_bind_param_str(
this, "con", con, drv_stmt, dd,
params, param_count, key, sv, 0
);
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
goto _reconnect;
default:
goto _cleanup;
}
}
goto _execute;
}
}
for( i = 2; i < items; i ++ ) {
r = dbe_bind_param_num(
this, "con", con, drv_stmt, dd, i - 1, ST(i), 0, NULL );
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
goto _reconnect;
default:
goto _cleanup;
}
}
_execute:
r = dd->stmt_execute( drv_stmt, &res );
}
else {
if( dd->con_query == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "query" );
r = DBE_ERROR;
goto _cleanup;
}
r = dd->con_query( con->drv_data, sql3, sql_len, &res );
}
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
_reconnect:
if( dbe_con_reconnect( this, "con", con ) == DBE_OK )
goto _retry;
default:
ST(0) = sv_newmortal();
goto _cleanup;
}
if( res == NULL ) {
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- selectrow_arrayref('%s')= NULL", sql );
ST(0) = sv_newmortal();
goto _cleanup;
}
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- selectrow_arrayref('%s')= TRUE (columns: %u)",
sql, res->num_fields
);
if( res->type == RES_TYPE_DRV ) {
if( dd->res_fetch_row == NULL )
NOFUNCTION_XS( this, "con", con, "fetch_row" );
if( dbe_res_fetch_row( res, FALSE ) != DBE_OK ) {
ST(0) = sv_newmortal();
goto _cleanup;
}
av = newAV();
for( i = 0; i < (int) res->num_fields; i ++ ) {
if( res->sv_buffer[i] != NULL )
av_push( av, res->sv_buffer[i] );
else
av_push( av, newSV(0) );
}
ST(0) = sv_2mortal( newRV( sv_2mortal( (SV *) av ) ) );
}
else { /* RES_TYPE_VRT */
dbe_vrt_res_t *vres = res->vrt_res;
dbe_vrt_row_t *vrow = vres->rows;
if( ! vres->row_count ) {
ST(0) = sv_newmortal();
goto _cleanup;
}
av = newAV();
for( i = 0; i < (int) vres->column_count; i ++ ) {
av_push( av, dbe_vrt_res_fetch(
vres->columns[i].type, vrow->data[i], vrow->lengths[i] ) );
}
ST(0) = sv_2mortal( newRV( sv_2mortal( (SV *) av ) ) );
}
_cleanup:
Safefree( sql2 );
if( params != NULL ) {
for( i = 0; i < param_count; i ++ )
Safefree( params[i] );
Safefree( params );
}
if( res != NULL )
dbe_res_free( res );
if( drv_stmt != NULL && dd->stmt_free != NULL )
dd->stmt_free( drv_stmt );
if( r != DBE_OK ) {
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- selectrow_arrayref('%s')= FALSE", sql );
HANDLE_ERROR_XS( this, "con", con, "Select row array ref" );
}
XSRETURN(1);
#/*****************************************************************************
# * CON_selectrow_hashref( this, query )
# *****************************************************************************/
void
CON_selectrow_hashref( this, query, ... )
SV *this;
SV *query;
PREINIT:
dbe_con_t *con;
dbe_res_t *res = NULL;
drv_def_t *dd;
char *sql, *sql2 = NULL, *sql3, **params, *key;
STRLEN sql_len;
drv_stmt_t *drv_stmt = NULL;
HV *hv = NULL;
SV *sv;
int i, r, param_count, key_len;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
sql = SvPV( query, sql_len );
for( ; isSPACE( *sql ); sql ++, sql_len -- );
if( *sql == '%' ) {
sql3 = sql2 = dbe_convert_query(
con, &sql[1], sql_len - 1, &sql_len, ¶ms, ¶m_count );
}
else {
sql3 = sql;
params = NULL;
param_count = 0;
}
#ifdef DBE_DEBUG
_debug( "SQL: %s\n", sql );
#endif
_retry:
if( items > 2 || param_count ) {
if( dd->con_prepare == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "prepare" );
r = DBE_ERROR;
goto _cleanup;
}
if( dd->stmt_execute == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "execute" );
r = DBE_ERROR;
goto _cleanup;
}
r = dd->con_prepare( con->drv_data, sql3, sql_len, &drv_stmt );
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
goto _reconnect;
default:
goto _cleanup;
}
if( items > 2 && SvROK( ST(2) ) ) {
sv = SvRV( ST(2) );
if( SvTYPE( sv ) == SVt_PVHV ) {
hv = (HV *) sv, hv_iterinit( hv );
while( (sv = hv_iternextsv( hv, &key, &key_len )) != NULL ) {
r = dbe_bind_param_str(
this, "con", con, drv_stmt, dd,
params, param_count, key, sv, 0
);
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
goto _reconnect;
default:
goto _cleanup;
}
}
goto _execute;
}
}
for( i = 2; i < items; i ++ ) {
r = dbe_bind_param_num(
this, "con", con, drv_stmt, dd, i - 1, ST(i), 0, NULL );
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
goto _reconnect;
default:
goto _cleanup;
}
}
_execute:
r = dd->stmt_execute( drv_stmt, &res );
}
else {
if( dd->con_query == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "query" );
r = DBE_ERROR;
goto _cleanup;
}
r = dd->con_query( con->drv_data, sql3, sql_len, &res );
}
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
_reconnect:
if( dbe_con_reconnect( this, "con", con ) == DBE_OK )
goto _retry;
default:
ST(0) = sv_newmortal();
goto _cleanup;
}
if( res == NULL ) {
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- selectrow_hashref('%s')= NULL", sql );
ST(0) = sv_newmortal();
goto _cleanup;
}
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- selectrow_hashref('%s')= TRUE (columns: %u)",
sql, res->num_fields
);
if( res->type == RES_TYPE_DRV ) {
if( dbe_res_fetch_hash( this, res, &hv, 0 ) == DBE_OK )
ST(0) = newRV_noinc( (SV *) hv );
else
ST(0) = sv_newmortal();
}
else {
if( dbe_vrt_res_fetch_hash( res, &hv, 0 ) == DBE_OK )
ST(0) = newRV_noinc( (SV *) hv );
else
ST(0) = sv_newmortal();
}
_cleanup:
Safefree( sql2 );
if( params != NULL ) {
for( i = 0; i < param_count; i ++ )
Safefree( params[i] );
Safefree( params );
}
if( res != NULL )
dbe_res_free( res );
if( drv_stmt != NULL && dd->stmt_free != NULL )
dd->stmt_free( drv_stmt );
if( r != DBE_OK ) {
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- selectrow_hashref('%s')= FALSE", sql );
HANDLE_ERROR_XS( this, "con", con, "Select row hash ref" );
}
XSRETURN(1);
#/*****************************************************************************
# * CON_selectall_arrayref( this, query )
# *****************************************************************************/
void
CON_selectall_arrayref( this, query, ... )
SV *this;
SV *query;
PREINIT:
dbe_con_t *con;
dbe_res_t *res = NULL;
drv_def_t *dd;
char *sql, *sql2 = NULL, *sql3, **params, *key;
STRLEN sql_len;
drv_stmt_t *drv_stmt = NULL;
AV *av = NULL;
HV *hv;
SV *sv;
int i, r, fn = 0, param_count, key_len;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
sql = SvPV( query, sql_len );
for( ; isSPACE( *sql ); sql ++, sql_len -- );
if( *sql == '%' ) {
sql3 = sql2 = dbe_convert_query(
con, &sql[1], sql_len - 1, &sql_len, ¶ms, ¶m_count );
}
else {
sql3 = sql;
params = NULL;
param_count = 0;
}
#ifdef DBE_DEBUG
_debug( "SQL: %s\n", sql );
#endif
_retry:
if( items > 2 ) {
if( SvROK( ST(2) ) && SvTYPE( SvRV( ST(2) ) ) == SVt_PVHV )
fn = 1;
}
if( items > 3 ) {
if( dd->con_prepare == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "prepare" );
r = DBE_ERROR;
goto _cleanup;
}
if( dd->stmt_execute == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "execute" );
r = DBE_ERROR;
goto _cleanup;
}
r = dd->con_prepare( con->drv_data, sql3, sql_len, &drv_stmt );
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
goto _reconnect;
default:
goto _cleanup;
}
if( items > 3 && SvROK( ST(3) ) ) {
sv = SvRV( ST(3) );
if( SvTYPE( sv ) == SVt_PVHV ) {
hv = (HV *) sv, hv_iterinit( hv );
while( (sv = hv_iternextsv( hv, &key, &key_len )) != NULL ) {
r = dbe_bind_param_str(
this, "con", con, drv_stmt, dd,
params, param_count, key, sv, 0
);
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
goto _reconnect;
default:
goto _cleanup;
}
}
goto _execute;
}
}
for( i = 3; i < items; i ++ ) {
r = dbe_bind_param_num(
this, "con", con, drv_stmt, dd, i - 2, ST(i), 0, NULL );
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
goto _reconnect;
default:
goto _cleanup;
}
}
_execute:
r = dd->stmt_execute( drv_stmt, &res );
}
else {
if( dd->con_query == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "query" );
r = DBE_ERROR;
goto _cleanup;
}
r = dd->con_query( con->drv_data, sql3, sql_len, &res );
}
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
_reconnect:
if( dbe_con_reconnect( this, "con", con ) == DBE_OK )
goto _retry;
default:
ST(0) = sv_newmortal();
goto _cleanup;
}
if( res == NULL ) {
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- selectall_arrayref('%s')= NULL", sql );
ST(0) = sv_newmortal();
goto _cleanup;
}
if( res->type == RES_TYPE_DRV ) {
if( dbe_res_fetchall_arrayref( this, res, &av, fn ) == DBE_OK ) {
ST(0) = sv_2mortal( newRV( (SV *) av ) );
}
else {
ST(0) = sv_newmortal();
r = DBE_ERROR;
goto _cleanup;
}
}
else {
if( dbe_vrt_res_fetchall_arrayref( res, &av, fn ) == DBE_OK ) {
ST(0) = sv_2mortal( newRV( (SV *) av ) );
}
else {
ST(0) = sv_newmortal();
r = DBE_ERROR;
goto _cleanup;
}
}
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- selectall_arrayref('%s')= TRUE (columns: %u, rows: %d)",
sql, res->num_fields, (av != NULL) ? (av_len( av ) + 1) : 0
);
_cleanup:
Safefree( sql2 );
if( params != NULL ) {
for( i = 0; i < param_count; i ++ )
Safefree( params[i] );
Safefree( params );
}
if( res != NULL )
dbe_res_free( res );
if( drv_stmt != NULL && dd->stmt_free != NULL )
dd->stmt_free( drv_stmt );
if( r != DBE_OK ) {
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- selectall_arrayref('%s')= FALSE", sql );
HANDLE_ERROR_XS( this, "con", con, "selectall_arrayref failed" );
}
XSRETURN(1);
#/*****************************************************************************
# * CON_selectall_hashref( this, query, key )
# *****************************************************************************/
void
CON_selectall_hashref( this, query, keyfield, ... )
SV *this;
SV *query;
SV *keyfield;
PREINIT:
dbe_con_t *con;
dbe_res_t *res = NULL;
drv_def_t *dd;
dbe_str_t *fields = NULL;
char *sql, *sql2 = NULL, *sql3, **params, *key;
STRLEN sql_len;
drv_stmt_t *drv_stmt = NULL;
HV *hv = NULL;
AV *av;
SV **psv, *sv;
int i, ks, r, param_count, key_len;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
sql = SvPV( query, sql_len );
for( ; isSPACE( *sql ); sql ++, sql_len -- );
if( *sql == '%' ) {
sql3 = sql2 = dbe_convert_query(
con, &sql[1], sql_len - 1, &sql_len, ¶ms, ¶m_count );
}
else {
sql3 = sql;
params = NULL;
param_count = 0;
}
#ifdef DBE_DEBUG
_debug( "SQL: %s\n", sql );
#endif
_retry:
if( SvROK( keyfield ) && (keyfield = SvRV( keyfield )) ) {
if( SvTYPE( keyfield ) != SVt_PVAV ) {
dbe_drv_set_error( con, -1,
"Type of key fields must be an arrayref" );
r = DBE_ERROR;
goto _cleanup;
}
av = (AV *) keyfield;
ks = av_len( av ) + 1;
if( ks <= 0 ) {
dbe_drv_set_error( con, -1, "List of key fields is empty" );
r = DBE_ERROR;
goto _cleanup;
}
Newx( fields, ks, dbe_str_t );
for( i = 0; i < ks; i ++ ) {
psv = av_fetch( av, i, 0 );
if( psv != NULL )
fields[i].value = SvPV( (*psv), fields[i].length );
else {
fields[i].value = "";
fields[i].length = 0;
}
}
}
else {
ks = 1;
Newx( fields, ks, dbe_str_t );
fields[0].value = SvPV( keyfield, fields[0].length );
}
if( items > 3 || param_count ) {
if( dd->con_prepare == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "prepare" );
r = DBE_ERROR;
goto _cleanup;
}
if( dd->stmt_execute == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "execute" );
r = DBE_ERROR;
goto _cleanup;
}
r = dd->con_prepare( con->drv_data, sql3, sql_len, &drv_stmt );
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
goto _reconnect;
default:
goto _cleanup;
}
if( items > 3 && SvROK( ST(3) ) ) {
sv = SvRV( ST(3) );
if( SvTYPE( sv ) == SVt_PVHV ) {
hv = (HV *) sv, hv_iterinit( hv );
while( (sv = hv_iternextsv( hv, &key, &key_len )) != NULL ) {
r = dbe_bind_param_str(
this, "con", con, drv_stmt, dd,
params, param_count, key, sv, 0
);
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
goto _reconnect;
default:
goto _cleanup;
}
}
goto _execute;
}
}
for( i = 3; i < items; i ++ ) {
r = dbe_bind_param_num(
this, "con", con, drv_stmt, dd, i - 2, ST(i), 0, NULL );
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
goto _reconnect;
default:
goto _cleanup;
}
}
_execute:
r = dd->stmt_execute( drv_stmt, &res );
}
else {
if( dd->con_query == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "query" );
r = DBE_ERROR;
goto _cleanup;
}
r = dd->con_query( con->drv_data, sql3, sql_len, &res );
}
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
_reconnect:
if( dbe_con_reconnect( this, "con", con ) == DBE_OK )
goto _retry;
default:
ST(0) = sv_newmortal();
goto _cleanup;
}
if( res == NULL ) {
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- selectall_hashref('%s')= TRUE", sql );
ST(0) = sv_newmortal();
goto _cleanup;
}
if( res->type == RES_TYPE_DRV ) {
if( dbe_res_fetchall_hashref( this, res, fields, ks, &hv ) == DBE_OK )
ST(0) = sv_2mortal( newRV( (SV *) hv ) );
else {
ST(0) = sv_newmortal();
r = DBE_ERROR;
goto _cleanup;
}
}
else {
if( dbe_vrt_res_fetchall_hashref( res, fields, ks, &hv ) == DBE_OK )
ST(0) = sv_2mortal( newRV( (SV *) hv ) );
else {
ST(0) = sv_newmortal();
r = DBE_ERROR;
goto _cleanup;
}
}
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- selectall_hashref('%s')= TRUE (keys: %d, columns: %u)",
sql, ks, res->num_fields
);
_cleanup:
Safefree( fields );
Safefree( sql2 );
if( params != NULL ) {
for( i = 0; i < param_count; i ++ )
Safefree( params[i] );
Safefree( params );
}
if( res != NULL )
dbe_res_free( res );
if( drv_stmt != NULL && dd->stmt_free != NULL )
dd->stmt_free( drv_stmt );
if( r != DBE_OK ) {
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- selectall_hashref('%s')= FALSE", sql );
HANDLE_ERROR_XS( this, "con", con, "selectall_hashref failed" );
}
XSRETURN(1);
#/*****************************************************************************
# * CON_select_col( this, query )
# *****************************************************************************/
void
CON_select_col( this, query, ... )
SV *this;
SV *query;
PREINIT:
dbe_con_t *con;
dbe_res_t *res = NULL;
drv_def_t *dd;
char *sql, *sql2 = NULL, *sql3, *skey, **params;
STRLEN sql_len, lkey;
u_long i, j;
drv_stmt_t *drv_stmt = NULL;
int r, itemp, type, param_count;
SV *store = NULL, *sv, **psv;
AV *av = NULL;
HV *hv = NULL;
long col1 = 0, col2 = 1;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
/* select_col( <sql>, [store, col1, col2], @bind_values ) */
itemp = 2;
if( items > 2 ) {
if( SvROK(ST(2)) && (sv = SvRV(ST(2))) && SvTYPE(sv) == SVt_PVAV ) {
itemp = 3;
av = (AV *) sv;
psv = av_fetch( av, 0, FALSE );
if( psv != NULL ) {
if( SvROK( *psv ) ) {
store = SvRV( *psv );
}
else {
col1 = (long) SvIV( *psv );
}
}
psv = av_fetch( av, 1, FALSE );
if( psv != NULL ) {
if( store != NULL ) {
col1 = (long) SvIV( *psv );
psv = av_fetch( av, 2, FALSE );
if( psv != NULL ) {
col2 = (long) SvIV( *psv );
}
}
else {
col2 = (long) SvIV( *psv );
}
}
av = NULL;
if( store != NULL ) {
if( SvTYPE(store) == SVt_PVAV ) {
av = (AV *) store;
}
else if( SvTYPE(store) == SVt_PVHV ) {
hv = (HV *) store;
}
}
}
}
dd = con->drv->def;
sql = SvPV( query, sql_len );
for( ; isSPACE( *sql ); sql ++, sql_len -- );
if( *sql == '%' ) {
sql3 = sql2 = dbe_convert_query(
con, &sql[1], sql_len - 1, &sql_len, ¶ms, ¶m_count );
}
else {
sql3 = sql;
params = NULL;
param_count = 0;
}
#ifdef DBE_DEBUG
_debug( "SQL: %s\n", sql );
#endif
_retry:
if( items > itemp || param_count ) {
if( dd->con_prepare == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "prepare" );
r = DBE_ERROR;
goto _cleanup;
}
if( dd->stmt_execute == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "execute" );
r = DBE_ERROR;
goto _cleanup;
}
r = dd->con_prepare( con->drv_data, sql3, sql_len, &drv_stmt );
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
goto _reconnect;
default:
goto _cleanup;
}
if( items > itemp && SvROK( ST(itemp) ) ) {
sv = SvRV( ST(itemp) );
if( SvTYPE( sv ) == SVt_PVHV ) {
char *key;
int key_len;
hv = (HV *) sv, hv_iterinit( hv );
while( (sv = hv_iternextsv( hv, &key, &key_len)) != NULL ) {
r = dbe_bind_param_str(
this, "con", con, drv_stmt, dd,
params, param_count, key, sv, 0
);
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
goto _reconnect;
default:
goto _cleanup;
}
}
goto _execute;
}
}
for( i = itemp, j = 1; i < (u_long) items; i ++, j ++ ) {
r = dbe_bind_param_num(
this, "con", con, drv_stmt, dd, j, ST(i), 0, NULL );
if( r != DBE_OK )
XSRETURN_EMPTY;
}
_execute:
r = dd->stmt_execute( drv_stmt, &res );
}
else {
if( dd->con_query == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "query" );
r = DBE_ERROR;
goto _cleanup;
}
r = dd->con_query( con->drv_data, sql3, sql_len, &res );
}
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
_reconnect:
if( dbe_con_reconnect( this, "con", con ) == DBE_OK )
goto _retry;
default:
goto _cleanup;
}
if( res == NULL ) {
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- selectcol_array('%s')= NULL", sql );
XPUSHs( sv_newmortal() );
goto _cleanup;
}
if( res->type == RES_TYPE_DRV ) {
psv = res->sv_buffer;
if( col1 < 0 ) {
col1 = (long) res->num_fields + col1;
if( col1 < 0 )
col1 = 0;
}
else if( (u_long) col1 >= res->num_fields ) {
col1 = res->num_fields - 1;
}
if( dd->res_fetch_row == NULL )
NOFUNCTION_XS( this, "con", con, "fetch_row" );
if( av != NULL ) {
i = 0;
while( dbe_res_fetch_row( res, FALSE ) == DBE_OK ) {
for( j = 0; j < res->num_fields; j ++ ) {
if( j == col1 ) {
if( psv[col1] != NULL )
av_store( av, i, psv[col1] );
else
av_store( av, i, newSV(0) );
}
else if( psv[j] != NULL )
sv_2mortal( psv[j] );
}
i ++;
}
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- selectcol_array('%s')= TRUE, rows: %lu (col: %d)",
sql, i, col1
);
XPUSHs( sv_2mortal( newSViv( 1 ) ) );
}
else if( hv != NULL ) {
if( col2 < 0 ) {
col2 = (long) res->num_fields + col2;
if( col2 < 0 )
col2 = 0;
}
else
if( (u_long) col2 >= res->num_fields ) {
col2 = res->num_fields - 1;
}
i = 0;
while( dbe_res_fetch_row( res, TRUE ) == DBE_OK ) {
if( psv[col1] != NULL ) {
skey = SvPV( psv[col1], lkey );
(void) hv_store(
hv, skey, (I32) lkey,
psv[col2] != NULL ? psv[col2] : newSV(0),
0
);
sv_2mortal( psv[col1] );
}
else {
(void) hv_store(
hv, "", 0,
psv[col2] != NULL ? psv[col2] : newSV(0),
0
);
}
for( j = 0; j < res->num_fields; j ++ ) {
if( j != col1 && j != col2 && psv[j] != NULL )
sv_2mortal( psv[j] );
}
i ++;
}
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- selectcol_array('%s')= TRUE"
", rows: %lu (col1: %d, col2: %d)",
sql, i, col1, col2
);
XPUSHs( sv_2mortal( newSViv( 1 ) ) );
}
else {
i = 0;
while( dbe_res_fetch_row( res, FALSE ) == DBE_OK ) {
for( j = 0; j < res->num_fields; j ++ ) {
if( j == col1 ) {
if( psv[col1] != NULL )
XPUSHs( sv_2mortal( psv[col1] ) );
else
XPUSHs( sv_newmortal() );
}
else if( psv[j] != NULL )
sv_2mortal( psv[j] );
}
i ++;
}
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- selectcol_array('%s')= TRUE, rows: %lu (col: %d)",
sql, i, col1
);
}
}
else { /* RES_TYPE_VRT */
dbe_vrt_res_t *vres = res->vrt_res;
if( col1 < 0 ) {
col1 = vres->column_count + col1;
if( col1 < 0 )
col1 = 0;
}
else if( (u_long) col1 >= vres->column_count ) {
col1 = vres->column_count - 1;
}
type = vres->columns[col1].type;
if( av != NULL ) {
for( i = 0; i < vres->row_count; i ++ ) {
av_store( av, i, dbe_vrt_res_fetch(
type,
vres->rows[i].data[col1],
vres->rows[i].lengths[col1]
) );
}
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- selectcol_array('%s')= TRUE, rows: %lu (col: %d)",
sql, i, col1
);
XPUSHs( sv_2mortal( newSViv( 1 ) ) );
}
else if( hv != NULL ) {
if( col2 < 0 ) {
col2 = vres->column_count + col2;
if( col2 < 0 )
col2 = 0;
}
else if( (u_long) col2 >= vres->column_count ) {
col2 = vres->column_count - 1;
}
for( i = 0; i < vres->row_count; i ++ ) {
store = sv_2mortal( dbe_vrt_res_fetch(
type,
vres->rows[i].data[col1],
vres->rows[i].lengths[col1]
) );
skey = SvPVx( store, lkey );
(void) hv_store( hv, skey, (I32) lkey,
dbe_vrt_res_fetch(
type,
vres->rows[i].data[col2],
vres->rows[i].lengths[col2]
), 0
);
}
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- selectcol_array('%s')= TRUE"
", rows: %lu (col1: %d, col2: %d)",
sql, i, col1, col2
);
XPUSHs( sv_2mortal( newSViv( 1 ) ) );
}
else {
for( i = 0; i < vres->row_count; i ++ ) {
XPUSHs( sv_2mortal( dbe_vrt_res_fetch(
type,
vres->rows[i].data[col1],
vres->rows[i].lengths[col1]
) ) );
}
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- selectcol_array('%s')= TRUE, rows: %lu (col: %d)",
sql, i, col1
);
}
}
_cleanup:
Safefree( sql2 );
if( params != NULL ) {
for( i = 0; i < (u_long) param_count; i ++ )
Safefree( params[i] );
Safefree( params );
}
if( res != NULL )
dbe_res_free( res );
if( drv_stmt != NULL && dd->stmt_free != NULL )
dd->stmt_free( drv_stmt );
if( r != DBE_OK ) {
IF_TRACE_CON( con, DBE_TRACE_SQL )
TRACE( con, DBE_TRACE_SQL, TRUE,
"<- selectcol_array('%s')= FALSE", sql );
HANDLE_ERROR_XS( this, "con", con, "Select failed" );
}
#/*****************************************************************************
# * CON_insert_id( this [, catalog [, schema [, table [, field]]]] )
# *****************************************************************************/
void
CON_insert_id( this, catalog = NULL, schema = NULL, table = NULL, field = NULL )
SV *this;
SV *catalog;
SV *schema;
SV *table;
SV *field;
PREINIT:
dbe_con_t *con;
drv_def_t *dd;
const char *cat_str, *schem_str, *tab_str, *field_str;
STRLEN cat_len, schem_len, tab_len, field_len;
u_xlong id;
int r;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
if( dd->con_insert_id == NULL )
NOFUNCTION_XS( this, "con", con, "insert_id" );
dbe_drv_set_error( con, 0, NULL );
if( catalog != NULL && SvPOK( catalog ) )
cat_str = SvPV( catalog, cat_len );
else
cat_str = NULL, cat_len = 0;
if( schema != NULL && SvPOK( schema ) )
schem_str = SvPV( schema, schem_len );
else
schem_str = NULL, schem_len = 0;
if( table != NULL && SvPOK( table ) )
tab_str = SvPV( table, tab_len );
else
tab_str = NULL, tab_len = 0;
if( field != NULL && SvPOK( field ) )
field_str = SvPV( field, field_len );
else
field_str = NULL, field_len = 0;
_retry:
r = dd->con_insert_id( con->drv_data, cat_str, cat_len,
schem_str, schem_len, tab_str, tab_len, field_str, field_len, &id );
switch( r ) {
case DBE_OK:
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- insert_id('%s','%s','%s','%s')= %lu",
cat_str, schem_str, tab_str, field_str, id
);
#if UVSIZE > 4
ST(0) = sv_2mortal( newSVuv( id ) );
#else
if( r <= 0xffffffff ) {
ST(0) = sv_2mortal( newSVuv( (UV) id ) );
}
else {
char tmp[22], *s1;
s1 = my_ultoa( tmp, id, 10 );
ST(0) = sv_2mortal( newSVpvn( tmp, s1 - tmp ) );
}
#endif
XSRETURN(1);
break;
case DBE_CONN_LOST:
if( dbe_con_reconnect( this, "con", con ) == DBE_OK )
goto _retry;
case DBE_ERROR:
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- insert_id('%s','%s','%s','%s')= FALSE",
cat_str, schem_str, tab_str, field_str
);
HANDLE_ERROR_XS( this, "con", con, "Get insert id failed" );
}
#/*****************************************************************************
# * CON_affected_rows( this )
# *****************************************************************************/
void
CON_affected_rows( this )
SV *this;
PREINIT:
dbe_con_t *con;
drv_def_t *dd;
xlong r;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
if( dd->con_affected_rows == NULL )
NOFUNCTION_XS( this, "con", con, "affected_rows" );
r = dd->con_affected_rows( con->drv_data );
#if IVSIZE >= 8
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- affected_rows= %ld", r );
XPUSHs( sv_2mortal( newSViv( (IV) r ) ) );
#else
if( r >= -2147483647 && r <= 2147483647 ) {
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- affected_rows= %ld", (size_t) r );
XPUSHs( sv_2mortal( newSViv( (IV) r ) ) );
}
else {
char tmp[22], *s1;
s1 = my_ltoa( tmp, r, 10 );
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- affected_rows= %s", tmp );
XPUSHs( sv_2mortal( newSVpvn( tmp, s1 - tmp ) ) );
}
#endif
#/*****************************************************************************
# * CON_quote( this, str )
# *****************************************************************************/
void
CON_quote( this, str )
SV *this;
SV *str;
PREINIT:
dbe_con_t *con;
drv_def_t *dd;
char *val;
STRLEN val_len;
SV *r;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
val = SvPV( str, val_len );
dd = con->drv->def;
if( dd->con_quote != NULL )
r = dd->con_quote( con->drv_data, val, val_len );
else
r = dbe_quote( val, val_len );
if( r == NULL ) {
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- quote('%s')= UNDEF", val );
XSRETURN_EMPTY;
}
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- quote('%s')= %s", val, SvPV_nolen( r ) );
ST(0) = sv_2mortal( r );
XSRETURN( 1 );
#/*****************************************************************************
# * CON_quote_bin( this, bin )
# *****************************************************************************/
void
CON_quote_bin( this, bin )
SV *this;
SV *bin;
PREINIT:
dbe_con_t *con;
drv_def_t *dd;
char *val;
STRLEN val_len;
SV *r;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
if( dd->con_quote_bin == NULL )
NOFUNCTION_XS( this, "con", con, "quote_bin" );
val = SvPV( bin, val_len );
r = dd->con_quote_bin( con->drv_data, val, val_len );
if( r == NULL ) {
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE, "<- quote_bin= NULL" );
XSRETURN_EMPTY;
}
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- quote_bin= %s", SvPV_nolen( r ) );
ST(0) = sv_2mortal( r );
XSRETURN( 1 );
#/*****************************************************************************
# * CON_quote_id( this, arg1 )
# *****************************************************************************/
void
CON_quote_id( this, arg1, ... )
SV *this;
SV *arg1;
PREINIT:
dbe_con_t *con;
drv_def_t *dd;
SV *r;
char **args;
int argc, i;
STRLEN l;
PPCODE:
if( arg1 != NULL ) {} /* avoid compiler warning */
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
argc = items - 1;
Newx( args, argc, char * );
for( i = 0; i < argc; i ++ )
args[i] = SvPV( ST(i + 1), l );
dd = con->drv->def;
if( dd->con_quote_id != NULL )
r = dd->con_quote_id( con->drv_data, (const char **) args, argc );
else
r = dbe_quote_id(
(const char **) args, argc,
con->quote_id_prefix, con->quote_id_suffix,
con->catalog_separator, con->catalog_location
);
Safefree( args );
if( r == NULL ) {
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- quote_id= UNDEF" );
XSRETURN_EMPTY;
}
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- quote_id= %s", SvPV_nolen( r ) );
ST(0) = sv_2mortal( r );
XSRETURN( 1 );
#/*****************************************************************************
# * CON_escape_pattern( this, str )
# *****************************************************************************/
void
CON_escape_pattern( this, str )
SV *this;
SV *str;
PREINIT:
dbe_con_t *con;
drv_def_t *dd;
char *val;
STRLEN val_len;
SV *r;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
val = SvPVx( str, val_len );
r = dbe_escape_pattern( val, val_len, con->escape_pattern );
if( r == NULL ) {
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- escape_pattern('%s')= UNDEF", val );
XSRETURN_EMPTY;
}
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- escape_pattern('%s')= %s", val, SvPV_nolen( r ) );
ST(0) = sv_2mortal( r );
XSRETURN( 1 );
#/*****************************************************************************
# * CON_name_convert( this [, convert] )
# *****************************************************************************/
void
CON_name_convert( this, ... )
SV *this;
PREINIT:
dbe_con_t *con;
const char *convert;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
if( con->flags & CON_NAME_LC )
ST(0) = sv_2mortal( newSVpvn( "lc", 2 ) );
else if( con->flags & CON_NAME_UC )
ST(0) = sv_2mortal( newSVpvn( "uc", 2 ) );
else
ST(0) = sv_2mortal( newSVpvn( "", 0 ) );
if( items > 1 ) {
convert = SvPV_nolen( ST(1) );
if( my_stristr( convert, "LC" ) != NULL ) {
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- name_convert= lowercase" );
con->flags &= (~CON_NAME_UC);
con->flags |= CON_NAME_LC;
}
else if( my_stristr( convert, "UC" ) != NULL ) {
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- name_convert= uppercase" );
con->flags &= (~CON_NAME_LC);
con->flags |= CON_NAME_UC;
}
else {
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- name_convert= disabled" );
con->flags &= (~(CON_NAME_LC + CON_NAME_UC));
}
}
XSRETURN(1);
#/*****************************************************************************
# * CON_auto_commit( this [, mode] )
# *****************************************************************************/
void
CON_auto_commit( this, mode = 1 )
SV *this;
int mode;
PREINIT:
dbe_con_t *con;
drv_def_t *dd;
int r;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
if( dd->con_auto_commit == NULL )
NOFUNCTION_XS( this, "con", con, "auto_commit" );
_retry:
r = dd->con_auto_commit( con->drv_data, mode );
switch( r ) {
case DBE_OK:
IF_TRACE_CON( con, DBE_TRACE_SQLFULL )
TRACE( con, DBE_TRACE_SQLFULL, TRUE,
"<- auto_commit(%d)= TRUE", mode );
XSRETURN_YES;
case DBE_CONN_LOST:
if( dbe_con_reconnect( this, "con", con ) == DBE_OK )
goto _retry;
case DBE_ERROR:
IF_TRACE_CON( con, DBE_TRACE_SQLFULL )
TRACE( con, DBE_TRACE_SQLFULL, TRUE,
"<- auto_commit(%d)= FALSE", mode );
HANDLE_ERROR_XS( this, "con", con, "Set auto_commit failed" );
}
#/*****************************************************************************
# * CON_begin_work( this )
# *****************************************************************************/
void
CON_begin_work( this )
SV *this;
PREINIT:
dbe_con_t *con;
drv_def_t *dd;
int r;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
if( dd->con_begin_work == NULL )
NOFUNCTION_XS( this, "con", con, "begin_work" );
_retry1:
r = dd->con_begin_work( con->drv_data );
switch( r ) {
case DBE_OK:
IF_TRACE_CON( con, DBE_TRACE_SQLFULL )
TRACE( con, DBE_TRACE_SQLFULL, TRUE, "<- begin_work= TRUE" );
XSRETURN_YES;
case DBE_CONN_LOST:
if( dbe_con_reconnect( this, "con", con ) == DBE_OK )
goto _retry1;
case DBE_ERROR:
IF_TRACE_CON( con, DBE_TRACE_SQLFULL )
TRACE( con, DBE_TRACE_SQLFULL, TRUE, "<- begin_work= FALSE" );
HANDLE_ERROR_XS( this, "con", con, "Begin work failed" );
}
#/*****************************************************************************
# * CON_commit( this )
# *****************************************************************************/
void
CON_commit( this )
SV *this;
PREINIT:
dbe_con_t *con;
drv_def_t *dd;
int r;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
if( dd->con_commit == NULL )
NOFUNCTION_XS( this, "con", con, "commit" );
r = dd->con_commit( con->drv_data );
switch( r ) {
case DBE_OK:
IF_TRACE_CON( con, DBE_TRACE_SQLFULL )
TRACE( con, DBE_TRACE_SQLFULL, TRUE, "<- commit= TRUE" );
XSRETURN_YES;
case DBE_CONN_LOST:
/* transaction is lost! */
dbe_con_reconnect( this, "con", con );
case DBE_ERROR:
IF_TRACE_CON( con, DBE_TRACE_SQLFULL )
TRACE( con, DBE_TRACE_SQLFULL, TRUE, "<- commit= FALSE" );
HANDLE_ERROR_XS( this, "con", con, "Commit failed" );
}
#/*****************************************************************************
# * CON_rollback( this )
# *****************************************************************************/
void
CON_rollback( this )
SV *this;
PREINIT:
dbe_con_t *con;
drv_def_t *dd;
int r;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
if( dd->con_rollback == NULL )
NOFUNCTION_XS( this, "con", con, "rollback" );
r = dd->con_rollback( con->drv_data );
switch( r ) {
case DBE_OK:
IF_TRACE_CON( con, DBE_TRACE_SQLFULL )
TRACE( con, DBE_TRACE_SQLFULL, TRUE, "<- rollback= TRUE" );
XSRETURN_YES;
case DBE_CONN_LOST:
/* rollback should appear, no problem */
if( dbe_con_reconnect( this, "con", con ) == DBE_OK )
XSRETURN_YES;
case DBE_ERROR:
IF_TRACE_CON( con, DBE_TRACE_SQLFULL )
TRACE( con, DBE_TRACE_SQLFULL, TRUE, "<- rollback= FALSE" );
HANDLE_ERROR_XS( this, "con", con, "Rollback failed" );
}
#/*****************************************************************************
# * CON_getinfo( this, id )
# *****************************************************************************/
void
CON_getinfo( this, id )
SV *this;
int id;
PREINIT:
dbe_con_t *con;
drv_def_t *dd;
int max, len, r, trace = 0;
char *res, tmp[128], *tmpe = NULL;
const char *name = NULL;
enum dbe_getinfo_type type;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
if( dd->con_getinfo == NULL )
NOFUNCTION_XS( this, "con", con, "getinfo" );
max = 256;
Newx( res, max, char );
IF_TRACE_CON( con, DBE_TRACE_ALL ) {
name = dbe_trace_getinfo_name( id );
if( name != NULL )
tmpe = tmp + sprintf( tmp, "<- getinfo(%s)= ", name );
else
tmpe = tmp + sprintf( tmp, "<- getinfo(%d)= ", id );
trace = 1;
}
_retry:
r = dd->con_getinfo( con->drv_data, id, res, max, &len, &type );
switch( r ) {
case DBE_OK:
switch( type ) {
case DBE_GETINFO_STRING:
default:
if( trace ) {
my_strcpy( tmpe, "'%s'" );
TRACE( con, DBE_TRACE_ALL, TRUE, tmp, res );
}
ST(0) = sv_2mortal( newSVpvn( res, len ) );
break;
case DBE_GETINFO_SHORTINT:
if( trace ) {
my_strcpy( tmpe, "%d" );
TRACE( con, DBE_TRACE_ALL, TRUE, tmp, *((short int *) res) );
}
ST(0) = sv_2mortal( newSViv( *((short int *) res) ) );
break;
case DBE_GETINFO_INTEGER:
if( trace ) {
my_strcpy( tmpe, "%d" );
TRACE( con, DBE_TRACE_ALL, TRUE, tmp, *((int *) res) );
}
ST(0) = sv_2mortal( newSViv( *((int *) res) ) );
break;
case DBE_GETINFO_32BITMASK:
if( trace ) {
my_strcpy( tmpe, "%u" );
TRACE( con, DBE_TRACE_ALL, TRUE, tmp, *((unsigned int *) res) );
}
ST(0) = sv_2mortal( newSVuv( *((unsigned int *) res) ) );
break;
}
Safefree( res );
XSRETURN(1);
case DBE_GETINFO_TRUNCATE:
max = len + 256;
Renew( res, max, char );
goto _retry;
case DBE_GETINFO_INVALIDARG:
if( trace ) {
my_strcpy( tmpe, "FALSE" );
TRACE( con, DBE_TRACE_ALL, TRUE, tmp );
}
Safefree( res );
dbe_drv_set_error( con,
DBE_GETINFO_INVALIDARG, "Invalid argument (%d)", id );
HANDLE_ERROR_XS( this, "con", con, "getinfo" );
case DBE_CONN_LOST:
if( dbe_con_reconnect( this, "con", con ) == DBE_OK )
goto _retry;
case DBE_ERROR:
default:
if( trace ) {
my_strcpy( tmpe, "NULL" );
TRACE( con, DBE_TRACE_ALL, TRUE, tmp );
}
Safefree( res );
HANDLE_ERROR_XS( this, "con", con, "getinfo" );
}
#/*****************************************************************************
# * CON_tables( this [, catalog [, schema [, table [, type]]]] )
# *****************************************************************************/
void
CON_tables( this, catalog = NULL, schema = NULL, table = NULL, type = NULL )
SV *this;
SV *catalog;
SV *schema;
SV *table;
SV *type;
PREINIT:
dbe_con_t *con;
dbe_res_t *res = NULL;
drv_def_t *dd;
STRLEN yl = 0, cl = 0, sl = 0, tl = 0;
char *ys = NULL, *cs = NULL, *ss = NULL, *ts = NULL;
int r;
SV *sv;
HV *hv;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
if( dd->con_tables == NULL )
NOFUNCTION_XS( this, "con", con, "tables" );
if( catalog != NULL && SvPOK( catalog ) )
cs = SvPVx( catalog, cl );
if( schema != NULL && SvPOK( schema ) )
ss = SvPVx( schema, sl );
if( table != NULL && SvPOK( table ) )
ts = SvPVx( table, tl );
if( type != NULL && SvPOK( type ) )
ys = SvPVx( type, yl );
_retry1:
r = dd->con_tables(
con->drv_data, cs, cl, ss, sl, ts, tl, ys, yl, &res );
if( r != DBE_OK || res == NULL ) {
switch( r ) {
case DBE_CONN_LOST:
if( dbe_con_reconnect( this, "con", con ) == DBE_OK )
goto _retry1;
case DBE_ERROR:
case DBE_OK:
default:
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- tables('%s','%s','%s','%s')= NULL",
cs, ss, ts, ys
);
if( r != DBE_OK )
HANDLE_ERROR_XS( this, "con", con, "Get tables failed" );
dbe_drv_set_error( con, 0, NULL );
XSRETURN_EMPTY;
}
}
dbe_res_add( con, res );
hv = gv_stashpv( con->drv->class_res, FALSE );
sv = sv_2mortal( newSViv( (IV) res->id ) );
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- tables('%s','%s','%s','%s')= %s=SCALAR(0x%x)",
cs, ss, ts, ys, con->drv->class_res, (size_t) sv
);
ST(0) = sv_2mortal( sv_bless( newRV( sv ), hv ) );
XSRETURN(1);
#/*****************************************************************************
# * CON_columns( this [, catalog [, schema [, table [, column]]]] )
# *****************************************************************************/
void
CON_columns( this, catalog = NULL, schema = NULL, table = NULL, column = NULL )
SV *this;
SV *catalog;
SV *schema;
SV *table;
SV *column;
PREINIT:
dbe_con_t *con;
dbe_res_t *res = NULL;
drv_def_t *dd;
STRLEN cl = 0, sl = 0, tl = 0, ol = 0;
char *cs = NULL, *ss = NULL, *ts = NULL, *os = NULL;
int r;
SV *sv;
HV *hv;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
if( dd->con_columns == NULL )
NOFUNCTION_XS( this, "con", con, "columns" );
if( catalog != NULL && SvPOK( catalog ) )
cs = SvPVx( catalog, cl );
if( schema != NULL && SvPOK( schema ) )
ss = SvPVx( schema, sl );
if( table != NULL && SvPOK( table ) )
ts = SvPVx( table, tl );
if( column != NULL && SvPOK( column ) )
os = SvPVx( column, ol );
_retry1:
r = dd->con_columns(
con->drv_data, cs, cl, ss, sl, ts, tl, os, ol, &res );
if( r != DBE_OK || res == NULL ) {
switch( r ) {
case DBE_CONN_LOST:
if( dbe_con_reconnect( this, "con", con ) == DBE_OK )
goto _retry1;
case DBE_ERROR:
case DBE_OK:
default:
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- columns('%s','%s','%s','%s')= NULL",
cs, ss, ts, os
);
if( r != DBE_OK )
HANDLE_ERROR_XS( this, "con", con, "Get columns failed" );
dbe_drv_set_error( con, 0, NULL );
XSRETURN_EMPTY;
}
}
dbe_res_add( con, res );
hv = gv_stashpv( con->drv->class_res, FALSE );
sv = sv_2mortal( newSViv( (IV) res->id ) );
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- columns('%s','%s','%s','%s')= %s=SCALAR(0x%x)",
cs, ss, ts, os, con->drv->class_res, (size_t) sv
);
ST(0) = sv_2mortal( sv_bless( newRV( sv ), hv ) );
XSRETURN(1);
#/*****************************************************************************
# * CON_primary_keys( this, catalog, schema, table )
# *****************************************************************************/
void
CON_primary_keys( this, catalog, schema, table )
SV *this;
SV *catalog;
SV *schema;
SV *table;
PREINIT:
dbe_con_t *con;
dbe_res_t *res = NULL;
drv_def_t *dd;
STRLEN cl = 0, sl = 0, tl = 0;
char *cs = NULL, *ss = NULL, *ts = NULL;
int r;
SV *sv;
HV *hv;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
if( dd->con_primary_keys == NULL )
NOFUNCTION_XS( this, "con", con, "primary_keys" );
ts = SvPVx( table, tl );
if( ! tl ) {
dbe_drv_set_error( con, -1, "No table defined" );
XSRETURN_EMPTY;
}
if( schema != NULL && SvPOK( schema ) )
ss = SvPVx( schema, sl );
if( catalog != NULL && SvPOK( catalog ) )
cs = SvPVx( catalog, cl );
_retry1:
r = dd->con_primary_keys( con->drv_data, cs, cl, ss, sl, ts, tl, &res );
if( r != DBE_OK || res == NULL ) {
switch( r ) {
case DBE_CONN_LOST:
if( dbe_con_reconnect( this, "con", con ) == DBE_OK )
goto _retry1;
case DBE_ERROR:
case DBE_OK:
default:
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- primary_keys('%s','%s','%s')= NULL",
cs, ss, ts
);
if( r != DBE_OK )
HANDLE_ERROR_XS( this, "con", con, "Get primary keys failed" );
dbe_drv_set_error( con, 0, NULL );
XSRETURN_EMPTY;
}
}
dbe_res_add( con, res );
hv = gv_stashpv( con->drv->class_res, FALSE );
sv = sv_2mortal( newSViv( (IV) res->id ) );
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- primary_keys('%s','%s','%s')= %s=SCALAR(0x%x)",
cs, ss, ts, con->drv->class_res, (size_t) sv
);
ST(0) = sv_2mortal( sv_bless( newRV( sv ), hv ) );
XSRETURN(1);
#/*****************************************************************************
# * CON_foreign_keys( this, pk_cat, pk_schem, pk_table [, pk_cat [, pk_schem [, pk_table]]] )
# *****************************************************************************/
void
CON_foreign_keys( this, pk_cat, pk_schem, pk_table, fk_cat = NULL, fk_schem = NULL, fk_table = NULL )
SV *this;
SV *pk_cat;
SV *pk_schem;
SV *pk_table;
SV *fk_cat;
SV *fk_schem;
SV *fk_table;
PREINIT:
dbe_con_t *con;
dbe_res_t *res = NULL;
drv_def_t *dd;
STRLEN pcl = 0, psl = 0, ptl = 0;
char *pcs = NULL, *pss = NULL, *pts = NULL;
STRLEN fcl = 0, fsl = 0, ftl = 0;
char *fcs = NULL, *fss = NULL, *fts = NULL;
int r;
SV *sv;
HV *hv;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
if( dd->con_foreign_keys == NULL )
NOFUNCTION_XS( this, "con", con, "foreign_keys" );
if( pk_cat != NULL && SvPOK( pk_cat ) )
pcs = SvPVx( pk_cat, pcl );
if( pk_schem != NULL && SvPOK( pk_schem ) )
pss = SvPVx( pk_schem, psl );
if( pk_table != NULL && SvPOK( pk_table ) )
pts = SvPVx( pk_table, ptl );
if( fk_cat != NULL && SvPOK( fk_cat ) )
fcs = SvPVx( fk_cat, fcl );
if( fk_schem != NULL && SvPOK( fk_schem ) )
fss = SvPVx( fk_schem, fsl );
if( fk_table != NULL && SvPOK( fk_table ) )
fts = SvPVx( fk_table, ftl );
if( ! ptl && ! ftl ) {
dbe_drv_set_error( con, -1, "At least one table name is required" );
HANDLE_ERROR_XS( this, "con", con, "Get foreign keys" );
}
_retry1:
r = dd->con_foreign_keys( con->drv_data,
pcs, pcl, pss, psl, pts, ptl, fcs, fcl, fss, fsl, fts, ftl, &res );
if( r != DBE_OK || res == NULL ) {
switch( r ) {
case DBE_CONN_LOST:
if( dbe_con_reconnect( this, "con", con ) == DBE_OK )
goto _retry1;
case DBE_ERROR:
case DBE_OK:
default:
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- foreign_keys('%s','%s','%s','%s','%s','%s')= NULL",
pcs, pss, pts, fcs, fss, fts
);
if( r != DBE_OK )
HANDLE_ERROR_XS( this, "con", con, "Get foreign keys" );
dbe_drv_set_error( con, 0, NULL );
XSRETURN_EMPTY;
}
}
dbe_res_add( con, res );
hv = gv_stashpv( con->drv->class_res, FALSE );
sv = sv_2mortal( newSViv( (IV) res->id ) );
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- foreign_keys('%s','%s','%s','%s','%s','%s')= %s=SCALAR(0x%x)",
pcs, pss, pts, fcs, fss, fts, con->drv->class_res, (size_t) sv
);
ST(0) = sv_2mortal( sv_bless( newRV( sv ), hv ) );
XSRETURN(1);
#/*****************************************************************************
# * CON_data_sources( this [, wild] )
# *****************************************************************************/
void
CON_data_sources( this, wild = NULL )
SV *this;
SV *wild;
PREINIT:
dbe_con_t *con;
drv_def_t *dd;
char *sw = NULL;
STRLEN lw = 0;
int r, dc = 0;
SV **ds = NULL;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
if( dd->con_data_sources == NULL )
NOFUNCTION_XS( this, "con", con, "data_sources" );
if( wild != NULL )
sw = SvPVx( wild, lw );
_retry1:
r = dd->con_data_sources( con->drv_data, sw, lw, &ds, &dc );
if( r != DBE_OK || dc <= 0 || ds == NULL ) {
switch( r ) {
case DBE_CONN_LOST:
if( dbe_con_reconnect( this, "con", con ) == DBE_OK )
goto _retry1;
case DBE_ERROR:
case DBE_OK:
default:
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- data_sources('%s')= FALSE", sw );
if( r != DBE_OK )
HANDLE_ERROR_XS( this, "con", con, "Get data sources failed" );
dbe_drv_set_error( con, 0, NULL );
XSRETURN_EMPTY;
}
}
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- data_sources('%s')= TRUE (count: %d)", sw, dc );
if( ds == NULL )
XSRETURN_EMPTY;
for( r = 0; r < dc; r ++ ) {
if( ds[r] != NULL )
XPUSHs( sv_2mortal( ds[r] ) );
}
if( dd->mem_free != NULL )
dd->mem_free( ds );
else
Safefree( ds );
#/*****************************************************************************
# * CON_statistics( this [, catalog [, schema [, table [, unique_only]]]] )
# *****************************************************************************/
void
CON_statistics( this, catalog = NULL, schema = NULL, table = NULL, unique_only = 0 )
SV *this;
SV *catalog;
SV *schema;
SV *table;
int unique_only;
PREINIT:
dbe_con_t *con;
dbe_res_t *res = NULL;
drv_def_t *dd;
STRLEN cl = 0, sl = 0, tl = 0;
char *cs = NULL, *ss = NULL, *ts = NULL;
int r;
SV *sv;
HV *hv;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
if( dd->con_statistics == NULL )
NOFUNCTION_XS( this, "con", con, "statistics" );
if( catalog != NULL && SvPOK( catalog ) )
cs = SvPVx( catalog, cl );
if( schema != NULL && SvPOK( schema ) )
ss = SvPVx( schema, sl );
if( table != NULL && SvPOK( table ) )
ts = SvPVx( table, tl );
_retry1:
r = dd->con_statistics(
con->drv_data, cs, cl, ss, sl, ts, tl, unique_only, &res );
if( r != DBE_OK || res == NULL ) {
switch( r ) {
case DBE_CONN_LOST:
if( dbe_con_reconnect( this, "con", con ) == DBE_OK )
goto _retry1;
case DBE_ERROR:
case DBE_OK:
default:
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- statistics('%s','%s','%s','%d')= NULL",
cs, ss, ts, unique_only
);
if( r != DBE_OK )
HANDLE_ERROR_XS( this, "con", con, "Get statistics failed" );
dbe_drv_set_error( con, 0, NULL );
XSRETURN_EMPTY;
}
}
dbe_res_add( con, res );
hv = gv_stashpv( con->drv->class_res, FALSE );
sv = sv_2mortal( newSViv( (IV) res->id ) );
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- statistics('%s','%s','%s','%d')= %s=SCALAR(0x%x)",
cs, ss, ts, unique_only, con->drv->class_res, (size_t) sv
);
ST(0) = sv_2mortal( sv_bless( newRV( sv ), hv ) );
XSRETURN(1);
#/*****************************************************************************
# * CON_special_columns( this [, catalog [, schema [, table [, unique_only]]]] )
# *****************************************************************************/
void
CON_special_columns( this, type = 1, catalog = NULL, schema = NULL, table = NULL, scope = 0, nullable = 1 )
SV *this;
int type;
SV *catalog;
SV *schema;
SV *table;
int scope;
int nullable;
PREINIT:
dbe_con_t *con;
dbe_res_t *res = NULL;
drv_def_t *dd;
STRLEN cl = 0, sl = 0, tl = 0;
char *cs = NULL, *ss = NULL, *ts = NULL;
int r;
SV *sv;
HV *hv;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
/*
switch( type ) {
case SQL_BEST_ROWID:
case SQL_ROWVER:
break;
default:
dbe_drv_set_error( con, -1, "Invalid identifier type %d", type );
HANDLE_ERROR_XS( this, "con", con, "Get special columns" );
}
*/
dd = con->drv->def;
if( dd->con_special_columns == NULL )
NOFUNCTION_XS( this, "con", con, "special_columns" );
if( catalog != NULL && SvPOK( catalog ) )
cs = SvPVx( catalog, cl );
if( schema != NULL && SvPOK( schema ) )
ss = SvPVx( schema, sl );
if( table != NULL && SvPOK( table ) )
ts = SvPVx( table, tl );
_retry1:
r = dd->con_special_columns(
con->drv_data, type, cs, cl, ss, sl, ts, tl, scope, nullable,
&res
);
if( r != DBE_OK || res == NULL ) {
switch( r ) {
case DBE_CONN_LOST:
if( dbe_con_reconnect( this, "con", con ) == DBE_OK )
goto _retry1;
case DBE_ERROR:
case DBE_OK:
default:
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- special_columns('%s','%s','%s',%d,%d)= NULL",
cs, ss, ts, scope, nullable
);
if( r != DBE_OK )
HANDLE_ERROR_XS( this, "con", con, "Get special columns" );
dbe_drv_set_error( con, 0, NULL );
XSRETURN_EMPTY;
}
}
dbe_res_add( con, res );
hv = gv_stashpv( con->drv->class_res, FALSE );
sv = sv_2mortal( newSViv( (IV) res->id ) );
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- special_columns('%s','%s','%s',%d,%d)= %s=SCALAR(0x%x)",
cs, ss, ts, scope, nullable, con->drv->class_res, (size_t) sv
);
ST(0) = sv_2mortal( sv_bless( newRV( sv ), hv ) );
XSRETURN(1);
#/*****************************************************************************
# * CON_type_info( this, sql_type )
# *****************************************************************************/
void
CON_type_info( this, sql_type = NULL )
SV *this;
SV *sql_type;
PREINIT:
dbe_con_t *con;
dbe_res_t *res = NULL;
drv_def_t *dd;
int r;
enum sql_type type;
SV *sv;
HV *hv;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
if( dd->con_type_info == NULL )
NOFUNCTION_XS( this, "con", con, "type_info" );
if( sql_type == NULL || ! SvOK( sql_type ) )
type = SQL_ALL_TYPES;
else
if( SvIOK( sql_type ) )
type = (enum sql_type) SvIV( sql_type );
else
type = dbe_sql_type_by_name( SvPV_nolen( sql_type ) );
_retry1:
r = dd->con_type_info( con->drv_data, type, &res );
if( r != DBE_OK || res == NULL ) {
switch( r ) {
case DBE_CONN_LOST:
if( dbe_con_reconnect( this, "con", con ) == DBE_OK )
goto _retry1;
case DBE_ERROR:
case DBE_OK:
default:
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- type_info('%d')= NULL", (int) type );
if( r != DBE_OK )
HANDLE_ERROR_XS( this, "con", con, "Get type info failed" );
dbe_drv_set_error( con, 0, NULL );
XSRETURN_EMPTY;
}
}
dbe_res_add( con, res );
hv = gv_stashpv( con->drv->class_res, TRUE );
sv = sv_2mortal( newSViv( (IV) res->id ) );
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- type_info('%d')= %s=SCALAR(0x%x)",
(int) type, con->drv->class_res, (size_t) sv
);
ST(0) = sv_2mortal( sv_bless( newRV( sv ), hv ) );
XSRETURN(1);
#/*****************************************************************************
# * CON_row_limit()
# *****************************************************************************/
void
CON_row_limit( this, ... )
SV *this;
PREINIT:
dbe_con_t *con;
drv_def_t *dd;
u_xlong limit, offset;
int r;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
if( dd->con_row_limit == NULL )
NOFUNCTION_XS( this, "con", con, "row_limit" );
if( items > 1 )
limit = SvUV( ST(1) );
else
limit = 0;
if( items > 2 )
offset = SvUV( ST(2) );
else
offset = 0;
r = dd->con_row_limit( con->drv_data, limit, offset );
if( r != DBE_OK ) {
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- row_limit('%lu','%lu')= FALSE", limit, offset );
HANDLE_ERROR_XS( this, "con", con, "Set row limit" );
}
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- row_limit('%lu','%lu')= TRUE", limit, offset );
XSRETURN_YES;
#/*****************************************************************************
# * CON_driver_has( this, name )
# *****************************************************************************/
#define CON_driver_has(fnc) { \
f = 1; \
x = fnc; \
goto check; \
}
void
CON_driver_has( this, name )
SV *this;
const char *name;
PREINIT:
dbe_con_t *con;
drv_def_t *dd;
const void *x = NULL;
int f = 0;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
switch( tolower( *name ) ) {
case 'a':
if( my_stricmp( name, "affected_rows" ) == 0 )
CON_driver_has( dd->con_affected_rows );
if( my_stricmp( name, "attr_get" ) == 0 )
CON_driver_has( dd->con_get_attr );
if( my_stricmp( name, "attr_set" ) == 0 )
CON_driver_has( dd->con_set_attr );
if( my_stricmp( name, "auto_commit" ) == 0 )
CON_driver_has( dd->con_auto_commit );
break;
case 'b':
if( my_stricmp( name, "begin_work" ) == 0 )
CON_driver_has( dd->con_begin_work );
if( my_stricmp( name, "bind_param" ) == 0 )
CON_driver_has( dd->stmt_bind_param );
break;
case 'c':
if( my_stricmp( name, "columns" ) == 0 )
CON_driver_has( dd->con_columns );
if( my_stricmp( name, "commit" ) == 0 )
CON_driver_has( dd->con_commit );
break;
case 'd':
if( my_stricmp( name, "data_sources" ) == 0 )
CON_driver_has( dd->con_data_sources );
break;
case 'e':
if( my_stricmp( name, "execute" ) == 0 )
CON_driver_has( dd->stmt_execute );
break;
case 'f':
if( my_stricmp( name, "fetch_field" ) == 0 )
CON_driver_has( dd->res_fetch_field );
if( my_stricmp( name, "fetch_names" ) == 0 )
CON_driver_has( dd->res_fetch_names );
if( my_stricmp( name, "fetch_row" ) == 0 )
CON_driver_has( dd->res_fetch_row );
if( my_stricmp( name, "field_seek" ) == 0 )
CON_driver_has( dd->res_field_seek );
if( my_stricmp( name, "field_tell" ) == 0 )
CON_driver_has( dd->res_field_tell );
if( my_stricmp( name, "foreign_keys" ) == 0 )
CON_driver_has( dd->con_foreign_keys );
break;
case 'g':
if( my_stricmp( name, "getinfo" ) == 0 )
CON_driver_has( dd->con_getinfo );
break;
case 'i':
if( my_stricmp( name, "insert_id" ) == 0 )
CON_driver_has( dd->con_insert_id );
break;
case 'l':
if( my_stricmp( name, "lob_read" ) == 0 )
CON_driver_has( dd->con_lob_read );
if( my_stricmp( name, "lob_size" ) == 0 )
CON_driver_has( dd->con_lob_size );
if( my_stricmp( name, "lob_write" ) == 0 )
CON_driver_has( dd->con_lob_write );
break;
case 'p':
if( my_stricmp( name, "param_count" ) == 0 )
CON_driver_has( dd->stmt_param_count );
if( my_stricmp( name, "prepare" ) == 0 )
CON_driver_has( dd->con_prepare );
if( my_stricmp( name, "primary_keys" ) == 0 )
CON_driver_has( dd->con_primary_keys );
break;
case 'q':
if( my_stricmp( name, "query" ) == 0 )
CON_driver_has( dd->con_query );
if( my_stricmp( name, "quote" ) == 0 )
CON_driver_has( dd->con_quote );
if( my_stricmp( name, "quote_bin" ) == 0 )
CON_driver_has( dd->con_quote_bin );
if( my_stricmp( name, "quote_id" ) == 0 )
CON_driver_has( dd->con_quote_id );
break;
case 'r':
if( my_stricmp( name, "reconnect" ) == 0 )
CON_driver_has( dd->con_reconnect );
if( my_stricmp( name, "rollback" ) == 0 )
CON_driver_has( dd->con_rollback );
if( my_stricmp( name, "row_limit" ) == 0 )
CON_driver_has( dd->con_row_limit );
if( my_stricmp( name, "row_seek" ) == 0 )
CON_driver_has( dd->res_row_seek );
if( my_stricmp( name, "row_tell" ) == 0 )
CON_driver_has( dd->res_row_tell );
break;
case 's':
if( my_stricmp( name, "statistics" ) == 0 )
CON_driver_has( dd->con_statistics );
break;
case 't':
if( my_stricmp( name, "tables" ) == 0 )
CON_driver_has( dd->con_tables );
if( my_stricmp( name, "type_info" ) == 0 )
CON_driver_has( dd->con_type_info );
break;
}
check:
if( x != NULL ) {
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- driver_has('%s')= TRUE", name );
XSRETURN_YES;
}
if( f != 0 ) {
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- driver_has('%s')= FALSE", name
);
XSRETURN_NO;
}
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE, "<- driver_has('%s')= NULL", name );
dbe_drv_set_error( con, -1, "Unknown function '%s'", name );
HANDLE_ERROR_XS( this, "con", con, "Driver has" );
#/*****************************************************************************
# * CON_trace()
# *****************************************************************************/
void
CON_trace( this, ... )
SV *this;
PREINIT:
dbe_con_t *con;
drv_def_t *dd;
PPCODE:
/* Usage: $con->trace( [level [, handle]] ) */
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
dd = con->drv->def;
con->trace_local = 1;
if( items > 1 ) {
if( SvIOK( ST(1) ) ) {
con->trace_level = (int) SvIV( ST(1) );
}
else {
con->trace_level =
dbe_trace_level_by_name( SvPV_nolen( ST(1) ) );
}
}
else {
con->trace_level = DBE_TRACE_NONE;
}
if( dd->con_set_trace != NULL )
dd->con_set_trace( con->drv_data, con->trace_level );
if( con->trace_level <= DBE_TRACE_NONE )
XSRETURN_YES;
if( items > 2 && SvOK( ST(2) ) ) {
if( con->sv_trace_out != NULL )
SvREFCNT_dec( con->sv_trace_out );
con->sv_trace_out = SvREFCNT_inc( ST(2) );
}
TRACE( NULL, 0, TRUE,
"DBE v%s build %u %s trace level set to %s [%d] for %s=SCALAR(0x%lx)",
XS_VERSION, DBE_BUILD,
#ifdef USE_ITHREADS
"thread-multi",
#else
"",
#endif
dbe_trace_name_by_level( con->trace_level ),
con->trace_level, con->drv->class_con, SvRV( this )
);
if( ! PL_dowarn ) {
dbe_perl_print( con->sv_trace_out,
" Note: Perl is running without the -w option\n", 48
);
}
XSRETURN_YES;
#/*****************************************************************************
# * CON_attr_set( this, name, value )
# *****************************************************************************/
void
CON_attr_set( this, name, value )
SV *this;
char *name;
SV *value;
PREINIT:
dbe_con_t *con;
drv_def_t *dd;
int r;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
switch( tolower( *name ) ) {
case 'c':
if( my_stricmp( name, "croak" ) == 0 ) {
if( STR_TRUE( SvPV_nolen( value ) ) )
con->flags |= CON_ONERRORDIE;
else
con->flags &= (~CON_ONERRORDIE);
}
else
goto _default;
break;
case 'r':
if( my_stricmp( name, "reconnect" ) == 0 ) {
con->reconnect_count = (u_int) SvUV( value );
}
else
goto _default;
break;
case 'w':
if( my_stricmp( name, "warn" ) == 0 ) {
if( STR_TRUE( SvPV_nolen( value ) ) )
con->flags |= CON_ONERRORWARN;
else
con->flags &= (~CON_ONERRORWARN);
}
else
goto _default;
break;
default:
_default:
dd = con->drv->def;
if( dd->con_set_attr == NULL )
goto _invalid;
_retry:
r = dd->con_set_attr( con->drv_data, name, value );
switch( r ) {
case DBE_OK:
break;
case DBE_INVALIDARG:
goto _invalid;
case DBE_CONN_LOST:
if( dbe_con_reconnect( this, "con", con ) == DBE_OK )
goto _retry;
case DBE_ERROR:
default:
goto _error;
}
}
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- attr_set('%s' => '%s')= TRUE", name, SvPV_nolen( value ) );
XSRETURN_YES;
_invalid:
dbe_drv_set_error( con, -1, "Invalid attribute '%s'", name );
_error:
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- attr_set('%s' => '%s')= FALSE", name, SvPV_nolen( value ) );
HANDLE_ERROR_XS( this, "con", con, "Set attr failed" );
#/*****************************************************************************
# * CON_attr_get( this, name )
# *****************************************************************************/
void
CON_attr_get( this, name )
SV *this;
char *name;
PREINIT:
dbe_con_t *con;
drv_def_t *dd;
int r;
SV *ret = NULL;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
switch( tolower( *name ) ) {
case 'c':
if( my_stricmp( name, "croak" ) == 0 ) {
if( con->flags & CON_ONERRORDIE )
ret = newSViv( 1 );
else
ret = newSViv( 0 );
}
else
goto _default;
break;
case 'r':
if( my_stricmp( name, "reconnect" ) == 0 ) {
ret = newSVuv( (UV) con->reconnect_count );
}
else
goto _default;
break;
case 'w':
if( my_stricmp( name, "warn" ) == 0 ) {
if( con->flags & CON_ONERRORWARN )
ret = newSViv( 1 );
else
ret = newSViv( 0 );
}
else
goto _default;
break;
default:
_default:
dd = con->drv->def;
if( dd->con_get_attr == NULL )
goto _invalid;
_retry:
r = dd->con_get_attr( con->drv_data, name, &ret );
switch( r ) {
case DBE_OK:
break;
case DBE_INVALIDARG:
goto _invalid;
case DBE_CONN_LOST:
if( dbe_con_reconnect( this, "con", con ) == DBE_OK )
goto _retry;
case DBE_ERROR:
default:
goto _error;
}
}
if( ret == NULL )
ret = newSV(0);
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- attr_get('%s')= '%s'", name, SvPV_nolen( ret ) );
ST(0) = sv_2mortal( ret );
XSRETURN(1);
_invalid:
dbe_drv_set_error( con, -1, "Invalid attribute '%s'", name );
_error:
if( ret != NULL )
ret = sv_2mortal( ret );
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- attr_get('%s')= FALSE", name );
HANDLE_ERROR_XS( this, "con", con, "Get attr failed" );
#/*****************************************************************************
# * CON_set_error_handler( this, fnc, arg )
# *****************************************************************************/
void
CON_set_error_handler( this, fnc = NULL, arg = NULL )
SV *this;
SV *fnc;
SV *arg;
PREINIT:
dbe_con_t *con;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
if( con->error_handler != NULL ) {
sv_2mortal( con->error_handler );
if( con->error_handler_arg != NULL )
sv_2mortal( con->error_handler_arg );
}
if( fnc == NULL || ! SvOK(fnc) || (! SvROK(fnc) && ! SvPOK(fnc)) ) {
#ifdef DBE_DEBUG
_debug( "clear error_handler\n" );
#endif
con->error_handler = NULL;
con->error_handler_arg = NULL;
}
else {
#ifdef DBE_DEBUG
_debug( "set error_handler %d\n", SvTYPE( fnc ) );
#endif
con->error_handler = newSVsv( fnc );
if( arg != NULL )
con->error_handler_arg = newSVsv( arg );
}
XSRETURN_YES;
#/*****************************************************************************
# * CON_errno( this )
# *****************************************************************************/
void
CON_errno( this )
SV *this;
PREINIT:
dbe_con_t *con;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
XSRETURN_IV( (IV) con->last_errno );
#/*****************************************************************************
# * CON_error( this )
# *****************************************************************************/
void
CON_error( this )
SV *this;
PREINIT:
dbe_con_t *con;
PPCODE:
if( (con = dbe_con_find( this )) == NULL )
XSRETURN_EMPTY;
ST(0) = sv_2mortal( newSVpvn(
con->last_error, strlen( con->last_error ) ) );
XSRETURN(1);
#/*############################# RESULT CLASS ################################*/
MODULE = DBE PACKAGE = DBE::RES PREFIX = RES_
#/*****************************************************************************
# * RES_DESTROY()
# *****************************************************************************/
void
RES_DESTROY( ... )
PREINIT:
dbe_res_t *res;
CODE:
if( items < 1 || (res = dbe_res_find( ST(0) )) == NULL )
XSRETURN_EMPTY;
#ifdef DBE_DEBUG
_debug( "DESTROY called for res %lu, ref %d\n", res->id, res->refcnt );
#endif
res->refcnt --;
if( res->refcnt < 0 ) {
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- DESTROY= %s=SCALAR(0x%x)",
res->con->drv->class_res, SvRV( ST(0) )
);
dbe_res_rem( res );
}
#/*****************************************************************************
# * RES_close( this )
# *****************************************************************************/
void
RES_close( this )
SV *this;
PREINIT:
dbe_res_t *res;
CODE:
if( (res = dbe_res_find( this )) == NULL )
XSRETURN_EMPTY;
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- DESTROY= %s=SCALAR(0x%x)",
res->con->drv->class_res, SvRV( this )
);
dbe_res_rem( res );
XSRETURN_YES;
#/*****************************************************************************
# * RES_bind_column( this )
# *****************************************************************************/
void
RES_bind_column( this, c_num, c_var = NULL )
SV *this;
unsigned long c_num;
SV *c_var;
PREINIT:
dbe_res_t *res;
SV **psv;
PPCODE:
if( (res = dbe_res_find( this )) == NULL )
XSRETURN_EMPTY;
if( c_num < 1 || c_num > res->num_fields ) {
dbe_drv_set_error( res->con, -1, "Column %lu out of range (%lu-%lu)",
c_num, res->num_fields ? 1 : 0, res->num_fields );
HANDLE_ERROR_XS( this, "res", res->con, "Bind column failed" );
}
psv = res->sv_bind + c_num - 1;
if( *psv != NULL ) {
/* release previous bound variable */
#if (DBE_DEBUG > 1)
_debug( "sv 0x%lx refcnt %d\n", *psv, SvREFCNT( *psv ) );
#endif
SvREFCNT_dec( *psv );
}
if( c_var == NULL || SvREADONLY( c_var ) ) {
*psv = NULL;
}
else if( SvROK( c_var ) ) {
*psv = SvREFCNT_inc( SvRV( c_var ) );
#if (DBE_DEBUG > 1)
_debug( "sv 0x%lx refcnt %d\n", *psv, SvREFCNT( *psv ) );
#endif
}
else {
*psv = SvREFCNT_inc( c_var );
#if (DBE_DEBUG > 1)
_debug( "sv 0x%lx refcnt %d\n", *psv, SvREFCNT( *psv ) );
#endif
}
XSRETURN_YES;
#/*****************************************************************************
# * RES_bind( this )
# *****************************************************************************/
void
RES_bind( this, ... )
SV *this;
PREINIT:
dbe_res_t *res;
SV **psv, *c_var;
u_long i;
PPCODE:
if( (res = dbe_res_find( this )) == NULL )
XSRETURN_EMPTY;
for( i = 1, psv = res->sv_bind; i < (u_long) items; i ++, psv ++ ) {
if( i > res->num_fields ) {
dbe_drv_set_error( res->con, -1,
"Column %lu out of range (%lu-%lu)", i, 1, res->num_fields );
HANDLE_ERROR_XS( this, "res", res->con, "Bind column failed" );
}
c_var = ST(i);
if( *psv != NULL ) {
/* release previous bound variable */
#if (DBE_DEBUG > 1)
_debug( "sv 0x%lx refcnt %d\n", *psv, SvREFCNT( *psv ) );
#endif
SvREFCNT_dec( *psv );
}
if( c_var == NULL || SvREADONLY( c_var ) ) {
*psv = NULL;
}
else if( SvROK( c_var ) ) {
*psv = SvREFCNT_inc( SvRV( c_var ) );
#if (DBE_DEBUG > 1)
_debug( "sv 0x%lx refcnt %d\n", *psv, SvREFCNT( *psv ) );
#endif
}
else {
*psv = SvREFCNT_inc( c_var );
#if (DBE_DEBUG > 1)
_debug( "sv 0x%lx refcnt %d\n", *psv, SvREFCNT( *psv ) );
#endif
}
}
XSRETURN_YES;
#/*****************************************************************************
# * RES_num_fields( this )
# *****************************************************************************/
void
RES_num_fields( this )
SV *this;
PREINIT:
dbe_res_t *res;
PPCODE:
if( (res = dbe_res_find( this )) == NULL )
XSRETURN_EMPTY;
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- num_fields= %lu", res->num_fields );
ST(0) = sv_2mortal( newSVuv( res->num_fields ) );
XSRETURN(1);
#/*****************************************************************************
# * RES_num_rows( this )
# *****************************************************************************/
void
RES_num_rows( this )
SV *this;
PREINIT:
dbe_res_t *res;
drv_def_t *dd;
xlong r;
PPCODE:
if( (res = dbe_res_find( this )) == NULL )
XSRETURN_EMPTY;
switch( res->type ) {
case RES_TYPE_DRV:
dd = res->con->drv->def;
if( dd->res_num_rows != NULL ) {
r = dd->res_num_rows( res->drv_data );
#if IVSIZE >= 8
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- num_rows= %ld", r );
XPUSHs( sv_2mortal( newSViv( (IV) r ) ) );
#else
if( r >= -2147483647 && r <= 2147483647 ) {
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- num_rows= %ld", r );
XPUSHs( sv_2mortal( newSViv( (IV) r ) ) );
}
else {
char tmp[22], *s1;
s1 = my_ltoa( tmp, r, 10 );
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- num_rows= %s", tmp );
XPUSHs( sv_2mortal( newSVpvn( tmp, s1 - tmp ) ) );
}
#endif
}
else
XSRETURN_IV( -1 );
break;
case RES_TYPE_VRT:
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- num_rows= %lu",
((dbe_vrt_res_t *) res->drv_data)->row_count
);
XPUSHs( sv_2mortal( newSVuv(
((dbe_vrt_res_t *) res->drv_data)->row_count ) ) );
break;
}
#/*****************************************************************************
# * RES_fetch_names( this [, name] )
# *****************************************************************************/
void
RES_fetch_names( this, ... )
SV *this;
PREINIT:
dbe_res_t *res;
dbe_vrt_res_t *vres;
u_long i, cn = 0;
char *name = NULL, *p1;
size_t name_len = 0, len;
PPCODE:
if( items > 1 && SvPOK( ST(1) ) ) {
name = SvPVx( ST(1), name_len );
if( my_stristr( name, "LC" ) != NULL )
cn = 1;
else if( my_stristr( name, "UC" ) != NULL )
cn = 2;
else if( my_stristr( name, "ORG" ) != NULL )
cn = 3;
}
if( (res = dbe_res_find( this )) == NULL )
XSRETURN_EMPTY;
switch( res->type ) {
case RES_TYPE_DRV:
if( res->names == NULL ) {
if( dbe_res_fetch_names( this, res ) != DBE_OK ) {
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetch_names= FALSE" );
XSRETURN_EMPTY;
}
}
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetch_names= TRUE (columns: %d)", res->num_fields );
switch( cn ) {
case 1:
for( i = 0; i < res->num_fields; i ++ ) {
if( name_len < res->names[i].length * 2 ) {
name_len = res->names[i].length * 2;
Renew( name, name_len + 1, char );
}
len = my_utf8_toupper(
res->names[i].value, res->names[i].length, name, name_len );
if( len < 0 ) {
p1 = my_strcpyl( name, res->names[i].value );
len = p1 - name;
}
XPUSHs( sv_2mortal( newSVpvn( name, len ) ) );
}
break;
case 2:
for( i = 0; i < res->num_fields; i ++ ) {
if( name_len < res->names[i].length * 2 ) {
name_len = res->names[i].length * 2;
Renew( name, name_len + 1, char );
}
len = my_utf8_tolower(
res->names[i].value, res->names[i].length, name, name_len );
if( len < 0 ) {
p1 = my_strcpyu( name, res->names[i].value );
len = p1 - name;
}
XPUSHs( sv_2mortal( newSVpvn( name, len ) ) );
}
break;
case 3:
for( i = 0; i < res->num_fields; i ++ ) {
XPUSHs( sv_2mortal(
newSVpvn( res->names[i].value, res->names[i].length ) ) );
}
break;
default:
for( i = 0; i < res->num_fields; i ++ ) {
XPUSHs( sv_2mortal(
newSVpvn( res->names_conv[i].value,
res->names_conv[i].length ) ) );
}
break;
}
break;
case RES_TYPE_VRT:
vres = (dbe_vrt_res_t *) res->drv_data;
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetch_names= TRUE (fields: %d)", vres->column_count );
switch( cn ) {
case 1:
for( i = 0; i < vres->column_count; i ++ ) {
if( name_len < vres->columns[i].name_length ) {
name_len = vres->columns[i].name_length;
Renew( name, name_len + 1, char );
}
p1 = my_strcpyl( name, vres->columns[i].name );
XPUSHs( sv_2mortal( newSVpvn( name, p1 - name ) ) );
}
break;
case 2:
for( i = 0; i < vres->column_count; i ++ ) {
if( name_len < vres->columns[i].name_length ) {
name_len = vres->columns[i].name_length;
Renew( name, name_len + 1, char );
}
p1 = my_strcpyu( name, vres->columns[i].name );
XPUSHs( sv_2mortal( newSVpvn( name, p1 - name ) ) );
}
break;
default:
for( i = 0; i < vres->column_count; i ++ )
XPUSHs( sv_2mortal( newSVpvn(
vres->columns[i].name_conv,
vres->columns[i].name_conv_length ) ) );
break;
}
break;
}
Safefree( name );
#/*****************************************************************************
# * RES_fetch_field( this )
# *****************************************************************************/
void
RES_fetch_field( this )
SV *this;
PREINIT:
dbe_res_t *res;
drv_def_t *dd;
dbe_vrt_res_t *vres;
dbe_vrt_column_t *vcol;
SV **args = NULL;
int flags = 0, count, i;
PPCODE:
if( (res = dbe_res_find( this )) == NULL )
XSRETURN_EMPTY;
switch( res->type ) {
case RES_TYPE_DRV:
dd = res->con->drv->def;
if( dd->res_fetch_field == NULL )
NOFUNCTION_XS( this, "res", res->con, "fetch_field" );
count = dd->res_fetch_field( res->drv_data, &args, &flags );
if( count <= 0 || args == NULL ) {
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetch_field= FALSE" );
XSRETURN_EMPTY;
}
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetch_field= TRUE (keys: %d)", count / 2 );
if( (count % 2) != 0 )
Perl_croak( aTHX_ "Odd number of elements in hash" );
for( i = 0; i < count; i ++ ) {
if( args[i] != NULL )
XPUSHs( sv_2mortal( args[i] ) );
else
XPUSHs( sv_2mortal( newSV(0) ) );
}
if( (flags & DBE_FREE_ARRAY) != 0 ) {
if( dd->mem_free != NULL )
dd->mem_free( args );
else
Safefree( args );
}
break;
case RES_TYPE_VRT:
vres = (dbe_vrt_res_t *) res->drv_data;
if( vres->column_pos >= vres->column_count ) {
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetch_field= FALSE" );
XSRETURN_EMPTY;
}
vcol = vres->columns + vres->column_pos ++;
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetch_field= TRUE (keys: 4)" );
XPUSHs( sv_2mortal( newSVpvn( "COLUMN_NAME", 11 ) ) );
XPUSHs( sv_2mortal( newSVpvn( vcol->name, vcol->name_length ) ) );
XPUSHs( sv_2mortal( newSVpvn( "DATA_TYPE", 9 ) ) );
XPUSHs( sv_2mortal( newSViv( vcol->type ) ) );
XPUSHs( sv_2mortal( newSVpvn( "TYPE_NAME", 9 ) ) );
XPUSHs( sv_2mortal( newSVpv( DBE_TYPE_NAME( vcol->type ), 0 ) ) );
XPUSHs( sv_2mortal( newSVpvn( "NULLABLE", 8 ) ) );
XPUSHs( sv_2mortal( newSViv( 1 ) ) );
break;
}
#/*****************************************************************************
# * RES_field_seek( this, offset )
# *****************************************************************************/
void
RES_field_seek( this, offset )
SV *this;
unsigned long offset;
PREINIT:
dbe_res_t *res;
drv_def_t *dd;
dbe_vrt_res_t *vres;
int r;
PPCODE:
if( (res = dbe_res_find( this )) == NULL )
XSRETURN_EMPTY;
switch( res->type ) {
case RES_TYPE_DRV:
dd = res->con->drv->def;
if( dd->res_field_seek == NULL )
NOFUNCTION_XS( this, "res", res->con, "field_seek" );
r = dd->res_field_seek( res->drv_data, offset );
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- field_seek= %lu (previous: %lu)", offset, r );
XPUSHs( sv_2mortal( newSViv( r ) ) );
break;
case RES_TYPE_VRT:
vres = (dbe_vrt_res_t *) res->drv_data;
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- field_seek= %lu (previous: %lu)",
offset, vres->column_pos
);
XPUSHs( sv_2mortal( newSViv( vres->column_pos ) ) );
if( offset < 0 ) {
offset = vres->column_pos + offset;
if( offset < 0 )
offset = 0;
}
else
if( (u_long) offset > vres->column_count ) {
offset = vres->column_count;
}
vres->column_pos = offset;
break;
}
#/*****************************************************************************
# * RES_field_tell( this )
# *****************************************************************************/
void
RES_field_tell( this )
SV *this;
PREINIT:
dbe_res_t *res;
drv_def_t *dd;
u_long r;
PPCODE:
if( (res = dbe_res_find( this )) == NULL )
XSRETURN_EMPTY;
switch( res->type ) {
case RES_TYPE_DRV:
dd = res->con->drv->def;
if( dd->res_field_tell == NULL )
NOFUNCTION_XS( this, "res", res->con, "field_tell" );
r = dd->res_field_tell( res->drv_data );
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- field_tell= %lu", r );
XPUSHs( sv_2mortal( newSVuv( (UV) r ) ) );
break;
case RES_TYPE_VRT:
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- field_tell= %lu",
((dbe_vrt_res_t *) res->drv_data)->column_pos
);
XPUSHs( sv_2mortal( newSViv(
((dbe_vrt_res_t *) res->drv_data)->column_pos ) ) );
break;
}
#/*****************************************************************************
# * RES_fetch( this )
# *****************************************************************************/
void
RES_fetch( this, ... )
SV *this;
PREINIT:
dbe_res_t *res;
drv_def_t *dd;
dbe_vrt_res_t *vres;
dbe_vrt_row_t *vrow;
u_long i;
SV *sv;
PPCODE:
if( (res = dbe_res_find( this )) == NULL )
XSRETURN_EMPTY;
switch( res->type ) {
case RES_TYPE_DRV:
dd = res->con->drv->def;
if( dd->res_fetch_row == NULL )
NOFUNCTION_XS( this, "res", res->con, "fetch" );
if( dbe_res_fetch_row( res, FALSE ) != DBE_OK ) {
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetch= FALSE" );
XSRETURN_EMPTY;
}
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetch= TRUE (columns: %u, row: %lu)",
res->num_fields,
dd->res_row_tell ? dd->res_row_tell( res->drv_data ) + 1 : 0
);
for( i = 0; i < res->num_fields; i ++ ) {
sv = res->sv_buffer[i] != NULL
? sv_2mortal( res->sv_buffer[i] ) : sv_newmortal();
if( res->sv_bind[i] != NULL )
sv_setsv( res->sv_bind[i], sv );
}
break;
case RES_TYPE_VRT:
vres = res->vrt_res;
if( vres->row_pos >= vres->row_count ) {
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetch= FALSE" );
XSRETURN_EMPTY;
}
vrow = vres->rows + vres->row_pos ++;
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetch= TRUE (columns: %u, row: %lu)",
vres->column_count, vres->row_pos
);
for( i = 0; i < vres->column_count; i ++ ) {
sv = sv_2mortal( dbe_vrt_res_fetch(
vres->columns[i].type, vrow->data[i], vrow->lengths[i] ) );
if( res->sv_bind[i] != NULL )
sv_setsv( res->sv_bind[i], sv );
}
break;
}
XSRETURN_YES;
#/*****************************************************************************
# * RES_fetch_row( this )
# *****************************************************************************/
void
RES_fetch_row( this, ... )
SV *this;
PREINIT:
dbe_res_t *res;
drv_def_t *dd;
dbe_vrt_res_t *vres;
dbe_vrt_row_t *vrow;
u_long i, ft = 0;
AV *av = NULL;
HV *hv = NULL;
SV *sv;
PPCODE:
if( (res = dbe_res_find( this )) == NULL )
XSRETURN_EMPTY;
if( items > 1 && SvROK( ST(1) ) ) {
sv = SvRV( ST(1) );
if( SvTYPE( sv ) == SVt_PVAV ) {
av = (AV *) sv;
ft = 1;
}
else if( SvTYPE( sv ) == SVt_PVHV ) {
hv = (HV *) sv;
ft = 2;
}
}
switch( res->type ) {
case RES_TYPE_DRV:
dd = res->con->drv->def;
if( dd->res_fetch_row == NULL )
NOFUNCTION_XS( this, "res", res->con, "fetch_row" );
if( dbe_res_fetch_row( res, ft == 2 ) != DBE_OK ) {
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetch_row= FALSE" );
XSRETURN_EMPTY;
}
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetch_row= TRUE (columns: %u, row: %lu)",
res->num_fields,
dd->res_row_tell ? dd->res_row_tell( res->drv_data ) : 0
);
switch( ft ) {
case 1:
av_clear( av );
for( i = 0; i < res->num_fields; i ++ ) {
sv = res->sv_buffer[i] != NULL ? res->sv_buffer[i] : newSV(0);
if( res->sv_bind[i] != NULL )
sv_setsv( res->sv_bind[i], sv );
av_store( av, i, sv );
}
XSRETURN_YES;
case 2:
hv_clear( hv );
if( res->names == NULL )
if( dbe_res_fetch_names( this, res ) != DBE_OK )
XSRETURN_EMPTY;
for( i = 0; i < res->num_fields; i ++ ) {
sv = res->sv_buffer[i] != NULL ? res->sv_buffer[i] : newSV(0);
if( res->sv_bind[i] != NULL )
sv_setsv( res->sv_bind[i], sv );
(void) hv_store( hv,
res->names_conv[i].value, (I32) res->names_conv[i].length,
sv, 0
);
}
XSRETURN_YES;
}
for( i = 0; i < res->num_fields; i ++ ) {
sv = res->sv_buffer[i] != NULL ? res->sv_buffer[i] : newSV(0);
if( res->sv_bind[i] != NULL )
sv_setsv( res->sv_bind[i], sv );
XPUSHs( sv_2mortal( sv ) );
}
break;
case RES_TYPE_VRT:
vres = res->vrt_res;
if( vres->row_pos >= vres->row_count ) {
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetch_row= FALSE" );
XSRETURN_EMPTY;
}
vrow = vres->rows + vres->row_pos ++;
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetch_row= TRUE (columns: %u, row: %lu)",
vres->column_count, vres->row_pos
);
switch( ft ) {
case 1:
av_clear( av );
for( i = 0; i < vres->column_count; i ++ ) {
sv = dbe_vrt_res_fetch(
vres->columns[i].type, vrow->data[i], vrow->lengths[i] );
if( res->sv_bind[i] != NULL )
sv_setsv( res->sv_bind[i], sv );
av_store( av, i, sv );
}
XSRETURN_YES;
case 2:
hv_clear( hv );
for( i = 0; i < vres->column_count; i ++ ) {
sv = dbe_vrt_res_fetch(
vres->columns[i].type, vrow->data[i], vrow->lengths[i] );
if( res->sv_bind[i] != NULL )
sv_setsv( res->sv_bind[i], sv );
(void) hv_store( hv,
vres->columns[i].name_conv,
(I32) vres->columns[i].name_conv_length,
sv, 0
);
}
XSRETURN_YES;
}
for( i = 0; i < vres->column_count; i ++ ) {
sv = dbe_vrt_res_fetch(
vres->columns[i].type, vrow->data[i], vrow->lengths[i] );
if( res->sv_bind[i] != NULL )
sv_setsv( res->sv_bind[i], sv );
XPUSHs( sv_2mortal( sv ) );
}
break;
}
#/*****************************************************************************
# * RES_fetch_col( this [, store [, col1 [, col2]]] )
# *****************************************************************************/
void
RES_fetch_col( this, ... )
SV *this;
PREINIT:
dbe_res_t *res;
drv_def_t *dd;
dbe_vrt_res_t *vres;
u_long i, j;
int type;
SV **buffer, *store;
AV *av = NULL;
HV *hv = NULL;
char *skey;
STRLEN lkey;
long col1 = 0, col2 = 1;
PPCODE:
if( (res = dbe_res_find( this )) == NULL )
XSRETURN_EMPTY;
switch( items ) {
case 1:
store = NULL;
break;
case 4:
col2 = (long) SvIV( ST(3) );
case 3:
col1 = (long) SvIV( ST(2) );
case 2:
store = ST(1);
if( store != NULL && SvROK( store ) && (store = SvRV( store )) ) {
if( SvTYPE( store ) == SVt_PVAV )
av = (AV *) store;
else
if( SvTYPE( store ) == SVt_PVHV )
hv = (HV *) store;
}
else {
col1 = (long) SvIV( ST(1) );
}
break;
default:
Perl_croak( aTHX_ "Usage: $res->fetch_col(store=0,col1=0,col2=0)" );
}
switch( res->type ) {
case RES_TYPE_DRV:
dd = res->con->drv->def;
if( dd->res_fetch_row == NULL )
NOFUNCTION_XS( this, "res", res->con, "fetch_row" );
if( col1 < 0 ) {
col1 = (long) res->num_fields + col1;
if( col1 < 0 )
col1 = 0;
}
else
if( (u_long) col1 >= res->num_fields ) {
col1 = res->num_fields - 1;
}
buffer = res->sv_buffer;
if( av != NULL ) {
i = 0;
while( dbe_res_fetch_row( res, FALSE ) == DBE_OK ) {
for( j = 0; j < res->num_fields; j ++ ) {
if( j == col1 ) {
if( buffer[col1] != NULL )
av_store( av, i, buffer[col1] );
else
av_store( av, i, newSV(0) );
}
else if( buffer[j] != NULL )
sv_2mortal( buffer[j] );
}
i ++;
}
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetch_col= col: %d (rows: %lu)", col1, i );
XSRETURN_YES;
}
else if( hv != NULL ) {
if( col2 < 0 ) {
col2 = (long) res->num_fields + col2;
if( col2 < 0 )
col2 = 0;
}
else
if( (u_long) col2 >= res->num_fields ) {
col2 = res->num_fields - 1;
}
i = 0;
while( dbe_res_fetch_row( res, TRUE ) == DBE_OK ) {
if( buffer[col1] != NULL ) {
skey = SvPV( buffer[col1], lkey );
(void) hv_store(
hv, skey, (I32) lkey,
buffer[col2] != NULL ? buffer[col2] : newSV(0),
0
);
sv_2mortal( buffer[col1] );
}
else {
(void) hv_store(
hv, "", 0,
buffer[col2] != NULL ? buffer[col2] : newSV(0),
0
);
}
for( j = 0; j < res->num_fields; j ++ ) {
if( j != col1 && j != col2 && buffer[j] != NULL )
sv_2mortal( buffer[j] );
}
i ++;
}
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetch_col= col1: %d, col2: %d (rows: %lu)",
col1, col2, i
);
XSRETURN_YES;
}
else {
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetch_col= col: %d", col1 );
while( dbe_res_fetch_row( res, FALSE ) == DBE_OK ) {
for( j = 0; j < res->num_fields; j ++ ) {
if( j == col1 ) {
if( buffer[col1] != NULL )
XPUSHs( sv_2mortal( buffer[col1] ) );
else
XPUSHs( sv_2mortal( newSV(0) ) );
}
else if( buffer[j] != NULL )
sv_2mortal( buffer[j] );
}
}
}
break;
case RES_TYPE_VRT:
vres = (dbe_vrt_res_t *) res->drv_data;
if( col1 < 0 ) {
col1 = vres->column_count + col1;
if( col1 < 0 )
col1 = 0;
}
else if( (u_long) col1 >= vres->column_count ) {
col1 = vres->column_count - 1;
}
type = vres->columns[col1].type;
if( av != NULL ) {
for( i = vres->row_pos; i < vres->row_count; i ++ ) {
av_store( av, i, dbe_vrt_res_fetch(
type,
vres->rows[i].data[col1],
vres->rows[i].lengths[col1]
) );
}
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetch_col= col: %d (rows: %lu)",
col1, i - vres->row_pos
);
XSRETURN_YES;
}
else if( hv != NULL ) {
if( col2 < 0 ) {
col2 = vres->column_count + col2;
if( col2 < 0 )
col2 = 0;
}
else if( (u_long) col2 >= vres->column_count ) {
col2 = vres->column_count - 1;
}
for( i = vres->row_pos; i < vres->row_count; i ++ ) {
store = sv_2mortal( dbe_vrt_res_fetch(
type,
vres->rows[i].data[col1],
vres->rows[i].lengths[col1]
) );
skey = SvPVx( store, lkey );
(void) hv_store( hv, skey, (I32) lkey,
dbe_vrt_res_fetch(
type,
vres->rows[i].data[col2],
vres->rows[i].lengths[col2]
), 0
);
}
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetch_col= col1: %d, col2: %d (rows: %lu)",
col1, i - vres->row_pos
);
XSRETURN_YES;
}
else {
for( i = vres->row_pos; i < vres->row_count; i ++ ) {
XPUSHs( sv_2mortal( dbe_vrt_res_fetch(
type,
vres->rows[i].data[col1],
vres->rows[i].lengths[col1]
) ) );
}
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetch_col= %lu (col: %d)", i - vres->row_pos, col1 );
}
break;
}
#/*****************************************************************************
# * RES_fetchrow_arrayref( this )
# *****************************************************************************/
void
RES_fetchrow_arrayref( this )
SV *this;
PREINIT:
dbe_res_t *res;
drv_def_t *dd;
dbe_vrt_res_t *vres;
dbe_vrt_row_t *vrow;
u_long i;
AV *av;
SV *sv;
PPCODE:
if( (res = dbe_res_find( this )) == NULL )
XSRETURN_EMPTY;
switch( res->type ) {
case RES_TYPE_DRV:
dd = res->con->drv->def;
if( dd->res_fetch_row == NULL )
NOFUNCTION_XS( this, "res", res->con, "fetch_row" );
if( dbe_res_fetch_row( res, FALSE ) != DBE_OK ) {
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetchrow_arrayref= FALSE" );
XSRETURN_EMPTY;
}
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetchrow_arrayref= TRUE (columns: %u, row: %lu)",
res->num_fields,
dd->res_row_tell ? dd->res_row_tell( res->drv_data ) : 0
);
av = (AV *) sv_2mortal( (SV *) newAV() );
for( i = 0; i < res->num_fields; i ++ ) {
sv = res->sv_buffer[i] != NULL ? res->sv_buffer[i] : newSV(0);
if( res->sv_bind[i] != NULL )
sv_setsv( res->sv_bind[i], sv );
av_push( av, sv );
}
ST(0) = sv_2mortal( newRV( (SV *) av ) );
break;
case RES_TYPE_VRT:
vres = (dbe_vrt_res_t *) res->drv_data;
if( vres->row_pos >= vres->row_count ) {
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetchrow_arrayref= FALSE" );
XSRETURN_EMPTY;
}
vrow = vres->rows + vres->row_pos ++;
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetchrow_arrayref= TRUE (columns: %u, row: %lu)",
vres->column_count, vres->row_pos
);
av = (AV *) sv_2mortal( (SV *) newAV() );
for( i = 0; i < (int) vres->column_count; i ++ ) {
sv = dbe_vrt_res_fetch(
vres->columns[i].type, vrow->data[i], vrow->lengths[i] );
if( res->sv_bind[i] != NULL )
sv_setsv( res->sv_bind[i], sv );
av_push( av, sv );
}
ST(0) = sv_2mortal( newRV( (SV *) av ) );
break;
}
XSRETURN(1);
#/*****************************************************************************
# * RES_fetchrow_hashref( this [, name] )
# *****************************************************************************/
void
RES_fetchrow_hashref( this, ... )
SV *this;
PREINIT:
dbe_res_t *res;
drv_def_t *dd;
u_long cn = 0;
HV *hv = NULL;
char *name;
STRLEN name_len;
PPCODE:
if( (res = dbe_res_find( this )) == NULL )
XSRETURN_EMPTY;
if( items > 1 && SvPOK( ST(1) ) ) {
name = SvPVx( ST(1), name_len );
if( my_stristr( name, "LC" ) != NULL )
cn = 1;
else if( my_stristr( name, "UC" ) != NULL )
cn = 2;
}
switch( res->type ) {
case RES_TYPE_DRV:
dd = res->con->drv->def;
if( dbe_res_fetch_hash( this, res, &hv, cn ) == DBE_OK ) {
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetchrow_hashref= TRUE (columns: %u, row: %lu)",
res->num_fields,
dd->res_row_tell ? dd->res_row_tell( res->drv_data ) : 0
);
XPUSHs( sv_2mortal( newRV( (SV *) hv ) ) );
}
else {
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetchrow_hashref= FALSE" );
}
break;
case RES_TYPE_VRT:
if( dbe_vrt_res_fetch_hash( res, &hv, cn ) == DBE_OK ) {
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetchrow_hashref= TRUE (columns: %u, row: %lu)",
((dbe_vrt_res_t *) res->drv_data)->column_count,
((dbe_vrt_res_t *) res->drv_data)->row_pos
);
XPUSHs( sv_2mortal( newRV( (SV *) hv ) ) );
}
else {
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetchrow_hashref= FALSE" );
}
break;
}
#/*****************************************************************************
# * RES_fetchall_arrayref( this )
# *****************************************************************************/
void
RES_fetchall_arrayref( this, ... )
SV *this;
PREINIT:
dbe_res_t *res;
int fn = 0;
AV *av;
SV *sv;
PPCODE:
if( (res = dbe_res_find( this )) == NULL )
XSRETURN_EMPTY;
if( items > 1 ) {
if( SvROK( ST(1) ) ) {
sv = SvRV( ST(1) );
if( SvTYPE( sv ) == SVt_PVHV )
fn = 1;
}
}
switch( res->type ) {
case RES_TYPE_DRV:
if( dbe_res_fetchall_arrayref( this, res, &av, fn ) != DBE_OK ) {
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetchall_arrayref= FALSE" );
XSRETURN_EMPTY;
}
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetchall_arrayref= TRUE (rows: %d)", av_len( av ) + 1 );
XPUSHs( sv_2mortal( newRV( (SV *) av ) ) );
break;
case RES_TYPE_VRT:
if( dbe_vrt_res_fetchall_arrayref( res, &av, fn ) != DBE_OK ) {
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetchall_arrayref= FALSE" );
XSRETURN_EMPTY;
}
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetchall_arrayref= TRUE (rows: %d)", av_len( av ) + 1 );
XPUSHs( sv_2mortal( newRV( (SV *) av ) ) );
break;
}
#/*****************************************************************************
# * RES_fetchall_hashref( this, key )
# *****************************************************************************/
void
RES_fetchall_hashref( this, key, ... )
SV *this;
SV *key;
PREINIT:
dbe_res_t *res;
dbe_str_t *fields = NULL;
HV *hv;
u_long ks, i;
PPCODE:
if( (res = dbe_res_find( this )) == NULL )
XSRETURN_EMPTY;
if( SvROK( key ) && (key = SvRV( key )) ) {
AV *av;
SV **psv;
if( SvTYPE( key ) != SVt_PVAV ) {
dbe_drv_set_error( res->con, -1, "Unknown type for key fields" );
HANDLE_ERROR_XS( this, "res", res->con, "fetchall_hashref failed" );
}
av = (AV *) key;
ks = av_len( av ) + 1;
if( ! ks ) {
dbe_drv_set_error( res->con, -1, "List of key fields is empty" );
HANDLE_ERROR_XS( this, "res", res->con, "fetchall_hashref failed" );
}
Newx( fields, ks, dbe_str_t );
for( i = 0; i < ks; i ++ ) {
psv = av_fetch( av, i, 0 );
if( psv != NULL )
fields[i].value = SvPVx( (*psv), fields[i].length );
else {
fields[i].value = "";
fields[i].length = 0;
}
}
}
else {
ks = items - 1;
Newx( fields, ks, dbe_str_t );
for( i = 0; i < ks; i ++ )
fields[i].value = SvPVx( ST(i + 1), fields[i].length );
}
switch( res->type ) {
case RES_TYPE_DRV:
if( dbe_res_fetchall_hashref( this, res, fields, ks, &hv ) != DBE_OK )
goto error;
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetchall_hashref= TRUE" );
ST(0) = sv_2mortal( newRV( (SV *) hv ) );
break;
case RES_TYPE_VRT:
if( dbe_vrt_res_fetchall_hashref( res, fields, ks, &hv ) != DBE_OK )
goto error;
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetchall_hashref= TRUE" );
ST(0) = sv_2mortal( newRV( (SV *) hv ) );
break;
}
Safefree( fields );
XSRETURN(1);
error:
Safefree( fields );
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- fetchall_hashref= FALSE" );
HANDLE_ERROR_XS( this, "res", res->con, "fetchall_hashref failed" );
#/*****************************************************************************
# * RES_row_seek( this, offset )
# *****************************************************************************/
void
RES_row_seek( this, offset )
SV *this;
UV offset;
PREINIT:
dbe_res_t *res;
drv_def_t *dd;
dbe_vrt_res_t *vres;
xlong r;
PPCODE:
if( (res = dbe_res_find( this )) == NULL )
XSRETURN_EMPTY;
switch( res->type ) {
case RES_TYPE_DRV:
dd = res->con->drv->def;
if( dd->res_row_seek == NULL )
NOFUNCTION_XS( this, "res", res->con, "row_seek" );
r = dd->res_row_seek( res->drv_data, (u_xlong) offset );
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- row_seek= %lu (previous: %ld)", offset, r );
#if IVSIZE >= 8
XPUSHs( sv_2mortal( newSViv( r ) ) );
#else
if( r >= -2147483647 && r <= 2147483647 ) {
XPUSHs( sv_2mortal( newSViv( (IV) r ) ) );
}
else {
char tmp[21], *s1;
s1 = my_ltoa( tmp, r, 10 );
XPUSHs( sv_2mortal( newSVpvn( tmp, s1 - tmp ) ) );
}
#endif
break;
case RES_TYPE_VRT:
vres = (dbe_vrt_res_t *) res->drv_data;
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- row_seek= %lu (previous: %lu)", offset, vres->row_pos );
XPUSHs( sv_2mortal( newSVuv( vres->row_pos ) ) );
if( offset > vres->row_count )
vres->row_pos = vres->row_count;
else
vres->row_pos = (u_long) offset;
break;
}
#/*****************************************************************************
# * RES_row_tell( this )
# *****************************************************************************/
void
RES_row_tell( this )
SV *this;
PREINIT:
dbe_res_t *res;
drv_def_t *dd;
xlong r;
PPCODE:
if( (res = dbe_res_find( this )) == NULL )
XSRETURN_EMPTY;
switch( res->type ) {
case RES_TYPE_DRV:
dd = res->con->drv->def;
if( dd->res_row_tell == NULL )
NOFUNCTION_XS( this, "res", res->con, "row_tell" );
r = dd->res_row_tell( res->drv_data );
#if IVSIZE >= 8
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE, "<- row_tell= %ld", r );
XPUSHs( sv_2mortal( newSViv( r ) ) );
#else
if( r >= -2147483647 && r <= 2147483647 ) {
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- row_tell= %ld", r );
XPUSHs( sv_2mortal( newSViv( (IV) r ) ) );
}
else {
char tmp[22], *s1;
s1 = my_ltoa( tmp, r, 10 );
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- row_tell= %s", tmp );
XPUSHs( sv_2mortal( newSVpvn( tmp, s1 - tmp ) ) );
}
#endif
break;
case RES_TYPE_VRT:
IF_TRACE_CON( res->con, DBE_TRACE_ALL )
TRACE( res->con, DBE_TRACE_ALL, TRUE,
"<- row_tell= %lu",
((dbe_vrt_res_t *) res->drv_data)->row_pos
);
XPUSHs( sv_2mortal( newSVuv(
((dbe_vrt_res_t *) res->drv_data)->row_pos ) ) );
break;
}
#/*****************************************************************************
# * RES_dump( this )
# *****************************************************************************/
void
RES_dump( this, maxlen = 35, lsep = NULL, fsep = ", ", out = NULL, header = 1 )
SV *this;
unsigned int maxlen;
char *lsep;
char *fsep;
SV *out;
int header;
PREINIT:
dbe_res_t *res;
drv_def_t *dd;
dbe_vrt_res_t *vres;
dbe_vrt_row_t *vrow, *vrow_end;
SV **row, *sv;
u_long i, wc, is_utf8;
u_long row_count = 0;
size_t len;
char *str, *s2, *s3;
u_int pos;
PPCODE:
if( (res = dbe_res_find( this )) == NULL )
XSRETURN_EMPTY;
if( out == NULL )
out = newRV_noinc( (SV *) PL_stderrgv );
if( lsep == NULL )
lsep = (char *) "\n";
if( maxlen < 6 )
maxlen = 6;
maxlen -= 3;
switch( res->type ) {
case RES_TYPE_DRV:
dd = res->con->drv->def;
if( dd->res_fetch_row == NULL )
NOFUNCTION_XS( this, "res", res->con, "res_fetch_row" );
if( res->names == NULL )
if( dbe_res_fetch_names( this, res ) != DBE_OK )
XSRETURN_EMPTY;
if( dd->res_row_tell != NULL &&
dd->res_row_tell( res->drv_data ) > 0
) {
if( dd->res_row_seek != NULL )
dd->res_row_seek( res->drv_data, 0 );
}
if( header ) {
for( i = 0; i < res->num_fields; i ++ ) {
if( i > 0 )
dbe_perl_printf( out, "%s", fsep );
dbe_perl_printf( out, "'%s'", res->names_conv[i].value );
}
dbe_perl_printf( out, "%s", lsep );
}
row = res->sv_buffer;
while( dbe_res_fetch_row( res, FALSE ) == DBE_OK ) {
ENTER;
SAVETMPS;
for( i = 0; i < res->num_fields; i ++ ) {
if( i > 0 )
dbe_perl_printf( out, "%s", fsep );
if( (sv = row[i]) != NULL )
sv_2mortal( sv );
if( sv == NULL || ! SvOK( sv ) )
dbe_perl_printf( out, "undef" );
else {
str = SvPV( sv, len );
is_utf8 = 0;
pos = 0;
for( s2 = str, s3 = s2 + len; s2 < s3; s2 ++ ) {
if( pos ++ == maxlen ) {
my_strcpy( s2, "..." );
break;
}
wc = *s2;
if( wc & 0x80 ) {
if( (s2[1] & 0xC0) != 0x80 )
goto _no_utf8_1;
wc &= 0x3F;
if( wc & 0x20 ) {
if( (s2[2] & 0xC0) != 0x80 )
goto _no_utf8_1;
s2 += 2;
}
is_utf8 = 1;
s2 ++;
}
else if( wc > 127 ) {
_no_utf8_1:
*s2 = '?';
}
}
if( is_utf8 )
dbe_perl_printf( out, "\"%s\"", str );
else
dbe_perl_printf( out, "'%s'", str );
}
}
dbe_perl_printf( out, "%s", lsep );
row_count ++;
FREETMPS;
LEAVE;
}
break;
case RES_TYPE_VRT:
vres = res->vrt_res;
if( header ) {
for( i = 0; i < vres->column_count; i ++ ) {
if( i > 0 )
dbe_perl_printf( out, "%s", fsep );
dbe_perl_printf( out, "'%s'", vres->columns[i].name_conv );
}
dbe_perl_printf( out, "%s", lsep );
}
vrow = vres->rows;
vrow_end = vres->rows + vres->row_count;
for( ; vrow < vrow_end; vrow ++ ) {
ENTER;
SAVETMPS;
for( i = 0; i < vres->column_count; i ++ ) {
if( i > 0 )
dbe_perl_printf( out, "%s", fsep );
sv = dbe_vrt_res_fetch(
vres->columns[i].type, vrow->data[i], vrow->lengths[i] );
sv_2mortal( sv );
if( sv == NULL || ! SvOK( sv ) )
dbe_perl_printf( out, "undef" );
else {
str = SvPV( sv, len );
is_utf8 = 0;
pos = 0;
for( s2 = str, s3 = s2 + len; s2 < s3; s2 ++ ) {
if( pos ++ == maxlen ) {
my_strcpy( s2, "..." );
break;
}
wc = *s2;
if( wc & 0x80 ) {
if( (s2[1] & 0xC0) != 0x80 )
goto _no_utf8_2;
wc &= 0x3F;
if( wc & 0x20 ) {
if( (s2[2] & 0xC0) != 0x80 )
goto _no_utf8_2;
s2 += 2;
}
is_utf8 = 1;
s2 ++;
}
else if( wc > 127 ) {
_no_utf8_2:
*s2 = '?';
}
}
if( is_utf8 )
dbe_perl_printf( out, "\"%s\"", str );
else
dbe_perl_printf( out, "'%s'", str );
}
}
dbe_perl_printf( out, "%s", lsep );
row_count ++;
FREETMPS;
LEAVE;
}
break;
}
dbe_perl_printf( out, "%lu rows%s", row_count, lsep );
#/*****************************************************************************
# * RES_errno( this )
# *****************************************************************************/
void
RES_errno( this )
SV *this;
PREINIT:
dbe_res_t *res;
PPCODE:
if( (res = dbe_res_find( this )) == NULL )
XSRETURN_EMPTY;
XSRETURN_IV( (IV) res->con->last_errno );
#/*****************************************************************************
# * RES_error( this )
# *****************************************************************************/
void
RES_error( this )
SV *this;
PREINIT:
dbe_res_t *res;
PPCODE:
if( (res = dbe_res_find( this )) == NULL )
XSRETURN_EMPTY;
ST(0) = sv_2mortal( newSVpvn(
res->con->last_error, strlen( res->con->last_error ) ) );
XSRETURN(1);
#/*########################### STATEMENT CLASS ###############################*/
MODULE = DBE PACKAGE = DBE::STMT PREFIX = STMT_
#/*****************************************************************************
# * STMT_DESTROY()
# *****************************************************************************/
void
STMT_DESTROY( ... )
PREINIT:
dbe_stmt_t *stmt;
CODE:
if( items < 1 || (stmt = dbe_stmt_find( ST(0) )) == NULL )
XSRETURN_EMPTY;
#ifdef DBE_DEBUG
_debug( "DESTROY called for stmt %lu, ref %d\n", stmt->id, stmt->refcnt );
#endif
stmt->refcnt --;
if( stmt->refcnt < 0 ) {
IF_TRACE_CON( stmt->con, DBE_TRACE_ALL )
TRACE( stmt->con, DBE_TRACE_ALL, TRUE,
"<- DESTROY= %s=SCALAR(0x%x)",
stmt->con->drv->class_stmt, SvRV( ST(0) )
);
dbe_stmt_rem( stmt );
}
#/*****************************************************************************
# * STMT_close( this )
# *****************************************************************************/
void
STMT_close( this )
SV *this;
PREINIT:
dbe_stmt_t *stmt;
CODE:
if( (stmt = dbe_stmt_find( this )) == NULL )
XSRETURN_EMPTY;
IF_TRACE_CON( stmt->con, DBE_TRACE_ALL )
TRACE( stmt->con, DBE_TRACE_ALL, TRUE,
"<- DESTROY= %s=SCALAR(0x%x)",
stmt->con->drv->class_stmt, SvRV( this )
);
dbe_stmt_rem( stmt );
XSRETURN_YES;
#/*****************************************************************************
# * STMT_param_count( this )
# *****************************************************************************/
void
STMT_param_count( this )
SV *this;
PREINIT:
dbe_stmt_t *stmt;
drv_def_t *dd;
PPCODE:
if( (stmt = dbe_stmt_find( this )) == NULL )
XSRETURN_EMPTY;
dd = stmt->con->drv->def;
if( dd->stmt_param_count == NULL )
NOFUNCTION_XS( this, "stmt", stmt->con, "param_count" );
XSRETURN_IV( (IV) dd->stmt_param_count( stmt->drv_data ) );
#/*****************************************************************************
# * STMT_bind_param( this, p_num [, p_val [, p_type]] )
# *****************************************************************************/
void
STMT_bind_param( this, p_num, p_val = NULL, p_type = NULL )
SV *this;
SV *p_num;
SV *p_val;
SV *p_type;
PREINIT:
dbe_stmt_t *stmt;
drv_def_t *dd;
int r = DBE_OK, i;
char *name = NULL, type;
dbe_param_t *param;
STRLEN name_len;
u_long pos;
PPCODE:
if( (stmt = dbe_stmt_find( this )) == NULL )
XSRETURN_EMPTY;
type = p_type != NULL && SvOK( p_type ) ? SvPV_nolen( p_type )[0] : 0;
dd = stmt->con->drv->def;
if( SvIOK( p_num ) ) {
pos = (u_long) SvUV( p_num );
_bind_num:
r = dbe_bind_param_num(
this, "stmt", stmt->con, stmt->drv_data, dd,
pos, p_val, type, name
);
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
if( dbe_con_reconnect( this, "stmt", stmt->con ) == DBE_OK )
dbe_drv_set_error( stmt->con, -1,
"Connection restored, but statement is lost" );
default:
HANDLE_ERROR_XS( this, "stmt", stmt->con, "Bind param failed" );
}
}
else if( SvPOK( p_num ) ) {
name = SvPV( p_num, name_len );
if( isDIGIT( *name ) ) {
pos = (u_long) SvUV( p_num );
goto _bind_num;
}
if( (param = stmt->params) == NULL ) {
if( dd->stmt_param_position == NULL )
NOFUNCTION_XS( this, "stmt", stmt->con, "param_position" );
r = dd->stmt_param_position(
stmt->drv_data, name, name_len, &pos );
if( r != DBE_OK )
HANDLE_ERROR_XS( this, "stmt", stmt->con, "Bind param failed" );
goto _bind_num;
}
else {
if( *name == ':' )
name ++, name_len --;
r = DBE_ERROR;
if( (param = stmt->params) != NULL ) {
for( i = 1; i <= stmt->num_params; i ++, param ++ ) {
if( param->name != NULL &&
my_stricmp( param->name, name ) == 0
) {
r = dbe_bind_param_num(
this, "stmt", stmt->con, stmt->drv_data, dd,
i, p_val, type, name
);
if( r != DBE_OK ) {
HANDLE_ERROR_XS(
this, "stmt", stmt->con, "Bind param failed" );
}
param->type = type;
}
}
}
if( r != DBE_OK ) {
dbe_drv_set_error( stmt->con,
-1, "Invalid parameter name \"%s\"", name );
HANDLE_ERROR_XS( this, "stmt", stmt->con, "Bind param failed" );
}
}
}
else {
dbe_drv_set_error( stmt->con, -1, "Invalid parameter identifier" );
HANDLE_ERROR_XS( this, "stmt", stmt->con, "Bind param failed" );
}
XSRETURN_YES;
#/*****************************************************************************
# * STMT_bind( this )
# *****************************************************************************/
void
STMT_bind( this, ... )
SV *this;
PREINIT:
dbe_stmt_t *stmt;
drv_def_t *dd;
u_long i;
int r;
PPCODE:
if( (stmt = dbe_stmt_find( this )) == NULL )
XSRETURN_EMPTY;
dd = stmt->con->drv->def;
if( items <= 1 )
XSRETURN_YES;
for( i = 1; i < (u_long) items; i ++ ) {
r = dbe_bind_param_num( this, "stmt",
stmt->con, stmt->drv_data, dd, i, ST(i), '\0', NULL );
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
/* statement is lost! */
if( dbe_con_reconnect( this, "stmt", stmt->con ) == DBE_OK )
dbe_drv_set_error( stmt->con, -1,
"Connection restored, but statement is lost" );
default:
HANDLE_ERROR_XS( this, "stmt", stmt->con, "Bind param failed" );
}
}
#/*****************************************************************************
# * STMT_execute( this )
# *****************************************************************************/
void
STMT_execute( this, ... )
SV *this;
PREINIT:
dbe_stmt_t *stmt;
dbe_res_t *res = NULL;
drv_def_t *dd;
SV *sv;
HV *hv;
char *key;
int i, r, key_len;
dbe_param_t *param;
PPCODE:
if( (stmt = dbe_stmt_find( this )) == NULL )
XSRETURN_EMPTY;
dd = stmt->con->drv->def;
if( dd->stmt_execute == NULL )
NOFUNCTION_XS( this, "stmt", stmt->con, "execute" );
if( items > 1 ) {
if( SvROK( ST(1) ) ) {
sv = SvRV( ST(1) );
if( SvTYPE( sv ) == SVt_PVHV ) {
hv = (HV *) sv, hv_iterinit( hv );
while( (sv = hv_iternextsv( hv, &key, &key_len )) != NULL ) {
r = dbe_bind_param_str(
this, "stmt", stmt->con, stmt->drv_data, dd,
stmt->param_names, stmt->num_params, key, sv, 0
);
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
goto _reconnect;
default:
goto _error;
}
}
goto _bind_inout;
}
}
for( i = 1; i < items; i ++ ) {
r = dbe_bind_param_num(
this, "stmt", stmt->con, stmt->drv_data, dd,
i, ST(i), 0, NULL
);
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
goto _reconnect;
default:
goto _error;
}
}
}
_bind_inout:
if( stmt->params != NULL ) {
param = stmt->params;
for( i = 1; i <= stmt->num_params; i ++, param ++ ) {
if( param->sv == NULL )
continue;
r = dbe_bind_param_num(
this, "stmt", stmt->con, stmt->drv_data, dd,
i, param->sv, param->type, param->name
);
switch( r ) {
case DBE_OK:
break;
case DBE_CONN_LOST:
goto _reconnect;
default:
goto _error;
}
}
}
switch( dd->stmt_execute( stmt->drv_data, &res ) ) {
case DBE_OK:
if( res == NULL ) {
IF_TRACE_CON( stmt->con, DBE_TRACE_SQLFULL )
TRACE( stmt->con, DBE_TRACE_SQLFULL, TRUE, "<- execute= TRUE" );
XSRETURN_YES;
}
if( stmt->params != NULL ) {
dbe_param_t *param = stmt->params;
for( i = 0; i < stmt->num_params; i ++, param ++ ) {
if( i >= (int) res->num_fields )
break;
if( param->sv != NULL )
res->sv_bind[i] = SvREFCNT_inc( param->sv );
}
}
dbe_res_add( stmt->con, res );
hv = gv_stashpv( stmt->con->drv->class_res, TRUE );
sv = sv_2mortal( newSViv( (IV) res->id ) );
IF_TRACE_CON( stmt->con, DBE_TRACE_SQLFULL )
TRACE( stmt->con, DBE_TRACE_SQLFULL, TRUE,
"<- execute= %s=SCALAR(0x%lx)",
stmt->con->drv->class_res, (size_t) sv
);
XPUSHs( sv_2mortal( sv_bless( newRV( sv ), hv ) ) );
break;
case DBE_CONN_LOST:
/* statement is lost! */
_reconnect:
if( dbe_con_reconnect( this, "stmt", stmt->con ) == DBE_OK )
dbe_drv_set_error( stmt->con, -1,
"Connection restored, but statement is lost" );
default:
_error:
IF_TRACE_CON( stmt->con, DBE_TRACE_SQLFULL )
TRACE( stmt->con, DBE_TRACE_SQLFULL, TRUE, "<- execute= FALSE" );
HANDLE_ERROR_XS( this, "stmt", stmt->con, "Execute failed" );
}
#/*****************************************************************************
# * STMT_errno( this )
# *****************************************************************************/
void
STMT_errno( this )
SV *this;
PREINIT:
dbe_stmt_t *stmt;
PPCODE:
if( (stmt = dbe_stmt_find( this )) == NULL )
XSRETURN_EMPTY;
XSRETURN_IV( (IV) stmt->con->last_errno );
#/*****************************************************************************
# * STMT_error( this )
# *****************************************************************************/
void
STMT_error( this )
SV *this;
PREINIT:
dbe_stmt_t *stmt;
PPCODE:
if( (stmt = dbe_stmt_find( this )) == NULL )
XSRETURN_EMPTY;
ST(0) = sv_2mortal( newSVpvn(
stmt->con->last_error, strlen( stmt->con->last_error ) ) );
XSRETURN(1);
#/*########################### LOB STREAM CLASS ##############################*/
MODULE = DBE PACKAGE = DBE::LOB PREFIX = LOB_
void
LOB_DESTROY( this, ... )
SV *this;
PREINIT:
dbe_lob_stream_t *ls;
dbe_con_t *con;
PPCODE:
ls = dbe_lob_stream_find( this, &con );
if( ls == NULL )
XSRETURN_EMPTY;
#ifdef DBE_DEBUG
_debug( ">>>>>> lob stream %lu destroy, refcnt %d\n", ls->id, ls->refcnt );
#endif
ls->refcnt --;
if( ls->refcnt < 0 ) {
IF_TRACE_CON( con, DBE_TRACE_ALL )
TRACE( con, DBE_TRACE_ALL, TRUE,
"<- DESTROY= DBE::LOB=SCALAR(0x%x)", SvRV( this ) );
dbe_lob_stream_rem( con, ls );
}
XSRETURN_EMPTY;
void
LOB_close( this, ... )
SV *this;
PREINIT:
dbe_lob_stream_t *ls;
dbe_con_t *con;
drv_def_t *dd;
PPCODE:
ls = dbe_lob_stream_find( this, &con );
if( ls == NULL )
XSRETURN_EMPTY;
#ifdef DBE_DEBUG
_debug( ">>>>>> lob stream %lu close\n", ls->id );
#endif
if( ! ls->closed ) {
dd = con->drv->def;
if( dd->con_lob_close != NULL )
dd->con_lob_close( con->drv_data, ls->context );
ls->closed = TRUE;
}
XSRETURN_YES;
void
LOB_tell( this, ... )
SV *this;
PREINIT:
dbe_lob_stream_t *ls;
PPCODE:
ls = dbe_lob_stream_find( this, NULL );
if( ls == NULL )
XSRETURN_EMPTY;
#ifdef DBE_DEBUG
_debug( ">>>>>> lob stream %lu tell\n", ls->id );
#endif
XSRETURN_IV( ls->offset );
void
LOB_seek( this, position, whence, ... )
SV *this;
int position;
int whence;
PREINIT:
dbe_lob_stream_t *ls;
drv_def_t *dd;
dbe_con_t *con;
SV *err;
PPCODE:
ls = dbe_lob_stream_find( this, &con );
if( ls == NULL )
XSRETURN_EMPTY;
#ifdef DBE_DEBUG
_debug( ">>>>>> lob stream %lu seek position %d, whence %d\n",
ls->id, position, whence );
#endif
dd = con->drv->def;
if( ls->size < 0 ) {
if( dd->con_lob_size == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "lob_size" );
goto _error;
}
ls->size = dd->con_lob_size( con->drv_data, ls->context );
if( ls->size == DBE_NO_DATA )
dbe_drv_set_error( con, -1, "Unknown size of LOB" );
if( ls->size < 0 )
goto _error;
}
switch( whence ) {
case SEEK_SET:
ls->offset = position;
break;
case SEEK_CUR:
ls->offset += position;
break;
case SEEK_END:
ls->offset = ls->size + position;
break;
default:
dbe_drv_set_error( con,
-1, "Invalid value for parameter whence: %d", whence );
goto _error;
}
if( ls->offset > ls->size )
ls->offset = ls->size;
else if( ls->offset < 0 )
ls->offset = 0;
XSRETURN_YES;
_error:
err = get_sv( "!", TRUE );
sv_setiv( err, (IV) con->last_errno );
sv_setpv( err, con->last_error );
SvIOK_on( err );
HANDLE_ERROR_XS( this, "lob", con, "Seek LOB failed" );
void
LOB_size( this )
SV *this;
PREINIT:
dbe_lob_stream_t *ls;
drv_def_t *dd;
dbe_con_t *con;
SV *err;
PPCODE:
ls = dbe_lob_stream_find( this, &con );
if( ls == NULL )
XSRETURN_EMPTY;
#ifdef DBE_DEBUG
_debug( ">>>>>> lob stream %lu size\n", ls->id );
#endif
dd = con->drv->def;
if( ls->size < 0 ) {
if( dd->con_lob_size == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "lob_size" );
goto _error;
}
ls->size = dd->con_lob_size( con->drv_data, ls->context );
if( ls->size == DBE_NO_DATA )
dbe_drv_set_error( con, -1, "Unknown size of LOB" );
if( ls->size < 0 )
goto _error;
}
XSRETURN_IV( ls->size );
_error:
err = get_sv( "!", TRUE );
sv_setiv( err, (IV) con->last_errno );
sv_setpv( err, con->last_error );
SvIOK_on( err );
HANDLE_ERROR_XS( this, "lob", con, "Get size of LOB failed" );
void
LOB_eof( this, ... )
SV *this;
PREINIT:
dbe_lob_stream_t *ls;
drv_def_t *dd;
dbe_con_t *con;
SV *err;
PPCODE:
ls = dbe_lob_stream_find( this, &con );
if( ls == NULL )
XSRETURN_EMPTY;
#ifdef DBE_DEBUG
_debug( ">>>>>> lob stream %lu eof\n", ls->id );
#endif
dd = con->drv->def;
if( ls->size < 0 ) {
if( dd->con_lob_size == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "lob_size" );
goto _error;
}
ls->size = dd->con_lob_size( con->drv_data, ls->context );
if( ls->size == DBE_NO_DATA )
dbe_drv_set_error( con, -1, "Unknown size of LOB" );
if( ls->size < 0 )
goto _error;
}
XSRETURN_IV( ls->offset >= ls->size );
_error:
err = get_sv( "!", TRUE );
sv_setiv( err, (IV) con->last_errno );
sv_setpv( err, con->last_error );
SvIOK_on( err );
HANDLE_ERROR_XS( this, "lob", con, "Get end of LOB failed" );
void
LOB_getc( this, ... )
SV *this;
PREINIT:
dbe_lob_stream_t *ls;
drv_def_t *dd;
dbe_con_t *con;
char tmp[8], *s1;
int length, ch;
SV *err;
PPCODE:
ls = dbe_lob_stream_find( this, &con );
if( ls == NULL )
XSRETURN_EMPTY;
#ifdef DBE_DEBUG
_debug( ">>>>>> lob stream %lu getc\n", ls->id );
#endif
dd = con->drv->def;
if( dd->con_lob_read == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "lob_read" );
goto _error;
}
length = dd->con_lob_read(
con->drv_data, ls->context, &ls->offset, tmp, 1 );
if( length == DBE_ERROR )
goto _error;
if( length == DBE_NO_DATA )
XSRETURN_IV( -1 );
ch = 0;
for( s1 = tmp; length; length --, s1 ++ ) {
ch <<= 8;
ch |= (unsigned char) *s1;
}
XSRETURN_IV( ch );
_error:
err = get_sv( "!", TRUE );
sv_setiv( err, (IV) con->last_errno );
sv_setpv( err, con->last_error );
SvIOK_on( err );
HANDLE_ERROR_XS( this, "lob", con, "Read LOB failed" );
void
LOB_read( this, scalar, length, ... )
SV *this;
SV *scalar;
int length;
PREINIT:
dbe_lob_stream_t *ls;
drv_def_t *dd;
dbe_con_t *con;
SV *err;
char *buf;
PPCODE:
ls = dbe_lob_stream_find( this, &con );
if( ls == NULL )
XSRETURN_EMPTY;
#ifdef DBE_DEBUG
_debug( ">>>>>> lob stream %lu read %ld\n", ls->id, length );
#endif
if( ! SvPOK( scalar ) )
sv_setpvn( scalar, "", 0 );
SvGROW( scalar, (STRLEN) length + 1 );
buf = SvPV_nolen( scalar );
dd = con->drv->def;
if( dd->con_lob_read == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "lob_read" );
goto _error;
}
length = dd->con_lob_read(
con->drv_data, ls->context, &ls->offset, buf, length
);
if( length == DBE_ERROR )
goto _error;
if( length == DBE_NO_DATA )
length = 0;
buf[length] = '\0';
SvCUR_set( scalar, length );
XSRETURN_IV( length );
_error:
err = get_sv( "!", TRUE );
sv_setiv( err, (IV) con->last_errno );
sv_setpv( err, con->last_error );
SvIOK_on( err );
*buf = '\0';
SvCUR_set( scalar, 0 );
HANDLE_ERROR_XS( this, "lob", con, "Read LOB failed" );
void
LOB_print( this, ... )
SV *this;
PREINIT:
dbe_lob_stream_t *ls;
drv_def_t *dd;
dbe_con_t *con;
SV *err;
int i, r;
char *str;
STRLEN str_len;
PPCODE:
ls = dbe_lob_stream_find( this, &con );
if( ls == NULL )
XSRETURN_EMPTY;
#ifdef DBE_DEBUG
_debug( ">>>>>> lob stream %lu print %d items\n", ls->id, items - 1 );
#endif
dd = con->drv->def;
if( dd->con_lob_write == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "lob_write" );
goto _error;
}
for( i = 1; i < items; i ++ ) {
str = SvPV( ST(i), str_len );
r = dd->con_lob_write(
con->drv_data, ls->context, &ls->offset, str, str_len );
if( r < 0 )
goto _error;
}
XSRETURN_YES;
_error:
err = get_sv( "!", TRUE );
sv_setiv( err, (IV) con->last_errno );
sv_setpv( err, con->last_error );
SvIOK_on( err );
HANDLE_ERROR_XS( this, "lob", con, "Write LOB failed" );
void
LOB_write( this, buffer, ... )
SV *this;
SV *buffer;
PREINIT:
dbe_lob_stream_t *ls;
drv_def_t *dd;
dbe_con_t *con;
SV *err;
int r;
char *str;
STRLEN str_len, x;
PPCODE:
ls = dbe_lob_stream_find( this, &con );
if( ls == NULL )
XSRETURN_EMPTY;
str = SvPVbyte( buffer, str_len );
#ifdef DBE_DEBUG
_debug( ">>>>>> lob stream %lu write %u bytes\n", ls->id, str_len );
#endif
if( items > 2 ) {
x = SvUV( ST(2) );
if( x <= str_len )
str += x, str_len -= x;
else
str += str_len, str_len = 0;
}
if( items > 3 ) {
x = SvUV( ST(3) );
if( x <= str_len )
str_len -= x;
else
str_len = 0;
}
dd = con->drv->def;
if( dd->con_lob_write == NULL ) {
dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, "lob_write" );
goto _error;
}
r = dd->con_lob_write(
con->drv_data, ls->context, &ls->offset, str, str_len );
if( r < 0 )
goto _error;
XSRETURN_YES;
_error:
err = get_sv( "!", TRUE );
sv_setiv( err, (IV) con->last_errno );
sv_setpv( err, con->last_error );
SvIOK_on( err );
HANDLE_ERROR_XS( this, "lob", con, "Write LOB failed" );