You are on page 1of 12

EMBEDDED SQL IN C: GETTING STARTED

Frans Coenen
Liverpool University Department of Computer Science
September 1992 (Revised May 1997 & March 1998) Version III

Contents: 1. 2. 3. 4. 5. 6. 7. Introduction. Syntax. SQLCA. DEClaration GENerator. The cursor. Example program. Final note on empty fields.

1. INTRODUCTION
Embedded SQL is an embedding of the database language SQL into a procedural programming language (C in this document) referred to as the host language.

2. SYNTAX
The syntax of a SQL database statement embedded in any host language is as follows:

(margin) EXEC SQL SQL_STATEMENT (terminator)

In C there is no specified margin, the terminator is ";".

2.1 An example program outline


Below is given a (very rough) outline of an example embedded SQL program which features the elements common to most embedded SQL applications. The program retrieves an employee's name where emp_number is equal to 10001 from a table called employees contained in a database called frans and then prints them.
/* Begin program */ EXEC SQL INCLUDE SQLCA; EXEC SQL BEGIN DECLARE SECTION host_name character_string(20) host_emp_number integer EXEC SQL END DECLARE SECTION EXEC SQL WHENEVER SQLERROR STOP EXEC SQL CONNECT frans /* Formulate query, something like: */ EXEC SQL SELECT name emp_number INTO host_name host_emp_number FROM employees WHERE emp_number = 10001 /* Print host_name and host_emp_number */ EXEC SQL DISCONNECT /* End program */

All statements that begin EXEC SQL are embedded SQL database statements. The function of some of the above statements are obvious others require some additional explanation as follows: 1. EXEC SQL INCLUDE SQLCA: Incorporates SQL's error handling mechanism (SQL Communications Area). 2. DECLARE SECTION: Host variables must be declared to SQL prior to their use in any embedded SQL statements (the variable names used may be identical to those contained in the actual table). 3. EXEC SQL WHENEVER SQLERROR STOP: An error handling mechanism must precede all executable embedded SQL statements in a program. 4. EXEC SQL CONNECT personnel: Initiates access to the frans data base. A CONNECT statement must precede any references to a database.

5. EXEC SQL SELECT: This is the familiar SQL select statement. It is followed by an INTO clause. This associates retrieved values with host variables in the program. 6. EXEC SQL DISCONNECT: Severs the connection between the program and the database. We are not limited to SELECT statements. We can embed any statement available in standard SQL. For example we can create a table as follows:
EXEC SQL CREATE TABLE employees (name c20, emp_number int)

This creates a table called employees that has two fields, name a character field with 20 characters and emp_number an integer field.

3. SQLCA
One of the results of issuing an INCLUDE SQLCA statement is the declaration of the SQLCA structure, which can be used for error handling. This structure is as follows:
typedef struct { char sqlcaid[8]; long sqlcabc; long sqlcode; struct { short sqlerrml; char sqlerrmc [70]; } sqlerrm; struct { char sqlwarn0; char sqlwarn1; char sqlwarn2; char sqlwarn3; char sqlwarn4; char sqlwarn5; char sqlwarn6; char sqlwarn7; } sqlwarn char sqlext[8]; } IISQLCA; static IISQLCA sqlca = {0};

Many of the variables defined by the IISQLCA structure are not used. However the following should be noted:

1. The SQLCA is initialised after the connect statement is issued. The fields sqlcaid and sqlcabc are initialised to the string "SQCLA" and the constant 136, respectively. These values do not change. 2. The nested structure sqlerrm is a varying length character string consisting of two variables sqlerrml and sqlerrmc. If sqlerrml is set to 1 an error has been encountered, otherwise it is set to 0. 3. sqlcode is an integer which is set as follows: o 0 = Statement executed successfully. o < 0 = An error has occurred. The value is the negative of the error number. o > 0 = Statement executed successfully but some exceptional condition has occurred, e.g. no data processed (empty field encountered). N.B.
o sqlcode = 100: empty row encountered. o sqlcode = -40202: row containing empty fields encountered. The sqlerrd array is a set of six 4 byte integers. Only sqlerrd(3) is used number of rows affected by an INSERT, UPDATE or DELETE statement. Variables sqlwarn0 to sqlwarn7 denote warnings.

4. 5.

