/*******************************************************************************
**
** Source File Name = utilemb.sqc 
**
** Licensed Materials - Property of IBM
**
** (C) COPYRIGHT International Business Machines Corp. 1995, 2000
** All Rights Reserved.
**
** US Government Users Restricted Rights - Use, duplication or
** disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
**
**
**    PURPOSE :
**    - contains various functions, used by most other samples:
**
**    1. SQL_CHECK section
**
**        1.1 - SqlInfoPrint - prints on the screen everything that
**                             goes unexpected.
**        1.2 - TransRollback - rolls back the transaction
**
**     2. non SQL_CHECK section
**
**          2.1 - CmdLineArgsCheck1 - checks the command line arguments, ver.1
**          2.2 - CmdLineArgsCheck2 - checks the command line arguments, ver.2
**          2.3 - EmbAppInit - connects to the database
**          2.4 - EmbAppTerm - disconnects from the database
**
** For more information about these samples see the README file.
**
** For more information on programming in C, see the:
**   -  "Programming in C and C++" section of the Application Development Guide
** For more information on Building C Applications, see the:
**   -  "Building C Applications" section of the Application Building Guide.
**
** For more information on the SQL language see the SQL Reference.
**
*******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <sql.h>
#include <sqlenv.h>
#include <sqlda.h>
#include <sqlca.h>
#include <string.h>
#include <ctype.h>
#include "utilemb.h"


/*#############################################################################
**    1. SQL_CHECK section
**
**        1.1 - SqlInfoPrint - prints on the screen everything that
**                             goes unexpected.
**        1.2 - TransRollback - rolls back the transaction
#############################################################################*/

/******************************************************************************
**        1.1 - SqlInfoPrint - prints on the screen everything that
**                             goes unexpected.
******************************************************************************/
int SqlInfoPrint( char *         appMsg, 
                  struct sqlca * pSqlca,
                  int            line,      
                  char *         file )
{   int   rc = 0;

    char  sqlInfo[1024];
    char  sqlInfoToken[1024];

    char  sqlstateMsg[1024];
    char  errorMsg[1024];


    if (pSqlca->sqlcode != 0 && pSqlca->sqlcode != 100)
    {   strcpy(sqlInfo, "");

        if( pSqlca->sqlcode < 0)
        {   sprintf( sqlInfoToken, "\n---- error report ----\n");
            strcat( sqlInfo, sqlInfoToken);
        } 
        else 
        {   sprintf( sqlInfoToken, "\n---- warning report ----\n");
            strcat( sqlInfo, sqlInfoToken);
        } /* endif */

        sprintf( sqlInfoToken, "  app. message      = %s\n", appMsg);
        strcat( sqlInfo, sqlInfoToken);	
        sprintf( sqlInfoToken, "  line              = %d\n", line);
        strcat( sqlInfo, sqlInfoToken);	
        sprintf( sqlInfoToken, "  file              = %s\n", file);
        strcat( sqlInfo, sqlInfoToken);	
        sprintf( sqlInfoToken, "  SQLCODE           = %ld\n", pSqlca->sqlcode);
        strcat( sqlInfo, sqlInfoToken);		
 
        /* GET ERROR MESSAGE */
        rc = sqlaintp( errorMsg, 1024, 80, pSqlca);
        /* return code is the length of the errorMsg string */	
        if( rc > 0)
        {   sprintf( sqlInfoToken, "%s\n", errorMsg);
            strcat( sqlInfo, sqlInfoToken);
        } 
 
        /* get SQLSTATE message */
        rc = sqlogstt( sqlstateMsg, 1024, 80, pSqlca->sqlstate);
        if (rc == 0)
        {   sprintf( sqlInfoToken, "%s\n", sqlstateMsg);
            strcat( sqlInfo, sqlInfoToken);
        }  	


        if( pSqlca->sqlcode < 0)
        {   sprintf( sqlInfoToken, "--- end error report ---\n");
            strcat( sqlInfo, sqlInfoToken);

            printf("%s", sqlInfo);
            return 1;
        } 
        else 
        {   sprintf( sqlInfoToken, "--- end warning report ---\n");
            strcat( sqlInfo, sqlInfoToken);
 
            printf("%s", sqlInfo);
            return 0;
        } /* endif */
    } /* endif */

    return 0;
}

