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 提出。我看不到任何现有报告,但我没有仔细查看,所以可能某处潜伏着一个。
我 运行 尝试对 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 提出。我看不到任何现有报告,但我没有仔细查看,所以可能某处潜伏着一个。