.ad 8
.bm 8
.fm 4
.bt $Copyright (c) 2000-2004 SAP AG$$Page %$
.tm 12
.hm 6
.hs 3
.tt 1 $SQL$Project Distributed Database System$VPA12AC$
.tt 2 $$$
.TT 3 $BurkhardD$LEVEL 2 FUNCTIONS A - N$2001-07-10$
***********************************************************
.nf

.nf


    ========== licence begin  GPL
    Copyright (c) 2000-2004 SAP AG

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end

.fo


.fo
Module  :
=========
.sp
Purpose :
.CM *-END-* purpose -------------------------------------
Define  :
.CM *-END-* define --------------------------------------
Use     :
.CM *-END-* use -----------------------------------------
Synonym :
.CM *-END-* synonym -------------------------------------
.sp;.cp 3
Author  : BurkhardD
.sp
.cp 3
Created : 08-31-1993
.sp
.cp 3
Version : 1994-04-25
.sp
.cp 3
Release :  7.3    Date : 2001-07-10
.sp
Specification:
.CM *-END-* specification -------------------------------
.sp 2
***********************************************************
.sp
.cp 10
.fo
.oc _/1
Description:
.CM *-END-* description ---------------------------------
.sp 2
***********************************************************
.sp
.cp 10
.nf
.oc _/1
Structure:
.CM *-END-* structure -----------------------------------
.sp 2
**********************************************************
.sp
.cp 10
.nf
.CM -lll-
Code    :
#ifndef DEBUG
#line 63 "vpa12ac"
#endif

#include "vpa00global.h"
#include "vpa10Handle.h"
#include "vpa20Desc.h"
#include "vpa50Env.h"
#include "vpa40DBC.h"
#include "vpa60Stmt.h"
#include "vpa80String.h"
#include "vpa30.h"
#include "vpa06.h"
#include "vpa09.h"
#include "SAPDB/SAPDBCommon/MemoryManagement/SAPDBMem_Alloca.h"

/* CONSTANTS */
#ifndef SAPDB_FAST
#ifndef _UNICODE_ODBC
UCHAR *PA12_FN_PA12FETCHONEROW  = { (UCHAR*) "pa12FetchOneRow" };
UCHAR *PA12_FN_PA12SELECTONEROW  = { (UCHAR*) "pa12SelectOneRow" };
UCHAR *PA12_FN_SQLBULKOPERATIONS = { (UCHAR*) "SQLBulkOperations" };
UCHAR *PA12_FN_SQLCOLUMNPRIVILEGES = { (UCHAR*) "SQLColumnPrivileges" };
UCHAR *PA12_FN_SQLDESCRIBEPARAM = { (UCHAR*) "SQLDescribeParam" };
UCHAR *PA12_FN_SQLEXTENDEDFETCH  = { (UCHAR*) "SQLExtendedFetch" };
UCHAR *PA12_FN_SQLFOREIGNKEYS = { (UCHAR*) "SQLForeignKeys" };
UCHAR *PA12_FN_SQLMORERESULTS = { (UCHAR*) "SQLMoreResults" };
UCHAR *PA12_FN_SQLNUMPARAMS  = { (UCHAR*) "SQLNumParams" };
#else
extern UCHAR *PA12_FN_PA12FETCHONEROW;
extern UCHAR *PA12_FN_PA12SELECTONEROW;
extern UCHAR *PA12_FN_SQLBULKOPERATIONS;
extern UCHAR *PA12_FN_SQLCOLUMNPRIVILEGES;
extern UCHAR *PA12_FN_SQLDESCRIBEPARAM;
extern UCHAR *PA12_FN_SQLEXTENDEDFETCH;
extern UCHAR *PA12_FN_SQLFOREIGNKEYS;
extern UCHAR *PA12_FN_SQLMORERESULTS;
extern UCHAR *PA12_FN_SQLNUMPARAMS;
#endif
#endif

#ifndef _UNICODE_ODBC
/* ------------------------------------------------------------------------ */
/* SQLForeingKeys SQL-Statements */
const char *PA12SELECTFOREIGNKEYS = \
"SELECT PKTABLE_QUALIFIER, PKTABLE_OWNER, PKTABLE_NAME, PKCOLUMN_NAME, \
FKTABLE_QUALIFIER, FKTABLE_OWNER, FKTABLE_NAME, FKCOLUMN_NAME, KEY_SEQ, \
UPDATE_RULE, DELETE_RULE, FK_NAME, PK_NAME \
FROM %sFOREIGNKEYS WHERE ";

const char *PA12SELECTFOREIGNKEYS_OV3 = \
"SELECT PKTABLE_QUALIFIER PKTABLE_CAT, PKTABLE_OWNER PKTABLE_SCHEM, \
PKTABLE_NAME, PKCOLUMN_NAME, \
FKTABLE_QUALIFIER FK_TABLE_CAT, FKTABLE_OWNER FKTABLE_SCHEM, \
FKTABLE_NAME, FKCOLUMN_NAME, KEY_SEQ, \
UPDATE_RULE, DELETE_RULE, FK_NAME, PK_NAME, NUM(7) DEFERRABILITY \
FROM %sFOREIGNKEYS WHERE ";

const char *PA12SELECTPKKEYS = \
"PKTABLE_OWNER = '%'=.*S' AND PKTABLE_NAME = '%'=.*S' ";
const char *PA12SELECTFKKEYS = \
"FKTABLE_OWNER = '%'=.*S' AND FKTABLE_NAME = '%'=.*S' ";

const char *PA12AND = "AND ";
const char *PA12ORDERBYPKTABLE = "ORDER BY 2, 3, 9";
const char *PA12ORDERBYFKTABLE = "ORDER BY 6, 7 ";
/* ------------------------------------------------------------------------ */
/* SQLColumnPrivileges SQL-Statements */
const char *PA12SELECTCOLUMNPRIVILEGES = \
"SELECT TABLE_QUALIFIER, TABLE_OWNER, TABLE_NAME, COLUMN_NAME, GRANTOR, \
GRANTEE, PRIVILEGE, IS_GRANTABLE FROM %sCOLUMNPRIVILEGES \
WHERE TABLE_OWNER = '%%'=.*S' AND TABLE_NAME = '%%'=.*S' AND COLUMN_NAME LIKE '%%'=.*S' \
ESCAPE '%%c' ORDER BY 2, 3, 4, 7 ";

/* ODBC version 3.x defines differnet column names */
const char *PA12SELECTCOLUMNPRIVILEGES_OV3 = \
"SELECT TABLE_QUALIFIER TABLE_CAT, TABLE_OWNER TABLE_SCHEM, \
TABLE_NAME, COLUMN_NAME, GRANTOR, GRANTEE, PRIVILEGE, IS_GRANTABLE \
FROM %sCOLUMNPRIVILEGES \
WHERE TABLE_OWNER = '%%'=.*S' AND TABLE_NAME = '%%'=.*S' AND COLUMN_NAME LIKE '%%'=.*S' \
ESCAPE '%%c' ORDER BY 2, 3, 4, 7 ";

#else
/* ------------------------------------------------------------------------ */
const char *PA12SELECTFOREIGNKEYS;
const char *PA12SELECTFOREIGNKEYS_OV3;
const char *PA12SELECTPKKEYS;
const char *PA12SELECTFKKEYS;
const char *PA12AND;
const char *PA12ORDERBYPKTABLE;
const char *PA12ORDERBYFKTABLE;
const char *PA12SELECTCOLUMNPRIVILEGES;
const char *PA12SELECTCOLUMNPRIVILEGES_OV3;
#endif  /* of _UNICODE_ODBC */


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

/* PROTOTYPES from VPA12AC */

/* SQLExtendedFetch version for dynamic cursors. For dynamic cursors
 * "FOR REUSE" is not appended to a select statement.
 * This does not support bookmarks, because bookmarks are implemented as
 * absolute row numbers which are not available when the result table
 * is not generated.
 */
RETCODE pa12_DynamicCursorExtendedFetch( SQLHSTMT       hstmt,
                                         UWORD          fFetchType,
                                         SQLROWOFFSET   irow,
                                         SQLROWSETSIZE *pcrow,
                                         UWORD         *rgfRowStatus );

/* Initialize status array with SQL_ROW_NOROW */
VOID pa12_InitRowStatusArray( UWORD *rgfRowStatus, UDWORD rowset_size );
    
/* Execute select statement again to refresh the result table.
 * The cursor is moved to its original position.
 */
RETCODE pa12_UpdateResultTable( SQLHSTMT hstmt );


/* Check if the fetch is "forward" or "backward". The function returns
 * 1 for "forward", -1 for "backward" and 0 for FETCH_RELATIVE(0).
 */
SWORD pa12_GetDirection( UWORD         fetchtype,
                         SQLROWOFFSET  irow );


/* Determines the fetchtype and position to fetch the first row
 * of the new rowset. The fetchtype and position depend on fFetchType,
 * irow and also on the current rowset position (stmt->rowset_pos)
 */
RETCODE pa12_GetFetchTypeAndPos( SQLHSTMT      hstmt,
                                 UWORD         fFetchType,
                                 SQLROWOFFSET  irow,
                                 UWORD        *firstFetchType,
                                 SQLROWOFFSET *pos );


/* Check if the fetchtype is allowed in the current cursor model
 * and if rowset_size is valid.
 */
RETCODE pa12_FetchTypeIsValid( UWORD          fetchtype,
                               tpa60Stmt     *stmt_block_ptr,
                               UWORD         *sqlState     );


/* Set the signed fetch-pos in the stmt_block depending on the fetch
 * described by fetchtype an irow.
 * This is the position of the cursor beginning at the first row
 * (positive values) or the last row (negative values) of the result set.
 */
void pa12_SetSFetchPos( tpa60Stmt     *stmt_block_ptr,
                        UWORD          fFetchType,
                        SQLROWOFFSET  irow );

RETCODE pa12FetchOneRow( tpa50Env       *env_block_ptr, 
                         tpa40DBC       *dbc_block_ptr,
                         tpa60Stmt      *stmt_block_ptr,
                         tpa20Desc      *appl_desc_ptr,
                         tpa20Desc      *impl_desc_ptr,                        
                         UWORD           fetchtype, 
                         SQLROWOFFSET    pos,
                         UWORD          *rowstatus,
                         SQLHSTMT        hstmt);

VOID pa12ProcessRetcode( RETCODE retcode, RETCODE *rowret);


RETCODE pa12SelectOneRow( SQLHSTMT   hstmt,
                          tpa20Desc *appl_desc_ptr,
                          tpa20Desc *impl_desc_ptr );

#ifndef _UNICODE_ODBC
/* returns the table prefix needed to distinguish between the old system tables/views (<= 7.5)
   used by ODBC and the new schema based ones (>= 7.6). The static SQL statements have a
   %s at the right place, where the table prefix will be complemented.
   http://pts:1080/webpts?wptsdetail=yes&ErrorType=1&ErrorID=1130120 */
const char * pa12_getTablePrefix (tpa40DBC *dbc_block_ptr)
{
  const char *tableprefix;

  if (dbc_block_ptr->kernelVersionString[0] < '7'  ||
      (dbc_block_ptr->kernelVersionString[0] == '7'  &&
       dbc_block_ptr->kernelVersionString[1] == '0'  &&
       dbc_block_ptr->kernelVersionString[2] <= '5'))
    tableprefix = ""SYSDBA".SYSODBC";
  else
    tableprefix = "SYSODBC.";       /* schema for all system views/tables */

  return tableprefix;
}
#else
const char * pa12_getTablePrefix (tpa40DBC *dbc_block_ptr);
#endif

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

typedef struct {
  tpr05_String  *cpriv_qualifier;
  tpr05_String  *cpriv_owner;
  tpr05_String  *cpriv_table;
  tpr05_String  *cpriv_column;
} pa11StackSQLColumnPrivileges;

#ifdef _UNICODE_ODBC
ODBC_PROC(SQLColumnPrivilegesW,
	  (SQLHSTMT      hstmt,
	   SQLWCHAR     *szTableQualifier,
	   SQLSMALLINT   cbTableQualifier,
	   SQLWCHAR     *szTableOwner,
	   SQLSMALLINT   cbTableOwner,
	   SQLWCHAR     *szTableName,
	   SQLSMALLINT   cbTableName,
	   SQLWCHAR     *szColumnName,
	   SQLSMALLINT   cbColumnName),
	  (hstmt, szTableQualifier, cbTableQualifier, szTableOwner,
           cbTableOwner, szTableName, cbTableName, szColumnName, cbColumnName))
#else
ODBC_PROC(SQLColumnPrivileges,
	  (SQLHSTMT     hstmt,
	   SQLCHAR     *szTableQualifier,
	   SQLSMALLINT  cbTableQualifier,
	   SQLCHAR     *szTableOwner,
	   SQLSMALLINT  cbTableOwner,
	   SQLCHAR     *szTableName,
	   SQLSMALLINT  cbTableName,
	   SQLCHAR     *szColumnName,
	   SQLSMALLINT  cbColumnName),
	  (hstmt, szTableQualifier, cbTableQualifier, szTableOwner,
           cbTableOwner, szTableName, cbTableName, szColumnName, cbColumnName))
