21. Juni 2007

Bedingtes Kompilieren mit PL/SQL

Noch nicht so bekannt ist, dass PL/SQL inwzischen einen Präprozessor hat. Man kann also mit PL/SQL, ähnlich wie mit C oder C++ auch bedingtes Kompilieren durchführen. Wozu das gut ist? Nun, man kann bspw. Code für verschiedene Editionen oder Datenbankversionen erstellen. Ein Beispiel könnten Debugging-Informationen sein:
set serveroutput on 

alter session set  PLSQL_CCFLAGS = 'DEBUG_TABLE:false'
/

begin
  for i in 1..1000 loop
    $if $$DEBUG_TABLE $then
      insert into debug_table (datum, message) values (sysdate, 'Durchlauf ' ||i);
    $else
      dbms_output.put_line(sysdate||': Durchlauf '||i);
    $end
  end loop;
end;
/

01.07.07: Durchlauf 1
01.07.07: Durchlauf 2
01.07.07: Durchlauf 3
01.07.07: Durchlauf 4
01.07.07: Durchlauf 5
:
Lassen Sie diesen Code laufen, ohne die Tabelle DEBUG_TABLE anzulegen. Normalerweise würde man einen Fehler erwarten ... aber der Code läuft durch und die Ausgaben erscheinen auf dem Bildschirm. Ändert man allerdings den ALTER SESSION-Befehl zu Beginn, dann sieht das Ergebnis anders aus:
set serveroutput on 

alter session set  PLSQL_CCFLAGS = 'DEBUG_TABLE:true'
/

begin
  for i in 1..1000 loop
    $if $$DEBUG_TABLE $then
      insert into debug_table (datum, message) values (sysdate, 'Durchlauf ' ||i);
    $else
      dbms_output.put_line(sysdate||': Durchlauf '||i);
    $end
  end loop;
end;
/

FEHLER in Zeile 4:
ORA-06550: Zeile 4, Spalte 19:
PL/SQL: ORA-00942: Tabelle oder View nicht vorhanden
ORA-06550: Zeile 4, Spalte 7:
Grund für den Erfolg im ersten Fall ist, dass, dass die INSERT-Anweisung erst gar nicht kompiliert wird - der Präprozessor filtert sie vorher aus. Dies kann man auch selbst überprüfen - dazu gibt es das Paket DBMS_PREPROCESSOR:
alter session set  PLSQL_CCFLAGS = 'DEBUG_TABLE:false'
/

begin
  dbms_preprocessor.print_post_processed_source(q'#
   begin
    for i in 1..1000 loop
     $if $$DEBUG $then
      insert into debug_table (datum, message) values (sysdate, 'Durchlauf ' ||i);
     $else
      dbms_output.put_line(sysdate||': Durchlauf '||i);
     $end
    end loop;
   end;#'
  );
end;
/

begin
 for i in 1..1000 loop
  dbms_output.put_line(sysdate||': Durchlauf '||i);
 end loop;
end;
Sie sehen so den tatsächlich kompilierten Code. Wenn ein Zweig also durch Präprozessor-Anweisungen ausgefiltert wurde, kann dieser durchaus ungültigen Code enthalten. Somit wird es möglich, ein- und dieselbe PL/SQL-Prozedur auf verschiedenen Umgebungen, sogar Datenbankversionen laufen zu lassen ... Ein Beispiel:
$IF DBMS_DB_VERSION.VER_LE_10_1 $THEN
   /* Code für Versionen kleiner oder gleich 10.1
$ELSIF DBMS_DB_VERSION.VER_LE_10_2 $THEN
   /* Code für Versionen kleiner oder gleich 10.2
$ELSE
   /* In Zukunft dann: Code für 11 und später
$END

Kommentare:

Anonym hat gesagt…

Eine kurze Frage:

Wie soll dein Beispiel funktionieren, wenn es diese Funktionalität erst ab 10.2 gibt?

$IF DBMS_DB_VERSION.VER_LE_10_1 $THEN
/* Code für Versionen kleiner oder gleich 10.1

Oder liege ich da falsch?

Carsten Czarski hat gesagt…

In der Tat wurde die Conditional Compilation erst mit 10.2 eingeführt. Über die Patchsets fand jedoch ein Backport auf die älteren Versionen statt - Nähere Informationen enthält dieses Paper hier:
http://www.oracle.com/technology/tech/pl_sql/pdf/plsql_conditional_compilation.pdf

Anonym hat gesagt…

Danke erstmal für diesen sehr gelungen Blog!

Zu dem Thema würde mich interessieren, ob Oracle Statistiken darüber führt, wie viele Installationen auf dem neusten Patchset fahren?

Aus meiner Erfahrungen sind das so einige...

Carsten Czarski hat gesagt…

Leider gibt es keine verlässlichen, öffentlichen Statistiken. Die aus dem Support-System gewonnenen Informationen sind leider zu unscharf, denn sie hängen zu sehr davon ab, was der jeweilige Kunde angegeben hat - in vielen Fällen steht dort nur bspw. 10.2. und eben keine genaue Patchset-Angabe.

Beliebte Postings