Thursday, 8 September 2011

Immediate termination of a procedure

Question:

How do I exit immediately from a procedure?

Answer:

There are two ways to immediately terminate the execution of a procedure:
1. Execute the RETURN; statement. When this statement is encountered by the PL/SQL runtime engine, the current block is terminated, and control is returned to the calling block.
2. Raise an exception. When a RAISE statement is encountered, the executable section is closed down. If there is a handler for the exception that was raised, it will be executed. If not, the exception will be propagated unhandled to the calling block.
Here is an example of the RETURN; statement:
PROCEDURE stop_now (
value_in IN VARCHAR2
)
IS
BEGIN
IF value_in = 'STOP!'
THEN
RETURN;
ELSIF value_in = 'GO'
THEN
do_stuff;
END IF;

do_more_stuff;
END stop_now;
Here is an example of using an exception to achieve the same effect:
PROCEDURE stop_now (
value_in IN VARCHAR2
)
IS
stop_program EXCEPTION;
BEGIN
IF value_in = 'STOP!'
THEN
RAISE stop_program;
ELSIF value_in = 'GO'
THEN
do_stuff;
END IF;

do_more_stuff;
EXCEPTION
WHEN stop_program
THEN
NULL;
END stop_now;
Having shown you those two techniques, I suggest that you avoid using them. They are both very unstructured approaches to managing the logical flow of your subprogram. They both violate a commonly-accepted principle of structured methodology: "One way in, one way out." In other words, there should be one way in to the executable section, and there should be one way out (assuming that an exception has not been raised): the last line of the executable section is reached and control is returned to the calling block.
With both RETURN; and RAISE, you are adding another way "out" of the procedure. With multiple exit points come an increased complexity in your subprogram, making it more difficult to debug and maintain the code. You should also, generally, not use the RAISE mechanism to "raise" actions, as opposed to errors.
Instead, you should rely on the native flow control statements of PL/SQL, including IF, CASE, and LOOP, to determine which lines of code in your subprogram are executed and when control is returned to the calling block.
In the case of the simplistic stop_now procedure, it would be better to write code more along these lines:
PROCEDURE stop_now (
value_in IN VARCHAR2
)
IS
BEGIN
IF value_in = 'GO'
THEN
do_stuff;
END IF;

IF value_in != 'STOP!'
THEN
do_more_stuff;
END IF;
END stop_now;
Here is similar advice based on the same principles:
Don't use GOTO statements to direct the logical flow of your program. It is extremely difficult to understand and maintain code that relies on GOTOs. You can almost always avoid a GOTO by careful restructuring of your program. One of the few scenarios that might justify use of a GOTO is when you need to perform a "surgical" edit to an existing, complex program that does not have a regression test. You want to implement you change and then "get out" as cleanly as possible so as to minimize your impact in the program. Certainly, however, when building new programs, there is no reason to use a GOTO.
Don't use the EXIT statement to terminate FOR and WHILE loops. The EXIT statement should only be used to terminate a simple loop (LOOP ... END LOOP;). Both FOR and WHILE have a built-in exit clause. FOR loops stop when you have gone through all the specified iterations. WHILE loops stop when the Boolean expression associated with the WHILE evaluates to FALSE or NULL.
Here is an example of "one way in, two ways out" in a WHILE loop (code courtesy of Bryn Llewellyn, hence the different formatting style!):
declare
subtype Idx_t is pls_integer;
Overall_Lim constant Idx_t := 53;
Inner_Lim constant Idx_t := 7;
Counter Idx_t := 0;
Inner_Runner Idx_t;

procedure Do_Stuff(i in Idx_t) is
begin
DBMS_Output.Put_Line(i);
end Do_Stuff;
begin
<>loop
Inner_Runner := 0;
<>while Inner_Runner < Inner_Lim loop
Counter := Counter + 1;
exit Outer when Counter > Overall_Lim;
Inner_Runner := Inner_Runner + 1;
Do_Stuff(Inner_Runner);
end loop Inner;
end loop Outer;
end;
/

No comments:

Post a Comment