Oracle
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Oracle

Khắc phục lỗi cột thả trong Oracle 18c và 19c

Con đường tiến bộ đôi khi có thể gập ghềnh. Oracle phiên bản 18 và 19 cũng không ngoại lệ. Cho đến phiên bản 18.x Oracle không gặp vấn đề gì với việc đánh dấu các cột là không được sử dụng và cuối cùng bỏ chúng đi. Do một số trường hợp thú vị, hai phiên bản Oracle mới nhất có thể gây ra lỗi ORA-00600 khi các cột được đặt là không sử dụng và sau đó bị loại bỏ. Các điều kiện gây ra lỗi này có thể không phổ biến nhưng có một số lượng lớn các bản cài đặt Oracle trên toàn cầu và rất có thể ai đó ở đâu đó sẽ gặp phải lỗi này.

Câu chuyện bắt đầu với hai bảng và một trình kích hoạt:

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

Dữ liệu được chèn vào bảng TRG_TST1 và, với điều kiện đáp ứng các điều kiện, dữ liệu được sao chép sang bảng TRG_TST2. Hai hàng được chèn vào TRG_TST1 để chỉ một trong các hàng đã chèn sẽ được sao chép sang TRG_TST2. Sau mỗi lần chèn bảng TRG_TST2 được truy vấn và kết quả hiển thị:

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 > 

Bây giờ "niềm vui" bắt đầu - hai cột trong TST_TRG1 được đánh dấu là "không sử dụng" và sau đó bị loại bỏ và bảng TST_TRG2 bị cắt bớt. Các phần chèn vào TST_TRG1 được thực thi lại, nhưng lần này lỗi ORA-00600 đáng sợ được tạo ra. Để xem tại sao những lỗi này xảy ra, trạng thái của trình kích hoạt được báo cáo từ 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 > 

Vấn đề là, trong Oracle 18c và 19c, hành động "thả các cột không sử dụng" KHÔNG làm mất hiệu lực của trình kích hoạt, khiến nó ở trạng thái "HỢP LỆ" và thiết lập các giao dịch tiếp theo không thành công. Vì trình kích hoạt không được biên dịch lại trong lần gọi tiếp theo nên môi trường biên dịch ban đầu vẫn có hiệu lực, một môi trường bao gồm các cột hiện đã bị loại bỏ. Oracle không thể tìm thấy các cột C1 và C2, nhưng trình kích hoạt vẫn mong đợi chúng tồn tại, do đó xảy ra lỗi ORA-00600. Bộ phận Hỗ trợ Oracle của tôi báo cáo đây là lỗi:

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

và báo cáo rằng nguyên nhân thực tế là do không làm mất hiệu lực của trình kích hoạt với việc giảm cột bị hoãn lại.

Vậy làm thế nào để giải quyết vấn đề này? Một cách là biên dịch trình kích hoạt một cách rõ ràng sau khi các cột không sử dụng bị loại bỏ:

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 > 

Với trình kích hoạt hiện đang sử dụng môi trường và cấu hình bảng hiện tại, chức năng chèn hoạt động chính xác và trình kích hoạt sẽ kích hoạt như mong đợi:

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 > 

Có một cách khác để giải quyết vấn đề này; Đừng đánh dấu các cột là không sử dụng và chỉ cần bỏ chúng khỏi bảng. Việc loại bỏ các bảng gốc, tạo lại chúng và thực hiện ví dụ này với một lần thả thẳng cột không có dấu hiệu của ORA-00600 và trạng thái kích hoạt sau khi thả cột chứng tỏ rằng sẽ không có lỗi nào xảy ra:

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 > 

Các phiên bản Oracle trước 18c hoạt động như mong đợi, với việc thả cột hoãn lại đặt chính xác trạng thái kích hoạt thành ‘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 >

Cách các cột bị loại bỏ trong các phiên bản cũ hơn 18c không có gì khác biệt vì bất kỳ trình kích hoạt nào trên bảng bị ảnh hưởng sẽ không hợp lệ. Lệnh gọi tiếp theo tới bất kỳ trình kích hoạt nào trên bảng đó sẽ dẫn đến biên dịch lại 'tự động', thiết lập môi trường thực thi đúng cách (nghĩa là các cột bị thiếu trong bảng bị ảnh hưởng sẽ không nằm trong ngữ cảnh thực thi).

Không có khả năng cơ sở dữ liệu sản xuất sẽ bị sụt cột nếu không thực hiện những thay đổi như vậy trước trong cơ sở dữ liệu DEV hoặc TST. Rất tiếc, việc kiểm tra chèn sau khi cột bị loại bỏ có thể không phải là kiểm tra được thực hiện sau khi những thay đổi đó được thực hiện và trước khi mã được thăng cấp thành PRD. Có nhiều người kiểm tra hậu quả của việc thả cột dường như là một ý tưởng tuyệt vời, vì như câu ngạn ngữ cũ đã chứng minh, 'Hai cái đầu tốt hơn một cái đầu.' Càng nhiều càng vui trong một tình huống thử nghiệm để có nhiều con đường của lỗi có thể xảy ra có thể được trình bày và thực hiện. Thời gian dành thêm để kiểm tra kỹ lưỡng hơn một thay đổi có nghĩa là ít có khả năng xảy ra các lỗi không lường trước được ảnh hưởng nghiêm trọng hoặc ngừng hoạt động sản xuất.

# # #

Xem các bài viết của 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. Oracle - Tại sao tôi nên sử dụng các gói thay vì các thủ tục hoặc hàm độc lập

  2. Tại sao Oracle.ManagedDataAccess không hoạt động khi Oracle.DataAccess hoạt động?

  3. Thay thế văn bản trong một cột BLOB

  4. Phương thức ExecuteBatch trả về mảng giá trị -2 trong java

  5. ORA-01653:không thể mở rộng bảng trong không gian bảng ORA-06512