/******************************************************************************
**        1.2 - TransRollback - rolls back the transaction
******************************************************************************/
void TransRollback( )
{   struct sqlca sqlca;
    int        rc = 0;

    /* rollback the transaction */
    printf( "\nRolling back the transaction ...\n") ;
    EXEC SQL ROLLBACK; 
    rc = SqlInfoPrint( "ROLLBACK", &sqlca, __LINE__, __FILE__);
    if( rc == 0)
    {   printf( "The transaction was rolled back.\n") ;
    }		
    
}



/*#############################################################################
**     2. non SQL_CHECK section
**
**          2.1 - CmdLineArgsCheck1 - checks the command line arguments, ver.1
**          2.2 - CmdLineArgsCheck2 - checks the command line arguments, ver.2
**          2.3 - EmbAppInit - connects to the database
**          2.4 - EmbAppTerm - disconnects from the database
#############################################################################*/

/******************************************************************************
**          2.1 - CmdLineArgsCheck1 - checks the command line arguments, ver.1
******************************************************************************/
int  CmdLineArgsCheck1( int argc,
                        char * argv[],
                        char dbAlias[],
                        char user[],
                        char pswd[] )
{   int rc = 0;

    switch (argc)
    {    case 1:
             strcpy(dbAlias, "sample");
             strcpy(user, "");
             strcpy(pswd, "");
             break;
         case 2:
             strcpy(dbAlias, argv[1]);
             strcpy(user, "");
             strcpy(pswd, "");
             break;
         case 4:
             strcpy(dbAlias, argv[1]);
             strcpy(user, argv[2]);
             strcpy(pswd, argv[3]);
             break;    
         default:
             printf( "\nUSAGE: %s [dbAlias [userid  passwd]]]\n", argv[0] ) ;
             rc = 1;
             break;
    }  /* endswitch */

    return (rc);
}

/******************************************************************************
**          2.2 - CmdLineArgsCheck2 - checks the command line arguments, ver.2
******************************************************************************/
int  CmdLineArgsCheck2( int argc,
                        char * argv[],
                        char dbAlias[],
                        char user[],
                        char pswd[],
                        char remoteNodeName[] )
{   int rc = 0;

    switch (argc)
    {    case 1:
             strcpy(dbAlias, "sample");
             strcpy(user, "");
             strcpy(pswd, "");
             strcpy(remoteNodeName, "");   
             break;
         case 2:
             strcpy(dbAlias, argv[1]);
             strcpy(user, "");
             strcpy(pswd, "");
             strcpy(remoteNodeName, "");   	     
             break;
         case 4:
             strcpy(dbAlias, argv[1]);
             strcpy(user, argv[2]);
             strcpy(pswd, argv[3]);
             strcpy(remoteNodeName, "");   	     
             break;   
         case 5:
             strcpy(dbAlias, argv[1]);
             strcpy(user, argv[2]);
             strcpy(pswd, argv[3]);
             strcpy(remoteNodeName, argv[4]);   	     
             break;   	     
         default:
             printf( "\nUSAGE: %s [dbAlias [userid passwd [remoteNodeName]]]\n",
                     argv[0] ) ;
             rc = 1;
             break;
    }  /* endswitch */

    return (rc);
}

/******************************************************************************
**          2.3 - EmbAppInit - connects to the database
******************************************************************************/
int EmbAppInit( char paramDbAlias[],
                char paramUser[],
                char paramPswd[] )
{   struct sqlca sqlca;
    int        rc = 0;

    EXEC SQL BEGIN DECLARE SECTION;
        char dbAlias[15] ;
        char user[15] ; 
        char pswd[15] ; 
    EXEC SQL END DECLARE SECTION;

    strcpy( dbAlias, paramDbAlias) ;
    strcpy( user, paramUser) ;    
    strcpy( pswd, paramPswd) ;    

    printf( "\nConnecting to %s ...\n", dbAlias ) ;
    if ( strlen( user) == 0)
    {   EXEC SQL CONNECT TO :dbAlias;
        EMB_SQL_CHECK( "CONNECT");
    }
    else 
    {   EXEC SQL CONNECT TO :dbAlias USER :user USING :pswd;
        EMB_SQL_CHECK( "CONNECT");
    }
    printf( "Connected to %s.\n\n", dbAlias ) ;      
 
    return( 0 ) ;
}

