在 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,尽管还有其他工具。这些工具通常提供方便的功能,例如转义单引号以及使重新创建和调试函数更容易。
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 个)。如果您正在编写一个生成其他函数的函数,则可能只需要这样,如 示例 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 提供了额外的检查
。启用后,根据配置,它们可用于在函数编译期间发出 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)
如果您在文档中发现任何不正确、与您使用特定功能的体验不符或需要进一步说明的地方,请使用此表单报告文档问题。