Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Behebung des Drop-Column-Bugs in Oracle 18c und 19c

Der Weg des Fortschritts kann manchmal holprig sein. Die Oracle-Versionen 18 und 19 sind da keine Ausnahme. Bis Version 18.x hatte Oracle keine Probleme damit, Spalten als unbenutzt zu markieren und schließlich zu löschen. Angesichts einiger interessanter Umstände können die letzten beiden Oracle-Versionen ORA-00600-Fehler auslösen, wenn Spalten als unbenutzt festgelegt und dann gelöscht werden. Die Bedingungen, die zu diesem Fehler führen, sind möglicherweise nicht üblich, aber es gibt eine große Anzahl von Oracle-Installationen auf der ganzen Welt, und es ist sehr wahrscheinlich, dass jemand irgendwo auf diesen Fehler stößt.

Die Geschichte beginnt mit zwei Tabellen und einem Trigger:

erstelle Tabelle trg_tst1 (c0 varchar2(30), c1 varchar2(30), c2 varchar2(30), c3 varchar2(30), c4 varchar2(30));erstelle Tabelle trg_tst2 (c_log varchar2(30));erstelle oder ersetzen Sie den Trigger trg_tst1_cpy_valafter insert oder update auf trg_tst1for each rowbegin IF :new.c3 is not null then insert into trg_tst2 values ​​(:new.c3); end if;end;/

Die Daten werden in die Tabelle TRG_TST1 eingefügt und, sofern die Bedingungen erfüllt sind, die Daten in die Tabelle TRG_TST2 repliziert. Zwei Zeilen werden in TRG_TST1 eingefügt, sodass nur eine der eingefügten Zeilen in TRG_TST2 kopiert wird. Nach jedem Einfügen wird die Tabelle TRG_TST2 abgefragt und die Ergebnisse angezeigt:

SMERBLE @ gwunkus> SMERBLE @ gwunkus> insert into trg_tst1(c3) values ​​('Inserting c3 - should log');1 Zeile erstellt.SMERBLE @ gwunkus> select * from trg_tst2;C_LOG-------- ----------------------Einfügen von c3 - sollte logSMERBLE @ gwunkus> SMERBLE @ gwunkus> insert into trg_tst1(c4) values ​​('Einfügen von c4 - sollte nicht loggen' );1 Zeile erstellt.SMERBLE @ gwunkus> select * from trg_tst2;C_LOG------------------------------Einfügen von c3 - sollte logSMERBLE @ gwunkus> 

Jetzt beginnt der „Spaß“ – zwei Spalten in TST_TRG1 werden als „nicht verwendet“ markiert und dann gelöscht, und die Tabelle TST_TRG2 wird abgeschnitten. Die Einfügungen in TST_TRG1 werden erneut ausgeführt, aber diesmal werden die gefürchteten ORA-00600-Fehler erzeugt. Um zu sehen, warum diese Fehler auftreten, wird der Status des Triggers von USER_OBJECTS:

gemeldet
SMERBLE @ gwunkus> SMERBLE @ gwunkus> -- ==================================SMERBLE @ gwunkus> -- Einige Spalten in zwei Schritten löschen thenSMERBLE @ gwunkus> -- Trg_tst2 abschneiden und den Test wiederholenSMERBLE @ gwunkus> --SMERBLE @ gwunkus> -- ORA-00600-Fehler werden ausgelöstSMERBLE @ gwunkus> --SMERBLE @ gwunkus> -- Der Trigger wird nicht ungültig gemacht und SMERBLE @ gwunkus> -- wird also nicht neu kompiliert. SMERBLE @ gwunkus> -- ==================================SMERBLE @ gwunkus> SMERBLE @ gwunkus> Tabelle ändern trg_tst1 unbenutzt setzen (c1, c2);Tabelle geändert.SMERBLE @ gwunkus> Tabelle ändern trg_tst1 unbenutzte Spalten löschen;Tabelle geändert.SMERBLE @ gwunkus> SMERBLE @ gwunkus> select object_name, status from user_objects where object_name in (select trigger_name from user_triggers);OBJECT_NAME STATUS------------------------------- ---- -------TRG_TST1_CPY_VAL VALIDSMERBLE @ gwunkus> SMERBLE @ gwunkus> Tabelle abschneiden trg_tst2;Tabelle abgeschnitten.SMERBLE @ gwun kus> SMERBLE @ gwunkus> in trg_tst1(c3)-Werte einfügen ('Einfügen von c3 - sollte protokollieren'); in trg_tst1(c3)-Werte einfügen ('Einfügen von c3 - sollte protokollieren') *FEHLER in Zeile 1:ORA-00600:intern Fehlercode, Argumente:[insChkBuffering_1], [4], [4], [], [], [], [], [], [], [], [], []SMERBLE @ gwunkus> select * from trg_tst2;keine Zeilen ausgewähltSMERBLE @ gwunkus> SMERBLE @ gwunkus> in trg_tst1(c4)-Werte einfügen ('Einfügen von c4 - sollte nicht protokollieren');in trg_tst1(c4)-Werte einfügen ('Einfügen von c4 - sollte nicht protokollieren') *ERROR at Zeile 1:ORA-00600:interner Fehlercode, Argumente:[insChkBuffering_1], [4], [4], [], [], [], [], [], [], [], [], [ ]SMERBLE @ gwunkus> select * from trg_tst2;no rows selectedSMERBLE @ gwunkus> 

