PL/SQL 编译失败时如何让 SQL*Plus 退出

Posted

技术标签:

【中文标题】PL/SQL 编译失败时如何让 SQL*Plus 退出【英文标题】:How can I make SQL*Plus exit when PL/SQL compilation fails 【发布时间】:2016-03-09 21:32:42 【问题描述】:

我的脚本文件有以下代码:

WHENEVER SQLERROR EXIT SQL.SQLCODE

@pkg_t.pks
@pkg_t.pkb

我正在从 SQL*Plus 运行该脚本。如果pkg_t.pkb 无效,我会看到:

Warning: Package created with compilation errors.

但 SQL*Plus 不退出。我了解到pkg_t.pkb 有一个 PL/SQL 错误编译,它与 SQL 错误不同。

需要添加什么指令让SQL*Plus在遇到PL/SQL编译错误时退出?

【问题讨论】:

【参考方案1】:

您收到的警告并不完全是 PL/SQL 错误或 SQL 错误。 PL/SQL 运行时错误会导致类似 ORA-06550 的问题,您的 whenever sqlerror 会捕获该错误,并且脚本将使用该代码退出。但是,您会看到一个编译错误,这是不同的,它是由客户端生成的。

捕获它的一种方法是检查存储在数据字典中的错误:

WHENEVER SQLERROR EXIT SQL.SQLCODE

@pkg_t.pks
@pkg_t.pkb

declare
  l_errors pls_integer;
begin
  select count(*) into l_errors from user_errors;
  if l_errors > 0 then
    raise_application_error(-20001, 'Stored PL/SQL has compilation errors');
  end if;
end;
/

exit 0;

如果包规范或正文出现错误,则计数将非零,您将看到:

declare
*
ERROR at line 1:
ORA-20001: Stored PL/SQL has compilation errors
ORA-06512: at line 6

您还可以在user_objects 中检查对象状态。如果你想检查一个特定的对象,你可以命名它,但这会降低它的灵活性;如果您预计周围还有其他现有的无效对象,则可能需要这样做。保持其通用性意味着您可以拥有一个单独的脚本,您可以重复调用该脚本以尽早捕获错误,例如 check_errors.sql 仅包含:

declare
  l_errors pls_integer;
begin
  select count(*) into l_errors from user_errors;
  if l_errors > 0 then
    raise_application_error(-20001, 'Stored PL/SQL has compilation errors');
  end if;
end;
/

然后你可以这样做:

@pkg_t.pks
@check_errors
@pkg_t.pkb
@check_errors

当然,您可以使脚本更精美,并进行其他检查。如果您现有的 .pks 和 .pkb 脚本还没有这样做,您可以添加 show errors 以便查看实际问题。


关于SQL.SQLCODE 的旁注。那将有实际的错误代码,例如6550 或 20001。但大多数 shell 只允许错误代码达到较小的值,例如127 或 255。这意味着实际的错误代码将“换行”,因此它不太可能有意义,但更重要的是,可能有一个错误代码恰好换行为零 - 这意味着如果您正在检查返回代码,您可能会错误地认为它是成功的。例如,如果我完成了:

raise_application_error(-20224, 'Stored PL/SQL has compilation errors');

然后 -20224 值将归零; SQL*Plus 将退出该代码,但如果我在 bash 中检查$?,它将是0,并且看起来它已经成功了。类似地,-20223 会换行到 255,-20225 会换行到 1 - bash 会将两者都视为错误,但不会指出实际错误是什么。

所以使用固定值会更安全。我经常只使用whenever sql error exit failure,通常会添加rollback,但如果你只是在做DDL,那就没关系了。

【讨论】:

check_errors 显示所有错误,目前尚未修复。有没有办法只看到由以前的脚本引起的那些?我想以某种方式重置 user_errors。 @GerritGriebel - 不是真的。我能想到的唯一方法是将脚本开始时间记录为变量,然后在该点之后查找创建或修改的对象(last_ddl_time)的错误,将 user_objects 加入 user_errors。不过,这有点超出范围;也许尝试一下,如果你不能让它工作,请提出一个新问题?

以上是关于PL/SQL 编译失败时如何让 SQL*Plus 退出的主要内容,如果未能解决你的问题,请参考以下文章

SQL*Plus 不运行 PL/SQL 块

如何在 PL/SQL 中使用 select 语句为 SQL*Plus 变量赋值?

如何从 PL/SQL 有条件地退出 SQL Plus

PL/SQL 编译失败,没有错误消息

如何将 PL/SQL .sql 文件转换为 SQL*PLUS,以便它可以与 Windows 任务计划程序一起运行

获取 SQL 执行计划。 PL/SQL Developer = 奇怪的行为。 SQL*Plus = 未选择任何行