Logo Search packages:      
Sourcecode: unixodbc version File versions  Download package

results.c

/* Module:          results.c
 *
 * Description:     This module contains functions related to 
 *                  retrieving result information through the ODBC API.
 *
 * Classes:         n/a
 *
 * API functions:   SQLRowCount, SQLNumResultCols, SQLDescribeCol, SQLColAttributes,
 *                  SQLGetData, SQLFetch, SQLExtendedFetch, 
 *                  SQLMoreResults(NI), SQLSetPos, SQLSetScrollOptions(NI),
 *                  SQLSetCursorName, SQLGetCursorName
 *
 * Comments:        See "notice.txt" for copyright and license information.
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string.h>
#include "psqlodbc.h"
#include "dlg_specific.h"
#include "environ.h"
#include "connection.h"
#include "statement.h"
#include "bind.h"
#include "qresult.h"
#include "convert.h"
#include "pgtypes.h" 

#include <stdio.h>

#ifndef WIN32
#include "isqlext.h"
#else
#include <windows.h>
#include <sqlext.h>
#endif

extern GLOBAL_VALUES globals;



SQLRETURN   SQLRowCount(SQLHSTMT hstmt,
         SQLLEN *pcrow)
{
static char* const func="SQLRowCount";
StatementClass *stmt = (StatementClass *) hstmt;
QResultClass *res;
char *msg, *ptr;

      if ( ! stmt) {
            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 ? -1 : QR_get_num_tuples(res);
                        return SQL_SUCCESS;
                  }
            }
      } else {

            res = SC_get_Result(stmt);
            if (res && pcrow) {
                  msg = QR_get_command(res);
                  mylog("*** msg = '%s'\n", msg);
                  trim(msg);  /*    get rid of trailing spaces */
                  ptr = strrchr(msg, ' ');
                  if (ptr) {
                        *pcrow = atoi(ptr+1);
                        mylog("**** SQLRowCount(): THE ROWS: *pcrow = %d\n", *pcrow);
                  }
                  else {
                        *pcrow = -1;

                        mylog("**** SQLRowCount(): NO ROWS: *pcrow = %d\n", *pcrow);
                  }

            return SQL_SUCCESS;
            }
      }

      SC_log_error(func, "Bad return value", stmt);
      return SQL_ERROR;     
}


/*      This returns the number of columns associated with the database */
/*      attached to "hstmt". */


RETCODE SQL_API SQLNumResultCols(
        HSTMT     hstmt,
        SWORD FAR *pccol)
{       
static char* const func="SQLNumResultCols";
StatementClass *stmt = (StatementClass *) hstmt;
QResultClass *result;
char parse_ok;

      if ( ! stmt) {
            SC_log_error(func, "", NULL);
            return SQL_INVALID_HANDLE;
      }

      SC_clear_error(stmt);    

      parse_ok = FALSE;
      if (globals.parse && stmt->statement_type == STMT_TYPE_SELECT) {

            if (stmt->parse_status == STMT_PARSE_NONE) {
                  mylog("SQLNumResultCols: calling parse_statement on stmt=%u\n", stmt);
                  parse_statement(stmt);
            }

            if (stmt->parse_status != STMT_PARSE_FATAL) {
                  parse_ok = TRUE;
                  *pccol = stmt->nfld;
                  mylog("PARSE: SQLNumResultCols: *pccol = %d\n", *pccol);
            }
      }

      if ( ! parse_ok) {

            SC_pre_execute(stmt);       
            result = SC_get_Result(stmt);

            mylog("SQLNumResultCols: result = %u, status = %d, numcols = %d\n", result, stmt->status, result != NULL ? QR_NumResultCols(result) : -1);
            if (( ! result) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE)) ) {
                  /* no query has been executed on this statement */
                  SC_set_error(stmt, STMT_SEQUENCE_ERROR, "No query has been executed with that handle");
                  SC_log_error(func, "", stmt);
                  return SQL_ERROR;
            }

            *pccol = QR_NumResultCols(result);
      }

      return SQL_SUCCESS;
}


/*      -       -       -       -       -       -       -       -       - */



