Bug in Mixed Compilation Modus in Oracle11.2

Vor einiger Zeit bin ich auf ein seltsames Verhalten in Oracle 11.2.03 gestoßen. Wenn der Code entweder Native oder Interpreted kompiliert ist, läuft alles super. Doch mischt man die beiden, so kommt es vor, dass die Exceptions verschluckt werden.
Da ich in Oracle auch OO Programmiere habe ich ein Beispiel mit den Object Types konstruiert um das Problem nachzustellen.
Folgende ausgangssituation:
Ich habe Zwei Object Types. Der eine erbt von dem anderen.

-- Object Type 1
CREATE OR REPLACE TYPE TYPE1 force AS OBJECT
(
id NUMBER
,constructor FUNCTION TYPE1 RETURN SELF AS RESULT
,constructor FUNCTION TYPE1(IN_ID IN NUMBER) RETURN SELF AS RESULT
) NOT final;
/
CREATE OR REPLACE
TYPE BODY TYPE1 AS

  constructor FUNCTION TYPE1 RETURN SELF AS RESULT AS
  BEGIN
    /* TODO Implementierung erforderlich */
    RETURN ;
  END TYPE1;

  constructor FUNCTION TYPE1(IN_ID IN NUMBER) RETURN SELF AS RESULT AS
  BEGIN
    /* TODO Implementierung erforderlich */
    SELF.ID := IN_ID;
    RETURN ;
  exception
    WHEN others THEN
      dbms_output.put_line('Fehler!');
  END TYPE1;

END;
/

--Object Type 2
CREATE OR REPLACE TYPE TYPE2 force UNDER TYPE1
(
name varchar2(3)
,constructor FUNCTION TYPE2 RETURN SELF AS RESULT
,constructor FUNCTION TYPE2(in_name IN varchar2) RETURN SELF AS RESULT
) NOT final
/
CREATE OR REPLACE
TYPE BODY TYPE2 AS

  constructor FUNCTION TYPE2 RETURN SELF AS RESULT AS
  BEGIN
    /* TODO Implementierung erforderlich */
    RETURN ;
  END TYPE2;

  constructor FUNCTION TYPE2(in_name IN varchar2) RETURN SELF AS RESULT AS
  BEGIN
    /* TODO Implementierung erforderlich */
   
    SELF.NAME := in_name;
   
    RETURN ;
  exception
    WHEN others THEN
      dbms_output.put_line('Fehler!');
  END TYPE2;

END;
/

Im ersten Fall, wenn der Code in einem bestimmten Typ kompiliert ist, funktioniert alles wunderbar. Wir kompilieren als Native und instanzieern dann ein Object mit einem falschem Wert.

ALTER TYPE TYPE1 compile specification PLSQL_CODE_TYPE = NATIVE;
ALTER TYPE TYPE1 compile body PLSQL_CODE_TYPE = NATIVE;
ALTER TYPE TYPE2 compile specification PLSQL_CODE_TYPE = NATIVE;
ALTER TYPE TYPE2 compile body PLSQL_CODE_TYPE = NATIVE;
SET serveroutput ON;
DECLARE
  t type2;
BEGIN

  dbms_output.put_line('Start Test1');
  t := type2(in_name=>'ABCD');
  dbms_output.put_line('End Test1');
exception
 WHEN others THEN
   dbms_output.put_line('Error Test1');
END;
/

In diesem Fall verhält sich das System wie erwartet und man bekommt folgende Ausgabe.

Start Test1
Fehler!
Error Test1

Das Programm bricht also ab und die Zeile End Test1 wird nicht ausgegeben.
Wenn der Code in gemixter Form kompiliert wird, so verhält sich dieser Komplet anderes.

alter type TYPE1 compile specification PLSQL_CODE_TYPE = INTERPRETED;
alter type TYPE1 compile body PLSQL_CODE_TYPE = INTERPRETED;
alter type TYPE2 compile specification PLSQL_CODE_TYPE = INTERPRETED;
alter type TYPE2 compile body PLSQL_CODE_TYPE = NATIVE;
set serveroutput on;
declare
  t type2;
begin

  dbms_output.put_line('Start Test2');
  t := type2(in_name=>'A');
  dbms_output.put_line('End Test2');
exception
 when others then
   dbms_output.put_line('Error Test2');
end;
/

In diesem Fall bekommt man eine erfolgreiche Ausführung des Codes:

Start Test2
End Test2

Das ganze wurde von mir auf Oracle 11.2.03 getestet und für unschön befunden.
Fazit: Wenn man auf Native umsteigt, muss man vorsichtig sein, dass auch alles in native kompiliert wird, sonst besteht die Gefahr etwas wichtiges zu übersehen.

Sommer-/Winterzeit und Oracle DBMS_SCHEDULER

Ende März hatte ich ein Projekt gehabt, bei dem auch ein Schedulerjob angelegt werden musste. Der Job sollte von Montag bis Freitag um 8 Uhr morgens starten und zum letzten mal um 18 Uhr gestartet werden. Dabei sollte der Job automatisch gestartet werden, falls die DB mal runtergefahren werden sollte
So legte ich den Job an:

BEGIN
dbms_scheduler.create_job ( job_name => 'MY_JOB'
, job_type => 'STORED_PROCEDURE'
, job_action => 'SCOTT.MY_JOB'
, start_date => SYSDATE
, repeat_interval => 'freq=SECONDLY; byday=MON,TUE,WED,THU,FRI; BYHOUR=8,9,10,11,12,13,14,15,16,17,18'
, end_date => NULL
, enabled => TRUE
, comments => 'Job to test the effect of time zone.'
);
END;
/

