Skip to main content

Oh those tricky exception sections! Why won't it handle my error?

Fielded a question the other day from a frustrated PL/SQL developer. He included an exception handler for an exception that was being raised in his program, but the exception propagated out unhandled from the block anyway. What's with that?

To answer that question, why not test your own knowledge of PL/SQL: what will you see on the screen when you execute the following block (with server output enabled):

[Note, sure you could copy and paste the code and run it, but I urge you, I implore you, to just read the code and see if you can sort it out yourself. Reading code - verifying things logically - is a critical skill for any developer to, um, develop.]

DECLARE
   aname VARCHAR2(50);
BEGIN
   DECLARE
      aname VARCHAR2(5) := 'Big String';
   BEGIN
      DBMS_OUTPUT.PUT_LINE (aname);

   EXCEPTION
      WHEN VALUE_ERROR
      THEN
         DBMS_OUTPUT.PUT_LINE ('Inner block');
   END;
   
   DBMS_OUTPUT.put_line (SQLCODE);
   DBMS_OUTPUT.PUT_LINE ('What error?');
EXCEPTION
   WHEN VALUE_ERROR
   THEN
      DBMS_OUTPUT.PUT_LINE ('Outer block');
END;

I present this little quiz in my classes on error management and the answers usually cover the logical range:

"Big S"
"Inner block"
"Inner block then 0 then What error?"

and so on. Well, the answer is:

Outer block

and nothing else. Why? Because in the world of PL/SQL, the exception section can only possibly handle an exception raised in the executable section. When it's raised in the declaration section, the exception always propagates out unhanded.

In this block of code, I try to stuff "Big String" into the nested a name variable - and it's just too long. PL/SQL does not assign the first five characters. Instead it raises the VALUE_ERROR exception (ORA-06502). Since this happened in the declaration section, that inner block's exception section never comes into play. 

The exception propagates out unhandled, shutting down the outer block's executable section, and control is transferred to the exception section, where a match is found and "Outer block" is displayed.


Comments

  1. Slight distraction that the wrong answer is "Big St" instead of "Big S".

    Now, a question, "Would it be an improvement to allow pre-amble EXCEPTION message definitions in PL/SQL in declaration blocks to localize the reported error?" If not, what would your recommendation be for verifying the plausible correctness of declaration blocks? (not rhetorical; I'm actually interested in how you do these things, my friend.)

    ReplyDelete
  2. Thanks, changed "Big St" to "Big S". Maybe I was thinking the space didn't count? "-)

    rsiz, I am a simple PL/SQL programmer. Could you explain perhaps with an example your "pre-amble EXCEPTION message definitions"?

    Also not entirely sure what you mean by plausible correctness, but generally the way I would approach this problem (I want to assign a default value to my variable!) is:

    1. If the default value is a literal, assign it in declaration section. Take responsibility for actually READING your code and verifying it is "plausible".

    2. If calling a function or executing an expression, move the assignment into a nested init procedure and call that at the start of your block. That way you can handle an exception inside the proc, and you do not clutter up your main exec section with assignments.

    BEGIN
    init;
    /* Now rest of block */

    ReplyDelete
  3. So my made-up name fails the "obviousness" test if it was unclear to you. What I mean is essentially directives that you could code up in the style of exceptions but which would fire at parse time instead of execution time so the machine does a bit of desk checking for you. Your suggestion #1 is good, but it would have leaked even for you on "Big St" as 5, and yes, I make trivial mistakes too. Suggestion #2 probably handles the machine automation check as well as implementing the putative feature would and is probably better style. For the example (which now seems moot), I guess allowing EXCEPTION BLOCKS immediately after the DECLARE line and before the actual declares would do it. So in your example the "Inner Block" exception block code would appear after the DECLARE line and before aname.... "Big String";

    I love your blog, by the way.

    ReplyDelete
  4. Oracle offers compile time warnings that will also flag "Big St" for us, as of 11.2 - PLW-6017. As for putting exception sections inside the declaration section, I am fairly confident in saying that is not going to happen in my lifetime. :-)

    ReplyDelete
  5. And thanks for your kind words about my blog. Invite others to check it out, please!

    ReplyDelete
  6. Nice Steven, I always find your article interesting and learning :)

    ReplyDelete

Post a Comment

Popular posts from this blog

Running out of PGA memory with MULTISET ops? Watch out for DISTINCT!

A PL/SQL team inside Oracle made excellent use of nested tables and MULTISET operators in SQL, blending data in tables with procedurally-generated datasets (nested tables).  All was going well when they hit the dreaded: ORA-04030: out of process memory when trying to allocate 2032 bytes  They asked for my help.  The error occurred on this SELECT: SELECT  *    FROM header_tab trx    WHERE (generated_ntab1 SUBMULTISET OF trx.column_ntab)       AND ((trx.column_ntab MULTISET             EXCEPT DISTINCT generated_ntab2) IS EMPTY) The problem is clearly related to the use of those nested tables. Now, there was clearly sufficient PGA for the nested tables themselves. So the problem was in executing the MULTISET-related functionality. We talked for a bit about dropping the use of nested tables and instead doing everything in SQL, to avoid the PGA error. That would, however require lots of work, revamping algorithms, ensuring correctness, you know the score. Then my eyes snagge

How to Pick the Limit for BULK COLLECT

This question rolled into my In Box today: In the case of using the LIMIT clause of BULK COLLECT, how do we decide what value to use for the limit? First I give the quick answer, then I provide support for that answer Quick Answer Start with 100. That's the default (and only) setting for cursor FOR loop optimizations. It offers a sweet spot of improved performance over row-by-row and not-too-much PGA memory consumption. Test to see if that's fast enough (likely will be for many cases). If not, try higher values until you reach the performance level you need - and you are not consuming too much PGA memory.  Don't hard-code the limit value: make it a parameter to your subprogram or a constant in a package specification. Don't put anything in the collection you don't need. [from Giulio Dottorini] Remember: each session that runs this code will use that amount of memory. Background When you use BULK COLLECT, you retrieve more than row with each fetch,

Quick Guide to User-Defined Types in Oracle PL/SQL

A Twitter follower recently asked for more information on user-defined types in the PL/SQL language, and I figured the best way to answer is to offer up this blog post. PL/SQL is a strongly-typed language . Before you can work with a variable or constant, it must be declared with a type (yes, PL/SQL also supports lots of implicit conversions from one type to another, but still, everything must be declared with a type). PL/SQL offers a wide array of pre-defined data types , both in the language natively (such as VARCHAR2, PLS_INTEGER, BOOLEAN, etc.) and in a variety of supplied packages (e.g., the NUMBER_TABLE collection type in the DBMS_SQL package). Data types in PL/SQL can be scalars, such as strings and numbers, or composite (consisting of one or more scalars), such as record types, collection types and object types. You can't really declare your own "user-defined" scalars, though you can define subtypes  from those scalars, which can be very helpful from the p