/*******************************************************************************
**
** Source File Name = util.c 1.10
**
** Licensed Materials - Property of IBM
**
** (C) COPYRIGHT International Business Machines Corp. 1995, 1999
** All Rights Reserved.
**
** US Government Users Restricted Rights - Use, duplication or
** disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
**
**
** PURPOSE : Utility procedures for API implemented programs. Procedures
** include the checking of error, and a procedure that retreives
** data
** APIs USED :
** GET SQLSTATE MESSAGE sqlogstt()
** GET ERROR MESSAGE sqlaintp()
**
** STRUCTURES USED :
** sqlda
** sqlca
**
** 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 "system.h" */
#ifdef DB268K
/* Need to include ASLM for 68K applications */
#include <LibraryManager.h>
#endif
#define SQLSTATE sqlca.sqlstate
#ifndef max
#define max(A, B) ((A) > (B) ? (A) : (B))
#endif
#ifndef min
#define min(A, B) ((A) > (B) ? (B) : (A))
#endif
#define CHECKERR(CE_STR) check_error (CE_STR, &sqlca)
#define LOBLENGTH 29
#if defined(DB2OS2)
#include <conio.h>
#elif defined (DB2AIX)
#include <curses.h>
#endif
short *errdata = NULL; /* Hold error counts */
#define mem_error(xx) \
{ printf("SQCERROR--Out of memory when %s.\n",xx); \
exit(-1); }
#define MAXERRORS 65536
struct lob_file {
unsigned long name_length;
unsigned long data_length;
unsigned long file_option;
char name[255];
};
struct col_info {
char name[19]; /* name of column */
int coltype; /* column type */
short length, /* length of the column */
colno; /* column number */
};
struct tab_info {
char name[19], /* name of table */
creator[9], /* creator of the table */
type;
short colcount; /* number of columns */
struct col_info **alpha_list, /* alphabetical listing of columns */
**num_list; /* numerical listing of columns */
};
/*******************************************************************************
** Procedure : check_error
**
** Purpose : This procedure checks the SQLCACODE flag and prints out any
** information that is available related to the specific error.
**
*******************************************************************************/
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) {
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);
printf("%s", message);
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);
printf("%s", message);
return 0;
} /* endif */
} /* endif */
return 0;
}
/* COMMENT OUT ON */
/* 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 = (long)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 */
long *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 = (long *) ptr ;
printf("%*ld", maxCollen, *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 {
long 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 */
/******************************************************************************
* init_error_log -- This routine prepares the buffer for recording error
* occurrences.
******************************************************************************/
void init_error_log(void) {
long i = 0; /* Loop counter */
if ((errdata=(short *)calloc(MAXERRORS,sizeof(short))) == NULL)
mem_error("creating error buffer");
for (i=0; i<MAXERRORS; i++)
errdata[i] = 0;
return;
} /* end init_error_log */
/******************************************************************************
* sqlerror -- This routine displays the error information contained in
* the SQLCA and logs the changes in the error buffer (if
* allocated).
******************************************************************************/
void sqlerror(struct sqlca *sqlca) {
char errbuf[500]; /* Hold error message */
long i,j; /* Loop counters */
int rc; /* Return code from sqlaintp() */
if (sqlca->sqlcode != 0 || sqlca->sqlwarn[0] != ' ') {
if (sqlca->sqlcode == 0)
printf("SQL Informational Message:\n");
else if (sqlca->sqlcode < 0)
printf("SQL Error:\n");
else
printf("SQL Warning:\n");
printf("\n SQLCA Dump (SQLCODE = %d)\n",sqlca->sqlcode);
printf(" ---------------------------------------------------------------------------\n");
for (i=0; i<SQLCA_SIZE; i+=20) {
printf(" ");
for (j=0; j<20; j++) {
if (i+j < SQLCA_SIZE) printf("%02X",((unsigned char *)sqlca)[i+j]);
else printf(" ");
if (j % 2) printf(" ");
} /* end for */
printf("| ");
for (j=0; j<20; j++) {
if (i+j < SQLCA_SIZE)
printf("%c",isprint(((char *)sqlca)[i+j]) ? ((char *)sqlca)[i+j] : '.');
else
printf(" ");
} /* end for */
printf("\n");
} /* end for */
printf(" ---------------------------------------------------------------------------\n\n");
if (errdata !=NULL && sqlca->sqlcode >=-32768 && sqlca->sqlcode <=32767)
errdata[MAXERRORS/2 + sqlca->sqlcode]++;
printf("\tSQLCODE:\t%ld\n",sqlca->sqlcode);
printf("\tSQLERRP:\t");
for (i=0; i<8; i++)
printf("%c",sqlca->sqlerrp[i]);
printf("\n");
for (i=0; i<6; i++)
if (sqlca->sqlerrd[i])
printf("\tSQLERRD%d:\t%d\n",i+1,sqlca->sqlerrd[i]);
for (i=0; i<11; i++)
if (sqlca->sqlwarn[i] != ' ')
printf("\tSQLWARN%c:\t%c\n",'0'+i,sqlca->sqlwarn[i]);
printf("\tSQLSTATE:\t");
for (i=0; i<5; i++)
printf("%c",sqlca->sqlstate[i]);
printf("\n");
if (sqlca->sqlcode != 0) {
switch (rc=sqlaintp(errbuf,500,0,sqlca)) {
case -1:
printf("SQCERROR--Insufficient memory to GET ERROR MESSAGE.\n");
printf("\tSQLERRMC:\t%s\n",errbuf);
sprintf(errbuf,"sqlaintp=%d",rc);
return;
break;
case -2:
printf("SQCERROR--No error.\n");
printf("\tSQLERRMC:\t%s\n",errbuf);
sprintf(errbuf,"sqlaintp=%d",rc);
break;
case -3:
printf("SQCERROR--SQLCA does not reference a valid error.\n");
printf("\tSQLERRMC:\t%s\n",errbuf);
sprintf(errbuf,"sqlaintp=%d",rc);
return;
break;
case -4:
printf("SQCERROR--The line width is less than 0.\n");
printf("\tSQLERRMC:\t%s\n",errbuf);
sprintf(errbuf,"sqlaintp=%d",rc);
return;
break;
case -5:
printf("SQCERROR--The SQLCA is invalid.\n");
printf("\tSQLERRMC:\t%s\n",errbuf);
sprintf(errbuf,"sqlaintp=%d",rc);
return;
break;
default:
if (rc < 0) {
printf("SQCERROR--Unknown return code from sqlaintp. (rc = %d)\n",rc);
printf("\tSQLERRMC:\t%s\n",errbuf);
sprintf(errbuf,"sqlaintp=%d",rc);
return;
} /* end if */
break;
} /* end switch */
printf("\tSQLERRMC:\t%s\n",errbuf);
} else {
printf("\tSQLERRMC:\tn/a\n");
} /* end if */
} /* end if */
return;
} /* end sqlerror */
/******************************************************************************
* display_errors -- This routine displays a summary of the errors in the log.
******************************************************************************/
void display_errors(void) {
long i; /* Loop counter */
if (errdata != NULL) {
printf("\n\n===========================================\n");
printf(" Error Summary\n");
printf("===========================================\n\n");
printf(" SQLCODE| Count\n");
printf(" -------+-------\n");
for (i=0; i<MAXERRORS; i++)
if (errdata[i])
printf(" %6ld | %5d\n",i - MAXERRORS/2,errdata[i]);
printf(" ---------------\n");
} /* end if */
return;
} /* end display_errors */
/******************************************************************************
* free_error_log -- This routine releases the buffer for recording error
* occurrences.
******************************************************************************/
void free_error_log(void) {
long i = 0; /* Loop counter */
free(errdata);
return;
} /* end free_error_log */