to indicate the

Error handling with SQCLA can be done implicitly by using WHENEVER statements, or explicitly by checking the contents of the SQLCA fields sqlcode, sqlerrml and sqlwarn[3].

4. DCLGEN (DEClaration GENerator).


DCLGEN

is a structure-generating utility that maps the columns of a database table into a structure that you can include in a variable declaration. DCLGEN is invoked thus:
dclgen language dbname tablename filename structurename

where:
language = The embedded SQL host language, for example C. dbname = Name of database containing the table of interest, for example frans. tablename = Name of table, for example employees. filename = Output filename in which the output declaration is placed, for example

employees.dcl.
structurename = Name of the example employees_struct.

host language structure that the command generates, for

Thus:
dclgen c frans employees employees.dcl employees_struct

If you invoke this you should see output of the form:


INGRES DCLGEN -- Copyright (c) 1985, 1993 Ingres Corporation DCLGEN: Working . . . DCLGEN: Describing table 'employees' . . .

The file employees.dcl created by this command will contain a comment and two statements as follows:
/* Description of table employees from database frans */ EXEC SQL DECLARE employees TABLE (emp_number integer, name c20); struct employees_struct_ { long emp_number; char name[21]; } employees_struct;

Note that the structure tag is the structure name followed by an underscore character "_". Note also that the length of the character buffers has been increased by 1 to accommodate the C null terminator. To incorporate this file into a C program it must be compiled into c code using the esqlc compiler. Thus:
esqlc employees.dcl

This will produce a file employees.c as follows:


/* Description of table employees from database frans */ /* # line 6 "employees.dcl" */ /* host code */ struct employees_struct_ { long emp_number; char name[21]; } employees_struct;

No specific reference to this file will be made in the application embedded sql in C source code, however, it is required when this source code compiled.

5. THE CURSOR.
We have noted that data can be retrieved from an INGRES database using the select SQL statement. However the select statement is not in it self sufficient to perform this task in embedded SQL. This is because an application needs to process each record, or row, individually. The syntax of the select statement offers no way of doing this in an

embedded setting. To do this embedded SQL uses a cursor. This can be thought of as a row marker. Special cursor-oriented statements are available. What ever the case a cursor must be declared before it can be used. For example:
EXEC SQL DECLARE empcsr CURSOR FOR SELECT name, emp_number FROM employees WHERE emp_number = 10001;

Once declared we must "open" the cursor:


EXEC SQL OPEN empcsr;

5.1. "FETCHING" the data


The data retrieved using a select statement can be assigned to the host variables, a single row at a time, with the FETCH statement. For example:
EXEC SQL FETCH empcsr INTO :host_name, :host_emp_number;

where empcsr is the cursor name (declared earlier), host_name and host_emp_number are host variable names, note the use of the ":" symbol preceding each host name. The statement performs two functions. First it moves the cursor to the next row in the table, which becomes the current row. Then it loads the values indicated in the SELECT part of the DECLARE CURSOR statement into the host variables. The data can then be processed as required. When a cursor is no longer required it should be "closed":
EXEC SQL CLOSE empcsr

5.2 The SQL WHENEVER statement


WHENEVER

provides a convenient method for handling error and exception conditions arising from embedded SQL database statements. It stipulates that some action occurs when the program attains a specific condition. The following conditions may be tested for: 1. SQLWARNING. Indicates that the last embedded SQL statement produced a warning condition. 2. SQLERROR. Indicates that an error occurred in the last SQL statement. 3. NOTFOUND. Indicates that an SQL SELECT, FETCH, UPDATE or DELETE statement affected no rows. Possible actions may be one of the following:

4. COPY SQLERROR. Allows access to the text of an error message. If a program has determined that an error has occurred (sqlcode is negative) it can use this statement to put the text of the error message into a variable that can be printed. 5. CONTINUE. 6. STOP. 7. GOTO. 8. CALL

5.3 Putting The Above Together


