要了解规则系统的运行方式,需要了解它的调用时间以及它的输入和输出是什么。
规则系统位于解析器和规划器之间。它将解析器的输出——一个查询树——以及用户定义的重写规则(也是一些附加了附加信息查询树)作为输入,然后创建零个或多个查询树作为结果。因此,其输入和输出始终是解析器本身可能已经生成的内容,因而其所看到的内容基本上都可以表示为一个SQL语句。
现在,什么是查询树?它是SQL语句的内部表示,并会单独存储由其生成的不同部分。可以在服务器日志中显示这些查询树,方法是设置配置参数 debug_print_parse
、debug_print_rewritten
或 debug_print_plan
。规则动作也会存储为查询树,存储在系统目录 pg_rewrite
中。它们的格式没有日志输出的格式,但它们包含完全相同的信息。
阅读原始查询树需要一些经验。但是,由于SQL查询树的表示形式对于了解规则系统已经足够,因此本章不会指导如何阅读它们。
阅读本章中的SQL查询树的表示形式时,有必要能够识别当语句在查询树结构中时所分解的部分。查询树的部分有
此值十分简单,说明哪个命令(SELECT
、INSERT
、UPDATE
、DELETE
)生成了查询树。
范围表是查询中使用的关系的列表。在 SELECT
语句中,这些是 FROM
关键字后所给的关系。
每个范围表条目都会标识一个表或视图,并说明在查询的其他部分中以哪个名称调用它。在查询树中,范围表条目是按编号而非名称引用的,因此,与SQL语句中一样,这里没有重复名称也没有关系。这种情况可能会发生在已经合并规则的范围表之后。本章中的示例不会出现这种情况。
这是范围表中的索引,用于识别查询结果存放到的关系。
SELECT
查询没有结果关系。(SELECT INTO
的特例基本上与 CREATE TABLE
后接 INSERT ... SELECT
相同,并且此处未单独讨论它。)
对于 INSERT
、UPDATE
和 DELETE
命令,结果关系是变更将生效的表(或视图!)。
目标列表是一个表达式列表,它定义查询结果。对于 SELECT
,这些表达式是构建查询最终输出的表达式。它们对应于关键字 SELECT
和 FROM
之间的表达式。(*
只是关系所有列名的缩写。它被解析器扩展为各个列,因此规则系统永远不会看到它。)
DELETE
命令不需要普通目标列表,因为它们不会产生任何结果。取而代之的是,计划器向空目标列表添加一个特殊CTID项,以便执行器找到要删除的行。(CTID在结果关系是一个普通表时添加。如果它是一个视图,则由规则系统添加一个全行变量,如 39.2.4 节所述。)
对于 INSERT
命令,目标列表描述应该进入结果关系的新行。它包括 VALUES
子句中的表达式或 INSERT ... SELECT
中 SELECT
子句中的表达式。重写过程的第一步是为任何未由原始命令分配但具有默认值的列添加目标列表项。计划器将为任何剩余的列(既没有给定值也没有默认值)填充一个常量 null 表达式。
对于 UPDATE
命令,目标列表描述应该替换旧行的新行。在规则系统中,它只包含命令的 SET column = expression
部分的表达式。计划器将通过插入从旧行中将值复制到新行的表达式来处理缺少的列。就像 DELETE
一样,CTID添加全行变量,以便执行器可以识别要更新的旧行。
目标列表中的每个条目都包含一个表达式,该表达式可以是一个常量值、一个指向范围表中某一关系列的变量、一个参数或由函数调用、常量、变量、运算符等构成的表达式树。
查询限定符是一个与目标列表项中所包含的表达式类似的表达式。此表达式的结果值是一个布尔值,用于说明是否执行最终结果行 INSERT
、UPDATE
、DELETE
或 SELECT
操作。它对应于SQL语句。
WHERE
子句查询的联接树显示了 FROM
子句的结构。对于 SELECT ... FROM a, b, c
这样的简单查询,联接树只是 FROM
项的列表,因为我们允许以任意顺序联接它们。但当使用 JOIN
表达式(尤其是外部联接)时,我们必须按照联接所示的顺序来联接。在这种情况下,联接树显示 JOIN
表达式的结构。与特定 JOIN
子句(来自 ON
或 USING
表达式)关联的限制作为限定符表达式存储,并附加到这些联接树节点。将顶级 WHERE
表达式作为限定符存储并附加到顶级联接树项,这很方便。所以联接树实际上既代表 SELECT
的 FROM
子句,也代表其 WHERE
子句。
查询树的其他部分,如 ORDER BY
子句,在此不属于讨论重点。规则系统在应用规则时会替换那里的部分条目,但这与规则系统基础无关。