Oracle
 sql >> Baza danych >  >> RDS >> Oracle

Rozwiązanie problemu Drop Column Bug w Oracle 18c i 19c

Droga postępu może być czasami trudna. Wersje Oracle 18 i 19 nie są wyjątkiem. Aż do wersji 18.x firma Oracle nie miała problemów z oznaczaniem kolumn jako nieużywanych i ich usuwaniem. Biorąc pod uwagę kilka interesujących okoliczności, ostatnie dwie wersje Oracle mogą generować błędy ORA-00600, gdy kolumny są ustawione jako nieużywane, a następnie usuwane. Warunki, które powodują ten błąd, mogą nie być powszechne, ale na całym świecie istnieje duża liczba instalacji Oracle i jest bardzo prawdopodobne, że ktoś gdzieś napotka ten błąd.

Opowieść zaczyna się od dwóch tabel i wyzwalacza:

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;
/

Dane są wstawiane do tabeli TRG_TST1 i, jeśli warunki są spełnione, dane są replikowane do tabeli TRG_TST2. Dwa wiersze są wstawiane do TRG_TST1, tak że tylko jeden z wstawionych wierszy zostanie skopiowany do TRG_TST2. Po każdym wstawieniu tabeli TRG_TST2 jest odpytywany i wyświetlane są wyniki:

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 > 

Teraz zaczyna się „zabawa” — dwie kolumny w TST_TRG1 są oznaczone jako „nieużywane”, a następnie usuwane, a tabela TST_TRG2 jest obcinana. Wstawki do TST_TRG1 są ponownie wykonywane, ale tym razem pojawiają się przerażające błędy ORA-00600. Aby zobaczyć, dlaczego występują te błędy, stan wyzwalacza jest zgłaszany przez 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 > 

Problem polega na tym, że w Oracle 18c i 19c akcja „upuść nieużywane kolumny” NIE unieważnia wyzwalacza, pozostawiając go w stanie „WAŻNY” i ustawiając kolejne transakcje na niepowodzenie. Ponieważ wyzwalacz nie został ponownie skompilowany przy następnym wywołaniu, oryginalne środowisko kompilacji nadal działa, środowisko zawierające teraz porzucone kolumny. Oracle nie może znaleźć kolumn C1 i C2, ale wyzwalacz nadal oczekuje ich istnienia, stąd błąd ORA-00600. Moja pomoc techniczna Oracle zgłasza to jako błąd:

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

i zgłasza, że ​​przyczyną jest w rzeczywistości brak unieważnienia wyzwalacza z odroczonym spadkiem kolumny.

Jak więc obejść ten problem? Jednym ze sposobów jest jawna kompilacja wyzwalacza po usunięciu nieużywanych kolumn:

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 > 

Gdy wyzwalacz korzysta teraz z bieżącego środowiska i konfiguracji tabeli, wstawki działają poprawnie, a wyzwalacz uruchamia się zgodnie z oczekiwaniami:

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 > 

Istnieje inny sposób na obejście tego problemu; Nie oznaczaj kolumn jako nieużywanych i po prostu usuń je z tabeli. Usunięcie oryginalnych tabel, odtworzenie ich i wykonanie tego przykładu z prostym upuszczeniem kolumny nie wykazuje oznak ORA-00600, a stan wyzwalacza po upuszczeniu kolumny dowodzi, że takie błędy nie zostaną zgłoszone:

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 > 

Wersje Oracle przed 18c zachowują się zgodnie z oczekiwaniami, a odroczone upuszczenie kolumny prawidłowo ustawia stan wyzwalacza na „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 >

Sposób, w jaki kolumny są usuwane w wersjach starszych niż 18c, nie ma znaczenia, ponieważ wszelkie wyzwalacze w tabeli, której dotyczy problem, zostaną unieważnione. Następne wywołanie dowolnego wyzwalacza w tej tabeli spowoduje „automatyczną” ponowną kompilację, prawidłowo ustawiając środowisko wykonywania (co oznacza, że ​​brakujące kolumny w tabeli, której dotyczy problem, nie pozostaną w kontekście wykonywania).

Jest mało prawdopodobne, że produkcyjna baza danych zostanie poddana upuszczaniu kolumn bez uprzedniego wprowadzenia takich zmian w bazie danych DEV lub TST. Niestety testowanie wstawek po usunięciu kolumn może nie być testem wykonywanym po wprowadzeniu takich zmian i przed promocją kodu do PRD. Posiadanie więcej niż jednej osoby testującej następstwa upuszczania kolumn wydaje się być doskonałym pomysłem, ponieważ, jak potwierdza stare powiedzenie, „co dwie głowy to nie jedna.” Im więcej, tym weselej w sytuacji testowej, tak wiele możliwości możliwej awarii można przedstawić i wykonać. Dodatkowy czas poświęcony na dokładniejsze przetestowanie zmiany oznacza mniejsze prawdopodobieństwo wystąpienia nieprzewidzianych błędów, które poważnie wpłyną lub zatrzymają produkcję.

# # #

Zobacz artykuły Davida Fitzjarrella


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak wyeksportować wyniki zapytania do pliku CSV w SQLcl (Oracle)

  2. POWRÓT Oracle do użycia w Javie (JDBC, Prepared Statement)

  3. Zapytania sparametryzowane w C# dla Oracle — poważny i niebezpieczny błąd!

  4. Zwróć wyniki zapytania sql jako JSON w oracle 12c

  5. Jak zwrócić tablicę z Javy do PL/SQL?