/******************************************************************************
**        2.4 - EmbAppTerm - disconnects from the database
******************************************************************************/
int EmbAppTerm( char * dbAlias )
{   struct sqlca sqlca;
    int        rc = 0;
    
    printf( "\nDisconnecting from %s ...\n", dbAlias ) ;
    EXEC SQL CONNECT RESET ;
    EMB_SQL_CHECK( "CONNECT RESET") ;
    printf( "Disconnected from %s.\n", dbAlias ) ;    

    return( 0 ) ;
}



/* functions/procedures on setting up & outputting info from SQLDA */

/*******************************************************************************
* PROCEDURE : init_da
* The following procedure allocate memory space for the prepared SQL statement
*  to reside in.  The use of an internally described function called
*  SQLDASIZE is used to calculate the proper amount of memory is needed.
*******************************************************************************/
void init_da (struct sqlda **DAPointer, int DAsqln) {
   *DAPointer = (struct sqlda *) malloc (SQLDASIZE(DAsqln));
   if (*DAPointer == NULL) {
      fprintf (stderr, "\ninit_da : out of memory error.\n");
      exit (-1);
   }
   strncpy((*DAPointer)->sqldaid, "SQLDA   ", sizeof ((*DAPointer)->sqldaid));
   (*DAPointer)->sqldabc = (sqlint32)SQLDASIZE(DAsqln);
   (*DAPointer)->sqln = DAsqln;
   (*DAPointer)->sqld = 0;
}



/*******************************************************************************
* FUNCTION : alloc_host_vars
* This function allocates memory to be filled with the data directed from an
*  SQLDA pointer.
*******************************************************************************/
void alloc_host_vars (struct sqlda *sqldaPointer) {
   short idx;
   unsigned int memsize =0;
   long longmemsize =0;
   int precision  =0;

   for (idx = 0; idx < sqldaPointer->sqld; idx++) {
      switch (sqldaPointer->sqlvar[idx].sqltype ) {
      case SQL_TYP_BLOB:
      case SQL_TYP_NBLOB:
      case SQL_TYP_CLOB:
      case SQL_TYP_NCLOB:
      case SQL_TYP_DBCLOB:
      case SQL_TYP_NDBCLOB:
         longmemsize = GETSQLDALONGLEN(sqldaPointer, idx);

#if defined(DB2WIN)
         memsize = (unsigned int) (min(longmemsize, 64000));
         SETSQLDALONGLEN(sqldaPointer, idx, memsize);
#else
         memsize = longmemsize;
#endif

         sqldaPointer->sqlvar[idx].sqldata = (char *SQL_POINTER)
            malloc (memsize);
         break;
      case SQL_TYP_VARCHAR:
      case SQL_TYP_NVARCHAR:
      case SQL_TYP_LONG:
      case SQL_TYP_NLONG:
      case SQL_TYP_DATE:
      case SQL_TYP_NDATE:
      case SQL_TYP_TIME:
      case SQL_TYP_NTIME:
      case SQL_TYP_STAMP:
      case SQL_TYP_NSTAMP:
         sqldaPointer->sqlvar[idx].sqltype = SQL_TYP_NCSTR;
         sqldaPointer->sqlvar[idx].sqldata = (char *SQL_POINTER)
            malloc ((sqldaPointer->sqlvar[idx].sqllen));
         memsize = (sqldaPointer->sqlvar[idx].sqllen);
         break;
      case SQL_TYP_DECIMAL:
      case SQL_TYP_NDECIMAL:
         precision = ((char *)&(sqldaPointer->sqlvar[idx].sqllen))[0];
         sqldaPointer->sqlvar[idx].sqldata = (char *SQL_POINTER)
            malloc ((precision + 2) /2);
         memsize = (precision +2) /2;
         break;

      default:
         sqldaPointer->sqlvar[idx].sqldata = (char *SQL_POINTER)
            malloc (sqldaPointer->sqlvar[idx].sqllen);
         memsize = sqldaPointer->sqlvar[idx].sqllen;
         break;
      } /* endswitch */



      if (sqldaPointer->sqlvar[idx].sqldata == NULL) {
         fprintf (stderr, "\nalloc host vars: out of memory error.\n");
         exit (-1);
      } else {
         memset (sqldaPointer->sqlvar[idx].sqldata,'\0',memsize);
      } /* endif */


      /*
      ** If the SQLTYPE is odd, the host var requires a null indicator variable
      ** this is needed to allocate memory for "sqlind" which is used in the
      ** "display_da" procedure when detecting whether or not the host indicator
      ** is NULLable or not.
      */
      if ( sqldaPointer->sqlvar[idx].sqltype & 1 ) {
         /* Allocate storage for short int */
         sqldaPointer->sqlvar[idx].sqlind = (short *)malloc(sizeof(short));
         /* Detect memory allocation error */
         if ( sqldaPointer->sqlvar[idx].sqlind == NULL ) {
            fprintf(stderr, "Out of dynamic memory while "
               "allocating for your select statement\n") ;
            exit(-1) ;
         } else {
            /* initialize memory to zero */
            memset(sqldaPointer->sqlvar[idx].sqldata,'\0',sizeof(short));
         } /* endif */
      } /* endif */
   } /* endfor */
}

