Redrock Postgres 搜索 英文
版本: 9.3 / 9.4 / 9.5 / 9.6 / 10 / 11 / 12 / 13 / 14 / 15 / 16 / 17

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 命令以测试该函数。

使用 GUI 数据库访问工具来开发 PL/pgSQL 是一个不错的选择,这种工具有助于使用过程语言进行开发。pgAdmin 就是这种工具的一个例子,当然还有很多别的工具。这些工具经常提供简便的功能,如转义单引号,并让重新创建和调试函数变得更简单。

41.12.1. 处理引号 #

PL/pgSQL 函数的代码在 CREATE FUNCTION 中作为一个字符串文本指定。如果你使用普通方式编写字符串文本,即将其用单引号引起来,那么函数正文中的任何单引号都必须加倍;类似地,任何反斜杠也必须加倍(假设使用转义字符串语法)。引用重复引号充其量是件乏味的事,在更加复杂的情况下,代码可能变得完全不可理解,因为你很可能发现自己需要五六个或更多个相邻的引号。建议你将该函数正文写成 美元引号 字符串文本(请参阅 第 4.1.2.4 节)。在美元引用方法中,你永远不必引用任何引号,而应小心为每一层嵌套选择不同的美元引用定界符。例如,你可以将 CREATE FUNCTION 命令写成

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

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

以下图表展示了不使用美元引用编写引号时你必须做的事情。在将美元引用之前的代码转换成更易理解的内容时,它可能很有用。

1 个引号 #

要为函数正文开头和结尾加上引号,例如

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

在引号引用的函数正文内部的任何地方,引号 必须 成对出现。

2 个引号 #

对于函数正文内的字符串文本,例如

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

在美元引用方法中,你只需编写

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

这正是 PL/pgSQL 解析器在任意一种情况下都会看到的内容。

4 个引号 #

当你需要在函数正文内的字符串常量中使用单个引号时,例如

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 个引号 #

当字符串中的单个引号紧邻字符串常量的末尾(例如)时

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

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

使用美元符号引号方法,将变成

a_output := a_output || $$ AND name LIKE 'foobar'$$
10 个引号 #

当您希望在字符串常量中使用两个单引号(就是 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 。启用这些检查时,可根据配置,在编译函数期间,可以使用它们来发出 WARNINGERROR 。收到 WARNING 的函数可执行,而无需生成进一步的消息,因此建议您在单独的开发环境中进行测试。

建议在开发和/或测试环境中将 plpgsql.extra_warningsplpgsql.extra_errors(视情况而定)设置为 "all"

可通过配置变量 plpgsql.extra_warnings(表示警告)和 plpgsql.extra_errors(表示错误)启用这些其他检查。这两者都可以设置为以逗号分隔的检查列表, "none""all" 。默认值是 "none" 。目前,可用检查列表包括

shadowed_variables #

检查声明是否掩盖了之前定义的变量。

strict_multi_assignment #

PL/pgSQL 中的一些命令允许一次为多个变量赋值,例如 SELECT INTO。通常情况下,目标变量和源变量的数量应该匹配,尽管 PL/pgSQL 会对缺失值使用 NULL,多余的变量会被忽略。启用此项检查会导致 PL/pgSQL 在目标变量数量与源变量数量不同时抛出 WARNINGERROR

too_many_rows #

启用此项检查会导致 PL/pgSQL 在使用 INTO 子句时检查给定查询是否返回多于一行。由于 INTO 语句只会使用一行,因此让查询返回多行通常会造成低效和/或非确定性,因此很可能是错误。

以下示例展示了将 plpgsql.extra_warnings 设置为 shadowed_variables 的效果

SET plpgsql.extra_warnings TO 'shadowed_variables';

CREATE FUNCTION foo(f1 int) RETURNS int AS $$
DECLARE
f1 int;
BEGIN
RETURN f1;
END;
$$ LANGUAGE plpgsql;
WARNING:  variable "f1" shadows a previously defined variable
LINE 3: f1 int;
        ^
CREATE FUNCTION

以下示例展示了将 plpgsql.extra_warnings 设置为 strict_multi_assignment 的效果

SET plpgsql.extra_warnings TO 'strict_multi_assignment';

CREATE OR REPLACE FUNCTION public.foo()
 RETURNS void
 LANGUAGE plpgsql
AS $$
DECLARE
  x int;
  y int;
BEGIN
  SELECT 1 INTO x, y;
  SELECT 1, 2 INTO x, y;
  SELECT 1, 2, 3 INTO x, y;
END;
$$;

SELECT foo();
WARNING:  number of source and target fields in assignment does not match
DETAIL:  strict_multi_assignment check of extra_warnings is active.
HINT:  Make sure the query returns the exact list of columns.
WARNING:  number of source and target fields in assignment does not match
DETAIL:  strict_multi_assignment check of extra_warnings is active.
HINT:  Make sure the query returns the exact list of columns.

 foo
-----

(1 row)