#

| Chapter 2 Executing Dynamic SQL and PL/SQL |

## 2.2 Getting Started with DBMS_SQL

Before you start using DBMS_SQL, you need to make sure that it is installed and that the appropriate users have access to this package. In addition, you should be aware of how privileges are applied to programs that execute dynamic SQL.

### 2.2.1 Creating the DBMS_SQL Package

The DBMS_SQL package is created when the Oracle database is installed. The dbmssql.sql script (found in the built-in packages source code directory, as described in Chapter 1, Introduction) contains the source code for this package's specification. This script is called by catproc.sql, which is normally run immediately after database creation. The script creates the public synonym DBMS_SQL for the package and grants EXECUTE privilege on the package to public. All Oracle users can reference and make use of this package.

Given the power, flexibility, and potential impact of dynamic SQL, you may actually want to revoke public access to DBMS_SQL and instead grant EXECUTE privilege to only those users who need to perform dynamic SQL.

To "hide" DBMS_SQL, issue this command from the SYS account:

>     REVOKE EXECUTE ON DBMS_SQL FROM PUBLIC;

To grant EXECUTE privilege to a specific user, issue this command from SYS:

>     GRANT EXECUTE ON DBMS_SQL TO whatever_user;

### 2.2.2 Security and Privilege Issues

Generally, when you run stored code (and all DBMS_* built-in packages are certainly stored in the database!), that code executes under the authority and using the privileges associated with the owner of the code. If this rule were applied to DBMS_SQL, then anyone who had EXECUTE privilege on DBMS_SQL would be able to act as SYS. This is clearly not a viable approach.

When you execute a DBMS_SQL program from within an anonymous block, that program is executed using the privileges of the current schema. If you embed DBMS_SQL programs within a stored program, those dynamic SQL programs will execute using the privileges of the owner of the stored program. DBMS_SQL is, in other words, a "run as user" package, rather than a "run as owner" package. This can lead to a number of complications, discussed in more detail in the "Tips on Using Dynamic SQL" section later in this chapter.

### 2.2.3 DBMS_SQL Programs

DBMS_SQL is one of the most complex built-in packages, with a large number of programs and data structures defined in the package specification. Table 2.1 summarizes the programs defined in the DBMS_SQL package.

Table 2.1: DBMS_SQL Programs
Name | Description | Use in SQL?
--- | --- | ---
BIND_ARRAY | Binds a specific value to a host array (PL/SQL8 only). | No
BIND_VARIABLE | Binds a specific value to a host variable. | No
CLOSE_CURSOR | Closes the cursor. | No
COLUMN_VALUE | Retrieves a value from the cursor into a local variable. | No
COLUMN_VALUE_LONG | Retrieves a selected part of a LONG value from a cursor's column defined with DEFINE_COLUMN_LONG. | No
DEFINE_ARRAY | Defines an array to be selected from the specified cursor (PL/SQL8 only). | No
DEFINE_COLUMN | Defines a column to be selected from the specified cursor. | No
DEFINE_COLUMN_LONG | Defines a LONG column to be selected from the specified cursor. | No
DESCRIBE_COLUMNS | Describes the columns for a dynamic cursor (PL/SQL8 only). | No
EXECUTE | Executes the cursor. | No
EXECUTE_AND_FETCH | Executes the cursor and fetches its row(s). | No
FETCH_ROWS | Fetches the row(s) from the cursor. | No
IS_OPEN | Returns TRUE if the cursor is open. | No
LAST_ERROR_POSITION | Returns the byte offset in the SQL statement where the error occurred. | No
LAST_ROW_COUNT | Returns the total number of rows fetched from the cursor. | No
LAST_ROW_ID | Returns the ROWID of the last row fetched from the cursor. | No
LAST_SQL_FUNCTION_CODE | Returns the SQL function code for the SQL statement. | No
OPEN_CURSOR | Opens the cursor. | No
PARSE | Parses the specified SQL statement. If the statement is a DDL statement, then the parse also executes the statement. | No
VARIABLE_VALUE | Gets a value of a variable in a cursor. | No

### 2.2.4 Types of Dynamic SQL

There are four distinct types, or methods, of dynamic SQL that you can execute with the programs of DBMS_SQL; these are listed in Table 2.2. Familiarity with these methods and the kinds of code you need to write for each will help you use DBMS_SQL most effectively.

