Tải bản đầy đủ
PLVio: Reading and Writing PL/SQL Source Code

PLVio: Reading and Writing PL/SQL Source Code

Tải bản đầy đủ

[Appendix A] Appendix: PL/SQL Exercises
The PLVhlp package's use of PLVio shows the power of abstracting the concept of source and repository for
PL/SQL code. PLVhlp offers an architecture for displaying online help text for your own PL/SQL programs.
The default mode for PLVhlp is to display the help text to standard output or your screen. Thus, I can call
PLVhlp (through a frontend procedure in the PLV package) as shown below to get high−level information
about PL/Vision itself:
SQL> exec PLV.help
Help for PLV
Overview of PL/Vision
PL/Vision is a development accelerator for PL/SQL programmers.
It is made up of a set of packages which you can use as
plug−and−play components in your own applications. Here is a
quick overview of some of the available packages:
PLVdyn − performs dynamic SQL and PL/SQL operations.
PLVexc − High−level exception handling capabilities.
PLVlog − Generic log mechanism.
PLVvu − View stored code and compile errors.

What if you are not developing applications in SQL*Plus and you cannot rely on
DBMS_OUTPUT.PUT_LINE to display the help text? Instead of abandoning online help, you can instead
redirect the output of the PLVhlp package to a PL/SQL table, for example, as shown below:
PLVio.settrg (PLV.pstab);

After this call, any attempts to display the help text will send that information to a PL/SQL table
(PLVio.target_table, to be precise). You can then build your own program to read from this PL/SQL
table and display the results in your development environment. You could even display this information (to
reassure yourself, perhaps, that it really is there) with this command:
PLVio.disptrg;

Notice that I did not have to make any changes to the PLVhlp package to achieve this redirection; nor does
any of the code you have built to extract the help text from your own program units need to be modified.
PL/Vision packages make extensive use of the PLVio package. To use it yourself, you will first set your
source and target. Then you must use get_line to read from the source and put_line to write to the
target. To make this package as flexible as possible, PLVio offers lots of additional programs, constants, and
data structures. Don't be intimidated by the number and variety. Dip into the package as you need it,
experiment with its nuances, and look at the examples of its usage in PL/Vision.
The following sections show how to use each of the different elements of the PLVio package.

11.8 Tracing PLVobj
Activity

12.2 Code Repositories
Supported by PLVio

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

12. PLVio: Reading and Writing PL/SQL Source Code

353

Chapter 12
PLVio: Reading and
Writing PL/SQL Source
Code

12.2 Code Repositories Supported by PLVio
The various programs in the PLVio package that manage the source and target repositories and that read and
write lines of text are independent of the particular sources and targets. When you call put_line, for
instance, you do not write code that says "write this line to a file." You simply "say" with your code: "Write
this line to the target." You define the target independently of the actual read and write commands. This
separation of logical and physical aspects of PL/SQL code I/O makes it easy to support a wide range of
repositories −− and to add to that range as PL/SQL's capabilities expand.
When I first built PLVio, I was working with Release 2.1 of the PL/SQL language. I was able, therefore, to
write PLVio to read to and write from database tables, but I could not read and write operating system files.
That feature was not available until Release 2.3. I was still able to build the package and put it to use
throughout PL/Vision. When Release 2.3 became available, I enhanced PLVio to support this new repository
option and, with the simple act of a recompile only of the PLVio package body, my existing utilities could
now manipulate PL/SQL source code in operating system files!
The PLVio package supports the following types of repositories:

PL/SQL string

Database table

PL/SQL table

Operating system file (server side)

Standard output (the screen, usually)
You set the source by calling PLVio.setsrc: you set the target by calling PLVio.settrg. Both are
described in later sections.
Before diving into all the different programs, here are some details about how these different repositories are
handled in PLVio, both as source and target.

12.2.1 String Source or Target
When you specify a string as source, you pass that string of text in to PLVio when you call the
PLVio.setsrc procedure. At that time, your string will be assigned to the text_in field of the
string_repos. The string_repos record is defined as an instance of the following record TYPE:
354

[Appendix A] Appendix: PL/SQL Exercises

TYPE string_repostype IS RECORD
(text_in PLV.max_varchar2%TYPE,
start_pos INTEGER := 1,
text_len INTEGER := NULL,
text_out PLV.max_varchar2%TYPE := NULL);