#endif
{
    RETCODE       retcode=SQL_SUCCESS;
    API_RETCODE   api_retcode;
    UWORD         state;

    pa11StackSQLColumnPrivileges LV;
    SQLHENV     henv;
    SQLHDBC     hdbc;
    tpa40DBC   *dbc_block_ptr;
    tpa50Env   *env_block_ptr;
    tpa60Stmt  *stmt_block_ptr;
    UDWORD      odbcVersion;
    char       *selectColumnPrivileges;
    const char *tableprefix;
#ifdef _UNICODE_ODBC
    const tsp77encoding *encodingType = sp77nativeUnicodeEncoding ();
    SQLCHAR      sql_string [((API_USERID_MAX_LEN*2 + API_TABLE_NAME_MAX*4) + 
                              sizeof (PA12SELECTCOLUMNPRIVILEGES_OV3) + 60)
                            * sizeof (tsp81_UCS2Char)];
#else
    const tsp77encoding *encodingType = sp77encodingAscii;
    SQLCHAR      sql_string [((API_USERID_MAX_LEN*2 + API_TABLE_NAME_MAX*4) + 
                              sizeof (PA12SELECTCOLUMNPRIVILEGES_OV3) + 60)];
#endif   
    const int charSize = encodingType->fixedCharacterSize;
    
    /* -------------------------------------------------------------ha--- */

    API_TRACE(API_TR_ENTRY,"PA12_FN_SQLCOLUMNPRIVILEGES",0);
    API_TRACE(API_TR_HANDLE,"hstmt",&hstmt);
    API_TRACE_LEN(API_TR_ODBC_STRING,"szTableQualifier",
                  szTableQualifier, cbTableQualifier);
    API_TRACE(API_TR_SWORD,"cbTableQualifier",&cbTableQualifier);
    API_TRACE_LEN(API_TR_ODBC_STRING,"szTableOwner",
                  szTableOwner, cbTableOwner);
    API_TRACE(API_TR_SWORD,"cbTableOwner",&cbTableOwner);
    API_TRACE_LEN(API_TR_ODBC_STRING,"szTableName",
                  szTableName, cbTableName);
    API_TRACE(API_TR_SWORD,"cbTableName",&cbTableName);
    API_TRACE_LEN(API_TR_ODBC_STRING,"szColumnName",
                  szColumnName, cbColumnName);
    API_TRACE(API_TR_SWORD,"cbColumnName",&cbColumnName);

    LV.cpriv_qualifier = LV.cpriv_owner = LV.cpriv_table = LV.cpriv_column = NULL;

    if (cbTableQualifier != SQL_NTS)
      cbTableQualifier *= charSize;
    if (cbTableOwner != SQL_NTS)
      cbTableOwner *= charSize;
    if (cbTableName != SQL_NTS)
      cbTableName *= charSize;
    if (cbColumnName != SQL_NTS)
      cbColumnName *= charSize;

    /* ------------------------------------------------------------------ */
    api_retcode = apmstfc(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, 
                          SQL_API_SQLCOLUMNPRIVILEGES);
    if (api_retcode != API_OK) {
        retcode = SQL_INVALID_HANDLE;
    }
    /* ------------------------------------------------------------------ */
    else {
        if (pa60VerifyStmt( hstmt ) != API_OK) {        /* hstmt valid ? */
            retcode = SQL_INVALID_HANDLE;
        }
        else {
            apmlocp(&hstmt, &stmt_block_ptr, &hdbc, &dbc_block_ptr, 
                    &henv, &env_block_ptr);
            state = stmt_block_ptr->state;
            /* determine which ODBC version the application wants */
            if (!pa10GetODBCVersion( SQL_HANDLE_STMT, hstmt,
                                     &odbcVersion )) {
                retcode = SQL_ERROR;
                /* general error */
                pa60PutError( hstmt, API_ODBC_S1000, NULL);
                goto exit_1_lock;                    
            }; /* if */
            tableprefix = pa12_getTablePrefix (dbc_block_ptr);
            /* ODBC version 3.0 defines different column-names */
            if (odbcVersion == SQL_OV_ODBC3) {
                selectColumnPrivileges = (char *) alloca (strlen(PA12SELECTCOLUMNPRIVILEGES_OV3) + 30);
                sprintf (selectColumnPrivileges, PA12SELECTCOLUMNPRIVILEGES_OV3, tableprefix);
            } else { /* version < 3.x */
                selectColumnPrivileges = (char *) alloca (strlen(PA12SELECTCOLUMNPRIVILEGES) + 30);
                sprintf (selectColumnPrivileges, PA12SELECTCOLUMNPRIVILEGES, tableprefix);
            }; /* else */
            PA09ENTERASYNCFUNCTION(dbc_block_ptr, stmt_block_ptr);
            if (pa09IsAsync(stmt_block_ptr, dbc_block_ptr, NULL)) {
                if (pa09IsConnectionBusy(stmt_block_ptr, dbc_block_ptr)) {
                    retcode = SQL_ERROR;
                    pa60PutError( hstmt,
                                  API_ODBC_S1010, NULL);
                    goto exit_1_lock;
                }	
                if (state == API_STMT_POSITIONED) {
                    /* function seqeunces error */
                    retcode = SQL_ERROR;            /* SQLSTATE: 24000 */
                    pa60PutError( hstmt, 
                                  API_ODBC_24000, NULL);
                    goto exit_1_lock;
                }

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

                /* create strings  */
                LV.cpriv_qualifier = 
                  pr05IfCom_String_NewDynString (charSize * ((API_TABLE_NAME_MAX*2)+1), encodingType);
                LV.cpriv_owner     = 
                  pr05IfCom_String_NewDynString (charSize * ((API_USERID_MAX_LEN*2)+1), encodingType);
                LV.cpriv_table     = 
                  pr05IfCom_String_NewDynString (charSize * ((API_TABLE_NAME_MAX*2)+1), encodingType);
                LV.cpriv_column    = 
                  pr05IfCom_String_NewDynString (charSize * ((API_TABLE_NAME_MAX*2)+1), encodingType);

                if (LV.cpriv_qualifier == NULL || LV.cpriv_owner == NULL || 
                    LV.cpriv_table     == NULL || LV.cpriv_column == NULL) {
                  retcode = SQL_ERROR;            
                  pa60PutError( hstmt, API_ODBC_S1001, NULL);
                  goto exit_1_lock;
                }
                
                /* ***** szTableQualifier */
                if (szTableQualifier == NULL) {
/*                     API_STRCPY ((UCHAR*)LV.cpriv_qualifier->rawString, (UCHAR*)""); */
                }
                else {
                    api_retcode = pa80ODBCtoTpr05 (LV.cpriv_qualifier,
                                                   encodingType,
                                                   szTableQualifier,
                                                   cbTableQualifier);
                    if (api_retcode != API_OK) {
                        retcode = SQL_ERROR;
                        pa60PutError( hstmt, API_ODBC_S1090, NULL);
                        goto exit_1_lock;
                    }
                }
                /* ***** szTableOwner */
                if (szTableOwner == NULL) {
                  if (pr05IfCom_String_strcpy (LV.cpriv_owner, dbc_block_ptr->userid) != ok_epr05)
                    api_retcode = API_NOT_OK;
                }
                else {
                    api_retcode = pa80ODBCtoTpr05 (LV.cpriv_owner,
                                                   encodingType,
                                                   szTableOwner,
                                                   cbTableOwner);
                    if (api_retcode != API_OK) {
                        retcode = SQL_ERROR;
                        pa60PutError( hstmt, API_ODBC_S1090, NULL);
                        goto exit_1_lock;
                    }
                }
                /* ***** szTableName */
                if (szTableName == NULL) {
/*                     API_STRCPY ((UCHAR*)LV.cpriv_table->rawString, (UCHAR*)""); */
                }
                else {
                    api_retcode = pa80ODBCtoTpr05 (LV.cpriv_table,
                                                   encodingType,
                                                   szTableName,
                                                   cbTableName);
                    if (api_retcode != API_OK) {
                        retcode = SQL_ERROR;
                        pa60PutError( hstmt, API_ODBC_S1090, NULL);
                        goto exit_1_lock;
                    }
                }
                /* ***** szColumnName */
                if (szColumnName == NULL) {
                    pr05IfCom_String_strcatP (LV.cpriv_column, "%", 
                                              (tsp00_Uint4) API_STRLEN ("%"),
                                              sp77encodingAscii);
                }
                else {
                    api_retcode = pa80ODBCtoTpr05 (LV.cpriv_column,
                                                   encodingType,
                                                   szColumnName,
                                                   cbColumnName);
                    if (api_retcode != API_OK) {
                        retcode = SQL_ERROR;
                        pa60PutError( hstmt, API_ODBC_S1090, NULL);
                        goto exit_1_lock;
                    }
                }
  
                stmt_block_ptr -> dbc_special.special =
                    API_SPEC_SQLCOLUMNPRIVILEGES;
                if (pa09IsAsync(stmt_block_ptr, dbc_block_ptr, &retcode)) {
                    /* store the local stack to restore get the values in
                     * async call */
                    pa09SaveStack( &stmt_block_ptr->async.hLocalValues,
                                   &LV, sizeof(LV));
                    pa09PrepareAsyncCall( hstmt, SQL_API_SQLCOLUMNPRIVILEGES);     
                }
            } /* async exec */
            if (pa09IsAsync(stmt_block_ptr, dbc_block_ptr, &retcode)) {
                pa09ExecuteAsyncCall(hstmt);
                goto exit_async;
            }
            else {
                /* restore the stack values from async caller */
                if (stmt_block_ptr->async.hLocalValues) {
                    pa09RestoreStack(stmt_block_ptr->async.hLocalValues, &LV);
                    apdfree(stmt_block_ptr->async.hLocalValues);
                    stmt_block_ptr->async.hLocalValues = API_NULL_HANDLE;
                }
            }

            /* --------------------------------------------------------*/
            /* SQLColumnPrivileges */
            sp77sprintfUnicode (encodingType,
                                sql_string, sizeof (sql_string),
                                selectColumnPrivileges,
                                LV.cpriv_owner->encodingType,
                                LV.cpriv_owner->cbLen,
                                LV.cpriv_owner->rawString,
                                LV.cpriv_table->encodingType,
                                LV.cpriv_table->cbLen,
                                LV.cpriv_table->rawString,
                                LV.cpriv_column->encodingType,
                                LV.cpriv_column->cbLen,
                                LV.cpriv_column->rawString,
                                API_ESCAPE_CHAR);
            retcode = SQL_SUCCESS;

            /* ------------------------------------------------------- */
#ifdef _UNICODE_ODBC
            retcode = CALL_ODBC(SQLPrepareW, (hstmt, (SQLWCHAR*) sql_string, SQL_NTS));
#else
            retcode = CALL_ODBC(SQLPrepare, (hstmt, sql_string, SQL_NTS));
#endif
            if (retcode == SQL_SUCCESS) {
              retcode = CALL_ODBC(SQLExecute, (hstmt));
            }

        exit_1_lock:
            pr05IfCom_String_DeleteString (LV.cpriv_qualifier);
            pr05IfCom_String_DeleteString (LV.cpriv_owner);
            pr05IfCom_String_DeleteString (LV.cpriv_table);
            pr05IfCom_String_DeleteString (LV.cpriv_column);
        exit_async:
            PA09LEAVEASYNCFUNCTION();
        }  /* start processing */  
    } /* set odbc_funtion */ 
    API_TRACE(API_TR_EXIT,"PA12_FN_SQLCOLUMNPRIVILEGES",0);
    API_TRACE(API_TR_RETCODE,"retcode",&retcode);

    return(retcode);
} /* SQLColumnPrivileges */


#ifndef _UNICODE_ODBC
/* ODBC 3.0 */
ODBC_PROC( SQLBulkOperations, (SQLHSTMT    hstmt,
                               SQLSMALLINT operation),
          (hstmt, operation))
{
   RETCODE    retcode = SQL_ERROR;
   tpa60Stmt *stmt_block_ptr;
   
   API_TRACE( API_TR_ENTRY, PA12_FN_SQLBULKOPERATIONS, 0);
   API_TRACE( API_TR_UWORD, "operation", &operation);

    if (pa60VerifyStmt( hstmt ) != API_OK) { /* hstmt valid? */
        retcode = SQL_INVALID_HANDLE;
    } else {
        stmt_block_ptr = (tpa60Stmt*) hstmt;

        retcode = CALL_ODBC( SQLSetPos, ( hstmt, 0, SQL_ADD,
                                          SQL_LOCK_NO_CHANGE ));
        if (operation == SQL_ADD) {
        } else {
            retcode = SQL_ERROR;
            pa60PutError( hstmt, API_ODBC_HY092, NULL );
            /* Invalid attribute/option identifier */
        }; /* else */
    }; /* else */
    
    API_TRACE( API_TR_EXIT, PA12_FN_SQLBULKOPERATIONS, 0);
    API_TRACE( API_TR_RETCODE, "retcode", &retcode);

    return (retcode);
} /* SQLBulkOperations */


/* aus vpa11pc : !!!*/
#define SQL_OUTPRM_FACTOR 100

/* conversion : SQL_TYPE_DATE -> SQL_DATE, SQL_C_TYPE_DATE -> SQL_C_DATE
   etc. */
void convDateTime( SWORD *typePtr )
{
    SWORD factor = 1;
    SWORD type;
    
    if (typePtr == NULL) {
        return;
    }; /* if */
    
    if (*typePtr >= SQL_OUTPRM_FACTOR) {
        type = *typePtr / SQL_OUTPRM_FACTOR;
        factor = SQL_OUTPRM_FACTOR;
    } else {
        type = *typePtr;
    }; /* else */
    
    switch (type) {
        /* Note: The corrosponding C-types have the same values.
         * So SQL_C_TYPE_... are also handled by this routine */
    case SQL_TYPE_DATE:
    case SQL_DATE:
        *typePtr = SQL_DATE*factor;
        break;
    case SQL_TYPE_TIME:
    case SQL_TIME:
        *typePtr = SQL_TIME*factor;
        break;
    case SQL_TYPE_TIMESTAMP:
    case SQL_TIMESTAMP:
        *typePtr = SQL_TIMESTAMP*factor;
        break;
    default:
        break;
    }; /* switch */
} /* convDateTime */

