PostgreSQL与Oracle:PL/pgSQL的"编译时"检查

tts*_*ras 7 oracle postgresql plsql plpgsql

执行摘要: PostgreSQL很棒,但由于它推迟了许多PL/pgSQL代码检查直到运行时间,因此我们面临着许多工作中的问题.有没有办法让它在这方面更像甲骨文的PL/SQL

例如...

尝试在任何Oracle DB中执行此操作:

create function foo return number as
begin
  select a from dual;
  return a;
end;
Run Code Online (Sandbox Code Playgroud)

Oracle将立即(即在编译时!)响应:

[Error] ORA-00904: invalid identifier
Run Code Online (Sandbox Code Playgroud)

现在尝试PostgreSQL中语义相同的东西:

CREATE OR REPLACE FUNCTION public.foo ()
    RETURNS integer AS
$body$
    BEGIN
        select a;
        return a;
    END;
$body$
LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

你会看到它 - 不幸的是! - 执行正常...没有报告错误.

但是,当您尝试调用此函数(即在运行时)时,您将获得:

ERROR:  column "a" does not exist
LINE 1: select a
Run Code Online (Sandbox Code Playgroud)

有没有办法强制PostgreSQL在函数定义时执行语法分析和检查 - 而不是在运行时?我们有很多传统的PL/SQL代码在工作,我们正在移植到PostgreSQL - 但缺乏编译时检查是非常痛苦的,迫使我们做手工工作 - 即编写代码来测试所有函数中的所有代码路径/程序 - 在Oracle中以其他方式自动化.

vye*_*rov 7

是的,这是一个已知问题.

PL/pgSQL(与其他任何函数一样,除了on SQL)是PostgreSQL的"黑盒子",因此除了在运行时之外不可能检测到错误.

你可以做几件事:

  1. 将调用SQL查询的函数包装到BEGIN/ COMMITstatements中,以便更好地控制错误;
  2. 在代码中添加EXCEPTION以捕获和跟踪错误.但请注意,这会影响功能性能;
  3. 使用plpgsql_check扩展,由PavelStěhule开发,他是PL/pgSQL开发的主要贡献者之一.我想最终这个扩展会使它成为PostgreSQL的核心,但它需要一些时间(现在我们处于9.4beta3状态);
  4. 您可能还会查看此相关问题:postgresql语法检查而不运行查询

而且看起来你真的需要一个单元测试框架.

  • 关于测试的主题,我会建议原始的海报立即到http://pgtap.org/,让他的生活*更容易*. (2认同)

Pav*_*ule 5

Plpgsql 语言的设计没有在编译时进行语义检查。我不确定这个特性是旧 plpgsql 实现的意图还是副作用,但随着时间的推移,我们发现了它的一些优点(但也有你提到的缺点)。

加:

  • 函数和其他数据库对象之间的依赖性问题较少。这是循环依赖问题的简单解决方案。plpgsql 函数的部署更容易,因为您不需要尊重依赖性。
  • 使用惰性依赖检查可以实现一些带有临时表的模式。这是必要的,因为 Postgres 不支持全局临时表。

例子:

BEGIN
  CREATE TEMP TABLE xx(a int);
  INSERT INTO xx VALUES(10); -- isn't possible with compile-time dependency checks
END;
Run Code Online (Sandbox Code Playgroud)

减:

  • 编译时深度检查是不可能的(标识符检查),尽管有时是可能的。

对于一些较大的项目,应使用混合解决方案:

  • 回归和单元测试 - 这是基础,因为某些情况无法静态检查 - 例如动态 SQL。
  • plpgsql_check - 它是一些大公司和更大的 plpgsql 用户使用的外部但受支持的项目。它可以强制对 SQL 标识符的有效性进行静态检查。您可以通过 DDL 触发器强制执行此检查。