Tải bản đầy đủ
2 PLVtrc: Tracing Execution of PL/SQL Programs

2 PLVtrc: Tracing Execution of PL/SQL Programs

Tải bản đầy đủ

[Appendix A] Appendix: PL/SQL Exercises
PROCEDURE turn_off;
FUNCTION tracing RETURN BOOLEAN;

To activate the trace facility, enter:
SQL> execute PLVtrc.turn_on;

To de−activate the trace facility enter:
SQL> execute PLVtrc.turn_off;

To control logging of trace messages using the PLVlog package, PLVtrc offers these programs:
PROCEDURE log;
PROCEDURE nolog;
FUNCTION logging RETURN BOOLEAN;

This command turns on logging of trace message:
SQL> execute PLVtrc.log;

To turn off logging, enter:
SQL> execute PLVtrc.nolog;

You can also request that when trace information is displayed the current module is included in the message.
You control the inclusion of that data with this toggle:
PROCEDURE dispmod;
PROCEDURE nodispmod;
FUNCTION displaying_module RETURN BOOLEAN;

So to include the module in trace messages, enter:
SQL> execute PLVtrc.dispmod;

To ignore the module name enter:
SQL> execute PLVtrc.nodispmod;

To turn on all these options, you need to execute all three "on" procedures:
SQL> exec PLVtrc.dispmod;
SQL> exec PLVtrc.log;
SQL> exec PLVtrc.turn_on;

The order in which you call these toggle programs is not important. Just remember that you cannot write
information to the log or display the current module unless the overall trace is turned on.
In all examples above I have shown the syntax for executing the programs from within SQL*Plus. You can
also call these programs from within a PL/SQL program, in which case you would not use the execute
command.

21.2.2 Accessing the PL/SQL Call Stack
The PLVtrc package provides you with the ability to access and parse the call stack maintained by the
PL/SQL runtime engine. This call stack is available with a call to the
DBMS_UTILITY.FORMAT_CALL_STACK function. Here is an example of the string returned by this
function:
21.2.2 Accessing the PL/SQL Call Stack

584

[Appendix A] Appendix: PL/SQL Exercises
−−−−− PL/SQL Call Stack −−−−−
object
line
object
handle
number name
88ce3f74
8 package STEVEN.DISPCSTK
88e49fc4
2 function STEVEN.COMPANY_TYPE
88e384c8
1 procedure STEVEN.CALC_PROFITS
88e3823c
1 procedure STEVEN.CALC_TOTALS
88e49390
1 procedure STEVEN.CALC_NET_WORTH
88e2bd20
1 anonymous block

The string actually contains many newline characters (you can find these by searching for CHR(10) with the
INSTR function). It is designed for easy display, but not easy manipulation within a programmatic setting.
The PLVtrc package offers two programs to access this PL/SQL call stack:
ps_callstack
Returns the same string returned by the FORMAT_CALL_STACK function. It is provided for
consistency and to save you some typing.
ps_module
Returns the nth module in the PL/SQL call stack, with the default being the "most recent module,"
(i.e., the program that was active before the PLVtrc.module function was called.) This, by the
way, is the second module in the stack.
NOTE: One big problem with FORMAT_CALL_STACK is that it will not provide the name
of the current program within a package. If you are executing a standalone function or
procedure, FORMAT_CALL_STACK shows you its name. If you are running a function
within a package, however, it only shows you the package name. If your code design is
package−driven, this fact renders the FORMAT_CALL_STACK function largely irrelevant.
This shortcoming is the main reason that PLVtrc also maintains its own program call stack in
a PL/Vision stack.

21.2.3 Tracing Code Execution
PLVtrc offers two programs to trace the execution of your code: action and show. The action program's
header is:
PROCEDURE action
(string_in IN VARCHAR2 := NULL,
counter_in IN INTEGER := NULL,
prefix_in IN VARCHAR2 := NULL);

You pass a string, a numeric counter or indicator, and another string that is used as a prefix on the trace
message. The action procedure is used by startup, terminate, and the other activity trace module,
show. It is, in other words, the lowest−level trace procedure.
The show procedure is heavily overloaded. Like the p.l procedure, the show program comes in many
flavors of argument combinations, as shown in the list below. This is done to make it easy for you to pass
different combinations of data for display without having to perform TO_CHAR conversions and
concatenations.
The following datatype combinations are supported by PLVtrc.show:
Single−value Double−value Triple−value
string

