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