/*      Return information about the database column the user wants */
/*      information about. */
RETCODE SQL_API SQLDescribeCol(
        HSTMT      hstmt,
        UWORD      icol,
        UCHAR  FAR *szColName,
        SWORD      cbColNameMax,
        SWORD  FAR *pcbColName,
        SWORD  FAR *pfSqlType,
        SQLULEN FAR *pcbColDef,
        SWORD  FAR *pibScale,
        SWORD  FAR *pfNullable)
{
static char* const func="SQLDescribeCol";
    /* gets all the information about a specific column */
StatementClass *stmt = (StatementClass *) hstmt;
QResultClass *res;
char *col_name = NULL;
Int4 fieldtype = 0;
int precision = 0;
ConnInfo *ci;
char parse_ok;
char buf[255];
int len = 0;
RETCODE result;


      mylog("%s: entering...\n", func);

    if ( ! stmt) {
            SC_log_error(func, "", NULL);
        return SQL_INVALID_HANDLE;
      }

      ci = &(stmt->hdbc->connInfo);

    SC_clear_error(stmt);

      /*    Dont check for bookmark column.  This is the responsibility
            of the driver manager.  
      */

      icol--;           /* use zero based column numbers */


      parse_ok = FALSE;
      if (globals.parse && stmt->statement_type == STMT_TYPE_SELECT) {

            if (stmt->parse_status == STMT_PARSE_NONE) {
                  mylog("SQLDescribeCol: calling parse_statement on stmt=%u\n", stmt);
                  parse_statement(stmt);
            }


            mylog("PARSE: DescribeCol: icol=%d, stmt=%u, stmt->nfld=%d, stmt->fi=%u\n", icol, stmt, stmt->nfld, stmt->fi);

            if (stmt->parse_status != STMT_PARSE_FATAL && stmt->fi && stmt->fi[icol]) {

                  if (icol >= stmt->nfld) {
                        SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR, "Invalid column number in DescribeCol.");
                        SC_log_error(func, "", stmt);
                        return SQL_ERROR;
                  }
                  mylog("DescribeCol: getting info for icol=%d\n", icol);

                  fieldtype = stmt->fi[icol]->type;
                  col_name = stmt->fi[icol]->name;
                  precision = stmt->fi[icol]->precision;

                  mylog("PARSE: fieldtype=%d, col_name='%s', precision=%d\n", fieldtype, col_name, precision);
                  if (fieldtype > 0)
                        parse_ok = TRUE;
            }
      }


      /*    If couldn't parse it OR the field being described was not parsed (i.e., because
            it was a function or expression, etc, then do it the old fashioned way.
      */
      if ( ! parse_ok) {
            SC_pre_execute(stmt);
      
            res = SC_get_Result(stmt);

            mylog("**** SQLDescribeCol: res = %u, stmt->status = %d, !finished=%d, !premature=%d\n", res, stmt->status, stmt->status != STMT_FINISHED, stmt->status != STMT_PREMATURE);
            if ( (NULL == res) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE))) {
                  /* no query has been executed on this statement */
                  SC_set_error(stmt, STMT_SEQUENCE_ERROR, "No query has been assigned to this statement.");
                  SC_log_error(func, "", stmt);
                  return SQL_ERROR;
            }

            if (icol >= QR_NumResultCols(res)) {
                  SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR, "Invalid column number in DescribeCol.");
                  sprintf(buf, "Col#=%d, #Cols=%d", icol, QR_NumResultCols(res));
                  SC_log_error(func, buf, stmt);
                  return SQL_ERROR;
            }

            col_name = QR_get_fieldname(res, icol);
        fieldtype = QR_get_field_type(res, icol);

            precision = pgtype_precision(stmt, fieldtype, icol, globals.unknown_sizes);  /* atoi(ci->unknown_sizes) */
      }

      mylog("describeCol: col %d fieldname = '%s'\n", icol, col_name);
      mylog("describeCol: col %d fieldtype = %d\n", icol, fieldtype);
      mylog("describeCol: col %d precision = %d\n", icol, precision);


      result = SQL_SUCCESS;

      /************************/
      /*          COLUMN NAME     */
      /************************/
      len = strlen(col_name);

      if (pcbColName)
            *pcbColName = len;

      if (szColName) {
            strncpy_null((char*)szColName, col_name, cbColNameMax);

            if (len >= cbColNameMax)  {
                  result = SQL_SUCCESS_WITH_INFO;
                  SC_set_error(stmt, STMT_TRUNCATED, "The buffer was too small for the result.");
            }
    }


      /************************/
      /*          SQL TYPE        */
      /************************/
    if (pfSqlType) {
        *pfSqlType = pgtype_to_sqltype(stmt, fieldtype);

            mylog("describeCol: col %d *pfSqlType = %d\n", icol, *pfSqlType);
      }

      /************************/
      /*          PRECISION       */
      /************************/
    if (pcbColDef) {

            if ( precision < 0)
                  precision = 0;          /* "I dont know" */

            *pcbColDef = precision;

            mylog("describeCol: col %d  *pcbColDef = %d\n", icol, *pcbColDef);
      }

      /************************/
      /*          SCALE           */
      /************************/
    if (pibScale) {
        Int2 scale;
        scale = pgtype_scale(stmt, fieldtype, icol);
        if(scale == -1) { scale = 0; }
        
        *pibScale = scale;
            mylog("describeCol: col %d  *pibScale = %d\n", icol, *pibScale);
    }

      /************************/
      /*          NULLABILITY     */
      /************************/
    if (pfNullable) {
            *pfNullable = (parse_ok) ? stmt->fi[icol]->nullable : pgtype_nullable(stmt, fieldtype);

            mylog("describeCol: col %d  *pfNullable = %d\n", icol, *pfNullable);
    }

    return result;
}