/*******************************************************************************
* FUNCTION : free_da
* This function frees up the memory that has been allocated for the use of
*  an SQLDA data structure.
*******************************************************************************/
void free_da (struct sqlda *sqldaPointer) {
   short idx;
   for (idx = 0; idx < sqldaPointer->sqld; idx++) {
      free (sqldaPointer->sqlvar[idx].sqldata);
      if (sqldaPointer->sqlvar[idx].sqltype & 1) {
         free (sqldaPointer->sqlvar[idx].sqlind);
      }
   } /* endfor */
   free (sqldaPointer);
}

/*******************************************************************************
* PROCEDURE : print_var
* The following procedure prints out the SQLDA SQLVAR variables.
* The procedure first determines which type the data is and then goes through
*  the appropriate sub-routines that are required to print out the data.
*
*******************************************************************************/
void print_var (char *ptr, int type, short collen, short datalen) {
   short idx, ind ;            /* Array idx variables */
   /* Variables for decoding packed decimal data */
   short bottom, point ;
   unsigned short top, precision, scale;

   short *iptr;                           /* Pointer for short integer data. */
   char blen;                             /* Single byte length variable */
   sqlint32 *lptr ;                       /* Pointer for long integer data */
   double *dptr ;                         /* Pointer for float data */
   short maxCollen = max(collen, datalen);

   maxCollen = max(maxCollen, 5);         /* setting a minimum column length */
   /*
   ** Determine the type of data, coerce or decode the data for output
   ** if necessary, then output the data.
   */

   switch ( type ) {
      case SQL_TYP_INTEGER:   /* long */
      case SQL_TYP_NINTEGER:  /* long with null indicator */
         lptr = (sqlint32 *) ptr ;
         printf("%*ld", maxCollen, (long)*lptr ) ;
         break ;
      case SQL_TYP_SMALL:     /* short */
      case SQL_TYP_NSMALL:    /* short with null indicator */
         iptr = (short *) ptr ;
         printf("%*d", maxCollen, *iptr ) ;
         break ;
      case SQL_TYP_DECIMAL:   /* decimal */
      case SQL_TYP_NDECIMAL:  /* decimal with null indicator */
      /* Determine the scale and precision */
         precision = ((char *)&(maxCollen))[0];
         scale = ((char *)&(maxCollen))[1];

/*****************************************************************************/
/* Note:  Precision can only be odd because internally only odd are stored.  */
/*        When and if it happens that an even precision can occur, the       */
/*        precision must be incremented by 1 in order for the proper         */
/*        calculation of "idx" and "point" to occur, and the correct value */
/*        outputted.                                                         */
/*****************************************************************************/
         if ((precision %2) == 0) precision += 1;

         /* Calculate the total number of bytes */
         idx = ( short ) ( precision + 2 ) / 2 ;
         point = precision - scale ;

         /* Determine the sign */
         bottom = *(ptr + idx -1) & 0x000F ;   /* sign */
         if ( (bottom == 0x000D) || (bottom == 0x000B) ) {
            printf("-") ;
         } else {
            printf(" ") ;
         }


         /* Decode and print the decimal number */
         for (ind=0; ind < idx; ind++) {
            top = *(ptr + ind) & 0x00F0 ;
            top = (top >> 4 ) ;
            bottom = *(ptr + ind) & 0x000F ;
               if ( point-- == 0 ) printf(".") ;
               printf("%d", top ) ;
/*****************************************************************************/
/* Ignore bottom of last half byte because its the sign.                     */
/*****************************************************************************/
            if ( ind < idx - 1 ) { /* sign half byte ? */
               if ( point-- == 0 ) printf(".") ;
               printf("%d", bottom ) ;
            }
         }
         if ( scale == 0 ) printf(".") ;
         break ;

      case SQL_TYP_FLOAT:  /* double */
      case SQL_TYP_NFLOAT: /* double with null indicator */
         dptr = (double *) ptr ;
         printf("%*.*e", maxCollen, datalen, *dptr ) ;
         break ;
      case SQL_TYP_CHAR:  /* fixed length character string */
      case SQL_TYP_NCHAR: /* fixed length character string with null
                             indicator */
         printf ("%-*.*s", maxCollen, datalen, ptr);
         break;


      case SQL_TYP_LSTR:  /* varying length character string, 1-byte length */
      case SQL_TYP_NLSTR: /* varying length character string, 1-byte length,
                             with null indicator */

         /*
         ** Initialize blen to the value the length field in the
         ** varchar data structure.
         */
         blen = *ptr;

         /* Advance the data pointer beyond the length field */
         ptr+=sizeof(char);
         for (idx = 0; (idx < (short)blen) && (idx < maxCollen);
            idx++)
            printf("%c",*(ptr + idx) ) ;
         break ;
      case SQL_TYP_CSTR:  /* null terminated varying length character string */
      case SQL_TYP_NCSTR: /* null terminate varying length character
                              string with null indicator */
         /*
         ** Advance the data pointer beyond the length field and print the data.
         */
         printf ("%-*.*s", maxCollen, datalen, ptr);
         break ;
      default:
         /* Unknown data type */
         printf("%-*.*s", maxCollen, datalen, "UNKNOWN");
         break ;
   }
   /* print the column indicator */
   printf (" | ");
}


