pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
PL/pgSQL 语句中使用的所有表达式都由服务器 常规的 SQL 执行器处理。看起来只包含常量的表达式 实际上可能需要运行时求值(例如 timestamp 类型的 'now'),因此 PL/pgSQL 解析器无法识别 NULL 关键字之外的真实常量值。所有表达式 都通过使用 SPI 管理器执行查询
SELECT expression
在内部求值。求值时,PL/pgSQL 变量标识符的 出现被替换为参数,变量的实际值通过参数数组传递给执行器。这使 SELECT 的查询计划只需预备一次,之后即可重用于后续 求值。
PostgreSQL 主解析器所做的求值对常量值的 解释有一些副作用。具体来说,下面这两个函数的行为是有差异的:
CREATE FUNCTION logfunc1(text) RETURNS timestamp AS '
DECLARE
logtxt ALIAS FOR $1;
BEGIN
INSERT INTO logtable VALUES (logtxt, ''now'');
RETURN ''now'';
END;
' LANGUAGE plpgsql;
和
CREATE FUNCTION logfunc2(text) RETURNS timestamp AS '
DECLARE
logtxt ALIAS FOR $1;
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 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。