Oracle 在后面的块中使用来自第一个块的异常处理程序

Oracle uses exception handler from first block in later blocks

我 运行 尝试对 Flyway 脚本和 Oracle 中的几个 Oracle PL/SQL 块使用特定于案例的异常处理程序,这显然与其记录的范围界定相矛盾异常处理程序,将所有异常发送到第一个块的异常处理程序。例如,在这段代码中:

begin

  begin
    execute immediate '
create table "test" (
  "id" number not null,
  "name" varchar2(100) not null,
  constraint "test_pk" primary key ("id")
)
';
  exception
  when others then
    if sqlcode != -955 then raise; end if;
  end;

  begin
    execute immediate 'fail to create index "test_name_idx" on "test" ("name")';
  exception
  when others then
    if sqlcode != -6512 then raise; end if;
  end;

end;

未捕获 ORA-06512 异常,并且从第 13 行开始标记引发的异常。

将块包裹在更多块中无济于事。

这是怎么回事?我该如何阻止这种情况发生?

这似乎是一个错误,(到目前为止)已在 11.2.0.4、12.1.0.2 和 12.2.0.1 中重现。它似乎不需要 DDL,也不需要第一个子块中的任何实际操作(尽管只是 null; 作为占位符不会触发它,可能是因为编译器将其删除),但它似乎确实两个异常处理程序中都需要 if

begin
  begin
    dbms_output.put_line('Dummy message');
  exception
    when others then
      dbms_output.put_line('In first exception handler');
      if 1=1 then
        raise;
      end if;
  end;

  begin
    execute immediate 'invalid';
  exception
    when others then
      dbms_output.put_line('In second exception handler');
      if 1=1 then
        raise;
      end if;
  end;
end;
/

Dummy message
In second exception handler

ORA-00900: invalid SQL statement
ORA-06512: at line 8
ORA-06512: at line 13

与您的示例一样,第 13 行抛出异常,因此应在第 18 行报告为(重新)引发;但它被报告为从第 8 行开始,这没有意义。 (at line 13 消息仅在 12.2 中显示;在 11.2 和 12.1 中,它仅报告第一个 ORA-06512,这更令人困惑。至少在 12 2 中,您对问题的真正所在有一些线索。)

从调试中您可以看到它实际上并没有使用第一个异常处理程序,而是进入了第二个异常处理程序。 'only' 似乎是在报告错误的行号,而不是执行错误的代码。

似乎在 if 内部做真正的工作,就在 raise 之前以某种方式解决问题 - 在 either 异常处理部分;这会在第一条消息中添加一条无法到达的消息:

begin
  begin
    dbms_output.put_line('Dummy message');
  exception
    when others then
      dbms_output.put_line('In first exception handler');
      if 1=1 then
        dbms_output.put_line('This avoids the bug somehow');
        raise;
      end if;
  end;

  begin
    execute immediate 'invalid';
  exception
    when others then
      dbms_output.put_line('In second exception handler');
      if 1=1 then
        raise;
      end if;
  end;
end;
/

Dummy message
In second exception handler

ORA-00900: invalid SQL statement
ORA-06512: at line 19
ORA-06512: at line 14

第二个是这个:

begin
  begin
    dbms_output.put_line('Dummy message');
  exception
    when others then
      dbms_output.put_line('In first exception handler');
      if 1=1 then
        raise;
      end if;
  end;

  begin
    execute immediate 'invalid';
  exception
    when others then
      dbms_output.put_line('In second exception handler');
      if 1=1 then
        dbms_output.put_line('This avoids the bug somehow');
        raise;
      end if;
  end;
end;
/

Dummy message
In second exception handler

ORA-00900: invalid SQL statement
ORA-06512: at line 19
ORA-06512: at line 13

在这两种情况下,报告的行号现在都是正确的。不知何故。

它不一定是 dbms_output 调用,任何东西似乎都有效,例如虚拟过程调用或查询,甚至是额外的子块(例如 begin execute immediate 'select * from dual'; end;,即使查询未执行,因为没有 into...)。同样,仅使用 null; 是行不通的。

这有点难看,但至少在某种程度上为您提供了一种阻止它发生的方法。

这显然是奇怪的、意外的和不一致的行为,并且已经存在了一段时间,所以它可能应该作为服务请求通过 My Oracle Support 提出。我看不到任何现有报告,但我没有仔细查看,所以可能某处潜伏着一个。