sql >> Database >  >> RDS >> Oracle

De Drop Column-bug in Oracle 18c en 19c aanpakken

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


  1. Correct volgehouden berekende kolommen

  2. Salesforce-gegevens bijwerken met een SQL Server-cursor

  3. RONDE(datum) Functie in Oracle

  4. MySQL #1140 - Mengen van GROEP-kolommen