PostgreSQL——查询重写——定义规则

2021SC@SDUSC

概述

我负责的PostgreSQL代码部分:查询的编译与执行
此篇博客分析内容:查询重写
之前的博客分析完了语义分析,Postgresql经过语义分析步骤得到了查询树。在获取查询树之后会立刻对查询树进行查询重写处理。所以接下来我会分析查询处理流程中的查询重写

查询处理完整流程

查询重写

查询重写:对语义分析后的查询树重写并生成新的查询树,以提供对规则和视图的支持
查询重写模块使用规则系统判断来进行查询树的重写,如果查询树中某个目标被定义了转换规则,则转换规则就会被用来重写查询树。
查询重写的源代码位于:src\Backend\rewrite文件夹中
查询重写的入口函数是:pg_rewrite_query

查询重写的核心----规则系统

查询重写的核心是规则系统,规则系统是由一系列的规则组成。由系统表pg_rewrite存储重写规则。
我去查了一下关于系统表pg_rewrite的资料,然后又整理了一下。pg_rewrite表的内容大致如下:

名字 类型 引用 描述
rulename name 规则名称
ev_class oid pg_class.oid 使用该规则的表名称
ev_attr int2 规则适用的属性
ev_type char 规则适用的命令类型:1=SELECT,2=UPDATE,3=INSERT,4=DELETE
is_instead bool 如果是INSTEAD规则,则为真;ALSO为假
ev_qual text 规则的条件表达式(WHERE子句)
ev_action text 规则动作的查询树(DO子句)

pg_rewrite表中一个元组代表一条规则,对于该表中的某条规则(即一条记录),在该条记录的ev_class属性表示该规则适用的表(ev_class)上执行特定的命令(ev_type)且满足规则的条件表达式(ev_equal),那么就用规则的动作(ev_action)来重写原始的查询树。

INSTEAD规则和ALSO规则
INSTEAD规则和ALSO规则通过pg_rewrite表的is_instead属性区分:为真则是 INSTEAD规则,反之则为ALSO规则。
INSTEAD:用规则中定义的动作替代原始的查询树中的对规则所在表的引用。
ALSO:原始查询和规则动作都会执行

为了能解释清楚规则系统,我用视图的创建作为例子
先给出下面规则,此规则的作用是——将对视图的查询改写成对基表的查询。

create rule view_rule
as on select
to test_view
do instead
select s.sname, p.pname
from supplier s, sells se, part p
where s.sno = se.sno and
p.pno = se.pno;

当检测到对关系 test_view 的 select 时,就会触发此规则。那么,用户写的对视图的查询(见下面引用)就会被转换成执行do里面的规则,而不是从 test_view 里选择元组。

用户写的对视图的查询
select sname
from test_view
where sname <> ‘Smith’;

实际上当检测到对关系 test_view 的 select 时,就会触发规则,将查询重写为规则里的动作(即do后面的语句)即:首先,获取在规则的动作部分给出的语句——do后(此时需要判断是INSTEAD还是ALSO)因为举例的规则是INSTEAD,所以在此我们用INSTEAD。其次,调整目标列表以便与用户查询给出的字段的数目和顺序相匹配。 最后,把用户查询的 where 子句里的条件部分追加到规则动作部分的查询的条件上。所以用户给出的对test_view的查询将会被重写为

select s.sname
from supplier s, sells se, part p
where s.sno = se.sno and
p.pno = se.pno and
s.sname <> ‘Smith’;

从视图查询的例子可以知道,PostgreSQL的视图实际上是用规则实现的,在对视图进行查询时,则会用相应的规则将对视图的查询改写成对基表的查询。

查询重写的处理操作

查询重写部分的处理操作主要包括定义规则,删除规则,利用规则进行查询重写。我将按照这三个部分逐一对每个操作进行分析

查询重写的处理操作——定义规则

在使用规则系统进行查询重写前,需要定义规则即遇到什么条件时执行什么样的规则动作。规则的定义通过CREATE RULE命令完成。

CREATE RULE 命令:
CREATE RULE 规则名 AS
ON {SELECT | INSERT | UPDATE | DELETE}
TO 表名 [WHERE 规则条件]
DO [INSTEAD] {NOTHING | 命令 | (命令, 命令…)}

定义重写规则的操作主要是由函数DefineRule实现。而CREATE RULE 命令被之前分析过的词法分析和语法分析部分首先处理,然后将分离出的相关信息存储在RuleStmt结构中,然后由查询执行模块把RuleStmt结构输入到函数DefineRule中完成规则的创建。
RuleStmt结构体

typedef struct RuleStmt
{
   
	NodeTag		type;//节点类型
	RangeVar   *relation;//规则关系
	char	   *rulename;//规则名
	Node	   *whereClause;//条件字句
	CmdType		event;	//动作类型 SELECT,UPDATE,INSERT,DELETE
	bool		instead;//是否是instead,为真则是instead,否则为also
	List	   *actions;//查询重写规则的替换的动作
	bool		replace;//创建语句中是否有OR REPLACE
} RuleStmt;

DefineRule函数

DefineRule(RuleStmt *stmt, const char *queryString)
{
   
	List	   *actions;//保存动作的链表
	Node	   *whereClause;//存放where子句对应的表达式树
	Oid			relId;//表的oid
	//首先调用transformRuleStmt对RuleStmt结构体进行处理
	transformRuleStmt(stmt, queryString, &actions, &whereClause);

	//RangeVarGetRelid函数通过调用RangeVarGetRelidExtended函数选择正确的命名空间并找到表的OID
	relId = RangeVarGetRelid(stmt->relation, AccessExclusiveLock, false);
	//调用DefineQueryRewrite函数,将已经处理好的规则,作为一个元组,插入到系统表pg_rewrite中,DefineQueryRewrite会把处理好的where子句的表达式树以及规则的动作作为其参数之一
	return DefineQueryRewrite(stmt->rulename,
							  relId,
							  whereClause,
							  stmt->event,
							  stmt->instead,
							  stmt->replace,
							  actions);
}

DefineRule调用的transformRuleStmt函数

transformRuleStmt(RuleStmt *stmt, const char *queryString,
				  List **actions, Node **whereClause)
{
   
	Relation	rel;
	ParseState *pstate;
	RangeTblEntry *oldrte;//存放old的RTE结构体
	Ra
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_47373497

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值