/******************************************************************************
**
** Source File Name = largevol.sqc
**
** Licensed Materials - Property of IBM
**
** (C) COPYRIGHT International Business Machines Corp. 1997
** All Rights Reserved.
**
** US Government Users Restricted Rights - Use, duplication or
** disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
**
**
** PURPOSE: This sample program demonstrates the extracting large
**          volumes of data from several separate queries, one at
**          each node. Each query generates the local result set
**          that can be redirected to a file and merged into a single
**          result set. (On AIX, you can use a advantage of NFS
**          (Network File System) to automate such merging.)
**
**    EXTERNAL DEPENDENCIES :
**       - Ensure existence of database for precompile purposes.
**       - Precompile with the SQL precompiler (PREP in DB2)
**       - Bind to a database (BIND in DB2)
**       - Compile and link with the IBM Cset++ compiler (AIX and OS/2)
**         or the Microsoft Visual C++ compiler (Windows) 
**         or the compiler supported on your platform.
**
*******************************************************************************/

#define _POSIX_SOURCE
#define INCL_32

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sqlenv.h>
#include <errno.h>
#include <sys/access.h>
#include <sys/flock.h>
#include <unistd.h>

#define BUF_SIZE 1500000  /* Local buffer to store the fetched records */
#define MAX_RECORD_SIZE 80 /* >= size of one written record */