ODBC_PROC(SQLDescribeParam,
	  (SQLHSTMT      hstmt,
	   SQLUSMALLINT  ipar,
	   SQLSMALLINT   *pfSqlType,
	   SQLULEN       *pcbColDef,
	   SQLSMALLINT   *pibScale,
	   SQLSMALLINT   *pfNullable),
	  (hstmt, ipar, pfSqlType, pcbColDef, pibScale, pfNullable))
{
    RETCODE      retcode;
    API_RETCODE  api_retcode;

    UWORD        number_parms;
    UWORD        state;
    tpa60Stmt   *stmt_block_ptr;
    tpa50Env    *envPtr;
  
    API_TRACE(API_TR_ENTRY,"SQLDescribeParam",0);
    API_TRACE(API_TR_HANDLE,"hstmt",&hstmt);
    API_TRACE(API_TR_UWORD,"ipar",&ipar);
    API_TRACE(API_TR_PTR,"pfSqlType",&pfSqlType);
    API_TRACE(API_TR_PTR,"pcbColDef",&pcbColDef);
    API_TRACE(API_TR_PTR,"pibScale",&pibScale);
    API_TRACE(API_TR_PTR,"pfNullable",&pfNullable);

    api_retcode = apmstfc(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, 
                          SQL_API_SQLDESCRIBEPARAM);
    if (api_retcode != API_OK) {
        retcode = SQL_INVALID_HANDLE;
    }
    else {
        if (pa60VerifyStmt( hstmt ) != API_OK) { /* hstmt valid? */
            retcode = SQL_INVALID_HANDLE;
        }
        else {
            stmt_block_ptr = (tpa60Stmt FAR *) apdlock(hstmt);
            state = stmt_block_ptr->state;
            API_ASSERT_PTR(stmt_block_ptr);
            number_parms = stmt_block_ptr -> number_parms;
            API_TRACE(API_TR_UWORD,"number_parms",&number_parms);
            /* verify statement prepared */
            if (state == API_STMT_ALLOCATED) {
                retcode = SQL_ERROR;
                /* function sequence error */
                pa60PutError( hstmt, API_ODBC_S1010, NULL );
                goto exit_1_lock;
            }
            /* make sure the parameter */
            /* is valid             */
            if (ipar > number_parms || ipar == 0 ) {
                retcode = SQL_ERROR;
                /* invalid column number */
                pa60PutError( hstmt, API_ODBC_S1093, NULL); 
                goto exit_1_lock;
            }
    
            /* notice that only pointers  */
            /* are passed, no "handles",  */
            /* the storage management is  */
            /* done in this routine.      */
            api_retcode = pa20DescribeParam( &stmt_block_ptr->ipd,
                                             ipar,
                                             pfSqlType,
                                             pcbColDef,
                                             pibScale,
                                             pfNullable,
                                             stmt_block_ptr
                                             ->stmtopt.passthrough );
                                   
            switch (api_retcode) {
            case (API_OK):
                envPtr = (tpa50Env*) pa60GetParentEnv( stmt_block_ptr );
                if (envPtr != NULL) {
                    if (envPtr->odbc_version == SQL_OV_ODBC2) {
                        /* convert datetime codes */
                        convDateTime( pfSqlType );
                    }; /* if */
                    retcode = SQL_SUCCESS;
                } else {
                    API_ASSERT( 0 );
                    retcode = SQL_ERROR;
                }; /* else */
                break;
            case (API_NOT_OK):
                retcode = SQL_ERROR;
                break;
            }
            
        exit_1_lock:
            {};
        } /* end of if hstmt valid */
    }/* end of set odbc_function */
    
    pa06ConvDateTimeTypes( stmt_block_ptr, pfSqlType );
    
    if (pfSqlType != NULL) {
        API_TRACE(API_TR_SWORD,"*pfSqlType",pfSqlType);
    }
    if (pcbColDef != NULL) {
        API_TRACE(API_TR_UDWORD,"*pcbColDef",pcbColDef);
    }
    if (pibScale != NULL) {
        API_TRACE(API_TR_SWORD,"*pibScale",pibScale);
    }
    if (pfNullable != NULL) {
        API_TRACE(API_TR_SWORD,"*pfNullable",pfNullable);
    }
    
    API_TRACE(API_TR_EXIT,"SQLDescribeParam",0);
    API_TRACE(API_TR_RETCODE,"retcode",&retcode);

    return(retcode);
} /* SQLDescribeParam */

ODBC_PROC(SQLExtendedFetch,
	  (SQLHSTMT        hstmt,
	   SQLUSMALLINT    fFetchType,
	   SQLROWOFFSET    irow,
	   SQLROWSETSIZE  *pcrow,
	   SQLUSMALLINT   *rgfRowStatus),
	  (hstmt, fFetchType, irow, pcrow, rgfRowStatus))
{
    RETCODE retcode;
    RETCODE rowret = SQL_NO_DATA_FOUND;
    API_RETCODE api_retcode;
    UWORD state;
    tpa62DBCSpecial *dbc_special_ptr;
    UWORD number_cols;
    tpa60Stmt FAR * stmt_block_ptr;
    sqldatype FAR * sqlda_ptr;
    API_HANDLE sqlda_handle;
    API_HANDLE tuple_handle;
    UCHAR FAR * tuple_ptr;
    UDWORD tuple_size;
    SQLHDBC hdbc;
    tpa40DBC FAR * dbc_block_ptr;
    SQLHENV henv;
    tpa50Env FAR * env_block_ptr;
    tpa61StmtAttr FAR * stmtopt_ptr;
    UDWORD row;
    api_row_status * rowstat_ptr=NULL;
    UWORD FAR *rowstatus;
    UDWORD rowset_size;
    UDWORD previous_rowset_size;
    UDWORD keyset_size;
    UDWORD cursor_type;
    UDWORD concurrency;
    SQLROWOFFSET fetch_pos;
    SDWORD number_rows;
    SQLROWOFFSET max_rows;
    UWORD fetchtype;
    SQLROWOFFSET pos, fetchpos;
    SDWORD direction;
    UDWORD crow;
    UCHAR status;
    tsp00_Bool fetchBefore = false;
        
    tpa20Desc       *local_ard = NULL;
    tpa20Desc       *local_ird = NULL;
    tpa20Desc       *ard_ptr   = NULL;
    tpa20Desc       *ird_ptr   = NULL;
    tpa21DescRecord *ard_rec_ptr = NULL;
    tpa21DescRecord *ird_rec_ptr = NULL;

    API_TRACE(API_TR_ENTRY,PA12_FN_SQLEXTENDEDFETCH,0);
    API_TRACE(API_TR_HANDLE,"hstmt",&hstmt);
    API_TRACE(API_TR_FETCHTYPE, "fFetchType", &fFetchType);
    API_TRACE(API_TR_SDWORD,"irow",&irow);
    API_TRACE(API_TR_PTR,"pcrow",&pcrow);
    API_TRACE(API_TR_PTR,"rgfRowStatus",&rgfRowStatus);

    if (pa60VerifyStmt( hstmt ) != API_OK) { /* hstmt valid? */
        retcode = SQL_INVALID_HANDLE;
        goto no_lock_exit;
    }; /* if */
    apmlocp( &hstmt, &stmt_block_ptr, &hdbc, &dbc_block_ptr,
             &henv, &env_block_ptr);
    api_retcode = apmstfc( SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, 
                           SQL_API_SQLEXTENDEDFETCH);
    if (api_retcode != API_OK) {
        retcode = pa06stfcerror(api_retcode, hstmt);
        goto no_lock_exit;
    }      

    state = stmt_block_ptr->state;    
    if (state != API_STMT_POSITIONED && state != API_STMT_EXECUTED) {
        retcode = SQL_ERROR;
        pa60PutError( hstmt, API_ODBC_S1010, NULL);
        goto no_lock_exit;
    }
  
    rowset_size = stmt_block_ptr->ardPtr->header.arraySize;
    rowstatus   = rgfRowStatus;
    stmt_block_ptr->ardPtr->header.arrayStatusPtr = rgfRowStatus;
    
    if (rgfRowStatus != NULL) {
        pa12_InitRowStatusArray( rgfRowStatus, rowset_size );
    }; /* if */
    if (stmt_block_ptr->row_not_found) {
        retcode = SQL_NO_DATA_FOUND;
        goto no_lock_exit;
    }; /* if */
    
    if ( stmt_block_ptr->stmtopt.cursor_type == SQL_CURSOR_DYNAMIC
         || (stmt_block_ptr->stmtopt.cursor_type == SQL_CURSOR_FORWARD_ONLY
             && rowset_size == 1)
         || (stmt_block_ptr->stmtopt.cursor_type == SQL_CURSOR_KEYSET_DRIVEN
             && stmt_block_ptr->stmtopt.optimize_cursor == SQL_TRUE)) {
        retcode = pa12_DynamicCursorExtendedFetch( hstmt,
                                                   fFetchType,
                                                   irow,
                                                   pcrow,
                                                   rgfRowStatus );
        goto write_trace;
    }; /* if */

    PA09ENTERASYNCFUNCTION(dbc_block_ptr, stmt_block_ptr);
    /* Check Input Values */
    stmtopt_ptr = &stmt_block_ptr->stmtopt;
    previous_rowset_size = stmt_block_ptr->ardPtr->header.previousArraySize;
    keyset_size = stmtopt_ptr->keyset_size;
    cursor_type = stmtopt_ptr->cursor_type;
    concurrency = stmtopt_ptr->concurrency;
    fetch_pos   = stmt_block_ptr->fetch_pos;
    number_cols = stmt_block_ptr->number_cols;
    max_rows = stmtopt_ptr->max_select;  
    number_rows = stmt_block_ptr->number_rows;
    API_TRACE(API_TR_UDWORD, "number_rows", &number_rows);
    fetchtype = fFetchType;
    API_TRACE(API_TR_UWORD,  "number_cols", &number_cols);
    API_TRACE(API_TR_UDWORD, "keyset_size", &keyset_size);
    API_TRACE(API_TR_UDWORD, "rowset_size", &rowset_size);
    API_TRACE(API_TR_UDWORD, "cursor_type", &cursor_type);
    API_TRACE(API_TR_UDWORD, "concurrency", &concurrency);
    API_TRACE(API_TR_SDWORD, "max_rows", &max_rows);
    API_TRACE(API_TR_UDWORD, "fetch_pos", &fetch_pos);
    API_TRACE(API_TR_UDWORD, "rowset_pos", &stmt_block_ptr->rowset_pos);
    API_TRACE(API_TR_HANDLE, "hrowstat", &stmt_block_ptr->hrowstat);
    if (stmt_block_ptr->hrowstat != API_NULL_HANDLE) {
        rowstat_ptr = (api_row_status*) stmt_block_ptr->hrowstat;
        API_TRACE(API_TR_UDWORD, "numentries", &rowstat_ptr->numentries);
    }
    switch (fetchtype) {
    case (SQL_FETCH_NEXT): {
        break;
    }
    case (SQL_FETCH_FIRST): {}     
    case (SQL_FETCH_BOOKMARK): {}
    case (SQL_FETCH_ABSOLUTE): {}
    case (SQL_FETCH_PRIOR): {}
    case (SQL_FETCH_LAST): {}
    case (SQL_FETCH_RELATIVE): {
        if (cursor_type == SQL_CURSOR_FORWARD_ONLY) {
            pa60PutError( hstmt, API_ODBC_S1106, NULL); /* Fetch type out of
                                                         * range */
            retcode = SQL_ERROR;
            goto lock3_exit;     
        }
        break;
    }
    default: {
        pa60PutError( hstmt, API_ODBC_S1106, NULL);
        retcode = SQL_ERROR;
        goto lock3_exit;     
        break;
    }
    }
    if (cursor_type == SQL_CURSOR_KEYSET_DRIVEN) {
        if (keyset_size > 0 && keyset_size < rowset_size) {
            pa60PutError( hstmt, API_ODBC_S1107, NULL);
            retcode = SQL_ERROR;
            goto lock3_exit;     
        }
    }
  
    dbc_special_ptr = &stmt_block_ptr -> dbc_special;
    sqlda_handle = stmt_block_ptr ->output_sqlda_handle;
    sqlda_ptr = (sqldatype*) sqlda_handle;
    API_ASSERT_PTR(sqlda_ptr);
    stmt_block_ptr->output_sqlda_ptr = sqlda_ptr;

    tuple_handle = stmt_block_ptr -> col_handle;
    tuple_size = stmt_block_ptr -> col_size;
    if ((tuple_handle == 0 && tuple_size == 0) || number_cols == 0) {
        retcode = SQL_ERROR;
        pa60PutError( hstmt, API_ODBC_24000, NULL);
        goto lock2_exit;
    }
    tuple_ptr = (UCHAR FAR *) apdlock(tuple_handle);
    API_ASSERT_PTR(tuple_ptr);

    /* update the resulttable to get the changes */
    if ( cursor_type == SQL_CURSOR_DYNAMIC
         && concurrency != SQL_CONCUR_READ_ONLY) {
        /* SQLFreeStmt frees the hrowstat array */
        retcode = CALL_ODBC( SQLFreeStmt, (hstmt, SQL_CLOSE));
        if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
            retcode = CALL_ODBC(SQLExecute, (hstmt));
            if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
                state = API_STMT_EXECUTED;
            }
            else
                goto lock1_exit;		 
        }
        else 
            goto lock1_exit;		 	      
    }; /* if */
  
    number_rows = stmt_block_ptr->number_rows;
    API_TRACE(API_TR_UDWORD, "number_rows", &number_rows);
    max_rows = (max_rows > 0) ?
        ((max_rows > number_rows) ? number_rows : max_rows) : 
            number_rows /*(number_rows > 0) ? number_rows : (LONG_MAX-1)*/;
    API_TRACE(API_TR_SDWORD, "max_rows", &max_rows);
     
    /* calculate the fetch position for the new fetch */
    /* depending from fetch_pos of the last call */
    /* fetch_pos points to the first row of the last result set */
    /* first jump to the first row of the row set */
    fetchtype = SQL_FETCH_ABSOLUTE;
    switch (fFetchType) {
    case SQL_FETCH_BOOKMARK : {
        pos = irow;
        break;
    }
    case SQL_FETCH_NEXT : {
        if (fetch_pos == 0)
            pos = 1;
        else	   
            pos = fetch_pos + previous_rowset_size;
        break;
    }
    case SQL_FETCH_FIRST : {
        pos = 1;
        break;
    }
    case SQL_FETCH_LAST : {	
        pos = max_rows-rowset_size+1;
        if (pos <= 0)
            pos = 1;
        break;
    }
    case SQL_FETCH_PRIOR : {
        pos = fetch_pos - rowset_size;
        if (fetch_pos <= 0 && pos <= 0)
            pos = 0;
        else
            if (fetch_pos > 1 && pos <= 0) {
                fetchBefore = true;
                pos = 1;
            };
        break;
    }
    case SQL_FETCH_RELATIVE : {
        if (fetch_pos == 0 && irow < 0)
            pos = 1;
        else
            pos = fetch_pos + irow;
        if (fetch_pos > 0 && pos < 0)
            pos = 0;
        break;
    }
    case SQL_FETCH_ABSOLUTE :{
        if (irow < 0)
            pos = max_rows + irow + 1;
        else
            pos = irow;
        break;
    }
    default: {
        pos = 0;
        break;
    }
    }; /* switch */
    if (pos <= 0) {
        retcode = SQL_NO_DATA_FOUND;     
        stmt_block_ptr->fetch_pos = 0;
        stmt_block_ptr->rowset_pos = 0;
        goto lock1_exit;
    }
    API_TRACE(API_TR_SDWORD, "pos", &pos);
    if (rowset_size > 1 ) {
        /* PTS 1116229: if a stmt was unbound, count is zero.
           reuse number_cols (determined in SQLPrepare) */
        stmt_block_ptr->ardPtr->count = stmt_block_ptr->number_cols;
        stmt_block_ptr->ird.count     = stmt_block_ptr->number_cols;

        local_ard = pa20CloneDesc( stmt_block_ptr->ardPtr );
        local_ird = pa20CloneDesc( &stmt_block_ptr->ird );
        if ( local_ard == NULL || local_ird == NULL) {
            retcode = SQL_ERROR;
            pa60PutError( hstmt, API_ODBC_S1001, NULL);
            goto lock1_exit;
        } else {
            ard_ptr = local_ard;
            ird_ptr = local_ird;
        }; /* else */
    } else {
        ard_ptr = stmt_block_ptr->ardPtr;
        ird_ptr = &stmt_block_ptr->ird;
    }; /* else */
    
    /* calculate startposition */
    /* in case of not forward only cursor */
    /* fetch backward thrue the resulttable so that the cursor */
    /* is placed at the beginnig of the rowset */
    /* in case of forward only go forward thrue the resulttable */
    /* fetching backward would be cost some time in case of */
    /* not completly generated resultttable */
     
    direction = 1;
    fetchpos  = pos;
    fetchtype = SQL_FETCH_ABSOLUTE;

    API_TRACE(API_TR_UDWORD, "fetchpos", &fetchpos);
    API_TRACE(API_TR_SDWORD, "max_rows", &max_rows);
    API_TRACE(API_TR_UDWORD, "direction", &direction);
    /* start the FETCH */
    crow = 0;
    for (row=1; row <= rowset_size;) {          
        if (max_rows > 0L && fetchpos > max_rows) {
            if(rowstatus) {
                *rowstatus = SQL_ROW_NOROW;
                rowstatus+=direction;
            }
            if (rowset_size > 1) {
                pa20SetAttrAddr( NULL,
                                 ard_ptr,
                                 ird_ptr,
                                 number_cols,
                                 ard_ptr->header.bindType,
                                 direction, 0);
                fetchpos+=direction;
            }; /* if */
            row++;
            continue;
        }
        api_retcode = pa20SetAttr( sqlda_ptr,
                                   ird_ptr,
                                   number_cols,
                                   tuple_ptr,
                                   tuple_size,
                                   CPR_INFO_OUTPUT );
        API_TRACE(API_TR_UDWORD, "fetchpos", &fetchpos);
        status = (rowstat_ptr) ?
            rowstat_ptr->entry[fetchpos-1].status : (UCHAR)SQL_ROW_SUCCESS;
        switch(status) {
        case (SQL_ROW_SUCCESS) : {}
        case (SQL_ROW_UPDATED) : {
            if ( cursor_type == SQL_CURSOR_KEYSET_DRIVEN
                 && concurrency != SQL_CONCUR_READ_ONLY) {
                retcode = pa12FetchOneRow( env_block_ptr,
                                           dbc_block_ptr,
                                           stmt_block_ptr, 
                                           NULL,
                                           NULL,
                                           fetchtype,
                                           fetchpos,
                                           rowstatus,
                                           hstmt);	   
                if ( retcode == SQL_SUCCESS
                     || retcode == SQL_SUCCESS_WITH_INFO) {
                    RETCODE ret;
                    ret = pa12SelectOneRow( hstmt, ard_ptr, ird_ptr );
                    if (ret == SQL_NO_DATA_FOUND) {
                        if (rowstatus)
                            *rowstatus = SQL_ROW_DELETED;
                    }
                }
            }
            else {
                retcode = pa12FetchOneRow( env_block_ptr,
                                           dbc_block_ptr,
                                           stmt_block_ptr, 
                                           ard_ptr,
                                           ird_ptr,
                                           fetchtype,
                                           fetchpos,
                                           rowstatus,
                                           hstmt );
            }
            break;
        }
        case (SQL_ROW_DELETED) : {
            UWORD i;
            if ( cursor_type == SQL_CURSOR_KEYSET_DRIVEN
                 && concurrency != SQL_CONCUR_READ_ONLY) {
                retcode = pa12FetchOneRow( env_block_ptr,
                                           dbc_block_ptr,
                                           stmt_block_ptr, 
                                           NULL,
                                           NULL,
                                           fetchtype,
                                           fetchpos,
                                           rowstatus,
                                           hstmt );
                if ( retcode == SQL_SUCCESS
                     || retcode == SQL_SUCCESS_WITH_INFO) {
                    RETCODE ret;
                    ret = pa12SelectOneRow( hstmt, ard_ptr, ird_ptr );
                    if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
                        if (rowstatus)
                            *rowstatus = SQL_ROW_SUCCESS;
                        if (rowstat_ptr)
                            rowstat_ptr->entry[fetchpos-1].status =
                                SQL_ROW_SUCCESS;
                    }
                }
            }
            else {
                retcode = pa12FetchOneRow( env_block_ptr,
                                           dbc_block_ptr,
                                           stmt_block_ptr, 
                                           ard_ptr,
                                           ird_ptr,
                                           fetchtype,
                                           fetchpos,
                                           rowstatus,
                                           hstmt );
            }
            if (rowstatus)
                *rowstatus = SQL_ROW_DELETED;
            for ( i=1; i <= number_cols; i++) {
                SDWORD null_data=SQL_NULL_DATA;

                ard_rec_ptr = pa20GetRecord( ard_ptr, i );
                API_ASSERT_PTR( ard_ptr );
                ird_rec_ptr = pa20GetRecord( ird_ptr, i );
                API_ASSERT_PTR( ird_ptr );
                /* http://pts:1080/webpts?wptsdetail=yes&ErrorType=0&ErrorID=1122725 */
                if (ard_rec_ptr->indicatorPtr)
                    API_MEMCPY( PA20_INDICATOR_PTR( ard_rec_ptr ),
                                &null_data,
                                sizeof(ard_rec_ptr->octetLengthPtr) );
                if (ard_rec_ptr->octetLengthPtr)
                    API_MEMCPY( PA20_OCTETLENGTH_PTR( ard_rec_ptr ),
                                &null_data,
                                sizeof(ard_rec_ptr->octetLengthPtr) );
                if (ird_rec_ptr->optimizeNullPtr)
                    API_MEMCPY( ird_rec_ptr->optimizeNullPtr,
                                &null_data,
                                sizeof( ird_rec_ptr->optimizeNullPtr) );
            }; /* for */
            break;
        }
        }; /* switch( status) */
        row++;
        if (rowset_size > 1) { /* speed optimisation for rowsetsize == 1 */
            pa20SetAttrAddr( NULL,
                             ard_ptr,
                             ird_ptr,
                             number_cols,
                             ard_ptr->header.bindType,
                             direction,
                             0 );
            if (rowstatus)
                rowstatus+=direction;
        }
        pa12ProcessRetcode(retcode, &rowret);  
        state = API_STMT_POSITIONED;     
        if (direction > 0) {
            if (retcode == SQL_NO_DATA_FOUND) {
                /* only with forward only cursor we can have max_rows -1 */
                if (max_rows == -1) 
                    max_rows = fetchpos;
                if(stmt_block_ptr->number_rows == -1)
                  stmt_block_ptr->number_rows = (SDWORD) max_rows;    /* ia64 cast */
                API_TRACE(API_TR_UDWORD, "max_rows", &max_rows);
            }
            fetchtype = SQL_FETCH_NEXT;
        }
        else
            fetchtype = SQL_FETCH_PRIOR;
        fetchpos+=direction;
        API_TRACE(API_TR_UWORD, "rowstatus", rowstatus);
        if (retcode != SQL_ERROR && retcode != SQL_NO_DATA_FOUND) {
            crow++;
            continue;	
        }
    }
  
    API_TRACE(API_TR_SDWORD,"pos", &pos);
    if ( max_rows > 0 && pos > max_rows) {
        retcode = SQL_NO_DATA_FOUND;
    }
     
    if (retcode == SQL_NO_DATA_FOUND) {
        stmt_block_ptr->fetch_pos = max_rows+1;     
        stmt_block_ptr->rowset_pos = rowset_size+1;
    }; /* if */
    
    if (rowret == SQL_SUCCESS || rowret == SQL_SUCCESS_WITH_INFO) {
        /* Fetch the last row again. Use the original attr_block so
         * SQLGetData works */
        if (rowset_size > 1 ) { /* speed optimisation for rowsetsize == 1 */
            api_retcode = pa20SetAttr( sqlda_ptr,
                                       &stmt_block_ptr->ird,
                                       number_cols,
                                       tuple_ptr,
                                       tuple_size,
                                       CPR_INFO_OUTPUT );
            if ( cursor_type == SQL_CURSOR_KEYSET_DRIVEN
                 && concurrency != SQL_CONCUR_READ_ONLY) {
                /* get actual data with select direct after positioning
                 * PTS 1104974 */
                pa12FetchOneRow( env_block_ptr,
                                 dbc_block_ptr,
                                 stmt_block_ptr, 
                                 NULL,
                                 NULL,
                                 SQL_FETCH_ABSOLUTE,
                                 pos,
                                 NULL,
                                 hstmt );
                if ( retcode == SQL_SUCCESS
                     || retcode == SQL_SUCCESS_WITH_INFO) {
                    RETCODE ret;
                    ret = pa12SelectOneRow( hstmt,
                                            stmt_block_ptr->ardPtr,
                                            &stmt_block_ptr->ird  );
                } else {
                    pa12FetchOneRow( env_block_ptr,
                                     dbc_block_ptr,
                                     stmt_block_ptr, 
                                     stmt_block_ptr->ardPtr,
                                     &stmt_block_ptr->ird,
                                     SQL_FETCH_ABSOLUTE,
                                     pos,
                                     NULL,
                                     hstmt );
                }; /* else */
            };
        }; /* if */
        stmt_block_ptr->fetch_pos  = pos;
        stmt_block_ptr->rowset_pos = 1;
        retcode = rowret;
        stmt_block_ptr -> state = API_STMT_POSITIONED;
        if (pcrow)
            *pcrow = crow;
        if (stmt_block_ptr->ird.header.rowsProcessedPtr != NULL) {
            *stmt_block_ptr->ird.header.rowsProcessedPtr = (SQLUINTEGER) crow;
        };
    }; /* if */
        
    if (local_ard != NULL) {
        pa20FreeCopiedDesc( local_ard );
    }
    if (local_ird != NULL) {
        pa20FreeCopiedDesc( local_ird );
    }

    if (retcode == SQL_SUCCESS && fetchBefore) {
        retcode = SQL_SUCCESS_WITH_INFO;
        /* attempt to fetch before the result set returned the first
         * rowset */
        pa60PutError( hstmt, API_ODBC_01S06, NULL );
    };
