/****************************************************************************
** (c) Copyright IBM Corp. 2007 All rights reserved.
** 
** The following sample of source code ("Sample") is owned by International 
** Business Machines Corporation or one of its subsidiaries ("IBM") and is 
** copyrighted and licensed, not sold. You may use, copy, modify, and 
** distribute the Sample in any form without payment to IBM, for the purpose of 
** assisting you in the development of your applications.
** 
** The Sample code is provided to you on an "AS IS" basis, without warranty of 
** any kind. IBM HEREBY EXPRESSLY DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR 
** IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Some jurisdictions do 
** not allow for the exclusion or limitation of implied warranties, so the above 
** limitations or exclusions may not apply to you. IBM shall not be liable for 
** any damages you suffer as a result of using, copying, modifying or 
** distributing the Sample, even if IBM has been advised of the possibility of 
** such damages.
*****************************************************************************
**
** SOURCE FILE NAME: dbthrds.sqC
**
** SAMPLE: How to use multiple context APIs on Windows
**
**         This program uses the WIN32 threads APIs for thread creation and
**         management.
**
**         The program maintains a pool of contexts. A generate_work
**         function is executed from main(), and creates dynamic SQL
**         statements that are executed by worker threads. When a
**         context becomes available, a thread is created and dispatched
**         to do the specified work.
**
**         The work generated consists of statements to delete entries
**         from either the STAFF or EMPLOYEE tables of the SAMPLE database.
**
**         Compile and link with C compiler options for multi-threaded
**         applications supported by your platform.
**
**         Note:
**           On some environments, the output may appear garbled because
**           one thread process outputs information at the same time as
**           another process, thereby overwriting output strings. If this
**           is a concern, you can add a locking mechanism for the output
**           so only one process outputs at any one time.
**
** SQL STATEMENTS USED:
**         COMMIT
**         CONNECT
**         EXECUTE IMMEDIATE
**         ROLLBACK
**
**                           
*****************************************************************************
**
** For more information on the sample programs, see the README file.
**
** For information on developing embedded SQL applications see the Developing Embedded SQL Applications book.
**
** For information on using SQL statements, see the SQL Reference.
**
** For information on DB2 APIs, see the Administrative API Reference.
**
** For the latest information on programming, building, and running DB2
** applications, visit the DB2 Information Center:
**     http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/index.jsp
****************************************************************************/

#include <stdio.h>
#include <sqlenv.h>
#include <windows.h>

#if ((__cplusplus >= 199711L) && !defined DB2HP && !defined DB2AIX) || \
    (DB2LINUX && (__LP64__ || (__GNUC__ >= 3)) )
   #include <iostream>
   using namespace std;
#else
   #include <iostream.h>
#endif



#define check_expected(condition)                                  \
{                                                                  \
  if(!(condition))                                                 \
  {                                                                \
    cerr << __FILE__ << ":" << __LINE__ << " unexpected error: \"" \
         << #condition << "\" was false" << endl;                  \
    exit(1);                                                       \
  }                                                                \
}

#define CHECKERR(context, CE_STR, pStatus)              \
{                                                       \
  char buf[256];                                        \
  sprintf(buf, "Context nb.: %i\n%s", context, CE_STR); \
  if(check_error(buf, &sqlca) != 0)                     \
  {                                                     \
    *(pStatus) = sqlca.sqlcode;                         \
  }                                                     \
}

class Dbthrds
{
  public:
    void initialize(int argc, char *argv[]);
    void generate_work();
    void dispatch(struct work *work_item);
    friend DWORD WINAPI do_work(LPVOID args);
    void clean_up(struct work *work, int connect_done, int *pStatus);

    struct context *ctxlist; // list of contexts
    HANDLE *hThreads;        // thread handles stored in an array (used to
                             // join threads).  Every thread that is created
                             // gets a handle.
    int verbose;  // display verbose output
    int contexts; // size of context pool

  private:
    // user supplied arguments
    int numArgs;  // acceptable number of command line arguments
    int loops;    // amount of work for the client to create
    int commit;   // commit the work done
    char database[15];
    char username[15];
    char password[15];

    // for management of the context pool
    int contexts_free; // number of contexts that are currently free
    HANDLE hEvent;     // event handle
    HANDLE hMutex;     // mutex handle
};

