/******************************************************************************
**                                                                        
** Source File Name = sfetch.c  1.3                                      
**                                                                        
** Licensed Materials - Property of IBM                                   
**                                                                        
** (C) COPYRIGHT International Business Machines Corp. 1995, 1999
** All Rights Reserved.                                                   
**                                                                        
** US Government Users Restricted Rights - Use, duplication or            
** disclosure restricted by GSA ADP Schedule Contract with IBM Corp.      
**                                                                        
**                                                                        
**    PURPOSE :                                                           
**
** A modification of the CLI sample xfetch.c to demonstrate scrollable
** cursors. Demonstrates the use of the following types of fetch:
**      o SQL_FETCH_FIRST
**      o SQL_FETCH_LAST
**      o SQL_FETCH_NEXT
**      o SQL_FETCH_ABSOLUTE
**      o SQL_FETCH_RELATIVE
**      o SQL_FETCH_BOOKMARK
**
** The same set of SQLFetchScroll() statements are issued twice, once
** using column-wise binding, then once using row-wise binding.
**                                                                        
** For more information about these samples see the README file.
**
** For more information on programming in CLI see the:
**     - "Building CLI Applications" section of the Application Building Guide, and the
**     - CLI Guide and Reference.
**
** For more information on the SQL language see the SQL Reference.
**
******************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "sqlcli.h"
#include "sqlcli1.h"
#include "samputil.h"           /* Header file for CLI sample code */

/* For the Macintosh environment when generating 68K applications */
#ifdef DB268K
   /* Need to include ASLM for 68K applications */
   #include <LibraryManager.h>
#endif

#define MAX_STMT_LEN 255
#define ROWSET_SIZE 5

/*  Global Variables for user id and password.
 *  To keep samples simple, not a recommended practice.
 */
    SQLCHAR   server[SQL_MAX_DSN_LENGTH + 1];
    SQLCHAR   uid[MAX_UID_LENGTH + 1];
    SQLCHAR   pwd[MAX_PWD_LENGTH + 1];

