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 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。