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 */