lock1_exit:

lock2_exit:
lock3_exit:  
    PA09LEAVEASYNCFUNCTION();

no_lock_exit:
    if (retcode != SQL_INVALID_HANDLE) {
        /* use actual array size for fetch next in next call
         * of SQLExtendedFetch */
        stmt_block_ptr->ardPtr->header.previousArraySize =
            stmt_block_ptr->ardPtr->header.arraySize;
        stmt_block_ptr->fetchRetcode = retcode;
    };

write_trace:
    API_TRACE(API_TR_EXIT, PA12_FN_SQLEXTENDEDFETCH, 0);
    API_TRACE(API_TR_RETCODE,"retcode",&retcode);

    return(retcode);
} /* SQLExtendedFetch */


VOID pa12_InitRowStatusArray( UWORD *rgfRowStatus, UDWORD rowset_size )
{
    UDWORD i;
    for (i = 0; i<rowset_size; i++) {
        rgfRowStatus[i] = SQL_ROW_NOROW;
    }; /* for */
} /* pa12_InitRowStatusArray */


RETCODE pa12_DynamicCursorExtendedFetch( SQLHSTMT       hstmt,
                                         UWORD          fFetchType,
                                         SQLROWOFFSET   irow,
                                         SQLROWSETSIZE *pcrow,
                                         UWORD         *rgfRowStatus )
{
    RETCODE         retcode;
    RETCODE         rowret = SQL_NO_DATA_FOUND;
    UWORD           sqlState; /* ODBC error state */
    UWORD           state;    /* state of stmt */
    API_RETCODE     api_retcode;
    tpa60Stmt      *stmt_block_ptr;
    UCHAR          *tuple_ptr;
    SQLHDBC         hdbc;
    tpa40DBC FAR   *dbc_block_ptr;
    SQLHENV         henv;
    tpa50Env FAR   *env_block_ptr;
    tpa61StmtAttr  *stmtopt_ptr;
    UDWORD          row;
    api_row_status *rowstat_ptr=NULL;
    UWORD          *rowstatus;
    UWORD           fetchtype;
    SQLROWOFFSET    pos;
    SWORD           direction;
    UDWORD          crow;
    UDWORD          rowset_size; 

    tpa20Desc       *local_ard = NULL;
    tpa20Desc       *local_ird = NULL;
    tpa20Desc       *ard_ptr   = NULL;
    tpa20Desc       *ird_ptr   = NULL;
    
    API_TRACE(API_TR_ENTRY, "pa12_DynamicCursorExtendedFetch",0);
    API_TRACE(API_TR_HANDLE,"hstmt",&hstmt);
    API_TRACE(API_TR_FETCHTYPE, "fFetchType", &fFetchType);
    API_TRACE(API_TR_SDWORD,"irow",&irow);
    API_TRACE(API_TR_PTR,"pcrow",&pcrow);
    API_TRACE(API_TR_PTR,"rgfRowStatus",&rgfRowStatus);

    /* Error-Checking
    ******************/
    api_retcode = apmstfc( SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, 
                           SQL_API_SQLEXTENDEDFETCH);
    if (api_retcode != API_OK) {
        retcode = SQL_INVALID_HANDLE;
        goto no_lock_exit;
    }      
    if (pa60VerifyStmt( hstmt ) != API_OK) { /* hstmt valid? */
        retcode = pa06stfcerror(api_retcode, hstmt);
        goto no_lock_exit;
    }; /* if */
    apmlocp( &hstmt, &stmt_block_ptr, &hdbc, &dbc_block_ptr,
             &henv, &env_block_ptr);
    state = stmt_block_ptr->state;
    if ( state != API_STMT_POSITIONED && state != API_STMT_EXECUTED) {
        retcode = SQL_ERROR;
        pa60PutError( hstmt, API_ODBC_S1010, NULL );
        goto no_lock_exit;
    }
  
    PA09ENTERASYNCFUNCTION( dbc_block_ptr, stmt_block_ptr );
    /* Check Input Values */
    stmtopt_ptr = &stmt_block_ptr->stmtopt;
    rowset_size = stmt_block_ptr->ardPtr->header.arraySize;
    API_TRACE(API_TR_UDWORD, "number_rows", &stmt_block_ptr->number_rows);
    API_TRACE(API_TR_UWORD,  "number_cols", &stmt_block_ptr->number_cols);
    API_TRACE(API_TR_UDWORD, "keyset_size", &stmtopt_ptr->keyset_size);
    API_TRACE(API_TR_UDWORD, "rowset_size", &rowset_size);
    API_TRACE(API_TR_UDWORD, "cursor_type", &stmtopt_ptr->cursor_type);
    API_TRACE(API_TR_UDWORD, "concurrency", &stmtopt_ptr->concurrency);
    API_TRACE(API_TR_UDWORD, "max_rows", &stmtopt_ptr->max_select);
    API_TRACE(API_TR_UDWORD, "rowset_pos", &stmt_block_ptr->rowset_pos);
    API_TRACE(API_TR_HANDLE, "hrowstat", &stmt_block_ptr->hrowstat);
    
    if (stmt_block_ptr->hrowstat != API_NULL_HANDLE) {
        rowstat_ptr = (api_row_status*) apdlock(stmt_block_ptr->hrowstat);
        API_TRACE(API_TR_UDWORD, "numentries", &rowstat_ptr->numentries);
    }; /* if */

    if (pa12_FetchTypeIsValid( fFetchType,
                               stmt_block_ptr,
                               &sqlState ) != SQL_SUCCESS) {
        pa60PutError( hstmt, sqlState, NULL);
        retcode = SQL_ERROR;
        goto lock3_exit;
    }; /* if */
    
    rowstatus = rgfRowStatus;
    if (rgfRowStatus != NULL) {
        pa12_InitRowStatusArray( rgfRowStatus, rowset_size );
    }; /* if */
    stmt_block_ptr->ardPtr->header.arrayStatusPtr = rgfRowStatus;    
    tuple_ptr = (UCHAR*) apdlock( stmt_block_ptr->col_handle );
    API_ASSERT_PTR(tuple_ptr);
    stmt_block_ptr->output_sqlda_ptr =
        (sqldatype*) apdlock(stmt_block_ptr->output_sqlda_handle);
    API_ASSERT_PTR( stmt_block_ptr->output_sqlda_ptr );

    /* update the resulttable to get the changes */
    if ( stmtopt_ptr->cursor_type == SQL_CURSOR_DYNAMIC
         && stmtopt_ptr->concurrency != SQL_CONCUR_READ_ONLY) {
        retcode = pa12_UpdateResultTable( hstmt );
        if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO ) {
            goto lock1_exit;
        }; /* if */
    }; /* if */

    /* get fetch type and pos for the first row in the new rowset */
    retcode = pa12_GetFetchTypeAndPos( hstmt,
                                       fFetchType,
                                       irow,
                                       &fetchtype,
                                       &pos );

    if (retcode == SQL_NO_DATA_FOUND) {
        goto lock1_exit;
    }; /* if */

    /* use local descriptors for rowsets */
    API_TRACE(API_TR_SDWORD, "pos", &pos);
    if (rowset_size > 1 ) {
        local_ard = pa20CloneDesc( stmt_block_ptr->ardPtr );
        local_ird = pa20CloneDesc( &stmt_block_ptr->ird );
        if ( local_ard == NULL || local_ird == NULL) {
            retcode = SQL_ERROR;
            pa60PutError( hstmt, API_ODBC_S1001, NULL); /* Memory allocation
                                                         * failure */
            goto lock1_exit;
        } else {
            ard_ptr = local_ard;
            ird_ptr = local_ird;
        }; /* else */
    } else {
        ard_ptr = stmt_block_ptr->ardPtr;
        ird_ptr = &stmt_block_ptr->ird;
    }; /* else */
    
    /* start the FETCH */
    crow = 0;
    for (row=1; row <= rowset_size; row++) {
        api_retcode = pa20SetAttr( stmt_block_ptr->output_sqlda_ptr,
                                   ird_ptr,
                                   stmt_block_ptr->number_cols,
                                   tuple_ptr,
                                   stmt_block_ptr -> col_size,
                                   CPR_INFO_OUTPUT );
        if ( stmtopt_ptr->cursor_type == SQL_CURSOR_KEYSET_DRIVEN
             && stmtopt_ptr->concurrency != SQL_CONCUR_READ_ONLY
             && stmt_block_ptr->number_rows > 0 /* result-table is copied */ )
            {
                retcode = pa12FetchOneRow( env_block_ptr,
                                           dbc_block_ptr,
                                           stmt_block_ptr, 
                                           NULL,
                                           NULL,
                                           fetchtype,
                                           pos,
                                           rowstatus,
                                           hstmt);
                if ( retcode == SQL_SUCCESS
                     || retcode == SQL_SUCCESS_WITH_INFO) {
                    RETCODE ret;
                    ret = pa12SelectOneRow( hstmt, ard_ptr, ird_ptr );
                    if (ret == SQL_NO_DATA_FOUND) {
                        if (rowstatus)
                            *rowstatus = SQL_ROW_DELETED;
                    }
                }
            }
        else
            {
                retcode = pa12FetchOneRow( env_block_ptr,
                                           dbc_block_ptr,
                                           stmt_block_ptr, 
                                           ard_ptr,
                                           ird_ptr,
                                           fetchtype,
                                           pos,
                                           rowstatus,
                                           hstmt );
            }; /* else */
        if (rowset_size > 1 && retcode != SQL_NO_DATA_FOUND) {
            /* speed optimisation for rowsetsize == 1 */
            fetchtype = SQL_FETCH_NEXT;
            pa20SetAttrAddr( NULL,
                             ard_ptr,
                             ird_ptr,
                             stmt_block_ptr->number_cols,
                             ard_ptr->header.bindType,
                             1,
                             0 );
            if (rowstatus)
                rowstatus++;
        }; /* if */
        pa12ProcessRetcode( retcode, &rowret );
        state = API_STMT_POSITIONED;
        
        API_TRACE(API_TR_UWORD, "rowstatus", rowstatus);
        if (retcode != SQL_ERROR && retcode != SQL_NO_DATA_FOUND) {
            crow++;
            continue;
        } else if (retcode == SQL_NO_DATA_FOUND) {
            break;
        };
    }; /* for */

    /* determine if cursor stands before or after the result set */
    if (retcode == SQL_NO_DATA_FOUND) {
        /* stmt_block_ptr->fetch_pos = max_rows+1; */
        stmt_block_ptr->fetch_pos = 0; /* signed fetch pos is undefined */
        direction = pa12_GetDirection( fetchtype, pos );
        if ( direction == 1) {
            /* forward -> cursor stands after the result set */
            stmt_block_ptr->rowset_pos = rowset_size+1;
            stmt_block_ptr->fetch_pos  = 0;
        } else if (direction = -1) {
            /* cursor stands before result set */
            stmt_block_ptr->rowset_pos = 0;
            stmt_block_ptr->fetch_pos  = 0;
        }; /* else */
    } else {
        stmt_block_ptr->rowset_pos = 1; /* first row in rowset */
    }; /* else */        
    
    if (rowret == SQL_SUCCESS || rowret == SQL_SUCCESS_WITH_INFO) {
        /* if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { */
        /* Fetch the last row again. Use the original attr_block so
         * SQLGetData works */
        if ( crow > 1 ) {
            /* we fetched more than one row
             * now fetch first row of rowset with orig descriptors */
            pos = -((SDWORD) crow);
            if (retcode == SQL_SUCCESS) {
                pos++; /* cursor is positioned "inside" the resultable */
            }; /* if */
            api_retcode = pa20SetAttr( stmt_block_ptr->output_sqlda_ptr,
                                       &stmt_block_ptr->ird,
                                       stmt_block_ptr->number_cols,
                                       tuple_ptr,
                                       stmt_block_ptr -> col_size,
                                       CPR_INFO_OUTPUT );
            retcode = pa12FetchOneRow( env_block_ptr,
                                       dbc_block_ptr,
                                       stmt_block_ptr, 
                                       stmt_block_ptr->ardPtr,
                                       &stmt_block_ptr->ird,
                                       SQL_FETCH_RELATIVE,
                                       pos,
                                       NULL,
                                       hstmt );
            if (retcode != SQL_SUCCESS) {
                pa60PutError( hstmt, API_ODBC_S1000, NULL);
                goto lock1_exit;
            }; /* if */
        }; /* if */
        /* stmt_block_ptr->fetch_pos  = fetch_pos; */
        stmt_block_ptr -> state = API_STMT_POSITIONED;
        if (pcrow)
            *pcrow = crow; /* number of rows fetched */
        if (stmt_block_ptr->ird.header.rowsProcessedPtr != NULL) {
            *stmt_block_ptr->ird.header.rowsProcessedPtr = (SQLUINTEGER) crow;
        };
    }; /* if */
    retcode = rowret;
        
    if (local_ard != NULL) {
        pa20FreeCopiedDesc( local_ard );
    }
    if (local_ird != NULL) {
        pa20FreeCopiedDesc( local_ird );
    }
  
