Oracle
 sql >> Teknologi Basis Data >  >> RDS >> Oracle

Mengatasi Bug Kolom Drop di Oracle 18c dan 19c

Jalan kemajuan terkadang sulit. Oracle versi 18 dan 19 tidak terkecuali. Hingga versi 18.x Oracle tidak memiliki masalah dengan menandai kolom sebagai tidak digunakan dan akhirnya menjatuhkannya. Mengingat beberapa keadaan yang menarik, dua versi Oracle terbaru dapat menimbulkan kesalahan ORA-00600 ketika kolom ditetapkan sebagai tidak digunakan dan kemudian dihapus. Kondisi yang menyebabkan kesalahan ini mungkin tidak umum tetapi ada sejumlah besar instalasi Oracle di seluruh dunia dan kemungkinan besar seseorang, di suatu tempat, akan menemukan bug ini.

Kisah ini dimulai dengan dua meja dan sebuah pemicu:

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 dimasukkan ke dalam tabel TRG_TST1 dan, dengan syarat terpenuhi, data direplikasi ke tabel TRG_TST2. Dua baris dimasukkan ke TRG_TST1 sehingga hanya satu dari baris yang disisipkan yang akan disalin ke TRG_TST2. Setelah setiap tabel penyisipan TRG_TST2 ditanyai dan hasilnya ditampilkan:

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 > 

Sekarang 'kesenangan' dimulai — dua kolom di TST_TRG1 ditandai 'tidak digunakan' dan kemudian dibuang, dan tabel TST_TRG2 terpotong. Penyisipan ke TST_TRG1 dieksekusi lagi, tetapi kali ini kesalahan ORA-00600 yang ditakuti dihasilkan. Untuk melihat mengapa kesalahan ini terjadi, status pemicu dilaporkan dari 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 > 

Masalahnya adalah, di Oracle 18c dan 19c, tindakan 'jatuhkan kolom yang tidak digunakan' TIDAK membatalkan pemicu sehingga membiarkannya dalam status 'VALID' dan mengatur transaksi berikutnya untuk gagal. Karena pemicu tidak dikompilasi ulang pada panggilan berikutnya, lingkungan kompilasi asli masih berlaku, lingkungan yang menyertakan kolom yang sekarang dijatuhkan. Oracle tidak dapat menemukan kolom C1 dan C2, tetapi pemicunya masih mengharapkan kolom tersebut ada, sehingga kesalahan ORA-00600. Dukungan Oracle saya melaporkan ini sebagai bug:

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

dan melaporkan bahwa penyebab sebenarnya adalah kegagalan untuk membatalkan pemicu dengan penurunan kolom yang ditangguhkan.

Jadi bagaimana untuk menyiasati masalah ini? Salah satu caranya adalah dengan mengompilasi pemicu secara eksplisit setelah kolom yang tidak digunakan dihapus:

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 > 

Dengan pemicu yang sekarang menggunakan lingkungan dan konfigurasi tabel saat ini, sisipan berfungsi dengan benar dan pemicu menyala seperti yang diharapkan:

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 > 

Ada cara lain untuk mengatasi masalah ini; Jangan tandai kolom sebagai tidak terpakai dan jatuhkan saja dari tabel. Menjatuhkan tabel asli, membuatnya kembali, dan menjalankan contoh ini dengan penurunan kolom lurus tidak menunjukkan tanda ORA-00600, dan status pemicu setelah penurunan kolom membuktikan bahwa tidak ada kesalahan seperti itu yang akan terjadi:

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 > 

Versi Oracle sebelum 18c berperilaku seperti yang diharapkan, dengan penurunan kolom yang ditangguhkan dengan benar mengatur status pemicu ke '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 >

Bagaimana kolom dijatuhkan dalam versi yang lebih lama dari 18c tidak ada bedanya karena pemicu apa pun pada tabel yang terpengaruh akan dianggap tidak valid. Panggilan berikutnya ke pemicu apa pun di tabel itu akan menghasilkan kompilasi ulang 'otomatis', yang menyetel lingkungan eksekusi dengan benar (artinya kolom yang hilang di tabel yang terpengaruh tidak akan tetap berada dalam konteks eksekusi).

Tidak mungkin database produksi akan mengalami penurunan kolom tanpa terlebih dahulu membuat perubahan seperti itu di database DEV atau TST. Sayangnya, pengujian sisipan setelah kolom dijatuhkan mungkin bukan pengujian yang dijalankan setelah perubahan tersebut dibuat dan sebelum kode dipromosikan ke PRD. Memiliki lebih dari satu orang untuk menguji efek samping dari menjatuhkan kolom tampaknya merupakan ide yang sangat baik, karena, seperti yang dibuktikan oleh pepatah lama, 'Dua kepala lebih baik dari satu.' Semakin banyak semakin meriah dalam situasi pengujian sehingga banyak jalan kemungkinan kegagalan dapat disajikan dan dieksekusi. Waktu ekstra yang dibutuhkan untuk menguji perubahan secara lebih menyeluruh berarti kecilnya kemungkinan kesalahan tak terduga yang secara serius memengaruhi, atau menghentikan, produksi.

# # #

Lihat artikel oleh David Fitzjarrell


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Bagaimana cara memberikan batasan unik untuk kombinasi kolom di Oracle?

  2. java.sql.SQLException:- ORA-01000:kursor terbuka maksimum terlampaui

  3. Menghasilkan sisipan sql ke untuk Oracle

  4. Cara Mendapatkan Hari Terakhir Bulan Ini di Oracle

  5. ASCII() Fungsi di Oracle