string, date

string, number, number

date

string, number string, number, date

21.2.3 Tracing Code Execution

585

[Appendix A] Appendix: PL/SQL Exercises
number

string, boolean string, number, boolean

boolean
Here are the headers for the single−value show procedures:
PROCEDURE
PROCEDURE
PROCEDURE
PROCEDURE

show
show
show
show

(stg1 IN VARCHAR2);
(date1 IN DATE, mask_in IN VARCHAR2 := PLV.datemask);
(bool1 IN BOOLEAN);
(num1 IN NUMBER);

Here are the headers for the double−value show procedures:
PROCEDURE show (stg1 IN VARCHAR2, num1 IN NUMBER);
PROCEDURE show
(stg1 IN VARCHAR2,
date1 IN DATE, mask_in IN VARCHAR2 := PLV.datemask);
PROCEDURE show (stg1 IN VARCHAR2, bool1 IN BOOLEAN);

Here are the headers for the triple−value show procedures:
PROCEDURE show
(stg1 IN VARCHAR2, num1
PROCEDURE show
(stg1 IN VARCHAR2, num1
date1 IN DATE, mask_in
PROCEDURE show
(stg1 IN VARCHAR2, num1

IN NUMBER, num2 IN NUMBER);
IN NUMBER,
IN VARCHAR2 := PLV.datemask);
IN NUMBER, bool1 IN BOOLEAN);

You can place calls to both action and show in your programs. No output is generated from these message
lines until you turn on the trace. And since there is very little overhead involved in calling these programs,
you can leave the trace in your code even when it goes into production status. When you have to debug the
code, you simply call PLVtrc.turn_on, run the application, and you have a wealth of information
available to you.

21.2.4 The PLVtrc Program Stack
PLVtrc offers two programs to build its own program execution stack: startup and terminate. You can
also get information about the current and previous modules of the stack.
The PLVtrc call stack operations provide two key advantages over the builtin:
1.
The call stack contains the names of the specific programs being executed (or whatever strings you
pass to represent the names of programs).
2.
You can call startup at any point, so you can give names in your call stack to anonymous blocks as
well as named modules. Just don't forget to terminate if you run startup.
21.2.4.1 startup
You should call PLVtrc.startup as the first line in the body of your program. Its header is:
PROCEDURE startup
(module_in IN VARCHAR2, string_in IN VARCHAR2 := NULL);

You provide the module name or abbreviation or whatever string you want to record as representing the
21.2.4 The PLVtrc Program Stack

586

[Appendix A] Appendix: PL/SQL Exercises
program. You can also pass in a second string argument. This value is displayed or logged by PLVtrc,
according to the toggle settings. This second argument allows you to pass variable data into the trace.
21.2.4.2 terminate
The terminate program performs a task opposite that of startup: it pops off the stack the most recently
pushed module and sets the previous module variable. The header for terminate is:
PROCEDURE terminate (string_in IN VARCHAR2 := NULL);

As with startup, you can provide a string to be displayed or logged, depending on the status of the PLVtrc
toggles. You should call PLVtrc.terminate as the last executable statement in your procedure, and
immediately before your RETURN statement in a function.
You should also call PLVtrc.terminate in each of your exception handlers in a PL/SQL block where
startup was called. Otherwise the enclosing module will not be popped off the stack when the block fails.
If you use one of the high−level handlers of PLVexc to handle your exception, however, you do not have
to −− and should not −− call terminate. Those handlers do that for you.
21.2.4.3 Current module
Each time PLVtrc.startup is executed, it pushes the current module onto the PLVtrc execution stack and
sets the current module to the first argument in the call to startup.
You can obtain the name of the current module in the PLVtrc environment by calling the currmod function,
whose header is:
FUNCTION currmod RETURN VARCHAR2;

21.2.4.4 Previous module
To see the name of the previous module, you can call the prevmod function, whose header is:
FUNCTION prevmod RETURN VARCHAR2;

The PLVexc package makes use of this function so that it can record the program in which an exception was
raised. You might not have too much use for prevmod.
See Section 21.2.5, "Using PLVtrc" to see how you put these pieces together.
21.2.4.5 Emptying the stack
The clearecs procedure empties the execution stack by recreating it. The header for this procedure is:
PROCEDURE clearecs;

