PostgreSQL源码分析——视图查询重写

这里我们分析一下查询重写的过程,主要分析视图的查询重写的过程。通过以下语句进行分析:

sql 复制代码
create table t1(a int, b int);
insert into t1 values(1,1);
-- 创建视图
create view vt1 as select * from t1;
-- 查询
select * from vt1;
查询重写过程分析

select * from vt1生成一个抽象语法树SelectStmt。其中视图名vt1通过RangeVar节点表示。再由SelectStmt经由语义分析转为查询树Query

语法解析部分的源码主流程如下:

c 复制代码
exec_simple_query
--> pg_parse_query
    --> raw_parser
        --> base_yyparse
--> pg_analyze_and_rewrite
    --> parse_analyze
        --> transformStmt
            --> transformFromClause
                --> transformFromClauseItem
    --> pg_rewrite_query
        --> QueryRewrite   // 查询重写,视图重写为子查询是在这里发生的。
            // Step 1: Apply all non-SELECT rules possibly getting 0 or many queries
            --> RewriteQuery
            // Step 2: Apply all the RIR rules on each query
            --> fireRIRrules
                --> ApplyRetrieveRule  // expand an ON SELECT rule
                    --> fireRIRrules    // Recursively expand any view references inside the view.
            /* Step 3: Determine which, if any, of the resulting queries is supposed to set
	                   the command-result tag; and update the canSetTag fields accordingly. */
--> pg_plan_queries

视图定义的规则展开的关键函数为ApplyRetrieveRule,我们重点分析一下:

c 复制代码
/* ApplyRetrieveRule - expand an ON SELECT rule*/
static Query *ApplyRetrieveRule(Query *parsetree, RewriteRule *rule, int rt_index, Relation relation, List *activeRIRs)
{
	Query	   *rule_action;
	RangeTblEntry *rte, *subrte;
	RowMarkClause *rc;

    // ...

	/*
	 * 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.
	 */
	rule_action = copyObject(linitial(rule->actions));

	AcquireRewriteLocks(rule_action, true, (rc != NULL));

	/*
	 * 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, converting the relation's
	 * original RTE to a subquery RTE.
	 */
	rte = rt_fetch(rt_index, parsetree->rtable);

	rte->rtekind = RTE_SUBQUERY;  // 视图会通过子查询进行替换,对应的就是由RTE_SUBQUERY替换掉原先的RTE_RELATION
	rte->subquery = rule_action;
	rte->security_barrier = RelationIsSecurityView(relation);
	/* Clear fields that should not be set in a subquery RTE */
	rte->relid = InvalidOid;
	rte->relkind = 0;
	rte->rellockmode = 0;
	rte->tablesample = NULL;
	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);
	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;
	subrte->extraUpdatedCols = rte->extraUpdatedCols;

    // ...
	return parsetree;
}

可以看到,最核心的部分就是将RangeTblEntry由视图转换为子查询。

分析完源码,我们再回过头来研究一下其过程:

执行创建视图命令后create view vt1 as select * from t1;,向pg_rewrite系统表中插入了一套数据:

sql 复制代码
postgres@postgres=# select * from pg_rewrite order by oid desc limit 1;
oid        | 16390
rulename   | _RETURN
ev_class   | 16387   -- 视图vt1的表oid
ev_type    | 1
ev_enabled | O
is_instead | t       -- instead规则
ev_qual    | <>
ev_action  | ({QUERY :commandType 1 :querySource 0 :canSetTag true :utilityStmt <> :resultRelation 0 :hasAggs false :hasWindowFuncs false :hasTargetSRFs false :hasSubLinks false :hasDistinctOn false :hasRecursive false :hasModifyingCTE false :hasForUpdate false :hasRowSecurity false :cteList <> :rtable ({RTE :alias {ALIAS :aliasname old :colnames <>} :eref {ALIAS :aliasname old :colnames ("a" "b")} :rtekind 0 :relid 16387 :relkind v :rellockmode 1 :tablesample <> :lateral false :inh false :inFromCl false :requiredPerms 0 :checkAsUser 0 :selectedCols (b) :insertedCols (b) :updatedCols (b) :extraUpdatedCols (b) :securityQuals <>} {RTE :alias {ALIAS :aliasname new :colnames <>} :eref {ALIAS :aliasname new :colnames ("a" "b")} :rtekind 0 :relid 16387 :relkind v :rellockmode 1 :tablesample <> :lateral false :inh false :inFromCl false :requiredPerms 0 :checkAsUser 0 :selectedCols (b) :insertedCols (b) :updatedCols (b) :extraUpdatedCols (b) :securityQuals <>} {RTE :alias <> :eref {ALIAS :aliasname t1 :colnames ("a" "b")} :rtekind 0 :relid 16384 :relkind r :rellockmode 1 :tablesample <> :lateral false :inh true :inFromCl true :requiredPerms 2 :checkAsUser 0 :selectedCols (b 8 9) :insertedCols (b) :updatedCols (b) :extraUpdatedCols (b) :securityQuals <>}) :jointree {FROMEXPR :fromlist ({RANGETBLREF :rtindex 3}) :quals <>} :targetList ({TARGETENTRY :expr {VAR :varno 3 :varattno 1 :vartype 23 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnosyn 3 :varattnosyn 1 :location 26} :resno 1 :resname a :ressortgroupref 0 :resorigtbl 16384 :resorigcol 1 :resjunk false} {TARGETENTRY :expr {VAR :varno 3 :varattno 2 :vartype 23 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnosyn 3 :varattnosyn 2 :location 26} :resno 2 :resname b :ressortgroupref 0 :resorigtbl 16384 :resorigcol 2 :resjunk false}) :override 0 :onConflict <> :returningList <> :groupClause <> :groupingSets <> :havingQual <> :windowClause <> :distinctClause <> :sortClause <> :limitOffset <> :limitCount <> :limitOption 0 :rowMarks <> :setOperations <> :constraintDeps <> :withCheckOptions <>})

建表以及创建视图后,会向pg_class系统表插入如下的信息。

sql 复制代码
postgres@postgres=# select oid,relname,relkind,relhasrules,relrewrite from pg_class where relname='vt1';
-[ RECORD 1 ]------
oid         | 16387     -- 表OID
relname     | vt1       -- 视图名  
relkind     | v         -- 表示视图
relhasrules | t         -- 表是否定义了规则
relrewrite  | 0

postgres@postgres=# select oid,relname,relkind,relhasrules,relrewrite from pg_class where relname='t1';
-[ RECORD 1 ]------
oid         | 16384     -- 表OID
relname     | t1        -- 表名
relkind     | r         -- 表示是普通表
relhasrules | f         -- 表是否定义了规则
relrewrite  | 0

当执行select * from vt1时,先查pg_class系统表,找到表vt1类型为视图,同时该表定义了规则。查看pg_rewrite系统表,找到该表定义的规则的类型以及行为,应用规则。

相关推荐
FIN技术铺15 分钟前
问:数据库的六种锁机制实践总结?
数据库·sql·oracle
程序员劝退师_1 小时前
优惠券秒杀的背后原理
java·数据库
Gauss松鼠会1 小时前
GaussDB全密态数据库等值查询
数据库·oracle·gaussdb
JSUITDLWXL1 小时前
在Oracle数据中更新整个对象和更新对象的某几个字段时,他们的锁是相同的吗
数据库·oracle
杏花春雨江南1 小时前
ddl/dml/dcl
数据库·oracle
Matrix701 小时前
HBase理论_HBase架构组件介绍
大数据·数据库·hbase
不太灵光的程序员1 小时前
【HBase分布式数据库】第七章 数据的导入导出 importtsv导入数据
数据库·分布式·hbase
Mephisto.java1 小时前
【大数据学习 | HBASE高级】region split机制和策略
数据库·学习·hbase
Lucifer三思而后行1 小时前
YashanDB YAC 入门指南与技术详解
数据库·后端
大气层煮月亮2 小时前
python调用MySql详细步骤
数据库·mysql