/*      Returns result column descriptor information for a result set. */

SQLRETURN  SQLColAttributes(
    SQLHSTMT           hstmt,
    SQLUSMALLINT       icol,
    SQLUSMALLINT       fDescType,
    SQLPOINTER         rgbDesc,
    SQLSMALLINT        cbDescMax,
    SQLSMALLINT     *pcbDesc,
    SQLLEN              *pfDesc)
{
static char* const func = "SQLColAttributes";
StatementClass *stmt = (StatementClass *) hstmt;
Int4 field_type = 0;
ConnInfo *ci;
int unknown_sizes;
int cols = 0;
char parse_ok;
RETCODE result;
char *p = NULL;
int len = 0, value = 0;

      mylog("%s: entering...\n", func);

      if( ! stmt) {
            SC_log_error(func, "", NULL);
            return SQL_INVALID_HANDLE;
      }

      ci = &(stmt->hdbc->connInfo);

      /*    Dont check for bookmark column.  This is the responsibility
            of the driver manager.  For certain types of arguments, the column
            number is ignored anyway, so it may be 0.
      */

      icol--;

      unknown_sizes = globals.unknown_sizes;          /* atoi(ci->unknown_sizes); */
      if (unknown_sizes == UNKNOWNS_AS_DONTKNOW)            /* not appropriate for SQLColAttributes() */
            unknown_sizes = UNKNOWNS_AS_MAX;

      parse_ok = FALSE;
      if (globals.parse && stmt->statement_type == STMT_TYPE_SELECT) {

            if (stmt->parse_status == STMT_PARSE_NONE) {
                  mylog("SQLColAttributes: calling parse_statement\n");
                  parse_statement(stmt);
            }

            cols = stmt->nfld;

            /*    Column Count is a special case.  The Column number is ignored
                  in this case.
            */
            if (fDescType == SQL_COLUMN_COUNT) {
                  if (pfDesc)
                        *pfDesc = cols;

                  return SQL_SUCCESS;
            }

            if (stmt->parse_status != STMT_PARSE_FATAL && stmt->fi && stmt->fi[icol]) {

                  if (icol >= cols) {
                        SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR, "Invalid column number in DescribeCol.");
                        SC_log_error(func, "", stmt);
                        return SQL_ERROR;
                  }

                  field_type = stmt->fi[icol]->type;
                  if (field_type > 0)
                        parse_ok = TRUE;
            }
      }

      if ( ! parse_ok) {
            SC_pre_execute(stmt);       

            mylog("**** SQLColAtt: result = %u, status = %d, numcols = %d\n", stmt->result, stmt->status, stmt->result != NULL ? QR_NumResultCols(stmt->result) : -1);

            if ( (NULL == stmt->result) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE)) ) {
                  SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Can't get column attributes: no result found.");
                  SC_log_error(func, "", stmt);
                  return SQL_ERROR;
            }

            cols = QR_NumResultCols(stmt->result);

            /*    Column Count is a special case.  The Column number is ignored
                  in this case.
            */
            if (fDescType == SQL_COLUMN_COUNT) {
                  if (pfDesc)
                        *pfDesc = cols;

                  return SQL_SUCCESS;
            }

            if (icol >= cols) {
                  SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR, "Invalid column number in DescribeCol.");
                  SC_log_error(func, "", stmt);
                  return SQL_ERROR;
            }

            field_type = QR_get_field_type(stmt->result, icol);
      }

      mylog("colAttr: col %d field_type = %d\n", icol, field_type);

      switch(fDescType) {
      case SQL_COLUMN_AUTO_INCREMENT:
            value  = pgtype_auto_increment(stmt, field_type);
            if (value == -1)  /*  non-numeric becomes FALSE (ODBC Doc) */
                  value = FALSE;
                        
            break;

      case SQL_COLUMN_CASE_SENSITIVE:
            value = pgtype_case_sensitive(stmt, field_type);
            break;

      /*    This special case is handled above.

      case SQL_COLUMN_COUNT: 
      */

    case SQL_COLUMN_DISPLAY_SIZE:
            value = (parse_ok) ? stmt->fi[icol]->display_size : pgtype_display_size(stmt, field_type, icol, unknown_sizes);

            mylog("SQLColAttributes: col %d, display_size= %d\n", icol, value);

        break;

      case SQL_COLUMN_LABEL:
            if (parse_ok && stmt->fi[icol]->alias[0] != '\0') {
                  p = stmt->fi[icol]->alias;

                  mylog("SQLColAttr: COLUMN_LABEL = '%s'\n", p);
                  break;

            }     /* otherwise same as column name -- FALL THROUGH!!! */

      case SQL_COLUMN_NAME:

            p = (parse_ok) ? stmt->fi[icol]->name : QR_get_fieldname(stmt->result, icol);

            mylog("SQLColAttr: COLUMN_NAME = '%s'\n", p);
            break;

      case SQL_COLUMN_LENGTH:
            value = (parse_ok) ? stmt->fi[icol]->length :  pgtype_length(stmt, field_type, icol, unknown_sizes); 

            mylog("SQLColAttributes: col %d, length = %d\n", icol, value);
        break;

      case SQL_COLUMN_MONEY:
            value = pgtype_money(stmt, field_type);
            break;

      case SQL_COLUMN_NULLABLE:
            value = (parse_ok) ? stmt->fi[icol]->nullable : pgtype_nullable(stmt, field_type);
            break;

      case SQL_COLUMN_OWNER_NAME:
            p = "";
            break;

      case SQL_COLUMN_PRECISION:
            value = (parse_ok) ? stmt->fi[icol]->precision : pgtype_precision(stmt, field_type, icol, unknown_sizes);

            mylog("SQLColAttributes: col %d, precision = %d\n", icol, value);
        break;

      case SQL_COLUMN_QUALIFIER_NAME:
            p = "";
            break;

      case SQL_COLUMN_SCALE:
            value = pgtype_scale(stmt, field_type, icol);
            break;

      case SQL_COLUMN_SEARCHABLE:
            value = pgtype_searchable(stmt, field_type);
            break;

    case SQL_COLUMN_TABLE_NAME:

            p = (parse_ok && stmt->fi[icol]->ti) ? stmt->fi[icol]->ti->name : "";

            mylog("SQLColAttr: TABLE_NAME = '%s'\n", p);
        break;

      case SQL_COLUMN_TYPE:
            value = pgtype_to_sqltype(stmt, field_type);
            break;

      case SQL_COLUMN_TYPE_NAME:
            p = pgtype_to_name(stmt, field_type);
            break;

      case SQL_COLUMN_UNSIGNED:
            value = pgtype_unsigned(stmt, field_type);
            if(value == -1)   /* non-numeric becomes TRUE (ODBC Doc) */
                  value = TRUE;

            break;

      case SQL_COLUMN_UPDATABLE:
            /*    Neither Access or Borland care about this.

            if (field_type == PG_TYPE_OID)
                  *pfDesc = SQL_ATTR_READONLY;
            else
            */

            value = SQL_ATTR_WRITE;

            mylog("SQLColAttr: UPDATEABLE = %d\n", value);
            break;
    }

      result = SQL_SUCCESS;

      if (p) {  /* char/binary data */
            len = strlen(p);

            if (rgbDesc) {
                  strncpy_null((char *)rgbDesc, p, (size_t)cbDescMax);

                  if (len >= cbDescMax)  {
                        result = SQL_SUCCESS_WITH_INFO;
                        SC_set_error(stmt, STMT_TRUNCATED, "The buffer was too small for the result.");
                  }
            }

            if (pcbDesc) 
                  *pcbDesc = len;
      }
      else {      /* numeric data */

            if (pfDesc)
                  *pfDesc = value;

      }


    return result;
}

