Using Embedded SQL

advertisement
Using Embedded SQL
This chapter helps you to understand and apply the basic techniques of embedded SQL programming. Topics are:
 Using Host Variables
 Using Indicator Variables
 The Basic SQL Statements
 Using the SELECT Statement
 Using the INSERT Statement
 Using Subqueries
 Using the UPDATE Statement
 Using the DELETE Statement
 Using the WHERE Clause
 Using Cursors
 Using the DECLARE CURSOR Statement
 Using the OPEN Statement
 Using the FETCH Statement
 Using the CLOSE Statement
 Optimizer Hints
 Using the CURRENT OF Clause
 Using All the Cursor Statements
 A Complete Example
A Complete Example
The following complete program illustrates the use of a cursor and the FETCH statement. The program prompts for
a department number, then displays the names of all employees in that department.
All FETCHes except the final one return a row and, if no errors were detected during the FETCH, a success status
code. The final FETCH fails and returns the "no data found" Oracle error code to sqlca.sqlcode. The cumulative
number of rows actually FETCHed is found in sqlerrd[2] in the SQLCA.
#include <stdio.h>
/* declare host variables */
char userid[12] = "SCOTT/TIGER";
char emp_name[10];
int emp_number;
int dept_number;
char temp[32];
void sql_error();
/* include the SQL Communications Area */
#include <sqlca.h>
1
main()
{ emp_number = 7499;
/* handle errors */
EXEC SQL WHENEVER SQLERROR do sql_error("Oracle error");
/* connect to Oracle */
EXEC SQL CONNECT :userid;
printf("Connected.\n");
/* declare a cursor */
EXEC SQL DECLARE emp_cursor CURSOR FOR
SELECT ename
FROM emp
WHERE deptno = :dept_number;
printf("Department number? ");
gets(temp);
dept_number = atoi(temp);
/* open the cursor and identify the active set */
EXEC SQL OPEN emp_cursor;
printf("Employee Name\n");
printf("-------------\n");
/* fetch and process data in a loop
exit when no more data */
EXEC SQL WHENEVER NOT FOUND DO break;
while (1)
{
EXEC SQL FETCH emp_cursor INTO :emp_name;
printf("%s\n", emp_name);
}
EXEC SQL CLOSE emp_cursor;
EXEC SQL COMMIT WORK RELEASE;
exit(0);
}
void
sql_error(msg)
char *msg;
{
char buf[500];
int buflen, msglen;
EXEC SQL WHENEVER SQLERROR CONTINUE;
EXEC SQL ROLLBACK WORK RELEASE;
buflen = sizeof (buf);
sqlglm(buf, &buflen, &msglen);
printf("%s\n", msg);
printf("%*.s\n", msglen, buf);
exit(1);
}
2
Using Embedded PL/SQL
This chapter shows you how to improve performance by embedding PL/SQL transaction processing blocks in your
program. After pointing out the advantages of PL/SQL, this chapter discusses the following subjects:
 Embedding PL/SQL Blocks
 Using Host Variables
 Using Indicator Variables
 Using Host Arrays
 Using Cursors
 Stored Subprograms
 Using Dynamic SQL
