/* odbc-test-09.c: Scrolling in a result set
 * This program compiled and run, without changes, on:
 *   Red Hat Linux 8.0, gcc 3.2, unixODBC 2.2.2
 *     ("gcc -Wall odbc-test-09.c -o odbc-test-09 -lodbc")
 *   Windows 2000 Professional, Microsoft Visual C++ 6.0
 *   Windows 2000 Professional, Borland C++ Builder 5.0
 * All of those with Mimer 9.1.3, and Mimer ODBC driver 9.something
 * Thomas Padron-McCarthy (Thomas.Padron-McCarthy@tech.oru.se)
 * October 7, 2003
 */

#include <stdlib.h>
#include <stdio.h>
#if defined(_MSDOS) || defined(_WIN32)
#include <windows.h>
#endif
#include "sqlext.h"

struct PersonRow {
  SQLINTEGER number;
  SQLCHAR name[6 + 1];
  SQLINTEGER number_size, name_size;
};

#define NR_ROWS 3

int main(void) {
  SQLHENV eh;
  SQLHDBC ch;
  SQLHSTMT sh;
  struct PersonRow persons[NR_ROWS];
  SQLUSMALLINT row_status[NR_ROWS];
  SQLUINTEGER rows_fetched;
  SQLRETURN rc;

  /* ODBC 2.x uses SQLAllocEnv, SQLAllocConnect and SQLAllocStmt.
   * ODBC 3.x uses SQLAllocHandle, with different arguments, for all of those.
   * First: Allocate environment handle.
   */
  if (SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &eh) != SQL_SUCCESS) {
    fprintf(stderr, "Kunde inte allokera en ODBC-omgivning.\n");
    return EXIT_FAILURE;
  }

  /* Set the ODBC version attribute,
   * so ODBC knows which ODBC version this application was written for.
   */
  if (SQLSetEnvAttr(eh,
		    SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3,
		    SQL_IS_INTEGER) != SQL_SUCCESS) {
    fprintf(stderr, "Failed to set ODBC version to SQL_OV_ODBC3.\n");
    return EXIT_FAILURE;
  }

  /* Allocate connection handle */
  if (SQLAllocHandle(SQL_HANDLE_DBC, eh, &ch) != SQL_SUCCESS) {
    fprintf(stderr, "Kunde inte allokera ett anslutningsobjekt.\n");
    return EXIT_FAILURE;
  }

  if (SQLConnect(ch, (SQLCHAR*)"demobasen", SQL_NTS,
		 (SQLCHAR*)"demouser", SQL_NTS,
		 (SQLCHAR*)"fnord", SQL_NTS) != SQL_SUCCESS) {
    fprintf(stderr, "Kunde inte ansluta till datakällan 'demobasen'.\n");
    return EXIT_FAILURE;
  }

  /* Allocate statement handles */
  if (SQLAllocHandle(SQL_HANDLE_STMT, ch, &sh) != SQL_SUCCESS) {
    fprintf(stderr, "Kunde inte allokera statement handle sh.\n");
    return EXIT_FAILURE;
  }

  /* Set these statement attributes BEFORE we execute the query */
  /* How big is a row struct? */
  SQLSetStmtAttr(sh, SQL_ATTR_ROW_BIND_TYPE, (SQLPOINTER)sizeof(struct PersonRow), 0); 
  /* How many rows shall we fetch at time? */
  SQLSetStmtAttr(sh, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)NR_ROWS, 0);
  /* The status array, which returns a status value for each fetched row */
  SQLSetStmtAttr(sh, SQL_ATTR_ROW_STATUS_PTR, (SQLPOINTER)row_status, 0);
  /* How many rows did we actually fetch? */
  SQLSetStmtAttr(sh, SQL_ATTR_ROWS_FETCHED_PTR, (SQLPOINTER)&rows_fetched, 0);
  /* Set the cursor "static", meaning that the result set doesn't change */
  SQLSetStmtAttr(sh, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0);

  SQLBindCol(sh, 1, SQL_C_SLONG, &persons[0].number, sizeof persons[0].number, &persons[0].number_size);
  SQLBindCol(sh, 2, SQL_C_CHAR, &persons[0].name, sizeof persons[0].name, &persons[0].name_size);

  if (SQLPrepare(sh, (SQLCHAR*)"select Nummer, Namn from Person", SQL_NTS)
      != SQL_SUCCESS) {
    fprintf(stderr, "SQLPrepare misslyckades.\n");
    return EXIT_FAILURE;
  }

  if (SQLExecute(sh) != SQL_SUCCESS) {
    fprintf(stderr, "Kunde inte köra frågan.\n");
    return EXIT_FAILURE;
  }

  while (   (rc = SQLFetchScroll(sh, SQL_FETCH_NEXT, 0)) == SQL_SUCCESS
	 || rc == SQL_SUCCESS_WITH_INFO) {
    int i;
    printf("De %d person(er) vi jobbar med just nu:\n", (int)rows_fetched);
    for (i = 0; i < rows_fetched; ++i) {
      printf("  Nummer %d, med namnet %s\n",
	     (int)persons[i].number, persons[i].name);
    }
  }

  SQLCloseCursor(sh);
  SQLFreeHandle(SQL_HANDLE_STMT, sh);
  SQLDisconnect(ch);
  SQLFreeHandle(SQL_HANDLE_DBC, ch);
  SQLFreeHandle(SQL_HANDLE_ENV, eh);

  return EXIT_SUCCESS;
} /* main */