lock1_exit:

lock3_exit:  
    PA09LEAVEASYNCFUNCTION();

no_lock_exit:
    if (retcode != SQL_INVALID_HANDLE) {
        /* use actual array size for fetch next in next call of
         * SQLExtendedFetch */
        stmt_block_ptr->ardPtr->header.previousArraySize =
            stmt_block_ptr->ardPtr->header.arraySize;
        stmt_block_ptr->fetchRetcode = retcode;
    };

    API_TRACE(API_TR_EXIT, "pa12_DynamicCursorExtendedFetch", 0);
    API_TRACE(API_TR_RETCODE,"retcode",&retcode);

    return(retcode);
} /* pa12_DynamicCursorExtendedFetch */


RETCODE pa12_UpdateResultTable( SQLHSTMT hstmt )
{
    RETCODE    retcode = SQL_SUCCESS;
    UDWORD     tmp_rowset_pos;
    SQLROWOFFSET     tmp_fetch_pos;
    tpa60Stmt *stmt_block_ptr;
    SQLHDBC    hdbc;
    tpa40DBC  *dbc_block_ptr;
    SQLHENV    henv;
    tpa50Env  *env_block_ptr;
    UWORD     *rowstatus;
    
    apmlocp( &hstmt, &stmt_block_ptr, &hdbc, &dbc_block_ptr,
             &henv, &env_block_ptr);

    rowstatus = stmt_block_ptr->ardPtr->header.arrayStatusPtr;

    /* keep rowset-pos */
    tmp_rowset_pos = stmt_block_ptr->rowset_pos;
    tmp_fetch_pos = stmt_block_ptr->fetch_pos;
    /* SQLFreeStmt frees the hrowstat array */
    retcode = CALL_ODBC( SQLFreeStmt, (hstmt, SQL_CLOSE));
    if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
        retcode = CALL_ODBC(SQLExecute, (hstmt));
        stmt_block_ptr->rowset_pos = tmp_rowset_pos;
        if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
            stmt_block_ptr->state = API_STMT_EXECUTED;
            /* re-position the cursor */
            if (tmp_fetch_pos<0) {
                retcode = pa12FetchOneRow( env_block_ptr,
                                           dbc_block_ptr,
                                           stmt_block_ptr, 
                                           NULL,
                                           NULL,
                                           SQL_FETCH_LAST,
                                           0,
                                           rowstatus,
                                           hstmt);
                if (tmp_fetch_pos<-1) {
                    retcode = pa12FetchOneRow( env_block_ptr,
                                               dbc_block_ptr,
                                               stmt_block_ptr, 
                                               NULL,
                                               NULL,
                                               SQL_FETCH_RELATIVE,
                                               tmp_fetch_pos+1,
                                               rowstatus,
                                               hstmt);
                }; /* if */
            } else if (tmp_fetch_pos>0) {
                retcode = pa12FetchOneRow( env_block_ptr,
                                           dbc_block_ptr,
                                           stmt_block_ptr, 
                                           NULL,
                                           NULL,
                                           SQL_FETCH_ABSOLUTE,
                                           tmp_fetch_pos,
                                           rowstatus,
                                           hstmt);
            }; /* if */                    
        }; /* if */
    }; /* if */

    return retcode;
} /* pa12_UpdateResultTable */


RETCODE pa12_FetchTypeIsValid( UWORD          fetchtype,
                               tpa60Stmt     *stmt_block_ptr,
                               UWORD         *sqlState     )
{
    RETCODE retcode = SQL_SUCCESS;
    
    switch (fetchtype) {
    case (SQL_FETCH_NEXT): {
        /* always allowed */
        break;
    }
    case (SQL_FETCH_FIRST): {}     
    case (SQL_FETCH_BOOKMARK): {}
    case (SQL_FETCH_ABSOLUTE): {}
    case (SQL_FETCH_PRIOR): {}
    case (SQL_FETCH_LAST): {}
    case (SQL_FETCH_RELATIVE): {
        if (stmt_block_ptr->stmtopt.cursor_type == SQL_CURSOR_FORWARD_ONLY) {
            *sqlState = API_ODBC_S1106, NULL; /* Fetch type out of range */
            retcode = SQL_ERROR;
        }
        break;
    }
    default: {
        *sqlState = API_ODBC_S1106;
        retcode = SQL_ERROR;
        break;
    }
    }; /* switch */

    if (retcode != SQL_ERROR) {
        if (stmt_block_ptr->stmtopt.cursor_type == SQL_CURSOR_KEYSET_DRIVEN) {
            if ( stmt_block_ptr->stmtopt.keyset_size > 0
                 && stmt_block_ptr->stmtopt.keyset_size
                    < stmt_block_ptr->ardPtr->header.arraySize ) {
                *sqlState = API_ODBC_S1107; /* Row value out of range */
                retcode = SQL_ERROR;
            }
        }  
        
        if ( (stmt_block_ptr->col_handle == 0 && stmt_block_ptr->col_size == 0)
             || stmt_block_ptr->number_cols == 0) {
            retcode = SQL_ERROR;
            *sqlState = API_ODBC_24000; /* Invalid Cursor state */
        }
    }; /* if */

    return retcode;
} /* pa12_FetchTypeIsValid */


SWORD pa12_GetDirection( UWORD         fetchtype,
                         SQLROWOFFSET  irow )
{
    if ( fetchtype == SQL_FETCH_NEXT
         || (fetchtype == SQL_FETCH_ABSOLUTE && irow>0)
         || (fetchtype == SQL_FETCH_RELATIVE && irow>0)) {
        return 1;
    } else {
        return -1;
    }; /* else */
} /* pa12_GetDirection */    


