DSN8EDXI

Inserts a new record into the sample XML database .

 /*********************************************************************
 * Module name = DSN8EDXI (DB2 sample program)                        *
 *                                                                    *
 * DESCRIPTIVE NAME = Sample XML record insert program                *
 *                                                                    *
 *    LICENSED MATERIALS - PROPERTY OF IBM                            *
 *    5675-DB2                                                        *
 *    (C) COPYRIGHT 1999, 2000 IBM CORP. ALL RIGHTS RESERVED.         *
 *                                                                    *
 *    STATUS = VERSION 9                                              *
 *                                                                    *
 * Function: Inserts a new record into the sample XML database        *
 *                                                                    *
 * Notes:                                                             *
 *   Dependencies: Requires IBM C/C++ for z/OS                        *
 *                                                                    *
 *   Restrictions:                                                    *
 *                                                                    *
 * Module type: C program                                             *
 *   Processor: IBM C/C++ for z/OS                                    *
 * Module size: See linkedit output                                   *
 *  Attributes: Re-entrant and re-usable                              *
 *                                                                    *
 * Entry Point: DSN8EDXI                                              *
 *     Purpose: See Function                                          *
 *     Linkage: Standard z/OS linkage                                 *
 *                                                                    *
 *                                                                    *
 *  Parameters: None                                                  *
 *                                                                    *
 * Normal Exit: Return Code: 0000                                     *
 *              - Message: none                                       *
 *                                                                    *
 *  Error Exit: Return Code: 0012                                     *
 *              - Message: Unable to open <DD-name>                   *
 *              - Message: Unable to close <DD-name>                  *
 *              - Message: <formatted SQL text from DSNTIAR>          *
 *                                                                    *
 *    External References:                                            *
 *             - Routines/Services: DSNTIAR: DB2 msg text formatter   *
 *             - Data areas       : None                              *
 *             - Control blocks   : None                              *
 *                                                                    *
 *  Pseudocode:                                                       *
 *   DSN8EDXI:                                                        *
 *   - Call printGreeting to output the DSN8EDXI greeting             *
 *   - Call displayProductTable to list IDs of all products current-  *
 *     ly in the product table                                        *
 *   - Call readProductDescription to get the description of the      *
 *     product to be added                                            *
 *   - Call getProductId to extract the ID of the product to be added *
 *     from the inputted product description record                   *
 *   - Insert the record for the specified product ID                 *
 *     - If unexpected SQLCODE, call issueSqlError                    *
 *   - Call displayProductTable to list IDs of the products now in    *
 *     the product table                                              *
 *   - Rollback the change to preserve the sample table               *
 *     - If unexpected SQLCODE, call issueSqlError                    *
 *   - Call displayProductTable to list IDs of all products in the    *
 *     product table                                                  *
 *   End DSN8EDXI                                                     *
 *                                                                    *
 *   printGreeting:                                                   *
 *   - Output the DSN8EDXI greeting                                   *
 *                                                                    *
 *   displayProductTable:                                             *
 *   - Open a cursor for all records in the product table             *
 *     - If unexpected SQLCODE, call issueSqlError                    *
 *   - Fetch and display each record                                  *
 *     - If unexpected SQLCODE, call issueSqlError                    *
 *   - Close the cursor                                               *
 *     - If unexpected SQLCODE, call issueSqlError                    *
 *                                                                    *
 *   readProductDescription:                                          *
 *   - Open the file allocated to the PRODDESC DD                     *
 *     - If an error occurs, call issueDataSetOpeningError            *
 *   - Read the file                                                  *
 *     - If an error occurs, call issueDataSetReadingError            *
 *   - Call trimTrailingBlanks to remove trailing blanks from the     *
 *     entry                                                          *
 *   - Validate the length of the entry                               *
 *     - Call issueInvalidDataLengthError if length is unexpected     *
 *   - Close the file allocated to the PRODID DD                      *
 *     - If an error occurs, call issueDataSetClosingError            *
 *                                                                    *
 *   getProductId:                                                    *
 *   - locate the product ID in the inputted product description      *
 *     record                                                         *
 *                                                                    *
 *   issueSqlError:                                                   *
 *   - call DSNTIAR to format the unexpected SQLCODE.                 *
 *                                                                    *
 *   issueDataSetOpeningError                                         *
 *   - Write a diagnostic message for error when opening a data set   *
 *                                                                    *
 *   issueDataSetReadingError                                         *
 *   - Write a diagnostic message for error when reading a data set   *
 *                                                                    *
 *   trimTrailingBlanks                                               *
 *   - Remove trailing blanks from a string                           *
 *                                                                    *
 *   issueInvalidDataLengthError                                      *
 *   - Write a diagnostic message when the length of inputted data    *
 *     is too long or too short                                       *
 *                                                                    *
 *   issueDataSetClosingError                                         *
 *   - Write a diagnostic message for error when closing a data set   *
 *                                                                    *
 * Change log:                                                        *
 *                                                                    *
 *********************************************************************/
 /**************************** Equates *******************************/
 #define         BLANK        ' '      /* Blank character            */
 #define         NULLCHAR     '\0'     /* Null character             */
 #define         LF           '\x25'   /* Line Feed character        */
 #define         RETNRM         0      /* Normal  return code        */
 #define         RETWRN         4      /* Warning return code        */
 #define         RETERR         8      /* Error return code          */
 #define         RETSEV        12      /* Severe error return code   */

 enum flag                  {No, Yes}; /* Settings for flags         */

 /********************** C library definitions ***********************/
 #include        <decimal.h>
 #include        <errno.h>
 #include        <stdio.h>
 #include        <stdlib.h>
 #include        <string.h>

 /******************* DB2 SQL Communication Area *********************/
 EXEC SQL INCLUDE SQLCA;

 /************************ DB2 Host Variables ************************/
 EXEC SQL BEGIN DECLARE SECTION;
   struct
     { struct
         { short int  PID_len;
           char       PID_data[10];
         }            PID;
       struct
         { short int  NAME_len;
           char       NAME_data[128];
         }            NAME;
       decimal(30,2)  PRICE;
       decimal(30,2)  PROMOPRICE;
       char       PROMOSTART[11];
       char       PROMOEND[11];
       SQL TYPE IS XML AS CLOB(1M) DESCRIPTION;
     } PRODUCT;
 EXEC SQL END DECLARE SECTION;

 /******************** DSN8EDXI Global Variables *********************/
 long int        rc               = 0; /* program return code        */
 unsigned long   maxXmlLength          /* max length of XML column   */
                 = sizeof(PRODUCT.DESCRIPTION.data)-1;

 /******************* DSN8EDXI Function Prototypes *******************/
 int main( int argc, char *argv[] );
 void printGreeting( void );           /* Write welcome message      */
 void displayProductTable( void );     /* Show contents of prod tbl  */
 void clearBuffers( void );            /* Initialize work buffers    */
 void readProductDescription           /* Read product description   */
   ( char *productDescription,         /* -out: product description  */
     unsigned long
          *productDescriptionLength    /* -out: product descr length */
   );
 void getProductId                     /* Extract PID from prod desc */
   ( char *productDescription,         /* -in:  product description  */
     char *productId,                  /* -out: product identifier   */
     short *productIdLen               /* -out: product id length    */
   );
 void issueDataSetClosingError         /* Handler for ds close error */
   ( char            *DDname,          /* - in: name of errant DD    */
     int             LEerrno           /* - in: LE diagnostic errno  */
   );
 void issueDataSetOpeningError         /* Handler for ds open error  */
   ( char            *DDname,          /* - in: name of errant DD    */
     int             LEerrno           /* - in: LE diagnostic errno  */
   );
 void issueDataSetReadingError         /* Handler for ds read error  */
   ( char            *DDname,          /* - in: name of errant DD    */
     int             LEerrno           /* - in: LE diagnostic errno  */
   );
 void issueInvalidDataLengthError      /* Handler for data len error */
   ( char            *DdName,          /* - in: identify of DD       */
     int             minLength,        /* - in: min valid length     */
     unsigned long   maxLength         /* - in: max valid length     */
   );
 void issueSqlError                    /* Handler for SQL error      */
   ( char            *locMsg           /* - in: Call location        */
   );
 void trimTrailingBlanks               /* Strip off trailing blanks  */
   ( char            *string           /* - in: string to be trimmed */
   );

 /*********************************************************************
 * List current PRODUCTS, then insert a new product, then relist      *
 *********************************************************************/
 int main( int argc, char *argv[] )
   { printGreeting();                  /* Say hello                  */
                                       /* List PIDs already in table */
     printf( "(1) List the product IDs for items already "
             "in the table:\n" );
     displayProductTable();            /* List the product ids       */

     if( rc < RETSEV )                 /* If all is well so far..    */
       { clearBuffers();               /* ..initialize work buffers  */
                                       /* ..and read data for insert */
         readProductDescription( PRODUCT.DESCRIPTION.data,
                                 &PRODUCT.DESCRIPTION.length );
       }

     if( rc < RETSEV )                 /* If all is well so far..    */
       {                               /* ..extract product id from  */
                                       /*   the data to be inserted  */
         getProductId(PRODUCT.DESCRIPTION.data
                     ,PRODUCT.PID.PID_data
                     ,&PRODUCT.PID.PID_len );
       }

     if( rc < RETSEV )                 /* If all is well so far..    */
       { printf( "\n" );               /* ..advertise insert         */
         printf( "(2) Insert a record for product ID %s\n",
                 PRODUCT.PID.PID_data );
         EXEC SQL                      /* ..and insert the new data  */
           INSERT INTO PRODUCT         /*   into the PRODUCT table   */
                 ( PID
                  ,DESCRIPTION )
           VALUES( :PRODUCT.PID
                  ,:PRODUCT.DESCRIPTION );

         if( SQLCODE != 0 )            /* ..report error, if any     */
           issueSqlError( "DSN8EDXI: Error "
                          "inserting XML statement" );
       }

     if( rc < RETSEV )                 /* If all is well so far..    */
       { clearBuffers();               /* ..initialize work buffers  */
                                       /* ..list PIDs now in table   */
         printf( "\n" );
         printf( "(3) List the product IDs for items "
                 "now in the table:\n" );
         displayProductTable();        /* ..list PIDs now in table   */
       }

     printf( "\n" );
     printf( "(4) Undo the change (for next time)\n" );
     EXEC SQL                          /* Rollback changes so program*/
       ROLLBACK;                       /* will run clean next time   */
     if( SQLCODE != 0 )                /* Report error, if any       */
       issueSqlError( "DSN8EDXI: Error "
                      "performing ROLLBACK" );

     if( rc < RETSEV )                 /* If all is well so far..    */
       { clearBuffers();               /* ..initialize work buffers  */
                                       /* ..list PIDs still in table */
         printf( "\n" );
         printf( "(5) List the product IDs for items "
                 "remaining in the table:\n" );
         displayProductTable();        /* ..list PIDs now in table   */
       }

     return( rc );                     /* Set return code and exit   */
   } /* end of main routine */


 void printGreeting( void )            /* Write welcome message      */
   /*******************************************************************
   * Writes a greeting when the program is started                    *
   *******************************************************************/
   { printf( "**************************************************"
             "******************************\n" );
     printf( "DSN8EDXI: Inserts a record into the PRODUCT table "
             "of the DB2 sample XML database\n" );
     printf( "**************************************************"
             "******************************\n" );
   } /* End of printGreeting */


 void displayProductTable( void )      /* Show contents of prod tbl  */
   /*******************************************************************
   * Lists the product ids currently in the PRODUCT table             *
   *******************************************************************/
   { EXEC SQL                          /* Cursor to get PIDs         */
       DECLARE productCursor
         CURSOR FOR
           SELECT PID
             FROM PRODUCT;

     EXEC SQL                          /* Open the cursor            */
       OPEN productCursor;
     if( SQLCODE != 0 )                /* Report error, if any       */
       issueSqlError( "DSN8EDXI: Error opening PRODUCT table cursor" );

     if( rc < RETSEV )                 /* If all is well so far..    */
       { EXEC SQL                      /* ..fetch the first row      */
           FETCH productCursor
            INTO :PRODUCT.PID;
                                       /* ..and process all rows     */
         while( SQLCODE == 0  &&  rc < RETSEV )
           { printf( "     %s\n",
                      PRODUCT.PID.PID_data );

             EXEC SQL
               FETCH productCursor
                INTO :PRODUCT.PID;
           }                           /* ..report error, if any     */
         if( SQLCODE != 0 && SQLCODE != 100 )
           issueSqlError( "DSN8EDXI: Error "
                          "fetching from PRODUCT table cursor" );
       }

     EXEC SQL                          /* Close the cursor           */
       CLOSE productCursor;
     if( SQLCODE != 0 )                /* Report error, if any       */
       issueSqlError( "DSN8EDXI: Error closing PRODUCT table cursor" );

   } /* end of displayProductTable */


 void clearBuffers( void )             /* Initialize work buffers    */
   /*******************************************************************
   * Initializes work buffers for next use                            *
   *******************************************************************/
   { memset( PRODUCT.DESCRIPTION.data,
             NULLCHAR,
             PRODUCT.DESCRIPTION.length );
     PRODUCT.DESCRIPTION.length = 0;
   } /* End of clearBuffers */


 void readProductDescription           /* Read product description   */
   ( char *productDescription,         /* -out: product description  */
     unsigned long
          *productDescriptionLength    /* -out: product descr length */
   )
   /*******************************************************************
   * Reads a product description of 0-32704 bytes from the PRODDESC DD*
   *******************************************************************/
   { FILE           *prodDescFile;     /* Ptr to PRODDESC DD         */
     char           prodDescDD[12];    /* DD handle                  */
     char           prodDescRec[80];   /* PRODDESC file input record */
     short int      recordLength  = 0; /* Length of record           */
     unsigned short moreRecords = Yes; /* EOF indicator              */

     strcpy( prodDescDD,"DD:PRODDESC" );

     *productDescriptionLength = 0;
     errno = 0;                        /* clear LE errno             */
     prodDescFile = fopen( prodDescDD,
                           "rb,lrecl=80,type=record" );
     if( prodDescFile == NULL )
       issueDataSetOpeningError( prodDescDD,errno );

     while( moreRecords == Yes  &&  rc < RETSEV )
       { recordLength
           = fread( prodDescRec,       /* Read into PRODDESC rec area*/
                    1,                 /* ..1 record                 */
                    80,                /* ..of 80 bytes              */
                    prodDescFile );    /* ..from PRODDESC file       */

         if( ferror(prodDescFile) )    /* Handle IO errors           */
            issueDataSetReadingError( prodDescDD,errno );

         else if(feof(prodDescFile)) /* Handle EOF                   */
            moreRecords = No;
                                       /* Discard bytes 73-80 and    */
         else                          /* strip off trailing blanks  */
           { strncat( productDescription,prodDescRec,72 );
             trimTrailingBlanks( productDescription );
           }
                                       /* Don't overfill return area */
         if( rc < RETSEV && strlen(productDescription) > maxXmlLength )
           issueInvalidDataLengthError( "PRODDESC",1,maxXmlLength );
       }

     if( rc < RETSEV )
       { *productDescriptionLength = strlen(productDescription) - 1;
         if( fclose( prodDescFile ) != 0 )
           issueDataSetClosingError( prodDescDD,errno );
       }

   } /* end of readProductDescription */


 void getProductId
   ( char *productDescription,         /* -in:  product description  */
     char *productId,                  /* -out: product identifier   */
     short *productIdLen               /* -out: product id length    */
   )
   /*******************************************************************
   * Called to extract the product id from a read-in product descrip- *
   * tion record.                                                     *
   *******************************************************************/
   { int         i;                    /* for array indexing         */
     char       *tokPtr;               /* string ptr for token parser*/
     int         productDescriptionLen;

                                       /* Locate PID field in descr  */
     productDescriptionLen = strlen(productDescription);
     tokPtr = strstr( productDescription,"<product pid=\"" );
     if( tokPtr == NULL )              /* Report error if none found */
       { productId[0] = NULLCHAR;
         *productIdLen = 0;
         printf("Error Parsing product XML "
                "Could not find product \n");
         rc = RETSEV;
       }
     else                              /* Otherwise, grab it         */
       { tokPtr = tokPtr + strlen("<product pid=\"");
         productDescriptionLen
           = productDescriptionLen - strlen("<product pid=\"");
         strncpy( productId,tokPtr,10 );
         *productIdLen = 10;
       }
   } /* end of getProductId */


 void issueDataSetClosingError         /* Handler for ds close error */
   ( char            *DDname,          /* - in: name of errant DD    */
     int             LEerrno           /* - in: LE diagnostic errno  */
   )
   /*******************************************************************
   * Called when a TSO data set cannot be closed                      *
   *******************************************************************/
   { printf( "ERROR: Unable to close %s\n", DDname );
     printf( "%s \n",strerror(LEerrno) );
     printf( "-----> Processing halted\n" );
     rc = RETSEV;
   } /* end of issueDataSetClosingError */


 void issueDataSetOpeningError         /* Handler for ds open error  */
   ( char            *DDname,          /* - in: name of errant DD    */
     int             LEerrno           /* - in: LE diagnostic errno  */
   )
   /*******************************************************************
   * Called when a TSO data set cannot be opened                      *
   *******************************************************************/
   { printf( "ERROR: Unable to open %s\n", DDname );
     printf( "%s \n",strerror(LEerrno) );
     printf( "-----> Processing halted\n" );
     rc = RETSEV;
   } /* end of issueDataSetOpeningError */


 void issueDataSetReadingError         /* Handler for ds read error  */
   ( char            *DDname,          /* - in: name of errant DD    */
     int             LEerrno           /* - in: LE diagnostic errno  */
   )
   /*******************************************************************
   * Called when a TSO data set cannot be read                        *
   *******************************************************************/
   { printf( "ERROR: Unable to read %s\n", DDname );
     printf( "%s \n",strerror(LEerrno) );
     printf( "-----> Processing halted\n" );
     rc = RETSEV;
   } /* end of issueDataSetReadingError */


 void issueInvalidDataLengthError      /* Handler for data len error */
   ( char            *DdName,          /* - in: identify of DD       */
     int             minLength,        /* - in: min valid length     */
     unsigned long   maxLength         /* - in: max valid length     */
   )
   /*******************************************************************
   * Called when the length of data read from a DSN8EDXI input file   *
   * (identified by DdName) does not fall within the valid bounds for *
   * size (minLength and maxLength).                                  *
   *******************************************************************/
   { printf( "ERROR: The length of the data read for read from the %s "
                    "DD \n",DdName );
     printf( "       does not fall within the required bounds of %i "
                    "and %u\n",minLength,maxLength );
     printf( "-----> Processing halted\n" );
     rc = RETSEV;
   } /* end of issueInvalidDataLengthError */


 #pragma linkage(dsntiar, OS)
 void issueSqlError                    /* Handler for SQL error      */
   ( char *locMsg                      /* - in: Call location        */
   )
   /*******************************************************************
   * Called when an unexpected SQLCODE is returned from a DB2 call    *
   *******************************************************************/
   { struct      error_struct {        /* DSNTIAR message structure  */
       short int   error_len;
       char        error_text[10][80];
       }         error_message = {10 * 80};

     extern short int dsntiar( struct      sqlca         *sqlca,
                               struct      error_struct  *msg,
                               int                       *len );

     short int   DSNTIARrc;            /* DSNTIAR Return code        */
     int         j;                    /* Loop control               */
     static int  lrecl = 80;           /* Width of message lines     */

     /*****************************************************************
     * print the locator message                                      *
     *****************************************************************/
     printf( "ERROR: %-80s\n", locMsg );
     printf( "-----> Processing halted\n" );

     /*****************************************************************
     * format and print the SQL message                               *
     *****************************************************************/
     DSNTIARrc = dsntiar( &sqlca, &error_message, &lrecl );
     if( DSNTIARrc == 0 )
       for( j = 0; j <= 10; j++ )
         printf( " %.80s\n", error_message.error_text[j] );
     else
       {
         printf( " *** ERROR: DSNTIAR could not format the message\n" );
         printf( " ***        SQLCODE is %d\n",SQLCODE );
         printf( " ***        SQLERRM is \n" );
         for( j=0; j<sqlca.sqlerrml; j++ )
           printf( "%c", sqlca.sqlerrmc[j] );
         printf( "\n" );
       }

     /*****************************************************************
     * set severe error code                                          *
     *****************************************************************/
     rc = RETSEV;

   } /* end of issueSqlError */


 void trimTrailingBlanks               /* Strip off trailing blanks  */
   ( char            *string           /* - in: string to be trimmed */
   )
   /*******************************************************************
   * Strips trailing blanks from a string                             *
   *******************************************************************/
   { int             i;
     for( i = strlen(string) - 1; string[i] == ' '; i-- );
     string[++i] = '\n';
     string[++i] = '\0';
   } /* end of trimTrailingBlanks */