为什么在触发器内部还没有执行raise_application_error之前的代码?

时间:2018-04-26 20:42:47

标签: sql database plsql triggers

如果我创建了这个触发器,那么当在表上使用drop或truncate时会引发错误,但是没有任何内容插入到logTable中,但是如果我删除了RAISE_APPLICATION_ERROR ...则将值插入到logTable中,但是drop / truncate也被执行。为什么?如何避免在Schema上删除/截断(如果我使用而不是触发器,只有在架构的所有者正在删除/截断某些内容时才会触发它)。

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 个答案:

答案 0 :(得分:1)

根据documentation

  

声明级原子性

     

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

     

成功的声明与已提交的事务不同。一个   如果数据库解析和,则单个SQL语句成功执行   在没有错误的情况下运行它作为原子单元,就像所有行都被更改一样   在多行更新中。

     

如果SQL语句在执行期间导致错误,则不会   成功,所以声明的所有效果都会被回滚。这个   operation是语句级回滚

该过程是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