在 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 就是这种工具的一个例子,当然还有很多别的工具。这些工具经常提供简便的功能,如转义单引号,并让重新创建和调试函数变得更简单。
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$
,依此类推。
以下图表展示了不使用美元引用编写引号时你必须做的事情。在将美元引用之前的代码转换成更易理解的内容时,它可能很有用。
要为函数正文开头和结尾加上引号,例如
CREATE FUNCTION foo() RETURNS integer AS ' .... ' LANGUAGE plpgsql;
在引号引用的函数正文内部的任何地方,引号 必须 成对出现。
对于函数正文内的字符串文本,例如
a_output := ''Blah''; SELECT * FROM users WHERE f_name=''foobar'';
在美元引用方法中,你只需编写
a_output := 'Blah'; SELECT * FROM users WHERE f_name='foobar';
这正是 PL/pgSQL 解析器在任意一种情况下都会看到的内容。
当你需要在函数正文内的字符串常量中使用单个引号时,例如
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$$
注意不要使用代码周围的美元符号-引号分隔符 $$
.
当字符串中的单个引号紧邻字符串常量的末尾(例如)时
a_output := a_output || '' AND name LIKE ''''foobar''''''
附加到 a_output
的值将是: AND name LIKE 'foobar'
。
使用美元符号引号方法,将变成
a_output := a_output || $$ AND name LIKE 'foobar'$$
当您希望在字符串常量中使用两个单引号(就是 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
中放入单引号,因为它将在使用之前重新加上引号。
为帮助用户在一些简单但常见的问题导致问题之前找到它们, PL/pgSQL 提供了其他 checks
。启用这些检查时,可根据配置,在编译函数期间,可以使用它们来发出 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)