PLVio defines a line within this string source as all text up to the next newline character in the string
(equivalent to CHR(10) and available as a named constant of PLVchr.newline_char). The maximum
size of a line in PLVio is 2000 bytes, so you will need to break up a large program into multiple strings
separated by a newline if you want to specify a string as a source. The maximize size of the entire text is
32,767 −− the maximum length of a PL/SQL variable length string (represented in the above record
definition by the PLV.max_varchar%TYPE anchored declaration).
You can view the current contents of the source or target strings by calling the PLVio.srcstg or
PLVio.trgstg functions, respectively.

12.2.2 Database Source or Target
You can use a database table as a source or target for PL/SQL code. In either case, you can use the default
table (which is the USER_SOURCE data dictionary view for source and the PLV_source table for target) or
you can specify your own table. Since PLVio uses PLVdyn to execute dynamic SQL, you can provide the
name of the table and its columns. Regardless of their names, however, the columns of a database repository
for PLVio must have at least four columns structured as shown in the record TYPE for a repository below:
TYPE repos_rectype IS
RECORD
(name VARCHAR2(60),
type VARCHAR2(10) := c_notset,
name_col VARCHAR2(60) := 'name',
type_col VARCHAR2(60) := 'type',
line#_col VARCHAR2(60) := 'line',
text_col VARCHAR2(60) := 'text',
select_sql VARCHAR2(2000),
insert_sql VARCHAR2(2000),
where_clause VARCHAR2(1000) := NULL,
starting_at VARCHAR2(1000) := NULL);

In other words, you will need a name string column, a type string column, a line number column, and a text
string column. These columns can be named whatever you want and you can have other columns in addition
to these four, but these columns must be available and specified to PLVio.
Given these requirements, the table shown in the left−hand column below is valid for use in PLVio, while the
table in the right−hand column cannot be used, since it lacks a line number column:
Valid for PLVio Source

Not Usable for PLVio Source

CREATE TABLE temp_source
CREATE TABLE temp_source
(progname VARCHAR2(100),
(objname VARCHAR2(100),
progtype VARCHAR2(30),
objtype VARCHAR2(30),
linenum INTEGER,
objline VARCHAR2(120));
linetext VARCHAR2(120));

As you can see, the record TYPE for a PLVio repository also stores other database−related information, such
as the dynamically constructed SELECT and INSERT strings and the optional WHERE clause.
You need to have SELECT privileges only on the source database table. You will need INSERT and DELETE
authority on the target database table. You may not, therefore, specify the USER_SOURCE data dictionary
view as the target for output from PLVio.
12.2.2 Database Source or Target

355

[Appendix A] Appendix: PL/SQL Exercises
When you specify a database table as the source repository, you will also make use of the PLVobj package to
indicate the schema, program name, and program type you are interested in. Examples of this dependency are
shown in Section 12.3, "Managing the Source Repository".

12.2.3 PL/SQL Table Target
If you want to avoid the SQL layer, you can use a PL/SQL table defined inside PLVio as the target for
PL/SQL source code. PLVio does not currently support PL/SQL tables as sources for reading PL/SQL code.
The PL/SQL table is defined in the PLVio specification as follows:
target_table PLVtab.vc2000_table;
target_row BINARY_INTEGER;

Since the target_table is in the specification, a user of PLVio can directly access and change the
contents of target_table. It is up to you to only use this table in ways that are appropriate to PLVio
and/or your specific coding objectives.
The target_row variable will tell you how many lines of code are defined in the PL/SQL table. The row
number is treated as the line number for the source code. Once you have populated the table, you can display
its contents or pass the table as an argument to another program to process the data in that table.

12.2.4 File Source or Target
You can request that PLVio.put_line write its text to an operating system file. In this case,
PLVio.put_line calls the PLVfile.put_line program. This procedure in turn calls the appropriate
elements of the builtin UTL_FILE package to interact with the operating system file. For more information on
the requirements and restrictions when working with UTL_FILE, see Chapter 13, PLVfile: Reading and
Writing Operating System Files.

12.2.5 Standard Output Target
You can request that output from calls to PLVio.put_line be directed to standard output or the screen.
When you do this, PLVio.put_line is, in effect, calling the DBMS_OUTPUT.PUT_LINE program to
display output (although it does happen through the p package). This is the way that PLVgen generates its
PL/SQL source code, for example.
So if you ever execute a PLVgen program to generate code and you don't see anything, check your PLVio
target type (with a call to the PLVio.trgtype function). You might be writing your code to a file or
PL/SQL table or database table!

12.1 Why PLVio?

12.3 Managing the Source
Repository

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

12.2.3 PL/SQL Table Target

356

Chapter 12
PLVio: Reading and
Writing PL/SQL Source
Code