RETCODE pa12_GetFetchTypeAndPos( SQLHSTMT      hstmt,
                                 UWORD         fFetchType,
                                 SQLROWOFFSET  irow,
                                 UWORD        *firstFetchType,
                                 SQLROWOFFSET *pos )
{
    RETCODE    retcode = SQL_SUCCESS;
    SQLHDBC    hdbc;
    tpa40DBC  *dbc_block_ptr;
    SQLHENV    henv;
    tpa50Env  *env_block_ptr;
    tpa60Stmt *stmt_block_ptr;
    UDWORD     rowset_pos;
    UDWORD     rowset_size;
    UDWORD     previous_rowset_size;
    
    apmlocp( &hstmt, &stmt_block_ptr, &hdbc, &dbc_block_ptr,
             &henv, &env_block_ptr);

    rowset_pos  = stmt_block_ptr->rowset_pos;
    rowset_size = stmt_block_ptr->ardPtr->header.arraySize;
    previous_rowset_size = stmt_block_ptr->ardPtr->header.previousArraySize;
    
    switch (fFetchType) {
    case SQL_FETCH_BOOKMARK : {
        *firstFetchType = SQL_FETCH_ABSOLUTE;
        *pos = irow;
        stmt_block_ptr -> fetch_pos = irow;
        break;
    }
    case SQL_FETCH_NEXT : {
        /* for forward only cursor we can always use fetch next PTS 1106941 */
        if ( rowset_pos == 0
             && stmt_block_ptr->stmtopt.cursor_type != SQL_CURSOR_FORWARD_ONLY ) {
            /* cursor stands before the result set */
            *firstFetchType = SQL_FETCH_FIRST;
        } else
            if ( rowset_pos == rowset_size+1 ) {
                /* cursor stands behind the result set */
                retcode = SQL_NO_DATA_FOUND;
            } else {
                /* cursor stands inside the current rowset */
                if (previous_rowset_size>1) {
                    *firstFetchType = SQL_FETCH_RELATIVE;
                    *pos = previous_rowset_size - rowset_pos + 1;
                } else {
                    *firstFetchType = SQL_FETCH_NEXT;
                }; /* else */
            }; /* else */
        break;
    }
    case SQL_FETCH_FIRST : {
        *firstFetchType = SQL_FETCH_FIRST;
        break;
    }
    case SQL_FETCH_LAST : {
        /* position cursor at last row of result table */
        if (rowset_size>1) {
            retcode = pa12FetchOneRow( env_block_ptr,
                                       dbc_block_ptr,
                                       stmt_block_ptr, 
                                       NULL,
                                       NULL,
                                       SQL_FETCH_LAST,
                                       0,
                                       NULL,
                                       hstmt);        
            *pos = -((SDWORD) rowset_size)+1;
            *firstFetchType = SQL_FETCH_RELATIVE;
        } else {
            *firstFetchType = SQL_FETCH_LAST;
        }; /* else */
        break;
    }
    case SQL_FETCH_PRIOR : {
        if ( rowset_pos == 0 ) {
            /* cursor stand before result set */
            retcode = SQL_NO_DATA_FOUND;
        } else if ( rowset_pos == rowset_size+1 ) {
            /* cursor stand after result set */
            /* position cursor at last row */
            retcode = pa12FetchOneRow( env_block_ptr,
                                       dbc_block_ptr,
                                       stmt_block_ptr, 
                                       NULL,
                                       NULL,
                                       SQL_FETCH_LAST,
                                       0,
                                       NULL,
                                       hstmt);            
            *pos = -((SDWORD) rowset_size)+1;
            *firstFetchType = SQL_FETCH_RELATIVE;
        } else {
            *firstFetchType = SQL_FETCH_RELATIVE;
            *pos = -((SDWORD) (rowset_size+rowset_pos)) + 1;
        }; /* else */
        break;
    }
    case SQL_FETCH_RELATIVE : {
        if ( irow < 0 ) {
            if ( rowset_pos == 0 ) { /* cursor stands before result set */
                retcode = SQL_NO_DATA_FOUND;
            };
        } else if (irow > 0) {
            if ( rowset_pos == rowset_size+1 ) {
                /* cursor stands after result set */
                retcode = SQL_NO_DATA_FOUND;
            };
        } else if ( rowset_pos == rowset_size+1 || rowset_pos == 0 ) {
            /* after or before the result set with fetch relative 0 */
            retcode = SQL_NO_DATA_FOUND;
        }; /* else */
        if (retcode == SQL_SUCCESS) {
            *firstFetchType = SQL_FETCH_RELATIVE;
            if ( rowset_pos == rowset_size+1 || rowset_pos == 0 ) {
                /* we stand before or after the result set */
                *pos = irow;
            } else { /* we stand inside of the last row-set */
                *pos = irow - rowset_pos + 1;
            }; /* else */
        }; /* if */
        break;
    }
    case SQL_FETCH_ABSOLUTE :{
        if (irow < 0) {
            if ( rowset_size > 1 || irow < -1 ) {
                /* position cursor at last row of result table */
                retcode = pa12FetchOneRow( env_block_ptr,
                                           dbc_block_ptr,
                                           stmt_block_ptr, 
                                           NULL,
                                           NULL,
                                           SQL_FETCH_LAST,
                                           0,
                                           NULL,
                                           hstmt);
                *firstFetchType = SQL_FETCH_RELATIVE;
                *pos = irow+1;
            } else { /* rowset_size == 1 && irow == -1 */
                *firstFetchType = SQL_FETCH_LAST;
            }; /* else */
        } else {
            *firstFetchType = SQL_FETCH_ABSOLUTE;
            *pos = irow;
        }; /* else */
        break;
    }
    default: {
        *pos = 0;
        break;
    }
    }; /* switch */

    return retcode;
} /* pa12_GetFetchTypeAndPos */


void pa12_SetSFetchPos( tpa60Stmt     *stmt_block_ptr,
                        UWORD          fFetchType,
                        SQLROWOFFSET   irow )
{
    switch (fFetchType) {
    case SQL_FETCH_NEXT : {
        stmt_block_ptr -> fetch_pos++;
        break;
    }
    case SQL_FETCH_FIRST : {
        stmt_block_ptr -> fetch_pos = 1;
        break;
    }
    case SQL_FETCH_LAST : {
        stmt_block_ptr -> fetch_pos = -1;
        break;
    }
    case SQL_FETCH_PRIOR : {
        stmt_block_ptr -> fetch_pos--;
        break;
    }
    case SQL_FETCH_RELATIVE : {
        stmt_block_ptr -> fetch_pos += irow;
        break;
    }
    case SQL_FETCH_ABSOLUTE :{
        stmt_block_ptr -> fetch_pos = irow;
        break;
    }
    default: {
        break;
    }
    }; /* switch */
} /* pa12_SetSFetchPos */

#endif  /* of _UNICODE_ODBC */
                       
typedef struct {
  tpr05_String *pk_qualifier;
  tpr05_String *pk_owner;
  tpr05_String *pk_table;
  tpr05_String *fk_qualifier;
  tpr05_String *fk_owner;
  tpr05_String *fk_table;
  UWORD         mode;
} pa11SQLForeignKeys;

#ifdef _UNICODE_ODBC
ODBC_PROC(SQLForeignKeysW,
	  (SQLHSTMT     hstmt,
	   SQLWCHAR    *szPkTableQualifier,
	   SQLSMALLINT  cbPkTableQualifier,
	   SQLWCHAR    *szPkTableOwner,
	   SQLSMALLINT  cbPkTableOwner,
	   SQLWCHAR    *szPkTableName,
	   SQLSMALLINT  cbPkTableName,
	   SQLWCHAR    *szFkTableQualifier,
	   SQLSMALLINT  cbFkTableQualifier,
	   SQLWCHAR    *szFkTableOwner,
	   SQLSMALLINT  cbFkTableOwner,
	   SQLWCHAR    *szFkTableName,
	   SQLSMALLINT  cbFkTableName),
	  ( hstmt, szPkTableQualifier, cbPkTableQualifier, szPkTableOwner, cbPkTableOwner, 
            szPkTableName, cbPkTableName, szFkTableQualifier, cbFkTableQualifier, 
            szFkTableOwner, cbFkTableOwner, szFkTableName, cbFkTableName ))
#else
ODBC_PROC(SQLForeignKeys,
	  (SQLHSTMT     hstmt,
	   SQLCHAR     *szPkTableQualifier,
	   SQLSMALLINT  cbPkTableQualifier,
	   SQLCHAR     *szPkTableOwner,
	   SQLSMALLINT  cbPkTableOwner,
	   SQLCHAR     *szPkTableName,
	   SQLSMALLINT  cbPkTableName,
	   SQLCHAR     *szFkTableQualifier,
	   SQLSMALLINT  cbFkTableQualifier,
	   SQLCHAR     *szFkTableOwner,
	   SQLSMALLINT  cbFkTableOwner,
	   SQLCHAR     *szFkTableName,
	   SQLSMALLINT  cbFkTableName),
	  ( hstmt, szPkTableQualifier, cbPkTableQualifier, szPkTableOwner, cbPkTableOwner, 
            szPkTableName, cbPkTableName, szFkTableQualifier, cbFkTableQualifier, 
            szFkTableOwner, cbFkTableOwner, szFkTableName, cbFkTableName ))