Embedding PL/SQL Blocks
The Pro*C/C++ Precompiler treats a PL/SQL block like a single embedded SQL statement. So, you can place a
PL/SQL block anywhere in a program that you can place a SQL statement.
To embed a PL/SQL block in your Pro*C/C++ program, simply bracket the PL/SQL block with the keywords
EXEC SQL EXECUTE and END-EXEC as follows:
EXEC SQL EXECUTE
DECLARE
...
BEGIN
...
END;
END-EXEC;
The keyword END-EXEC must be followed by a semicolon.
After writing your program, you precompile the source file in the
usual way.
When the program contains embedded PL/SQL, you must use the SQLCHECK=SEMANTICS command-line
option, since the PL/SQL must be parsed by the Oracle Server. SQLCHECK=SEMANTICS requires the USERID
option also, to connect to a server. For more information, see "Using the Precompiler Options" on page 9-10.
Using Host Variables
Host variables are the key to communication between a host language and a PL/SQL block. Host variables can be
shared with PL/SQL, meaning that PL/SQL can set and reference host variables.
For example, you can prompt a user for information and use host variables to pass that information to a PL/SQL
block. Then, PL/SQL can access the database and use host variables to pass the results back to your host program.
3
Inside a PL/SQL block, host variables are treated as global to the entire block and can be used anywhere a PL/SQL
variable is allowed. Like host variables in a SQL statement, host variables in a PL/SQL block must be prefixed with
a colon. The colon sets host variables apart from PL/SQL variables and database objects.
Restrictions on Host Variables
You can not use complex C expressions such as structure-member dereferencing in PL/SQL blocks. For more details
and examples, see "Restriction" on page 6-12.
An Example
The following example illustrates the use of host variables with PL/SQL. The program prompts the user for an
employee number, then displays the job title, hire date, and salary of that employee.
char username[100], password[20];
char job_title[20], hire_date[9], temp[32];
int emp_number;
float salary;
#include <sqlca.h>
printf("Username? \n");
gets(username);
printf("Password? \n");
gets(password);
EXEC SQL WHENEVER SQLERROR GOTO sql_error;
EXEC SQL CONNECT :username IDENTIFIED BY :password;
printf("Connected to Oracle\n");
for (;;)
{
printf("Employee Number (0 to end)? ");
gets(temp);
emp_number = atoi(temp);
if (emp_number == 0)
{
EXEC SQL COMMIT WORK RELEASE;
printf("Exiting program\n");
break;
}
/*-------------- begin PL/SQL block -----------------*/
EXEC SQL EXECUTE
BEGIN
SELECT job, hiredate, sal
INTO :job_title, :hire_date, :salary
FROM emp
WHERE empno = :emp_number;
END;
END-EXEC;
/*-------------- end PL/SQL block -----------------*/
printf("Number
Job Title
Hire Date
4
Salary\n");
printf("------------------------------------\n");
printf("%6d %8.8s %9.9s %6.2f\n",
emp_number, job_title, hire_date, salary);
}
...
exit(0);
sql_error:
EXEC SQL WHENEVER SQLERROR CONTINUE;
EXEC SQL ROLLBACK WORK RELEASE;
printf("Processing error\n");
exit(1);
Notice that the host variable emp_number is set before the PL/SQL block is entered, and the host variables job_title,
hire_date, and salary are set inside the block.
VARCHAR Pseudotype
Recall from Chapter 3 that you can use the VARCHAR datatype to declare variable-length character strings. If the
VARCHAR is an input host variable, you must tell Oracle what length to expect. So, set the length component to the
actual length of the value stored in the string component.
If the VARCHAR is an output host variable, Oracle automatically sets the length component. However, to use a
VARCHAR output host variable in your PL/SQL block, you must initialize the length component before entering
the block. So, set the length component to the declared (maximum) length of the VARCHAR, as shown here:
int
emp_number;
varchar emp_name[10];
float
salary;
...
emp_name.len = 10;
/* initialize length component */
EXEC SQL EXECUTE
BEGIN
SELECT ename, sal INTO :emp_name, :salary
FROM emp
WHERE empno = :emp_number;
...
END;
END-EXEC;
...
Restriction
Do not use C pointer or array syntax in PL/SQL blocks. The PL/SQL compiler does not understand C host-variable
expressions and is, therefore, unable to parse them. For example, the following is invalid:
EXEC SQL EXECUTE
BEGIN
:x[5].name := 'SCOTT';
...
END;
5
END-EXEC;
To avoid syntax errors, use a placeholder (a temporary variable), to hold the address of the structure field to populate
structures as shown in the following valid example:
name = &employee.name
EXEC SQL EXECUTE
BEGIN
:name := ...;
...
END;
END-EXEC;
However, a host language such as C needs indicator variables because it cannot manipulate nulls. Embedded
PL/SQL meets this need by letting you use indicator variables to
 accept nulls input from a host program
 output nulls or truncated values to a host program
When used in a PL/SQL block, indicator variables are subject to the following rules:
 You cannot refer to an indicator variable by itself; it must be appended to its associated host variable.
 If you refer to a host variable with its indicator variable, you must always refer to it that way in the same
block.
In the following example, the indicator variable ind_comm appears with its host variable commission in the
SELECT statement, so it must appear that way in the IF statement:
...
EXEC SQL EXECUTE
BEGIN
SELECT ename, comm
INTO :emp_name, :commission :ind_comm
FROM emp
WHERE empno = :emp_number;
IF :commission :ind_comm IS NULL THEN ...
...
END;
END-EXEC;
Notice that PL/SQL treats :commission :ind_comm like any other simple variable. Though you cannot refer directly
to an indicator variable inside a PL/SQL block, PL/SQL checks the value of the indicator variable when entering the
block and sets the value correctly when exiting the block.
Stored Subprograms
Unlike anonymous blocks, PL/SQL subprograms (procedures and functions) can be compiled separately, stored in
an Oracle database, and invoked. A subprogram explicitly CREATEd using an Oracle tool such as SQL*Plus or
SQL*DBA is called a stored subprogram. Once compiled and stored in the data dictionary, it is a database object,
which can be re-executed without being recompiled.
When a subprogram within a PL/SQL block or stored procedure is sent to Oracle by your application, it is called an
inline subprogram. Oracle compiles the inline subprogram and caches it in the System Global Area (SGA) but does
not store the source or object code in the data dictionary.
6
Subprograms defined within a package are considered part of the package, and so are called packaged subprograms.
Stored subprograms not defined within a package are called stand-alone subprograms.
Creating Stored Subprograms
You can embed the SQL statements CREATE FUNCTION, CREATE PROCEDURE, and CREATE PACKAGE in
a host program, as the following example shows:
EXEC SQL CREATE
FUNCTION sal_ok (salary REAL, title CHAR)
RETURN BOOLEAN AS
min_sal REAL;
max_sal REAL;
BEGIN
SELECT losal, hisal INTO min_sal, max_sal
FROM sals
WHERE job = title;
RETURN (salary >= min_sal) AND
(salary <= max_sal);
END sal_ok;
END-EXEC;
Notice that the embedded CREATE {FUNCTION | PROCEDURE | PACKAGE} statement is a hybrid. Like all
other embedded CREATE statements, it begins with the keywords EXEC SQL (not EXEC SQL EXECUTE). But,
unlike other embedded CREATE statements, it ends with the PL/SQL terminator END-EXEC.
In the example below, you create a package that contains a procedure named get_employees, which fetches a batch
of rows from the EMP table. The batch size is determined by the caller of the procedure, which might be another
stored subprogram or a client application.
The procedure declares three PL/SQL tables as OUT formal parameters, then fetches a batch of employee data into
the PL/SQL tables. The matching actual parameters are host arrays. When the procedure finishes, it automatically
assigns all row values in the PL/SQL tables to corresponding elements in the host arrays.
EXEC SQL CREATE OR REPLACE PACKAGE emp_actions AS
TYPE CharArrayTyp IS TABLE OF VARCHAR2(10)
INDEX BY BINARY_INTEGER;
TYPE NumArrayTyp IS TABLE OF FLOAT
INDEX BY BINARY_INTEGER;
PROCEDURE get_employees(
dept_number IN
INTEGER,
batch_size IN
INTEGER,
found
IN OUT INTEGER,
done_fetch OUT
INTEGER,
emp_name
OUT
CharArrayTyp,
job-title
OUT
CharArrayTyp,
salary
OUT
NumArrayTyp);
END emp_actions;
END-EXEC;
EXEC SQL CREATE OR REPLACE PACKAGE BODY emp_actions AS
CURSOR get_emp (dept_number IN INTEGER) IS
SELECT ename, job, sal FROM emp
WHERE deptno = dept_number;
7
PROCEDURE get_employees(
dept_number IN
INTEGER,
batch_size IN
INTEGER,
found
IN OUT INTEGER,
done_fetch OUT
INTEGER,
emp_name
OUT
CharArrayTyp,
job_title
OUT
CharArrayTyp,
salary
OUT
NumArrayTyp) IS
BEGIN
IF NOT get_emp%ISOPEN THEN
OPEN get_emp(dept_number);
END IF;
done_fetch := 0;
found := 0;
FOR i IN 1..batch_size LOOP
FETCH get_emp INTO emp_name(i),
job_title(i), salary(i);
IF get_emp%NOTFOUND THEN
CLOSE get_emp;
done_fetch := 1;
EXIT;
ELSE
found := found + 1;
END IF;
END LOOP;
END get_employees;
END emp_actions;
END-EXEC;
You specify the REPLACE clause in the CREATE statement to redefine an existing package without having to drop
the package, recreate it, and regrant privileges on it. For the full syntax of the CREATE statement see Oracle8 SQL
Reference.
If an embedded CREATE {FUNCTION | PROCEDURE | PACKAGE} statement fails, Oracle generates a warning,
not an error.
Calling a Stored Subprogram
To invoke (call) a stored subprogram from your host program, you must use an anonymous PL/SQL block. In the
following example, you call a stand-alone procedure named raise_salary:
EXEC SQL EXECUTE
BEGIN
raise_salary(:emp_id, :increase);
END;
END-EXEC;
Notice that stored subprograms can take parameters. In this example, the actual parameters emp_id and increase are
C host variables.
In the next example, the procedure raise_salary is stored in a package named emp_actions, so you must use dot
notation to fully qualify the procedure call:
8
EXEC SQL EXECUTE
BEGIN
emp_actions.raise_salary(:emp_id, :increase);
END;
END-EXEC;
An actual IN parameter can be a literal, scalar host variable, host array, PL/SQL constant or variable, PL/SQL table,
PL/SQL user-defined record, procedure call, or expression. However, an actual OUT parameter cannot be a literal,
procedure call, or expression.
In the following example, three of the formal parameters are PL/SQL tables, and the corresponding actual
parameters are host arrays. The program calls the stored procedure get_employees (see page 6-21) repeatedly,
displaying each batch of employee data, until no more data is found. This program is available on-line in the demo
directory, in the file sample9.pc. A SQL script to create the CALLDEMO stored package is available in the file
calldemo.sql.
/*************************************************************
Sample Program 9: Calling a stored procedure
This program connects to ORACLE using the SCOTT/TIGER
account. The program declares several host arrays, then
calls a PL/SQL stored procedure (GET_EMPLOYEES in the
CALLDEMO package) that fills the table OUT parameters. The
PL/SQL procedure returns up to ASIZE values.
Sample9 keeps calling GET_EMPLOYEES, getting ASIZE arrays
each time, and printing the values, until all rows have been
retrieved. GET_EMPLOYEES sets the done_flag to indicate "no
more data."
*************************************************************/
#include <stdio.h>
#include <string.h>
EXEC SQL INCLUDE sqlca.h;
typedef char asciz[20];
typedef char vc2_arr[11];
EXEC SQL BEGIN DECLARE SECTION;
/* User-defined type for null-terminated strings */
EXEC SQL TYPE asciz IS STRING(20) REFERENCE;
/* User-defined type for a VARCHAR array element. */
EXEC SQL TYPE vc2_arr IS VARCHAR2(11) REFERENCE;
asciz
username;
asciz
password;
int
dept_no;
vc2_arr
emp_name[10];
vc2_arr
job[10];
float
salary[10];
int
done_flag;
int
array_size;
int
num_ret;
EXEC SQL END DECLARE SECTION;
/* which department to query? */
/* array of returned names */
/* number of rows returned */
9
long
SQLCODE;
void print_rows();
void sql_error();
main()
{
int
char
/* produces program output
*/
/* handles unrecoverable errors */
i;
temp_buf[32];
/* Connect to ORACLE. */
EXEC SQL WHENEVER SQLERROR DO sql_error();
strcpy(username, "scott");
strcpy(password, "tiger");
EXEC SQL CONNECT :username IDENTIFIED BY :password;
printf("\nConnected to ORACLE as user: %s\n\n", username);
printf("Enter department number: ");
gets(temp_buf);
dept_no = atoi(temp_buf);/* Print column headers. */
printf("\n\n");
printf("%-10.10s%-10.10s%s\n", "Employee", "Job", "Salary");
printf("%-10.10s%-10.10s%s\n", "--------", "---", "------");
/* Set the array size. */
array_size = 10;
done_flag = 0;
num_ret = 0;
/*
*
*
*
*/
Array fetch loop.
The loop continues until the OUT parameter done_flag is set.
Pass in the department number, and the array size-get names, jobs, and salaries back.
for (;;)
{
EXEC SQL EXECUTE
BEGIN calldemo.get_employees
(:dept_no, :array_size, :num_ret, :done_flag,
:emp_name, :job, :salary);
END;
END-EXEC;
print_rows(num_ret);
if (done_flag)
break;
}
/* Disconnect from the database. */
EXEC SQL COMMIT WORK RELEASE;
10
exit(0);
}
void
print_rows(n)
int n;
{
int i;
if (n == 0)
{
printf("No rows retrieved.\n");
return;
}
for (i = 0; i < n; i++)
printf("%10.10s%10.10s%6.2f\n",
emp_name[i], job[i], salary[i]);
}
/* Handle errors. Exit on any error. */
void
sql_error()
{
char msg[512];
int buf_len, msg_len;
EXEC SQL WHENEVER SQLERROR CONTINUE;
buf_len = sizeof(msg);
sqlglm(msg, &buf_len, &msg_len);
printf("\nORACLE error detected:");
printf("\n%.*s \n", msg_len, msg);
EXEC SQL ROLLBACK WORK RELEASE;
exit(1);
}
Remember, the datatype of each actual parameter must be convertible to the datatype of its corresponding formal
parameter. Also, before a stored procedure is exited, all OUT formal parameters must be assigned values. Otherwise,
the values of corresponding actual parameters are indeterminate.
Remote Access
PL/SQL lets you access remote databases via database links. Typically, database links are established by your DBA
and stored in the Oracle data dictionary. A database link tells Oracle where the remote database is located, the path
to it, and what Oracle username and password to use. In the following example, you use the database link dallas to
call the raise_salary procedure:
EXEC SQL EXECUTE
BEGIN
raise_salary@dallas(:emp_id, :increase);
END;
11
END-EXEC;
You can create synonyms to provide location transparency for remote subprograms, as the following example
shows:
CREATE PUBLIC SYNONYM raise_salary
FOR raise_salary@dallas;
Getting Information about Stored Subprograms
Chapter 3 described how to embed OCI calls in your host program. After calling the library routine SQLLDA to set
up the LDA, use the OCI call odessp to get useful information about a stored subprogram. When you call odessp,
you must pass it a valid LDA
and the name of the subprogram. For packaged subprograms, you must also pass the name of the package. odessp
returns information about each subprogram parameter such as its datatype, size, position, and so on. For details, see
Programmer's Guide to the Oracle Call Interface.
You can also use the DESCRIBE_PROCEDURE stored procedure, in the DBMS_DESCRIBE package. See Oracle8
Application Developer's Guide for more information about this procedure.
Using Dynamic SQL
Recall that the precompiler treats an entire PL/SQL block like a single SQL statement. Therefore, you can store a
PL/SQL block in a string host variable. Then, if the block contains no host variables, you can use dynamic SQL
Method 1 to EXECUTE the PL/SQL string. Or, if the block contains a known number of host variables, you can use
dynamic SQL Method 2 to PREPARE and EXECUTE the PL/SQL string. If the block contains an unknown number
of host variables, you must use dynamic SQL Method 4.
For more information, refer to Chapter 13, "Using Dynamic SQL", and Chapter 14, "Using Dynamic SQL:
Advanced Concepts".
Warning: In dynamic SQL Method 4, you cannot bind a host array to a PL/SQL procedure with a parameter of type
"table." For more information, see "Using Method 4" on page 13-25.
12
Download