/*      Returns result data for a single column in the current row. */

RETCODE SQL_API PG_SQLGetData(
        HSTMT      hstmt,
        UWORD      icol,
        SWORD      fCType,
        PTR        rgbValue,
        SDWORD     cbValueMax,
        SDWORD FAR *pcbValue)
{
static char* const func="SQLGetData";
QResultClass *res;
StatementClass *stmt = (StatementClass *) hstmt;
int num_cols, num_rows;
Int4 field_type;
void *value = NULL;
int result;
char get_bookmark = FALSE;

mylog("SQLGetData: enter, stmt=%u\n", stmt);

    if( ! stmt) {
            SC_log_error(func, "", NULL);
        return SQL_INVALID_HANDLE;
    }
      res = stmt->result;

    if (STMT_EXECUTING == stmt->status) {
      SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Can't get data while statement is still executing.");
      SC_log_error(func, "", stmt);
      return SQL_ERROR;
    }

    if (stmt->status != STMT_FINISHED) {
      SC_set_error(stmt, STMT_STATUS_ERROR, "GetData can only be called after the successful execution on a SQL statement");
      SC_log_error(func, "", stmt);
      return SQL_ERROR;
    }

    if (icol == 0) {

            if (stmt->options.use_bookmarks == SQL_UB_OFF) {
                  SC_set_error(stmt, STMT_COLNUM_ERROR, "Attempt to retrieve bookmark with bookmark usage disabled");
                  SC_log_error(func, "", stmt);
                  return SQL_ERROR;
            }

            /*    Make sure it is the bookmark data type */
            if (fCType != SQL_C_BOOKMARK && fCType != SQL_C_BINARY ) {
                  SC_set_error(stmt, STMT_PROGRAM_TYPE_OUT_OF_RANGE, "Column 0 is not of type SQL_C_BOOKMARK");
                  SC_log_error(func, "", stmt);
                  return SQL_ERROR;
            }
            
            get_bookmark = TRUE;

    }

      else {

            /* use zero-based column numbers */
            icol--;

            /* make sure the column number is valid */
            num_cols = QR_NumResultCols(res);
            if (icol >= num_cols) {
                  SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR, "Invalid column number.");
                  SC_log_error(func, "", stmt);
                  return SQL_ERROR;
            }
      }

      if ( stmt->manual_result || ! globals.use_declarefetch) {
            /* make sure we're positioned on a valid row */
            num_rows = QR_get_num_tuples(res);
            if((stmt->currTuple < 0) ||
               (stmt->currTuple >= num_rows)) {
                  SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Not positioned on a valid row for GetData.");
                  SC_log_error(func, "", stmt);
                  return SQL_ERROR;
            }
            mylog("     num_rows = %d\n", num_rows);

            if ( ! get_bookmark) {
                  if ( stmt->manual_result) {
                        value = QR_get_value_manual(res, stmt->currTuple, icol);
                  }
                  else {
                        value = QR_get_value_backend_row(res, stmt->currTuple, icol);
                  }
                  mylog("     value = '%s'\n", value);
            }
      }
      else { /* it's a SOCKET result (backend data) */
            if (stmt->currTuple == -1 || ! res || ! res->tupleField) {
                  SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Not positioned on a valid row for GetData.");
                  SC_log_error(func, "", stmt);
                  return SQL_ERROR;
            }

            if ( ! get_bookmark)
                  value = QR_get_value_backend(res, icol);

            mylog("  socket: value = '%s'\n", value);
      }

      if ( get_bookmark) {
            *((UDWORD *) rgbValue) = SC_get_bookmark(stmt);

            if (pcbValue)
                  *pcbValue = 4;

            return SQL_SUCCESS;
      }

      field_type = QR_get_field_type(res, icol);

      mylog("**** SQLGetData: icol = %d, fCType = %d, field_type = %d, value = '%s'\n", icol, fCType, field_type, value);

      stmt->current_col = icol;

    result = copy_and_convert_field(stmt, field_type, value, 
                                    fCType, rgbValue, cbValueMax, (SQLLEN*)pcbValue);

      stmt->current_col = -1;

      switch(result) {
      case COPY_OK:
            return SQL_SUCCESS;

      case COPY_UNSUPPORTED_TYPE:
            SC_set_error(stmt, STMT_RESTRICTED_DATA_TYPE_ERROR, "Received an unsupported type from Postgres.");
            SC_log_error(func, "", stmt);
            return SQL_ERROR;

      case COPY_UNSUPPORTED_CONVERSION:
            SC_set_error(stmt, STMT_RESTRICTED_DATA_TYPE_ERROR, "Couldn't handle the necessary data type conversion.");
            SC_log_error(func, "", stmt);
            return SQL_ERROR;

      case COPY_RESULT_TRUNCATED:
            SC_set_error(stmt, STMT_TRUNCATED, "The buffer was too small for the result.");
            return SQL_SUCCESS_WITH_INFO;

      case COPY_GENERAL_ERROR:      /* error msg already filled in */
            SC_log_error(func, "", stmt);
            return SQL_ERROR;

      case COPY_NO_DATA_FOUND:
            /* SC_log_error(func, "no data found", stmt); */
            return SQL_NO_DATA_FOUND;

    default:
      SC_set_error(stmt, STMT_INTERNAL_ERROR, "Unrecognized return value from copy_and_convert_field.");
      SC_log_error(func, "", stmt);
      return SQL_ERROR;
    }
}