Das Problem ist, dass in Oracle 18c und 19c die Aktion „unbenutzte Spalten löschen“ den Trigger NICHT ungültig macht, ihn in einem „GÜLTIGEN“ Zustand belässt und die nächsten Transaktionen für einen Fehler einrichtet. Da der Trigger beim nächsten Aufruf nicht neu kompiliert wurde, ist die ursprüngliche Kompilierungsumgebung immer noch wirksam, eine Umgebung, die die jetzt gelöschten Spalten enthält. Oracle kann die Spalten C1 und C2 nicht finden, aber der Trigger erwartet immer noch, dass sie vorhanden sind, daher der Fehler ORA-00600. Mein Oracle-Support meldet dies als Fehler:

Bug 30404639:TRIGGER FUNKTIONIERT NICHT KORREKT NACH ALTER TABLE DROP UNUSED COLUMN.

und berichtet, dass die Ursache tatsächlich darin besteht, dass der Trigger mit dem verzögerten Spaltenlöschen nicht ungültig gemacht wurde.

Wie kann man also dieses Problem umgehen? Eine Möglichkeit besteht darin, den Trigger explizit zu kompilieren, nachdem die nicht verwendeten Spalten gelöscht wurden:

SMERBLE @ gwunkus> --SMERBLE @ gwunkus> -- Kompiliert den Trigger, nachdem die Spalte gelöscht wurdeSMERBLE @ gwunkus> --SMERBLE @ gwunkus> alter trigger trg_tst1_cpy_val compile;Trigger verändert 

Wenn der Trigger jetzt die aktuelle Umgebung und Tabellenkonfiguration verwendet, funktionieren die Einfügungen korrekt und der Trigger wird wie erwartet ausgelöst:

SMERBLE @ gwunkus> --SMERBLE @ gwunkus> -- Wiedereinfügungen versuchenSMERBLE @ gwunkus> --SMERBLE @ gwunkus> insert into trg_tst1(c3) values ​​('Inserting c3 - should log');1 Zeile erstellt.SMERBLE @ gwunkus> select * from trg_tst2;C_LOG------------------------------Einfügen von c3 - sollte logSMERBLE @ gwunkus> SMERBLE @ gwunkus> insert into trg_tst1(c4) values ​​('Inserting c4 - should not log');1 Zeile erstellt.SMERBLE @ gwunkus> select * from trg_tst2;C_LOG------------------ ------------Einfügen von c3 - sollte logSMERBLE @ gwunkus> 

Es gibt eine andere Möglichkeit, dieses Problem zu umgehen; Markieren Sie die Spalten nicht als unbenutzt und löschen Sie sie einfach aus der Tabelle. Das Löschen der ursprünglichen Tabellen, das erneute Erstellen und das Ausführen dieses Beispiels mit einem direkten Spaltenabbruch zeigt keine Anzeichen von ORA-00600, und der Triggerstatus nach dem Spaltenabbruch beweist, dass keine solchen Fehler ausgegeben werden:

