Tải bản đầy đủ
PLVdyn and PLVfk: Dynamic SQL and PL/SQL

PLVdyn and PLVfk: Dynamic SQL and PL/SQL

Tải bản đầy đủ

[Appendix A] Appendix: PL/SQL Exercises
For detailed information about the PLVexc package, see Chapter 22, Exception Handling.

V. Plug−and−Play
Packages

19.2 Declarative
Programming in PL/SQL

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

19. PLVdyn and PLVfk: Dynamic SQL and PL/SQL

520

Chapter 19
PLVdyn and PLVfk:
Dynamic SQL and PL/SQL

19.2 Declarative Programming in PL/SQL
When you call a PLVexc exception handler, you shift from a procedural to a declarative style of programming
in PL/SQL. To understand what I mean, think about the SQL language. You do not write programs in SQL.
Instead, you use a declarative syntax to describe the set of data you want to see or modify. The underlying
SQL engine then figures out the best way to accomplish the task. This declarative or "set at a time" mode of
processing is a critical element of the power of the SQL language.
PL/SQL is very different from SQL. It is a procedural programming language. When you want to get
something done in PL/SQL, you write a program. This gives you a tremendous amount of control, but it
definitely cuts into your productivity −− and offers many over−ripe opportunities for introducing bugs into
your code. Wouldn't it be wonderful to combine the control of the procedural language with the high−level
abstraction and productivity of a declarative syntax? Well, we are not talking idle chatter here. We are talking
packages.
Constructed properly, the PL/SQL package offers the opportunity for developers to write code in a declarative
style. Take a look again at the exception section that utilizes PLVexc. To write this code, a user of PLVexc
only had to know what kind of action she wanted to perform. Ignore the error? Record and continue? Record
and halt? Just describe the action desired, pass the necessary data, and then let the underlying engine (the
PLVexc package) figure out how to implement your request. Declarative coding in PL/SQL! Keep these
concepts in mind as you read the chapters in Part V.

19.1 About Plug−and−Play

19.3 The Dynamic
Packages of PL/Vision

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

521

Chapter 19
PLVdyn and PLVfk:
Dynamic SQL and PL/SQL

19.3 The Dynamic Packages of PL/Vision
The builtin DBMS_SQL package supports all four methods of dynamic SQL, as well as dynamic PL/SQL
code execution. It is a very powerful and useful tool that can in many ways transform the way in which you
build applications, especially generic, reusable utilities, with PL/SQL.
There is, however, a tiny, little problem with DBMS_SQL: it is just too darn complicated. It consists of more
than a dozen procedures and functions. These programs need to be used in a very particular sequence.
Furthermore, depending on the method of dynamic SQL you wish to implement, you will use different
combinations of those builtins. Finally, it just comes down to an awful lot of typing and know−how, even if
you want to do something relatively simple. (See the sidebar for a general description of the flow of program
calls for dynamic SQL. See Chapter 15, PLVvu: Viewing Source Code and Compile Errors, for a more
complete description of DBMS_SQL.)
The result of this complexity is that relatively few developers take full advantage of all that DBMS_SQL has
to offer. And since many of the actions required for dynamic SQL are the same regardless of the SQL
statement, those individuals will be writing the same code over again.
What is wrong with this picture? Code redundancy is a maintenance nightmare. Requiring all developers to
know the picayune details of technology like dynamic SQL is a productivity nightmare. Getting all of these
versions of dynamic SQL to work is a code quality nightmare. Hey! Working with PL/SQL should not
resemble a Freddy Krueger sequel. There's got to be something we can do here.
A DBMS_SQL Recap
The builtin DBMS_SQL package allows you to dynamically construct and execute SQL and PL/SQL
statements. You get full programmatic control −− and with it comes full responsibility. With DBMS_SQL,
nothing is taken for granted. You must specify each and every operation on the SQL statement, usually with a
wide variety of procedure calls, from the SQL statement itself down to the values of bind variables and the
data types of columns in SELECT statements.
To execute dynamic SQL (and PL/SQL) with the DBMS_SQL you must follow this general flow:
Open a cursor. When you open a cursor, you ask the RDBMs to set aside and maintain a valid cursor structure
for your use with future DBMS_SQL calls. The RDBMs returns an INTEGER "handle" to this cursor. You
will use this handle in all future calls to DBMS_SQL modules for this dynamic SQL statement. Note that this
cursor is completely distinct from static PL/SQL cursors (whether implicit or explicit).
Parse the SQL statement. Before you can specify bind variable values and column structures for the SQL
statement, it must be parsed by the RDBMs. This parse phase verifies that the SQL statement is properly
constructed. It then associates the SQL statement with your cursor handle. Note that when you parse a DDL
statement it is also executed immediately. Upon successful completion of the DDL parse, the RDBMs also
issues an implicit commit. This behavior is consistent with that of SQL*Plus.

522