// The function generate_work creates the following type of struct which
// is passed to each worker thread.  This struct tells the thread where
// to connect and what work to perform.
struct work
{
  char database[15]; // database for thread to connect to
  char username[15]; // username to supply during connection
  char password[15]; // password to supply during connection
  char *command;     // dynamic SQL statement to execute

  int context;       // context to use for connection
  Dbthrds *pDbthrds;
};

// the context pool consists of an array of 'struct context' types
struct context
{
  void *ctx; // used by the sqle* APIs
  int free;  // whether the context is free or in use
};

// Helper method to help check for errors.
int check_error(char eString[], struct sqlca *caPointer);

int main(int argc, char* argv[])
{
  Dbthrds dbthrds;

  cout << endl << "How to use multiple context APIs" << endl << endl << endl;

  // set initial parameters, parse command line arguments
  dbthrds.initialize(argc, argv);

  // begin generating work
  dbthrds.generate_work();

  // wait for all threads to complete before exiting
  WaitForMultipleObjects(dbthrds.contexts,  // maximum number of running
                                            // threads
                         dbthrds.hThreads,  // array of all thread handles
                         TRUE,              // wait for ALL threads to finish
                         INFINITE);         // wait forever for threads to
                                            // finish

  if(dbthrds.verbose)
  {
    cout << "all workers started, exiting main\n" << flush;
  }

  // free memory we allocated earlier
  delete [] dbthrds.hThreads;
  delete [] dbthrds.ctxlist;

  return 0;
} // Main

// Helper function that checks for errors.
int check_error(char eString[], struct sqlca *caPointer)
{
  char eBuffer[1024];
  char sBuffer[1024];
  char message[1024];
  char messToken[1024];
  short rc, Erc;
  int status = 0;

  if(caPointer->sqlcode != 0 && caPointer->sqlcode != 100
       && caPointer->sqlcode != -438 && caPointer->sqlcode != -532)
  {
    strcpy(message, "");

    sprintf(messToken, "--- error report ---\n");
    strcat(message, messToken);

    sprintf(messToken, "ERROR occurred : %s.\nSQLCODE : %ld\n",
            eString, caPointer->sqlcode);
    strcat(message, messToken);

    // get sqlstate message
    rc = sqlogstt(sBuffer, 1024, 80, caPointer->sqlstate);

    // get error message API called
    Erc = sqlaintp(eBuffer, 1024, 80, caPointer);

    // return code is the length of the eBuffer string
    if(Erc > 0)
    {
      sprintf(messToken, "%s", eBuffer);
      strcat(message, messToken);
    }

    if(caPointer->sqlcode < 0)
    {
      if(rc == 0)
      {
        sprintf(messToken, "\n%s", sBuffer);
        strcat(message, messToken);
      }
      sprintf(messToken, "--- end error report ---\n");
      strcat(message, messToken);

      cout << endl << message << endl;

      return 1;
    }
    else
    {
      // errorCode is just a warning message
      if(rc == 0)
      {
        sprintf(messToken, "\n%s", sBuffer);
        strcat(message, messToken);
      }
      sprintf(messToken, "--- end error report ---\n");
      strcat(message, messToken);

      sprintf(messToken, "WARNING - CONTINUING PROGRAM WITH WARNINGS!\n");
      strcat(message, messToken);

      cout << endl << message << endl;
    }
  }
  return 0;
} // Check_error

