规则系统的工作机制及查询树解析
一、规则系统的核心定位与工作流程
要深入理解规则系统的运行逻辑,核心在于明确其调用时机、输入参数及输出结果。规则系统在数据库架构中处于解析器与规划器之间,承担着查询重写的关键职责,其工作流程具有明确的输入输出边界。
规则系统的输入包含两部分:
- 解析器输出的查询树;
- 用户定义的重写规则(本质上也是查询树,仅附加少量额外描述信息)。
其输出为零个或多个查询树,这一特性使得规则系统的输入输出与规划器的处理对象完全兼容——所有经过规则系统的内容,均可转化为对应的SQL语句,确保了整个数据库执行链路的连贯性。
二、查询树的定义与呈现方式
查询树是SQL语句的内部表示形式,SQL语句的每一个组成部分都会被独立存储在查询树中,形成结构化的层级关系。
2.1 查询树的查看方式
通过配置数据库参数,可在服务器日志中查看查询树的具体内容:设置debug_print_parse、debug_print_rewritten或debug_print_plan参数后,解析阶段、重写阶段及规划阶段的查询树将分别输出至日志。
2.2 查询树的存储形式
规则动作对应的查询树存储于系统目录pg_rewrite中,其存储格式虽与日志输出的格式化形式不同,但包含的信息完全一致。
说明:直接阅读原始查询树需具备一定的实践经验,而查询树的SQL表示形式已足够支撑对规则系统的理解,本文不涉及原始查询树的阅读方法,仅聚焦于其SQL表现形式及核心组成部分。
三、查询树的核心组成部分
3.1 命令类型
命令类型是一个基础标识值,用于明确生成该查询树的SQL命令类型,具体包括SELECT、INSERT、UPDATE、DELETE四类,直接决定了查询树的处理方向和后续执行逻辑。
3.2 范围表
范围表是查询中涉及的所有关系(表、视图等)的集合列表。对于SELECT语句,范围表对应于FROM关键字后指定的所有关系。
每个范围表项均包含两层核心信息:
- 标识对应的表或视图;
- 定义该关系在查询其他部分中的引用名称。
在查询树内部,范围表项通过编号而非名称进行引用,这一设计可有效避免SQL语句中出现重复名称导致的冲突(此类冲突可能在规则范围表合并后产生),确保了查询树对复杂命名场景的兼容性。
3.3 结果关系
结果关系是指向范围表的索引,用于指定查询结果的存储或作用目标关系。不同类型的SQL命令对结果关系的需求存在差异:
SELECT查询无结果关系(特殊场景SELECT INTO本质等价于CREATE TABLE与INSERT ... SELECT的组合,本文不单独讨论);INSERT、UPDATE、DELETE命令的结果关系为待修改的表或视图,是命令执行的核心目标对象。
3.4 目标列表
目标列表是一组表达式的集合,用于定义查询的输出结果,其构成和作用随命令类型变化而不同:
| 命令类型 | 目标列表的核心作用 | 补充说明 |
|---|---|---|
SELECT | 对应SELECT与FROM之间的表达式(解析器会将通配符*扩展为具体列名,规则系统不直接处理*),构建查询最终输出 | - |
DELETE | 无需目标列表(无输出结果);规划器自动添加CTID项(普通表)或整行变量(视图),用于定位待删除行 | CTID为系统列,存储行物理位置 |
INSERT | 描述待插入的新行数据,由VALUES子句或INSERT ... SELECT中的SELECT子句表达式构成 | 重写阶段补充有默认值的未赋值列,规划阶段填充无值无默认值列的空值 |
UPDATE | 描述替换旧行的新行数据,仅包含SET column = expression中的表达式 | 规划阶段补充缺失列(复制旧行值),并添加CTID/整行变量定位旧行 |
目标列表中每个表达式的构成形式灵活,可是常量值、范围表关系的列变量、参数,也可是由函数调用、常量、变量、操作符组合形成的表达式树。
3.5 条件
查询条件是一个布尔型表达式,其构成逻辑与目标列表表达式一致,核心作用是判断是否对最终结果行执行对应操作(SELECT/INSERT/UPDATE/DELETE),对应SQL语句中的WHERE子句。
条件表达式的结果直接决定了行级操作的执行与否,是查询过滤的核心依据。
3.6 连接树
连接树用于描述FROM子句的结构,适配不同复杂度的查询场景:
- 简单查询(如
SELECT ... FROM a, b, c):连接树为FROM项的无序列表,允许按任意顺序执行连接操作; - 含
JOIN表达式的查询(尤其是外连接):连接树需严格遵循JOIN的显式顺序,呈现层级结构。
与特定JOIN子句(ON/USING)关联的约束条件,会作为附加条件表达式存储在对应连接树节点中。顶层WHERE表达式也会被存储为顶层连接树项的附加条件,因此连接树本质上整合了SELECT语句的FROM子句与WHERE子句的核心逻辑。
3.7 其他部分
查询树还包含ORDER BY子句等其他组成部分,此类内容虽会在规则系统应用过程中被部分替换,但与规则系统的核心工作机制关联较弱,本文不再展开。
四、PostgreSQL视图与规则系统的实践应用
4.1 SELECT规则的工作机制
ON SELECT规则是所有查询的最后一步应用规则,无论原始命令类型,且会就地修改查询树而非新建(与其他规则语义不同)。
目前ON SELECT规则仅支持单个无条件INSTEAD SELECT动作,该限制保障了规则安全性,使其行为贴合视图特性。
4.1.1 基础数据表创建
示例基于鞋店数据场景,需创建3张基础表(存储鞋子、鞋带及单位转换信息),创建语句如下:
-- 鞋子信息表
CREATE TABLE shoe_data (
shoename text, -- 主键
sh_avail integer, -- 可用的双数
slcolor text, -- 首选的鞋带颜色
slminlen real, -- 最小鞋带长度
slmaxlen real, -- 最大鞋带长度
slunit text -- 长度单位
);
-- 鞋带信息表
CREATE TABLE shoelace_data (
sl_name text, -- 主键
sl_avail integer, -- 可用的双数
sl_color text, -- 鞋带颜色
sl_len real, -- 鞋带长度
sl_unit text -- 长度单位
);
-- 单位转换表
CREATE TABLE unit (
un_name text, -- 主键
un_fact real -- 转换到厘米的参数
);
4.1.2 视图创建
基于基础表创建3个嵌套视图,实现数据关联与单位转换,语句如下:
-- 鞋子视图(单位转换)
CREATE VIEW shoe AS
SELECT sh.shoename,
sh.sh_avail,
sh.slcolor,
sh.slminlen,
sh.slminlen * un.un_fact AS slminlen_cm,
sh.slmaxlen,
sh.slmaxlen * un.un_fact AS slmaxlen_cm,
sh.slunit
FROM shoe_data sh, unit un
WHERE sh.slunit = un.un_name;
-- 鞋带视图(单位转换)
CREATE VIEW shoelace AS
SELECT s.sl_name,
s.sl_avail,
s.sl_color,
s.sl_len,
s.sl_unit,
s.sl_len * u.un_fact AS sl_len_cm
FROM shoelace_data s, unit u
WHERE s.sl_unit = u.un_name;
-- 鞋子-鞋带匹配视图
CREATE VIEW shoe_ready AS
SELECT rsh.shoename,
rsh.sh_avail,
rsl.sl_name,
rsl.sl_avail,
least(rsh.sh_avail, rsl.sl_avail) AS total_avail
FROM shoe rsh, shoelace rsl
WHERE rsl.sl_color = rsh.slcolor
AND rsl.sl_len_cm >= rsh.slminlen_cm
AND rsl.sl_len_cm <= rsh.slmaxlen_cm;
说明:CREATE VIEW会同步创建视图关系及pg_rewrite目录项(记录重写规则,引用视图时触发);该规则为无条件INSTEAD规则,动作对应视图创建时的SELECT查询树;pg_rewrite项中的NEW/OLD范围表项对SELECT规则无影响。
4.1.3 数据插入与查询演示
插入测试数据后,查询视图验证规则作用:
-- 插入单位转换数据
INSERT INTO unit VALUES ('cm', 1.0), ('m', 100.0), ('inch', 2.54);
-- 插入鞋子数据
INSERT INTO shoe_data VALUES
('sh1', 2, 'black', 70.0, 90.0, 'cm'),
('sh2', 0, 'black', 30.0, 40.0, 'inch'),
('sh3', 4, 'brown', 50.0, 65.0, 'cm'),
('sh4', 3, 'brown', 40.0, 50.0, 'inch');
-- 插入鞋带数据
INSERT INTO shoelace_data VALUES
('sl1', 5, 'black', 80.0, 'cm'),
('sl2', 6, 'black', 100.0, 'cm'),
('sl3', 0, 'black', 35.0 , 'inch'),
('sl4', 8, 'black', 40.0 , 'inch'),
('sl5', 4, 'brown', 1.0 , 'm'),
('sl6', 0, 'brown', 0.9 , 'm'),
('sl7', 7, 'brown', 60 , 'cm'),
('sl8', 1, 'brown', 40 , 'inch');
-- 查询shoelace视图
SELECT * FROM shoelace;
查询结果(标准化表格展示):
| sl_name | sl_avail | sl_color | sl_len | sl_unit | sl_len_cm |
|---|---|---|---|---|---|
| sl1 | 5 | black | 80 | cm | 80.0 |
| sl2 | 6 | black | 100 | cm | 100.0 |
| sl3 | 0 | black | 35 | inch | 88.9 |
| sl4 | 8 | black | 40 | inch | 101.6 |
| sl5 | 4 | brown | 1.0 | m | 100.0 |
| sl6 | 0 | brown | 0.9 | m | 90.0 |
| sl7 | 7 | brown | 60 | cm | 60.0 |
| sl8 | 1 | brown | 40 | inch | 101.6 |
4.1.4 SELECT规则的作用过程
以SELECT * FROM shoelace为例,规则系统的处理流程如下:
-
解析生成原始查询树:解析器将语句解析为含目标列与
shoelace视图范围表的查询树:-
SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace shoelace;
-
-
匹配重写规则:规则系统遍历范围表,匹配到
shoelace视图的_RETURN规则(动作对应视图创建时的SELECT查询树); -
重写查询树:重写器用规则动作的子查询替换原始视图引用,重写后:
-
SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM (SELECT s.sl_name, s.sl_avail, s.sl_color, s.sl_len, s.sl_unit, s.sl_len * u.un_fact AS sl_len_cm FROM shoelace_data s, unit u WHERE s.sl_unit = u.un_name) shoelace; - 注:子查询范围表含
shoelace old/new项,用于存储视图访问权限信息,确保执行器验证权限。
-
-
递归检查:规则系统递归检查顶层及子查询范围表,确认无其他视图引用后,将最终查询树提交给规划器。
4.1.5 多视图嵌套查询的规则应用
查询嵌套视图(如shoe_ready)时,规则系统会递归应用重写规则。例如执行:
SELECT * FROM shoe_ready WHERE total_avail >= 2;
4.1.5.1 子查询提升示例
规则重写后生成三层嵌套SQL(层级清晰但执行效率低),规划器通过“子查询提升”优化为单层SQL(便于优化连接路径):
提升前(三层嵌套SQL)
SELECT shoe_ready.shoename,
shoe_ready.sh_avail,
shoe_ready.sl_name,
shoe_ready.sl_avail,
shoe_ready.total_avail
FROM (-- shoe_ready视图子查询
SELECT rsh.shoename,
rsh.sh_avail,
rsl.sl_name,
rsl.sl_avail,
least(rsh.sh_avail, rsl.sl_avail) AS total_avail
FROM (-- shoe视图子查询
SELECT sh.shoename,
sh.sh_avail,
sh.slcolor,
sh.slminlen * un.un_fact AS slminlen_cm,
sh.slmaxlen * un.un_fact AS slmaxlen_cm
FROM shoe_data sh, unit un
WHERE sh.slunit = un.un_name) rsh,
(-- shoelace视图子查询
SELECT s.sl_name,
s.sl_avail,
s.sl_color,
s.sl_len * u.un_fact AS sl_len_cm
FROM shoelace_data s, unit u
WHERE s.sl_unit = u.un_name) rsl
WHERE rsl.sl_color = rsh.slcolor
AND rsl.sl_len_cm >= rsh.slminlen_cm
AND rsl.sl_len_cm <= rsh.slmaxlen_cm) shoe_ready
WHERE shoe_ready.total_avail >= 2;
提升后(单层合并SQL)
SELECT sh.shoename,
sh.sh_avail,
s.sl_name,
s.sl_avail,
least(sh.sh_avail, s.sl_avail) AS total_avail
FROM shoe_data sh, unit un1, shoelace_data s, unit un2
WHERE sh.slunit = un1.un_name -- shoe视图关联条件
AND s.sl_unit = un2.un_name -- shoelace视图关联条件
AND s.sl_color = sh.slcolor -- shoe_ready视图关联条件
AND (s.sl_len * un2.un_fact) >= (sh.slminlen * un1.un_fact)
AND (s.sl_len * un2.un_fact) <= (sh.slmaxlen * un1.un_fact)
AND least(sh.sh_avail, s.sl_avail) >= 2; -- 顶层过滤条件
4.1.5.2 子查询提升的核心逻辑
- 表合并:提取各层基础表至顶层
FROM,用别名(如un1/un2)区分重复表引用,避免冲突; - 条件整合:合并各层关联条件、过滤条件至顶层
WHERE,形成完整逻辑; - 表达式简化:直接代入子查询计算表达式,消除冗余别名映射。
该优化使规划器可全局评估表连接顺序与过滤优先级,避免嵌套导致的局部优化局限,生成高效执行计划。
4.2 非SELECT语句中的视图规则
SELECT与非SELECT命令(INSERT/UPDATE/DELETE)的查询树核心结构一致,仅命令类型及结果关系存在差异——非SELECT命令的结果关系指向目标关系项。
4.2.1 示例对比
以表t1(a, b)、t2(a, b)为例,两条语句的查询树结构高度相似:
-- SELECT语句
SELECT t2.b FROM t1, t2 WHERE t1.a = t2.a;
-- UPDATE语句
UPDATE t1 SET b = t2.b FROM t2 WHERE t1.a = t2.a;
规划器为UPDATE补充缺失列(复制旧行值)及CTID项,用于定位待更新行,等效于:
SELECT t1.a, t2.b, t1.ctid FROM t1, t2 WHERE t1.a = t2.a;
4.2.2 执行器处理逻辑
执行器通过CTID定位旧行,插入新行后标记旧行为失效,事务提交后由清理器移除(这是PostgreSQL快速回滚的核心原因)。
规则系统对非SELECT命令的视图处理逻辑,与SELECT完全一致。