43.12. PL/pgSQL 开发提示 #
在 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,当然也有其他工具。它们通常提供一些方便的特性,例如转义单引号,以及让重新创建和调试函数变得更容易。
43.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 个引号)时。通常只有在编写生成其他函数的函数时(如例 43.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中,因为在使用前会对它再次加引号。
43.12.2. 额外的编译时和运行时检查 #
为了辅助用户在一些简单但常见的问题产生危害之前找到它们,PL/pgSQL 提供了额外的检查。当被启用时,根据配置,它们可以在一个函数的编译期间被用来发出
WARNING 或者 ERROR。一个已经收到了
WARNING 的函数可以被继续执行而不会产生进一步的消息,因此建议你在一个单独的开发环境中进行测试。
建议在开发和/或测试环境中,根据需要将 plpgsql.extra_warnings 或 plpgsql.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 在目标变量的数量和源变量的数量不同时抛出WARNING或ERROR。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)
报告文档问题
阅读 上游文档. 通过 PostgreSQL 文档反馈表单.