本节简单介绍了PG查询语句优化部分查询重写相关的内容。查询重写在pg_rewrite_query函数中实现,该函数位于文件postgres.c中。
一、测试脚本
testdb=# CREATE VIEW vw_dwxx AS SELECT * FROM t_dwxx;
CREATE VIEW
testdb=# select dw.dwmc,gr.grbh,gr.xm
from vw_dwxx dw inner join t_grxx gr
on dw.dwbh = gr.dwbh
where dw.dwbh = '1001';
二、查询重写
可以把查询重写系统当作某种宏展开的机制。
创建视图的SQL命令:
CREATE VIEW vw_dwxx AS SELECT * FROM t_dwxx;
与下面两个命令相比没有什么不同:
CREATE TABLE vw_dwxx (same column list as t_dwxx);
CREATE RULE "_RETURN" AS ON SELECT TO vw_dwxx DO INSTEAD
SELECT * FROM t_dwxx;
查询重写前的Query结构:
重写前的Query结构
查询重写后的Query结构:
重写后的Query结构
三、源码解读
pg_rewrite_query
/*
* Perform rewriting of a query produced by parse analysis.
*
* Note: query must just have come from the parser, because we do not do
* AcquireRewriteLocks() on it.
*/
static List *
pg_rewrite_query(Query *query) //输入查询树
{
List *querytree_list;
if (Debug_print_parse) //debug模式,输出parse tree树
elog_node_display(LOG, "parse tree", query,
Debug_pretty_print);
if (log_parser_stats)
ResetUsage();
if (query->commandType == CMD_UTILITY) //工具类语句
{
/* don't rewrite utilities, just dump 'em into result list */
querytree_list = list_make1(query);
}
else//非工具类语句
{
/* rewrite regular queries */
querytree_list = QueryRewrite(query); //进入查询重写
}
if (log_parser_stats)
ShowUsage("REWRITER STATISTICS");
#ifdef COPY_PARSE_PLAN_TREES
/* Optional debugging check: pass querytree output through copyObject() */
{
List *new_list;
new_list = copyObject(querytree_list);
/* This checks both copyObject() and the equal() routines... */
if (!equal(new_list, querytree_list))
elog(WARNING, "copyObject() failed to produce equal parse tree");
else
querytree_list = new_list;
}
#endif
if (Debug_print_rewritten)
elog_node_display(LOG, "rewritten parse tree", querytree_list,
Debug_pretty_print);
return querytree_list;
}
QueryRewrite
/*
* QueryRewrite -
* Primary entry point to the query rewriter.
* Rewrite one query via query rewrite system, possibly returning 0
* or many queries.
*
* NOTE: the parsetree must either have come straight from the parser,
* or have been scanned by AcquireRewriteLocks to acquire suitable locks.
*/
List *
QueryRewrite(Query *parsetree) //输入查询树
{
uint64 input_query_id = parsetree->queryId;//查询id
List *querylist;//查询树,中间结果
List *results;//最终结果
ListCell *l;//临时变量
CmdType origCmdType;//命令类型
bool foundOriginalQuery;
Query *lastInstead;
/*
* This function is only applied to top-level original queries
*/
Assert(parsetree->querySource == QSRC_ORIGINAL);
Assert(parsetree->canSetTag);
/*
* Step 1
*
* Apply all non-SELECT rules possibly getting 0 or many queries
* 第1步,应用所有非SELECT规则(UPDATE/INSERT等),查询不需要执行
*/
querylist = RewriteQuery(parsetree, NIL);
/*
* Step 2
*
* Apply all the RIR rules on each query
*
* This is also a handy place to mark each query with the original queryId
* 第2步,应用所有RIR规则
* RIR是Retrieve-Instead-Retrieve的缩写,The name is based on history.RETRIEVE was the PostQUEL keyword
* for what you know as SELECT. A rule fired on a RETRIEVE event, that is an unconditional INSTEAD rule
* with exactly one RETRIEVE action is called RIR-rule.
*/
results = NIL;
foreach(l, querylist) //循环
{
Query *query = (Query *) lfirst(l);//获取Query
query = fireRIRrules(query, NIL);//应用RIR规则
query->queryId = input_query_id;//设置查询id
results = lappend(results, query);//加入返回结果列表中
}
/*
* Step 3
*
* 第3步,确定哪一个Query设置命令结果标签,并更新canSetTag字段
* Determine which, if any, of the resulting queries is supposed to set
* the command-result tag; and update the canSetTag fields accordingly.
*
*
* If the original query is still in the list, it sets the command tag.
* Otherwise, the last INSTEAD query of the same kind as the original is
* allowed to set the tag. (Note these rules can leave us with no query
* setting the tag. The tcop code has to cope with this by setting up a
* default tag based on the original un-rewritten query.)
*
* The Asserts verify that at most one query in the result list is marked
* canSetTag. If we aren't checking asserts, we can fall out of the loop
* as soon as we find the original query.
*/
origCmdType = parsetree->commandType;
foundOriginalQuery = false;
lastInstead = NULL;
foreach(l, results)
{
Query *query = (Query *) lfirst(l);
if (query->querySource == QSRC_ORIGINAL)
{
Assert(query->canSetTag);
Assert(!foundOriginalQuery);
foundOriginalQuery = true;
#ifndef USE_ASSERT_CHECKING
break;
#endif
}
else
{
Assert(!query->canSetTag);
if (query->commandType == origCmdType &&
(query->querySource == QSRC_INSTEAD_RULE ||
query->querySource == QSRC_QUAL_INSTEAD_RULE))
lastInstead = query;
}
}
if (!foundOriginalQuery && lastInstead != NULL)
lastInstead->canSetTag = true;
return results;
}
fireRIRrules
/*
* fireRIRrules -
* Apply all RIR rules on each rangetable entry in the given query
* 在每一个RTE上应用所有的RIR规则
*
* activeRIRs is a list of the OIDs of views we're already processing RIR
* rules for, used to detect/reject recursion.
*/
static Query *
fireRIRrules(Query *parsetree, List *activeRIRs)
{
int origResultRelation = parsetree->resultRelation;//结果Relation
int rt_index;//RTE的index
ListCell *lc;//临时变量
/*
* don't try to convert this into a foreach loop, because rtable list can
* get changed each time through...
*/
rt_index = 0;
while (rt_index < list_length(parsetree->rtable)) //循环
{
RangeTblEntry *rte;//RTE
Relation rel;//关系
List *locks;//锁列表
RuleLock *rules;//规则锁
RewriteRule *rule;//重写规则
int i;//临时比那里
++rt_index;//索引+1
rte = rt_fetch(rt_index, parsetree->rtable);//获取RTE
/*
* A subquery RTE can't have associated rules, so there's nothing to
* do to this level of the query, but we must recurse into the
* subquery to expand any rule references in it.
*/
if (rte->rtekind == RTE_SUBQUERY)//子查询
{
rte->subquery = fireRIRrules(rte->subquery, activeRIRs);//递归处理
continue;
}
/*
* Joins and other non-relation RTEs can be ignored completely.
*/
if (rte->rtekind != RTE_RELATION)//非RTE_RELATION无需处理
continue;
/*
* Always ignore RIR rules for materialized views referenced in
* queries. (This does not prevent refreshing MVs, since they aren't
* referenced in their own query definitions.)
*
* Note: in the future we might want to allow MVs to be conditionally
* expanded as if they were regular views, if they are not scannable.
* In that case this test would need to be postponed till after we've
* opened the rel, so that we could check its state.
*/
if (rte->relkind == RELKIND_MATVIEW)//物化视图类的Relation无需处理
continue;
/*
* In INSERT ... ON CONFLICT, ignore the EXCLUDED pseudo-relation;
* even if it points to a view, we needn't expand it, and should not
* because we want the RTE to remain of RTE_RELATION type. Otherwise,
* it would get changed to RTE_SUBQUERY type, which is an
* untested/unsupported situation.
*/
if (parsetree->onConflict &&
rt_index == parsetree->onConflict->exclRelIndex)//INSERT ... ON CONFLICT 无需处理
continue;
/*
* If the table is not referenced in the query, then we ignore it.
* This prevents infinite expansion loop due to new rtable entries
* inserted by expansion of a rule. A table is referenced if it is
* part of the join set (a source table), or is referenced by any Var
* nodes, or is the result table.
*/
if (rt_index != parsetree->resultRelation &&
!rangeTableEntry_used((Node *) parsetree, rt_index, 0))//相应的RTE为NULL,无需处理
continue;
/*
* Also, if this is a new result relation introduced by
* ApplyRetrieveRule, we don't want to do anything more with it.
*/
if (rt_index == parsetree->resultRelation &&
rt_index != origResultRelation)//结果关系
continue;
/*
* We can use NoLock here since either the parser or
* AcquireRewriteLocks should have locked the rel already.
*/
rel = heap_open(rte->relid, NoLock);//根据relid获取"关系"
/*
* Collect the RIR rules that we must apply
*/
rules = rel->rd_rules;//获取关系上的规则
if (rules != NULL)
{
locks = NIL;
for (i = 0; i < rules->numLocks; i++)
{
rule = rules->rules[i];//获取规则
if (rule->event != CMD_SELECT)
continue;//非SELECT类型,继续下一个规则
locks = lappend(locks, rule);//添加到列表中
}
/*
* If we found any, apply them --- but first check for recursion!
*/
if (locks != NIL)
{
ListCell *l;
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 = lfirst(l);
parsetree = ApplyRetrieveRule(parsetree,
rule,
rt_index,
rel,
activeRIRs);//应用规则
}
activeRIRs = list_delete_first(activeRIRs);//删除已应用的规则
}
}
heap_close(rel, NoLock);//释放资源
}
/* Recurse into subqueries in WITH */
foreach(lc, parsetree->cteList) //WITH 语句处理
{
CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
cte->ctequery = (Node *)
fireRIRrules((Query *) cte->ctequery, activeRIRs);
}
/*
* Recurse into sublink subqueries, too. But we already did the ones in
* the rtable and cteList.
*/
if (parsetree->hasSubLinks) //存在子链接
query_tree_walker(parsetree, fireRIRonSubLink, (void *) activeRIRs,
QTW_IGNORE_RC_SUBQUERIES);
/*
* Apply any row level security policies. We do this last because it
* requires special recursion detection if the new quals have sublink
* subqueries, and if we did it in the loop above query_tree_walker would
* then recurse into those quals a second time.
*/
rt_index = 0;
foreach(lc, parsetree->rtable)//应用行级安全策略
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
Relation rel;
List *securityQuals;
List *withCheckOptions;
bool hasRowSecurity;
bool hasSubLinks;
++rt_index;
/* Only normal relations can have RLS policies */
if (rte->rtekind != RTE_RELATION ||
(rte->relkind != RELKIND_RELATION &&
rte->relkind != RELKIND_PARTITIONED_TABLE))
continue;
rel = heap_open(rte->relid, NoLock);
/*
* Fetch any new security quals that must be applied to this RTE.
*/
get_row_security_policies(parsetree, rte, rt_index,
&securityQuals, &withCheckOptions,
&hasRowSecurity, &hasSubLinks);
if (securityQuals != NIL || withCheckOptions != NIL)
{
if (hasSubLinks)
{
acquireLocksOnSubLinks_context context;
/*
* Recursively process the new quals, checking for infinite
* recursion.
*/
if (list_member_oid(activeRIRs, RelationGetRelid(rel)))
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("infinite recursion detected in policy for relation \"%s\"",
RelationGetRelationName(rel))));
activeRIRs = lcons_oid(RelationGetRelid(rel), activeRIRs);
/*
* get_row_security_policies just passed back securityQuals
* and/or withCheckOptions, and there were SubLinks, make sure
* we lock any relations which are referenced.
*
* These locks would normally be acquired by the parser, but
* securityQuals and withCheckOptions are added post-parsing.
*/
context.for_execute = true;
(void) acquireLocksOnSubLinks((Node *) securityQuals, &context);
(void) acquireLocksOnSubLinks((Node *) withCheckOptions,
&context);
/*
* Now that we have the locks on anything added by
* get_row_security_policies, fire any RIR rules for them.
*/
expression_tree_walker((Node *) securityQuals,
fireRIRonSubLink, (void *) activeRIRs);
expression_tree_walker((Node *) withCheckOptions,
fireRIRonSubLink, (void *) activeRIRs);
activeRIRs = list_delete_first(activeRIRs);
}
/*
* Add the new security barrier quals to the start of the RTE's
* list so that they get applied before any existing barrier quals
* (which would have come from a security-barrier view, and should
* get lower priority than RLS conditions on the table itself).
*/
rte->securityQuals = list_concat(securityQuals,
rte->securityQuals);
parsetree->withCheckOptions = list_concat(withCheckOptions,
parsetree->withCheckOptions);
}
/*
* Make sure the query is marked correctly if row level security
* applies, or if the new quals had sublinks.
*/
if (hasRowSecurity)
parsetree->hasRowSecurity = true;
if (hasSubLinks)
parsetree->hasSubLinks = true;
heap_close(rel, NoLock);
}
return parsetree;//返回
}
ApplyRetrieveRule
* ApplyRetrieveRule - expand an ON SELECT rule
*/
static Query *
ApplyRetrieveRule(Query *parsetree,//查询树
RewriteRule *rule,//重写规则
int rt_index,//RTE index
Relation relation,//关系
List *activeRIRs)//RIR链表
{
Query *rule_action;
RangeTblEntry *rte,
*subrte;//RTE
RowMarkClause *rc;
if (list_length(rule->actions) != 1)
elog(ERROR, "expected just one rule action");
if (rule->qual != NULL)
elog(ERROR, "cannot handle qualified ON SELECT rule");
if (rt_index == parsetree->resultRelation) //目标关系?
{
/*
* We have a view as the result relation of the query, and it wasn't
* rewritten by any rule. This case is supported if there is an
* INSTEAD OF trigger that will trap attempts to insert/update/delete
* view rows. The executor will check that; for the moment just plow
* ahead. We have two cases:
*
* For INSERT, we needn't do anything. The unmodified RTE will serve
* fine as the result relation.
*
* For UPDATE/DELETE, we need to expand the view so as to have source
* data for the operation. But we also need an unmodified RTE to
* serve as the target. So, copy the RTE and add the copy to the
* rangetable. Note that the copy does not get added to the jointree.
* Also note that there's a hack in fireRIRrules to avoid calling this
* function again when it arrives at the copied RTE.
*/
if (parsetree->commandType == CMD_INSERT)
return parsetree;
else if (parsetree->commandType == CMD_UPDATE ||
parsetree->commandType == CMD_DELETE)
{
RangeTblEntry *newrte;
Var *var;
TargetEntry *tle;
rte = rt_fetch(rt_index, parsetree->rtable);
newrte = copyObject(rte);
parsetree->rtable = lappend(parsetree->rtable, newrte);
parsetree->resultRelation = list_length(parsetree->rtable);
/*
* There's no need to do permissions checks twice, so wipe out the
* permissions info for the original RTE (we prefer to keep the
* bits set on the result RTE).
*/
rte->requiredPerms = 0;
rte->checkAsUser = InvalidOid;
rte->selectedCols = NULL;
rte->insertedCols = NULL;
rte->updatedCols = NULL;
/*
* For the most part, Vars referencing the view should remain as
* they are, meaning that they implicitly represent OLD values.
* But in the RETURNING list if any, we want such Vars to
* represent NEW values, so change them to reference the new RTE.
*
* Since ChangeVarNodes scribbles on the tree in-place, copy the
* RETURNING list first for safety.
*/
parsetree->returningList = copyObject(parsetree->returningList);
ChangeVarNodes((Node *) parsetree->returningList, rt_index,
parsetree->resultRelation, 0);
/*
* To allow the executor to compute the original view row to pass
* to the INSTEAD OF trigger, we add a resjunk whole-row Var
* referencing the original RTE. This will later get expanded
* into a RowExpr computing all the OLD values of the view row.
*/
var = makeWholeRowVar(rte, rt_index, 0, false);
tle = makeTargetEntry((Expr *) var,
list_length(parsetree->targetList) + 1,
pstrdup("wholerow"),
true);
parsetree->targetList = lappend(parsetree->targetList, tle);
/* Now, continue with expanding the original view RTE */
}
else
elog(ERROR, "unrecognized commandType: %d",
(int) parsetree->commandType);
}
/*
* Check if there's a FOR [KEY] UPDATE/SHARE clause applying to this view.
* 检查是否有FOR UPDATE/SHARE语句
* Note: we needn't explicitly consider any such clauses appearing in
* ancestor query levels; their effects have already been pushed down to
* here by markQueryForLocking, and will be reflected in "rc".
*/
rc = get_parse_rowmark(parsetree, rt_index);
/*
* Make a modifiable copy of the view query, and acquire needed locks on
* the relations it mentions. Force at least RowShareLock for all such
* rels if there's a FOR [KEY] UPDATE/SHARE clause affecting this view.
*/
//copy规则,上锁
rule_action = copyObject(linitial(rule->actions));
AcquireRewriteLocks(rule_action, true, (rc != NULL));
/*
* If FOR [KEY] UPDATE/SHARE of view, mark all the contained tables as
* implicit FOR [KEY] UPDATE/SHARE, the same as the parser would have done
* if the view's subquery had been written out explicitly.
*/
if (rc != NULL)
markQueryForLocking(rule_action, (Node *) rule_action->jointree,
rc->strength, rc->waitPolicy, true);
/*
* Recursively expand any view references inside the view.
* 递归展开
* Note: this must happen after markQueryForLocking. That way, any UPDATE
* permission bits needed for sub-views are initially applied to their
* RTE_RELATION RTEs by markQueryForLocking, and then transferred to their
* OLD rangetable entries by the action below (in a recursive call of this
* routine).
*/
rule_action = fireRIRrules(rule_action, activeRIRs);
/*
* Now, plug the view query in as a subselect, replacing the relation's
* original RTE.
*/
rte = rt_fetch(rt_index, parsetree->rtable);//获取原RTE
rte->rtekind = RTE_SUBQUERY;//转换为子查询
rte->relid = InvalidOid;//设置为0
rte->security_barrier = RelationIsSecurityView(relation);
rte->subquery = rule_action;//子查询设置为刚才构造的Query
rte->inh = false; /* must not be set for a subquery */
/*
* We move the view's permission check data down to its rangetable. The
* checks will actually be done against the OLD entry therein.
*/
subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable);//OLD RTE仍需要检查权限
Assert(subrte->relid == relation->rd_id);
subrte->requiredPerms = rte->requiredPerms;
subrte->checkAsUser = rte->checkAsUser;
subrte->selectedCols = rte->selectedCols;
subrte->insertedCols = rte->insertedCols;
subrte->updatedCols = rte->updatedCols;
rte->requiredPerms = 0; /* no permission check on subquery itself */
rte->checkAsUser = InvalidOid;
rte->selectedCols = NULL;
rte->insertedCols = NULL;
rte->updatedCols = NULL;
return parsetree;//返回结果
}
四、跟踪分析
SQL语句:
testdb=# select dw.dwmc,gr.grbh,gr.xm
from vw_dwxx dw inner join t_grxx gr
on dw.dwbh = gr.dwbh
where dw.dwbh = '1001';
启动gdb,跟踪调试:
(gdb) b QueryRewrite
Breakpoint 1 at 0x80a85e: file rewriteHandler.c, line 3571.
(gdb) c
Continuing.
Breakpoint 1, QueryRewrite (parsetree=0x2868820) at rewriteHandler.c:3571
3571 uint64 input_query_id = parsetree->queryId;
(gdb) n
3590 querylist = RewriteQuery(parsetree, NIL);
#parsetree查询树
#rtable有3个元素(RTE),查询重写注意处理这3个RTE
(gdb) p *parsetree
$6 = {type = T_Query, commandType = CMD_SELECT, querySource = QSRC_ORIGINAL, queryId = 0, canSetTag = true,
utilityStmt = 0x0, resultRelation = 0, hasAggs = false, hasWindowFuncs = false, hasTargetSRFs = false,
hasSubLinks = false, hasDistinctOn = false, hasRecursive = false, hasModifyingCTE = false,
hasForUpdate = false, hasRowSecurity = false, cteList = 0x0, rtable = 0x2868be0, jointree = 0x2962180,
targetList = 0x2961db8, override = OVERRIDING_NOT_SET, onConflict = 0x0, returningList = 0x0,
groupClause = 0x0, groupingSets = 0x0, havingQual = 0x0, windowClause = 0x0, distinctClause = 0x0,
sortClause = 0x0, limitOffset = 0x0, limitCount = 0x0, rowMarks = 0x0, setOperations = 0x0,
constraintDeps = 0x0, withCheckOptions = 0x0, stmt_location = 0, stmt_len = 110}
(gdb) p *parsetree->rtable
$7 = {type = T_List, length = 3, head = 0x2868bc0, tail = 0x2961bb8}
(gdb)
3604 query = fireRIRrules(query, NIL);
(gdb) step
fireRIRrules (parsetree=0x2868820, activeRIRs=0x0) at rewriteHandler.c:1721
1721 int origResultRelation = parsetree->resultRelation;
(gdb) n
...
1729 rt_index = 0;
(gdb)
1730 while (rt_index < list_length(parsetree->rtable))
1741 rte = rt_fetch(rt_index, parsetree->rtable);
(gdb)
1748 if (rte->rtekind == RTE_SUBQUERY)
#第1个RTE,视图vw_dwxx
#rtekind = RTE_RELATION, relid = 16403, relkind = 118 'v'
(gdb) p *rte
$12 = {type = T_RangeTblEntry, rtekind = RTE_RELATION, relid = 16403, relkind = 118 'v', tablesample = 0x0,
subquery = 0x0, security_barrier = false, jointype = JOIN_INNER, joinaliasvars = 0x0, functions = 0x0,
funcordinality = false, tablefunc = 0x0, values_lists = 0x0, ctename = 0x0, ctelevelsup = 0,
self_reference = false, coltypes = 0x0, coltypmods = 0x0, colcollations = 0x0, enrname = 0x0,
enrtuples = 0, alias = 0x2867f08, eref = 0x2868a40, lateral = false, inh = true, inFromCl = true,
requiredPerms = 2, checkAsUser = 0, selectedCols = 0x29614e8, insertedCols = 0x0, updatedCols = 0x0,
securityQuals = 0x0}
1796 rel = heap_open(rte->relid, NoLock);
(gdb)
1801 rules = rel->rd_rules;
#关系Relation
(gdb) p *rel
$13 = {rd_node = {spcNode = 1663, dbNode = 16384, relNode = 16403}, rd_smgr = 0x0, rd_refcnt = 1,
rd_backend = -1, rd_islocaltemp = false, rd_isnailed = false, rd_isvalid = true, rd_indexvalid = 0 '\000',
rd_statvalid = false, rd_createSubid = 0, rd_newRelfilenodeSubid = 0, rd_rel = 0x7f57a6b90df8,
rd_att = 0x7f57a6b90f08, rd_id = 16403, rd_lockInfo = {lockRelId = {relId = 16403, dbId = 16384}},
rd_rules = 0x2945bc0, rd_rulescxt = 0x2947140, trigdesc = 0x0, rd_rsdesc = 0x0, rd_fkeylist = 0x0,
rd_fkeyvalid = false, rd_partkeycxt = 0x0, rd_partkey = 0x0, rd_pdcxt = 0x0, rd_partdesc = 0x0,
rd_partcheck = 0x0, rd_indexlist = 0x0, rd_oidindex = 0, rd_pkindex = 0, rd_replidindex = 0,
rd_statlist = 0x0, rd_indexattr = 0x0, rd_projindexattr = 0x0, rd_keyattr = 0x0, rd_pkattr = 0x0,
rd_idattr = 0x0, rd_projidx = 0x0, rd_pubactions = 0x0, rd_options = 0x0, rd_index = 0x0,
rd_indextuple = 0x0, rd_amhandler = 0, rd_indexcxt = 0x0, rd_amroutine = 0x0, rd_opfamily = 0x0,
rd_opcintype = 0x0, rd_support = 0x0, rd_supportinfo = 0x0, rd_indoption = 0x0, rd_indexprs = 0x0,
rd_indpred = 0x0, rd_exclops = 0x0, rd_exclprocs = 0x0, rd_exclstrats = 0x0, rd_amcache = 0x0,
rd_indcollation = 0x0, rd_fdwroutine = 0x0, rd_toastoid = 0, pgstat_info = 0x0}
#rules
(gdb) p *rel->rd_rules #rules是指向RewriteRule数组的指针,元素只有一个(numLocks)
$15 = {numLocks = 1, rules = 0x2947268}
(gdb) p *(RewriteRule *)rel->rd_rules->rules[0]
$28 = {ruleId = 16406, event = CMD_SELECT, qual = 0x0, actions = 0x2945b90, enabled = 79 'O',
isInstead = true}
#查看rules中的actions链表
(gdb) p *rel->rd_rules->rules[0]->actions
$31 = {type = T_List, length = 1, head = 0x2945b70, tail = 0x2945b70}
(gdb) p *(Node *)rel->rd_rules->rules[0]->actions->head->data.ptr_value
$32 = {type = T_Query}
(gdb) set $action=(Query *)rel->rd_rules->rules[0]->actions->head->data.ptr_value
#重写规则中的action为Query!
(gdb) p $action
$34 = (Query *) 0x29472c8
(gdb) p *$action
$35 = {type = T_Query, commandType = CMD_SELECT, querySource = QSRC_ORIGINAL, queryId = 0, canSetTag = true,
utilityStmt = 0x0, resultRelation = 0, hasAggs = false, hasWindowFuncs = false, hasTargetSRFs = false,
hasSubLinks = false, hasDistinctOn = false, hasRecursive = false, hasModifyingCTE = false,
hasForUpdate = false, hasRowSecurity = false, cteList = 0x0, rtable = 0x2947708, jointree = 0x2945820,
targetList = 0x2945990, override = OVERRIDING_NOT_SET, onConflict = 0x0, returningList = 0x0,
groupClause = 0x0, groupingSets = 0x0, havingQual = 0x0, windowClause = 0x0, distinctClause = 0x0,
sortClause = 0x0, limitOffset = 0x0, limitCount = 0x0, rowMarks = 0x0, setOperations = 0x0,
constraintDeps = 0x0, withCheckOptions = 0x0, stmt_location = -1, stmt_len = -1}
(gdb) p *$action->rtable
$36 = {type = T_List, length = 3, head = 0x29476e8, tail = 0x2945800}
(gdb) p *(Node *)$action->rtable->head->data.ptr_value
$37 = {type = T_RangeTblEntry}
(gdb) p *(RangeTblEntry *)$action->rtable->head->data.ptr_value
$38 = {type = T_RangeTblEntry, rtekind = RTE_RELATION, relid = 16403, relkind = 118 'v', tablesample = 0x0,
subquery = 0x0, security_barrier = false, jointype = JOIN_INNER, joinaliasvars = 0x0, functions = 0x0,
funcordinality = false, tablefunc = 0x0, values_lists = 0x0, ctename = 0x0, ctelevelsup = 0,
self_reference = false, coltypes = 0x0, coltypmods = 0x0, colcollations = 0x0, enrname = 0x0,
enrtuples = 0, alias = 0x29474e8, eref = 0x2947588, lateral = false, inh = false, inFromCl = false,
requiredPerms = 0, checkAsUser = 10, selectedCols = 0x0, insertedCols = 0x0, updatedCols = 0x0,
securityQuals = 0x0}
(gdb) p *((RangeTblEntry *)$action->rtable->head->data.ptr_value)->eref
$42 = {type = T_Alias, aliasname = 0x29475b8 "old", colnames = 0x2947608}
(gdb) p *((RangeTblEntry *)$action->rtable->head->next->data.ptr_value)->eref
$43 = {type = T_Alias, aliasname = 0x29478c0 "new", colnames = 0x2947930}
(gdb) p *((RangeTblEntry *)$action->rtable->head->next->next->data.ptr_value)->eref
$44 = {type = T_Alias, aliasname = 0x2945698 "t_dwxx", colnames = 0x2945708}
...
1826 activeRIRs = lcons_oid(RelationGetRelid(rel), activeRIRs);
(gdb)
1828 foreach(l, locks)
(gdb) p *activeRIRs
$46 = {type = T_OidList, length = 1, head = 0x29320c8, tail = 0x29320c8}
(gdb) p activeRIRs->head->data.oid_value
$47 = 16403
#进入ApplyRetrieveRule
(gdb) n
1830 rule = lfirst(l);
(gdb)
1832 parsetree = ApplyRetrieveRule(parsetree,
(gdb) step
ApplyRetrieveRule (parsetree=0x2868820, rule=0x2947298, rt_index=1, relation=0x7f57a6b90be8,
activeRIRs=0x29320e8) at rewriteHandler.c:1462
...
#进入ApplyRetrieveRule调用fireRIRrules
1581 rule_action = fireRIRrules(rule_action, activeRIRs);
(gdb) step
fireRIRrules (parsetree=0x2970118, activeRIRs=0x29320e8) at rewriteHandler.c:1721
1721 int origResultRelation = parsetree->resultRelation;
...
(gdb) finish #结束ApplyRetrieveRule->fireRIRrules的调用
Run till exit from #0 fireRIRrules (parsetree=0x2970118, activeRIRs=0x29320e8) at rewriteHandler.c:1788
0x00000000008079cf in ApplyRetrieveRule (parsetree=0x2868820, rule=0x2947298, rt_index=1,
relation=0x7f57a6b90be8, activeRIRs=0x29320e8) at rewriteHandler.c:1581
1581 rule_action = fireRIRrules(rule_action, activeRIRs);
Value returned is $56 = (Query *) 0x2970118
...
#重写为子查询
(gdb) n
1589 rte->rtekind = RTE_SUBQUERY;
(gdb) p *rte
$58 = {type = T_RangeTblEntry, rtekind = RTE_RELATION, relid = 16403, relkind = 118 'v', tablesample = 0x0,
subquery = 0x0, security_barrier = false, jointype = JOIN_INNER, joinaliasvars = 0x0, functions = 0x0,
funcordinality = false, tablefunc = 0x0, values_lists = 0x0, ctename = 0x0, ctelevelsup = 0,
self_reference = false, coltypes = 0x0, coltypmods = 0x0, colcollations = 0x0, enrname = 0x0,
enrtuples = 0, alias = 0x2867f08, eref = 0x2868a40, lateral = false, inh = true, inFromCl = true,
requiredPerms = 2, checkAsUser = 0, selectedCols = 0x29614e8, insertedCols = 0x0, updatedCols = 0x0,
securityQuals = 0x0}
(gdb) n
1590 rte->relid = InvalidOid;
(gdb)
1591 rte->security_barrier = RelationIsSecurityView(relation);
(gdb)
1592 rte->subquery = rule_action;
(gdb)
1593 rte->inh = false; /* must not be set for a subquery */
(gdb)
1599 subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable);
(gdb) p *rte
$59 = {type = T_RangeTblEntry, rtekind = RTE_SUBQUERY, relid = 0, relkind = 118 'v', tablesample = 0x0,
subquery = 0x2970118, security_barrier = false, jointype = JOIN_INNER, joinaliasvars = 0x0,
functions = 0x0, funcordinality = false, tablefunc = 0x0, values_lists = 0x0, ctename = 0x0,
ctelevelsup = 0, self_reference = false, coltypes = 0x0, coltypmods = 0x0, colcollations = 0x0,
enrname = 0x0, enrtuples = 0, alias = 0x2867f08, eref = 0x2868a40, lateral = false, inh = false,
inFromCl = true, requiredPerms = 2, checkAsUser = 0, selectedCols = 0x29614e8, insertedCols = 0x0,
updatedCols = 0x0, securityQuals = 0x0}
...
#结束ApplyRetrieveRule调用,返回上层的fireRIRrules
(gdb) finish
Run till exit from #0 ApplyRetrieveRule (parsetree=0x2868820, rule=0x2947298, rt_index=1,
relation=0x7f57a6b90be8, activeRIRs=0x29320e8) at rewriteHandler.c:1601
0x0000000000807fe3 in fireRIRrules (parsetree=0x2868820, activeRIRs=0x29320e8) at rewriteHandler.c:1832
1832 parsetree = ApplyRetrieveRule(parsetree,
Value returned is $60 = (Query *) 0x2868820
(gdb) n
1828 foreach(l, locks)
(gdb)
1839 activeRIRs = list_delete_first(activeRIRs);
(gdb)
1843 heap_close(rel, NoLock);
#RIR处理完毕
(gdb) p activeRIRs
$61 = (List *) 0x0
(gdb) finish
Run till exit from #0 fireRIRrules (parsetree=0x2868820, activeRIRs=0x0) at rewriteHandler.c:1843
0x000000000080a8b5 in QueryRewrite (parsetree=0x2868820) at rewriteHandler.c:3604
3604 query = fireRIRrules(query, NIL);
Value returned is $62 = (Query *) 0x2868820
(gdb) finish
Run till exit from #0 0x000000000080a8b5 in QueryRewrite (parsetree=0x2868820) at rewriteHandler.c:3604
0x000000000084c945 in pg_rewrite_query (query=0x2868820) at postgres.c:759
759 querytree_list = QueryRewrite(query);
Value returned is $63 = (List *) 0x29320e8
(gdb) c
Continuing.
#DONE!
五、数据结构
RewriteRule
/*
* RuleLock -
* all rules that apply to a particular relation. Even though we only
* have the rewrite rule system left and these are not really "locks",
* the name is kept for historical reasons.
*/
typedef struct RuleLock //rd_rules
{
int numLocks;
RewriteRule **rules;
} RuleLock;
/*
* RewriteRule -
* holds an info for a rewrite rule
*
*/
typedef struct RewriteRule
{
Oid ruleId;
CmdType event;
Node *qual;
List *actions;
char enabled;
bool isInstead;
} RewriteRule;
六、小结
1、查询重写:简单介绍了查询重写的基本概念以及改写前后的异同;
2、重写实现:通过跟踪分析,PG利用系统定义的规则进行重写,定义的规则Actions为Query结构,重写后在Query中把视图转变为子查询。
网友评论