Table 2.2: Types of Dynamic SQL
Type | Description | DBMS_SQL Programs Used
--- | --- | ---
Method 1 | No queries; just DDL statements and UPDATEs, INSERTs, or DELETEs, which have no bind variables. | EXECUTE
Method 2 | No queries; just UPDATEs, INSERTs, or DELETEs, with a fixed number of bind variables. | EXECUTE
Method 3 | Queries (SELECT statements) with a fixed numbers of columns and bind variables. | VARIABLE_VALUE
Method 4 | Queries (SELECT statements) with a variable numbers of columns and bind variables. In other words, you don't know until runtime how many bind variables there may be. | Same as for Method 3, but the code you must write is much more complex.

The following DDL statement is an example of Method 1 dynamic SQL:

>     CREATE INDEX emp_ind_1 on emp (sal, hiredate)

And this update statement is also Method 1 dynamic SQL:

>     UPDATE emp SET sal = 10000 WHERE empno = 1506

Of course, that UPDATE statement also is not very dynamic. If I now add a placeholder to this DML statement (indicated by the colon) so that I do not "hard-code" the employee number, I then have Method 2 dynamic SQL:

>     UPDATE emp SET sal = 10000 WHERE empno = :employee_id

A call to BIND_VARIABLE will be required for the previous statement to be executed successfully with DBMS_SQL.

A Method 3 dynamic SQL statement is a query with a fixed number of bind variables (or none). This will be the most common type of dynamic SQL you will execute. Here is an example:

>     SELECT ename, :second_column FROM emp WHERE deptno = :dept_id

In this case, I am leaving until runtime the decision about which column I will retrieve with my query. Now, this statement looks like Method 3 dynamic SQL, but this dynamic stuff can get very tricky. What if I substituted the string "hiredate, sal" for the placeholder "second_column"? I could then have a variable number of columns in the select list, and this would be Method 4 dynamic SQL.

How can you tell the difference? Well, you really can't just by looking at the string. The code, however, will tell. If you do not plan for Method 4 (variable number of columns in the select list, in this case), then your PL/SQL program will fail. It will not issue the right number of calls to DEFINE_COLUMN.

Usually, when you are dealing with Method 4 dynamic SQL, you will have strings that look more like this:

>     SELECT :select_list FROM emp WHERE :where_clause

Now there can be no doubt: there is no way to know how many columns you are retrieving. So how do you write your PL/SQL program to handle this complexity? Slowly and carefully, with lots of debugging. You will need to write logic to parse strings, locate placeholders, and then call the appropriate DBMS_SQL program.

Very few developers will have to deal with Method 4 dynamic SQL. You can find an example of the kind of code you will have to write in the later section, "Displaying Table Contents with Method 4 Dynamic SQL."

### 2.2.5 DBMS_SQL Exceptions

The DBMS_SQL defines a single exception in its specification as follows:

>     DBMS_SQL.INCONSISTENT_TYPE EXCEPTION;
>     PRAGMA EXCEPTION_INIT(DBMS_SQL.INCONSISTENT_TYPE, -6562);

This exception can be raised by either the COLUMN_VALUE or the VARIABLE_VALUE procedure if the type of the specified OUT argument is different from the type of the value which is being returned. You can trap this exception and handle it with the following syntax in your exception section:

>     EXCEPTION
>        WHEN DBMS_SQL.INCONSISTENT_TYPE
>        THEN
>            . . .

You may encounter other exceptions when working with dynamic SQL (in fact, there will be times when you believe that all you can do with DBMS_SQL is raise exceptions). The table on the following page displays some of the most common errors.

Error Number | Description
--- | ---
ORA-00942 | Table or view does not exist. You have referenced an object that does not exist in your schema. Remember that when you execute SQL from within a programmatic interface, that SQL is executed under the schema of the owner of the program, not that of the account running the PL/SQL program.
ORA-01001 | Invalid cursor. You have tried to use a value which has not been initialized as a DBMS_SQL cursor through a call to OPEN_CURSOR.
ORA-01002 | Fetch out of sequence. If you execute FETCH_CURSOR more than once after the cursor's result set is exhausted, you will raise this exception.
ORA-01008 | Not all variables bound. You have included a placeholder in your SQL statement string in the form :BINDVAR, but you did not call BIND_VARIABLE to bind a value to that placeholder.
ORA-01027 | Bind variables not allowed for data definition operations. You cannot include a bind variable (an identifier with a colon in front of it) in a DDL statement executed dynamically.
ORA-01031 | Insufficient privileges. You have tried to execute a SQL statement for which you do not have the appropriate privileges. Remember that when you execute a SQL statement inside a PL/SQL program, all roles are disabled. You will need to have directly granted privileges on your objects to affect them from within PL/SQL and the DBMS_SQL package.
ORA-29255 | This occurs with array processing in PL/SQL8. The cursor may not contain both bind and define arrays. For more information, see the section "Array Processing with DBMS_SQL."