#endif
{
    RETCODE retcode=SQL_SUCCESS;
    API_RETCODE api_retcode;
    /* ------------------------------------------------------------ha--- */

    pa11SQLForeignKeys LV;
    UWORD         state;

    tpa60Stmt *stmt_block_ptr;
    SQLHENV henv;
    tpa50Env *env_block_ptr;
    SQLHDBC hdbc;
    tpa40DBC *dbc_block_ptr;
    UDWORD odbcVersion;
    char       *selectForeignKeys;
    const char *tableprefix;
    tpr05_String   *sqlString = NULL, *whereString = NULL;

#ifdef _UNICODE_ODBC
    const tsp77encoding *encodingType = sp77nativeUnicodeEncoding ();
#else
    const tsp77encoding *encodingType = sp77encodingAscii;
#endif   
    const int charSize = encodingType->fixedCharacterSize;

    /* ----------------------------------------------------------ha--- */

    API_TRACE(API_TR_ENTRY, PA12_FN_SQLFOREIGNKEYS, 0);
    API_TRACE(API_TR_HANDLE,"hstmt",&hstmt);
    API_TRACE_LEN(API_TR_ODBC_STRING,"szPkTableQualifier",
                  szPkTableQualifier, cbPkTableQualifier);
    API_TRACE(API_TR_SWORD,"cbPkTableQualifier",&cbPkTableQualifier);
    API_TRACE_LEN(API_TR_ODBC_STRING,"szPkTableOwner",
                  szPkTableOwner, cbPkTableOwner);
    API_TRACE(API_TR_SWORD,"cbPkTableOwner",&cbPkTableOwner);
    API_TRACE_LEN(API_TR_ODBC_STRING,"szPkTableName",
                  szPkTableName, cbPkTableName);
    API_TRACE(API_TR_SWORD,"cbPkTableName",&cbPkTableName);
    API_TRACE_LEN(API_TR_ODBC_STRING,"szFkTableQualifier",
                  szFkTableQualifier, cbFkTableQualifier);
    API_TRACE(API_TR_SWORD,"cbFkTableQualifier",&cbFkTableQualifier);
    API_TRACE_LEN(API_TR_ODBC_STRING,"szFkTableOwner",
                  szFkTableOwner, cbFkTableOwner);
    API_TRACE(API_TR_SWORD,"cbFkTableOwner",&cbFkTableOwner);
    API_TRACE_LEN(API_TR_ODBC_STRING,"szFkTableName",
                  szFkTableName, cbFkTableName);
    API_TRACE(API_TR_SWORD,"cbFkTableName",&cbFkTableName);

    LV.pk_qualifier = LV.pk_owner = LV.pk_table = NULL;
    LV.fk_qualifier = LV.fk_owner = LV.fk_table = NULL;

    if (cbPkTableQualifier != SQL_NTS)
      cbPkTableQualifier *= charSize;
    if (cbPkTableOwner != SQL_NTS)
      cbPkTableOwner *= charSize;
    if (cbPkTableName != SQL_NTS)
      cbPkTableName *= charSize;
    if (cbFkTableQualifier != SQL_NTS)
      cbFkTableQualifier *= charSize;
    if (cbFkTableOwner != SQL_NTS)
      cbFkTableOwner *= charSize;
    if (cbFkTableName != SQL_NTS)
      cbFkTableName *= charSize;

    /* --------------------------------------------------------------- */
    api_retcode = apmstfc(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, 
                          SQL_API_SQLFOREIGNKEYS);
    if (api_retcode != API_OK) {
        retcode = SQL_INVALID_HANDLE;
    }
    /* ---------------------------------------------------------------- */
    else { 
        if (pa60VerifyStmt(hstmt) != API_OK) {        /* hstmt valid ? */
            retcode = SQL_INVALID_HANDLE;
        }
        else {
            /* determine which ODBC version the application wants */
            if (!pa10GetODBCVersion( SQL_HANDLE_STMT, hstmt,
                                     &odbcVersion )) {
                retcode = SQL_ERROR;
                /* general error */
                pa60PutError( hstmt, API_ODBC_S1000, NULL);
                goto exit_1_lock;                    
            }; /* if */
            
            apmlocp(&hstmt, &stmt_block_ptr, &hdbc, &dbc_block_ptr, 
                    &henv, &env_block_ptr);
            state = stmt_block_ptr->state;

            tableprefix = pa12_getTablePrefix (dbc_block_ptr);
            /* ODBC version 3.0 defines different column-names */
            if (odbcVersion == SQL_OV_ODBC3) {
                selectForeignKeys = (char *) alloca (strlen(PA12SELECTFOREIGNKEYS_OV3) + 30);
                sprintf (selectForeignKeys, PA12SELECTFOREIGNKEYS_OV3, tableprefix);
            } else { /* version < 3.x */
                selectForeignKeys = (char *) alloca (strlen(PA12SELECTFOREIGNKEYS) + 30);
                sprintf (selectForeignKeys, PA12SELECTFOREIGNKEYS, tableprefix);
            }; /* else */

            PA09ENTERASYNCFUNCTION(dbc_block_ptr, stmt_block_ptr);
            if (pa09IsAsync(stmt_block_ptr, dbc_block_ptr, NULL)) { 
                if (pa09IsConnectionBusy(stmt_block_ptr, dbc_block_ptr)) {
                    retcode = SQL_ERROR;
                    pa60PutError( hstmt,
                                  API_ODBC_S1010, NULL);
                    goto exit_1_lock;
                }	
                if (state == API_STMT_POSITIONED) {
                    /* function seqeunces error */
                    retcode = SQL_ERROR;            /* SQLSTATE: 24000 */
                    pa60PutError( hstmt, 
                                  API_ODBC_24000, NULL);
                    goto exit_1_lock;
                }

                /* --------------------------------------------------- */
                /* ***** szPkTableQualifier */

                /* create strings (ASCII) */
                LV.pk_qualifier = pr05IfCom_String_NewDynString (charSize * ((API_TABLE_NAME_MAX)+1), 
                                                                 encodingType);
                LV.pk_owner     = pr05IfCom_String_NewDynString (charSize * ((API_USERID_MAX_LEN)+1),
                                                                 encodingType);
                LV.pk_table     = pr05IfCom_String_NewDynString (charSize * ((API_TABLE_NAME_MAX)+1),
                                                                 encodingType);
                LV.fk_qualifier = pr05IfCom_String_NewDynString (charSize * ((API_TABLE_NAME_MAX)+1),
                                                                 encodingType);
                LV.fk_owner     = pr05IfCom_String_NewDynString (charSize * ((API_USERID_MAX_LEN)+1),
                                                                 encodingType);
                LV.fk_table     = pr05IfCom_String_NewDynString (charSize * ((API_TABLE_NAME_MAX)+1), 
                                                                 encodingType);

                if (LV.pk_qualifier == NULL || LV.pk_table == NULL || LV.pk_table == NULL ||
                    LV.fk_qualifier == NULL || LV.fk_table == NULL || LV.fk_table == NULL) {
                  retcode = SQL_ERROR;            
                  pa60PutError( hstmt, API_ODBC_S1001, NULL);
                  goto exit_1_lock;
                }

                if (szPkTableQualifier == NULL) {
/*                     API_STRCPY( (UCHAR*) LV.pk_qualifier->rawString, (UCHAR*) ""); */
                }
                else {
                    api_retcode = pa80ODBCtoTpr05 (LV.pk_qualifier,
                                                   encodingType,
                                                   szPkTableQualifier,
                                                   cbPkTableQualifier);
                    if (api_retcode != API_OK) {
                        retcode = SQL_ERROR;
                        pa60PutError( hstmt, 
                                      API_ODBC_S1090, NULL);
                        goto exit_1_lock;
                    }
                }
                /* ***** szPkTableOwner */
                if (szPkTableOwner == NULL) {
                  if (pr05IfCom_String_strcpy (LV.pk_owner, dbc_block_ptr->userid) != ok_epr05)
                    api_retcode = API_NOT_OK;
                }
                else {
                    api_retcode = pa80ODBCtoTpr05 (LV.pk_owner,
                                                   encodingType,
                                                   szPkTableOwner,
                                                   cbPkTableOwner);
                    if (api_retcode != API_OK) {
                        retcode = SQL_ERROR;
                        pa60PutError( hstmt, 
                                      API_ODBC_S1090, NULL);
                        goto exit_1_lock;
                    }
                }
                /* ***** szPkTableName */
                if (szPkTableName == NULL) {
/*                     API_STRCPY( (UCHAR*) LV.pk_table->rawString, (UCHAR*) ""); */
                }
                else {
                    api_retcode = pa80ODBCtoTpr05 (LV.pk_table,
                                                   encodingType,
                                                   szPkTableName,
                                                   cbPkTableName);
                    if (api_retcode != API_OK) {
                        retcode = SQL_ERROR;
                        pa60PutError( hstmt, 
                                      API_ODBC_S1090, NULL);
                        goto exit_1_lock;
                    }
                }
                /* ***** szFkTableQualifier */
                if (szFkTableQualifier == NULL) {
/*                     API_STRCPY ((UCHAR*) LV.fk_qualifier->rawString, (UCHAR*) ""); */
                }
                else {
                    api_retcode = pa80ODBCtoTpr05 (LV.fk_qualifier,
                                                   encodingType,
                                                   szFkTableQualifier,
                                                   cbFkTableQualifier);
                    if (api_retcode != API_OK) {
                        retcode = SQL_ERROR;
                        pa60PutError( hstmt, 
                                      API_ODBC_S1090, NULL);
                        goto exit_1_lock;
                    }
                }
                /* ***** szFkTableOwner */
                if (szFkTableOwner == NULL) {
                  if (pr05IfCom_String_strcpy (LV.fk_owner, dbc_block_ptr->userid) != ok_epr05)
                    api_retcode = API_NOT_OK;
                }
                else {
                    api_retcode = pa80ODBCtoTpr05 (LV.fk_owner,
                                                   encodingType,
                                                   szFkTableOwner,
                                                   cbFkTableOwner);
                    if (api_retcode != API_OK) {
                        retcode = SQL_ERROR;
                        pa60PutError( hstmt, API_ODBC_S1090, NULL);
                        goto exit_1_lock;
                    }
                }
                /* ***** szFkTableName */
                if (szFkTableName == NULL) {
/*                     API_STRCPY ((UCHAR*) LV.fk_table->rawString, (UCHAR*) ""); */
                }
                else {
                    api_retcode = pa80ODBCtoTpr05 (LV.fk_table,
                                                   encodingType,
                                                   szFkTableName,
                                                   cbFkTableName);
                    if (api_retcode != API_OK) {
                        retcode = SQL_ERROR;
                        pa60PutError( hstmt, 
                                      API_ODBC_S1090, NULL);
                        goto exit_1_lock;
                    }
                }
                /* ---------------------------------------------------- */
                /* ***** szPkTableName=NULL and szFkTableName=NULL ? */
                if ( (szPkTableName == NULL) && (szFkTableName == NULL) ) {
                    retcode = SQL_ERROR;
                    pa60PutError( hstmt, 
                                  API_ODBC_S1009, NULL);
                    goto exit_1_lock;
                }
                /* default LV.mode */
                LV.mode = API_PKTABLE_SCAN;        

                /* ***** szPkTableName!=NULL and szFkTableName=NULL ? */
                if ( (szPkTableName != NULL) && (szFkTableName == NULL) ) {
                    LV.mode = API_PKTABLE_SCAN;
                }

                /* ***** szPkTableName=NULL and szFkTableName!=NULL ? */
                if ( (szPkTableName == NULL) && (szFkTableName != NULL) ) {
                    LV.mode = API_FKTABLE_SCAN;
                }

                /* ***** szPkTableName=NULL and szFkTableName!=NULL ? */
                if ( (szPkTableName != NULL) && (szFkTableName != NULL) ) {
                    LV.mode = API_PKTABLE_FKTABLE_SCAN;
                }
	
                stmt_block_ptr -> dbc_special.special =
                    API_SPEC_SQLFOREIGNKEYS;
                if (pa09IsAsync(stmt_block_ptr, dbc_block_ptr, &retcode)) {
                    /* store the local stack to restore get the values in
                     * async call */
                    pa09SaveStack( &stmt_block_ptr->async.hLocalValues,
                                   &LV,
                                   sizeof(LV) );
                    pa09PrepareAsyncCall( hstmt, SQL_API_SQLFOREIGNKEYS);     
                }
            } /* async exec */
            if (pa09IsAsync(stmt_block_ptr, dbc_block_ptr, &retcode)) {
                pa09ExecuteAsyncCall(hstmt);
                goto exit_async;
            }
            else {
                /* restore the stack values from async caller */
                if (stmt_block_ptr->async.hLocalValues) {
                    pa09RestoreStack(stmt_block_ptr->async.hLocalValues, &LV);
                    apdfree(stmt_block_ptr->async.hLocalValues);
                    stmt_block_ptr->async.hLocalValues = API_NULL_HANDLE;
                }
            }

            /* ------------------------------------------------------- */
            sqlString       = pr05IfCom_String_NewDynString (charSize * API_SQL_STRING_SWAG * 2, 
                                                             encodingType);
            whereString     = pr05IfCom_String_NewDynString (charSize * API_SQL_STRING_SWAG / 2, 
                                                             encodingType);
            if (sqlString == NULL || whereString == NULL)  {
                retcode = SQL_ERROR;            
                pa60PutError( hstmt, API_ODBC_S1001, NULL);
                goto exit_1_lock;
                }

            pr05IfCom_String_strcatP (sqlString, (char*) selectForeignKeys, 
                                      (tsp00_Uint4) API_STRLEN (selectForeignKeys),
                                      sp77encodingAscii);
            switch (LV.mode) {
            case (API_PKTABLE_SCAN): {
              whereString->cbLen = sp77sprintfUnicode (encodingType,
                                  whereString->rawString, whereString->cbMaxLen, 
                                  PA12SELECTPKKEYS,
                                  LV.pk_owner->encodingType,
                                  LV.pk_owner->cbLen,
                                  LV.pk_owner->rawString,
                                  LV.pk_table->encodingType,
                                  LV.pk_table->cbLen,
                                  LV.pk_table->rawString);
              pr05IfCom_String_strcat (sqlString, whereString);
              /*              API_STRCAT( sql_ptr, where_string); */
              pr05IfCom_String_strcatP (sqlString, PA12ORDERBYPKTABLE, 
                                        (tsp00_Uint4) API_STRLEN (PA12ORDERBYPKTABLE),
                                        sp77encodingAscii);
/*               API_STRCAT( sql_ptr, PA12ORDERBYPKTABLE); */
              retcode = SQL_SUCCESS;
              break;
            }
            case (API_FKTABLE_SCAN): {
              whereString->cbLen 
                = sp77sprintfUnicode (encodingType,
                                      whereString->rawString, whereString->cbMaxLen, 
                                      PA12SELECTFKKEYS,
                                      LV.fk_owner->encodingType,
                                      LV.fk_owner->cbLen,
                                      LV.fk_owner->rawString,
                                      LV.fk_table->encodingType,
                                      LV.fk_table->cbLen,
                                      LV.fk_table->rawString);
/*               API_STRCAT( sql_ptr, where_string); */
/*               API_STRCAT( sql_ptr, PA12ORDERBYFKTABLE); */
              pr05IfCom_String_strcat (sqlString, whereString);
              pr05IfCom_String_strcatP (sqlString, PA12ORDERBYFKTABLE, 
                                        (tsp00_Uint4) API_STRLEN (PA12ORDERBYFKTABLE),
                                        sp77encodingAscii);
              retcode = SQL_SUCCESS;
              break;
            }
            case (API_PKTABLE_FKTABLE_SCAN): {
              whereString->cbLen 
                = sp77sprintfUnicode (encodingType,
                                      whereString->rawString, whereString->cbMaxLen, 
                                      PA12SELECTPKKEYS,
                                      LV.pk_owner->encodingType,
                                      LV.pk_owner->cbLen,
                                      LV.pk_owner->rawString,
                                      LV.pk_table->encodingType,
                                      LV.pk_table->cbLen,
                                      LV.pk_table->rawString);
              pr05IfCom_String_strcat (sqlString, whereString);
              pr05IfCom_String_strcatP (sqlString, PA12AND, 
                                        (tsp00_Uint4) API_STRLEN (PA12AND),
                                        sp77encodingAscii);
/*               API_STRCAT( sql_ptr, where_string); */
/*               API_STRCAT( sql_ptr, PA12AND); */
              whereString->cbLen 
                = sp77sprintfUnicode (encodingType,
                                      whereString->rawString, whereString->cbMaxLen, 
                                      PA12SELECTFKKEYS,
                                      LV.fk_owner->encodingType,
                                      LV.fk_owner->cbLen,
                                      LV.fk_owner->rawString,
                                      LV.fk_table->encodingType,
                                      LV.fk_table->cbLen,
                                      LV.fk_table->rawString);
              pr05IfCom_String_strcat (sqlString, whereString);
              pr05IfCom_String_strcatP (sqlString, PA12ORDERBYFKTABLE, 
                                        (tsp00_Uint4) API_STRLEN (PA12ORDERBYFKTABLE),
                                        sp77encodingAscii);
/*               API_STRCAT( sql_ptr, where_string); */
/*               API_STRCAT( sql_ptr, PA12ORDERBYFKTABLE); */
              retcode = SQL_SUCCESS;
              break;
            }
            default: {
              retcode = SQL_ERROR;
            }
            }
            /* ------------------------------------------------------ */
            if (retcode == SQL_SUCCESS) {
#ifdef _UNICODE_ODBC
              retcode = CALL_ODBC(SQLPrepareW, (hstmt, (SQLWCHAR*) sqlString->rawString, 
                                                sqlString->cbLen / charSize));
#else
              retcode = CALL_ODBC(SQLPrepare, (hstmt, (SQLCHAR*) sqlString->rawString, sqlString->cbLen));
#endif
              if (retcode == SQL_SUCCESS) {
                retcode = CALL_ODBC(SQLExecute, (hstmt));
              }
            }

        exit_1_lock:
            pr05IfCom_String_DeleteString (sqlString);
            pr05IfCom_String_DeleteString (whereString);

            pr05IfCom_String_DeleteString (LV.pk_qualifier);
            pr05IfCom_String_DeleteString (LV.pk_owner);
            pr05IfCom_String_DeleteString (LV.pk_table);
            pr05IfCom_String_DeleteString (LV.fk_qualifier);
            pr05IfCom_String_DeleteString (LV.fk_owner);
            pr05IfCom_String_DeleteString (LV.fk_table);
        exit_async:
            PA09LEAVEASYNCFUNCTION();
        }  /* start processing */
    } /* set odbc_funtion */ 

    API_TRACE(API_TR_EXIT, PA12_FN_SQLFOREIGNKEYS, 0);
    API_TRACE(API_TR_RETCODE,"retcode",&retcode);

    return (retcode);
} /* SQLForeignKeys */


#ifndef _UNICODE_ODBC

ODBC_PROC(SQLMoreResults,
	  (SQLHSTMT  hstmt),
	  (hstmt))
{
    RETCODE retcode;
    API_RETCODE api_retcode;

    API_TRACE(API_TR_ENTRY, PA12_FN_SQLMORERESULTS, 0);
    API_TRACE(API_TR_HANDLE,"hstmt",&hstmt);

    api_retcode = apmstfc(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, 
                          SQL_API_SQLMORERESULTS);
    if (api_retcode != API_OK) {
        retcode = SQL_INVALID_HANDLE;
    }
    else {
        retcode = CALL_ODBC(SQLFreeStmt, (hstmt, SQL_CLOSE));
        if (retcode == SQL_SUCCESS)
            retcode = SQL_NO_DATA_FOUND;
    }
    API_TRACE(API_TR_EXIT, PA12_FN_SQLMORERESULTS, 0);
    API_TRACE(API_TR_RETCODE,"retcode",&retcode);

    return (retcode);
} /* SQLMoreResults */


/* returns number of parameters in statement/procedure */
ODBC_PROC(SQLNumParams,
	  (SQLHSTMT     hstmt,
	   SQLSMALLINT *pcpar),
	  (hstmt, pcpar))
{
    UWORD       state;
    tpa60Stmt  *stmt_block_ptr;
    UWORD       number_parms;
    RETCODE     retcode;
    API_RETCODE api_retcode;
    
    API_TRACE(API_TR_ENTRY, PA12_FN_SQLNUMPARAMS, 0);
    API_TRACE(API_TR_HANDLE,"hstmt",&hstmt);
    API_TRACE(API_TR_PTR,"pcpar",&pcpar);

    api_retcode = apmstfc(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, 
                          SQL_API_SQLNUMPARAMS);
    if (api_retcode != API_OK) {
        retcode = SQL_INVALID_HANDLE;
    }
    else {
        if (pa60VerifyStmt( hstmt ) != API_OK) { /* hstmt valid? */
            retcode = SQL_INVALID_HANDLE;
        }
        else {
            state = ((tpa60Stmt*) hstmt)->state;
            if (state != API_STMT_PREPARED && state != API_STMT_EXECUTED &&
                state != API_STMT_POSITIONED) {
                retcode = SQL_ERROR;
                pa60PutError( hstmt, API_ODBC_S1010, NULL); /* function
                                                             * sequence
                                                             * error */
            }
            else {
                if(pcpar) {
                    stmt_block_ptr = (tpa60Stmt     *) apdlock(hstmt);
                    API_ASSERT_PTR(stmt_block_ptr);
                    number_parms = stmt_block_ptr -> number_parms;	   
                    *pcpar = number_parms;
                    retcode = SQL_SUCCESS;
                }
            }
        }
    }
    API_TRACE(API_TR_EXIT, PA12_FN_SQLNUMPARAMS, 0);
    API_TRACE(API_TR_SWORD,"pcpar", pcpar);
    API_TRACE(API_TR_RETCODE,"retcode",&retcode);

    return (retcode);
} /* SQLNumberParams */