SQLRETURN   SQLGetData(SQLHSTMT hstmt,
           SQLUSMALLINT icol, SQLSMALLINT fCType,
           SQLPOINTER rgbValue, SQLLEN cbValueMax,
           SQLLEN *pcbValue)
{
    return PG_SQLGetData( hstmt, 
                        icol, 
                        fCType, 
                        rgbValue, 
                        cbValueMax, 
                        (SDWORD FAR *)pcbValue );
}

/*      Returns data for bound columns in the current row ("hstmt->iCursor"), */
/*      advances the cursor. */

RETCODE SQL_API PG_SQLFetch(
        HSTMT   hstmt)
{
static char* const 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)) {
            SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Null statement result in SQLFetch.");
            SC_log_error(func, "", stmt);
            return SQL_ERROR;
      }

      /*    Not allowed to bind a bookmark column when using SQLFetch. */
      if ( stmt->bookmark.buffer) {
            SC_set_error(stmt, STMT_COLNUM_ERROR, "Not allowed to bind a bookmark column when using SQLFetch");
            SC_log_error(func, "", stmt);
            return SQL_ERROR;
      }

      if (stmt->status == STMT_EXECUTING) {
            SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Can't fetch while statement is still executing.");
            SC_log_error(func, "", stmt);
            return SQL_ERROR;
      }


      if (stmt->status != STMT_FINISHED) {
            SC_set_error(stmt, STMT_STATUS_ERROR, "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 */
            SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Bindings were not allocated properly.");
            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);
}