Soweit war alles gut, der Job startete zu der gewünschten Zeit, bis die Zeitumstellung am 31. März kam. Als ich dann den Job am 1. April überprüfte stellte ich fest, dass der Job nicht wie gewünscht um 8 Uhr gestartet hat.
Die Abfrage auf der user_scheduler_jobs View ergab folgende Information

SQL> SELECT
           job_name
          ,start_date
          ,next_run_date
    FROM user_scheduler_jobs
    WHERE job_name ='MY_JOB';
 
JOB_NAME           START_DATE                  NEXT_RUN_DATE
------------------ ----------------------------------- ------------------------------------
MY_JOB 20-MAR-12 23.30.10.154755 AM +02:00  1-APR-12 08.00.00.685030 AM +02:00

Der Job sollte um 8 Uhr morgens gestartet werden jedoch in der Zeitzone +02:00. Nach der Zeitumstellung wurde die Zeitzone nicht aktualisiert und somit wurde starte der Job eine Stunde später.
Eine kleine recherche im Internet ergab, dass die Jobs solcher Art mit einer explizieten angabe der Timezone angelegt werden sollten

BEGIN
  dbms_scheduler.drop_job('MY_JOB');
END;
/
 
PL/SQL PROCEDURE successfully completed.
 
SQL> BEGIN
        dbms_scheduler.create_job ( job_name => 'MY_JOB'
        , job_type => 'STORED_PROCEDURE'
        , job_action => 'SCOTT.MY_JOB'
        , start_date => systimestamp at TIME zone 'EUROPE/BERLIN', -- Unterschied liegt hier
        , repeat_interval => 'freq=SECONDLY; byday=MON,TUE,WED,THU,FRI; BYHOUR=8,9,10,11,12,13,14,15,16,17,18'
        , end_date => NULL
        , enabled => TRUE
        , comments => 'Job to test the effect of time zone.'
        );
    END;
    /

So wir der Scheduler Job auch mit der Zeitumstellung zurecht kommen. Und würde auch nach der weiteren Umstellung auf Winterzeit um 8 Uhr starten.

weitere Informationen zu Scheduler Jobs in Oracle:
* Job-Steuerung in Oracle Datenbanken
* Scheduler (DBMS_SCHEDULER) Enhancements in Oracle Database 11g Release 2
* Oracle 11.1 Doku- Scheduler Jobs

Unit Test für Object Types in SQLDeveloper

SqlDeveloper ist eine kostenlose IDE von Oracle für SQL und PL/SQL Entwicklung, sowie Verwaltung der Datenbank. Mitlerweile befindet es sich in der Version 3.0. Leider gibt es in der Version immer noch keine Unterstützung der Unittests für object types. Dies ist ein sehr wichtiges Feature, wer in Oracle objektorientiert programmiert und somit sehr oft Types verwendet.
Dieses Feature wurde bei Oracle angefordert und für Release 3.0 eingeplant, doch bis jetzt ist noch nichts passiert. Bitte Voted für das Feature, damit es auch realisiert wird und der Entwicklung.
Zum Feature request
wiki über SqlDeveloper
Download SqlDeveloper 3.0

PL/SQL Challenge vom 31.03.2011

Es war eine super Frage von Jeff Kemp in dem gestrigen PL/SQL Challenge. Die Frage war kurz und verständlich und zwar
was kommt raus?

DECLARE
   d1   DATE := TO_DATE ('09/2011', 'MM/YYYY');
   d2   DATE := TO_DATE ('10/2011', 'MM/YYYY');
   v1   VARCHAR2 (10);
   v2   VARCHAR2 (10);
BEGIN
   v1 := TO_CHAR (d1, 'Month');
   v2 := TO_CHAR (d2, 'Month');

   DBMS_OUTPUT.put_line (
      CASE
         WHEN v1 = 'Sep' AND v2 = 'Oct' THEN 'Short and Sweet'
         WHEN v1 = 'September' AND v2 = 'October' THEN 'Long and Sour'
         ELSE 'Not Short and Not Long'
      END);
EXCEPTION
   WHEN OTHERS
   THEN
      DBMS_OUTPUT.put_line ('Something else entirely');
END;

Auf den ersten Blick sieht der Code recht simpel aus, so dachten ich mit den Arbeitskollegen auch. Ohne das ganze auszuprobieren, wusste ich, dass TO_CHAR (d1, ‘Month’); den vollen Monatsnamen zurückgibt. So war die Variante mit ‘Short and Sweet’ ganz klar ausgeschlossen. Da wir aber eine deutsche Session benutzen, würden wir statt, October, Oktober zurückbekommen.
So habe ich mir überlegt, wenn der Autor nicht erwähnt hat, dass es explizit die englische Session sein müsste, wird ein rerank geben.
Mit Kollegen haben wir zuerst auch nur das Session Problem ausdiskutiert, und haben auf rerank gehoft.
Der Hacken war aber woanders, nämlich wird beim Format ‘month’ in der to_char Funktion, der String auf die Länge 9 gepadded im oberen Beispiel, und somit scheitert beim Vergleich von v2 = ‘October’, da v2 := ‘October ‘ und somit in dem ELSE Zweig landet.

Echt erstaunlich, was es für Kleinigkeiten in einer Sprache geben kann, die man nicht kennt, und evnt. später nicht beachtet.