/*******************************************************************
** main
*******************************************************************/
int
main( int argc, char * argv[] )
{
    SQLHENV         henv;
    SQLHDBC         hdbc;
    SQLHSTMT        hstmt;
    SQLRETURN       rc;
    SQLCHAR         stmt[] =
    "SELECT deptnumb, deptname, id, name FROM staff, org \
                     WHERE dept=deptnumb AND job = 'Mgr' \
                     ORDER BY name";

    /* Column-Wise */
    SQLINTEGER      deptnumb[ROWSET_SIZE];

    SQLCHAR         deptname[ROWSET_SIZE][15];
    SQLINTEGER      deptname_l[ROWSET_SIZE];

    SQLSMALLINT     id[ROWSET_SIZE];

    SQLCHAR         name[ROWSET_SIZE][10];
    SQLINTEGER      name_l[ROWSET_SIZE];


    /* New for sfetch.c, A variable to store the number of rows fetched */
    SQLUINTEGER     numrowsfetched;
    SQLUSMALLINT    rowStatus[ROWSET_SIZE];
    static char ROWSTATVALUE[][26] =  {  "SQL_ROW_SUCCESS", \
                                       "SQL_ROW_SUCCESS_WITH_INFO", \
                                       "SQL_ROW_ERROR", \
                                       "SQL_ROW_NOROW" };

    /* Row-Wise (Includes buffer for both column data and length) */
    struct {
        SQLINTEGER      deptnumb_l; /* length */
        SQLINTEGER      deptnumb; /* value  */
        SQLINTEGER      deptname_l;
        SQLCHAR         deptname[15];
        SQLINTEGER      id_l;
        SQLSMALLINT     id;
        SQLINTEGER      name_l;
        SQLCHAR         name[10];
    }               R[ROWSET_SIZE];

    SQLUSMALLINT    Row_Stat[ROWSET_SIZE];
    SQLUINTEGER     pcrow;
    int             i;

    /* New for sfetch.c */
    SQLINTEGER      abookmark[4];
    SQLINTEGER      outlen;

/* For the Macintosh environment when generating 68K applications */
#ifdef DB268K
   /* Before making any API calls for 68K environment, need to initialize the
      Library Manager */
   InitLibraryManager(0,kCurrentZone,kNormalMemory);
   atexit(CleanupLibraryManager);
#endif

    /* macro to initalize server, uid and pwd */
    INIT_UID_PWD;

    /* allocate an environment handle    */
    rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
    if (rc != SQL_SUCCESS)
        return (terminate(henv, rc));

    /* allocate a connect handle, and connect */
    rc = DBconnect(henv, &hdbc);
    CHECK_HANDLE( SQL_HANDLE_DBC, hdbc, rc );

/*--> SQLL1X62.SCRIPT */
    /*********************************************/
    /* Column-Wise Binding                       */
    /*********************************************/
    rc = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
    CHECK_HANDLE( SQL_HANDLE_DBC, hdbc, rc );

    /* Set the number of rows in the rowset */
    rc = SQLSetStmtAttr( hstmt,
                         SQL_ATTR_ROW_ARRAY_SIZE,
                         (SQLPOINTER) ROWSET_SIZE,
                         0); 
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );

    /* New for sfetch.c: */
    /* Set the SQL_ATTR_ROW_BIND_TYPE statement attribute */
    /* to use column-wise binding: */
    rc = SQLSetStmtAttr( hstmt,
                         SQL_ATTR_ROW_BIND_TYPE,
                         SQL_BIND_BY_COLUMN,
                         0);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );

    /* New for sfetch.c: */
    /* Set the SQL_ATTR_ROWS_FETCHED_PTR statement attribute to */
    /* point to the variable numrowsfetched: */
    rc = SQLSetStmtAttr( hstmt,
                         SQL_ATTR_ROWS_FETCHED_PTR,
                         &numrowsfetched,
                         0);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );

    /* New for sfetch.c: */
    /* Set the SQL_ATTR_CURSOR_TYPE statement attribute */
    rc = SQLSetStmtAttr( hstmt,
                         SQL_ATTR_CURSOR_TYPE, 
                         (SQLPOINTER) SQL_CURSOR_STATIC, 
                         0);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );

    /* New for sfetch.c: */
    /* Indicate that we will use bookmarks by setting the */
    /* SQL_ATTR_USE_BOOKMARKS statement attribute to SQL_UB_VARIABLE: */
    rc = SQLSetStmtAttr( hstmt,
                         SQL_ATTR_USE_BOOKMARKS,
                         (SQLPOINTER) SQL_UB_VARIABLE,
                         0);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );

    /* New for sfetch.c: */
    /* Point the statement handle to the row status array */
    rc = SQLSetStmtAttr( hstmt,
                         SQL_ATTR_ROW_STATUS_PTR,
                         (SQLPOINTER) rowStatus,
                         0);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );
