29 January 2012

Different error handling behavior between EXECUTE IMMEDIATE and DBMS_SQL (11296)

One of the Q4 2011 playoff quizzes examined the way that user-defined exceptions are handled. If you didn't participate in the playoff, you may want to view this quiz - even try to answer it for yourself - before reading this post.

Iudith Mentzel, who placed 5th in the playoff, took a closer look at the handling of user-defined exceptions raised in a dynamic PL/SQL block - and discovered something odd: the behavior when native dynamic SQL (EXECUTE IMMEDIATE) was used is different from that of DBMS_SQL. Check it out....and let us know if you have an idea as to why this is happening.

1. Create a package with two user-defined exceptions.
CREATE OR REPLACE PACKAGE plch_pkg
IS
   e1   EXCEPTION;
   e2   EXCEPTION;
END;
/
2. Try to catch the exception with native dynamic SQL and it goes unhandled:
BEGIN
   EXECUTE IMMEDIATE 'BEGIN RAISE plch_pkg.e2; END;';
EXCEPTION
   WHEN plch_pkg.e1
   THEN
      DBMS_OUTPUT.put_line ('e1 caught');
   WHEN plch_pkg.e2
   THEN
      DBMS_OUTPUT.put_line ('e2 caught');
END;
/
BEGIN
*
ERROR at line 1:
ORA-06510: PL/SQL: unhandled user-defined exception
ORA-06512: at line 1
ORA-06512: at line 2
3. But with DBMS_SQL, it is trapped:
DECLARE
   c   INTEGER := DBMS_SQL.open_cursor;
   s   INTEGER;
BEGIN
   DBMS_OUTPUT.put_line ('Parsing ...');
   DBMS_SQL.parse (c, 'BEGIN RAISE plch_pkg.e2; END;', DBMS_SQL.native);

   DBMS_OUTPUT.put_line ('Executing ...');
   s := DBMS_SQL.execute (c);
EXCEPTION
   WHEN plch_pkg.e1
   THEN
      DBMS_OUTPUT.put_line ('e1 caught');
      DBMS_OUTPUT.put_line ('status=' || s);
      DBMS_OUTPUT.put_line ('sqlcode=' || SQLCODE);
   WHEN plch_pkg.e2
   THEN
      DBMS_OUTPUT.put_line ('e2 caught');
      DBMS_OUTPUT.put_line ('status=' || s);
      DBMS_OUTPUT.put_line ('sqlcode=' || SQLCODE);
END;
/
Parsing ...
Executing ...
e2 caught
status=
sqlcode=1
This is very interesting and unexpected (to me). Do any of you have any ideas on what might be causing this?

Thanks to Iudith for another fascinating exploration!

Cheers, SF

Q4 2011 Championship Playoff Results

You will find below the rankings for the Q4 2011 playoff; the number next to the player's name is the number of times that player has participated in a playoff. Congratulations first and foremost to our top-ranked players:

1st Place: Frank Schrader, Germany, wins an Amazon.com US$250 Gift Card.
2nd Place: Janis Baiza, Latvia, wins an Amazon.com US$175 Gift Card.
3rd Place: Valentin Nikotin, Russia, wins an Amazon.com US$100 Gift Card.

There are several results worthy of special comment:

1. Frank Schrader not only had the highest score, but actually got 100% of the quizzes right, the only player to do so. Very impressive, Frank! But even more impressive is that Frank won first place in the Q1 and Q3 2011 playoffs as well. In other words, Frank has placed 1st in 3 of 4 quarterly championships this year.

2. Valentin Nikotin pursued an interesting strategy. He completed the entire competition in just over 7 minutes, less than half that of almost all players. His % correct was "only" 86.2% (as compared to, say, that of Siim Kask with 96.6%, who placed just after him at 4th), which was enough to propel him to third place.

3. Vincent Malgrat participated in his first playoff, and broke into the top ten. Nice work, Vincent!

We have upgraded the Winners page to show you not only the rankings and results of all playoff participants (click on the All Playoff Prizes and Rankings button), but also make it easy for you to compare the players' performance in the playoff with that of the quarter.