[Appendix A] Appendix: PL/SQL Exercises
Bind all host variables. If the SQL statement contains references to host PL/SQL variables, you include
placeholders to those variables in the SQL statement by prefacing their names with a colon, as in :salary.
You must then bind the actual value for that variable into the SQL statement.
Define the columns in SELECT statements. Each column in the list of the SELECT must be defined. This
define phase sets up a correspondence between the expressions in the list of the SQL statement and local
PL/SQL variables that receive the values when a row is fetched (see COLUMN_VALUE). This step is
necessary only for SELECT statements and is roughly equivalent to the INTO clause of an implicit SELECT
statement in PL/SQL.
Execute the SQL statement. Execute the specified cursor, that is, its associated SQL statement. If the SQL
statement is an INSERT, UPDATE, or DELETE, the EXECUTE command returns the numbers of rows
processed. Otherwise you should ignore that return value.
Fetch rows from the dynamic SQL query. If you execute a SQL statement, you must then fetch the rows from
the cursor, as you would with a PL/SQL cursor. When you fetch, however, you do not fetch directly into local
PL/SQL variables.
Retrieve values from the execution of the dynamic SQL. If the SQL statement is a query, you will retrieve
values from the SELECT expression list using COLUMN_VALUE. If you have executed a PL/SQL block,
you will use VARIABLE_VALUE to retrieve any bind variables included in the PL/SQL code.
Close the cursor. Just as with normal PL/SQL cursors, you should always clean up by closing the cursor when
you are done. This releases the memory associated with the cursor.
The answer is simple, at least in concept: build a package. And that is what I did. In fact, I built three
packages for dynamic SQL that make it easier to use the builtin DBMS_SQL package:
PLVdyn
Gives a thorough layer of code built around the DBMS_SQL builtin package.
PLVdyn1
Supports single bind variable dynamic SQL.
PLVfk
Offers a generic utility to perform foreign key lookups for any table.
PLVdyn and PLVfk are covered in this chapter; PLVdyn1, which works in similar fashion to PLVdyn, is
described on the companion disk.

19.2 Declarative
Programming in PL/SQL

19.4 PLVdyn: A Code
Layer over DBMS_SQL

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

523

Chapter 19
PLVdyn and PLVfk:
Dynamic SQL and PL/SQL

19.4 PLVdyn: A Code Layer over DBMS_SQL
The PLVdyn (PL/Vision DYNamic SQL) package offers an easy−to−use, programmatic interface, or API, to
the DBMS_SQL builtin package. It combines many basic operations in DBMS_SQL into functions and
procedures, freeing up developers from having to know about many of the intricacies of using the builtin
package.
Operations include:

Execute a Data Definition Language (DDL) statement with one line of code

Execute a PL/SQL block with one line of code

Delete from or truncate the specified table

Drop one or more objects from the data dictionary

Execute an INSERT...SELECT FROM statement with an absolute minimum of SQL coding

View the contents of the specified table

Toggle on/off tracing that displays the dynamic SQL statement being parsed
The following sections show how to use each of the different elements of the PLVdyn package.

19.4.1 DDL Operations
The PLVdyn package offers several programmatic interfaces to DDL or Data Definition Language
commands. Before DBMS_SQL was available, you could not execute any DDL statements (such as CREATE
TABLE, CREATE INDEX, etc.) within PL/SQL. Now DBMS_SQL lets you execute anything you want −−
as long as you have the appropriate authority.
DDL statements in SQL are handled differently from DML (Data Manipulation Language) statements such as
UPDATE, INSERT, and so on. There is no need to execute a DDL statement. The simple act of parsing DDL
automatically executes it and performs a COMMIT. This is true in SQL*Plus and it is true in PL/SQL
programs. If you execute a DDL statement in your PL/SQL program, it commits all outstanding changes in
your session. This may or may not be acceptable, so factor it into your use of the following PLVdyn
programs.
524

[Appendix A] Appendix: PL/SQL Exercises
NOTE: To dynamically execute DDL from within PL/Vision, the account in which PLVdyn
is installed must have the appropriate privilege granted to it explicitly. If you rely solely on
role−based privileges, you receive an ORA−01031 error: insufficient
privileges. For example, if you want to create tables from within PLVdyn, you need to
have CREATE TABLE privilege granted to the PLVdyn account.
19.4.1.1 Generic DDL interface
First of all, PLVdyn offers a single, completely generic procedure to execute a DDL statement. Its header
follows:
PROCEDURE ddl (string_in IN VARCHAR2);

You pass the string to PLVdyn.ddl and it takes care of the details (the implementation of which is discussed
in the section called "Bundling Common Operations").
I can use the ddl procedure to perform any kind of DDL, as the following examples illustrate.
1.
Create an index on the emp table.
PLVdyn.ddl ('CREATE INDEX empsal ON emp (sal)');

2.
Create or replace a procedure called temp.
PLVdyn.ddl
('CREATE OR REPLACE PROCEDURE temp ' ||
'IS BEGIN NULL; END;');

PLVdyn offers a number of other, more specialized programs to execute various kinds of DDL. These are
described in the following sections.
19.4.1.2 Dropping and truncating objects with PLVdyn
PLVdyn offers two separate "cleanup" programs: drop_object and truncate. The drop_object
procedure provides a powerful, flexible interface for dropping one or many objects in your schema. The
header for drop_object is:
PROCEDURE drop_object
(type_in IN VARCHAR2,
name_in IN VARCHAR2,
schema_in IN VARCHAR2 := USER);

The truncate command truncates either tables or clusters and has the same interface as drop_object:
PROCEDURE truncate
(type_in IN VARCHAR2,
name_in IN VARCHAR2,
schema_in IN VARCHAR2 := USER);

The rest of this section describes the behavior and flexibility of drop_object. The same information
applies to truncate.
You provide the type of object to drop, the name of the object, and the schema, (if you do want to drop objects
in another schema). So instead of typing statements like this in SQL*Plus:
SQL> drop table emp;

19.4.1 DDL Operations

525