Vous êtes sur la page 1sur 8

1

EECS 484

Pro*C Guideline

Why Use Pro*C

SQL is not a procedural language. In order to be more powerful in
programming and use the many nice features that are already developed in
other programming languages, we need to embed SQL in a procedural
programming language.

Writing a Pro*C Program

1. Declare Section

1.1. Format

Only one DECLARE section can be used in one precompiled unit. But
a program can contain more than one DECLARE section.
We can define our declaration section as follows:
EXEC SQL BEGIN DECLARE SECTION;
varchar uid[20];
varchar pwd[20];
int empno;
char ename[20];
float sales;
__
EXEC SQL END DECLARE SECTION;

1.2. Host Variables and Indicator Variables

Your C program communicates with the Oracle server via host
variables and indicator variables. They have the following
properties:
- They should be explicitly declared in the declare section before
they are used.
- They are case-sensitive in C statements.
- They cannot be SQL reserved words.
- In SQL statements, they should be preceded by a colon.
- In C statements, they are referred in the standard way (without
a colon).

Also, a host variable may have an associated indicator variable. An
indicator variable has exactly one corresponding host variable and
must be preceded by its associated input host variable in the SQL
statement.
The main purpose of an indicator variable is to deal with NULL
values. The meaning of different values for an indicator
variable:
0: The returned value has been placed into the host variable.
It is not null and
has not been truncated.
-1: The returned value is NULL. The value of the main variable
is undefined.
>0: The returned value is truncated. The width of the main
variable wasnt sufficient.

2

Examples of using indicator variables:

- Insertion

strcpy (ename, "clair");
icomm = -1; /* icomm should be declared in the declaration */
/* section as short int */
EXEC SQL INSERT INTO emp (ename, comm) VALUES (:ename, :comm:icomm);

Equivalently,

strcpy (ename, "clair");
EXEC SQL INSERT INTO emp (ename, comm) VALUES (:ename,
NULL);

Note: The first case is better because we can control the value
of
attribute comm through icomm.

- Checking:

EXEC SQL SELECT ename, sal, comm INTO :ename, :sal,
:comm:icomm
FROM emp
WHERE empno = :empno;
if ( icomm == -1)
printf("comm is null");

1.3. VARCHAR Pseudo-Type

VARCHAR is used to accommodate variable string length. It can be
thought of as an extended C type or predeclared structure. The
following declaration:

EXEC SQL BEGIN DECLARE SECTION;
. . . .
VARCHAR userid[20];
. . . .
EXEC SQL END DECLARE SECTION;

can be expanded into the following structure variable:

struct {
unsigned short int len; /* length of the string */
unsigned char arr[20]; /* the actual string */
} userid;

VARCHAR is used as follows:

- Put a VARCHAR type value, say ename, to a table:

empno = 123456789;
strcpy(ename.arr,"Name");
ename.len = strlen((char*)ename.arr);
/* len should be set by the user's program when used as an */
/* input variable. */
EXEC SQL INSERT INTO emp(empno, ename) VALUES(:empno, :ename);

3
Read a VARCHAR type value from a table:

EXEC SQL SELECT ename INTO :ename
FROM emp
WHERE empno = :empno;
ename.arr[ename.len] = '\0';
/* in order to output the correct string, the mark of the */
/* end of the string should be set by the programmer */
printf("%s\n", ename.arr);


2. SQL Communication Area

EXEC SQL INCLUDE SQLCA;

This statement includes SQLCA in the program just as a normal C program
includes any .h file. SQLCA contains definition of variables and error
information.

Important variable (sqlca.sqlcode) in SQLCA:

- sqlca.sqlcode = 0: Successful execution.
- sqlca.sqlcode < 0: Error occurs. Detail error message is in the
document.
- sqlca.sqlcode > 0: Successful execution, with a status code. The only
available code is 1403, indicating "last row
encountered" or "no row found."


3. Connecting to the Database

Before accessing any data in the database, we need to connect to
the database by providing userid and password.

EXEC SQL CONNECT :userid IDENTIFIED BY :password;

This must be the first executable SQL statement in the Pro*C
program. You can provide userid and password directly in the
program, or you can code the program to prompt, via the terminal,
for a valid userid and password.


4. Application Body

4.1. Insert

EXEC SQL INSERT INTO jobtitle (jobcode, jobclass)
VALUES (:jobcode, :jobclass);

4.2. Delete

EXEC SQL DELETE FROM jobtitle
WHERE jobcode = :jobcode;

