天天看点

PostgreSQL查询引擎——select * from where = transform流程

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, ...)​

​打印出来的解析器输出。

PostgreSQL查询引擎——select * from where = transform流程

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代表继承表。

PostgreSQL查询引擎——select * from where = transform流程

从如下调用堆栈可以看出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;      
PostgreSQL查询引擎——select * from where = transform流程

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);