代码之家  ›  专栏  ›  技术社区  ›  cninicu

为什么在触发器内部没有执行raise\u application\u错误之前的代码?

  •  1
  • cninicu  · 技术社区  · 7 年前

    如果我创建此触发器,则在表上使用drop或truncate时会引发错误,但日志表中没有插入任何内容,但如果我删除RAISE\u APPLICATION\u error。。。然后将这些值插入logTable,但也会执行drop/truncate。为什么?如何避免对模式进行删除/截断(如果我使用而不是触发器,则仅当模式的所有者正在删除/截断某些内容时才会触发)。

    CREATE OR REPLACE TRIGGER trigger_name
    BEFORE DROP OR TRUNCATE ON DATABASE
    DECLARE
    username varchar2(100);
    BEGIN
      IF ora_dict_obj_owner = 'MySchema' THEN
        select user INTO username from dual; 
        INSERT INTO logTable VALUES(username , SYSDATE);
        RAISE_APPLICATION_ERROR (-20001,'ERROR, YOU CAN NOT DELETE THIS!!');
      END IF;
    END;
    
    1 回复  |  直到 7 年前
        1
  •  1
  •   krokodilko    7 年前

    根据 documentation :

    语句级原子性

    Oracle数据库支持语句级原子性,这意味着SQL语句是原子工作单元 要么完全成功,要么完全失败。

    成功的语句不同于提交的事务。A. 如果数据库解析并 将其作为一个原子单元运行时不会出错,就像所有行都更改时一样 在多行更新中。

    如果SQL语句在执行过程中导致错误,则不是 成功,因此语句的所有效果都将回滚。这 操作是 语句级回滚

    该过程是一个PL/SQL语句,它是原子的,如果在该过程中引发错误,则整个过程将失败,Oracle将回滚该过程所做的所有更改。


    但您可以使用 AUTONOMOUS_TRANSACTION Pragma 为了绕过这种行为,请通过以下方式:

    CREATE TABLE logtable(
       username varchar2(200),
       log_date date
    );
    
    CREATE OR REPLACE PROCEDURE log_message( username varchar2 ) IS
    PRAGMA AUTONOMOUS_TRANSACTION;
    BEGIN
      INSERT INTO logtable( username, log_date ) VALUES ( username, sysdate );
      COMMIT;
    END;
    /
    
    
    CREATE OR REPLACE TRIGGER trigger_name
    BEFORE DROP OR TRUNCATE ON DATABASE
    DECLARE
    username varchar2(100);
    BEGIN
      IF ora_dict_obj_owner = 'TEST' THEN
        log_message( user );
        RAISE_APPLICATION_ERROR (-20001,'ERROR, YOU CAN NOT DELETE THIS!!');
      END IF;
    END;
    

    现在:

    drop table table1;
    
    ORA-00604: error occurred at recursive SQL level 1
    ORA-20001: ERROR, YOU CAN NOT DELETE THIS!!
    ORA-06512: at line 6
    00604. 00000 -  "error occurred at recursive SQL level %s"
    *Cause:    An error occurred while processing a recursive SQL statement
               (a statement applying to internal dictionary tables).
    *Action:   If the situation described in the next error on the stack
               can be corrected, do so; otherwise contact Oracle Support.
    
    select * from logtable;
    
    USERNAME  LOG_DATE
    --------  -------------------
    TEST      2018-04-27 00:16:34