12.3 Managing the Source Repository
To read information from a PLVio repository, you must do the following:
1.
Define the source using the setsrc program.
2.
Fine−tune your access to the source repository. You can modify the WHERE clause of your SELECT
from a database table source with a number of procedures.
3.
Initialize the source using the initsrc program.
You are then ready to use the get_line procedure to retrieve lines of text from the source repository.
This section explains the setsrc and initsrc procedures. The next section explores how to modify the
WHERE clause of the source SELECT for database tables.

12.3.1 Setting the Source
The setsrc procedure assigns the type of source repository and defines the structure of the source (if a
database table). The header for setsrc is as follows:
PROCEDURE setsrc
(srctype_in IN VARCHAR2,
name_in IN VARCHAR2 := 'user_source',
name_col_in IN VARCHAR2 := 'name',
srctype_col_in IN VARCHAR2 := 'type',
line#_col_in IN VARCHAR2 := 'line',
text_col_in IN VARCHAR2 := 'text',
schema_col_in IN VARCHAR2 := NULL);

The first argument, srctype_in, is the type of repository. The second argument, name_in, is the name of
the repository. Its content varies according to the type of repository and will be explained below. The third
through seventh arguments provide the names of the columns required for a database table source. The default
values match the structure of the default table (the USER_SOURCE data dictionary view). Notice, therefore,
that the schema column name is NULL. This column would be used only if you specified a table/view like
ALL_SOURCE, which contains the source code for all programs to which you have access, regardless of
schema.
The setsrc procedure transfers the arguments to the srcrep record, which is defined using the
repos_rectype shown earlier. If you are using a string source, then the string_repos record is
updated. If you are using a database source, then the SELECT statement that will be used to query from that
table is constructed as follows (this is a simplified version of the actual SELECT, but gives you an idea of its
357

[Appendix A] Appendix: PL/SQL Exercises
structure):
srcrep.select_sql :=
'SELECT ' || source_text_col_in || ', ' ||
source_line#_col_in ||
' FROM ' || name_in || ' ' ||
' WHERE ' || source_name_col_in || ' = :name ' ||
'
AND ' || srctype_col_in || ' = :type' ||
' ORDER BY ' || source_line#_col_in;

If you are using a string source, the name_in argument contains the string which holds the text for the
program you want to read. All other arguments are ignored. This string is then assigned into the string
repository record as shown below:
IF string_source
THEN
string_repos.text_in := name_in;
string_repos.start_pos := 1;
string_repos.text_len := LENGTH (name_in);
string_repos.text_out := NULL;

Notice that the other fields of the string_repos record are also initialized.
If you are using a file source, then the name_in argument is the name of the file. The
source_name_col_in argument should contain the type:name specification for the object you are
reading. So if you are reading the package body of PLVvu from the file PLVvu.spb, you would call
setsrc as follows:
PLVio.setsrc (PLV.file, 'PLVvu.spb', 'b:PLVvu');

You must supply this third argument if you are writing (have set the target to) a database table.
If you are using a file source, all other arguments (after the first three) are ignored.

12.3.2 Initializing the Source
Once you have set the source repository, you can either move directly to initializing that repository, or you
can, in the case of a database table source, modify the WHERE clause of the SELECT constructed by
setsrc. In most cases, you will simply call initsrc, so I will discuss that procedure below. The next
section discusses how to modify the source repository WHERE clause.
The header for the initsrc procedure is overloaded as follows:
PROCEDURE initsrc
(starting_at_in IN INTEGER,
ending_at_in IN INTEGER,
where_in IN VARCHAR2 := NULL);
PROCEDURE initsrc
(starting_at_in IN VARCHAR2 := NULL,
ending_at_in IN VARCHAR2 := NULL,
where_in IN VARCHAR2 := NULL);

The "integer version" of initsrc accepts up to three arguments, as follows:
starting_at_in
The line number at which the query should start.
ending_at_in
12.3.2 Initializing the Source

358

[Appendix A] Appendix: PL/SQL Exercises
The line number at which the query should end.
where_in
An optional WHERE clause to add to the existing clause of the source's SELECT statement.
If the source is a database table, specifying start and/or end line numbers results in additional elements in the
WHERE clause of the SELECT statement. The where_in string is also appended to the SELECT's WHERE
clause, if provided. For any other code sources, these three arguments are currently ignored. In other words, if
you work with non−database table sources, you will always read the full set of lines of text in those sources. It
is easy to see how initsrc should be enhanced to support these arguments; it's just a matter of time and
resources. I encourage you to try adding this functionality yourself.
The "string version" of initsrc allows you to specify starting and ending strings for the source repository.
In this case (and only when the source is a database table), the get_line procedure will only read those
lines that come after the first occurrence of the starting string and before the first occurrence of the ending
string.

