...just the database guy
 
Startseite   Info   RSS

Ausführungsrechte mit AUTHID bestimmen

Prozeduren, Funktionen und Packages erhalten in Oracle standardmäßig die Berechtigungen des Users, mit dem sie erstellt wurden. Oracle nennt das eine Definers Right-Prozedur (bzw. Funktion oder Package). Es ist jedoch auch möglich Prozeduren, Funktionen oder Packages mit den so genannten Invokers Rights auszuführen. Eine entsprechend definierte Prozedur erhält beim Ausführen nur die Berechtigungen die auch der aufrufende User besitzt.

Umgesetzt wird dieses Verhalten mit der AUTHID-Klausel die grundsätzlich vor dem IS bzw. AS Schlüsselwort angegeben werden muss. Innerhalb von Packages muss AUTHID im Package-Header definiert sein und gilt dann immer für das komplette Package. Eine Definition auf Prozedur- bzw. Funktionsebene ist innerhalb von PL/SQL-Packages nicht möglich. In Code sieht das dann folgendermaßen aus:

CREATE OR REPLACE PROCEDURE testproc
AUTHID DEFINER
IS
BEGIN
   ...
END;

AUTHID DEFINER bedeutet hierbei, dass die Prozedur mit den Rechten des Erstellers ausgeführt wird. AUTHID DEFINER ist der Standardwert und könnte daher auch weggelassen werden. Die Alternative ist AUTHID CURRENT_USER. Hierbei wird die Prozedur immer mit den Rechten des Aufrufenden ausgeführt.

CREATE OR REPLACE PROCEDURE testproc2
AUTHID CURRENT_USER
IS
BEGIN
   ...
END;

Error Logging

Das Verarbeiten größerer Datenmengen in einem einzelnen DML-Statement birgt einen kapitalen Stolperstein: Ein einziger, fehlerhafter Datensatz führt zum Abbruch der gesamten Transaktion und dem damit verbundenen Zurückrollen aller bereits erfolgreich verarbeiteten Datensätze. In vielen Fällen ist es jedoch vollkommen akzeptabel eine kleinere Menge an fehlerhaften Datensätzen abzuweisen und den fehlerfreien Anteil trotzdem zu behalten.

Häufig wird dieses Problem entweder durch Einzelsatzverarbeitung oder durch Verwendung von BULK-Operationen und SAVE EXCEPTIONS gelöst. Ersteres ist aus Performance-Sicht in der Regel nicht zu empfehlen, zweiteres ist zwar sehr schnell aber unter Umständen auch mit erheblich komplexeren Code verbunden.

Oracle bietet jedoch auch eine dritte Lösung die ggf. den idealen Mittelweg bietet. Für alle DML-Befehle kann ein automatisches Fehlerlogging, inklusive der Möglichkeit fehlerhafte Datensätze innerhalb einer Transaktion zu überspringen, aktiviert werden. Die Grundlage dafür stellt das Package dbms_errlog dar.

Zunächst wird für die Zieltabelle der DML-Operation eine passende Fehlertabelle erstellt. Dies kann mit folgendem Beispielcode realisiert werden.

BEGIN
   dbms_errlog.create_error_log(
      dml_table_name => '<ZIELTABELLE>',
      err_log_table_name => '<FEHLERLOGTABELLE>',
      skip_unsupported => true
   );
END;

Oracle erstellt damit eine Loggingtabelle mit frei wählbarem Namen. Diese enthält alle Attribute der eigentliche Zieltabelle (hier jedoch immer mit dem Datentypen VARCHAR2(4000)) sowie zusätzliche Spalten u.a. für Fehlercode, Fehlermeldung und ROWID. Um diese nun auch zu verwenden, muss die jeweilige DML-Anweisung geringfügig erweitert werden.

INSERT INTO <ZIELTABELLE>
   SELECT *
     FROM <QUELLTABELLE>
log errors into <FEHLERLOGTABELLE> ('<TAG>')
   reject limit <ANZAHL>

Die hinter reject limit angegebene Anzahl bestimmt nun, wie viele Fehler bei der Verarbeitung der DML-Operation geduldet werden. Ist die Anzahl der tatsächlich auftretenden Fehler geringer als das angegebene Limit, werden für abgewiesene Datensätze Log-Einträge erzeugt. Fehlerfreie Datensätze werden verarbeitet. Erst wenn die Anzahl der Fehler das Limit übersteigt wird die gesamte Transaktion zurückgerollt. Der Inhalt der Fehlertabelle bleibt dabei natürlich erhalten, sie wird in einer autonomen Transaktion von der Oracle-Datenbank automatisch gepflegt.

Bleibt noch die Bedeutung das Parameters TAG zu klären: Hierbei handelt es sich quasi um ein Freitextfeld, welches 1:1 in das Feld ORA_ERR_TAG$ der Fehlerlogtabelle übernommen wird. Es kann genutzt werden um Fehlermeldungen einer DML-Operation zuordnen zu können, bei täglichen laufenden Importen könnte man hier z.B. einen Timestamp und den Namen des Imports mitgeben.