SMERBLE @ gwunkus> SMERBLE @ gwunkus> drop table trg_tst1 purge;Tabelle gelöscht.SMERBLE @ gwunkus> drop table trg_tst2 purge;Tabelle gelöscht.SMERBLE @ gwunkus> SMERBLE @ gwunkus> -- ==================================SMERBLE @ gwunkus> -- Führen Sie das Beispiel erneut aus, ohne SMERBLE @ gwunkus> -- Spalten als 'unused'SMERBLE zu markieren @ gwunkus> -- ==================================SMERBLE @ gwunkus> SMERBLE @ gwunkus> Tabelle erstellen trg_tst1 (c0 varchar2(30), c1 varchar2(30), c2 varchar2(30), c3 varchar2(30), c4 varchar2(30));Tabelle erstellt.SMERBLE @ gwunkus> Tabelle erstellen trg_tst2 (c_log varchar2(30));Tabelle erstellt.SMERBLE @ gwunkus> SMERBLE @ gwunkus> Trigger trg_tst1_cpy_val erstellen oder ersetzen 2 nach dem Einfügen oder Aktualisieren auf trg_tst1 3 für jede Zeile 4 beginnen 5 WENN :new.c3 nicht null ist, dann 6 in trg_tst2 Werte einfügen (:new.c3 ); 7 Ende wenn; 8 Ende; 9 /Trigger erstellt.SMERBLE @ gwunkus> SMERBLE @ gwunkus> Werte in trg_tst1(c3) einfügen ('Inserting c3 - should log');1 Zeile erstellt.SMERBLE @ gwunkus> select * from trg_tst2;C_LOG------ ------------------------Einfügen von c3 - sollte logSMERBLE @ gwunkus> SMERBLE @ gwunkus> insert into trg_tst1(c4) values ​​("Einfügen von c4 - sollte nicht log');1 Zeile erstellt.SMERBLE @ gwunkus> select * from trg_tst2;C_LOG------------------------------Einfügen von c3 - sollte logSMERBLE @ gwunkus> SMERBLE @ gwunkus> -- =================================SMERBLE @ gwunkus> -- Einige Spalten löschen,SMERBLE @ gwunkus> -- trg_tst2 abschneiden und den Test wiederholenSMERBLE @ gwunkus> --SMERBLE @ gwunkus> -- Es werden keine ORA-00600-Fehler als SMERBLE @ gwunkus ausgegeben> -- der Trigger wird durch theSMERBLE ungültig gemacht @ gwunkus> -- DDL. Oracle kompiliert dann theSMERBLE @ gwunkus> -- invalid trigger.SMERBLE @ gwunkus> -- ==================================SMERBLE @ gwunkus> SMERBLE @ gwunkus> alter table trg_tst1 drop (c1,c2);Tabelle geändert.SMERBLE @ gwunkus> SMERBLE @ gwunkus> select object_name, status from user_objects where object_name in (select trigger_name from user_triggers);OBJECT_NAME STATUS ----------------------------------- -------TRG_TST1_CPY_VAL INVALIDSMERBLE @ gwunkus> SMERBLE @ gwunkus> truncate table trg_tst2;Table truncated.SMERBLE @ gwunkus> SMERBLE @ gwunkus> insert into trg_tst1(c3) values ​​('Inserting c3 - should log');1 row created.SMERBLE @ gwunkus> select * from trg_tst2;C_LOG--- ---------------------------Einfügen von c3 - sollte logSMERBLE @ gwunkus> SMERBLE @ gwunkus> in trg_tst1(c4) Werte einfügen ("Einfügen von c4 - sollte nicht protokollieren');1 Zeile erstellt.SMERBLE @ gwunkus> select * from trg_tst2;C_LOG------------------------ - c3 einfügen - sollte logSMERBLE @ gwunkus> 

Oracle-Versionen vor 18c verhalten sich wie erwartet, wobei das verzögerte Spaltenlöschen den Triggerstatus korrekt auf „INVALID“ setzt:

SMARBLE @ gwankus> Banner aus v$version auswählen;BANNER------------------------------ ---------------------------------------------Oracle Database 12c Enterprise Edition Version 12.1.0.2.0 – 64-Bit ProductionPL/SQL Version 12.1.0.2.0 – ProductionCORE 12.1.0.2.0 ProductionTNS für Linux:Version 12.1.0.2.0 – ProductionNLSRTL Version 12.1.0.2.0 – ProductionSMARBLE @ gwankus>SMARBLE @ gwankus> Tabelle ändern trg_tst1 unbenutzt setzen (c1, c2);Tabelle geändert.SMARBLE @ gwankus> Tabelle ändern trg_tst1 unbenutzte Spalten löschen;Tabelle geändert.SMARBLE @ gwankus>SMARBLE @ gwankus> Objektname auswählen, Status von Benutzerobjekten, wobei Objektname in (Auslösername auswählen aus user_triggers);OBJECT_NAME STATUS----------------------------------- -------TRG_TST1_CPY_VAL INVALIDSMARBLE @ gwankus>

Wie die Spalten in älteren Versionen als 18c gelöscht werden, spielt keine Rolle, da alle Trigger für die betroffene Tabelle ungültig werden. Der nächste Aufruf eines beliebigen Triggers für diese Tabelle führt zu einer „automatischen“ Neukompilierung, wodurch die Ausführungsumgebung richtig eingestellt wird (was bedeutet, dass die fehlenden Spalten in der betroffenen Tabelle nicht im Ausführungskontext verbleiben).

Es ist unwahrscheinlich, dass in einer Produktionsdatenbank Spalten gelöscht werden, ohne dass zuvor solche Änderungen in einer DEV- oder TST-Datenbank vorgenommen werden. Leider ist das Testen von Einfügungen nach dem Löschen von Spalten möglicherweise kein Test, der ausgeführt wird, nachdem solche Änderungen vorgenommen wurden und bevor der Code zu PRD hochgestuft wird. Es scheint eine ausgezeichnete Idee zu sein, mehr als eine Person zu haben, die die Nachwirkungen fallender Säulen testet, da, wie das alte Sprichwort bestätigt, „zwei Köpfe besser sind als einer.“ Je mehr, desto besser in einer Testsituation, so dass viele Möglichkeiten bestehen eines möglichen Ausfalls dargestellt und ausgeführt werden. Die zusätzliche Zeit, die benötigt wird, um eine Änderung gründlicher zu testen, bedeutet eine geringere Wahrscheinlichkeit, dass unvorhergesehene Fehler die Produktion ernsthaft beeinträchtigen oder stoppen.

# # #

Siehe Artikel von David Fitzjarrell