int main(int argc, char *argv[]) {
    
    EXEC SQL INCLUDE SQLCA;
    EXEC SQL BEGIN DECLARE SECTION;
       char dbname[10];  /* Database name (argument of the program) */
       char userid[9];
       char passwd[19];
       char first_name[21];
       char last_name[21];
       char job_code[11];
    EXEC SQL END DECLARE SECTION;

       struct flock unlock ;  /* structures and variables for handling */
       struct flock lock ;  /* the NFS locking mechanism */
       int lock_command ; 
       int lock_rc ; 
       int iFileHandle ;  /* output file */
       int iOpenOptions = 0 ; 
       int iPermissions ; 
       char * file_buf ;  /* pointer to the buffer where the fetched
                             records are accumulated */
       char * write_ptr ;  /* position where the next record is written */
       int buffer_len = 0 ;  /* length of used portion of the buffer */
    
    /* Initialization */

       lock.l_type = F_WRLCK;  /* An exclusive write lock request */
       lock.l_start = 0;  /* To lock the entire file */
       lock.l_whence = SEEK_SET;
       lock.l_len = 0;
       unlock.l_type = F_UNLCK;  /* An release lock request */
       unlock.l_start = 0;  /* To unlock the entire file */
       unlock.l_whence = SEEK_SET;
       unlock.l_len = 0;
       lock_command = F_SETLKW;  /* Set the lock */
       iOpenOptions = O_CREAT;  /* Create the file if not exist */
       iOpenOptions |= O_WRONLY;  /* Open for writing only */
	
    /* Connect to the database */

       if (argc == 3) {
          strcpy( dbname, argv[2] ); /* get database name from the argument */
          EXEC SQL CONNECT TO :dbname IN SHARE MODE ;
          if ( SQLCODE != 0 ) {
             printf( "Error: CONNECT TO the database failed. SQLCODE = %ld\n",
	              SQLCODE );
	      exit(1);
	  }
       }
       else if ( argc == 5 ) {
         strcpy( dbname, argv[2] ); /* get  database name from the argument */
	 strcpy (userid, argv[3]);
	 strcpy (passwd, argv[4]);
	 EXEC SQL CONNECT TO :dbname IN SHARE MODE USER :userid USING :passwd;
	 if ( SQLCODE != 0 ) {
	    printf( "Error: CONNECT TO the database failed. SQLCODE = %ld\n",
	            SQLCODE );
	      exit( 1 );
         }
       }
       else {
	   printf ("\nUSAGE: largevol txt_file database [userid passwd]\n\n");
	   exit( 1 ) ;
       } /* endif */
	
      /* Open the input file with the specified access permissions */
    
     if ( ( iFileHandle = open(argv[1], iOpenOptions, 0666 ) ) == -1 ) {
       printf( "Error: Could not open %s.\n", argv[2] ) ;
       exit( 2 ) ;
    }
    
    /* Set up error and end of table escapes */
    
    EXEC SQL WHENEVER SQLERROR GO TO ext ;
    EXEC SQL WHENEVER NOT FOUND GO TO cls ;
    
    /* Declare and open the cursor */
    
    EXEC SQL DECLARE c1 CURSOR FOR
             SELECT firstnme, lastname, job FROM employee
             WHERE workdept IS NOT NULL
             AND NODENUMBER(lastname) = CURRENT NODE;
    EXEC SQL OPEN c1 ; 
    
    /* Set up the temporary buffer for storing the fetched result */
    
    if ( ( file_buf = ( char * ) malloc( BUF_SIZE ) ) == NULL ) {
       printf( "Error: Allocation of buffer failed.\n" ) ;
       exit( 3 ) ;
    }
    memset( file_buf, 0, BUF_SIZE ) ; /* reset the buffer */
    buffer_len = 0 ;  /* reset the buffer length */
    write_ptr = file_buf ;  /* reset the write pointer */
    /* For each fetched record perform the following    */
    /*  - insert it into the buffer following the       */
    /*    previously stored record                      */
    /*  - check if there is still enough space in  the  */
    /*    buffer for the next record and lock/write/    */
    /*    unlock the file and initialize the buffer     */
    /*    if not                                        */
    
    do {
       EXEC SQL FETCH c1 INTO :first_name, :last_name, :job_code;
        buffer_len += sprintf( write_ptr, "%s %s %s\n", 
                               first_name, last_name, job_code ); 
        buffer_len = strlen( file_buf ) ;
       /* Write the content of the buffer to the file if */
       /* the buffer reaches the limit                   */
       if ( buffer_len >= ( BUF_SIZE - MAX_RECORD_SIZE ) ) {
        /*  get excl. write lock */
        lock_rc = fcntl( iFileHandle, lock_command, &lock );
		 if ( lock_rc != 0 ) goto file_lock_err;
		 /*  position at the end of file */
		 lock_rc = lseek( iFileHandle, 0, SEEK_END ); 
		if ( lock_rc < 0 ) goto file_seek_err;
		/* write the buffer */
		lock_rc = write( iFileHandle, 
                               ( void * ) file_buf, buffer_len );
		if ( lock_rc < 0 ) goto file_write_err; 
		  /* release the lock */
		 lock_rc = fcntl( iFileHandle, lock_command, &unlock );
		 if ( lock_rc != 0 ) goto file_unlock_err;
		 file_buf[0] = '\0' ;  /* reset the buffer */
		  buffer_len = 0 ;  /* reset the buffer length */
		  write_ptr = file_buf ;  /* reset the write pointer */
       }
       else {
          write_ptr = file_buf + buffer_len ;  /* next write position */
       }
    } while (1) ;
    
cls:
    /* Write the last piece of data out to the file */
    if (buffer_len > 0) {
       lock_rc = fcntl(iFileHandle, lock_command, &lock);
       if (lock_rc != 0) goto file_lock_err;
      lock_rc = lseek(iFileHandle, 0, SEEK_END);
       if (lock_rc < 0) goto file_seek_err;
       lock_rc = write(iFileHandle, (void *)file_buf, buffer_len);
       if (lock_rc < 0) goto file_write_err;
       lock_rc = fcntl(iFileHandle, lock_command, &unlock);
      if (lock_rc != 0) goto file_unlock_err;
    }
    free(file_buf);
	close(iFileHandle);
    EXEC SQL CLOSE c1;
    exit (0);
 ext:
    if ( SQLCODE != 0 )
       printf( "Error:  SQLCODE = %ld.\n", SQLCODE );
    EXEC SQL WHENEVER SQLERROR CONTINUE;
    EXEC SQL CONNECT RESET;
    if ( SQLCODE != 0 ) {
       printf( "CONNECT RESET Error:  SQLCODE = %ld\n", SQLCODE );
       exit(4);
    }
    exit (5);
 file_lock_err:
    printf("Error: file lock error = %ld.\n",lock_rc); 
    /* unconditional unlock of the file */
    fcntl(iFileHandle, lock_command, &unlock); 
    exit(6);
file_seek_err:
    printf("Error: file seek error = %ld.\n",lock_rc);
    fcntl(iFileHandle, lock_command, &unlock);  /* unconditional unlock of the file */
   exit(7);
file_write_err: 
    printf("Error: file write error = %ld.\n",lock_rc); 
    fcntl(iFileHandle, lock_command, &unlock);  /* unconditional unlock of the file */
    exit(8);
file_unlock_err: 
    printf("Error: file unlock error = %ld.\n",lock_rc);
   fcntl(iFileHandle, lock_command, &unlock);  /* unconditional unlock of the file */
    exit(9);
}