Congratulations to everyone who played in the playoff. I hope you found it entertaining, challenging and educational.

Steven Feuerstein

Rank Name (# of Playoffs) Country Total Time Total Score
1Frank Schrader (6)Germany14 mins 22 secs2963
2Janis Baiza (4)Latvia22 mins 12 secs2641
3Valentin Nikotin (4)Russia07 mins 16 secs2635
4Siim Kask (5)Estonia24 mins 03 secs2619
5mentzel.iudith (5)Israel28 mins 44 secs2375
6Mike Pargeter (5)United Kingdom15 mins 08 secs2372
7Kevan Gelling (4)Isle of Man26 mins 55 secs2372
8Chris Saxon (3)United Kingdom15 mins 42 secs2356
9Jeff Kemp (7)Australia24 mins 33 secs2354
10Vincent Malgrat (1)French Republic23 mins 23 secs2277
11Niels Hecker (6)Germany25 mins 45 secs2250
12Randy Gettman (5)United States18 mins 39 secs2217
13Chad Lee (3)United States29 mins 39 secs2172
14John Hall (4)United States21 mins 01 secs2155
15james su (4)Canada22 mins 35 secs2123
16Joaquin Gonzalez (4)Spain14 mins 02 secs2094
17Dalibor Kovač (4)Croatia28 mins 10 secs2077
18Anna Onishchuk (4)Ireland27 mins 58 secs2056
19Andre van der Put (1)Netherlands12 mins 52 secs1998
20Viacheslav Stepanov (4)Russia19 mins 26 secs1971
21kowido (4)No Country Set29 mins 07 secs1943
22Stelios Vlasopoulos (2)Greece29 mins 54 secs1872
23Ninoslav Čerkez (2)Croatia29 mins 42 secs1871
24Frank Schmitt (1)Germany27 mins 38 secs1847
25Nina (1)Russia29 mins 24 secs1667
26Gideon Bruggink (1)Netherlands29 mins 56 secs1596
27Alain Boulianne (2)French Republic26 mins 40 secs1587
28_tiki_4_ (1)Germany25 mins 53 secs1377
29monpara.sanjay (1)India26 mins 28 secs1346
30Syed Ariful Bari (2)Bangladesh03 mins 20 secs1018

27 January 2012

Compiling Incomplete Program Units (9633)

The 25 January quiz tested your knowledge of the minimal amount of code needed to "create" a PL/SQL procedure in the data dictionary, even if the resulting program unit is invalid.

The choices offered were:

CREATE PROCEDURE plch_test;
CREATE PROCEDURE plch_test AS;
CREATE PROCEDURE plch_test NULL;
CREATE PROCEDURE plch_test IS BEGIN NULL; END;

The first was scored as incorrect and the last three as correct.

Several players demonstrated ways to execute these statements in either Toad or SQL Developer that either (a) caused the first statement to create an object (invalid procedure) or (b) caused the third statement to not create an object in the database.

The author of this quiz, Jeff Kemp, did some research and found the following:

SQL*Plus: no problem - the first statement does not create any object, the latter 3 statements do create an object.

TOAD - I get varying results for option 3, depending on how I invoke the statement in the SQL editor.

Quiz Option 1: CREATE PROCEDURE PLCH_TEST;

1. Highlight statement, press F5
  Result: "ORA-00911: invalid character" - no object created

2. Highlight statement, press F9
  Result: "[Error] Syntax check (5: 27): Found:  ';' Expecting: AS  IS   -or-   ......" - no object created

3. Highlight statement, press Ctrl+Enter
  Result: "[Error] Syntax check (5: 27): Found:  ';' Expecting: AS  IS   -or-   ......" - no object created

So I haven't been able to create an object using the first quiz option at all. By the way, each of the above tests were repeated after highlighting just the statement by itself, as well as highlighting the statement plus the following slash (/).

Quiz Option 3: CREATE PROCEDURE PLCH_TEST NULL;

1. Highlight statement, press F5
  Result: object created with compilation error

2. Highlight statement, press F9
  Result: "[Error] Syntax check (5: 27): Found:  ';' Expecting: AS  IS   -or-   ..." - no object created

3. Highlight statement, press Ctrl+Enter
  Result: "[Error] Syntax check (5: 27): Found:  ';' Expecting: AS  IS   -or-   ..." - no object created

Looks like if you use the F5 option, the third CREATE PROCEDURE quiz option does work in Toad.

I, on the other hand, was able to get the first choice to create an object by pressing the green arrow button in the Editor menu in Toad. Go figure.
The assumptions for the PL/SQL quizzes state that you can use your choice of IDE (SQL*Plus, Toad, SQL Developer, etc.). For this quiz, that turned out to be a very tricky assumption, because the IDEs offer multiple ways to execute statements that handle the terminating character of the statements differently.
 
For some, the semi-colon is treated as part of the code. In others, it is treated as the terminator of the statement, to trigger execution. 

As a result, I will be giving everyone credit for the 1st and 3rd choices. In addition, I have changed the question to specify that these statements execute in SQL*Plus and I have explicitly included the "/" to execute the buffer.

23 January 2012

Q4 2011 Championship Playoff on 25 January

The 7th quarterly championship playoff of the PL/SQL Challenge will take place on Wednesday, 25 January.

Forty-two players qualified to participate. In this playoff, players will have 30 minutes in which to take five quizzes. Scoring and ranking for the playoff follows the same formula used for the daily quiz.

While everyone cannot win, I wish all players the best as they demonstrate their expertise in the Oracle PL/SQL language!

Steven Feuerstein

19 January 2012

Exploring DBMS_UTILITY.FORMAT_CALL_STACK (9628)

The 18 January quiz tested your knowledge of the contents of strings returned by a call to DBMS_UTILITY.FORMAT_CALL_STACK, which (roughly speaking) answers the question: "How did I get here?" (with a nod to the Talking Heads, one of my all-time favorite bands)

Iudith Mentzel went exploring and came up with some interesting tidbits that I thought I would share.

First, she noted that "this quiz was very similar with the quiz from 21 April 2011. The only difference was that it mentioned a nested block for the procedure that is calling DBMS_UTILITY.FORMAT_CALL_STACK...by the way, in comparison with the previous one that was scored as Advanced, this one was even trickier and scored as Intermediate, which looks not very consistent."

You are absolutely right, Iudith. That was not very consistent. Among other things, once a reviewer pointed out that the bottom of the stack could under some circumstances not be an anonymous block, I should have changed it to Advanced.

With my spare moments, I am going back to older quizzes and cleaning them up (adding lesson summaries and verification code, for example, since these were not even stored with questions when the PL/SQL Challenge first started). In the process I am finding many quizzes whose levels of difficulty and other information should probably be adjusted. These changes would, however, also affect scoring. So at some point, we will tweak our backend so that "old" scores and ranking data are archived and will not be affected by changes in the questions.

Now on to more substantive thoughts from Iudith on the call stack function (her words in blue):

Checking the quiz scenario, I found some strange issues, here is my test case:
CREATE OR REPLACE PROCEDURE plch_proc1
IS
BEGIN
   BEGIN
       DBMS_OUTPUT.put_line (DBMS_UTILITY.format_call_stack);
   END;   
END;
/

-- Procedure created.

CREATE OR REPLACE PACKAGE plch_pkg1
IS
   PROCEDURE proc2;
END plch_pkg1;
/

-- Package created.

CREATE OR REPLACE PACKAGE BODY plch_pkg1
IS
   PROCEDURE proc2
   IS
   BEGIN
      plch_proc1;
   END;
END plch_pkg1;
/

-- Package body created.

CREATE OR REPLACE PROCEDURE plch_proc3
IS
BEGIN
   plch_pkg1.proc2;
END;
/

-- Procedure created.

-- testing
BEGIN
    plch_proc3;
END;
/

----- PL/SQL Call Stack -----
  object      line  object
  handle    number  name
000007FFD219EC48         5  procedure SCOTT.PLCH_PROC1
000007FFD1FF5810         6  package body SCOTT.PLCH_PKG1
000007FFD1F54060         4  procedure SCOTT.PLCH_PROC3
000007
For some reason, SQL*PLUS client (version 9i) did not display the output correctly; the "anonymous block" is NOT there ... though it should be.The above output looks like this after some editing, otherwise the lines were broken in the middle ...This was tested with an 11gR1 database (11.1.0.7.0) having UTF8 character set. The same SQL*PLUS client version, on a single-byte character set database was even stranger:
BEGIN
plch_proc3;
END;
/

ERROR:
ORA-06502: PL/SQL: numeric or value error: host bind array too small
ORA-06512: at line 1
Just "to make peace", I decided to put the value into a table and select afterwards to see it, so I rewrote plch_proc1 ... and now comes the interesting part:
CREATE TABLE plch_temp ( x VARCHAR2(4000) )
/

-- Table created.

--version 1 - insert the  function result directly into the table:

CREATE OR REPLACE PROCEDURE plch_proc1
IS
BEGIN
BEGIN
   INSERT INTO plch_temp VALUES (DBMS_UTILITY.format_call_stack);
END;   
END;
/

-- Procedure created.

BEGIN
plch_proc3;
END;
/

-- PL/SQL procedure successfully completed.

-- now we have "anonymous block" TWICE  !!!
SQL> SELECT * FROM plch_temp
2  /

----- PL/SQL Call Stack -----
object      line  object
handle    number  name
000007FFD1EE0C38         1  anonymous block
000007FFD219EC48         7  procedure SCOTT.PLCH_PROC1
000007FFD1FF5810         6  package body SCOTT.PLCH_PKG1
000007FFD1F54060         4  procedure SCOTT.PLCH_PROC3
000007FFD1E3EEB8         2  anonymous block

1 row selected.

ROLLBACK;
/

--version 2 - store the function result in a local variable before inserting:

CREATE OR REPLACE PROCEDURE plch_proc1
IS
x   VARCHAR2(4000);
BEGIN
BEGIN
-- DBMS_OUTPUT.put_line (DBMS_UTILITY.format_call_stack);      
-- INSERT INTO plch_temp VALUES (DBMS_UTILITY.format_call_stack);
   x := DBMS_UTILITY.format_call_stack;
   INSERT INTO plch_temp VALUES (x);
END;   
END;
/

-- Procedure created.

BEGIN
plch_proc3;
END;
/

-- PL/SQL procedure successfully completed.

-- now it is finally as expected 
SQL> SELECT * FROM plch_temp
2  /

----- PL/SQL Call Stack -----
object      line  object
handle    number  name
000007FFD219EC48        10  procedure SCOTT.PLCH_PROC1
000007FFD1FF5810         6  package body SCOTT.PLCH_PKG1
000007FFD1F54060         4  procedure SCOTT.PLCH_PROC3
000007FFD1F3F920         2  anonymous block

1 row selected.
Just as a last remark, the command line SQLPLUS.exe of Oracle client version 11g displayed the output almost correctly with the original DBMS_OUTPUT.put_line, that is, "anonymous block" was displayed once, but with the lines still broken in the middle ...

But the above tests that insert into a table look however strange ...it would be interesting to hear if anybody does have any clue for this behavior .

Looks like calling the function DIRECTLY from a SQL statement does generate an additional "anonymous block" layer in the call stack ... which can even become misleading if one seriously considers using this function not just for "PL/SQL fun" ...

I know that you are very "fond of" the DBMS_UTILITY.FORMAT_CALL_STACK function, however, I don't think that using it "as is" is such a "big gift" made to the developers ... Maybe only by hiding it into another "text analyzing" package, like the sample one supplied .

Back to Steven....it is unlikely that very many of you are still running an Oracle9i client, so that is probably not much of an issue, though interesting to discover. Also good to know that calling the call stack function from within an SQL statement, inside PL/SQL, introduces another anonymous block to the stack. It makes sense to me that this "internal" anonymous block would disappear in that last example. The function is not being called from within the SQL statement any longer.

Thanks, once again, to Iudith for her fascinating explorations. Any comments from other players?

Cheers, SF

11 January 2012

Serializable Transaction Impact Not Seen by Players (9622)

The 10 January quiz tested your knowledge of serializable transactions and system change numbers. Several players ran the verification code and got "b = 0" for the second and fourth choices (9005 and 9007), which would have made them correct (they were marked as incorrect). Here's the report from one player:

I checked your verification code for the yesterday challenge about isolation level. On my database (Ora 10.2.0.4-64) the choices 9005 and 9007 are working fine and the output is "b = 0". If you run the verification code without the choices 9004 and 9006 it runs without error. Here is my testcase:
SQL*Plus: Release 10.2.0.4.0 - Production on Wed Jan 11 16:03:28 2012
Copyright (c) 1982, 2007, Oracle.  All Rights Reserved.
Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> create table plch_test (a number, b number);
Table created.

SQL> begin
  insert into plch_test values (1, 0);
  insert into plch_test values (2, 0);
  commit;
end;
/ 

PL/SQL procedure successfully completed.

SQL> declare
  b number;
  2    3    procedure xxx
  4    is
  5      pragma autonomous_transaction;
  6    begin
  7      set transaction isolation level read committed;
  8       update plch_test
  9          set b = 1
 10        where a = 2;
 11        commit;
 12        select ORA_ROWSCN into b from plch_test
 13        where a = 1;
 14        dbms_output.put_line('ORA_ROWSCN_XXX = '||b);
 15    end;
 16  begin
 17    select ORA_ROWSCN into b from plch_test
 18    where a = 1;
 19    dbms_output.put_line('ORA_ROWSCN_start = '||b);
 20    set transaction isolation level serializable;
 21    dbms_lock.sleep(10);
 22    xxx;
 23    select ORA_ROWSCN into b from plch_test
 24    where a = 1 for update;
 25    dbms_output.put_line('ORA_ROWSCN_end = '||b);
 26  exception
 27    when others then
 28      dbms_output.put_line('Error');
 29  end;
 30  /
ORA_ROWSCN_start = 855358236
ORA_ROWSCN_XXX = 855358246
ORA_ROWSCN_end = 855358236
As you see, the SCN of the autonomous transaction is higher than the SCN from the select for update at the end. What is your explanation of this?

I have asked, _Nikotin, the author of the quiz to do some research and post his reply here.

10 January 2012

Exploring Mutating Table Errors and FORALL (9619)

The 5 January quiz tested players' knowledge of the fact that the mutating table error (ORA-04091) is raised differently for different ways of performing inserts and with a BEFORE row-level trigger.

Iudith Mentzel took the quiz as a starting point for some very interesting analysis, which I share here.

Hello Steven,

Following the quiz from January 5 about the mutating table error (ORA-04091), there was something in the explanation that arose my curiosity, so I tested it out and found something "half-strange".

Namely, it is the explanation of the correct choice [8740] that says the following:

"Oracle does not raise the mutating table error for the first row inserted. When it attempts to insert the row for the second element in the collection, the mutating table error is raised."

I performed the test below to prove that this is indeed the case and found the following:

CREATE TABLE plch_parts (
   partnum    NUMBER
 , partname   VARCHAR2 (30)
)
/

Table created.

CREATE OR REPLACE TRIGGER plch_parts_bir
   BEFORE INSERT
   ON plch_parts
   FOR EACH ROW
DECLARE
   cnt   NUMBER;
BEGIN
   -- just a control message
   DBMS_OUTPUT.put_line('BEFORE ROW trigger fired for '|| TO_CHAR(:new.partnum) );
   SELECT COUNT (*) INTO cnt FROM plch_parts;
END;
/

Trigger created.

/*
   Here we see that the mutating error happened indeed on the 2-nd row only,
   but it caused a rollback of the 1-st inserted row as well.
   This is usually NOT the case in a FORALL statement failure (for some other error),
   the results of the previous successful iterations are (generally) NOT rolled back
*/

DECLARE
   TYPE plch_parts_t IS TABLE OF plch_parts%ROWTYPE
                           INDEX BY PLS_INTEGER;
   t   plch_parts_t;
   cnt  NUMBER;
BEGIN
   t (1).partnum := 1;
   t (1).partname := 'A';
   t (2).partnum := 2;
   t (2).partname := 'B';

   FORALL i IN INDICES OF t
      INSERT INTO plch_parts
           VALUES t (i);
EXCEPTION
     WHEN OTHERS THEN
          DBMS_OUTPUT.put_line(SQLERRM);
        
          /* if the row inserted by the first iteration is not rolled back
             then here we should see "COUNT=1" */

          SELECT COUNT(*) INTO cnt FROM plch_parts;
          DBMS_OUTPUT.put_line('COUNT='||cnt);
END;
/

BEFORE ROW trigger fired for 1
BEFORE ROW trigger fired for 2

ORA-04091: table SCOTT.PLCH_PARTS is mutating, trigger/function may not see it
ORA-06512: at "SCOTT.PLCH_PARTS_BIR", line 7
ORA-04088: error during execution of trigger 'SCOTT.PLCH_PARTS_BIR'
COUNT=0  =====>  this is strange  !!!

PL/SQL procedure successfully completed.

/*
   If we add a SAVE EXCEPTIONS , then the 1-st inserted row is NOT rolled back
   which is the expected behavior.

   However, the error displayed by SQLERRM is ORA-04091 and not the usual ORA-24381,
   which shows that in this case the entire FORALL is handled like a "single multirow INSERT",
   and not like an "array of (separate) INSERTS", as FORALL usually behaves.

   In spite of this, it does preserve the 1-st row inserted,
   so it only behaves "partially" as a FORALL ... SAVE EXCEPTIONS statement.
*/

DECLARE
   TYPE plch_parts_t IS TABLE OF plch_parts%ROWTYPE
                           INDEX BY PLS_INTEGER;

   t   plch_parts_t;
   cnt  NUMBER;
BEGIN
   t (1).partnum := 1;
   t (1).partname := 'A';
   t (2).partnum := 2;
   t (2).partname := 'B';

   FORALL i IN INDICES OF t SAVE EXCEPTIONS
      INSERT INTO plch_parts
           VALUES t (i);
EXCEPTION
     WHEN OTHERS THEN
          /* here we expect ORA-24381, and not ORA-04091,
             if the later is raised for the 2-nd row */
          DBMS_OUTPUT.put_line(SQLERRM);

          /* if the row inserted by the first iteration is not rolled back
             then here we should see "COUNT=1" */

          SELECT COUNT(*) INTO cnt FROM plch_parts;

          DBMS_OUTPUT.put_line('COUNT='||cnt);
          DBMS_OUTPUT.put_line('ERRORS='||SQL%BULK_EXCEPTIONS.COUNT);

          FOR i IN 1 .. SQL%BULK_EXCEPTIONS.COUNT
          LOOP
             DBMS_OUTPUT.put_line(
'ERROR('||i||')='||SQL%BULK_EXCEPTIONS(i).ERROR_INDEX ||
                 '( '||SQL%BULK_EXCEPTIONS(i).ERROR_CODE||' )' );
          END LOOP;

END;
/

BEFORE ROW trigger fired for 1
BEFORE ROW trigger fired for 2

ORA-04091: table SCOTT.PLCH_PARTS is mutating, trigger/function may not see it
ORA-06512: at "SCOTT.PLCH_PARTS_BIR", line 7
ORA-04088: error during execution of trigger 'SCOTT.PLCH_PARTS_BIR'

COUNT=1  =====> this is expected, but strange for a non-ORA-24381 error !

ERRORS=1
ERROR(1)=2( 4091 )

PL/SQL procedure successfully completed.

I checked the above in both 11.1.0.7.0 and 11.2.0.1.0 and the behavior is the same. I wonder whether there are other cases for which we can see something similar.