pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
PL/pgSQL 语句中使用的所有表达式都由服务器的主 SQL 执行器处理。实际上,像下面这样的查询
SELECT expression
是通过 SPI 管理器执行的。在求值之前,出现的 PL/pgSQL 变量标识符会被替换为参数,而变量的实际值则通过参数数组传递给执行器。这使得 SELECT 的查询计划只需准备一次,随后就可以重复用于后续的求值。
PostgreSQL 主解析器所做的求值,对常量值的解释有一些副作用。具体来说,下面这两个函数的行为有所不同:
CREATE FUNCTION logfunc1(logtxt text) RETURNS timestamp AS $$
BEGIN
INSERT INTO logtable VALUES (logtxt, 'now');
RETURN 'now';
END;
$$ LANGUAGE plpgsql;
和
CREATE FUNCTION logfunc2(logtxt text) RETURNS timestamp AS $$
DECLARE
curtime timestamp;
BEGIN
curtime := 'now';
INSERT INTO logtable VALUES (logtxt, curtime);
RETURN curtime;
END;
$$ LANGUAGE plpgsql;
在logfunc1的例子中,PostgreSQL主解析器在为INSERT准备计划时就知道字符串'now'应被解释为timestamp,因为logtable的目标列就是该类型。这样,它会在此时为此生成一个常量,而该常量值随后在该会话的整个生命周期内logfunc1的所有调用中使用。毫无疑问,这并不是程序员想要的结果。
在logfunc2的例子中,PostgreSQL主解析器不知道'now'应转换成什么类型,因此它返回一个包含字符串now的text类型数据值。在随后向局部变量curtime赋值时,PL/pgSQL解释器通过调用text_out和timestamp_in函数进行转换,把这个字符串强制转换为timestamp类型。这样,计算得到的时间戳在每次执行时都会更新,正符合程序员的期望。
记录变量的可变本性在此方面带来一个问题。当记录变量的字段被用于表达式或语句中时,各字段的数据类型在同一表达式的多次调用之间不得改变,因为表达式将使用第一次到达时出现的数据类型进行规划。在编写处理多张表事件的触发器过程时,请牢记这一点。(必要时可以用EXECUTE来绕过这一问题。)
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。