RETCODE SQL_API SQLFetch(
        HSTMT   hstmt)
{
    return PG_SQLFetch( hstmt );
}

/*      This fetchs a block of data (rowset). */

SQLRETURN  SQLExtendedFetch(
    SQLHSTMT           hstmt,
    SQLUSMALLINT       fFetchType,
    SQLROWOFFSET         irow,
    SQLROWSETSIZE         *pcrow,
    SQLUSMALLINT    *rgfRowStatus)
{
static char* const func = "SQLExtendedFetch";
StatementClass *stmt = (StatementClass *) hstmt;
QResultClass *res;
int num_tuples, i, save_rowset_size;
RETCODE result;
char truncated, error;

mylog("SQLExtendedFetch: stmt=%u\n", stmt);

      if ( ! stmt) {
            SC_log_error(func, "", NULL);
            return SQL_INVALID_HANDLE;
      }

      if ( globals.use_declarefetch && ! stmt->manual_result) {
            if ( fFetchType != SQL_FETCH_NEXT) {
                  SC_set_error(stmt, STMT_NOT_IMPLEMENTED_ERROR, "Unsupported fetch type for SQLExtendedFetch with UseDeclareFetch option.");
                  return SQL_ERROR;
            }
      }

      SC_clear_error(stmt);

      if ( ! (res = stmt->result)) {
            SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Null statement result in SQLExtendedFetch.");
            SC_log_error(func, "", stmt);
            return SQL_ERROR;
      }

      /*    If a bookmark colunmn is bound but bookmark usage is off, then error */
      if (stmt->bookmark.buffer && stmt->options.use_bookmarks == SQL_UB_OFF) {
            SC_set_error(stmt, STMT_COLNUM_ERROR, "Attempt to retrieve bookmark with bookmark usage disabled");
            SC_log_error(func, "", stmt);
            return SQL_ERROR;
      }

      if (stmt->status == STMT_EXECUTING) {
            SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Can't fetch while statement is still executing.");
            SC_log_error(func, "", stmt);
            return SQL_ERROR;
      }

      if (stmt->status != STMT_FINISHED) {
            SC_set_error(stmt, STMT_STATUS_ERROR, "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 */
            SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Bindings were not allocated properly.");
            SC_log_error(func, "", stmt);
            return SQL_ERROR;
      }

      /*    Initialize to no rows fetched */
      if (rgfRowStatus)
            for (i = 0; i < stmt->options.rowset_size; i++)
                  *(rgfRowStatus + i) = SQL_ROW_NOROW;

      if (pcrow)
            *pcrow = 0;

      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);


            /*    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->rowset_start = 0;
            break;

      case SQL_FETCH_LAST:
            mylog("SQL_FETCH_LAST: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);

            stmt->rowset_start = num_tuples <= 0 ? 0 : (num_tuples - stmt->options.rowset_size) ;
            break;

      case SQL_FETCH_ABSOLUTE:
            mylog("SQL_FETCH_ABSOLUTE: num_tuples=%d, currtuple=%d, irow=%d\n", num_tuples, stmt->currTuple, irow);

            /*    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->rowset_start = irow - 1;
            }
            /*    Position with respect to the end of the result set */
            else {
                  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;

      case SQL_FETCH_BOOKMARK:

            stmt->rowset_start = irow - 1;
            break;

      default:
            SC_log_error(func, "Unsupported SQLExtendedFetch Direction", stmt);
            return SQL_ERROR;   

      }           


      /***********************************/
      /*    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);

      truncated = error = FALSE;
      for (i = 0; i < stmt->options.rowset_size; i++) {

            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;
            }
      }

      /*    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;

}


/*      This determines whether there are more results sets available for */
/*      the "hstmt". */

/* CC: return SQL_NO_DATA_FOUND since we do not support multiple result sets */
RETCODE SQL_API SQLMoreResults(
        HSTMT   hstmt)
{
      return SQL_NO_DATA_FOUND;
}

/*     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,
        SQLSETPOSIROW   irow,
        UWORD   fOption,
        UWORD   fLock)
{
static char* const func = "SQLSetPos";
StatementClass *stmt = (StatementClass *) hstmt;
QResultClass *res;
int num_cols, i;
BindInfoClass *bindings = stmt->bindings;

      if ( ! stmt) {
            SC_log_error(func, "", NULL);
            return SQL_INVALID_HANDLE;
      }

      if (fOption != SQL_POSITION && fOption != SQL_REFRESH) {
            SC_set_error(stmt, STMT_NOT_IMPLEMENTED_ERROR, "Only SQL_POSITION/REFRESH is supported for SQLSetPos");
            SC_log_error(func, "", stmt);
            return SQL_ERROR;
      }

      if ( ! (res = stmt->result)) {
            SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Null statement result in SQLSetPos.");
            SC_log_error(func, "", stmt);
            return SQL_ERROR;
      }
      num_cols = QR_NumResultCols(res);

      if (irow == 0) {
            SC_set_error(stmt, STMT_ROW_OUT_OF_RANGE, "Driver does not support Bulk operations.");
            SC_log_error(func, "", stmt);
            return SQL_ERROR;
      }

      if (irow > stmt->last_fetch_count) {
            SC_set_error(stmt, STMT_ROW_OUT_OF_RANGE, "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;

}

/*      Sets options that control the behavior of cursors. */

SQLRETURN  SQLSetScrollOptions(    /*      Use SQLSetStmtOptions */
    SQLHSTMT           hstmt,
    SQLUSMALLINT       fConcurrency,
    SQLLEN             crowKeyset,
    SQLUSMALLINT       crowRowset)
{
static char* const func = "SQLSetScrollOptions";

      SC_log_error(func, "Function not implemented", (StatementClass *) hstmt);
      return SQL_ERROR;
}


/*      Set the cursor name on a statement handle */

RETCODE SQL_API SQLSetCursorName(
        HSTMT     hstmt,
        UCHAR FAR *szCursor,
        SWORD     cbCursor)
{
static char* const func="SQLSetCursorName";
StatementClass *stmt = (StatementClass *) hstmt;
int len;

mylog("SQLSetCursorName: hstmt=%u, szCursor=%u, cbCursorMax=%d\n", hstmt, szCursor, cbCursor);

      if ( ! stmt) {
            SC_log_error(func, "", NULL);
            return SQL_INVALID_HANDLE;
      }

      len = (cbCursor == SQL_NTS) ? strlen((char*)szCursor) : cbCursor;

      if (len <= 0 || len > sizeof(stmt->cursor_name) - 1) {
            SC_set_error(stmt, STMT_INVALID_CURSOR_NAME, "Invalid Cursor Name");
            SC_log_error(func, "", stmt);
            return SQL_ERROR;
      }

      strncpy_null((char*)stmt->cursor_name, (char*)szCursor, len+1);
      return SQL_SUCCESS;
}

/*      Return the cursor name for a statement handle */

RETCODE SQL_API SQLGetCursorName(
        HSTMT     hstmt,
        UCHAR FAR *szCursor,
        SWORD     cbCursorMax,
        SWORD FAR *pcbCursor)
{
static char* const func="SQLGetCursorName";
StatementClass *stmt = (StatementClass *) hstmt;
int len = 0;
RETCODE result;

mylog("SQLGetCursorName: hstmt=%u, szCursor=%u, cbCursorMax=%d, pcbCursor=%u\n", hstmt, szCursor, cbCursorMax, pcbCursor);

      if ( ! stmt) {
            SC_log_error(func, "", NULL);
            return SQL_INVALID_HANDLE;
      }

      if ( stmt->cursor_name[0] == '\0') {
            SC_set_error(stmt, STMT_NO_CURSOR_NAME, "No Cursor name available");
            SC_log_error(func, "", stmt);
            return SQL_ERROR;
      }

      result = SQL_SUCCESS;
      len = strlen(stmt->cursor_name);

      if (szCursor) {
            strncpy_null((char*)szCursor, (char*)stmt->cursor_name, cbCursorMax);

            if (len >= cbCursorMax)  {
                  result = SQL_SUCCESS_WITH_INFO;
                  SC_set_error(stmt, STMT_TRUNCATED, "The buffer was too small for the result.");
            }
      }

      if (pcbCursor)
            *pcbCursor = len;

      return result;
}



Generated by  Doxygen 1.6.0   Back to index