/*<-- */

    rc = SQLExecDirect(hstmt, stmt, SQL_NTS);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );

    rc = SQLBindCol(hstmt, 1, SQL_C_LONG, (SQLPOINTER) deptnumb, 0, NULL);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );

    rc = SQLBindCol(hstmt, 2, SQL_C_CHAR, (SQLPOINTER) deptname, 15, deptname_l);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );

    rc = SQLBindCol(hstmt, 3, SQL_C_SSHORT, (SQLPOINTER) id, 0, NULL);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );

    rc = SQLBindCol(hstmt, 4, SQL_C_CHAR, (SQLPOINTER) name, 10, name_l);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );

    printf("\nUse Column-Wise Binding to demonstrate SQLFetchScroll():\n");
    printf("\nDEPTNUMB DEPTNAME         ID       NAME\n");
    printf("-------- -------------- -------- ---------\n");


    /* (1.) Fetch the first ROWSET_SIZE rows. */
    printf("*** (1.) SQLFetchScroll with SQL_FETCH_FIRST ");
    rc = SQLFetchScroll(hstmt, SQL_FETCH_FIRST, 0);
    /* Indicate how many rows were in the result set. */
    printf("(%i rows). ***\n", numrowsfetched);
    if (rc != SQL_NO_DATA && rc != SQL_SUCCESS)
        CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );
    for (i = 0; i < numrowsfetched; i++) {
            printf("%8ld %-14s %8d %-9s\n", deptnumb[i], deptname[i], id[i], name[i]);
    }
    /* Output the Row Status Array if the complete rowset was not returned. */
    if (numrowsfetched != ROWSET_SIZE) {
        printf("     Previous rowset was not full, here is the Row Status Array:\n");
        for (i = 0; i < ROWSET_SIZE; i++)
                printf("       Row Status Array[%i] = %s\n", i, ROWSTATVALUE[rowStatus[i]]);
    }


    /* (2.) Fetch the next ROWSET_SIZE rows. */
    printf("*** (2.) SQLFetchScroll with SQL_FETCH_NEXT ");
    rc = SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0);
    /* Indicate how many rows were in the result set. */
    printf("(%i rows). ***\n", numrowsfetched);
    if (rc != SQL_NO_DATA && rc != SQL_SUCCESS)
        CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );
    for (i = 0; i < numrowsfetched; i++) {
            printf("%8ld %-14s %8d %-9s\n", deptnumb[i], deptname[i], id[i], name[i]);
    }
    /* Output the Row Status Array if the complete rowset was not returned. */
    if (numrowsfetched != ROWSET_SIZE) {
        printf("     Previous rowset was not full, here is the Row Status Array:\n");
        for (i = 0; i < ROWSET_SIZE; i++)
                printf("       Row Status Array[%i] = %s\n", i, ROWSTATVALUE[rowStatus[i]]);
    }

    /* (3.) Fetch ROWSET_SIZE rows starting at row 3. */
    printf("*** (3.) SQLFetchScroll with SQL_FETCH_ABSOLUTE at row 3 ");
    rc = SQLFetchScroll(hstmt, SQL_FETCH_ABSOLUTE, 3);
    /* Indicate how many rows were in the result set. */
    printf("(%i rows). ***\n", numrowsfetched);
    if (rc != SQL_NO_DATA && rc != SQL_SUCCESS)
        CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );
    for (i = 0; i < numrowsfetched; i++) {
            printf("%8ld %-14s %8d %-9s\n", deptnumb[i], deptname[i], id[i], name[i]);
    }
    /* Output the Row Status Array if the complete rowset was not returned. */
    if (numrowsfetched != ROWSET_SIZE) {
        printf("     Previous rowset was not full, here is the Row Status Array:\n");
        for (i = 0; i < ROWSET_SIZE; i++)
                printf("       Row Status Array[%i] = %s\n", i, ROWSTATVALUE[rowStatus[i]]);
    }

    /* Set a bookmark to come back to later. */
    /* Will use SQLGetData() for column 0 to get the bookmark value. */
    printf("+++ Storing the 1st row of the previous result set as a bookmark. +++\n");

    /* Use SQLSetPos to position the row within the rowset. */
    rc = SQLSetPos(
               hstmt, 
               1, /* Position at the 1st row of the rowset. */
               SQL_POSITION, 
               SQL_LOCK_NO_CHANGE); /* Do not change the lock state. */

    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
        CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );

    /* The bookmark value is stored in column 0; get its value. */
    rc = SQLGetData(hstmt, 0, SQL_C_LONG, abookmark, 255, &outlen);
    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
        CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );

    /* Now that we have the bookmark value we must set it. */
    rc = SQLSetStmtAttr(
               hstmt,
               SQL_ATTR_FETCH_BOOKMARK_PTR, 
               (SQLPOINTER) abookmark, 
               0);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );


    /* (4.) Fetch the last ROWSET_SIZE rows. */
    printf("*** (4.) SQLFetchScroll with SQL_FETCH_LAST ");
    rc = SQLFetchScroll(hstmt, SQL_FETCH_LAST, 0);
    /* Indicate how many rows were in the result set. */
    printf("(%i rows). ***\n", numrowsfetched);
    if (rc != SQL_NO_DATA && rc != SQL_SUCCESS)
        CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );
    for (i = 0; i < numrowsfetched; i++) {
            printf("%8ld %-14s %8d %-9s\n", deptnumb[i], deptname[i], id[i], name[i]);
    }
    /* Output the Row Status Array if the complete rowset was not returned. */
    if (numrowsfetched != ROWSET_SIZE) {
        printf("     Previous rowset was not full, here is the Row Status Array:\n");
        for (i = 0; i < ROWSET_SIZE; i++)
                printf("       Row Status Array[%i] = %s\n", i, ROWSTATVALUE[rowStatus[i]]);
    }



    /* (5.) Fetch ROWSET_SIZE rows using SQL_FETCH_RELATIVE */
    printf("*** (5.) SQLFetchScroll with SQL_FETCH_RELATIVE at row -3 ");
    rc = SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, -3);
    /* Indicate how many rows were in the result set. */
    printf("(%i rows). ***\n", numrowsfetched);
    if (rc != SQL_NO_DATA && rc != SQL_SUCCESS)
        CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );
    for (i = 0; i < numrowsfetched; i++) {
            printf("%8ld %-14s %8d %-9s\n", deptnumb[i], deptname[i], id[i], name[i]);
    }
    /* Output the Row Status Array if the complete rowset was not returned. */
    if (numrowsfetched != ROWSET_SIZE) {
        printf("     Previous rowset was not full, here is the Row Status Array:\n");
        for (i = 0; i < ROWSET_SIZE; i++)
                printf("       Row Status Array[%i] = %s\n", i, ROWSTATVALUE[rowStatus[i]]);
    }


    /* (2. again) Fetch the next ROWSET_SIZE rows. */
    printf("*** (2. again) SQLFetchScroll with SQL_FETCH_NEXT ");
    rc = SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0);
    /* Indicate how many rows were in the result set. */
    printf("(%i rows). ***\n", numrowsfetched);
    if (rc != SQL_NO_DATA && rc != SQL_SUCCESS)
        CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );
    for (i = 0; i < numrowsfetched; i++) {
            printf("%8ld %-14s %8d %-9s\n", deptnumb[i], deptname[i], id[i], name[i]);
    }
    /* Output the Row Status Array if the complete rowset was not returned. */
    if (numrowsfetched != ROWSET_SIZE) {
        printf("     Previous rowset was not full, here is the Row Status Array:\n");
        for (i = 0; i < ROWSET_SIZE; i++)
                printf("       Row Status Array[%i] = %s\n", i, ROWSTATVALUE[rowStatus[i]]);
    }


    /* (6.) Fetch ROWSET_SIZE rows starting from the saved bookmark. */
    printf("*** (6.) SQLFetchScroll with SQL_FETCH_BOOKMARK offset 0 ");
    rc = SQLFetchScroll(hstmt, SQL_FETCH_BOOKMARK, 0);
    /* Indicate how many rows were in the result set. */
    printf("(%i rows). ***\n", numrowsfetched);
    if (rc != SQL_NO_DATA && rc != SQL_SUCCESS)
        CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );
    for (i = 0; i < numrowsfetched; i++) {
            printf("%8ld %-14s %8d %-9s\n", deptnumb[i], deptname[i], id[i], name[i]);
    }
    /* Output the Row Status Array if the complete rowset was not returned. */
    if (numrowsfetched != ROWSET_SIZE) {
        printf("     Previous rowset was not full, here is the Row Status Array:\n");
        for (i = 0; i < ROWSET_SIZE; i++)
                printf("       Row Status Array[%i] = %s\n", i, ROWSTATVALUE[rowStatus[i]]);
    }


    /* (6. again) Fetch ROWSET_SIZE rows starting from the saved bookmark. */
    printf("*** (6.again) SQLFetchScroll with SQL_FETCH_BOOKMARK offset 2 ");
    rc = SQLFetchScroll(hstmt, SQL_FETCH_BOOKMARK, 2);
    /* Indicate how many rows were in the result set. */
    printf("(%i rows). ***\n", numrowsfetched);
    if (rc != SQL_NO_DATA && rc != SQL_SUCCESS)
        CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );
    for (i = 0; i < numrowsfetched; i++) {
            printf("%8ld %-14s %8d %-9s\n", deptnumb[i], deptname[i], id[i], name[i]);
    }
    /* Output the Row Status Array if the complete rowset was not returned. */
    if (numrowsfetched != ROWSET_SIZE) {
        printf("     Previous rowset was not full, here is the Row Status Array:\n");
        for (i = 0; i < ROWSET_SIZE; i++)
                printf("       Row Status Array[%i] = %s\n", i, ROWSTATVALUE[rowStatus[i]]);
    }

    /* (7.) Use SQLFetch() which is the same as calling SQLFetchScroll() */
    /* with the FetchOrientation set to SQL_FETCH_NEXT. */
    printf("*** (7) Use SQLFetch() to fetch the next rowset ");
    rc = SQLFetch( hstmt );
    /* Indicate how many rows were in the result set. */
    printf("(%i rows). ***\n", numrowsfetched);
    if (rc != SQL_NO_DATA && rc != SQL_SUCCESS)
        CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );
    for (i = 0; i < numrowsfetched; i++) {
            printf("%8ld %-14s %8d %-9s\n", deptnumb[i], deptname[i], id[i], name[i]);
    }
    /* Output Row Status Array if the complete rowset was not returned. */
    if (numrowsfetched != ROWSET_SIZE) {
        printf("     Previous rowset was not full, here is the Row Status Array:\n");
        for (i = 0; i < ROWSET_SIZE; i++)
          printf("       Row Status Array[%i] = %s\n", i, ROWSTATVALUE[rowStatus[i]]);
    }


    /* Finished with column-wise binding, close the statement handle. */
    rc = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);



    /*********************************************/
    /* Row-Wise Binding               */
    /*********************************************/
    rc = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
    if (rc != SQL_SUCCESS)
        CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );

    /* Set maximum number of rows to receive with each extended fetch */
    rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER) ROWSET_SIZE, 0);
    if (rc != SQL_SUCCESS)
        CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );

    /*
     * Set vparam to size of one row, used as offset for each bindcol
     * rgbValue
     *
     * ie. &(R[0].deptnumb) + vparam = &(R[1].deptnum)
     */ 

    rc = SQLSetStmtAttr(
               hstmt,
               SQL_ATTR_ROW_BIND_TYPE,
               (SQLPOINTER) (sizeof(R) / ROWSET_SIZE),
               0); 

    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );


    /* New for sfetch.c: */
    /* Set the SQL_ATTR_ROWS_FETCHED_PTR statement attribute to */
    /* point to numrowsfetched so that the results are stored there. */
    rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, &numrowsfetched, 0);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );

    /* New for sfetch.c: */
    /* Set the SQL_ATTR_CURSOR_TYPE statement attribute. */
    rc = SQLSetStmtAttr(
                hstmt,
                SQL_ATTR_CURSOR_TYPE,
                (SQLPOINTER) SQL_CURSOR_STATIC,
                0);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );

    /* New for sfetch.c: */
    /* Indicate that bookmarks will be used by setting the */
    /* SQL_ATTR_USE_BOOKMARKS statement attribute to SQL_UB_VARIABLE. */
    rc = SQLSetStmtAttr(
                hstmt,
                SQL_ATTR_USE_BOOKMARKS,
                (SQLPOINTER) SQL_UB_VARIABLE,
                0);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );


    rc = SQLExecDirect(hstmt, stmt, SQL_NTS);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );

    rc = SQLBindCol(hstmt, 1, SQL_C_LONG, (SQLPOINTER) & R[0].deptnumb, 0,
                    &R[0].deptnumb_l);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );

    rc = SQLBindCol(hstmt, 2, SQL_C_CHAR, (SQLPOINTER) R[0].deptname, 15,
                    &R[0].deptname_l);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );

    rc = SQLBindCol(hstmt, 3, SQL_C_SSHORT, (SQLPOINTER) & R[0].id, 0,
                    &R[0].id_l);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );

    rc = SQLBindCol(hstmt, 4, SQL_C_CHAR, (SQLPOINTER) R[0].name, 10, &R[0].name_l);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );

    /* Fetch ROWSET_SIZE rows at a time, and display */
    printf("\nNow do same as above using Column-Wise Binding:\n");
    printf("\nDEPTNUMB DEPTNAME         ID       NAME\n");
    printf("-------- -------------- -------- ---------\n");

    /* (1.) Fetch the first ROWSET_SIZE rows. */
    /* Same as Column-wise binding above. Only the printf is different. */
    printf("*** (1.) SQLFetchScroll with SQL_FETCH_FIRST ");
    rc = SQLFetchScroll(hstmt, SQL_FETCH_FIRST, 0);
    /* Indicate how many rows were in the result set. */
    printf("(%i rows). ***\n", numrowsfetched);
    if (rc != SQL_NO_DATA && rc != SQL_SUCCESS)
        CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );
    for (i = 0; i < numrowsfetched; i++) {
            printf("%8ld %-14s %8d %-9s\n", R[i].deptnumb, R[i].deptname,
                   R[i].id, R[i].name);
    }
    /* Output the Row Status Array if the complete rowset was not returned. */
    if (numrowsfetched != ROWSET_SIZE) {
        printf("     Previous rowset was not full, here is the Row Status Array:\n");
        for (i = 0; i < ROWSET_SIZE; i++)
                printf("       Row Status Array[%i] = %s\n", i, ROWSTATVALUE[rowStatus[i]]);
    }

    /* (2.) Fetch the next ROWSET_SIZE rows. */
    /* Same as Column-wise binding above. Only the printf is different. */
    printf("*** (2.) SQLFetchScroll with SQL_FETCH_NEXT ");
    rc = SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0);
    /* Indicate how many rows were in the result set. */
    printf("(%i rows). ***\n", numrowsfetched);
    if (rc != SQL_NO_DATA && rc != SQL_SUCCESS)
        CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );
    for (i = 0; i < numrowsfetched; i++) {
            printf("%8ld %-14s %8d %-9s\n", R[i].deptnumb, R[i].deptname,
                   R[i].id, R[i].name);
    }
    /* Output the Row Status Array if the complete rowset was not returned. */
    if (numrowsfetched != ROWSET_SIZE) {
        printf("     Previous rowset was not full, here is the Row Status Array:\n");
        for (i = 0; i < ROWSET_SIZE; i++)
                printf("       Row Status Array[%i] = %s\n", i, ROWSTATVALUE[rowStatus[i]]);
    }


    /* (3.) Fetch ROWSET_SIZE rows starting at row 3. */
    /* Same as Column-wise binding above. Only the printf is different. */
    printf("*** (3.) SQLFetchScroll with SQL_FETCH_ABSOLUTE at row 3 ");
    rc = SQLFetchScroll(hstmt, SQL_FETCH_ABSOLUTE, 3);
    /* Indicate how many rows were in the result set. */
    printf("(%i rows). ***\n", numrowsfetched);
    if (rc != SQL_NO_DATA && rc != SQL_SUCCESS)
        CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );
    for (i = 0; i < numrowsfetched; i++) {
            printf("%8ld %-14s %8d %-9s\n", R[i].deptnumb, R[i].deptname,
                   R[i].id, R[i].name);
    }
    /* Output the Row Status Array if the complete rowset was not returned. */
    if (numrowsfetched != ROWSET_SIZE) {
        printf("     Previous rowset was not full, here is the Row Status Array:\n");
        for (i = 0; i < ROWSET_SIZE; i++)
                printf("       Row Status Array[%i] = %s\n", i, ROWSTATVALUE[rowStatus[i]]);
    }

    /* Set a bookmark to come back to later. */
    /* Will use SQLGetData() for column 0 to get the bookmark value. */
    /* Exactly the same as Column-wise binding above. */
    printf("+++ Storing the 1st row of the previous result set as a bookmark. +++\n");

    /* Use SQLSetPos to position the row within the rowset. */
    rc = SQLSetPos(
               hstmt,
               1, /* Position at the 1st row of the rowset. */
               SQL_POSITION,
               SQL_LOCK_NO_CHANGE); /* Do not change the lock state. */

    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
        CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );

    /* The bookmark value is stored in column 0; get its value. */
    rc = SQLGetData(hstmt, 0, SQL_C_LONG, abookmark, 255, &outlen);
    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
        CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );

    /* Now that we have the bookmark value we must set it. */
    rc = SQLSetStmtAttr(
               hstmt,
               SQL_ATTR_FETCH_BOOKMARK_PTR,
               (SQLPOINTER) abookmark,
               0);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );


    /* (4.) Fetch the last ROWSET_SIZE rows. */
    /* Same as Column-wise binding above. Only the printf is different. */
    printf("*** (4.) SQLFetchScroll with SQL_FETCH_LAST ");
    rc = SQLFetchScroll(hstmt, SQL_FETCH_LAST, 0);
    /* Indicate how many rows were in the result set. */
    printf("(%i rows). ***\n", numrowsfetched);
    if (rc != SQL_NO_DATA && rc != SQL_SUCCESS)
        CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );
    for (i = 0; i < numrowsfetched; i++) {
            printf("%8ld %-14s %8d %-9s\n",  R[i].deptnumb, R[i].deptname,
                   R[i].id, R[i].name);
    }
    /* Output the Row Status Array if the complete rowset was not returned. */
    if (numrowsfetched != ROWSET_SIZE) {
        printf("     Previous rowset was not full, here is the Row Status Array:\n");
        for (i = 0; i < ROWSET_SIZE; i++)
                printf("       Row Status Array[%i] = %s\n", i, ROWSTATVALUE[rowStatus[i]]);
    }


    /* (5.) Fetch ROWSET_SIZE rows using SQL_FETCH_RELATIVE */
    /* Same as Column-wise binding above. Only the printf is different. */
    printf("*** (5.) SQLFetchScroll with SQL_FETCH_RELATIVE at row -3 ");
    rc = SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, -3);
    /* Indicate how many rows were in the result set. */
    printf("(%i rows). ***\n", numrowsfetched);
    if (rc != SQL_NO_DATA && rc != SQL_SUCCESS)
        CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );
    for (i = 0; i < numrowsfetched; i++) {
            printf("%8ld %-14s %8d %-9s\n",  R[i].deptnumb, R[i].deptname,
                   R[i].id, R[i].name);
    }
    /* Output Row Status Array if the complete rowset was not returned. */
    if (numrowsfetched != ROWSET_SIZE) {
        printf("     Previous rowset was not full, here is the Row Status Array:\n");
        for (i = 0; i < ROWSET_SIZE; i++)
                printf("       Row Status Array[%i] = %s\n", i, ROWSTATVALUE[rowStatus[i]]);
    }


    /* (2. again) Fetch the next ROWSET_SIZE rows. */
    /* Same as Column-wise binding above. Only the printf is different. */
    printf("*** (2. again) SQLFetchScroll with SQL_FETCH_NEXT ");
    rc = SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0);
    /* Indicate how many rows were in the result set. */
    printf("(%i rows). ***\n", numrowsfetched);
    if (rc != SQL_NO_DATA && rc != SQL_SUCCESS)
        CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );
    for (i = 0; i < numrowsfetched; i++) {
            printf("%8ld %-14s %8d %-9s\n", R[i].deptnumb, R[i].deptname,
                   R[i].id, R[i].name);
    }
    /* Output Row Status Array if the complete rowset was not returned. */
    if (numrowsfetched != ROWSET_SIZE) {
        printf("     Previous rowset was not full, here is the Row Status Array:\n");
        for (i = 0; i < ROWSET_SIZE; i++)
                printf("       Row Status Array[%i] = %s\n", i, ROWSTATVALUE[rowStatus[i]]);
    }


    /* (6.) Fetch ROWSET_SIZE rows starting from the saved bookmark. */
    /* Same as Column-wise binding above. Only the rintf is different. */
    printf("*** (6.) SQLFetchScroll with SQL_FETCH_BOOKMARK offset 0 ");
    rc = SQLFetchScroll(hstmt, SQL_FETCH_BOOKMARK, 0);
    /* Indicate how many rows were in the result set. */
    printf("(%i rows). ***\n", numrowsfetched);
    if (rc != SQL_NO_DATA && rc != SQL_SUCCESS)
        CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );
    for (i = 0; i < numrowsfetched; i++) {
            printf("%8ld %-14s %8d %-9s\n", R[i].deptnumb, R[i].deptname,
                   R[i].id, R[i].name);
    }
    /* Output the Row Status Array if the complete rowset was not returned. */
    if (numrowsfetched != ROWSET_SIZE) {
        printf("     Previous rowset was not full, here is the Row Status Array:\n");
        for (i = 0; i < ROWSET_SIZE; i++)
                printf("       Row Status Array[%i] = %s\n", i, ROWSTATVALUE[rowStatus[i]]);
    }


    /* (6. again) Fetch ROWSET_SIZE rows starting from the saved bookmark. */
    /* Same as Column-wise binding above. Only the printf is different. */
    printf("*** (6.again) SQLFetchScroll with SQL_FETCH_BOOKMARK offset 2 ");
    rc = SQLFetchScroll(hstmt, SQL_FETCH_BOOKMARK, 2);
    /* Indicate how many rows were in the result set. */
    printf("(%i rows). ***\n", numrowsfetched);
    if (rc != SQL_NO_DATA && rc != SQL_SUCCESS)
        CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );
    for (i = 0; i < numrowsfetched; i++) {
            printf("%8ld %-14s %8d %-9s\n", R[i].deptnumb, R[i].deptname,
                   R[i].id, R[i].name);
    }
    /* Output the Row Status Array if the complete rowset was not returned. */
    if (numrowsfetched != ROWSET_SIZE) {
        printf("     Previous rowset was not full, here is the Row Status Array:\n");
        for (i = 0; i < ROWSET_SIZE; i++)
                printf("       Row Status Array[%i] = %s\n", i, ROWSTATVALUE[rowStatus[i]]);
    }

    /* (7.) Use SQLFetch() which is the same as calling SQLFetchScroll() */
    /* with the FetchOrientation set to SQL_FETCH_NEXT. */
    printf("*** (7) Use SQLFetch() to fetch the next rowset ");
    rc = SQLFetch( hstmt );
    /* Indicate how many rows were in the result set. */
    printf("(%i rows). ***\n", numrowsfetched);
    if (rc != SQL_NO_DATA && rc != SQL_SUCCESS)
        CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );
    for (i = 0; i < numrowsfetched; i++) {
            printf("%8ld %-14s %8d %-9s\n", R[i].deptnumb, R[i].deptname,
                   R[i].id, R[i].name);
    }
    /* Output the Row Status Array if the complete rowset was not returned. */
    if (numrowsfetched != ROWSET_SIZE) {
        printf("     Previous rowset was not full, here is the Row Status Array:\n");
        for (i = 0; i < ROWSET_SIZE; i++)
                printf("       Row Status Array[%i] = %s\n", i, ROWSTATVALUE[rowStatus[i]]);
    }

    /* Done. */

    /* Free handles, commit, exit */
    rc = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc );

    rc = SQLEndTran(SQL_HANDLE_ENV, henv, SQL_COMMIT);
    CHECK_HANDLE( SQL_HANDLE_DBC, hdbc, rc );

    printf("\n>Disconnecting .....\n");
    rc = SQLDisconnect(hdbc);
    CHECK_HANDLE( SQL_HANDLE_DBC, hdbc, rc );

    rc = SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
    CHECK_HANDLE( SQL_HANDLE_DBC, hdbc, rc );

    rc = SQLFreeHandle(SQL_HANDLE_ENV, henv);
    if (rc != SQL_SUCCESS)
        return (terminate(henv, rc));

    return (SQL_SUCCESS);
}                               /* end main */