12.3.3 Using setsrc and initsrc
Here are some examples of calls to setsrc and initsrc.
1.
Set the source to a string and then initialize the source.
PLVio.setsrc (PLVio.c_string, long_code_string);
PLVIO.initsrc;

2.
Set the current object in PLVobj to the body of the PLVvu package. Set the source to the
ALL_SOURCE data dictionary view and restrict access to only the first five lines of the code for the
PLVvu package body.
PLVobj.setcurr ('b:PLVvu');
PLVio.setsrc (PLV.dbtab, 'all_source', schema_col_in => 'owner');
PLVio.initsrc (1, 5);

3.
Set the source to a table named temp_source with a set of completely different column names.
Request that only those rows for the procedure calc_totals containing the string RAISE be read.
Notice the use of named notation in my call to initsrc. Which of the two versions of initsrc is
executed?
PLVobj.setcurr ('p:calc_totals');
PLVio.setsrc
(PLV.dbtab, 'temp_source', 'progname', 'progtype', 'line#', 'line');
PLVio.initsrc (where_in => 'INSTR (line, ''RAISE'') > 0);

Answer: the string version of initsrc is executed, since there are default values for the string arguments.
The integer version requires that the start and end numbers be provided.

12.3.4 High−Level Source Management Programs
Recognizing the most common sources of PL/SQL code, PLVio offers two specialized programs to both set
and initialize the source, usrc and asrc. The usrc procedure sets the source repository to the
USER_SOURCE data dictionary view. The asrc procedure sets the source repository to the ALL_SOURCE
data dictionary view. Both usrc and asrc are overloaded with the same arguments as initsrc: the
12.3.3 Using setsrc and initsrc

359

[Appendix A] Appendix: PL/SQL Exercises
"starting at" string or line number, the "ending at" string or line number, and the optional WHERE clause. The
headers for these programs are shown below:
PROCEDURE usrc
(starting_at_in IN VARCHAR2 := NULL,
ending_at_in IN VARCHAR2 := NULL,
where_in IN VARCHAR2 := NULL);
PROCEDURE usrc
(starting_at_in IN INTEGER,
ending_at_in IN INTEGER,
where_in IN VARCHAR2 := NULL);
PROCEDURE asrc
(starting_at_in IN VARCHAR2 := NULL,
ending_at_in IN VARCHAR2 := NULL,
where_in IN VARCHAR2 := NULL);
PROCEDURE asrc
(starting_at_in IN INTEGER,
ending_at_in IN INTEGER,
where_in IN VARCHAR2 := NULL);

With asrc, for example, I could replace these three lines of code:
PLVobj.setcurr ('b:PLVvu');
PLVio.setsrc (PLV.dbtab, 'all_source', schema_col_in => 'owner');
PLVio.initsrc (1, 5);

with the following two lines:
PLVobj.setcurr ('b:PLVvu');
PLVio.asrc (1, 5);

You shouldn't have to deal with all those details when it is the kind of source setting you will be performing
again and again.

12.2 Code Repositories
Supported by PLVio

12.4 The Source WHERE
Clause

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

12.3.3 Using setsrc and initsrc

360

Chapter 12
PLVio: Reading and
Writing PL/SQL Source
Code

12.4 The Source WHERE Clause
PLVio provides a set of programs used within PLVio and also available to you to modify the contents of the
WHERE clause of the SELECT statement for a database table source. These programs must be called after the
call to setsrc and before the call to initsrc.
The default WHERE clause for the database source is:
WHERE name = PLVobj.currname
AND type = PLVobj.currtype

This WHERE clause reflects the relationship between the current object of PLVobj and the default PLVio
source database table, user_source. It is stored directly in the srcrep.select_sql field and is set in
the call to setsrc. Additional WHERE clause information is stored in the where_clause field of the
same srcrep record (see Section 12.2.2, "Database Source or Target" earlier in this chapter).
You can modify this WHERE clause in two ways: replace it completely or add additional elements to that
clause. The set_srcselect will do either of these actions. The set_line_limit applies additional
elements to the WHERE clause. rem_srcselect and rem_line_limit remove elements from the
WHERE clause. The srcselect function displays the current SELECT statement.
Each of these programs is explained below.

12.4.1 Viewing the Current Source SELECT
First, use the srcselect function to retrieve the current structure of the SELECT statement for the source
repository. In the following example, I use p.l to display the current SELECT.
SQL> exec p.l(PLVio.srcselect);
SELECT text, line FROM user_source WHERE instr (text, 'RAISE') > 0 AND
name = 'PLVEXC' ORDER BY line

This string is an example of a SELECT in which the WHERE clause was substituted completely by a call to
set_srcwhere. The following session in SQL*Plus sets the source to the ALL_SOURCE view. The
srcselect function returns the default (and more normal) kind of SELECT built and executed by PLVio.
SQL> exec PLVio.asrc
SQL> exec p.l(PLVio.srcselect);
SELECT text, line FROM all_source WHERE name = :name AND type = :type
AND owner = :owner ORDER BY line

12.4.2 Changing the WHERE Clause
To modify directly the WHERE clause of the SELECT statement, you will call the set_srcwhere
procedure, whose header is:
361

[Appendix A] Appendix: PL/SQL Exercises
PROCEDURE set_srcwhere (where_in IN VARCHAR2);

This procedure modifies the WHERE clause according to the following rules:
1.
If the string starts with AND, then the string is simply concatenated to the current WHERE clause.
2.
If the string starts with WHERE, then the entire current WHERE clause is replaced with the string
provided by the user.
3.
In all other cases, the core part of the WHERE clause (containing the bind variables for
PLVobj.currname and PLVobj.currtype) is preserved, but any other additional elements are
replaced by the specified string.
A few examples will demonstrate this procedure's impact. In each case, I initialize the SELECT statement
with a call to PLVio.asrc so that the select_stg contains this information:
SELECT
FROM
WHERE
AND
AND
ORDER

text, line
all_source
name = :name
type = :type
owner = :owner
BY line

Let's see what happens when I use set_srcselect to change the WHERE clause:
1.
Add a clause to request that only lines 1 through 5 are read from ALL_SOURCE:
PLVio.set_srcselect ('AND line BETWEEN 1 AND 5');

The srcselect now looks like this:
SELECT
FROM
WHERE
AND
AND
AND
ORDER

text, line
all_source
name = :name
type = :type
owner = :owner
line BETWEEN 1 AND 5
BY line

2.
Add the same clause as in Example 1 and then replace it with an element that limits rows retrieved to
those that start with the keyword IF.
PLVio.set_srcselect ('AND line BETWEEN 1 AND 5');
PLVio.set_srcselect ('LTRIM (text) LIKE ''IF%''');

The srcselect now looks like this:
SELECT text, line
FROM
WHERE
AND
AND
AND
ORDER

3.

all_source
name = :name
type = :type
owner = :owner
LTRIM (text) LIKE 'IF%'
BY line

362

[Appendix A] Appendix: PL/SQL Exercises
The following script displays all the lines currently stored in the USER_SOURCE data dictionary
view that contain the keyword RAISE.
DECLARE
line PLVio.line_type;
numlines NUMBER;
BEGIN
PLVio.setsrc (PLV.dbtab);
PLVio.set_srcwhere
('WHERE instr (text, ''RAISE'') > 0');
PLVio.initsrc;
LOOP
PLVio.get_line (line, numlines);
exit when line.eof;
p.l (line.text);
END LOOP;
END;
/

Notice that the string I pass to set_srcwhere begins with the WHERE keyword. This signals to
PLVio that the entire WHERE clause is to be discarded and replaced with the argument string so, in
this case, srcselect would display this string:
SELECT
FROM
WHERE
ORDER

text, line
all_source
instr (text, 'RAISE') > 0
BY line

12.4.3 Setting a Line Limit
The final program you can use to change the WHERE clause is the set_line_limit procedure. The
header of set_line_limit is:
PROCEDURE set_line_limit
(line_in IN INTEGER, loc_type_in IN VARCHAR2 := c_first);

The first argument, line_in, is the line number involved in the restriction. The loc_type_in argument
dictates how the line number is used to narrow down the rows retrieved. There are four possible location
types; the impact of each of these is explained in the table below.
Constant

Action

c_first

Retrieve lines >= specified line number

c_last

Retrieve lines <= specified line number

c_before Retrieve lines > specified line number
c_after Retrieve lines < specified line number
Here are some examples of the impact of set_line_limit:
1.
Request that only lines greater than 100 be retrieved:
PLVio.set_line_limit (100, PLVio.c_after);

which adds the following element to the WHERE clause:
/*LL100*/ AND line > 100 /*LL100*/

12.4.3 Setting a Line Limit

363