为啥在 raise_application_error 未执行之前的触发器内部代码?

Posted

技术标签:

【中文标题】为啥在 raise_application_error 未执行之前的触发器内部代码?【英文标题】:Why inside of a trigger the code before raise_application_error isn't executed?为什么在 raise_application_error 未执行之前的触发器内部代码? 【发布时间】:2018-04-26 20:42:46 【问题描述】:

如果我创建了这个触发器,那么当对表使用 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;

【问题讨论】:

出于兴趣,select user INTO username from dual的目的是什么? 目的是将想要从 MySchema 中删除/截断表的用户名添加到日志表中。 但你已经知道user。该查询似乎只是将其复制到名称略有不同的变量中。现在usernameuser 具有相同的值。它似乎没有给你任何你不知道的东西。 如果我有,例如,两个用户可以访问相同的模式并且有人试图删除一个表,那么在日志表中我有用户名和他试图删除表的时间. 但你不能只是insert into logTable values (user, sysdate)?必须有第二个变量和一个查询来填充它? 【参考方案1】:

根据documentation:

语句级原子性

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

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

如果 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

【讨论】:

以上是关于为啥在 raise_application_error 未执行之前的触发器内部代码?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 CoreGui Roblox 锁定在 DataModel 中,为啥受信任的用户不能使用 CoreScripts?

为啥 + 仅在客户端是 NaN?为啥不在 Node.js 中?

在执行语义分割任务时我应该减去图像均值吗?为啥或者为啥不?

为啥我们不能在 TypeScript 类中定义一个 const 字段,为啥静态只读不起作用?

为啥这个函数序言中没有“sub rsp”指令,为啥函数参数存储在负 rbp 偏移处?

为啥这个函数序言中没有“sub rsp”指令,为啥函数参数存储在负 rbp 偏移量?