/* odbc-test-10.c: The same program, with some convenient modifications
 * This program compiled and run, without changes, on:
 *   Red Hat Linux 8.0, gcc 3.2, unixODBC 2.2.2
 *     ("gcc -Wall odbc-test-10.c -o odbc-test-10 -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>
#include <stdarg.h>
#if defined(_MSDOS) || defined(_WIN32)
#include <windows.h>
#endif
#include "sqlext.h"

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

void show_odbc_diagnostics(SQLHSTMT sh) {
  SQLCHAR msg[SQL_MAX_MESSAGE_LENGTH + 1];
  SQLCHAR sqlstate[5 + 1];
  SQLSMALLINT msglen, msgno;
  SQLINTEGER nativeerror;
 
  fprintf(stderr, "Ett eller flera Felmeddelanden från ODBC:\n");
  msgno = 0;
  while (SQLGetDiagRec(SQL_HANDLE_STMT,
                       sh,
                       ++msgno,
                       sqlstate,
                       &nativeerror,
                       msg,
                       sizeof msg,
                       &msglen) == SQL_SUCCESS) {
    msg[msglen] = '\0';
    fflush(stdout);
    fflush(stderr);
    fprintf(stderr, "  Diagnostiskt ODBC-meddelande nummer %d:\n", (int)msgno);
    fprintf(stderr, "    SQLSTATE: '%s'\n", sqlstate);
    fprintf(stderr, "    Native-fel: %d\n", (int)nativeerror);
    fprintf(stderr, "    Meddelande: '%s'\n", msg);
  } /* while more messages */
} /* show_odbc_diagnostics */

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

void fatal_error(char* format_string, ...) {
    va_list argp;
#ifdef __BORLANDC__
    int c;
#endif

    fflush(stdout);
    fflush(stderr);

    fprintf(stderr, "*** Det har uppstått ett fel:\n");
    fflush(stderr);

    va_start(argp, format_string);
    vfprintf(stderr, format_string, argp);
    va_end(argp);
    fprintf(stderr, "\n");

#ifdef __BORLANDC__
	/* Borland C++ Builder 5.0 doesn't wait for a keypress.
	 * It just closes the text window with the output,
	 * so we don't have time to see it.
	 */
    fprintf(stderr, "Tryck på RETUR för att avsluta programmet: ");
    while ((c = getchar() != '\n') && c != EOF)
      ;
#endif

    fprintf(stderr, "Adjö. Vi beklagar det inträffade..\n");
    fflush(stdout);
    fflush(stderr);

    exit(EXIT_FAILURE);
} /* fatal_error */

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

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;
#ifdef __BORLANDC__
  int c;
#endif

  /* 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)
    fatal_error("Kunde inte allokera en ODBC-omgivning.");

  /* 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)
    fatal_error("Failed to set ODBC version to SQL_OV_ODBC3.");

  /* Allocate connection handle */
  if (SQLAllocHandle(SQL_HANDLE_DBC, eh, &ch) != SQL_SUCCESS)
    fatal_error("Kunde inte allokera ett anslutningsobjekt.");

  if (SQLConnect(ch, (SQLCHAR*)"demobasen", SQL_NTS,
		 (SQLCHAR*)"demouser", SQL_NTS,
		 (SQLCHAR*)"fnord", SQL_NTS) != SQL_SUCCESS)
    fatal_error("Kunde inte ansluta till datakällan 'demobasen'.");

  /* Allocate statement handles */
  if (SQLAllocHandle(SQL_HANDLE_STMT, ch, &sh) != SQL_SUCCESS)
    fatal_error("Kunde inte allokera statement handle sh.");

  /* 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)
    fatal_error("SQLPrepare misslyckades.");

  if (SQLExecute(sh) != SQL_SUCCESS)
    fatal_error("Kunde inte köra frågan.");

  while (   (rc = SQLFetchScroll(sh, SQL_FETCH_NEXT, 0)) == SQL_SUCCESS
	 || rc == SQL_SUCCESS_WITH_INFO) {
    unsigned 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);

#ifdef __BORLANDC__
    fprintf(stderr, "Tryck på RETUR för att avsluta programmet: ");
    while ((c = getchar() != '\n') && c != EOF)
      ;
#endif

  return EXIT_SUCCESS;
} /* main */

