本章讨论 PostgreSQL 里的规则系统。 生产规则系统的概念是很简单的,但是在实际使用的时候会碰到很多细微的问题。
有些其它的数据库系统定义动态的数据库规则。这些通常是存储过程和触发器, 在 PostgreSQL 里,这些东西也可以通过函数和触发器来实现。
规则系统,(更准确地说,查询重写规则系统)是和存储过程和触发器完全不同的东西。 它把查询修改为需要考虑规则的形式,然后把修改过的查询传递给查询规划器执行。 这是非常有效的工具并且可以用于许多象查询语言过程,视图,和版本等。 这个规则系统的理论基础和能力在 有关数据库系统的规则,过程,缓存和视图 和 A Unified Framework for Version Modeling Using Production Rules in a Database System 里有讨论。
要理解规则系统如何工作,首先要知道规则何时被激发以及它的输入和结果是什么。
规则系统位于分析器和规划器之间。以分析器的输出 -- 一个查询树, 以及用户定义的重写规则作为输入, 重写规则也是一个查询树,只不过增加了一些扩展信息, 然后创建零个或者多个查询树作为结果。 所以它的输入和输出仍然是那些分析器可以生成的东西, 因而任何它(规则系统)看到的东西都是可以用 SQL 语句表达的。
那么什么是查询树呢? 它是一个 SQL 语句的内部表现形式, 这时组成该语句的每个独立地部分都是分别存储的。 如果你设置了配置参数 debug_print_parse, debug_print_rewritten,或 debug_print_plan, 那么你可以在服务器日志中看到这些查询树。 规则动作也是以查询树的方式存储的,存放在系统表pg_rewirte 里面。不过不是用象调试输出那样的格式,但包含的内容是完全一样的。
阅读一个裸查询树需要一定的经验, 但是因为理解查询树的 SQL 表现就足以理解规则系统, 所以这份文档将不会告诉你如何读取它们。
当我们读取本章中查询树的 SQL 表现时, 我们必须能够识别该语句被分解后放在查询树里的成员。 查询树的成员有
这是一个简单的值,说明哪条命令 (SELECT,INSERT,UPDATE,DELETE) 生成这个查询树。
范围表是一个查询中使用的关系的列表。 在SELECT语句里是在FORM关键字后面给出的关系。
每个范围表表示一个表或一个视图,表明是查询里哪个成员调用了它。 在查询树里,范围表是用代号而不是用名字引用的, 所以这里不用象在 SQL语句里一样关心是否有重名问题。 这种情况在引入了规则的范围表后可能会发生。 本章的例子将不讨论这种情况。
这是一个范围表的索引,用于标识查询结果前往的表。
SELECT查询通常没有结果关系表。特例SELECT INTO 几乎等于一个 CREATE TABLE, INSERT ... SELECT序列,所以这里我们就不单独讨论了。
在INSERT,UPDATE和DELETE命令里, 结果关系是更改发生影响的表(或视图!)。
目标列是一列定义查询结果的表达式。在SELECT的情况下, 这些表达式就是构建查询的最终输出的东西。 它们是位于SELECT和FROM关键字之间的表达式 (* 只是表明一个关系的所有字段的缩写,它被分析器扩展为独立的字段, 因此规则系统永远看不到它)。
DELETE 不需要目标列是因为它们不产生任何结果。 实际上规划器会向空目的标列中增加一条特殊的CTID记录。 但这是在规则系统之后并且我们将稍后讨论;对于规则系统而言,目标列是空的。
对于INSERT命令里面,目标列描述了应该进入结果关系的新行。 这些由那些在VALUES子句里的表达式或在 INSERT ... SELECT 语句里的 SELECT 子句里面的表达式构成。 重写过程的第一步就是为任何不是由原始的查询赋值,并且有缺省值的字段增加目标列表项。 任何其它的字段(既无给出值,也无缺省的)将由规划器自动赋予一个常量 NULL 表达式。
对于UPDATE命令,它(目标列)描述应该替换旧行的新行。 在规则系统里,它只包含来自查询的 SET attribute = expression部分抽取的表达式。 这时,规划器将通过插入从旧行抽取数据到新行的表达式的方法处理缺失的字段。 并且它也会象 在DELETE里那样增加特殊的CTID记录。
目标列里的每个元素都包含着一个表达式, 它可以为常量值,可以为一个指向某个范围表里面的关系的一个字段的变量, 可以为一个由函数调用,常量,变量,操作符等构成的表达式树。
查询条件是一个表达式,它非常类似那些包含在目标列里的条目。 这个表达式的值是一个布尔值,通过此值来判断对最终结果行是否要执操作 (INSERT,UPDATE,DELETE 或 SELECT)。 它是一个SQL 语句 的WHERE子句。
查询的连接树显示了FROM 子句的结构。 对于象SELECT FROM a, b, c这样的简单查询, 连接树只是一个FROM项的简单列表, 因为我们允许以任意顺序连接它们。 但如果使用了 JOIN 表达式 — 尤其是 outer join 的时候, 我们就必须按照该连接显示的顺序进行连接。 连接树显示 JOIN 表达式的结构。 与特定的 JOIN 子句(来自 ON 或者 USING 表达式)相关的限制做为附加在那些连接树节点的条件表达式存储。 事实证明把顶层 WHERE 表达式也当做附加在顶层连接树项的条件来存储是非常方便的。所以实际上连接树代表SELECT语句的 FROM 和 WHERE 子句。
查询树的其他部分,像ORDER BY 子句,我们不准备在这里讨论。 规则系统在附加规则时将在那里(ORDER BY 子句)替换一些条目, 但是这对于规则系统的基本原理并没有多大关系。