上一篇博客:,我们主要解析了查询重写模块的入口函数 pg_rewrite_query() 在重写查询树时用到的 QueryRewrite() 函数以及不重写查询树时用到的函数 list_make1()。QueryRewrite() 的复杂程度比 list_make1() 要高,这篇博客我来解析一下 QueryRewrite() 中调用的两个重要函数 RewriteQuery() 和 fireRIRrules()。
//代码清单1
//src/gausskernel/optimizer/rewrite/rewriteHandler.cpp
static List* RewriteQuery(Query* parsetree, List* rewrite_events)
{
CmdType event = parsetree->commandType;
bool instead = false;
bool returning = false;
Query* qual_product = NULL;
List* rewritten = NIL;
ListCell* lc1 = NULL;
······
foreach (lc1, parsetree->cteList)
{
CommonTableExpr* cte = (CommonTableExpr*)lfirst(lc1);
Query* ctequery = (Query*)cte->ctequery;
List* newstuff = NIL;
AssertEreport(IsA(ctequery, Query), MOD_OPT, "");
if (ctequery->commandType == CMD_SELECT)
continue;
······
}
if (event != CMD_SELECT && event != CMD_UTILITY)
{
int result_relation;
RangeTblEntry* rt_entry = NULL;
Relation rt_entry_relation;
List* locks = NIL;
result_relation = parsetree->resultRelation;
if (result_relation == 0)
return rewritten;
······
if (!instead)
{
if (parsetree->commandType == CMD_INSERT)
{
if (qual_product != NULL)
rewritten = lcons(qual_product, rewritten);
else
rewritten = lcons(parsetree, rewritten);
}
else
{
if (qual_product != NULL)
rewritten = lappend(rewritten, qual_product);
else
rewritten = lappend(rewritten, parsetree);
}
}
······
}
······
return rewritten;
}
在前一篇博客中我们已经知道,RewriteQuery() 被 QueryRewrite() 调用用来对由 INSERT/DELETE/UPDATE 语句构造而来的查询树进行重写,具体的操作我们来看一看代码清单1中的代码。
首先是代码清单1中第12行开始的 foreach 循环,实际上是 for 循环,关于这个可以看一下我之前写的这篇博客:的代码清单4,在此之前,我们需要明白 CTE 是什么。CTE 全称为 Common Table Experssion,表示公用表表达式,是一个临时命名结果集,始终返回结果集,基本格式如下:
//代码清单3
WITH <cte_name>[(column list)] AS (<inner query defining the CTE>) <outer query: SELECT | INSERT | UPDATE | DELETE | MERGE>
这可能不太好看懂,那我举一个例子,我先创建一个表并且向里面添加一些数据:
//代码清单4
create table student(name text primary key,
sex text,
age integer,
address text);
insert into student values
("小明","男",15,"江西"),
("小黄","男",14,"四川"),
("小黑","男",18,""),
("小红","女",18,"江苏"),
("小紫","女",17,"黑龙江");
然后,我们写一个 CTE,如下:
//代码清单5
with temptable(name,age)
as (select name,age from student where sex="男")
select * from temptable;
看一下执行结果:
结果很正常,也许有读者要问为什么我用的是 sqlite 而不是 PostgreSQL,这个是小问题,在这里重要的是 SQL,而不是哪个 DBMS,我们对这个 CTE 做一下划分:
这就很清楚了,说到底 CTE 就像是创建了一个临时表,它不会被存到磁盘上,我们正是利用 WITH 子句创建了 CTE。回到这个 foreach 循环中来,我们将要重写的,是 INSERT/UPDATE/DELETE 的 WITH 子句,其实就是 CTE 嘛。如果存在,那么就重写它们。如果想更详细地了解一下其代码,那建议看一下这篇博客:。
代码清单1第41行开始的 if 判断结构用来处理 RTE,RTE 全名叫 RangeTable Entry,意即范围表,它描述了查询中出现的表,它通常出现在查询语句的 FROM 子句中,范围表中既有常规意义上的堆表,还有子查询、连接表等。
//代码清单6
//src/gausskernel/optimizer/rewrite/rewriteHandler.cpp
static Query* fireRIRrules(Query* parsetree, List* activeRIRs, bool forUpdatePushedDown)
{
int origResultRelation = parsetree->resultRelation;
int rt_index;
ListCell* lc = NULL;
rt_index = 0;
while (rt_index < list_length(parsetree->rtable))
{
RangeTblEntry* rte = NULL;
Relation rel;
List* locks = NIL;
RuleLock* rules = NULL;
RewriteRule* rule = NULL;
int i;
++rt_index;
rte = rt_fetch(rt_index, parsetree->rtable);
if (rte->rtekind == RTE_SUBQUERY)
{
rte->subquery = fireRIRrules(rte->subquery, activeRIRs, (forUpdatePushedDown || get_parse_rowmark(parsetree, rt_index) != NULL));
continue;
}
if (rte->rtekind != RTE_RELATION) {
continue;
}
if (rte->relkind == RELKIND_MATVIEW) {
continue;
}
······
rel = heap_open(rte->relid, NoLock);
rules = rel->rd_rules;
if (rules == NULL) {
heap_close(rel, NoLock);
continue;
}
for (i = 0; i < rules->numLocks; i++) {
rule = rules->rules[i];
if (rule->event != CMD_SELECT) {
continue;
}
if (rule->attrno > 0) {
/* per-attr rule; do we need it? */
if (!attribute_used((Node*)parsetree, rt_index, rule->attrno, 0))
continue;
}
locks = lappend(locks, rule);
}
if (locks != NIL) {
ListCell* l = NULL;
if (list_member_oid(activeRIRs, RelationGetRelid(rel))) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg(
"infinite recursion detected in rules for relation \"%s\"", RelationGetRelationName(rel))));
}
activeRIRs = lcons_oid(RelationGetRelid(rel), activeRIRs);
foreach (l, locks) {
rule = (RewriteRule*)lfirst(l);
parsetree = ApplyRetrieveRule(parsetree, rule, rt_index, rule->attrno == -1, rel, activeRIRs, forUpdatePushedDown);
}
activeRIRs = list_delete_first(activeRIRs);
}
heap_close(rel, NoLock);
}
foreach (lc, parsetree->cteList) {
CommonTableExpr* cte = (CommonTableExpr*)lfirst(lc);
cte->ctequery = (Node*)fireRIRrules((Query*)cte->ctequery, activeRIRs, false);
}
if (parsetree->hasSubLinks) {
(void)query_tree_walker(parsetree, (bool (*)())fireRIRonSubLink, (void*)activeRIRs, QTW_IGNORE_RC_SUBQUERIES);
}
······
return parsetree;
}
如果得到的范围表是一个子查询,则递归调用 fireRIRrules() 函数,对它进行重写;如果不是一张表,则直接跳过该 while 循环剩余部分;如果是一个物化视图,则同样直接跳过。
利用第36行的 heap_open() 来打开范围表,即根据 relid 获取"关系",并且获取关系上我们需要的规则。如果规则不为空,那么我们挑出这些规则中的 SELECT 规则来,之后应用这些规则去重写查询树,待这些工作都做完后,利用 table_close() 释放资源。
然后,就是调用 fireRIRrules() 对 WITH 子句进行重写操作,之后,如果查询树还有子链接(子查询),则调用 query_tree_walker() 函数对整个查询树的子链接进行遍历,并使用 fireRIRonSubLink() 函数进行重写处理,而 fireRIRonSubLink() 函数实际上也是调用 fireRIRrules() 函数来完成重写工作。
总地来说,RewriteQuery() 和 fireRIRrules() 还是有着很明显的界限,对于可重写的非功能性语句即查询语句,有 SELECT/DELETE/UPDATE/INSERT 四种,而 RewriteQuery() 用来处理由 INSERT/DELETE/UPDATE 语句构造而来的查询树,fireRIRrules() 则用来处理由 SELECT 语句构造而来的查询树,这便是二者的最大区别。
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- 69lv.com 版权所有 湘ICP备2023021910号-1
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务