RETCODE pa12FetchOneRow( tpa50Env       *env_block_ptr, 
                         tpa40DBC       *dbc_block_ptr,
                         tpa60Stmt      *stmt_block_ptr,
                         tpa20Desc      *appl_desc_ptr,
                         tpa20Desc      *impl_desc_ptr,
                         UWORD           fetchtype, 
                         SQLROWOFFSET    pos,
                         UWORD          *rowstatus,
                         SQLHSTMT        hstmt)
{  
    RETCODE      retcode=SQL_SUCCESS; 
    API_RETCODE  api_retcode;
    API_RETCODE  conv_retcode;
    UWORD        number_cols;
    SQLROWOFFSET absoluteFetchPos;
    
    API_TRACE(API_TR_ENTRY, PA12_FN_PA12FETCHONEROW, 0);
    API_TRACE(API_TR_UWORD, "fetchtype", &fetchtype);
    API_TRACE(API_TR_UDWORD, "pos", &pos);
    API_TRACE(API_TR_PTR, "appl_desc_ptr", &appl_desc_ptr);
    API_TRACE(API_TR_PTR, "impl_desc_ptr", &impl_desc_ptr);
        
    number_cols = stmt_block_ptr->number_cols;
    api_retcode = apefetc( &dbc_block_ptr->esqblk,
                           dbc_block_ptr,
                           stmt_block_ptr, 
                           fetchtype,
                           (SDWORD) pos );      /* ia 64 cast */
    if (api_retcode != API_OK) { 
        retcode = SQL_ERROR;
        pa60PutError( hstmt, API_ODBC_S1001, NULL );
        goto error_exit;
    }
    /* end of data? */
    if (dbc_block_ptr->esqblk.esq_sql_code == PA_ROW_NOT_FOUND
        || dbc_block_ptr->esqblk.esq_sql_code == -7026 ) {
        /* -7026 : if FETCH_SAME and cursor stands before result set */
        retcode = SQL_NO_DATA_FOUND;
        goto error_exit;
    }
    if ( stmt_block_ptr->stmtopt.cursor_type == SQL_CURSOR_DYNAMIC
        || (stmt_block_ptr->stmtopt.cursor_type == SQL_CURSOR_KEYSET_DRIVEN
            && stmt_block_ptr->stmtopt.optimize_cursor == SQL_TRUE)) {
        pa12_SetSFetchPos( stmt_block_ptr, fetchtype, pos );
        absoluteFetchPos = stmt_block_ptr->fetch_pos;
    } else {
        API_ASSERT( fetchtype == SQL_FETCH_ABSOLUTE );
        absoluteFetchPos = pos;
    }; /* else */        
    if (dbc_block_ptr->esqblk.esq_sql_code != 0) { /* ESQ fetch ok? */
        retcode = SQL_ERROR;
        pa60PutError( hstmt,
                      pa41apmercv(&dbc_block_ptr->esqblk),
                      &dbc_block_ptr->esqblk);
        goto error_exit;
    }; /* if */
    
    if (appl_desc_ptr != NULL && impl_desc_ptr != NULL) {
        if (stmt_block_ptr->stmtopt.retrieve_data == SQL_RD_ON) {
            /* move the bounded columns */
            conv_retcode = pa20Move( appl_desc_ptr,
                                     impl_desc_ptr,
                                     number_cols,
                                     &stmt_block_ptr -> dbc_special );
            retcode = pa60PutESQError( hstmt, conv_retcode );
        }; /* if */
        /* move bookmark value */
        if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
            tpa21DescRecord *bookmark_rec =
                pa20GetRecord( appl_desc_ptr,
                               API_DESC_BOOKMARK_INDEX );
            API_ASSERT_PTR( bookmark_rec );
            if (bookmark_rec->bound) {
                /* assume that application buffer is big enough. Else we have
                 * to use moved_len .... */
                size_t copyLen = min( sizeof( absoluteFetchPos ),
                                      (size_t) bookmark_rec->length );
                conv_retcode = API_OK;
                if (bookmark_rec->dataPtr != NULL) {
                    API_MEMCPY( PA20_DATA_PTR(bookmark_rec),
                                &absoluteFetchPos,
                                copyLen );
                }; /* if */
                if (PA20_OCTETLENGTH_PTR(bookmark_rec) != NULL) {
                    *PA20_OCTETLENGTH_PTR(bookmark_rec) =
                        sizeof( absoluteFetchPos );
                }; /* if */
                if (copyLen < sizeof( absoluteFetchPos )) {
                    conv_retcode = API_TRUNCATE;
                }; /* if */
                retcode = pa60PutESQError( hstmt, conv_retcode );
            }; /* if */
        }; /* if */
        /* move long values */
        if ((retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
            && (stmt_block_ptr->stmtopt.retrieve_data == SQL_RD_ON)) {
            RETCODE ret = retcode;
            conv_retcode = pa60MoveLong( dbc_block_ptr,
                                         stmt_block_ptr,
                                         appl_desc_ptr,
                                         impl_desc_ptr,
                                         0,
                                         number_cols );
            retcode = pa60PutESQError( hstmt, conv_retcode );
            if (retcode == SQL_SUCCESS) {
                retcode = ret;
            }; /* if */
        }; /* if */
    }; /* if */
error_exit:   
    if (rowstatus) {
        switch(retcode) {
        case SQL_SUCCESS:{}
        case SQL_SUCCESS_WITH_INFO:{
            *rowstatus = SQL_ROW_SUCCESS;
            break;
        }
        case SQL_ERROR:{
            *rowstatus = SQL_ROW_ERROR;
            break;
        }
        case SQL_NO_DATA_FOUND:{
            *rowstatus = SQL_ROW_NOROW;
            break;
        }
        }
    }
    API_TRACE(API_TR_EXIT, PA12_FN_PA12FETCHONEROW, 0);
    API_TRACE(API_TR_RETCODE,"retcode",&retcode);
    return (retcode);
} /* pa12FetchOneRow */


VOID pa12ProcessRetcode( RETCODE retcode, RETCODE *rowret)
{
    switch (retcode) {
    case (SQL_SUCCESS_WITH_INFO): {
        *rowret = retcode;
        break;
    }	   	   
    case (SQL_SUCCESS): {
        if (*rowret == SQL_NO_DATA_FOUND)
            *rowret = retcode;
        break;
    }
    case (SQL_NO_DATA_FOUND): {
        break;
    }
    case (SQL_ERROR): {
        *rowret = retcode;
        break;
    }
    default: {
        if (*rowret != SQL_SUCCESS)
            *rowret = SQL_SUCCESS_WITH_INFO;
        break;
    }
    }; /* switch */
} /* pa12ProcessRetcode */


RETCODE pa12SelectOneRow( SQLHSTMT   hstmt,
                          tpa20Desc *ard_ptr,
                          tpa20Desc *ird_ptr )
{
    RETCODE       retcode=SQL_SUCCESS; 
    tpa60Stmt    *stmt_block_ptr;
    UWORD         number_cols;
    SDWORD        sql_len;
    /*    UCHAR        *sql_ptr; */
    tpr05_String  *SqlStmt;
    API_HANDLE    hand;
    API_HANDLE    sqlda_handle;
    sqldatype    *sqlda_ptr;
    UWORD         i;
    SQLHENV       henv;
    tpa50Env     *env_block_ptr;
    SQLHDBC       hdbc;
    tpa40DBC     *dbc_block_ptr;
    SQLHSTMT      hstmt_extfetch;
    
    API_TRACE(API_TR_ENTRY, PA12_FN_PA12SELECTONEROW, 0);
    apmlocp(&hstmt, &stmt_block_ptr, &hdbc, &dbc_block_ptr, 
            &henv, &env_block_ptr);
    PA09ENTERASYNCFUNCTION(dbc_block_ptr, stmt_block_ptr);
    hstmt_extfetch = stmt_block_ptr->hstmt_extfetch;
    if (hstmt_extfetch == SQL_NULL_HSTMT) {
        retcode = pa60AllocStmt( hdbc, &hstmt_extfetch );
        if (retcode == SQL_SUCCESS) {
            stmt_block_ptr->hstmt_extfetch = hstmt_extfetch;
        }
        else {
            goto error_lock1;
        }
        number_cols = stmt_block_ptr -> number_cols;
        sql_len = 80 + (API_COLUMN_NAME_MAX+5)*number_cols
                     + stmt_block_ptr->table_name->cbLen + API_ODBC_CURSOR_LEN;
        sql_len *= stmt_block_ptr->table_name->encodingType->fixedCharacterSize;
        hand = apdallo((DWORD) sql_len);
        SqlStmt = pr05IfCom_String_NewDynString (sql_len, 
                                                 stmt_block_ptr->table_name->encodingType);
        if (SqlStmt == NULL) {
            retcode = SQL_ERROR;            
            pa60PutError( hstmt, API_ODBC_S1001, NULL);
            goto error_lock1;
        }
        API_ASSERT_PTR (SqlStmt);

        pr05IfCom_String_strcatP (SqlStmt,
                                  "SELECT DIRECT \"",
                                  sizeof ("SELECT DIRECT \"")-1,
                                  sp77encodingAscii);

        sqlda_handle = stmt_block_ptr -> output_sqlda_handle;
        sqlda_ptr = (sqldatype     *) apdlock(sqlda_handle);
        API_ASSERT_PTR(sqlda_ptr);
        for (i=0;i < number_cols;i++) {
          pr05IfCom_String_strcatP (SqlStmt,
                                    (char*) sqlda_ptr->sqlvar[i].colname,
                                    sizeof(sqlda_ptr->sqlvar[0].colname),
                                    sp77encodingUTF8);
          pr05IfCom_String_strcatP (SqlStmt,
                                    "\"",
                                    sizeof("\"")-1,
                                    sp77encodingAscii);
          if (i < (UWORD)(number_cols-1)) {
            pr05IfCom_String_strcatP (SqlStmt,
                                      ",\"",
                                      sizeof(",\"")-1,
                                      sp77encodingAscii);
          }
          
        }
        pr05IfCom_String_strcatP (SqlStmt,
                                  " INTO ?",
                                  sizeof(" INTO ?")-1,
                                  sp77encodingAscii);

        for (i=1;i < number_cols;i++) {
            pr05IfCom_String_strcatP (SqlStmt,
                                      ",?",
                                      sizeof(",?")-1,
                                      sp77encodingAscii);
        }
        pr05IfCom_String_strcatP (SqlStmt,
                                  " FROM ",
                                  sizeof(" FROM ")-1,
                                  sp77encodingAscii);
        pr05IfCom_String_strcat (SqlStmt,
                                 stmt_block_ptr->table_name);
        pr05IfCom_String_strcatP (SqlStmt,
                                  " WHERE CURRENT OF \"",
                                  sizeof(" WHERE CURRENT OF \"")-1,
                                  sp77encodingAscii);
        pr05IfCom_String_strcat (SqlStmt,
                                 stmt_block_ptr->cursor_name);
        pr05IfCom_String_strcatP (SqlStmt,
                                  "\"",
                                  sizeof("\"")-1,
                                  sp77encodingAscii);
        API_TRACE_LEN(API_TR_TPR05STRING, "sql_ptr", SqlStmt, SqlStmt->cbLen);
        retcode = CALL_ODBC( SQLSetStmtAttr,
                             ( hstmt_extfetch,
                               SQL_CONNECT_OPT_DRVR_START+1,
                               (PTR) TRUE, 0));
        if (SqlStmt->encodingType == sp77encodingAscii) {
          retcode = CALL_ODBC(SQLPrepare, (hstmt_extfetch, 
                                           SqlStmt->rawString, 
                                           SqlStmt->cbLen));
        }
        else {
          retcode = CALL_ODBC(SQLPrepareW, (hstmt_extfetch, 
                                            (SQLWCHAR*) SqlStmt->rawString, 
                                            SqlStmt->cbLen / sp77encodingUCS2Native->fixedCharacterSize));
        }
        pr05IfCom_String_DeleteString (SqlStmt);
    }   
    if (retcode == SQL_SUCCESS) {
        tpa60Stmt *stmt_block_ptr = (tpa60Stmt*) apdlock(hstmt_extfetch);
        API_ASSERT_PTR(stmt_block_ptr);
        /* free long handles in ipd because they are overwritten in following
         * pa20CopyDesc */
        pa20FreeLongHandles( &stmt_block_ptr->ipd );
        if ( pa20CopyDesc( ard_ptr, stmt_block_ptr->apdPtr ) != API_OK
             || pa20CopyDesc( ird_ptr, &stmt_block_ptr->ipd ) != API_OK ) {
            retcode = SQL_ERROR;
            goto error_lock1;
        }; /* if */
        /* "correct" the descType */
        stmt_block_ptr->ipd.descType = API_DESC_TYPE_IPD;
        /* we only want to select one row */
        stmt_block_ptr->apdPtr->header.previousArraySize = 1;
        stmt_block_ptr->apdPtr->header.arraySize = 1;
        /* for select direct use the input attributes from the actual row */
        /* attr_ptr are the output atttributes from the resultset */
        retcode = CALL_ODBC(SQLExecute, (hstmt_extfetch));
        /* avoid freeing of long handles because they are copied
         * (PTS 1105213) */
        pa20InitLongHandles( &stmt_block_ptr->ipd );
        if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
            tpa60Stmt *stmt_block_ptr = (tpa60Stmt*) apdlock(hstmt_extfetch);
            API_ASSERT_PTR(stmt_block_ptr);
            if (stmt_block_ptr -> number_rows == 0) {
                retcode = SQL_NO_DATA_FOUND;
            }
        }
    }
    else {
      /* error message: select direct not supported starting 7.4 */
      pa60PutError( hstmt, API_ODBC_HY000, NULL );
      retcode = SQL_ERROR;
    }
      
error_lock1:
    PA09LEAVEASYNCFUNCTION();
    API_TRACE(API_TR_EXIT, PA12_FN_PA12SELECTONEROW, 0);
    API_TRACE(API_TR_RETCODE,"retcode",&retcode);
    return(retcode);
} /* pa12SelectOneRow */

#endif  /* of _UNICODE_ODBC*/

.CM *-END-* code ----------------------------------------
.SP 2 
***********************************************************