// The following function initializes the program state. It creates a
// context pool, marks all contexts as being free, and creates a new event
// and a new mutex to control critical sections for when threads become
// active.
void Dbthrds::initialize(int argc, char *argv[])
{
  int rc;
  struct sqlca sqlca;
  char buf[256];

  // default values for command line arguments (if none are specified)
  numArgs = 8;
  loops = 15;     // amount of work for the client to create
  contexts = 8;   // size of context pool
  commit = 0;     // commit the work done
  verbose = 1;    // display verbose output

  strcpy(database, "sample");
  strcpy(username, "");
  strcpy(password, "");

  // either all parameters were specified or no parameters were specified,
  // otherwise the syntax is incorrect
  if(!(argc == numArgs) && !(argc == 1))
  {
    cout << "usage: "
         << argv[0] << endl
         << "\t <Database name>" << endl
         << "\t <Username>" << endl
         << "\t <Password>" << endl
         << "\t <Loops>" << endl
         << "\t <Contexts>" << endl
         << "\t <Verbose> (0-1)" << endl
         << "\t <Commit> (0-1)" << endl;
    exit(1);
  }
  if(argc == numArgs)
  {
    strcpy(database, argv[1]);
    strcpy(username, argv[2]);
    strcpy(password, argv[3]);
    loops = atoi(argv[4]);
    contexts = atoi(argv[5]);
    verbose = atoi(argv[6]);
    commit = atoi(argv[7]);
  }

  cout << "Database: " << database << endl;
  cout << "Username: " << username << endl;
  cout << "Password: " << password << endl;
  cout << "Loops: " << loops << endl;
  cout << "Contexts: " << contexts << endl;
  cout << "Verbose: " << verbose << endl;
  cout << "Commit: " << commit << endl;

  contexts_free = contexts;
  if(loops < contexts)
  {
    cerr << "You need to specify more loops than contexts." << endl;
    exit(1);
  }
  check_expected(loops >= contexts);

  hThreads = new HANDLE[contexts];
  check_expected(hThreads != NULL);

  ctxlist = new context[contexts];
  check_expected(ctxlist != NULL);

  sqleSetTypeCtx(SQL_CTX_MULTI_MANUAL);

  if(verbose)
  {
    sprintf(buf, "creating context pool of size %i\n", contexts);
    cout << buf << flush;
  }

  for(int i = 0; i < contexts; i++)
  {
    rc = sqleBeginCtx(&ctxlist[i].ctx, SQL_CTX_CREATE_ONLY, NULL, &sqlca);
    check_expected(rc == 0 && sqlca.sqlcode == 0);
    ctxlist[i].free = 1;
  }

  // create a mutex
  hMutex = CreateMutex(NULL, FALSE, "cond_m");
  check_expected(hMutex != 0);

  // create an event
  hEvent = CreateEvent(NULL, FALSE, FALSE, "cond");
  check_expected(hEvent != 0);

  return;
} // Initialize

// The following function creates a new work struct and populates it with
// data.  It randomly generates a valid SQL statement, places this statement
// into the newly created struct, and dispatches the struct to the dispatch
// function so a thread can be created and start doing work.
void Dbthrds::generate_work()
{
  int i, empno;
  struct work *work_item;
  char buf[256];

  char *delete_str1 = "DELETE FROM STAFF WHERE ID=%i";
  char *delete_str2 = "DELETE FROM EMPLOYEE WHERE EMPNO='%06i'";

  // Generate work to be done in each thread.
  srand(GetCurrentProcessId());
  for(i = 0; i < loops; i++)
  {
    work_item = new work;

    strcpy(work_item->database, database);
    strcpy(work_item->username, username);
    strcpy(work_item->password, password);

    // the employee numbers are in the 10-350 range and are multiples of 10
    empno =((rand() % 1000) + 1) * 10;
    sprintf(buf, i % 2 ? delete_str1 : delete_str2, empno);

    work_item->command = strdup(buf);
    work_item->pDbthrds = this;

    dispatch(work_item);
  }

  return;
} // Generate_work

// The following function creates a thread to perform work specified in
// the work_item struct.  Since this function can be called while multiple
// threads are executing, a mutex and an event are both used to control the
// critical section.  As soon as at least one context becomes free, this
// function claims a context and creates a thread for it.
void Dbthrds::dispatch(struct work *work_item)
{
  int rc, ctx;
  DWORD dwWorkerThreadId;
  char buf[256];

  OpenMutex(NULL, TRUE, "cond_m");
  check_expected(hMutex != NULL);

  while(!contexts_free)
  {
    OpenEvent(NULL, TRUE, "cond");
    check_expected(hEvent != 0);
  }
  SetEvent(hEvent);

  // there is at least one free context at this point, find one
  for(ctx = 0; ctx < contexts; ctx++)
  {
    if(ctxlist[ctx].free)
    {
      break;
    }
  }
  ctxlist[ctx].free = 0;
  contexts_free--;

  ReleaseMutex(hMutex);
  check_expected(hMutex != 0);

  work_item->context = ctx;

  if(verbose)
  {
    sprintf(buf,
            "creating thread on context %i for SQL statement: \n\t\"%s\"\n",
            ctx,
            work_item->command);
    cout << buf << flush;
  }

  hThreads[ctx] =
    CreateThread(NULL, // default security attributes
                 0,    // default stack size
                 (LPTHREAD_START_ROUTINE)do_work, // thread "main" procedure
                 (LPVOID)work_item,  // argument: work_item
                 0,    // no special flags
                 &dwWorkerThreadId); // out: thread id of worker
  check_expected(hThreads[ctx] != 0);

  return;
} // Dispatch

