A Pro*C program is compiled in two steps. First, the Pro*C precompiler recognizes the SQL statements embedded in the program, and replaces them with appropriate calls to the functions in the SQL runtime library. The output is pure C/C++ code with all the pure C/C++ portions intact. Then, a regular C/C++ compiler is used to compile the code and produces the executable. For details, see the section on Demo Programs.
{ int a; /* ... */ EXEC SQL SELECT salary INTO :a FROM Employee WHERE SSN=876543210; /* ... */ printf("The salary is %d\n", a); /* ... */ }
#define THE_SSN 876543210 /* ... */ EXEC SQL SELECT salary INTO :a FROM Employee WHERE SSN = THE_SSN; /* INVALID */
EXEC SQL WHENEVER SQLERROR GOTO error_in_SQL; /* ... */ error_in_SQL: /* do error handling */We will come to what WHENEVER means later in the section on Error Handling.
A host variable reference must be prefixed with a colon ":" in SQL statements, but should not be prefixed with a colon in C statements. When specifying a string literal via a host variable, the single quotes must be omitted; Pro*C understands that you are specifying a string based on the declared type of the host variable. C function calls and most of the pointer arithmetic expressions cannot be used as host variable references even though they may indeed resolve to lvalues. The following code illustrates both legal and illegal host variable references:
int deptnos[3] = { 000, 111, 222 }; int get_deptno() { return deptnos[2]; } int *get_deptnoptr() { return &(deptnos[2]); } int main() { int x; char *y; int z; /* ... */ EXEC SQL INSERT INTO emp(empno, ename, deptno) VALUES(:x, :y, :z); /* LEGAL */ EXEC SQL INSERT INTO emp(empno, ename, deptno) VALUES(:x + 1, /* LEGAL: the reference is to x */ 'Big Shot', /* LEGAL: but not really a host var */ :deptnos[2]); /* LEGAL: array element is fine */ EXEC SQL INSERT INTO emp(empno, ename, deptno) VALUES(:x, :y, :(*(deptnos+2))); /* ILLEGAL: although it has an lvalue */ EXEC SQL INSERT INTO emp(empno, ename, deptno) VALUES(:x, :y, :get_deptno()); /* ILLEGAL: no function calls */ EXEC SQL INSERT INTO emp(empno, ename, deptno) VALUES(:x, :y, :(*get_depnoptr())); /* ILLEGAL: although it has an lvalue */ /* ... */ }
int *x; /* ... */ EXEC SQL SELECT xyz INTO :x FROM ...;The result of this SELECT statement will be written into *x, not x.
typedef struct { char name[21]; /* one greater than column length; for '\0' */ int SSN; } Emp; /* ... */ Emp bigshot; /* ... */ EXEC SQL INSERT INTO emp (ename, eSSN) VALUES (:bigshot);
int emp_number[50]; char name[50][11]; /* ... */ EXEC SQL INSERT INTO emp(emp_number, name) VALUES (:emp_number, :emp_name);which will insert all the 50 tuples in one go.
Arrays can only be single dimensional. The example char name[50][11] would seem to contradict that rule. However, Pro*C actually considers name a one-dimensional array of strings rather than a two-dimensional array of characters. You can also have arrays of structures.
When using arrays to store the results of a query, if the size of the host array (say n) is smaller than the actual number of tuples returned by the query, then only the first n result tuples will be entered into the host array.
short indicator_var; EXEC SQL SELECT xyz INTO :host_var:indicator_var FROM ...; /* ... */ EXEC SQL INSERT INTO R VALUES(:host_var INDICATOR :indicator_var, ...);You can use indicator variables in the INTO clause of a SELECT statement to detect NULL's or truncated values in the output host variables. The values Oracle can assign to an indicator variable have the following meanings:
-1 | The column value is NULL, so the value of the host variable is indeterminate. |
0 | Oracle assigned an intact column value to the host variable. |
>0 | Oracle assigned a truncated column value to the host variable. The integer returned by the indicator variable is the original length of the column value. |
-2 | Oracle assigned a truncated column variable to the host variable, but the original column value could not be determined. |
You can also use indicator variables in the VALUES and SET
clause of an INSERT or UPDATE statement to assign
NULL's
to input host variables. The values your program can assign to an indicator
variable have the following meanings:
-1 | Oracle will assign a NULL to the column, ignoring the value of the host variable. |
>=0 | Oracle will assign the value of the host variable to the column. |
The equivalencing can be done on a variable-by-variable basis using the VAR statement. The syntax is:
EXEC SQL VAR <host_var> IS <type_name> [ (<length>) ];For example, suppose you want to select employee names from the emp table, and then pass them to a routine that expects C-style '\0'-terminated strings. You need not explicitly '\0'-terminate the names yourself. Simply equivalence a host variable to the STRING external datatype, as follows:
char emp_name[21]; EXEC SQL VAR emp_name IS STRING(21);The length of the ename column in the emp table is 20 characters, so you allot emp_name 21 characters to accommodate the '\0'-terminator. STRING is an Oracle external datatype specifically designed to interface with C-style strings. When you select a value from the ename column into emp_name, Oracle will automatically '\0'-terminate the value for you.
You can also equivalence user-defined datatypes to Oracle external datatypes using the TYPE statement. The syntax is:
EXEC SQL TYPE <user_type> IS <type_name> [ (<length>) ] [REFERENCE];You can declare a user-defined type to be a pointer, either explicitly, as a pointer to a scalar or structure, or implicitly as an array, and then use this type in a TYPE statement. In these cases, you need to use the REFERENCE clause at the end of the statement, as shown below:
typedef unsigned char *my_raw; EXEC SQL TYPE my_raw IS VARRAW(4000) REFERENCE; my_raw buffer; /* ... */ buffer = malloc(4004);Here we allocated more memory than the type length (4000) because the precompiler also returns the length, and may add padding after the length in order to meet the alignment requirement on your system.
char *s = "INSERT INTO emp VALUES(1234, 'jon', 3)"; EXEC SQL PREPARE q FROM :s; EXEC SQL EXECUTE q;Alternatively, PREPARE and EXECUTE may be combined into one statement:
char *s = "INSERT INTO emp VALUES(1234, 'jon', 3)"; EXEC SQL EXECUTE IMMEDIATE :s;
If your program exits without calling EXEC SQL COMMIT, all database changes will be discarded.
To use SQLCA you need to include the header file sqlca.h using the #include directive. In case you need to include sqlca.h at many places, you need to first undefine the macro SQLCA with #undef SQLCA. The relevant chunk of sqlca.h follows:
#ifndef SQLCA #define SQLCA 1 struct sqlca { /* ub1 */ char sqlcaid[8]; /* b4 */ long sqlabc; /* b4 */ long sqlcode; struct { /* ub2 */ unsigned short sqlerrml; /* ub1 */ char sqlerrmc[70]; } sqlerrm; /* ub1 */ char sqlerrp[8]; /* b4 */ long sqlerrd[6]; /* ub1 */ char sqlwarn[8]; /* ub1 */ char sqlext[8]; }; /* ... */The fields in sqlca have the following meaning:
sqlcaid | This string component is initialized to "SQLCA" to identify the SQL Communications Area. | ||||||||||||||||
sqlcabc | This integer component holds the length, in bytes, of the SQLCA structure. | ||||||||||||||||
sqlcode | This integer component holds the status code of the most recently executed
SQL statement:
|
||||||||||||||||
sqlerrm | This embedded structure contains the following two components:
|
||||||||||||||||
sqlerrp | Reserved for future use. | ||||||||||||||||
sqlerrd | This array of binary integers has six elements:
|
||||||||||||||||
sqlwarn | This array of single characters has eight elements used as warning
flags. Oracle sets a flag by assigning to it the character 'W'.
|
||||||||||||||||
sqlext | Reserved for future use. |
SQLCA can only accommodate error messages up to 70 characters long in its sqlerrm component. To get the full text of longer (or nested) error messages, you need the sqlglm() function:
void sqlglm(char *msg_buf, size_t *buf_size, size_t *msg_length);where msg_buf is the character buffer in which you want Oracle to store the error message; buf_size specifies the size of msg_buf in bytes; Oracle stores the actual length of the error message in *msg_length. The maximum length of an Oracle error message is 512 bytes.
EXEC SQL WHENEVER <condition> <action>;Oracle automatically checks SQLCA for <condition>, and if such condition is detected, your program will automatically perform <action>.
<condition> can be any of the following:
EXEC SQL WHENEVER SQLWARNING DO print_warning_msg(); EXEC SQL WHENEVER NOT FOUND GOTO handle_empty;Here is a more concrete example:
/* code to find student name given id */ /* ... */ for (;;) { printf("Give student id number : "); scanf("%d", &id); EXEC SQL WHENEVER NOT FOUND GOTO notfound; EXEC SQL SELECT studentname INTO :st_name FROM student WHERE studentid = :id; printf("Name of student is %s.\n", st_name); continue; notfound: printf("No record exists for id %d!\n", id); } /* ... */Note that the WHENEVER statement does not follow regular C scoping rules. Scoping is valid for the entire program. For example, if you have the following statement somewhere in your program (such as before a loop):
EXEC SQL WHENEVER NOT FOUND DO break;
All SQL statements that occur after this line in the file would be affected. Make sure you use the following line to cancel the effect of WHENEVER when it is no longer needed (such as after your loop):
EXEC SQL WHENEVER NOT FOUND CONTINUE;
Several demo programs are available in /afs/ir/class/cs145/code/proc on the leland system. They are named sample*.pc (for C users) and cppdemo*.pc (for C++ users). ".pc" is the extension for Pro*C code. Do not copy these files manually, since there are a couple of customizations to do. To download and customize the demo programs, follow the instructions below:
If you happen to make any mistake when entering username or password in Step (2), just run clean_samples <db_username> <db_passwd> <sample_dir> in your home directory, and then repeat Steps (2) to (4).
For Step (4), you can also compile each sample program separately. For example, make sample1 compiles sample1.pc alone. The compilation process actually has two phases:
The demo programs operate on the following tables:
CREATE TABLE DEPT (DEPTNO NUMBER(2) NOT NULL, DNAME VARCHAR2(14), LOC VARCHAR2(13)); CREATE TABLE EMP (EMPNO NUMBER(4) NOT NULL, ENAME VARCHAR2(10), JOB VARCHAR2(9), MGR NUMBER(4), HIREDATE DATE, SAL NUMBER(7, 2), COMM NUMBER(7, 2), DEPTNO NUMBER(2)); CREATE TABLE PAY1 (ENAME VARCHAR2(10), SAL NUMBER(7, 2)); CREATE TABLE PAY2 (ENAME VARCHAR2(10), SAL NUMBER(7, 2));These tables are created automatically when you run load_samples in Step (2). A few tuples are also inserted. You may like to browse the tables before running the samples on them. You can also play with them as you like (e.g., inserting, deleting, or updating tuples). These tables will be dropped automatically when you run clean_samples. Note:clean_samples also wipes out the entire <sample_dir>; make sure you move your own files to some other place before running this command!
You should take a look at the sample source code before running it. The comments at the top describe what the program does. For example, sample1 takes an employee's EMPNO and retrieve the name, salary, and commission for that employee from the table EMP.
You are supposed to study the sample source code and learn the following:
EXEC SQL BEGIN DECLARE SECTION; // declarations... EXEC SQL END DECLARE SECTION;You need to follow this routine for declaring the host and indicator variables at all the places you do so.
Declarative Statements | |
EXEC SQL ARRAYLEN | To use host arrays with PL/SQL |
EXEC SQL BEGIN DECLARE SECTION
EXEC SQL END DECLARE SECTION |
To declare host variables |
EXEC SQL DECLARE | To name Oracle objects |
EXEC SQL INCLUDE | To copy in files |
EXEC SQL TYPE | To equivalence datatypes |
EXEC SQL VAR | To equivalence variables |
EXEC SQL WHENEVER | To handle runtime errors |
Executable Statements | |
EXEC SQL ALLOCATE | To define and control Oracle data |
EXEC SQL ALTER | |
EXEC SQL ANALYZE | |
EXEC SQL AUDIT | |
EXEC SQL COMMENT | |
EXEC SQL CONNECT | |
EXEC SQL CREATE | |
EXEC SQL DROP | |
EXEC SQL GRANT | |
EXEC SQL NOAUDIT | |
EXEC SQL RENAME | |
EXEC SQL REVOKE | |
EXEC SQL TRUNCATE | |
EXEC SQL CLOSE | |
EXEC SQL DELETE | To query and manipulate Oracle data |
EXEC SQL EXPLAIN PLAN | |
EXEC SQL FETCH | |
EXEC SQL INSERT | |
EXEC SQL LOCK TABLE | |
EXEC SQL OPEN | |
EXEC SQL SELECT | |
EXEC SQL UPDATE | |
EXEC SQL COMMIT | To process transactions |
EXEC SQL ROLLBACK | |
EXEC SQL SAVEPOINT | |
EXEC SQL SET TRANSACTION | |
EXEC SQL DESCRIBE | To use dynamic SQL |
EXEC SQL EXECUTE | |
EXEC SQL PREPARE | |
EXEC SQL ALTER SESSION | To control sessions |
EXEC SQL SET ROLE | |
EXEC SQL EXECUTE
END-EXEC |
To embed PL/SQL blocks |