Below the example outline embedded SQL program given in Section 2.1 has been extended to illustrate cursor processing (and to make it more c like).
/* Begin program */ EXEC SQL INCLUDE SQLCA; EXEC SQL BEGIN DECLARE SECTION; host_name character_string(21); host_emp_number int; EXEC SQL END DECLARE SECTION ; EXEC SQL WHENEVER SQLERROR STOP; EXEC SQL CONNECT frans; /* Define and open cursor */ EXEC SQL DECLARE empcsr CURSOR FOR SELECT name, emp_number FROM employees WHERE emp_number = 10001; EXEC SQL OPEN empcsr; EXEC SQL WHENEVER NOT FOUND GOTO close_empcsr; /* Loop indefinitely (the WHENEVER NOT FOUND statement will cause the loop to be terminated when the end of the table is encountered. */ /* Commence loop */ EXEC SQL FETCH INTO :host_name, :host_emp_number; /* Print host_name and host_emp_number */ /* End loop */ /* Close and disconnect */ close_empcsr: EXEC SQL CLOSE close_empcsr;

EXEC SQL DISCONNECT /* End program */

6. WRITING AN EMBEDDED SQL FILE IN C


To write an executable embedded SQL in C program the following sequence of steps should be followed: 9. Run the dclgen utility:
10. dclgen language dbname tablename filname1 structurename Compile the resulting .dcl file using the appropriate esql compiler, esqlc

11.
12.

in the

case of the C programming language:


esqlc filename1.dcl

13. Write the C source code but give it the post fix .sc (not .c). 14. Compile the SQL routines in the .sc source file into C using the appropriate esql compiler (i.e. esqlc this case):
15. esqlc filename.sc

This creates a file filename.c which can be examined. 16. Compile the .c file using
17. cc -Aa -o filename filename.c /cs/apps3/ingres/ingres/lib/libingres.a -lm -lc

or using the shorthand version:


ingcc -o filename filename.c

This then creates a file filename which is the final executable.

6.1 An embedded SQL in C example program


We will now put all of the above together to produce a program, tempest, that retrieves an employee's name where emp_number is equal to 10001 from a table called employees contained in a database called frans and then prints the result. The .sc code is as follows:
/* ---------------------------------------- */ /* */ /* EMBEDDED SQL IN C TEST PROGRAM */ /* */ /* Frans Coenen */

/* (20 March, 1998) */ /* */ /* ---------------------------------------- */ /* To compile (assuming an appropriate .dcl file exists): "esqlc tempest.sc" and then "cc -Aa -o tempest tempest.c /cs/apps3/ingres/ingres/lib/libingres.a -lm -lc" */ /* Include statements. */ #include EXEC SQL INCLUDE SQLCA; /* Function prototypes */ void cleanUp(void); /* ------ MAIN ------ */ /* Top level function */ void main(void) { EXEC SQL BEGIN DECLARE SECTION; char host_name[21]; int host_emp_number; EXEC SQL INCLUDE "employees.dcl"; EXEC SQL END DECLARE SECTION; EXEC SQL DECLARE empcsr CURSOR FOR SELECT name, emp_number FROM employees WHERE emp_number = 10001; /* An error when opening the frans database will cause the error to be printed and the program to be aborted */ EXEC SQL WHENEVER SQLERROR STOP; EXEC SQL CONNECT frans; /* Errors from here on will cause the program to clean up */ EXEC SQL WHENEVER SQLERROR CALL cleanUp; EXEC SQL OPEN empcsr; printf("Some values from the /"employees/" table /n"); /* When ever no more rows are fetched, close the cursor */ EXEC SQL WHENEVER NOT FOUND GOTO close_empcsr; /* The last executable SQL statement was OPEN so we know that the value of "sqlcode" cannot be SQLERROR or NOT FOUND */ while(sqlca.sqlcode == 0) { /* Loop is broken by NOT FOUND */

EXEC SQL FETCH empcsr INTO :host_name, :host_emp_number; /* This "printf" does not execute after the previous FETCH returns the NOT FOUND condition */ printf("%s %d/n",host_name,host_emp_number); } /* From this point onwards the program ignore all errors. Also turn off the NOT FOUND condition for consistency */ EXEC SQL WHENEVER SQLERROR CONTINUE; EXEC SQL WHENEVER NOT FOUND CONTINUE; close_empcsr: EXEC SQL CLOSE empcsr; EXEC SQL DISCONNECT; } /* ------ CLEAN UP ------ */ /* Error handling procedure (print error and disconnect) */ void cleanUp(void) { EXEC SQL BEGIN DECLARE SECTION; char errmsg[10]; EXEC SQL END DECLARE SECTION; EXEC SQL INQUIRE_SQL (:errmsg = ERRORTEXT); /* Get error message. */ /* Alternatively to get only the error number EXEC SQL COPY SQLERROR INTO :errmsg WITH 256; */ printf("Aborting because of error: /n%s/n",errmsg); EXEC SQL DISCONNECT; exit(-1); }

