sql >> Database teknologi >  >> RDS >> Oracle

Adressering af Drop Column Bug i Oracle 18c og 19c

Fremskridtsvejen kan til tider være barsk. Oracle version 18 og 19 er ingen undtagelse. Indtil version 18.x havde Oracle ingen problemer med at markere kolonner som ubrugte og til sidst droppe dem. I betragtning af nogle interessante omstændigheder kan de seneste to Oracle-versioner give ORA-00600-fejl, når kolonner er indstillet som ubrugte og derefter droppet. De forhold, der forårsager denne fejl, er muligvis ikke almindelige, men der er et stort antal Oracle-installationer over hele kloden, og det er meget sandsynligt, at nogen et eller andet sted vil støde på denne fejl.

Fortællingen begynder med to tabeller og en 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;
/

Data indsættes i tabel TRG_TST1, og forudsat at betingelserne er opfyldt, replikeres data til tabel TRG_TST2. To rækker indsættes i TRG_TST1, så kun én af de indsatte rækker vil blive kopieret til TRG_TST2. Efter hver insert-tabel forespørges TRG_TST2, og resultaterne vises:

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 begynder 'det sjove' - to kolonner i TST_TRG1 er markeret som 'ubrugt' og derefter droppet, og tabel TST_TRG2 afkortes. Indsætningerne i TST_TRG1 udføres igen, men denne gang produceres de frygtede ORA-00600-fejl. For at se, hvorfor disse fejl opstår, rapporteres status for triggeren fra 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 > 

Problemet er, at i Oracle 18c og 19c, "slip ubrugte kolonner" handlingen IKKE ugyldiggør triggeren, efterlader den i en "VALID" tilstand og indstiller de næste transaktioner til fejl. Da triggeren ikke blev rekompileret ved næste opkald, er det originale kompileringsmiljø stadig i kraft, et miljø der inkluderer de nu droppede kolonner. Oracle kan ikke finde kolonnerne C1 og C2, men udløseren forventer stadig, at de eksisterer, altså ORA-00600-fejlen. Min Oracle Support rapporterer dette som en fejl:

Bug 30404639 : TRIGGER DOES NOT WORK CORRECTLY AFTER ALTER TABLE DROP UNUSED COLUMN.

og rapporterer, at årsagen faktisk er, at triggeren ikke blev ugyldig med det udskudte kolonnefald.

Så hvordan kommer man uden om dette problem? En måde er eksplicit at kompilere triggeren, efter at de ubrugte kolonner er slettet:

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 > 

Når udløseren nu bruger det aktuelle miljø og tabelkonfiguration, fungerer indsætningerne korrekt, og udløseren udløses som forventet:

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 > 

Der findes en anden måde at omgå dette problem på; Markér ikke kolonnerne som ubrugte, og slip dem blot fra tabellen. Sletning af de originale tabeller, genskabelse af dem og udførelse af dette eksempel med et lige kolonnefald viser ingen tegn på en ORA-00600, og triggerstatussen efter kolonnefaldet beviser, at sådanne fejl ikke vil blive kastet:

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-versioner før 18c opfører sig som forventet, hvor det udskudte kolonnefald korrekt indstiller triggerstatus til 'UGYLDIG':

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 >

Hvordan kolonnerne slettes i versioner, der er ældre end 18c, gør ingen forskel, da eventuelle udløsere på den berørte tabel vil blive gjort ugyldige. Det næste kald til en hvilken som helst udløser på den tabel vil resultere i en "automatisk" omkompilering, der indstiller udførelsesmiljøet korrekt (hvilket betyder, at de manglende kolonner i den berørte tabel ikke forbliver i udførelseskonteksten).

Det er ikke sandsynligt, at en produktionsdatabase vil gennemgå kolonnefald uden først at foretage sådanne ændringer i en DEV- eller TST-database. Desværre er testindsættelser, efter at kolonner er slettet, muligvis ikke en test, der udføres efter sådanne ændringer er foretaget, og før koden er forfremmet til PRD. At have mere end én person til at teste eftervirkningerne af at tabe søjler ville synes at være en glimrende idé, eftersom, som det gamle ordsprog bevidner, "To hoveder er bedre end et." Jo flere, jo bedre i en testsituation, så der er mange veje af mulige fejl kan præsenteres og udføres. Den ekstra tid, det tager at teste en ændring mere grundigt, betyder en mindre sandsynlighed for, at uforudsete fejl alvorligt påvirker eller stopper produktionen.

# # #

Se artikler afDavid Fitzjarrell


  1. MySQL Forkert datetime-værdi:'0000-00-00 00:00:00'

  2. Referencer Oracle brugerdefinerede typer over DBLINK?

  3. Sådan kører du et SQL Plus-script i PowerShell

  4. Hvad er Oracle-ækvivalenten til SQL Servers IsNull()-funktion?