postgres=# explain select * from ptab01 where tm='2020-01-07'::timestamptz;
QUERY PLAN
---------------------------------------------------------------------
Seq Scan on ptab01_202001 (cost=0.00..80.80 rows=1 width=12)
Filter: (tm = '2020-01-07 00:00:00-08'::timestamp with time zone)
(2 rows)
从上面可以看出,声明式分区表只扫描了包括指定时间实际的分区ptab01_202001,没有扫描其他时间段的分区。首先我们看一下其抽象查询语法树AST,RawStmt结构体是单个语句的raw解析树的存储结构(container for any one statement’s raw parse tree),也就是
elog_node_display(LOG, "raw tree", parseTree, ...)
打印出来的解析器输出。
Select型查询语句SelectStmt定义在src/include/nodes/parsenodes.h中,如下其包含目标列域targetList、from子句fromClause、where条件whereClause。where条件whereClause结构体执行A_Expr表达式结构体(有如下成员
NodeTag type
、
A_Expr_Kind kind
、
List *name
、
Node *lexper
、
Node *rexpr
、
int location
),其中name指明了这是等号条件,左边的等式是ColumnRef,右边是被强制转换的常量’2020-01-07’;targetList指向RESTARTGET节点,其包含的是COLUMNREF,这里其代表A_START也就是“*”(所有列)[ 从下图可以看出ColumnRef能代表两种类型的节点,一是单列,而是所有列 ]。from子句fromClause指向RANGVAR结构体,其成员relname存储的是SQL中的表ptab01,inh为true代表继承表。
从如下调用堆栈可以看出transformOptionalSelectInto函数并没有执行有效代码,只是调用了transformStmt,进行后续针对不同类型的Stmt分类处理。走transformSelectStmt函数处理T_SelectStmt节点。
transformOptionalSelectInto (pstate=0x1de9848, parseTree=0x1dc4c80)
if (IsA(parseTree, SelectStmt))
SelectStmt *stmt = (SelectStmt *) parseTree;
while (stmt && stmt->op != SETOP_NONE) --> 由于stmt->op == SETOP_NONE,所以不走while
if (stmt->intoClause)
return transformStmt(pstate, parseTree); --> 运行该transformStmt函数
transformStmt (pstate=0x1de9848, parseTree=0x1dc4c80) at analyze.c:277
switch (nodeTag(parseTree))
case T_SelectStmt:
SelectStmt *n = (SelectStmt *) parseTree;
if (n->valuesLists)
else if (n->op == SETOP_NONE) <-- 走这个分支
result = transformSelectStmt(pstate, n);
return result;
transformSelectStmt函数用于转换Select Statement,如下选取与本文相关的流程罗列如下。首先对From子句进行转换,transformFromClauseItem函数将会对fromClause列表元素RANGEVAR结构体进行转换,图中的RANGEVAR不是CTE reference/tuplestore reference,所以只能是plain relation reference,调用transformTableEntry函数(如下图中的1和2)。其次对targetList子句进行转换,从语法树中可以看出RESTARGET的val成员指向的是ColumnRef,且其代表的是A_START也就是“*”(所有列),因此执行ExpandColumnRefStar函数(如下图中的3),需要利用步骤2存入ParseState中的p_namespace来获取RTE。第4步markTargetListOrigins将原表信息更新到TARGETENTRY的resorigtbl表oid和resorigcol表列号中。
transformSelectStmt (pstate=0x1de9848, stmt=0x1dc4c80) at analyze.c:1200
Query *qry = makeNode(Query);
qry->commandType = CMD_SELECT;
transformFromClause(pstate, stmt->fromClause);
qry->targetList = transformTargetList(pstate, stmt->targetList, EXPR_KIND_SELECT_TARGET);
markTargetListOrigins(pstate, qry->targetList);
qual = transformWhereClause(pstate, stmt->whereClause, EXPR_KIND_WHERE, "WHERE");
qry->rtable = pstate->p_rtable;
qry->jointree = makeFromExpr(pstate->p_joinlist, qual);