The dyn_sql Program
The dyn_sql.ec program is an ESQL/C demonstration program that uses dynamic SQL. The program prompts the user to enter a SELECT statement for the stores7 demonstration database and then uses a system-descriptor area to execute the SELECT dynamically.
By default, the program opens the stores7 database. If the demonstration database has been given a name other than stores7, however, you can specify the database name on the command line. The following command runs the dyn_sql executable on the mystores7 database:
Compiling the Program
Use the following command to compile the dyn_sql program:
The -o dyn_sql option causes the executable program to be named dyn_sql. Without the -o option, the name of the executable program defaults to a.out. For more information on the esql preprocessor command, see "Using the esql Command".
Guide to the dyn_sql.ec File
1
2 /*
3 This program prompts the user to enter a SELECT statement
4 for the stores7 database. It processes the statement using dynamic sql
5 and system descriptor areas and displays the rows returned by the
6 database server.
7 */
8 #include <stdio.h>
9 #include <ctype.h>
10 EXEC SQL include sqltypes;
11 EXEC SQL include locator;
12 EXEC SQL include datetime;
13 EXEC SQL include decimal;
14 #define WARNNOTIFY 1
15 #define NOWARNNOTIFY 0
16 #define LCASE(c) (isupper(c) ? tolower(c) : (c))
17 #define BUFFSZ 256
18 extern char statement[80];
Continued on page 16-50
Lines 8 to 13
These lines specify C and ESQL/C files to include in the program. The stdio.h file enables dyn_sql to use the standard C I/O library. The ctypes.h file contains macros that check the attributes of a character. For example, one macro determines whether a character is uppercase or lowercase.
The sqltypes.h header file contains symbolic constants that correspond to the data types that are found in Informix databases. The program uses these constants to determine the data types of columns that the dynamic SELECT statement returns.
The locator.h file contains the definition of the locator structure (loc_t), which is the type of host variable needed for TEXT and BYTE columns. The datetime.h file contains definitions of the datetime and interval structures, which are the data types of host variables for DATETIME and INTERVAL columns. The decimal.h file contains the definition of the dec_t structure, which is the type of host variable needed for DECIMAL columns.
Lines 14 to 17
The exp_chk() exception-handling function uses the WARNNOTIFY and NOWARNNOTIFY constants (lines 14 and 15). The second argument of exp_chk() tells the function to display information in the SQLSTATE and SQLCODE variables for warnings (WARNNOTIFY) or not to display information for warnings (NOWARNNOTIFY). The exp_chk() function is in the exp_chk.ec source file. For a description, see "Guide to the exp_chk.ec File".
Line 16 defines LCASE, a macro that converts an uppercase character to a lowercase character. Line 17 defines BUFFSZ to be the number 256 . The program uses BUFFSZ to specify the size of arrays that store input from the user.
Line 18
Line 18 declares statement as an external global variable to hold the name of the last SQL statement that the program asked the database server to execute. The exception-handling functions use this information. (See "Lines 399 to 406".)
19 EXEC SQL BEGIN DECLARE SECTION;
20 loc_t lcat_descr;
21 loc_t lcat_picture;
22 EXEC SQL END DECLARE SECTION;
23 int whenexp_chk();
24 main(argc, argv)
25 int argc;
26 char *argv[];
27 {
28 long ret, getrow();
29 short data_found = 0;
30 EXEC SQL BEGIN DECLARE SECTION;
31 char ans[BUFFSZ], db_name[30];
32 char name[40];
33 int sel_cnt, i;
34 short type;
35 EXEC SQL END DECLARE SECTION;
36 printf("DYN_SQL Sample ESQL Program running.\n\n");
37 EXEC SQL whenever sqlerror call whenexp_chk;
38 if (argc > 2) /* correct no. of args? */
39 {
40 printf("\nUsage: %s [database]\nIncorrect no. of argument(s)\n",
41 argv[0]);
42 printf("\nDYN_SQL Sample Program over.\n\n");
43 exit(1);
44 }
45 strcpy(db_name, "stores7");
46 if(argc == 2)
47 strcpy(db_name, argv[1]);
48 sprintf(statement,"CONNECT TO %s",db_name);
49 EXEC SQL connect to :db_name;
50 printf("Connected to %s\n", db_name);
51 ++argv;
Continued on page 16-52
Lines 19 to 23
Lines 19 to 23 define the global host variables that are used in SQL statements. Lines 20 and 21 define the locator structures that are the host variables for the cat_descr and cat_picture columns of the catalog table.
Lines 24 to 27
The main() function is the point where the program begins to execute. The argc parameter gives the number of arguments from the command line when the program was invoked. The argv parameter is an array of pointers to command-line arguments. This program expects only one argument (the name of the database to be accessed), and it is optional.
Lines 28 to 51
Line 28 defines a long integer data type (ret) to receive a return value from the getrow() function. Line 28 also declares that the getrow() function returns a long integer data type. Line 37 executes the WHENEVER statement to transfer control to whenexp_chk() if any errors occur in SQL statements. For more information on the whenexp_chk() function, see "Guide to the exp_chk.ec File".
Lines 30 to 35 define the host variables that are local to the main() program block. Lines 45 to 51 establish a connection to a database. If argc equals 2 , the program assumes that the user entered a database name on the command line (by convention the first argument is the name of the program), and the program opens this database. If the user did not enter a database name on the command line, the program opens the stores7 database (see line 45), which is the default. In both cases, the program connects to the default database server that is specified by the INFORMIXSERVER environment variable because no database server is specified.
52 while(1)
53 {
54 /* prompt for SELECT statement */
55 printf("\nEnter a SELECT statement for the %s database",
56 db_name);
57 printf("\n\t(e.g. select * from customer;)\n");
58 printf("\tOR a ';' to terminate program:\n>> ");
59 if(!getans(ans, BUFFSZ))
60 continue;
61 if (*ans == ';')
62 {
63 strcpy(statement, "DISCONNECT");
64 EXEC SQL disconnect current;
65 printf("\nDYN_SQL Sample Program over.\n\n");
66 exit(1);
67 }
68 /* prepare statement id */
69 printf("\nPreparing statement (%s)...\n", ans);
70 strcpy(statement, "PREPARE sel_id");
71 EXEC SQL prepare sel_id from :ans;
72 /* declare cursor */
73 printf("Declaring cursor 'sel_curs' for SELECT...\n");
74 strcpy(statement, "DECLARE sel_curs");
75 EXEC SQL declare sel_curs cursor for sel_id;
76 /* allocate descriptor area */
77 printf("Allocating system-descriptor area...\n");
78 strcpy(statement, "ALLOCATE DESCRIPTOR selcat");
79 EXEC SQL allocate descriptor 'selcat';
80 /* Ask the database server to describe the statement */
81 printf("Describing prepared SELECT...\n");
82 strcpy(statement,
83 "DESCRIBE sel_id USING SQL DESCRIPTOR selcat");
84 EXEC SQL describe sel_id using sql descriptor 'selcat';
85 if (SQLCODE != 0)
86 {
87 printf("** Statement is not a SELECT.\n");
88 free_stuff();
89 strcpy(statement, "DISCONNECT");
90 EXEC SQL disconnect current;
91 printf("\nDYN_SQL Sample Program over.\n\n");
92 exit(1);
93 }
Continued on page 16-54
Lines 52 to 67
The while(1) on line 52 begins a loop that continues to the end of the main() function. Lines 55 to 58 prompt the user to enter either a SELECT statement or, to terminate the program, a semicolon. The getans() function receives the input from the user. If the first character is not a semicolon, the program continues to process the input.
Lines 68 to 75
The PREPARE statement prepares the SELECT statement (which the user enters) from the array ans[] and assigns it the statement identifier sel_id. The PREPARE statement enables the database server to parse, validate, and generate an execution plan for the statement.
The DECLARE statement (lines 72 to 75) creates the sel_curs cursor for the set of rows that the SELECT statement returns, in case it returns more than one row.
Lines 76 to 79
The ALLOCATE DESCRIPTOR statement allocates the selcat system-descriptor area in memory. The statement does not include the WITH MAX clause and, therefore, uses the default memory allocation, which is for 100 columns.
Lines 80 to 93
The DESCRIBE statement obtains information from the database server about the statement that is in the sel_id statement identifier. The database server returns the information in the selcat system-descriptor area, which the preceding ALLOCATE DESCRIPTOR statement creates. The information that DESCRIBE puts into the system-descriptor area includes the number, names, data types, and lengths of the columns in the select list.
The DESCRIBE statement also sets the SQLCODE variable to a number that indicates the type of statement that was described. To check whether the statement type is SELECT, line 85 compares the value of SQLCODE to 0 (the value defined in the sqlstypes.h file for a SELECT statement with no INTO TEMP clause). If the statement is not a SELECT, line 87 displays a message to that effect and frees the cursor and the resources that have been allocated. Then it closes the connection and exits.
94 /* Determine the number of columns in the select list */
95 printf("Getting number of described values from ");
96 printf("system-descriptor area...\n");
97 strcpy(statement, "GET DESCRIPTOR selcat: COUNT field");
98 EXEC SQL get descriptor 'selcat' :sel_cnt = COUNT;
99 /* open cursor; process select statement */
100 printf("Opening cursor 'sel_curs'...\n");
101 strcpy(statement, "OPEN sel_curs");
102 EXEC SQL open sel_curs;
103 /*
104 * The following loop checks whether the cat_picture or
105 * cat_descr columns are described in the system-descriptor area.
106 * If so, it initializes a locator structure to read the simplelarge-object
107 * data into memory and sets the address of the locator structure
108 * in the system-descriptor area.
109 */
110 for(i = 1; i <= sel_cnt; i++)
111 {
112 strcpy(statement,
113 "GET DESCRIPTOR selcat: TYPE, NAME fields");
114 EXEC SQL get descriptor 'selcat' VALUE :i
115 :type = TYPE,
116 :name = NAME;
117 if (type == SQLTEXT && !strncmp(name, "cat_descr",
118 strlen("cat_descr")))
119 {
120 lcat_descr.loc_loctype = LOCMEMORY;
121 lcat_descr.loc_bufsize = -1;
122 lcat_descr.loc_oflags = 0;
123 strcpy(statement, "SET DESCRIPTOR selcat: DATA field");
124 EXEC SQL set descriptor 'selcat' VALUE :i
125 DATA = :lcat_descr;
126 }
127 if (type == SQLBYTES && !strncmp(name, "cat_picture",
128 strlen("cat_picture")))
129 {
130 lcat_picture.loc_loctype = LOCMEMORY;
131 lcat_picture.loc_bufsize = -1;
132 lcat_picture.loc_oflags = 0;
133 strcpy(statement, "SET DESCRIPTOR selcat: DATA field");
134 EXEC SQL set descriptor 'selcat' VALUE :i
135 DATA = :lcat_picture;
136 }
137 }
Continued on page 16-56
Lines 94 to 98
The GET DESCRIPTOR statement retrieves the COUNT value from the selcat system-descriptor area. The COUNT value indicates how many columns are described in the system-descriptor area.
Lines 99 to 102
The OPEN statement begins execution of the dynamic SELECT statement and activates the sel_curs cursor for the set of rows that it returns.
Lines 114 to 137
This section of the code uses the GET DESCRIPTOR statement to determine whether the simple large-object columns from the catalog table (cat_descr and cat_picture) are included in the select list. If you dynamically select a simple large-object column, you must set the address of a locator structure into the DATA field of the item descriptor to tell the database server where to return the locator structure.
First, however, the program initializes the locator structure, as follows:
(For more information on how to work with the TEXT and BYTE data types, see Chapter 8, "Working with Simple Large Objects.") Then the program uses the SET DESCRIPTOR statement to load the address of the locator structure into the DATA field of the descriptor area.
138 while(ret = getrow("selcat")) /* fetch a row */
139 {
140 data_found = 1;
141 if (ret < 0)
142 {
143 strcpy(statement, "DISCONNECT");
144 EXEC SQL disconnect current;
145 printf("\nDYN_SQL Sample Program over.\n\n");
146 exit(1);
147 }
148 disp_data(sel_cnt, "selcat"); /* display the data */
149 }
150 if (!data_found)
151 printf("** No matching rows found.\n");
152 free_stuff();
153 if (!more_to_do()) /* More to do? */
154 break; /* no, terminate loop */
155 }
156 }
157 /* fetch the next row for selected items */
158 long getrow(sysdesc)
159 EXEC SQL BEGIN DECLARE SECTION;
160 PARAMETER char *sysdesc;
161 EXEC SQL END DECLARE SECTION;
162 {
163 long exp_chk();
164 sprintf(statement, "FETCH %s", sysdesc);
165 EXEC SQL fetch sel_curs using sql descriptor :sysdesc;
166 return((exp_chk(statement)) == 100 ? 0 : 1);
167 }
Continued on page 16-58
Lines 138 to 149
The getrow() function retrieves the selected rows one by one. Each iteration of the while loop retrieves one row, which the program then processes with the disp_data() function (line 148). When all the rows are retrieved, getrow() returns a 0 (zero) and the while loop terminates. For more information on the getrow() function, see "Lines 157 to 167".
Line 152
The free_stuff() function frees resources that were allocated when the dynamic SELECT statement was processed. See "Lines 382 to 388".
Lines 153 to 156
When all the selected rows are processed, the program calls the more_to_do() function, which asks whether the user would like to process more SELECT statements. If the answer is no, more_to_do() returns 0 and the break statement terminates the while loop that began on line 52. If the answer is yes, the program begins the next iteration of the while statement on line 52 to accept and process another SELECT statement.
Lines 157 to 167
The getrow() function moves the cursor to and then fetches the next row in the set of rows that are returned by the dynamic SELECT statement. It fetches the row values into the system-descriptor area that is specified in the sysdesc variable. If there are no more rows to fetch (exp_chk() returns 100 ), getrow() returns 0 . If the FETCH encounters a runtime error, getrow() returns 1 .
168 {/*
169 * This function loads a column into a host variable of the correct
170 * type and displays the name of the column and the value, unless the
171 * value is NULL.
172 */
173 disp_data(col_cnt, sysdesc)
174 int col_cnt;
175 EXEC SQL BEGIN DECLARE SECTION;
176 PARAMETER char *sysdesc;
177 EXEC SQL END DECLARE SECTION;
178
179 EXEC SQL BEGIN DECLARE SECTION;
180 int int_data, i;
181 char *char_data;
182 long date_data;
183 datetime dt_data;
184 interval intvl_data;
185 decimal dec_data;
186 short short_data;
187 char name[40];
188 short char_len, type, ind;
189 EXEC SQL END DECLARE SECTION;
190 long size;
191 unsigned amount;
192 int x;
193 char shdesc[81], str[40], *p;
194 printf("\n\n");
195 /* For each column described in the system descriptor area,
196 * determine its data type. Then retrieve the column name and its
197 * value, storing the value in a host variable defined for the
198 * particular data type. If the column is not NULL, display the
199 * name and value.
200 */
201 for(i = 1; i <= col_cnt; i++)
202 {
203 strcpy(statement, "GET DESCRIPTOR: TYPE field");
204 EXEC SQL get descriptor :sysdesc VALUE :i
205 :type = TYPE;
206 switch(type)
207 {
208 case SQLSERIAL:
209 case SQLINT:
210 strcpy(statement,
211 "GET DESCRIPTOR: NAME, INDICATOR, DATA fields");
212 EXEC SQL get descriptor :sysdesc VALUE :i
213 :name = NAME,
214 ind = INDICATOR,
215 :int_data = DATA;
216 if(ind == -1)
217 printf("\n%.20s: NULL", name);
218 else
219 printf("\n%.20s: %d", name, int_data);
220 break;
221 case SQLSMINT:
222 strcpy(statement,
223 "GET DESCRIPTOR: NAME, INDICATOR, DATA fields");
224 EXEC SQL get descriptor :sysdesc VALUE :i
225 :name = NAME,
226 :ind = INDICATOR,
227 :short_data = DATA;
228 if(ind == -1)
229 printf("\n%.20s: NULL", name);
230 else
231 printf("\n%.20s: %d", name, short_data);
232 break;
233 case SQLDECIMAL:
234 case SQLMONEY:
235 strcpy(statement,
236 "GET DESCRIPTOR: NAME, INDICATOR, DATA fields");
237 EXEC SQL get descriptor :sysdesc VALUE :i
238 :name = NAME,
239 :ind = INDICATOR,
240 :dec_data = DATA;
241 if(ind == -1)
242 printf("\n%.20s: NULL", name);
243 else
244 {
245 if(type == SQLDECIMAL)
246 rfmtdec(&dec_data, "###,###,###.##", str);
247 else
248 rfmtdec(&dec_data, "$$$,$$$,$$$.$$", str);
249 printf("\n%.20s: %s", name, str);
250 }
251 break;
252 case SQLDATE:
253 strcpy(statement,
254 "GET DESCRIPTOR: NAME, INDICATOR, DATA fields");
255 EXEC SQL get descriptor :sysdesc VALUE :i
256 :name = NAME,
257 :ind = INDICATOR,
258 :date_data = DATA;
259 if(ind == -1)
260 printf("\n%.20s: NULL", name);
261 else
262 {
263 if((x = rfmtdate(date_data, "mmm. dd, yyyy",
264 str)) < 0)
265 printf("\ndisp_data() - DATE - fmt error");
266 else
267 printf("\n%.20s: %s", name, str);
268 }
269 break;
270 case SQLDTIME:
271 strcpy(statement,
272 "GET DESCRIPTOR: NAME, INDICATOR, DATA fields");
273 EXEC SQL get descriptor :sysdesc VALUE :i
274 :name = NAME,
275 :ind = INDICATOR,
276 :dt_data = DATA;
277 if(ind == -1)
278 printf("\n%.20s: NULL", name);
279 else
280 {
281 x = dttofmtasc(&dt_data, str, sizeof(str), 0);
282 printf("\n%.20s: %s", name, str);
283 }
284 break;
285 case SQLINTERVAL:
286 strcpy(statement,
287 "GET DESCRIPTOR: NAME, INDICATOR, DATA fields");
288 EXEC SQL get descriptor :sysdesc VALUE :i
289 :name = NAME,
290 :ind = INDICATOR,
291 :intvl_data = DATA;
292 if(ind == -1)
293 printf("\n%.20s: NULL", name);
294 else
295 {
296 if((x = intofmtasc(&intvl_data, str,
297 sizeof(str),
298 "%3d days, %2H hours, %2M minutes"))
299 < 0)
300 printf("\nINTRVL - fmt error %d", x);
301 else
302 printf("\n%.20s: %s", name, str);
303 }
304 break;
305 case SQLVCHAR:
306 case SQLCHAR:
307 strcpy(statement,
308 "GET DESCRIPTOR: LENGTH, NAME fields");
309 EXEC SQL get descriptor :sysdesc VALUE :i
310 :char_len = LENGTH,
311 :name = NAME;
312 amount = char_len;
313 if(char_data = (char *)(malloc(amount + 1)))
314 {
315 strcpy(statement,
316 "GET DESCRIPTOR: NAME, INDICATOR, DATA fields");
317 EXEC SQL get descriptor :sysdesc VALUE :i
318 :char_data = DATA,
319 :ind = INDICATOR;
320 if(ind == -1)
321 printf("\n%.20s: NULL", name);
322 else
323 printf("\n%.20s: %s", name, char_data);
324 }
325 else
326 {
327 printf("\n%.20s: ", name);
328 printf("Can't display: out of memory");
329 }
330 break;
331 case SQLTEXT:
332 strcpy (statement,
333 "GET DESCRIPTOR: NAME, INDICATOR, DATA fields");
334 EXEC SQL get descriptor :sysdesc VALUE :i
335 :name = NAME,
336 :ind = INDICATOR,
337 :lcat_descr = DATA;
338 size = lcat_descr.loc_size; /* get size of data */
339 printf("\n%.20s: ", name);
340 if(ind == -1)
341 {
342 printf("NULL");
343 break;
344 }
345 p = lcat_descr.loc_buffer; /* set p to buf addr */
346 /* print buffer 80 characters at a time */
347 while(size >= 80)
348 {
349 /* mv from buffer to shdesc */
350 ldchar(p, 80, shdesc);
351 printf("\n%80s", shdesc); /* display it */
352 size -= 80; /* decrement length */
353 p += 80; /* bump p by 80 */
354 }
355 strncpy(shdesc, p, size);
356 shdesc[size] = '\0';
357 printf("%-s\n", shdesc); /* dsply last segment */
358 break;
359 case SQLBYTES:
360 strcpy (statement,
361 "GET DESCRIPTOR: NAME, INDICATOR fields");
362 EXEC SQL get descriptor :sysdesc VALUE :i
363 :name = NAME,
364 :ind = INDICATOR;
365 if(ind == -1)
366 printf("%.20s: NULL", name);
367 else
368 {
369 printf("%.20s: ", name);
370 printf("Can't display BYTE type value");
371 }
372 break;
373 default:
374 printf("\nUnexpected data type: %d", type);
375 EXEC SQL disconnect current;
376 printf("\nDYN_SQL Sample Program over.\n\n");
377 exit(1);
378 }
379 }
380 printf("\n");
381 }
Continued on page 16-64
Lines 168 to 381
The disp_data() function displays the values that are stored in each row that the SELECT statement returns. The function must be able to receive and process any data type within the scope of the dynamic SELECT statement (in this case, any column within the stores7 database). This function accepts two arguments: col_cnt contains the number of columns that are contained in the system-descriptor area, and sysdesc contains the name of the system-descriptor area that contains the column information. This second argument must be declared with the PARAMETER keyword because the argument is used in the FETCH statement.
The disp_data() function first defines host variables for each of the data types that are found in the stores7 database (lines 179 to 189), except for the locator structures that have been globally defined already for the cat_descr and cat_picture columns of the catalog table (lines 19 to 22).
For each column that is described in the system-descriptor area, disp_data() retrieves its data type with a GET DESCRIPTOR statement. Next, disp_data() executes a switch on that data type and, for each type (column), it executes another GET DESCRIPTOR statement to retrieve the name of the column, the indicator flag, and the data. Unless the column is null, disp_data() moves the column data from the DATA field of the system-descriptor area to a corresponding host variable. Then it displays the column name and the content of the host variable.
The disp_data() function uses the symbolic constants defined in sqltypes.h to compare data types. It also uses the ESQL/C library functions rfmtdec(), rfmtdate(), dttofmtasc(), and intofmtosc() to format the DECIMAL and MONEY, DATE, DATETIME, and INTERVAL data types, respectively, for display.
For the TEXT and BYTE data types, you can retrieve the value of the column with the following two-stage process, because the database server returns a locator structure rather than the data:
1. The GET DESCRIPTOR statement (lines 334 and 362) retrieves the locator structure from the system-descriptor area and moves it to the loc_t host variable.
2. The disp_data() function obtains the address of the data buffer from the locator structure, in loc_buffer (lines 345), and retrieves the data from there.
In the case of the BYTE data type, for the sake of brevity disp_data() retrieves the locator structure but does not display the data. For an example of the type of logic required to display a BYTE column, see "Guide to the dispcat_pic.ec File".
382 free_stuff()
383 {
384 EXEC SQL free sel_id; /* free resources for statement */
385 EXEC SQL free sel_curs; /* free resources for cursor */
386 /* free system descriptor area */
387 EXEC SQL deallocate descriptor 'selcat';
388 }
389 /*
390 * The inpfuncs.c file contains the following functions used in this
391 * program:
392 * more_to_do() - asks the user to enter 'y' or 'n' to indicate
393 * whether to run the main program loop again.
394 *
395 * getans(ans, len) - accepts user input, up to 'len' number of
396 * characters and puts it in 'ans'
397 */
398 #include "inpfuncs.c"
399 /*
400 * The exp_chk.ec file contains the exception handling functions to
401 * check the SQLSTATE status variable to see if an error has occurred
402 * following an SQL statement. If a warning or an error has
403 * occurred, exp_chk() executes the GET DIAGNOSTICS statement and
404 * displays the detail for each exception that is returned.
405 */
406 EXEC SQL include exp_chk.ec;
Lines 382 to 388
The free_stuff() function frees resources that were allocated to process the dynamic statement. Line 384 frees resources that were allocated by the application when it prepared the dynamic SELECT statement. Line 385 releases resources allocated by the database server to process the sel_curs cursor. The DEALLOCATE DESCRIPTOR statement releases the memory allocated for the selcat system-descriptor area and its associated data areas.
Lines 389 to 398
Several of the ESQL/C demonstration programs also call the more_to_do() and getans() functions. Therefore, these functions are also broken out into a separate C source file and included in the appropriate demonstration program. Neither of these functions contain ESQL/C, so the program can use the C #include preprocessor statement to include the file. For a description of these functions, see "Guide to the inpfuncs.c File".
Lines 399 to 406
As a result of the WHENEVER statement on line 37, the whenexp_chk() function is called if an error occurs during the execution of an SQL statement. The whenexp_chk() function examines the SQLSTATE status variable to determine the outcome of an SQL statement. Because several demonstration programs use this function with the WHENEVER statement for exception handling, the whenexp_chk() function and its supporting functions have been broken out into a separate exp_chk.ec source file. The dyn_sql program must include this file with the ESQL/C include directive because the exception-handling functions use ESQL/C statements. The exp_chk.ec source file is described in Chapter 11, "Exception Handling."
|