Remember that a label in C begins with an alphabetic character or an underscore, it must be the first word on the line and must be terminated with a ":", thus close_empcsr:. Alternatively. to select all records in the employees table we would define the cursor as follows:
EXEC SQL DECLARE empcsr CURSOR FOR SELECT name, emp_number FROM employees;

6.2 Discussion

18. EXEC SQL INCLUDE: The INCLUDE statement provides a means of including external SQL files in the source code. Here it is used to include the SQLCA structure and the papers.dcl files. 19. EXEC SQL BEGIN .... END DECLARE SECTION: Host variables must be declared prior to their use. The declaration types must match those in the .dcl file. Also note the EXEC SQL INCLUDE "papers.dcl" statement. Host variables can be global or local. 20. EXEC SQL DECLARE CURSOR: Names a cursor for use with a specified set of retrieval criteria. Once declared the cursor can be opened, using an OPEN statement, which causes the select statement specified in DECLARE CURSOR to be executed. The run time retrieval actually occurs when a FETCH is subsequently performed. When all processing has been completed the cursor can be closed with the CLOSE statement. 21. EXEC SQL WHENEVER SQLERROR STOP: The WHENEVER statement stipulates that some action occurs when a given condition is satisfied. In this case the action is STOP and the condition SQLERROR. The SQLERROR condition is satisfied if sqlcode in the SQCLA structure is negative (this indicates that an error has occurred.) The STOP action simply terminates program execution after printing an error message. 22. EXEC SQL CONNECT: Connects the program to the named database. The statement must precede any statements using the database. 23. EXEC SQL WHENEVER SQLERROR CALL: Similar to 4. The CALL action is used to call a host language procedure (cleanUp in this case). No arguments can be passed to the procedure. 24. EXEC SQL OPEN: Opens the cursor (empcsr) for processing. A cursor must be opened before any data manipulation statements can be performed e.g. a FETCH statement. 25. EXEC SQL WHENEVER NOT FOUND GOTO: Similar to 4 and 6. The NOT FOUND condition becomes true when sqlcode in the SQLCA structure is set to a value of 100 thus indicating that (say) a FETCH statement affected no rows. The GOTO action is the same as the "goto" statement in C (apologies to Dijkstra!). 26. EXEC SQL FETCH: Used by the cursor (empcsr). It first advances the open cursor one row. Next it loads the values indicated in the SELECT clause of the DECLARE CURSOR statement into the host variables listed in its INTO clause. Once loaded into the host variables the values can be further processed. 27. EXEC SQL WHENEVER SQLERROR CONTINUE and EXEC SQL WHENEVER NOT FOUND CONTINUE: Similar to 4, 6 and 8. The CONTINUE action simply means that no action should be taken based on the associated condition, the program proceeds to the next statement. 28. EXEC SQL CLOSE: Close the open cursor (empcsr) 29. EXEC SQL DISCONNECT: Terminate access to the (frans) database. 30. EXEC SQL COPY SQLERROR: Allows access to text of error messages. Thus when an error occurs, i.e. sqlcode is negative, the text of the error message can be put into the name variable errmsg) and printed.

7. NOTE ON EMPTY FIELDS.


To list all fields in a table which includes empty fields will result in an error (sqlcode = 40202) when ever an empty field is encountered. We can test for this and over ride the error. However the result is that the entry from the last occasion when an instantiated value for the field was encountered is printed. This can not be tested for as entries for fields may correctly be repeated. Further there is no indication of which fields are empty. The error code only indicates that one or more empty field in a row have been encountered and not the position of the field or fields within the row. At present the only solution would appear to be to include a null string in empty character fields and a value of zero in empty numeric fields. This will be quite acceptable in the database under discussion here, however in some applications a numeric value of zero may be a legitimate value. It is hoped that a solution will be found in the near future!

Created and maintained by Frans Coenen. Last updated 10 January 2002

You might also like