39.1. 查询树

要了解规则系统是如何工作的,必须要知道它什么时候被调用以及它的输入和结果是什么。

规则系统位于解析器和规划器之间。它采用解析器的输出(即一个查询树)和用户定义的重写规则(也是查询树,不过带有一些额外信息),并且常见零个或者更多个查询树作为结果。因此它的输入和输出总是那些规划器自身就能产生的东西,并且因此它看到的任何东西都可以被表示成一个SQL语句。

那么什么是一个查询树?它是一个SQL语句的一种内部表示,其中用于创建它的每一个单独的部分都被独立存储。如果你设置了配置参数debug_print_parsedebug_print_rewrittendebug_print_plan,这些查询树可以被显示在服务器日志中。规则动作也被做为查询树存储在系统目录pg_rewrite中。它们没有被格式化为日志输出的形式,但是它们包含完全相同的信息。

阅读一棵未加工的查询树需要要一些经验。但是由于查询树的SQL表示形式足以用来理解规则系统,本章将不会教授如何阅读查询树。

在阅读本章中查询树的SQL表现形式时,读者需要能够知道语句被分解成了哪些部分并且能在查询树结构中标识它们。一棵查询树的部分有:

命令类型

这是一个简单的值来说明是哪一种命令(SELECTINSERTUPDATEDELETE)产生了该查询树。

范围表

范围表是被使用在该查询中的关系的列表。在一个SELECT语句中,范围表是在关键词FROM后面给出的关系。

每一个范围表项标识一个表或视图,并且说明在该查询的其他部分要以哪个名称调用它。在查询树中,范围表项被使用编号而不是名称来引用,因此在一个SQL语句中出现重复的名字也没有关系。在规则的范围表被合并以后可能会发生这种情况。本章中的例子将不会有这种情况。

结果关系

这是一个指向范围表的索引,它标识了该查询的结果应该去哪个关系。

SELECT查询没有结果关系(特殊情况SELECT INTO几乎等于CREATE TABLE后面跟上INSERT ... SELECT,并且不在这里单独讨论)。

对于INSERTUPDATEDELETE命令,结果关系是修改要进行的表(或视图!)。

目标列表

目标列表是一个表达式的列表,它定义了查询的结果。在一个SELECT的情况下,这些表达式会构建出该查询最终的输出。它们对应于关键字SELECTFROM之间的表达式(*是一个关系所有列名的缩写。解析器会把它扩展成独立的列,因此规则系统永远见不到它)。

DELETE命令不需要一个目标列表,因为它们不产生任何结果。相反,规则系统会向空的目标列表中加入一个特殊的CTID项来允许执行器找到要被删除的行(当结果关系是一个普通表时才加入CTID。如果结果关系是一个视图,会加入一个整行变量,如第 39.2.4 节所述)。

对于INSERT命令,目标列表描述了将要进入到结果关系中的新行。它由VALUES子句中的表达式或来自INSERT ... SELECTSELECT子句的表达式构成。重写处理的第一步会为那些没有被原始命令赋值但有默认值的列增加目标列项。任何剩余的列(既没有给定值也没有默认值)将被规划器用一个常量空值表达式填充。

对于UPDATE命令,目标列表描述要替换旧行的新行。在规则系统中,它只包含来自命令的SET column = expression部分的表达式。规划器将处理缺失的列,做法是为它们插入表达式,这种表达式会把旧行的值复制到新行。正如DELETE一样,规则系统会增加一个CTID或整行变量,这样执行器能够标识要被更新的旧行。

目标列表中的每一个项所包含的表达式可以是一个常量值、一个指向范围表中关系的列的变量、一个参数或一个由函数调用、常量、变量、操作符等构成的表达式树。

条件

查询的条件是一个表达式,它很像包含在目标列表项中的表达式。这个表达式的结果值是一个布尔值,它说明对最终结果行的操作(INSERTUPDATEDELETESELECT)是否应该被执行。它对应于一个SQL语句的WHERE子句。

连接树

查询的连接树展示了FROM子句的结构。对于一个SELECT ... FROM a, b, c这样的简单查询,连接树就是FROM项的一个列表,因为我们被允许以任何顺序连接它们。但是当JOIN表达式(特别是外连接)被使用时,我们必须按照连接显示的顺序来连接。在这种情况下,连接树展示了JOIN表达式的结构。与特定JOIN子句(来自ONUSING)相关的限制被存储为附加到那些连接树节点的条件表达式。我们发现把顶层WHERE表达式存储为附加到顶层连接树项的一个条件也很方便。这样实际上连接树表达了一个SELECTFROMWHERE子句。

其他

查询树的其他部分(如ORDER BY子句)在这里并不受到关注。规则系统在应用规则时会替换这里的某些项,但是这些与规则系统的基础没有什么关系。