SQL执行流程
执行SQL语句,分为两个大的阶段:优化和执行。这里主要介绍优化相关的东西。优化又分为几个阶段:
- 词法解析:生成Token
- 语法解析:生成语法树
- 语义分析:PG中主要是拿元数据,处理gp_rewite的重写规则
- 优化:把逻辑操作符树,转化为可以执行的,由物理操作符组成的执行计划,实际上也是一颗树。
语义分析
在PG中,会把语法树,转化为一个称为Query的结构体,这是一个中间形态,可以理解为一个上下文,为进一步操作做准备。在这个阶段,PG做了两件事:
- transform:获取必要的元数据,把语法树的节点,转化为更方便操作的数据结构,形成一颗新的树。
- rewrite: 一个主要的工作,是从pg_rewite中获取规则,并应用规则。
rewrite
CREATE RULE
<重写规范>
sql
CREATE [ OR REPLACE ] RULE name AS ON event
TO table_name [ WHERE condition ]
DO [ ALSO | INSTEAD ] { NOTHING | command | ( command ; command ... ) }
其中 event 可以是以下之一:
SELECT | INSERT | UPDATE | DELETE
- CREATE RULE定义一条应用于指定表或视图的规则。
- CREATE OR REPLACE RULE将创建一条新规则或者替换同一个表上具有同一名称的现有规则。
PostgreSQL规则系统允许我们定义 针对数据库表中插入、更新或者删除动作上的替代动作。当在一个给定表上执行给定命令时,会有几种情况:
- 触发执行指定的命令。
- INSTEAD规则可以用另一个命令替换给定的命令
- 禁止一个命令的执行。
- 实现Sql视图
sql
create rule r1 as on update to test DO INSTEAD NOTHING;
select * from pg_rewrite where rulename = 'r1';
oid | rulename | ev_class | ev_type | ev_enabled | is_instead | ev_qual |
24576 | r1 | 16385 | 2 | O | t | <> |
pg_rewrite
pg_rewrite系统目录表存储表和视图的重写规则。如果一个表在这个目录中有任何规则,其pg_class.relhasrules必须为真。
可以看到,CREATE RULE创建的规则,被存放到了pg_rewrite表中。
postgres 中重写的实现
上面说的语义分析的第一个阶段,在PG中被称为transform,这个阶段的总入口为:pg_analyze_and_rewrite。
parse_analyze:会把语法树,转化为合适的数据结构,放入Query中
- 表表达式转化为RangeTblEntry
- 列-->ColRef
pg_rewrite_query:一个主要的工作,是应用pg_rewrite中的规则。
在PG里面,一个关系的元数据,为了高效的获取,会被缓存起来,用`RelationData`数据结构管理起来,这里面有个对象:`rd_rules`,是用来管理这个表的重写规则的。这个数据结构由函数`RelationBuildRuleLock`初始化。下面是关键的初始化代码:
/*
* open pg_rewrite and begin a scan
*
* Note: since we scan the rules using RewriteRelRulenameIndexId, we will
* be reading the rules in name order, except possibly during
* emergency-recovery operations (ie, IgnoreSystemIndexes). This in turn
* ensures that rules will be fired in name order.
*/
rewrite_desc = table_open(RewriteRelationId, AccessShareLock);
rewrite_tupdesc = RelationGetDescr(rewrite_desc);
rewrite_scan = systable_beginscan(rewrite_desc,
RewriteRelRulenameIndexId,
true, NULL,
1, &key);
while (HeapTupleIsValid(rewrite_tuple = systable_getnext(rewrite_scan)))
{
...
rule = (RewriteRule *) MemoryContextAlloc(rulescxt,
sizeof(RewriteRule));
rule->ruleId = rewrite_form->oid;
rule->event = rewrite_form->ev_type - '0';
rule->enabled = rewrite_form->ev_enabled;
rule->isInstead = rewrite_form->is_instead;
rule_datum = heap_getattr(rewrite_tuple,
Anum_pg_rewrite_ev_action,
rewrite_tupdesc,
&isnull);
rule_datum = heap_getattr(rewrite_tuple,
Anum_pg_rewrite_ev_qual,
rewrite_tupdesc,
&isnull);
**rule_str = TextDatumGetCString(rule_datum);**
rule->qual = (Node *) stringToNode(rule_str);
......
rules[numlocks++] = rule;
}
/*
* end the scan and close the attribute relation
*/
systable_endscan(rewrite_scan);
table_close(rewrite_desc, AccessShareLock);
/*
* form a RuleLock and insert into relation
*/
rulelock = (RuleLock *) MemoryContextAlloc(rulescxt, sizeof(RuleLock));
rulelock->numLocks = numlocks;
rulelock->rules = rules;
relation->rd_rules = rulelock;
现在可以做规则的应用工作了:fireRules
foreach(l, locks)
{
RewriteRule *rule_lock = (RewriteRule *) lfirst(l);
......
/* Now process the rule's actions and add them to the result list */
foreach(r, actions)
{
Query *rule_action = lfirst(r);
if (rule_action->commandType == CMD_NOTHING)
continue;
rule_action = rewriteRuleAction(parsetree, rule_action,
event_qual, rt_index, event,
returning_flag);
......
results = lappend(results, rule_action);
}
}
上面的逻辑中,`locks`实际上就是从`RelationData`中取出来的,最后由`rewriteRuleAction`,应用规则