Tải bản đầy đủ
2 PLVcat: Cataloguing PL/SQL Source Code

2 PLVcat: Cataloguing PL/SQL Source Code

Tải bản đầy đủ

[Appendix A] Appendix: PL/SQL Exercises
using the PLVvu.code procedure. That can at times be an overwhelming volume of information −− and it
doesn't lend itself easily to generating a report showing this information in an outline view.
In the case of USER_DEPENDENCIES, the problem is not access to data; the problem is with a lack of data.
This view shows you which package another object relies on, but it does not "pierce the veil" of the package
to show you which element inside that package is the cause of the dependency. In other words, if my
calc_totals program makes the following call:
recalc.full_analysis;

then the USER_DEPENDENCIES view shows that calc_totals is dependent on recalc. It will not,
however, inform you that full_analysis is the program of the recalc package that is called by
calc_totals.
PL/Vision fixes these shortcomings with a set of programs that parses the contents of your PL/SQL code and
then stores the results of that process in database tables. You can then write simple SQL statements against
these tables to generate reports that provide a much greater granularity of detail about your PL/SQL programs.
These elements of the PLVcat package are explained in later sections.

18.2.2 The PLVcat Database Tables
The PLVcat programs generate information about your PL/SQL code and then deposit that information in one
of two database tables: PLVctlg or PLVrfrnc. These tables are created when you install PL/Vision. The
PLVctlg table contains the catalogue of the contents of packages (those elements defined in the
specification). The PLVrfrnc table contains the references or dependencies generated by calls to the ref
programs. These tables and how to interpret their contents are explained below.
18.2.2.1 PLVctlg table
The structure of the PLVctlg table is:
CREATE TABLE PLVctlg
(owner VARCHAR2(100),
name1 VARCHAR2(100), /* Package
name2 VARCHAR2(100), /* Element
type VARCHAR2(100), /* Same as
iname VARCHAR2(100), /* Name of
itype VARCHAR2(100), /* Type of
idatatype VARCHAR2(100),
overload INTEGER)

name */
name */
in user_objects */
object inside */
object inside */

The owner, name1, name2, and type columns define the program unit for which elements have been
catalogued. The name2 column is always NULL in this version of PLVcat, since PL/Vision currently
catalogues only package specifications.
The "inside" columns (iname, itype, and idatatype) indicate the element found in the program unit. The
idatatype column is non−NULL if the element is a function or TYPE statement. The overload column
contains the number of overloadings of a particular procedure or function name. All values are stored in
uppercase.
Examples of how this table is filled from calling PLVcat modules are shown later in this chapter.

18.2.2 The PLVcat Database Tables

511

[Appendix A] Appendix: PL/SQL Exercises
18.2.2.2 PLVrfrnc table
The PLVrfrnc table contains information about the references made to external elements from within a
PL/SQL program unit. The structure of this table is:
CREATE TABLE PLVrfrnc
(owner VARCHAR2(100),
name1 VARCHAR2(100),
name2 VARCHAR2(100),
type VARCHAR2(100),
reftype VARCHAR2 (100), /* Type of reference */
rowner VARCHAR2(100), /* Leave null if not specified */
rname1 VARCHAR2(100), /* Package name or stand alone */
rname2 VARCHAR2(100) /* Null if not in package. */
)

The owner, name1, name2, and type columns define the program unit for which references have been
analyzed. The reftype, rowner, rname1, and rname2 columns define the object that is referenced
inside the program unit. All values are stored in uppercase.
Examples of how this table is filled from calling PLVcat modules are shown later in this chapter.

18.2.3 Building a Catalogue
You can build a catalogue of your PL/SQL source code with the module and modules procedures. The
module procedure catalogues a single program unit, while the modules procedure can handle wildcarded
program names and automatically catalogue multiple program units, including all the stored code in a schema.
18.2.3.1 Cataloguing a single module
To build a catalogue of a single PL/SQL program, you call the module procedure, whose header is:
PROCEDURE module (module_in IN VARCHAR2);

You provide the name of the program you want to catalogue (currently only package specifications are
supported; any types provided to the left of the : are ignored). The module program automatically sets the
PLVio source repository to the ALL_SOURCE data dictionary view. It parses the source code using the
PLVprsps package, searching for the definitions of any of the following PL/SQL code elements:

Procedure header

Function header

Cursor header

TYPE statement
PLVcat.module does not, in other words, currently catalogue variables, constants, exceptions, or other
program elements that might appear in a PL/SQL package.
Since the module procedure works only with package specifications at this time, you do not have to tell
PLVcat the type of object you want to catalogue when you call PLVcat.module. You simply provide the
18.2.2 The PLVcat Database Tables

512

[Appendix A] Appendix: PL/SQL Exercises
name of the package and it automatically scans the specification. As an example, to catalogue the PLVio
package I would execute the following command in SQL*Plus:
SQL> exec PLVcat.module ('PLVio');

When control is returned back to the SQL*Plus prompt, the rows will have been written to PLVctlg and will
be available for reporting and analysis. If your package is large, it may take a minute or two to complete the
catalogue. String parsing and manipulation in the PL/SQL language is not known to be lightning fast.
18.2.3.2 Cataloguing multiple modules
The PLVcat.module procedure can only process a single package at a time; you cannot pass in wildcarded
package names for multiple−program cataloguing in one call. The PLVcat.modules procedure offers this
capability; its header is:
PROCEDURE modules (module_in IN VARCHAR2);

You can use modules to catalogue all the packages in your schema with this call:
SQL> exec PLVcat.modules ('%');

Or you can be more selective. The following call to modules will catalogue all packages in the PL/Vision
library:
SQL> exec PLVcat.modules ('PLV%');

The case you use to specify the package names is not significant. All program names are stored in the data
dictionary in uppercase. (All right, so if you surround your program name in double quotes you can actually
create programs with names in mixed case in the data dictionary; if you do this, you deserve all the ensuing
complexities!)
18.2.3.3 Examining the catalogue
To see how the PLVctlg table is populated by calls to PLVcat.module and PLVcat.modules,
consider the following simplified version of the PLVtmr package specification:
PACKAGE PLVtmr
IS
FUNCTION elapsed RETURN NUMBER;
PROCEDURE show_elapsed;
END PLVtmr;
/

After cataloguing this package with PLVcat.module, I will have two rows in the PLVctlg table as
follows:
Owner Name1 Type

Iname

Itype

Idatatype Overloading

PLV

PLVtmr PACKAGE elapsed

FUNCTION

NUMBER

PLV

PLVtmr PACKAGE show_elapsed PROCEDURE NULL

1
1

To obtain a list of all elements in the PLVtmr package, therefore, I could execute a SQL statement like this:
SELECT iname
FROM PLVctlg
WHERE name1 = 'PLVTMR';

To obtain a list of all functions catalogued for the PLV user account, I could execute a SQL statement like
this:
18.2.3 Building a Catalogue

513

[Appendix A] Appendix: PL/SQL Exercises
SELECT
FROM
WHERE
AND

iname
PLVctlg
owner = 'PLV'
itype = 'FUNCTION';

The script named inctlg.sql contains a more complex and useful SQL statement for viewing the contents
of the catalogue. The code for this script is:
TTITLE 'Elements Catalogued in &1'
SET VERIFY OFF
SET PAGESIZE 66
SET LINESIZE 60
COLUMN element FORMAT A60
SELECT DECODE (idatatype, NULL, NULL, idatatype || ' ') ||
itype || ' ' || owner || '.' ||
name1 || '.' || iname || ' ' ||
DECODE (overload, 1, NULL,
'(' || TO_CHAR(overload) || ')') element
FROM PLVctlg
WHERE name1 like UPPER ('&1')
ORDER BY owner, type, name1, itype, iname;

This script accepts as a single parameter the name of the package whose catalogue you wish to view.
Executing this script for the p package provides the following output:
SQL> @inctlg p
Sat Jun 01

page

1

Elements Catalogued in p
ELEMENT
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
VARCHAR2 FUNCTION PLV.P.LINESEP
VARCHAR2 FUNCTION PLV.P.PREFIX
PROCEDURE PLV.P.L (7)
PROCEDURE PLV.P.SET_LINESEP
PROCEDURE PLV.P.SET_PREFIX
PROCECURE PLV.P.TURN_OFF
PROCECURE PLV.P.TURN_ON

Notice that I am informed that the p.l procedure is overloaded seven times.
18.2.3.4 Cataloguing PL/Vision
You can generate the catalogue for PL/Vision packages by executing the plvcat.sql script, located in the
plvision\use subdirectory.
The code for plvcat.sql is simply:
BEGIN
FOR objind IN 1 .. PLV.numobjects
LOOP
PLVcat.module (PLV.objects(objind));
END LOOP;
END;
/

This script takes advantage of the list of PL/Vision objects that are stored in the PLV.objects PL/SQL
table. This PL/SQL table is created and assigned values in the initialization section of the PLV package. The
plvcat.sql script generates 396 rows in the PLVctlg table.

18.2.3 Building a Catalogue

514

[Appendix A] Appendix: PL/SQL Exercises

18.2.4 Identifying References and Dependencies
The other major area of functionality in PLVcat is to identify the references made within a program unit to
external program elements. Such a reference implies a dependency; this information can be very useful in
maintaining code, analyzing reuse and impact, and so on. The Oracle7 Server does maintain some dependency
information, but it is only the minimum data required by the database to validate the status of compiled code. I
cannot, for example, find out from the USER_DEPENDENCIES data dictionary view how many programs
use the PLVdyn.ddl procedure. The most I can determine is the set of programs that use something in
PLVdyn −− and this is not enough to support adequately an enterprise−wide deployment of PL/SQL
applications.
The PLVcat package offers three programs to generate dependency information right down to the name of the
package element that was referenced. It even lets you catalogue references to builtin functions like SUBSTR
and all the builtin package programs. The three procedures that perform this task are:
PROCEDURE refnonkw (module_in IN VARCHAR2);
PROCEDURE refbi (module_in IN VARCHAR2);
PROCEDURE refall (module_in IN VARCHAR2);

In all three cases, you pass in the name of the individual module for which you want references generated.
refnonkw
Identifies references to all non−keyword identifiers (application−specific elements).
refbi
Identifies references to all kinds of builtins.
refall
Identifies references to both non−keyword identifiers and builtins by calling refnonkw and refbi.
Note that in the current implementation of refall, two passes are made against the specified
module to parse the code.
The results of these scans are deposited in the PLVrfrnc table.
18.2.4.1 Examining the references
Let's look at an example of how this table is populated from the source code. Consider the testcase
package:
PACKAGE BODY testcase
IS
PROCEDURE save (string_in IN VARCHAR2)
IS
n INTEGER := DBMS_SQL.OPEN_CURSOR;
BEGIN
UPDATE PLV_output SET program = string_in;
IF SQL%ROWCOUNT = 0
THEN
INSERT INTO PLV_output VALUES (string_in)
END IF;
PLVcmt.perform_commit;
END;
END testcase;

After cataloguing all builtins in this package with the PLVcat.refbi, I will have four rows in the
PLVrfrnc table as follows:

18.2.4 Identifying References and Dependencies

515

[Appendix A] Appendix: PL/SQL Exercises
Owner Name1

Name2 Type

Rowner Rname1

Rname2

PLV

testcase NULL

PACKAGE BODY SYS

DBMS_SQL OPEN_CURSOR

PLV

testcase NULL

PACKAGE BODY SYS

INSERT

PLV

testcase NULL

PACKAGE BODY SYS

ROWCOUNT NULL

PLV

testcase NULL

PACKAGE BODY SYS

UPDATE

NULL
NULL

After extracting all non−keywords in this package with the PLVcat.refnonkw, I will have a single row in
the PLVrfrnc table as follows:
Owner Name1
PLV

Name2 Type
testcase NULL

Rowner Rname1 Rname2
PACKAGE BODY PLV

PLVCMT

PERFORM_COMMIT

Notice that PLVcat does not currently store references to non−PL/SQL objects, such as the PLV_output
table. The reason is that it uses DBMS_UTILITY.NAME_RESOLVE to locate the code and this builtin does
not work with non−PL/SQL objects.
To see a list of all program units that call the SUBSTR builtin function, you could execute this query:
SELECT owner || '.' || name1 program
FROM PLVrfrnc
WHERE rname1 = 'SUBSTR';

To see a list of all program units that call the open_and_parse function of the PLVdyn package, you
could execute this query:
SELECT
FROM
WHERE
AND

owner || '.' || name1 program
PLVrfrnc
rname1 = 'PLVdyn'
rname2 = 'OPEN_AND_PARSE';

The script named inctlg.sql contains a more complex and useful SQL statement for viewing the contents
of the catalogue. The code for this script is:
TTITLE 'Elements Referenced by &1'
SET VERIFY OFF
SET PAGESIZE 66
SET LINESIZE 60
COLUMN element FORMAT A60
SELECT owner || '.' ||
name1 || ' CONTAINS ' || rname1 ||
DECODE (rname2, NULL, NULL, '.' || rname2) element
FROM PLVrfrnc
WHERE name1 like UPPER ('&1')
ORDER BY owner, type, name1, rname1, rname2;

This script accepts as a single parameter the name of the program whose references you wish to view.
Executing this script for the testcase package body provides the following output:
SQL> start inrfrnc testcase
Sat Jun 01
page
1
Elements Referenced by testcase
ELEMENT
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
PLV.testcase CONTAINS DBMS_SQL.OPEN_CURSOR
PLV.testcase CONTAINS INSERT
PLV.testcase CONTAINS INSTR
PLV.testcase CONTAINS ROWCOUNT
PLV.testcase CONTAINS UPDATE

PLV.testcase CONTAINS PLVcmt.PERFORM_COMMIT

18.2.4 Identifying References and Dependencies

516

[Appendix A] Appendix: PL/SQL Exercises
Special Notes on PLVcat
Here are some factors to consider when working with PLVcat:

The PLVcat package supports only the ALL_SOURCE data dictionary view as a source repository for
the PL/SQL source code. Future versions of PL/Vision Professional will support reading from
operating system files.

The build program performs a commit after completing its inserts into the PLVctlg table. The
refnonkw and refbi programs perform a commit after completing their inserts into the
PLVrfrnc table. These commits are executed through the PLVcmt package, so you can turn off
commit processing by executing the PLVcmt.turn_off toggle (see Chapter 20, PLVcmt and
PLVrb: Commit and Rollback Processing ).

18.1 PLVcase: Converting
the Case of PL/SQL
Programs

V. Plug−and−Play
Packages

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

18.2.4 Identifying References and Dependencies

517

Chapter 19

518

19. PLVdyn and PLVfk: Dynamic SQL and PL/SQL
Contents:
About Plug−and−Play
Declarative Programming in PL/SQL
The Dynamic Packages of PL/Vision
PLVdyn: A Code Layer over DBMS_SQL
DML Operations
PLVfk: Generic Foreign Key Lookups
This chapter describes several packages that provide a programmatic interface to the builtin DBMS_SQL
package. These packages are the first in the "plug−and−play" category. Before plunging into a description of
the specifics of the packages, I want to explain what I mean here by "plug−and−play" in PL/SQL code.

19.1 About Plug−and−Play
Plug−and−play packages allow you to replace whole sections of code with programs from PL/Vision
packages. You essentially "plug−in" PL/SQL code and immediately gain benefits in your application. The
best example of a PL/Vision plug−and−play component is the PLVexc (PL/Vision EXCeption handling)
package. It provides very high−level exception−handling programs so that individual developers can simply
call a procedure that describes the desired action, such as "record and continue," and PLVexc figures out what
to do. It makes use of PLVlog to automatically write errors to the log of your choice (database table, PL/SQL
table, etc.).
To give you a sense of plug−and−play in PL/SQL code, consider the following exception section. It has two
different exception handlers: one for NO_DATA_FOUND and one for all other exceptions. When
NO_DATA_FOUND is raised, I request that PLVexc display a message to the user, record the error, and then
stop the program. When any other error occurs, I request that PLVexc record the error and then continue
processing.
EXCEPTION
WHEN NO_DATA_FOUND
THEN
PLVexc.recNstop ('Company has not been located.');
WHEN OTHERS
THEN
PLVexc.recNgo;
END;

So PLVexc does a lot of work for me. What's new about that? You build a module encapsulating multiple
actions and then use it over and over again. That's the central concept of modularization and black−boxing of
logic. Why give it a fancy name like "plug−and−play"? Maybe it's just a difference of semantics. But maybe
there's more to it.
The package structure of PL/SQL offers new opportunities when it comes to modularizing code. You can
think of a package as nothing more than a list of programs, a convenient way to collect together related
modules. With this perspective, you will not break new ground with packages. If, on the other hand, you look
upon the package as a self−contained environment or product or object, with its own internal data structures,
its own set of rules, you will find that you can construct a whole −− the package −− that is considerably
greater than the sum of its parts (the individual elements defined in the package).
The PLVexc package certainly hides a lot of implementational complexity from its users. The real power of
PLVexc is, however, reflected not so much in what or how much it hides. Rather, its strength resides more in
what it lets you accomplish in your own programs −− and how you go about doing it.

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

519

[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