// The following function is executed by newly created threads.  All of the
// actual work this sample does is performed within this function.  This
// function attaches to a context, connects to the database, executes the
// statement provided to it by the generate_work function, and finally calls
// the clean_up function before returning.
DWORD WINAPI do_work(LPVOID args)
{
  EXEC SQL BEGIN DECLARE SECTION;
    char dbname[15];
    char user[15];
    char pswd[15];
    char statement[256];
  EXEC SQL END DECLARE SECTION;

  int rc, status = 0;
  char buf[256];
  struct sqlca sqlca;
  struct work *work_item = (struct work *)args;

  strcpy(dbname, work_item->database);
  strcpy(user, work_item->username);
  strcpy(pswd, work_item->password);

  if(work_item->pDbthrds->verbose)
  {
    sprintf(buf, "%i: sqleAttachToCtx\n", work_item->context);
    cout << buf << flush;
  }
  rc = sqleAttachToCtx(work_item->pDbthrds->ctxlist[work_item->context].ctx,
                       NULL,
                       &sqlca);
  check_expected(rc == 0 && sqlca.sqlcode == 0);

  if(work_item->pDbthrds->verbose)
  {
    sprintf(buf, "%i: CONNECT TO %s\n", work_item->context, dbname);
    cout << buf << flush;
  }

  if(strlen(user) == 0)
  {
    EXEC SQL CONNECT TO :dbname;
  }
  else
  {
    EXEC SQL CONNECT TO :dbname USER :user USING :pswd;
  }
  CHECKERR(work_item->context, "CONNECT TO DATABASE", &status);
  if(sqlca.sqlcode != 0)
  {
    work_item->pDbthrds->clean_up(work_item, 0, &status);
  }
  else
  {
    strcpy(statement, work_item->command);

    if(work_item->pDbthrds->verbose)
    {
      sprintf(buf, "%i: EXECUTE \"%s\"\n", work_item->context, statement);
      cout << buf << flush;
    }

    EXEC SQL EXECUTE IMMEDIATE :statement;
    CHECKERR(work_item->context, "EXECUTE IMMEDIATE", &status);

    work_item->pDbthrds->clean_up(work_item, 1, &status);
  }

  return status;
} // Do_work

// The following function performs clean up.  It is called after a thread
// is done doing its work.  It either commits the work or performs a
// rollback, depending on if commit was specified as a command line option.
// The connection to the database is then terminated and the thread detaches
// from the context.  The context is also marked as being free so that other
// threads may connect to the context.
void Dbthrds::clean_up(struct work *work_item, int connect_done, int *pStatus)
{
  int rc;
  struct sqlca sqlca;
  char buf[256];

  if(connect_done)
  {
    if(commit)
    {
      if(verbose)
      {
        sprintf(buf, "%i: COMMIT\n", work_item->context);
        cout << buf << flush;
      }

      EXEC SQL COMMIT;
      CHECKERR(work_item->context, "COMMIT", pStatus);
    }
    else
    {
      if(verbose)
      {
        sprintf(buf, "%i: ROLLBACK\n", work_item->context);
        cout << buf << flush;
      }

      EXEC SQL ROLLBACK;
      CHECKERR(work_item->context, "ROLLBACK", pStatus);
    }

    if(verbose)
    {
      sprintf(buf, "%i: CONNECT RESET\n", work_item->context);
      cout << buf << flush;
    }

    EXEC SQL CONNECT RESET;
    CHECKERR(work_item->context, "CONNECT RESET", pStatus)}

  if(verbose)
  {
    sprintf(buf, "%i: sqleDetachFromCtx\n", work_item->context);
    cout << buf << flush;
  }
  rc = sqleDetachFromCtx(ctxlist[work_item->context].ctx, NULL, &sqlca);
  check_expected(rc == 0 && sqlca.sqlcode == 0);

  OpenMutex(NULL, TRUE, "cond_m");
  check_expected(hMutex != NULL);

  if(verbose)
  {
    sprintf(buf, "%i: marking context free\n", work_item->context);
    cout << buf << flush;
  }
  ctxlist[work_item->context].free = 1;
  contexts_free++;

  SetEvent(hEvent);
  check_expected(hEvent != 0);
  ReleaseMutex(hMutex);
  check_expected(hMutex != 0);

  delete work_item->command;
  delete work_item;

  return;
} // Clean_up