De weg van vooruitgang kan soms ruw zijn. Oracle-versies 18 en 19 zijn geen uitzondering. Tot versie 18.x had Oracle geen problemen met het markeren van kolommen als ongebruikt en ze uiteindelijk te laten vallen. Gezien enkele interessante omstandigheden, kunnen de laatste twee Oracle-versies ORA-00600-fouten veroorzaken wanneer kolommen worden ingesteld als ongebruikt en vervolgens worden verwijderd. De omstandigheden die deze fout veroorzaken, zijn misschien niet gebruikelijk, maar er zijn een groot aantal Oracle-installaties over de hele wereld en het is zeer waarschijnlijk dat iemand deze bug ergens tegenkomt.
Het verhaal begint met twee tabellen en een trigger:
create table trg_tst1 (c0 varchar2(30), c1 varchar2(30), c2 varchar2(30), c3 varchar2(30), c4 varchar2(30)); create table trg_tst2 (c_log varchar2(30)); create or replace trigger trg_tst1_cpy_val after insert or update on trg_tst1 for each row begin IF :new.c3 is not null then insert into trg_tst2 values (:new.c3); end if; end; /
Gegevens worden ingevoegd in tabel TRG_TST1 en, mits aan de voorwaarden wordt voldaan, worden gegevens gerepliceerd naar tabel TRG_TST2. Twee rijen worden ingevoegd in TRG_TST1 zodat slechts één van de ingevoegde rijen wordt gekopieerd naar TRG_TST2. Na elke invoegtabel wordt TRG_TST2 opgevraagd en worden de resultaten weergegeven:
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 ------------------------------ Inserting c3 - should log SMERBLE @ gwunkus > SMERBLE @ gwunkus > insert into trg_tst1(c4) values ('Inserting c4 - should not log'); 1 row created. SMERBLE @ gwunkus > select * from trg_tst2; C_LOG ------------------------------ Inserting c3 - should log SMERBLE @ gwunkus >
Nu begint het 'plezier' - twee kolommen in TST_TRG1 zijn gemarkeerd als 'ongebruikt' en worden vervolgens verwijderd, en tabel TST_TRG2 wordt afgekapt. De inserties in TST_TRG1 worden opnieuw uitgevoerd, maar deze keer worden de gevreesde ORA-00600-fouten geproduceerd. Om te zien waarom deze fouten optreden, wordt de status van de trigger gerapporteerd door USER_OBJECTS:
SMERBLE @ gwunkus > SMERBLE @ gwunkus > -- =================================== SMERBLE @ gwunkus > -- Drop some columns in two steps then SMERBLE @ gwunkus > -- truncate trg_tst2 and repeat the test SMERBLE @ gwunkus > -- SMERBLE @ gwunkus > -- ORA-00600 errors are raised SMERBLE @ gwunkus > -- SMERBLE @ gwunkus > -- The trigger is not invalidated and SMERBLE @ gwunkus > -- thus is not recompiled. SMERBLE @ gwunkus > -- =================================== SMERBLE @ gwunkus > SMERBLE @ gwunkus > alter table trg_tst1 set unused (c1, c2); Table altered. SMERBLE @ gwunkus > alter table trg_tst1 drop unused columns; Table altered. 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 VALID SMERBLE @ gwunkus > SMERBLE @ gwunkus > truncate table trg_tst2; Table truncated. SMERBLE @ gwunkus > SMERBLE @ gwunkus > insert into trg_tst1(c3) values ('Inserting c3 - should log'); insert into trg_tst1(c3) values ('Inserting c3 - should log') * ERROR at line 1: ORA-00600: internal error code, arguments: [insChkBuffering_1], [4], [4], [], [], [], [], [], [], [], [], [] SMERBLE @ gwunkus > select * from trg_tst2; no rows selected SMERBLE @ gwunkus > SMERBLE @ gwunkus > insert into trg_tst1(c4) values ('Inserting c4 - should not log'); insert into trg_tst1(c4) values ('Inserting c4 - should not log') * ERROR at line 1: ORA-00600: internal error code, arguments: [insChkBuffering_1], [4], [4], [], [], [], [], [], [], [], [], [] SMERBLE @ gwunkus > select * from trg_tst2; no rows selected SMERBLE @ gwunkus >
Het probleem is dat in Oracle 18c en 19c de actie 'ongebruikte kolommen verwijderen' de trigger NIET ongeldig maakt, waardoor deze in een 'GELDIGE' status blijft en de volgende transacties mislukt. Aangezien de trigger niet opnieuw is gecompileerd bij de volgende aanroep, is de oorspronkelijke compilatieomgeving nog steeds van kracht, een omgeving die de nu verwijderde kolommen bevat. Oracle kan de kolommen C1 en C2 niet vinden, maar de trigger verwacht nog steeds dat ze bestaan, dus de ORA-00600-fout. My Oracle Support meldt dit als een bug:
Bug 30404639 : TRIGGER DOES NOT WORK CORRECTLY AFTER ALTER TABLE DROP UNUSED COLUMN.
en meldt dat de oorzaak in feite het niet ongeldig maken van de trigger is met de uitgestelde kolomdaling.
Dus hoe dit probleem te omzeilen? Eén manier is om de trigger expliciet te compileren nadat de ongebruikte kolommen zijn verwijderd:
SMERBLE @ gwunkus > -- SMERBLE @ gwunkus > -- Compile the trigger after column drops SMERBLE @ gwunkus > -- SMERBLE @ gwunkus > alter trigger trg_tst1_cpy_val compile; Trigger altered. SMERBLE @ gwunkus >
Nu de trigger de huidige omgeving en tabelconfiguratie gebruikt, werken de inserts correct en wordt de trigger geactiveerd zoals verwacht:
SMERBLE @ gwunkus > -- SMERBLE @ gwunkus > -- Attempt inserts again 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 ------------------------------ Inserting c3 - should log SMERBLE @ gwunkus > SMERBLE @ gwunkus > insert into trg_tst1(c4) values ('Inserting c4 - should not log'); 1 row created. SMERBLE @ gwunkus > select * from trg_tst2; C_LOG ------------------------------ Inserting c3 - should log SMERBLE @ gwunkus >
Er is een andere manier om dit probleem te omzeilen; Markeer de kolommen niet als ongebruikt en laat ze gewoon van de tafel vallen. Als u de originele tabellen laat vallen, ze opnieuw maakt en dit voorbeeld uitvoert met een rechte kolomdaling, is er geen teken van een ORA-00600, en de triggerstatus na de kolomdaling bewijst dat dergelijke fouten niet worden gegenereerd:
SMERBLE @ gwunkus > SMERBLE @ gwunkus > drop table trg_tst1 purge; Table dropped. SMERBLE @ gwunkus > drop table trg_tst2 purge; Table dropped. SMERBLE @ gwunkus > SMERBLE @ gwunkus > -- =================================== SMERBLE @ gwunkus > -- Re-run the example without marking SMERBLE @ gwunkus > -- columns as 'unused' SMERBLE @ gwunkus > -- =================================== SMERBLE @ gwunkus > SMERBLE @ gwunkus > create table trg_tst1 (c0 varchar2(30), c1 varchar2(30), c2 varchar2(30), c3 varchar2(30), c4 varchar2(30)); Table created. SMERBLE @ gwunkus > create table trg_tst2 (c_log varchar2(30)); Table created. SMERBLE @ gwunkus > SMERBLE @ gwunkus > create or replace trigger trg_tst1_cpy_val 2 after insert or update on trg_tst1 3 for each row 4 begin 5 IF :new.c3 is not null then 6 insert into trg_tst2 values (:new.c3); 7 end if; 8 end; 9 / Trigger created. 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 ------------------------------ Inserting c3 - should log SMERBLE @ gwunkus > SMERBLE @ gwunkus > insert into trg_tst1(c4) values ('Inserting c4 - should not log'); 1 row created. SMERBLE @ gwunkus > select * from trg_tst2; C_LOG ------------------------------ Inserting c3 - should log SMERBLE @ gwunkus > SMERBLE @ gwunkus > -- =================================== SMERBLE @ gwunkus > -- Drop some columns, SMERBLE @ gwunkus > -- truncate trg_tst2 and repeat the test SMERBLE @ gwunkus > -- SMERBLE @ gwunkus > -- No ORA-00600 errors are raised as SMERBLE @ gwunkus > -- the trigger is invalidated by the SMERBLE @ gwunkus > -- DDL. Oracle then recompiles the SMERBLE @ gwunkus > -- invalid trigger. SMERBLE @ gwunkus > -- =================================== SMERBLE @ gwunkus > SMERBLE @ gwunkus > alter table trg_tst1 drop (c1,c2); Table altered. 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 INVALID SMERBLE @ 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 ------------------------------ Inserting c3 - should log SMERBLE @ gwunkus > SMERBLE @ gwunkus > insert into trg_tst1(c4) values ('Inserting c4 - should not log'); 1 row created. SMERBLE @ gwunkus > select * from trg_tst2; C_LOG ------------------------------ Inserting c3 - should log SMERBLE @ gwunkus >
Oracle-versies ouder dan 18c gedragen zich zoals verwacht, waarbij de uitgestelde kolomdaling de triggerstatus correct instelt op 'INVALID':
SMARBLE @ gwankus > select banner from v$version; BANNER -------------------------------------------------------------------------------- Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production PL/SQL Release 12.1.0.2.0 - Production CORE 12.1.0.2.0 Production TNS for Linux: Version 12.1.0.2.0 - Production NLSRTL Version 12.1.0.2.0 - Production SMARBLE @ gwankus > SMARBLE @ gwankus > alter table trg_tst1 set unused (c1, c2); Table altered. SMARBLE @ gwankus > alter table trg_tst1 drop unused columns; Table altered. SMARBLE @ gwankus > SMARBLE @ gwankus > select object_name, status from user_objects where object_name in (select trigger_name from user_triggers); OBJECT_NAME STATUS ----------------------------------- ------- TRG_TST1_CPY_VAL INVALID SMARBLE @ gwankus >
Hoe de kolommen worden neergezet in versies ouder dan 18c maakt geen verschil, aangezien eventuele triggers op de betreffende tabel ongeldig worden gemaakt. De volgende aanroep van een trigger op die tabel resulteert in een 'automatische' hercompilatie, waarbij de uitvoeringsomgeving correct wordt ingesteld (wat betekent dat de ontbrekende kolommen in de betreffende tabel niet in de uitvoeringscontext blijven).
Het is niet waarschijnlijk dat een productiedatabase kolomdalingen zal ondergaan zonder eerst dergelijke wijzigingen aan te brengen in een DEV- of TST-database. Helaas is het testen van invoegingen nadat kolommen zijn verwijderd mogelijk geen test die wordt uitgevoerd nadat dergelijke wijzigingen zijn aangebracht en voordat de code wordt gepromoveerd naar PRD. Het lijkt een uitstekend idee om meer dan één persoon de nawerkingen van het laten vallen van kolommen te laten testen, aangezien, zoals het oude gezegde bevestigt:'Twee hoofden zijn beter dan één'. van een mogelijke storing kan worden gepresenteerd en uitgevoerd. De extra tijd die nodig is om een wijziging grondiger te testen, betekent een kleinere kans op onvoorziene fouten die de productie ernstig beïnvloeden of stopzetten.
# # #
Zie artikelen van David Fitzjarrell