Mit Error Logging lassen sich also ohne großen Aufwand Fehler in DML-Operationen protokollieren und gleichzeitig fehlerhafte Datensätze komfortabel überspringen. Ein Punkt sollte jedoch beachtet werden: Die beschriebene Methode unterstützt keine LOB-Datentypen.

Deferred constraints

Oracle überprüft die Einhaltung von Constraints bereits während des Absetzens von DML-Anweisungen. Es gibt aber auch immer wieder Fälle, in denen es einfach nicht möglich ist alle Bedingungen schon beim Einfügen oder Löschen von Daten einzuhalten. Zum Beispiel immer dann, wenn sich zwei Tabellen gegenseitig referenzieren. Die übliche Lösung liegt dann im Deaktivieren/Aktivieren der betreffenden Constraints, was jedoch besonders bei Online-Systemen aufgrund der entstehenden Locks nicht zu empfehlen ist.

Die Datenbank selbst bietet von Haus aus eine Lösung für dieses Problem: Deferred Constraints. Damit wird es möglich, dass die Prüfung der Constraints nicht bereits beim Ausführen des DML sonders erst beim COMMIT erfolgt. Innerhalb der Transaktion können dann z.B. problemlos INSERTs abgesetzt werden, auch wenn innerhalb der Zieltabelle Fremdschlüsselbeziehungen existieren die zu diesem Zeitpunkt nicht korrekt gepflegt sind. Erst wenn die Transaktion mit einem COMMIT abgeschlossen wird, müssen alle Bedingungen korrekt erfüllt sein.

Deferred constraints werden mittels folgendem DDL angelegt:

ALTER TABLE <table>
   ADD CONSTRAINT <constraint>
      FOREIGN KEY (<foreignkey>)
   REFERENCES <reference>
   DEFERRABLE INITIALLY DEFERRED

Der angelegte Constraint wird damit als DEFERRABLE markiert. Durch die Verwendung von INITIALLY DEFERRED wird dies auch zu seinem Standardverhalten. Die INITIALLY-Anweisung ist optional, fehlt sie (oder wird INITIALLY IMMEDIATE verwendet) erfolgt die Prüfung von Constraints direkt beim Ausführen von DML. Das Setzen eines Contraints als DEFERRABLE allein führt also noch zu keinem geänderten Verhalten. Erst durch die Verwendung von INITIALLY DEFERRED wird die verzögerte Prüfung des Constraints am Transaktionende aktiviert.

Alternativ kann das Verhalten von DEFERRABLE Constraints auch zur Laufzeit beeinflusst werden.

SET CONSTRAINT
   [[NAME]]|ALL] [DEFERRED|IMMEDIATE]

Mit dieser SET-Anweisung kann das Verhalten eines als DEFERRABLE angelegten Constraints für die aktuelle Session umgeschaltet werden. Wird an Stelle eines konkreten Contraintnamens das Schlüsselwort ALL angegeben, wirkt sich die Änderung entsprechend auf alle vorhandenen Constraints aus. Voraussetzung bleibt natürlich, dass diese auch als DEFERRABLE angelegt sind.

Oracle XE 11g R2 unter Windows 7 Pro x64

Die Oracle Express Edition in der Version 11 ist bisher nur in einer Windows-Version für 32 Bit verfügbar. Prinzipiell läuft sie vollkommen problemlos unter Window 7 x64, allerdings ist der Installer etwas unkooperativ und beschwert sich über eine fehlende KEY_XE.reg. Die genaue Fehlermeldung lautet “The Installer is unable to initiate the file KEY_XE.reg. The file does not appear to exist.”.

Die Lösung ist relativ einfach: Zunächst die Installation ganz normal mittels Doppelklick auf die setup.exe starten und warten bis die Vorbereitungen des Installers abgeschlossen sind. Bevor nun die Installation fortgesetzt wird, folgendes Verzeichnis öffnen ”C:\Users\<USER>\AppData\Local\Temp”. Hier hat das Installationsprogramm ein temporäres Verzeichnis mit einem dynamisch generierten Namen erstellt. Der Name setzt sich dabei aus einer hexadezimalen Zahlenkolonne eingeschlossen in geschweiften Klammern zusammen und kann auf jedem Rechner anders lauten. Ist eine Datei “OracleMTSRecoveryService.reg” enthalten, ist der richtige Ordner gefunden. Nun einfach die “OracleMTSRecoveryService.reg” kopieren und in “KEY_XE.reg” umbenennen, dann kann die Installation fortgesetzt werden und sollte fehlerfrei durchlaufen.

Einen kleinen Fehler gibt es dann noch in der erzeugten “Get Started”-Verknüpfung zum Zugriff auf die Administrationsoberfläche. Diese läuft standardmäßig auf Port 8080, die erzeugte Verknüpfung enthält jedoch die nicht ersetzte Variable %HTTPPORT%. Diese muss nur noch durch die korrekte Portnummer ersetzt werden und sollte dann wie folgt lauten: http://127.0.0.1:8080/apex/f?p=4950.