sql >> データベース >  >> RDS >> Oracle

Oracle18cおよび19cのドロップ列のバグへの対処

    進歩の道は時々荒いことがあります。 Oracleバージョン18および19も例外ではありません。バージョン18.xまで、Oracleは列を未使用としてマークし、最終的にそれらを削除することに問題はありませんでした。いくつかの興味深い状況を考えると、最新の2つのOracleバージョンでは、列が未使用として設定されてから削除されると、ORA-00600エラーがスローされる可能性があります。このエラーを引き起こす条件は一般的ではないかもしれませんが、世界中に多数のOracleがインストールされており、どこかで誰かがこのバグに遭遇する可能性が非常に高いです。

    物語は2つのテーブルとトリガーから始まります:

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

    データはテーブルTRG_TST1に挿入され、条件が満たされている場合、データはテーブルTRG_TST2に複製されます。 2つの行がTRG_TST1に挿入されるため、挿入された行の1つだけがTRG_TST2にコピーされます。各挿入テーブルTRG_TST2が照会され、結果が表示された後:

    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 > 
    

    これで「楽しい」が始まります。TST_TRG1の2つの列が「未使用」とマークされてから削除され、テーブルTST_TRG2が切り捨てられます。 TST_TRG1への挿入が再度実行されますが、今回は恐ろしいORA-00600エラーが生成されます。これらのエラーが発生する理由を確認するために、トリガーのステータスが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 > 
    

    問題は、Oracle 18cおよび19cでは、「未使用の列を削除」アクションによってトリガーが無効にならず、トリガーが「有効」状態のままになり、次のトランザクションが失敗するように設定されることです。トリガーは次の呼び出しで再コンパイルされなかったため、元のコンパイル環境はまだ有効であり、現在ドロップされている列を含む環境です。 Oracleは列C1とC2を見つけることができませんが、トリガーはまだそれらが存在することを期待しているため、ORA-00600エラーが発生します。私のOracleサポートはこれをバグとして報告しています:

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

    そして、原因は、実際には、延期された列の削除でトリガーを無効にできなかったことであると報告しています。

    では、この問題を回避するにはどうすればよいですか? 1つの方法は、未使用の列が削除された後にトリガーを明示的にコンパイルすることです。

    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 > 
    

    トリガーが現在の環境とテーブル構成を使用するようになると、挿入は正しく機能し、トリガーは期待どおりに起動します。

    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 > 
    

    この問題を回避する別の方法があります。列を未使用としてマークせず、単にテーブルから削除します。元のテーブルを削除し、それらを再作成して、この例をまっすぐな列の削除で実行すると、ORA-00600の兆候は示されず、列の削除後のトリガーステータスは、そのようなエラーがスローされないことを証明します。

    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 > 
    
    である必要があります

    18cより前のOracleバージョンは期待どおりに動作し、遅延列ドロップによってトリガーステータスが「無効」に正しく設定されます。

    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 >
    

    18cより古いバージョンで列がドロップされる方法は、影響を受けるテーブルのトリガーが無効になるため、違いはありません。そのテーブルのトリガーを次に呼び出すと、「自動」再コンパイルが行われ、実行環境が適切に設定されます(つまり、影響を受けるテーブルで欠落している列が実行コンテキストに残りません)。

    DEVまたはTSTデータベースで最初にそのような変更を行わずに、本番データベースで列が削除される可能性はほとんどありません。残念ながら、列が削除された後の挿入のテストは、そのような変更が行われた後、コードがPRDにプロモートされる前に実行されるテストではない場合があります。古い格言が証明しているように、「2つの頭が1つよりも優れている」ので、複数の人に柱を落とす後遺症をテストさせることは素晴らしい考えのように思われます。発生する可能性のある障害を提示して実行できます。変更をより徹底的にテストするために余分な時間がかかるということは、予期しないエラーが本番環境に深刻な影響を与えたり、本番環境を停止したりする可能性が低くなることを意味します。

    ###

    David Fitzjarrellの記事を見る


    1. Oracleのビューとは何ですか?

    2. SQLServerでクエリデザイナを使用する方法

    3. Oracleから各グループの最新の行を選択します

    4. MicrosoftTreeViewControlチュートリアル