### 2.2.6 DBMS_SQL Nonprogram Elements

DBMS_SQL defines three constants that you use in calls to the PARSE procedure to specify how Oracle handles the SQL statement:

>     DBMS_SQL.NATIVE CONSTANT INTEGER := 1;
>     DBMS_SQL.V6 CONSTANT INTEGER := 0;
>     DBMS_SQL.V7 CONSTANT INTEGER := 2;

The PL/SQL8 version of the DBMS_SQL package also predefines a number of data structures for use in array processing and column describes.

When you want to parse very long SQL statements (in excess of 32Kbytes), you'll need to declare a table based on the DBMS_SQL.VARCHAR2S index-by table TYPE defined as follows:

>     SUBTYPE VARCHAR2S IS SYS.DBMS_SYS_SQL.VARCHAR2S;

A little investigation reveals that this table is, in turn, defined as:

>     TYPE VARCHAR2S IS TABLE OF VARCHAR2(256)
>        INDEX BY BINARY_INTEGER;

When you use the DESCRIBE_COLUMNS procedure, you'll need to declare records based on the DBMS_SQL.DESC_REC record TYPE and index-by tables based on the DBMS_SQL.DESC_TAB table TYPE. These are defined as:

>     TYPE DESC_REC IS RECORD (
>        col_type BINARY_INTEGER := 0, /* type of column */
>        col_max_len BINARY_INTEGER := 0, /* maximum length of column */
>        col_name VARCHAR2(32) := 0, /* name of column */
>        col_name_len BINARY_INTEGER := 0, /* length of column name */
>        col_schema_name BINARY_INTEGER := 0,
>           /* name of column type schema if an object type */
>        col_schema_name_len VARCHAR2(32) := 0, /* length of schema name */
>        col_precision BINARY_INTEGER := 0, /* precision if number */
>        col_scale BINARY_INTEGER := 0, /* scale if number */
>        col_charsetid BINARY_INTEGER := 0, /* character set identifier */
>        col_charsetform BINARY_INTEGER := 0, /* character set form */
>        col_null_ok BOOLEAN := TRUE /* TRUE if column can be NULL */
>        );

>     TYPE DESC_TAB IS TABLE OF DESC_REC INDEX BY BINARY_INTEGER;

When you perform array processing with the BIND_ARRAY and DEFINE_ARRAY procedures, you will rely on the following predefined index-by tables to set up and manipulate those arrays:

>     TYPE NUMBER_TABLE IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
>     TYPE VARCHAR2_TABLE IS TABLE OF VARCHAR2(2000) INDEX BY BINARY_INTEGER;
>     TYPE DATE_TABLE IS TABLE OF DATE INDEX BY BINARY_INTEGER;
>     TYPE BLOB_TABLE IS TABLE OF BLOB INDEX BY BINARY_INTEGER;
>     TYPE CLOB_TABLE IS TABLE OF CLOB INDEX BY BINARY_INTEGER;
>     TYPE BFILE_TABLE IS TABLE OF BFILE INDEX BY BINARY_INTEGER;

Remember that these index-by tables are also available for your use even when you are not using, for example, the DEFINE_ARRAY procedure. You can still declare your own CLOB index-by tables based on DBMS_SQL.CLOB_TABLE any time you want and under whichever circumstances. This will save you the trouble of defining the table TYPE.

> NOTE: BLOB, CLOB, NCLOB, and BFILE are various large object (LOB) datatypes available with PL/SQL8. See the discussion of the DBMS_LOB package in Chapter 8, Managing Large Objects , for more information on manipulating LOBs from within PL/SQL.

2.1 Examples of Dynamic SQL |  | 2.3 The DBMS_SQL Interface

Copyright (c) 2000 O'Reilly & Associates. All rights reserved.