/*******************************************************************************
* PROCEDURE : display_col_titles
* The following procedure displays the column names, which is available from
*  the SQLDA data structure.  This is then passed on to "print_var" to be
*  outputted on to the screen.
*******************************************************************************/
void display_col_titles (struct sqlda *sqldaPointer) {
   short sqlvarIndex, numBytes;

   for(sqlvarIndex=0; sqlvarIndex < sqldaPointer->sqld; sqlvarIndex++) {
      switch (sqldaPointer->sqlvar[sqlvarIndex].sqltype) {
      case SQL_TYP_DECIMAL:
      case SQL_TYP_NDECIMAL:
         numBytes = max (sqldaPointer->sqlvar[sqlvarIndex].sqlname.length,
            ((char *)&(sqldaPointer->sqlvar[sqlvarIndex].sqllen))[0] + 2);
         break;
      case SQL_TYP_BLOB:
      case SQL_TYP_NBLOB:
      case SQL_TYP_DBCLOB:
      case SQL_TYP_NDBCLOB:
         numBytes = LOBLENGTH;
         break;
      case SQL_TYP_CLOB:
      case SQL_TYP_NCLOB:
         numBytes = LOBLENGTH;
         break;
      case SQL_TYP_GRAPHIC:
      case SQL_TYP_NGRAPHIC:
         break;
      default:
         numBytes = max (sqldaPointer->sqlvar[sqlvarIndex].sqlname.length,
            sqldaPointer->sqlvar[sqlvarIndex].sqllen);
         break;
      } /* endswitch */
      print_var (sqldaPointer->sqlvar[sqlvarIndex].sqlname.data,
         SQL_TYP_CSTR, numBytes,
         sqldaPointer->sqlvar[sqlvarIndex].sqlname.length);
   }
   printf("\n");
}