You will want to use this program when you have finished running a test and you want to make sure that there
aren't any extraneous module names left on the stack.
21.2.4.6 Displaying the stack
You can display the contents of the stack with a call to showecs; the header is:
PROCEDURE showecs;

21.2.4 The PLVtrc Program Stack

587

[Appendix A] Appendix: PL/SQL Exercises
This program, in turn, calls PLVlst.display to display the contents of the list, which comprise the
underlying data structure for the stack (implemented, actually, with the PLVstk package −− an interesting
exercise in code layering).
21.2.4.7 Retrieving stack contents
If you do not want to directly display the PLVtrc stack, you can extract it as a string in much the same format
as that provided by the builtin FORMAT_CALL_STACK function with the ecs_string function. Its
header is:
FUNCTION ecs_string RETURN VARCHAR2;

Each module name in the call stack is separated by a newline character.
You can also retrieve a single module from the stack with the module procedure. This program's header is:
FUNCTION module (pos_in IN INTEGER := c_top_pos)
RETURN VARCHAR2;

where pos_in is the position in the stack in which you are interested. The current program is stored in the
top−most position of 0 (actually not yet on the call stack) and is encapsulated in the package constant,
c_top_pos. To obtain the name of the module that called the current program, you would pass in a position
of 1.

21.2.5 Using PLVtrc
The following examples show you how to use the different elements of the PLVtrc package.
1.
Use the startup and terminate procedures in my procedure to integrate it into the PLVtrc call
stack. In addition, handle exceptions using the PLVexc component (which also performs a
terminate).
CREATE OR REPLACE PROCEDURE proc (val in number) IS
BEGIN
PLVtrc.startup ('proc');
IF 1/val > 1 THEN NULL; END IF;
PLVtrc.terminate;
EXCEPTION
WHEN OTHERS THEN PLVexc.rec_continue;
END;
/

2.
Show the employee name and date before giving them a name.
FOR emp_rec IN emp_cur
LOOP
PLVtrc.show (emp_rec.ename, emp_rec.hiredate);
give_raise (emp_rec.empno);
END LOOP;

Remember: this call to show will not actually generate any output unless you turn on at least one of
the trace features.
3.
Use the startup and terminate procedures to track execution of a nested, anonymous block.
Notice that in the exception section, I explicitly call terminate for the NO_DATA_FOUND exception.
21.2.4 The PLVtrc Program Stack

588

