40.11. 开发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,当然还有其它的。这些工具通常提供了一些很有用的功能, 比如转义单引号使得重建和调试函数更简单等。

40.11.1. 引号标记处理

PL/pgSQL函数的代码都是在 CREATE FUNCTION里以一个字符串文本的方式声明的。 如果你用两边包围单引号的常规方式写字符串文本,那么任何函数体内的单引号都必须写双份; 类似的是反斜杠也必须双份。双份引号非常乏味,在更复杂的场合下,代码可能会让人难以理解, 因为你很容易发现自己需要半打甚至更多相连的引号。 建议你用"dollar-quoted"的字符串文本来写函数体。 (参阅第 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个)。 可能只有在写一个生成其它函数的函数的时候, 像例 40-9里那样。比如:

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里放单引号,因为在使用前它会被重新引号包围。

40.11.2. 额外的编译时检查

为了帮助用户在他们造成伤害之前找到简单但是常见问题的实例, PL/PgSQL提供了额外的检查。 启用时,根据配置,在函数编译期间发出一个WARNING或者一个ERROR。 接收到WARNING的函数可以被执行而不产生进一步消息, 所以建议你在一个单独的开发环境中测试。

这些额外的检查是通过配置变量plpgsql.extra_warningsplpgsql.extra_errors启用警告和错误的。两者都可以设置为逗号分隔的检查列表、 "none"或者"all"。缺省是"none"。 目前变量检查列表仅包含一项:

shadowed_variables

检查一个声明是否覆盖了以前定义的变量。

下面的示例显示了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