PostgreSQL 中文参考文档
🌐 英文原版 官方 ↗

41.12. PL/pgSQL 开发技巧与实用建议 #

41.12.1. 引号转义处理
41.12.2. 编译期与运行期扩展检查

开发 PL/pgSQL 的一个好方法是: 用你喜欢的文本编辑器创建函数,然后在另一个窗口中使用 psql 加载并测试这些函数。 如果采用这种方式,建议用 CREATE OR REPLACE FUNCTION 来书写函数。 这样,重新加载文件即可更新函数定义。例如:

CREATE OR REPLACE FUNCTION testfunc(integer) RETURNS integer AS $$
          ....
$$ LANGUAGE plpgsql;

在运行 psql 期间, 可以用以下命令加载或重新加载这样的函数定义文件:

\i filename.sql

然后立即发出 SQL 命令来测试该函数。

开发 PL/pgSQL 的另一个好方法是使用 便于过程语言开发的 GUI 数据库访问工具。这类工具的一个例子是 pgAdmin,此外还有其他工具。 这些工具通常提供一些便利特性,例如转义单引号, 以及更轻松地重建和调试函数。

41.12.1. 引号转义处理 #

PL/pgSQL 函数的代码在 CREATE FUNCTION 中以字符串字面值的形式给出。 如果你用普通的方式书写这个字符串字面值,即用单引号包围, 那么函数体内部的任何单引号都必须加倍; 同样,任何反斜线也必须加倍(假设使用的是转义字符串语法)。 引号加倍充其量是件繁琐的事,而在更复杂的情况下, 代码可能变得完全难以理解,因为你很容易发现自己需要 六个或更多相邻的引号。建议你改为把函数体写成一个 “美元符引用”的字符串字面值 (见 Section 4.1.2.4)。 在美元符引用方式下,你永远不需要把任何引号加倍, 只需要小心地为你需要的每一层嵌套选择不同的美元符引用定界符。 例如,你可以这样书写 CREATE FUNCTION 命令:

CREATE OR REPLACE FUNCTION testfunc(integer) RETURNS integer AS $PROC$
          ....
$PROC$ LANGUAGE plpgsql;

在其内部,你可以在 SQL 命令中对简单的字面字符串使用引号, 并用 $$ 来定界你正在作为字符串组装的 SQL 命令片段。如果你需要引用包含 $$ 的文本, 可以使用 $Q$,依此类推。

下表展示了在不使用美元符引用时书写引号必须做的事情。 在把美元符引用出现之前的代码转换成更容易理解的形式时, 它可能有用。

1 quotation mark #

用于开始和结束函数体,例如:

CREATE FUNCTION foo() RETURNS integer AS '
          ....
' LANGUAGE plpgsql;

在单引号函数体内部的任何位置,引号 必须成对出现。

2 quotation marks #

用于函数体内部的字符串字面值,例如:

a_output := ''Blah'';
SELECT * FROM users WHERE f_name=''foobar'';

在美元符引用方式下,你只需写:

a_output := 'Blah';
SELECT * FROM users WHERE f_name='foobar';

这正是 PL/pgSQL 解析器在两种写法下 所看到的内容。

4 quotation marks #

当你需要函数体内字符串常量中的单个引号时,例如:

a_output := a_output || '' AND name LIKE ''''foobar'''' AND xyz''

实际追加到 a_output 的值将是: AND name LIKE 'foobar' AND xyz。

在美元符引用方式下,你应写成:

a_output := a_output || $$ AND name LIKE 'foobar' AND xyz$$

注意,这周围任何美元符引用定界符都不要只写成 $$。

6 quotation marks #

当函数体内字符串中的单个引号紧邻该字符串常量的末尾时,例如:

a_output := a_output || '' AND name LIKE ''''foobar''''''

追加到 a_output 的值将是: AND name LIKE 'foobar'。

在美元符引用方式下,这变成:

a_output := a_output || $$ AND name LIKE 'foobar'$$

10 quotation marks #

当你想要字符串常量中的两个单引号(这占 8 个引号), 且它们紧邻该字符串常量的末尾(再加 2 个)时。 你可能只有在编写生成其他函数的函数时才需要它, 如 Example 41.10 所示。 例如:

a_output := a_output || '' if v_'' ||
    referrer_keys.kind || '' like ''''''''''
    || referrer_keys.key_string || ''''''''''
    then return ''''''  || referrer_keys.referrer_type
    || ''''''; end if;'';

a_output 的值将是:

if v_... like ''...'' then return ''...''; end if;

在美元符引用方式下,这变成:

a_output := a_output || $$ if v_$$ || referrer_keys.kind || $$ like '$$
    || referrer_keys.key_string || $$'
    then return '$$  || referrer_keys.referrer_type
    || $$'; end if;$$;

这里我们假定只需要把单引号放进 a_output,因为它在被使用之前会再次被引用。

41.12.2. 编译期与运行期扩展检查 #

为了帮助用户在简单但常见的问题造成危害之前发现它们, PL/pgSQL 提供了额外的 检查(checks)。启用后(取决于配置), 它们可用于在函数编译期间发出 WARNING 或 ERROR。收到过 WARNING 的函数可以在不产生进一步消息的情况下执行, 因此建议你在单独的开发环境中进行测试。

建议在开发和/或测试环境中酌情把 plpgsql.extra_warnings 或 plpgsql.extra_errors 设置为 "all"。

这些额外的检查通过配置变量启用: plpgsql.extra_warnings 对应警告, plpgsql.extra_errors 对应错误。 两者都可以被设置为以逗号分隔的检查列表、 "none" 或 "all"。 默认值为 "none"。当前可用的检查列表包括:

shadowed_variables #

检查某条声明是否遮蔽了先前定义的变量。