[Appendix A] Appendix: PL/SQL Exercises
For all other errors, I let the PLVexc package handle the PLVtrc.terminate and the error as
well.
PROCEDURE annual_calcs (val in number)
IS
BEGIN
PLVtrc.startup ('proc');
calc_gross_revenue;
BEGIN
PLVtrc.startup ('analyze');
calc_rev_distribution;
PLVtrc.terminate;
EXCEPTION
WHEN OTHERS THEN PLVexc.halt;
END;
call_profits;
PLVtrc.terminate;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
PLVtrc.terminate;
p.l ('Invalid value: ' || TO_CHAR (val);
WHEN OTHERS
THEN
/* This program calls PLVtrc.terminate */
PLVexc.rec_continue;
END;
/

21.1 PLVlog: Logging
Activity in PL/SQL
Programs

22. Exception Handling

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

21.2.4 The PLVtrc Program Stack

589

Chapter 22

590

22. Exception Handling
Contents:
The Challenge of Exception Handling
Application−Specific Exception Packages
Implementing PLVexc
The PLVexc (PL/Vision EXCeption handling) package provides a powerful, plug−and−play component to
perform exception handling in your PL/SQL programs. It makes use of PLVlog to automatically write errors
to the log of your choice (database table, PL/SQL table, etc.). It offers high−level exception handler 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.
This chapter first analyzes the need for exception handling and the traditional solutions in a PL/SQL
environment. It then presents the elements of the PLVexc package, along with the information you need to
apply this functionality in your own programs. Finally, the chapter explores the implementation of PLVexc in
two phases.

22.1 The Challenge of Exception Handling
Do you test your own programs? If you do, I am willing to wager large sums of money that your users feel as
if they are the ones doing the testing. Authors of a program cannot find all the bugs in their software. They
have, in fact, an uncanny ability to unconsciously follow a path through their code that avoids all bugs and
logical holes.Programmers cannot be responsible for testing their own code.
Tightly linked to proper testing is proper error handling. Even if a program does not have actual bugs, it does
need to handle abnormal conditions gracefully. Yet exception handling is the last thing any of us wants to
worry about when we build our PL/SQL programs. To recognize the need to write exception handlers is to
acknowledge that things might go wrong. Who can afford the time and resources to focus on the negative? It
is all we can do to get our programs to work properly under normal circumstances −− to conform, in other
words, to the specifications. How many times do you hear others (never yourself, right?) say things like this:
"After I get my program to work, I'll go back and put in the exception handlers." Of course, no one ever has
the time to "go back."
Anticipating and handling the problems in one's program is crucial to writing a robust application. PL/SQL
offers a very powerful architecture for dealing with abnormal conditions: the exception section and exception
handlers. Yet this architecture can also be difficult to use, particularly in a manner that ensures consistent
error handling across an entire application. This difficulty, combined with the importance of getting it right,
makes exception handling an excellent candidate for the use of a plug−and−play component.

22.1.1 A Package−Based Solution
A plug−and−play component, based in PL/SQL package technology, can hide the complexities and
difficulties of exception handling. It can provide a declarative interface to both responding to and logging
problems encountered in an application. With this declarative approach, programmers can, in the exception
section, state what they want the program to do and leave it to the underlying engine (the package) to figure
out how to get the job done. This is very similar to the theory and practices of the SQL language.
To give you a taste of what is possible, the following exception section has two different exception handlers:
one for NO_DATA_FOUND and one for all other exceptions. When NO_DATA_FOUND is raised, a
message is displayed to the user, the error is recorded, and then the program is halted. When any other error
occurs, that error is recorded and then processing continues.

22. Exception Handling

591

[Appendix A] Appendix: PL/SQL Exercises
EXCEPTION
WHEN NO_DATA_FOUND
THEN
PLVexc.recNstop ('Company has not been located.');
WHEN OTHERS
THEN
PLVexc.recNgo;
END;

Both of these exception handlers make use of the PLVexc package. This package automatically records the
current program, user, error number, and message. It relies on the PLVlog package so that the recording can
take place to a database table or to a PL/SQL table. It performs rollbacks to the last savepoint if requested so
that the current transaction is "erased," but the write to the log table is preserved.
There is, in other words, a lot going on when a developer makes a simple, high−level call to a PLVexc error
handling procedure. Yet all that activity is rendered invisible by the package. All the user of PLVexc has to
know is what kind of action he wants to perform. Ignore the error? Record and continue? Record and halt?
Just tell PLVexc what it is you want to do and let the component do the rest.
The PLVexc package is the best example in PL/Vision of a plug−and−play component, prebuilt code you can
plug into your own programs, instantly improving the functionality, reliability, and flexibility of your own
applications. In this chapter, I'll first show you how to use PLVexc. Then I'll present the implementation of
PLVexc in two stages: the first version I built and the second, "final" version of the package you will find on
the disk.
I show you the two stages of development and design of PLVexc so that you can follow the thought process I
used to move from a specific problem to an increasingly generalized solution. Learning how to use PLVexc in
your environment is important, but even more important and more difficult is to develop your own analytical
and programming skills so that you can build your own plug−and−play package−based components.

22.1.2 Package−Based Exceptions
The PLVexc package provides several predefined exceptions for you to use and also to provide a model for
adding additional exceptions in the same way to PLVexc. These exceptions are:
NO_SUCH_TABLE
ORA−00942: table or view does not exist
SNAPSHOT_TOO_OLD
ORA−01555: snapshot too old (rollback segment too small)
A third exception, process_halted, is used in the bailout feature and is discussed later in this chapter.
The code required to declare these exceptions is as follows:
no_such_table EXCEPTION;
PRAGMA EXCEPTION_INIT (no_such_table, −942);
snapshot_too_old EXCEPTION;
PRAGMA EXCEPTION_INIT (snapshot_too_old, −1555);

Why does PLVexc predefine these exceptions (and invite you to add more)? So every developer in your
organization does not have to take these steps over and over again. Specifically, to achieve:

Consistent error handling in the application: developers will not define their own named exceptions
willy−nilly throughout their programs. They make use of existing names for exceptions. Instead of
22.1.2 Package−Based Exceptions

592

[Appendix A] Appendix: PL/SQL Exercises
coding their own handlers for WHEN OTHERS, they can use the prebuilt program,
display_error.

Less buggy code: Novice and intermediate programmers do not have to deal with the
EXCEPTION_INIT pragma and other complicated issues related to exception handlers. With the
package, these details are shielded from the developer.
Once such exceptions are defined in a package, individual developers no longer need to know about the
specific error numbers, nor do they have to be hassled with EXCEPTION_INIT syntax.

22.1.3 Impact of Predefined Exceptions
Compare the two PL/SQL blocks below. The first demonstrates the kind of code one would have to write
without the predefined exceptions as found in PLVexc.
DECLARE
no_such_table EXCEPTION;
PRAGMA EXCEPTION_INIT (no_such_table, −942);
BEGIN
perform_action;
EXCEPTION
WHEN no_such_table
THEN
do_something;
END;

The second block shows how one's code would look with PLVexc in place:
BEGIN
perform_action;
EXCEPTION
WHEN PLVexc.no_such_table
THEN
PLVexc.handle
('action', SQLCODE, PLVexc.c_recNstop);
END;

No declaration section required, no need for the developer to write all that extra code. Just pick from the
package−based selections and keep on developing! The benefits of this approach go well beyond short−term
productivity gains. If all developers work from this predefined set of exceptions, overall application code
volume is reduced, and along with that the number of bugs that are introduced and then tested out of the
application.
The PLVexc package contains only two predefined exceptions. When you apply this technique into your own
environment you will undoubtedly want to add to this section any of the unnamed system exceptions your
developers will encounter routinely. And even if you don't think of them all before development begins, you
can always add to the set of exceptions as you proceed. Simply set as a guideline for programmers that they
never use the EXCEPTION_INIT pragma in their code. Instead, they take the time to add that exception to the
package and then reference the package−based exception.

22.1.4 Exception Handling Actions
One of the major improvements offered by PLVexc is the ability for a developer to simply state the kind of
action needed in a particular exception handler. You can do this in two ways: pass the action as the third
argument to the low−level handle procedure or call a high−level handler whose very name encapsulates the
action.
22.1.3 Impact of Predefined Exceptions

593

[Appendix A] Appendix: PL/SQL Exercises
PLVexc supports four different exception−handling actions. These are presented in the table below, along
with the corresponding packaged constants used in the call to handle and the corresponding high−level
handler program.
Action

Constant

Continue processing

Record and then
continue

Halt processing

Record and then halt
processing

Handler Program Description
go

Continue processing; do not record the error. This is
the equivalent of WHEN OTHERS THEN NULL,
which means "ignore this error."

recNgo

Record the error and then continue processing. This
action would be appropriate when the exception
affects the current block but is not severe enough to
stop the entire session.

stop

Stop processing; do not record the error. This action
causes PLVexc to raise the process_halted
exception. This action would be appropriate when the
exception is so severe (either in terms of the database
or the application) that it requires termination of the
entire session.

recNstop

Record the error and then

c_go

c_recNgo

c_stop

c_recNstop

stop processing. This action causes PLVexc to raise
the process_halted exception, as with c_stop.
By providing named constants, users of PLVexc do not have to be aware of the specific literal values used by
PLVexc. This advantage is even greater when calling the stop or recNgo programs. Instead of passing
cryptic acronyms, users can rely on named elements to make their code self−documenting and easy to
maintain. Most importantly, users do not have to be aware of either how the recording process takes place or
how the program is halted. They can just make the request.

22.1.5 What About the Reraise Action?
Another kind of action you might want to take is to reraise the same exception that brought you into the
exception section. You can do this in PL/SQL by issuing an unqualified RAISE statement, as shown below:
EXCEPTION
WHEN OTHERS
THEN
p.l ('Error code: ', SQLCODE);
RAISE;

In this fragment, when any error occurs, I display the error code and then reraise that same error to propagate
the exception out to the enclosing block.
This is a very useful feature of PL/SQL and it seemed only reasonable to implement this action in PLVexc
when I encountered this functionality about six months ago (yet another aspect of PL/SQL of which I was
ignorant when writing Oracle PL/SQL Programming ). Eager to please, I spent an hour or two adding the
necessary constant, high−level handler and accompanying code. A simplified version of my high−level
handler looked like this:
PROCEDURE reraise
IS
BEGIN
p.l ('description of error');
RAISE;
END;

22.1.5 What About the Reraise Action?

594