4.3. Update

EXEC SQL UPDATE jobtitle SET jobclass = :jobclass
WHERE jobcode = :jobcode;

4
4.4. Single row output queries

EXEC SQL SELECT ename, sal, job INTO :ename, :sal, :job
FROM emp
WHERE empno = :empno;

4.5. Multiple rows output queries

Use CURSOR along with the SELECT statement. To use CURSOR, you should
have the following steps:

1. Declare CURSOR.

EXEC SQL DECLARE C1 CURSOR FOR
select ename, empno, job, sal
from emp
where deptno = :deptno;

CURSOR names must be unique in one program. This is a declaration,
not execution.

2. Open CURSOR.

EXEC SQL OPEN C1;

3. Fetch rows by moving CURSOR.

EXEC SQL FETCH C1 INTO :ename, :empno, :job, :sal;

FETCH acts as a file pointer in the C programming language. Each
execution of FETCH moves the position of the CURSOR to the next row
of the selected set. When all the rows are fetched, FETCH will set
sqlca.sqlcode to 1403, indicating the last row.

4. Close CURSOR.

EXEC SQL CLOSE C1;

When a CURSOR is released, you cannot fetch data from the CURSOR.


5. Other Useful Commands

5.1. Commit and Abort

The user has the choice to either make the work that has been done
permanent or erase the previous work after some modifications have been
done. The commands that are available for this use are:

EXEC SQL COMMIT WORK [RELEASE];

This statement makes the work done since the last commit/abort statement
permanent. It has no effect on any other variables or on the control
flow of the program. With the release option, you log off the database.
If it is not the last unit of work you are doing, do not use this
option.

EXEC SQL ROLLBACK WORK [RELEASE];

This statement aborts the work done since the last commit/abort
statement. If the program terminates normally, commit is automatically
executed; otherwise, rollback is automatically executed.





5
5.2. Debugging

EXEC SQL WHENEVER [SQLERROR | SQLWARNING | NOT FOUND]
[STOP | GOTO label | CONTINUE | DO procedure];

This statement is very useful for debugging to find where in
the program the error occurs and corresponding error message
can be printed out by the user program.

Condition: SQLERROR (sqlca.sqlcode < 0);
SQLWARNING (sqlca.warn[0] = 'W');
NOT FOUND (sqlca.sqlcode = 1403).

Action: STOP (rollback and terminates the program);
GOTO (goto a labeled statement);
CONTINUE (ignore the error signal);
DO (do a user-defined error handling procedure).

5.3. An Example to Print Out the Error Messages

void
sql_error()
{
char err_msg[128]; /* buffer to put error message */
int buf_len, /* size of buffer */
int msg_len; /* size of the actual message */

EXEC SQL WHENEVER SQLERROR CONTINUE;

buf_len = sizeof (err_msg);
sqlglm(err_msg, &buf_len, &msg_len);
printf("%.*s\n", msg_len, err_msg);

EXEC SQL ROLLBACK WORK;
}



6
Running a PRO*C program

1. Getting started

- Remember to execute the usual command before using Pro*C:
source /usr/caen/oracle/local/muscle

- Please use CAEN Sun workstations for this assignment. We know
everything works properly on Suns. If you must use some other
kind of workstation, telnet to a Sun workstation such as
azure.engin.umich.edu.

2. Compile the Pro*C file(legal.pc).

%make

The makefile has rules to precompile the Pro*C file to produce a C
file called legal.C and then to this file to produce an executable
called "legal".

3. Run the program.

%legal

4. Debug the program.

4.1 Use information as specified in Section 5.2 Debugging
4.2 Have a function to check whether you execute the
SQL command successfully and to print out the error messages.

For example, you may have

void check_result(char *success_msg, *error_msg) {
char err_msg[128];
int buf_len;
int msg_len;

if (sqlca.sqlcode == 0) {
printf("%s", successMsg);
} else if (sqlca.sqlcode < 0) {
printf("%s", errorMsg);
buf_len = sizeof(err_msg);
sqlglm(err_msg, &buf_len, &msg_len);
printf("%.*s\n",msg_len, err_msg);
} else {
printf("row not found\n");
}
}

And you may use after any SQL statement that you want to know whether your
program executes that statement successfully.

For example, you may have

