@ -8,7 +8,7 @@
*
* API functions : SQLRowCount , SQLNumResultCols , SQLDescribeCol , SQLColAttributes ,
* SQLGetData , SQLFetch , SQLExtendedFetch ,
* SQLMoreResults ( NI ) , SQLSetPos ( NI ) , SQLSetScrollOptions ( NI ) ,
* SQLMoreResults ( NI ) , SQLSetPos , SQLSetScrollOptions ( NI ) ,
* SQLSetCursorName , SQLGetCursorName
*
* Comments : See " notice.txt " for copyright and license information .
@ -57,13 +57,18 @@ char *msg, *ptr;
SC_log_error ( func , " " , NULL ) ;
return SQL_INVALID_HANDLE ;
}
if ( stmt - > manual_result ) {
if ( pcrow )
* pcrow = - 1 ;
return SQL_SUCCESS ;
}
if ( stmt - > statement_type = = STMT_TYPE_SELECT ) {
if ( stmt - > status = = STMT_FINISHED ) {
res = SC_get_Result ( stmt ) ;
if ( res & & pcrow ) {
* pcrow = globals . use_declarefetch ? 0 : QR_get_num_tuples ( res ) ;
* pcrow = globals . use_declarefetch ? - 1 : QR_get_num_tuples ( res ) ;
return SQL_SUCCESS ;
}
}
@ -176,6 +181,7 @@ Int4 fieldtype = 0;
int precision = 0 ;
ConnInfo * ci ;
char parse_ok ;
char buf [ 255 ] ;
mylog ( " %s: entering... \n " , func ) ;
@ -247,7 +253,8 @@ char parse_ok;
if ( icol > = QR_NumResultCols ( result ) ) {
stmt - > errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR ;
stmt - > errormsg = " Invalid column number in DescribeCol. " ;
SC_log_error ( func , " " , stmt ) ;
sprintf ( buf , " Col#=%d, #Cols=%d " , icol , QR_NumResultCols ( result ) ) ;
SC_log_error ( func , buf , stmt ) ;
return SQL_ERROR ;
}
@ -610,7 +617,6 @@ int num_cols, num_rows;
Int4 field_type ;
void * value ;
int result ;
char multiple ;
mylog ( " SQLGetData: enter, stmt=%u \n " , stmt ) ;
@ -674,7 +680,7 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt);
mylog ( " value = '%s' \n " , value ) ;
}
else { /* its a SOCKET result (backend data) */
if ( stmt - > currTuple = = - 1 | | ! res | | QR_end_tuples ( res ) ) {
if ( stmt - > currTuple = = - 1 | | ! res | | ! res - > tupleField ) {
stmt - > errormsg = " Not positioned on a valid row for GetData. " ;
stmt - > errornumber = STMT_INVALID_CURSOR_STATE_ERROR ;
SC_log_error ( func , " " , stmt ) ;
@ -690,14 +696,12 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt);
mylog ( " **** SQLGetData: icol = %d, fCType = %d, field_type = %d, value = '%s' \n " , icol , fCType , field_type , value ) ;
/* Is this another call for the same column to retrieve more data? */
multiple = ( icol = = stmt - > current_col ) ? TRUE : FALSE ;
stmt - > current_col = icol ;
result = copy_and_convert_field ( stmt , field_type , value ,
fCType , rgbValue , cbValueMax , pcbValue , multiple ) ;
fCType , rgbValue , cbValueMax , pcbValue ) ;
stmt - > current_col = icol ;
stmt - > current_col = - 1 ;
switch ( result ) {
case COPY_OK :
@ -725,7 +729,7 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt);
return SQL_ERROR ;
case COPY_NO_DATA_FOUND :
SC_log_error ( func , " no data found " , stmt ) ;
/* SC_log_error(func, "no data found", stmt); */
return SQL_NO_DATA_FOUND ;
default :
@ -736,70 +740,27 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt);
}
}
// Returns data for bound columns in the current row ("hstmt->iCursor"),
// advances the cursor.
RETCODE SQL_API SQLFetch (
HSTMT hstmt )
RETCODE
SC_fetch ( StatementClass * stmt )
{
static char * func = " SQLFetch " ;
StatementClass * stmt = ( StatementClass * ) hstmt ;
QResultClass * res ;
int retval ;
static char * func = " SC_fetch " ;
QResultClass * res = stmt - > result ;
int retval , result ;
Int2 num_cols , lf ;
Oid type ;
char * value ;
ColumnInfoClass * ci ;
// TupleField *tupleField;
mylog ( " SQLFetch: stmt = %u, stmt->result= %u \n " , stmt , stmt - > result ) ;
if ( ! stmt ) {
SC_log_error ( func , " " , NULL ) ;
return SQL_INVALID_HANDLE ;
}
SC_clear_error ( stmt ) ;
if ( ! ( res = stmt - > result ) ) {
stmt - > errormsg = " Null statement result in SQLFetch. " ;
stmt - > errornumber = STMT_SEQUENCE_ERROR ;
SC_log_error ( func , " " , stmt ) ;
return SQL_ERROR ;
}
stmt - > last_fetch_count = 0 ;
ci = QR_get_fields ( res ) ; /* the column info */
if ( stmt - > status = = STMT_EXECUTING ) {
stmt - > errormsg = " Can't fetch while statement is still executing. " ;
stmt - > errornumber = STMT_SEQUENCE_ERROR ;
SC_log_error ( func , " " , stmt ) ;
return SQL_ERROR ;
}
if ( stmt - > status ! = STMT_FINISHED ) {
stmt - > errornumber = STMT_STATUS_ERROR ;
stmt - > errormsg = " Fetch can only be called after the successful execution on a SQL statement " ;
SC_log_error ( func , " " , stmt ) ;
return SQL_ERROR ;
}
if ( stmt - > bindings = = NULL ) {
// just to avoid a crash if the user insists on calling this
// function even if SQL_ExecDirect has reported an Error
stmt - > errormsg = " Bindings were not allocated properly. " ;
stmt - > errornumber = STMT_SEQUENCE_ERROR ;
SC_log_error ( func , " " , stmt ) ;
return SQL_ERROR ;
}
mylog ( " manual_result = %d, use_declarefetch = %d \n " , stmt - > manual_result , globals . use_declarefetch ) ;
if ( stmt - > manual_result | | ! globals . use_declarefetch ) {
if ( stmt - > currTuple > = QR_get_num_tuples ( res ) - 1 | |
( stmt - > maxRows > 0 & & stmt - > currTuple = = stmt - > maxRows - 1 ) ) {
( stmt - > options . maxRows > 0 & & stmt - > currTuple = = stmt - > options . maxRows - 1 ) ) {
/* if at the end of the tuples, return "no data found"
and set the cursor past the end of the result set
@ -833,10 +794,16 @@ mylog("SQLFetch: stmt = %u, stmt->result= %u\n", stmt, stmt->result);
num_cols = QR_NumResultCols ( res ) ;
result = SQL_SUCCESS ;
stmt - > last_fetch_count = 1 ;
for ( lf = 0 ; lf < num_cols ; lf + + ) {
mylog ( " fetch: cols=%d, lf=%d, stmt = %u, stmt->bindings = %u, buffer[] = %u \n " , num_cols , lf , stmt , stmt - > bindings , stmt - > bindings [ lf ] . buffer ) ;
/* reset for SQLGetData */
stmt - > bindings [ lf ] . data_left = - 1 ;
if ( stmt - > bindings [ lf ] . buffer ! = NULL ) {
// this column has a binding
@ -855,13 +822,12 @@ mylog("SQLFetch: stmt = %u, stmt->result= %u\n", stmt, stmt->result);
value = QR_get_value_backend_row ( res , stmt - > currTuple , lf ) ;
}
mylog ( " value = '%s' \n " , value ) ;
mylog ( " value = '%s' \n " , ( value = = NULL ) ? " <NULL> " : value ) ;
retval = copy_and_convert_field_bindinfo ( stmt , type , value , lf ) ;
mylog ( " copy_and_convert: retval = %d \n " , retval ) ;
switch ( retval ) {
case COPY_OK :
break ; /* OK, do next bound column */
@ -870,37 +836,99 @@ mylog("SQLFetch: stmt = %u, stmt->result= %u\n", stmt, stmt->result);
stmt - > errormsg = " Received an unsupported type from Postgres. " ;
stmt - > errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR ;
SC_log_error ( func , " " , stmt ) ;
return SQL_ERROR ;
result = SQL_ERROR ;
break ;
case COPY_UNSUPPORTED_CONVERSION :
stmt - > errormsg = " Couldn't handle the necessary data type conversion. " ;
stmt - > errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR ;
SC_log_error ( func , " " , stmt ) ;
return SQL_ERROR ;
result = SQL_ERROR ;
break ;
case COPY_RESULT_TRUNCATED :
stmt - > errornumber = STMT_TRUNCATED ;
stmt - > errormsg = " The buffer was too small for the result. " ;
return SQL_SUCCESS_WITH_INFO ;
result = SQL_SUCCESS_WITH_INFO ;
break ;
case COPY_GENERAL_ERROR : /* error msg already filled in */
SC_log_error ( func , " " , stmt ) ;
return SQL_ERROR ;
result = SQL_ERROR ;
break ;
/* This would not be meaningful in SQLFetch. */
case COPY_NO_DATA_FOUND :
SC_log_error ( func , " no data found " , stmt ) ;
return SQL_NO_DATA_FOUND ;
break ;
default :
stmt - > errormsg = " Unrecognized return value from copy_and_convert_field. " ;
stmt - > errornumber = STMT_INTERNAL_ERROR ;
SC_log_error ( func , " " , stmt ) ;
return SQL_ERROR ;
result = SQL_ERROR ;
break ;
}
}
}
return SQL_SUCCESS ;
return result ;
}
// Returns data for bound columns in the current row ("hstmt->iCursor"),
// advances the cursor.
RETCODE SQL_API SQLFetch (
HSTMT hstmt )
{
static char * func = " SQLFetch " ;
StatementClass * stmt = ( StatementClass * ) hstmt ;
QResultClass * res ;
mylog ( " SQLFetch: stmt = %u, stmt->result= %u \n " , stmt , stmt - > result ) ;
if ( ! stmt ) {
SC_log_error ( func , " " , NULL ) ;
return SQL_INVALID_HANDLE ;
}
SC_clear_error ( stmt ) ;
if ( ! ( res = stmt - > result ) ) {
stmt - > errormsg = " Null statement result in SQLFetch. " ;
stmt - > errornumber = STMT_SEQUENCE_ERROR ;
SC_log_error ( func , " " , stmt ) ;
return SQL_ERROR ;
}
if ( stmt - > status = = STMT_EXECUTING ) {
stmt - > errormsg = " Can't fetch while statement is still executing. " ;
stmt - > errornumber = STMT_SEQUENCE_ERROR ;
SC_log_error ( func , " " , stmt ) ;
return SQL_ERROR ;
}
if ( stmt - > status ! = STMT_FINISHED ) {
stmt - > errornumber = STMT_STATUS_ERROR ;
stmt - > errormsg = " Fetch can only be called after the successful execution on a SQL statement " ;
SC_log_error ( func , " " , stmt ) ;
return SQL_ERROR ;
}
if ( stmt - > bindings = = NULL ) {
// just to avoid a crash if the user insists on calling this
// function even if SQL_ExecDirect has reported an Error
stmt - > errormsg = " Bindings were not allocated properly. " ;
stmt - > errornumber = STMT_SEQUENCE_ERROR ;
SC_log_error ( func , " " , stmt ) ;
return SQL_ERROR ;
}
QR_set_rowset_size ( res , 1 ) ;
QR_inc_base ( res , stmt - > last_fetch_count ) ;
return SC_fetch ( stmt ) ;
}
// This fetchs a block of data (rowset).
@ -914,9 +942,10 @@ RETCODE SQL_API SQLExtendedFetch(
{
static char * func = " SQLExtendedFetch " ;
StatementClass * stmt = ( StatementClass * ) hstmt ;
int num_tuples ;
QResultClass * res ;
int num_tuples , i , save_rowset_size ;
RETCODE result ;
char truncated , error ;
mylog ( " SQLExtendedFetch: stmt=%u \n " , stmt ) ;
@ -925,43 +954,108 @@ mylog("SQLExtendedFetch: stmt=%u\n", stmt);
return SQL_INVALID_HANDLE ;
}
if ( globals . use_declarefetch ) {
SC_log_error ( func , " SQLExtendedFetch with UseDeclareFetch not yet supported " , stmt ) ;
if ( globals . use_declarefetch & & ! stmt - > manual_result ) {
if ( fFetchType ! = SQL_FETCH_NEXT ) {
stmt - > errornumber = STMT_NOT_IMPLEMENTED_ERROR ;
stmt - > errormsg = " Unsupported fetch type for SQLExtendedFetch with UseDeclareFetch option. " ;
return SQL_ERROR ;
}
}
SC_clear_error ( stmt ) ;
if ( ! ( res = stmt - > result ) ) {
stmt - > errormsg = " Null statement result in SQLExtendedFetch. " ;
stmt - > errornumber = STMT_SEQUENCE_ERROR ;
SC_log_error ( func , " " , stmt ) ;
return SQL_ERROR ;
}
if ( stmt - > status = = STMT_EXECUTING ) {
stmt - > errormsg = " Can't fetch while statement is still executing. " ;
stmt - > errornumber = STMT_SEQUENCE_ERROR ;
SC_log_error ( func , " " , stmt ) ;
return SQL_ERROR ;
}
if ( stmt - > status ! = STMT_FINISHED ) {
stmt - > errornumber = STMT_STATUS_ERROR ;
stmt - > errormsg = " ExtendedFetch can only be called after the successful execution on a SQL statement " ;
SC_log_error ( func , " " , stmt ) ;
return SQL_ERROR ;
}
if ( stmt - > bindings = = NULL ) {
// just to avoid a crash if the user insists on calling this
// function even if SQL_ExecDirect has reported an Error
stmt - > errormsg = " Bindings were not allocated properly. " ;
stmt - > errornumber = STMT_SEQUENCE_ERROR ;
SC_log_error ( func , " " , stmt ) ;
return SQL_ERROR ;
}
/* Initialize to no rows fetched */
if ( rgfRowStatus )
* rgfRowStatus = SQL_ROW_NOROW ;
for ( i = 0 ; i < stmt - > options . rowset_size ; i + + )
* ( rgfRowStatus + i ) = SQL_ROW_NOROW ;
if ( pcrow )
* pcrow = 0 ;
num_tuples = QR_get_num_tuples ( stmt - > result ) ;
num_tuples = QR_get_num_tuples ( res ) ;
/* Save and discard the saved rowset size */
save_rowset_size = stmt - > save_rowset_size ;
stmt - > save_rowset_size = - 1 ;
switch ( fFetchType ) {
case SQL_FETCH_NEXT :
/* From the odbc spec... If positioned before the start of the RESULT SET,
then this should be equivalent to SQL_FETCH_FIRST .
*/
if ( stmt - > rowset_start < 0 )
stmt - > rowset_start = 0 ;
else {
stmt - > rowset_start + = ( save_rowset_size > 0 ? save_rowset_size : stmt - > options . rowset_size ) ;
}
mylog ( " SQL_FETCH_NEXT: num_tuples=%d, currtuple=%d \n " , num_tuples , stmt - > currTuple ) ;
break ;
case SQL_FETCH_PRIOR :
mylog ( " SQL_FETCH_PRIOR: num_tuples=%d, currtuple=%d \n " , num_tuples , stmt - > currTuple ) ;
/* If already before result set, return no data found */
if ( stmt - > currTuple < = 0 )
return SQL_NO_DATA_FOUND ;
stmt - > currTuple - = 2 ;
/* From the odbc spec... If positioned after the end of the RESULT SET,
then this should be equivalent to SQL_FETCH_LAST .
*/
if ( stmt - > rowset_start > = num_tuples ) {
stmt - > rowset_start = num_tuples < = 0 ? 0 : ( num_tuples - stmt - > options . rowset_size ) ;
}
else {
stmt - > rowset_start - = stmt - > options . rowset_size ;
}
break ;
case SQL_FETCH_FIRST :
mylog ( " SQL_FETCH_FIRST: num_tuples=%d, currtuple=%d \n " , num_tuples , stmt - > currTuple ) ;
stmt - > currTuple = - 1 ;
stmt - > rowset_start = 0 ;
break ;
case SQL_FETCH_LAST :
mylog ( " SQL_FETCH_LAST: num_tuples=%d, currtuple=%d \n " , num_tuples , stmt - > currTuple ) ;
stmt - > currTuple = num_tuples < = 0 ? - 1 : ( num_tuples - 2 ) ;
stmt - > rowset_start = num_tuples < = 0 ? 0 : ( num_tuples - stmt - > options . rowset_size ) ;
break ;
case SQL_FETCH_ABSOLUTE :
@ -969,17 +1063,31 @@ mylog("SQLExtendedFetch: stmt=%u\n", stmt);
/* Position before result set, but dont fetch anything */
if ( irow = = 0 ) {
stmt - > rowset_start = - 1 ;
stmt - > currTuple = - 1 ;
return SQL_NO_DATA_FOUND ;
}
/* Position before the desired row */
else if ( irow > 0 ) {
stmt - > currTuple = irow - 2 ;
stmt - > rowset_start = irow - 1 ;
}
/* Position with respect to the end of the result set */
else {
stmt - > currTuple = num_tuples + irow - 1 ;
stmt - > rowset_start = num_tuples + irow ;
}
break ;
case SQL_FETCH_RELATIVE :
/* Refresh the current rowset -- not currently implemented, but lie anyway */
if ( irow = = 0 ) {
break ;
}
stmt - > rowset_start + = irow ;
break ;
default :
@ -988,18 +1096,95 @@ mylog("SQLExtendedFetch: stmt=%u\n", stmt);
}
/***********************************/
/* CHECK FOR PROPER CURSOR STATE */
/***********************************/
/* Handle Declare Fetch style specially because the end is not really the end... */
if ( globals . use_declarefetch & & ! stmt - > manual_result ) {
if ( QR_end_tuples ( res ) ) {
return SQL_NO_DATA_FOUND ;
}
}
else {
/* If *new* rowset is after the result_set, return no data found */
if ( stmt - > rowset_start > = num_tuples ) {
stmt - > rowset_start = num_tuples ;
return SQL_NO_DATA_FOUND ;
}
}
/* If *new* rowset is prior to result_set, return no data found */
if ( stmt - > rowset_start < 0 ) {
if ( stmt - > rowset_start + stmt - > options . rowset_size < = 0 ) {
stmt - > rowset_start = - 1 ;
return SQL_NO_DATA_FOUND ;
}
else { /* overlap with beginning of result set, so get first rowset */
stmt - > rowset_start = 0 ;
}
}
/* currTuple is always 1 row prior to the rowset */
stmt - > currTuple = stmt - > rowset_start - 1 ;
/* increment the base row in the tuple cache */
QR_set_rowset_size ( res , stmt - > options . rowset_size ) ;
QR_inc_base ( res , stmt - > last_fetch_count ) ;
/* Physical Row advancement occurs for each row fetched below */
mylog ( " SQLExtendedFetch: new currTuple = %d \n " , stmt - > currTuple ) ;
result = SQLFetch ( hstmt ) ;
truncated = error = FALSE ;
for ( i = 0 ; i < stmt - > options . rowset_size ; i + + ) {
if ( result = = SQL_SUCCESS ) {
if ( rgfRowStatus )
* rgfRowStatus = SQL_ROW_SUCCESS ;
if ( pcrow )
* pcrow = 1 ;
stmt - > bind_row = i ; // set the binding location
result = SC_fetch ( stmt ) ;
/* Determine Function status */
if ( result = = SQL_NO_DATA_FOUND )
break ;
else if ( result = = SQL_SUCCESS_WITH_INFO )
truncated = TRUE ;
else if ( result = = SQL_ERROR )
error = TRUE ;
/* Determine Row Status */
if ( rgfRowStatus ) {
if ( result = = SQL_ERROR )
* ( rgfRowStatus + i ) = SQL_ROW_ERROR ;
else
* ( rgfRowStatus + i ) = SQL_ROW_SUCCESS ;
}
}
return result ;
/* Save the fetch count for SQLSetPos */
stmt - > last_fetch_count = i ;
/* Reset next binding row */
stmt - > bind_row = 0 ;
/* Move the cursor position to the first row in the result set. */
stmt - > currTuple = stmt - > rowset_start ;
/* For declare/fetch, need to reset cursor to beginning of rowset */
if ( globals . use_declarefetch & & ! stmt - > manual_result ) {
QR_set_position ( res , 0 ) ;
}
/* Set the number of rows retrieved */
if ( pcrow )
* pcrow = i ;
if ( i = = 0 )
return SQL_NO_DATA_FOUND ; /* Only DeclareFetch should wind up here */
else if ( error )
return SQL_ERROR ;
else if ( truncated )
return SQL_SUCCESS_WITH_INFO ;
else
return SQL_SUCCESS ;
}
@ -1014,8 +1199,8 @@ RETCODE SQL_API SQLMoreResults(
return SQL_NO_DATA_FOUND ;
}
// This positions the cursor within a block of data .
// This positions the cursor within a rowset, that was positioned using SQLExtendedFetch .
// This will be useful (so far) only when using SQLGetData after SQLExtendedFetch.
RETCODE SQL_API SQLSetPos (
HSTMT hstmt ,
UWORD irow ,
@ -1023,12 +1208,57 @@ RETCODE SQL_API SQLSetPos(
UWORD fLock )
{
static char * func = " SQLSetPos " ;
char buf [ 128 ] ;
StatementClass * stmt = ( StatementClass * ) hstmt ;
QResultClass * res ;
int num_cols , i ;
BindInfoClass * bindings = stmt - > bindings ;
sprintf ( buf , " SQLSetPos not implemented: irow=%d, fOption=%d, fLock=%d \n " , irow , fOption , fLock ) ;
if ( ! stmt ) {
SC_log_error ( func , " " , NULL ) ;
return SQL_INVALID_HANDLE ;
}
if ( fOption ! = SQL_POSITION & & fOption ! = SQL_REFRESH ) {
stmt - > errornumber = STMT_NOT_IMPLEMENTED_ERROR ;
stmt - > errormsg = " Only SQL_POSITION/REFRESH is supported for SQLSetPos " ;
SC_log_error ( func , " " , stmt ) ;
return SQL_ERROR ;
}
if ( ! ( res = stmt - > result ) ) {
stmt - > errormsg = " Null statement result in SQLSetPos. " ;
stmt - > errornumber = STMT_SEQUENCE_ERROR ;
SC_log_error ( func , " " , stmt ) ;
return SQL_ERROR ;
}
num_cols = QR_NumResultCols ( res ) ;
if ( irow = = 0 ) {
stmt - > errornumber = STMT_ROW_OUT_OF_RANGE ;
stmt - > errormsg = " Driver does not support Bulk operations. " ;
SC_log_error ( func , " " , stmt ) ;
return SQL_ERROR ;
}
if ( irow > stmt - > last_fetch_count ) {
stmt - > errornumber = STMT_ROW_OUT_OF_RANGE ;
stmt - > errormsg = " Row value out of range " ;
SC_log_error ( func , " " , stmt ) ;
return SQL_ERROR ;
}
irow - - ;
/* Reset for SQLGetData */
for ( i = 0 ; i < num_cols ; i + + )
bindings [ i ] . data_left = - 1 ;
QR_set_position ( res , irow ) ;
stmt - > currTuple = stmt - > rowset_start + irow ;
return SQL_SUCCESS ;
SC_log_error ( func , buf , ( StatementClass * ) hstmt ) ;
return SQL_ERROR ;
}
// Sets options that control the behavior of cursors.