/*******************************************************************************
* PROCEDURE : display_da
* The following procedure displays the output of pointer which has been
*  passed through.  All pertinent information on the structure of the outputted
*  data is available from this pointer, and is further examined in the
*  procedure "print_var".
*******************************************************************************/
void display_da (struct sqlda *sqldaPointer) {
   short numBytes, sqlvarIndex;
   struct lob {
      sqlint32 length;
      char *data;
   } *lobPointer;

   /* Output the contents for all host variables */
   for(sqlvarIndex=0;sqlvarIndex < sqldaPointer->sqld; sqlvarIndex++) {
      numBytes = sqldaPointer->sqlvar[ sqlvarIndex ].sqllen ;

      if ( sqldaPointer->sqlvar[sqlvarIndex].sqltype & 1 &&
         *(sqldaPointer->sqlvar[sqlvarIndex].sqlind) < 0 ) {
         /* the data in the sqlda is NULL */
         switch (sqldaPointer->sqlvar[sqlvarIndex].sqltype) {
            case SQL_TYP_NBLOB:
            case SQL_TYP_NDBCLOB:
               printf ("LOB length = NULL       | ");
               break;
            case SQL_TYP_NCLOB:
               print_var ("NULL CLOB", SQL_TYP_CSTR, LOBLENGTH, LOBLENGTH);
               break;
            case SQL_TYP_NGRAPHIC:
               break;
            case SQL_TYP_NDECIMAL:
               numBytes = max (sqldaPointer->sqlvar[sqlvarIndex].sqlname.length,
                  ((char *)&(sqldaPointer->sqlvar[sqlvarIndex].sqllen))[0] + 2);

               print_var ("-", SQL_TYP_CSTR, numBytes,
                  sqldaPointer->sqlvar[sqlvarIndex].sqlname.length);
               break;
            default:
               /* Output the data in the sqlda */
               print_var( sqldaPointer->sqlvar[ sqlvarIndex ].sqldata,
                  sqldaPointer->sqlvar[ sqlvarIndex ].sqltype,
                  sqldaPointer->sqlvar[sqlvarIndex].sqlname.length,
                  numBytes);
               break;
         } /* endswitch */
      } else {   /* output the data in the sqlda */
         switch (sqldaPointer->sqlvar[sqlvarIndex].sqltype) {
            case SQL_TYP_CLOB:
            case SQL_TYP_NCLOB:
            case SQL_TYP_BLOB:
            case SQL_TYP_NBLOB:
            case SQL_TYP_DBCLOB:
            case SQL_TYP_NDBCLOB:
               lobPointer = (struct lob *)
                  (sqldaPointer->sqlvar[sqlvarIndex].sqldata);
               if (*sqldaPointer->sqlvar[sqlvarIndex].sqlind == 0) {
                  printf ("LOB length = %10ld       | ", lobPointer->length);
               } else {
               /* the sqlvar LOB has been truncated */
                  printf ("LOB length = %10ld trunc.| ", lobPointer->length);
               } /* endif */
               break;
            case SQL_TYP_GRAPHIC:
            case SQL_TYP_NGRAPHIC:
               printf ("GRAPHIC length = %5ld | ",
                  sqldaPointer->sqlvar[sqlvarIndex].sqlname.length);
               break;
            default:
               /* Output the data in the sqlda */
               print_var( sqldaPointer->sqlvar[ sqlvarIndex ].sqldata,
                  sqldaPointer->sqlvar[ sqlvarIndex ].sqltype,
                  sqldaPointer->sqlvar[sqlvarIndex].sqlname.length,
                  numBytes);
               break;
         } /* endswitch */
      } /* endif */
   } /* endfor */
   printf("\n") ;
}
/* COMMENT OUT OFF */