varchar bname[12];
int bid;
....
EXEC SQL
INSERT INTO BOOK(BOOK_ID, NAME)
VALUES ( :bid, :bname)
sprintf(successBuf,"Successfully inserting book %s\n",bname.arr);
sprintf(errorBuf,"Error in inserting book %s\n",bname.arr);
check_result(successBuf,errorBuf);



7
An Example Program

/*
*********************************************************************************
Sample C/Pro*C program
EECS 484
*********************************************************************************
*/

#include <stdio.h>
#include <string.h>

EXEC SQL BEGIN DECLARE SECTION;
varchar uid[20]; /* varchar is an Oracle-supplied struct, UC or LC */
varchar pwd[20];
int empno;
varchar ename[20];
float sales;
float quota;
float ratio;
float new_quota;
float bonus;
short int iquota;
EXEC SQL END DECLARE SECTION;

EXEC SQL INCLUDE SQLCA;
/* can also use #include <sqlca.h> */

main () {

char c[100];

/* Try to connect to Oracle database. */
strcpy ((char*)uid.arr, "XXX"); /* Put your Oracle login here */
uid.len = strlen ((char*)uid.arr); /* Put your Oracle password here */
strcpy((char*)pwd.arr, "XXXXXX");
pwd.len = strlen ((char*)pwd.arr);
printf("Connecting to Oracle database\n");
EXEC SQL CONNECT :uid IDENTIFIED BY :pwd;
if (sqlca.sqlcode < 0) { /* There's an error in connecting. */
fprintf (stderr, "example: cannot connect to Oracle database.\n");
exit (-1);
}

/* Drop bonus table, then create a new one. */
EXEC SQL DROP TABLE bonus;
EXEC SQL CREATE TABLE bonus (empno number,
ename varchar(20),
sales number,
quota number,
ratio number,
new_quota number,
bonus number);

/* Declare a cursor for the appropriate SQL query. */
EXEC SQL DECLARE C CURSOR FOR
select s.empno, s.salesperson, sum(s.amount), q.amount
from sales s, quota q
where s.empno = q.empno
group by s.empno, s.salesperson, q.amount;

/* Open the cursor C */
EXEC SQL OPEN C;

/* Get the next row. */
EXEC SQL FETCH C INTO :empno, :ename, :sales, :quota;
while (sqlca.sqlcode == 0) {


8
/* Did this salesperson sell more than 1.5 times their quota? */
if (quota != 0. && (ratio=(sales/quota)) > 1.5) {
printf ("\n Employee: %d %20s\n", empno, ename);
printf (" Sales: $%.2f Quota: $%.2f\n Ratio:%6.3f\n",
sales, quota, ratio);

/* Should we give this salesperson a bonus? */
printf ("Bonus? (y/n) ");
fflush (stdin);
scanf ("%s", c);
if (c[0] == 'y' || c[0]=='Y') {

/* Get bonus and new quota. */
printf (" Enter bonus: ");
fflush (stdin);
scanf ("%f", &bonus);
printf (" Enter new quota: ");
fflush (stdin);
scanf ("%f", &new_quota);

/* Make a new row in the BONUS table. */
EXEC SQL INSERT INTO bonus (empno, ename, sales, quota, ratio,
new_quota, bonus) VALUES
(:empno, :ename, :sales, :quota, :ratio,
:new_quota, :bonus);
if(sqlca.sqlcode != 0)
printf("Insert error.\n");

EXEC SQL UPDATE QUOTA SET amount=:new_quota
WHERE empno =:empno;

printf (" Tentatively updated bonus and quota for %20s.\n", ename);
} /* if c[0] */
} /* if quota */

/* Get the next row. */
EXEC SQL FETCH C INTO :empno, :ename, :sales, :quota;
} /* while */

/* Close the cursor C */
EXEC SQL CLOSE C;

printf ("\n\nCommit these changes?");
fflush (stdin);

scanf ("%s", c);
if (c[0] == 'y' || c[0] == 'Y')
EXEC SQL COMMIT WORK RELEASE;
else
EXEC SQL ROLLBACK WORK RELEASE;

exit (0);
}

/*End of example program.*/


Summary of corrections to this document:

1. Page 1, line 3, -1: The returned value is NULL. The value of the main variable
is undefined.
2. Page 4, 2
nd
line after EXEC SQL COMMIT WORK [RELEASE]; if it is not the last.
3. Page 5 restore the command: source /usr/caen/oracle-8.1.6/local/muscle
4. Pages 6-7 extensive revisions to Running a Pro*C Program 6/3/99

Vous aimerez peut-être aussi