千家信息网

PostgreSQL 源码解读(41)- 查询语句#26(query_planner函数#4)

发表于:2025-01-23 作者:千家信息网编辑
千家信息网最后更新 2025年01月23日,上一小节介绍了函数query_planner中子函数add_base_rels_to_query的实现逻辑,本节继续介绍其中的子函数build_base_rel_tlists/find_placeho
千家信息网最后更新 2025年01月23日PostgreSQL 源码解读(41)- 查询语句#26(query_planner函数#4)

上一小节介绍了函数query_planner中子函数add_base_rels_to_query的实现逻辑,本节继续介绍其中的子函数build_base_rel_tlists/find_placeholders_in_jointree/find_lateral_references,这几个子函数的目的仍然是完善RelOptInfo结构体信息。

query_planner代码片段:

     //...     /*      * Examine the targetlist and join tree, adding entries to baserel      * targetlists for all referenced Vars, and generating PlaceHolderInfo      * entries for all referenced PlaceHolderVars.  Restrict and join clauses      * are added to appropriate lists belonging to the mentioned relations. We      * also build EquivalenceClasses for provably equivalent expressions. The      * SpecialJoinInfo list is also built to hold information about join order      * restrictions.  Finally, we form a target joinlist for make_one_rel() to      * work from.      */     build_base_rel_tlists(root, tlist);//构建"base rels"的投影列      find_placeholders_in_jointree(root);//处理jointree中的PHI      find_lateral_references(root);//处理jointree中Lateral依赖      //...

一、重要的数据结构

PlannerInfo
PlannerInfo贯穿整个构建查询计划的全过程.
build_base_rel_tlists、find_placeholders_in_jointree和find_lateral_references函数完善了PlannerInfo->placeholder_list链表.

 /*----------  * PlannerInfo  *      Per-query information for planning/optimization  *  * This struct is conventionally called "root" in all the planner routines.  * It holds links to all of the planner's working state, in addition to the  * original Query.  Note that at present the planner extensively modifies  * the passed-in Query data structure; someday that should stop.  *----------  */ struct AppendRelInfo;  typedef struct PlannerInfo {     NodeTag     type;//Node标识      Query      *parse;          /* 查询树,the Query being planned */      PlannerGlobal *glob;        /* 当前的planner全局信息,global info for current planner run */      Index       query_level;    /* 查询层次,1标识最高层,1 at the outermost Query */      struct PlannerInfo *parent_root;    /* 如为子计划,则这里存储父计划器指针,NULL标识最高层,NULL at outermost Query */      /*      * plan_params contains the expressions that this query level needs to      * make available to a lower query level that is currently being planned.      * outer_params contains the paramIds of PARAM_EXEC Params that outer      * query levels will make available to this query level.      */     List       *plan_params;    /* list of PlannerParamItems, see below */     Bitmapset  *outer_params;      /*      * simple_rel_array holds pointers to "base rels" and "other rels" (see      * comments for RelOptInfo for more info).  It is indexed by rangetable      * index (so entry 0 is always wasted).  Entries can be NULL when an RTE      * does not correspond to a base relation, such as a join RTE or an      * unreferenced view RTE; or if the RelOptInfo hasn't been made yet.      */     /* RelOptInfo数组,存储"base rels",比如基表/子查询等.该数组与RTE的顺序一一对应,而且是从1开始,因此[0]无用 */     struct RelOptInfo **simple_rel_array;   /* All 1-rel RelOptInfos */     int         simple_rel_array_size;  /* 数组大小,allocated size of array */      /*      * simple_rte_array is the same length as simple_rel_array and holds      * pointers to the associated rangetable entries.  This lets us avoid      * rt_fetch(), which can be a bit slow once large inheritance sets have      * been expanded.      */     RangeTblEntry **simple_rte_array;   /* RTE数组,rangetable as an array */      /*      * append_rel_array is the same length as the above arrays, and holds      * pointers to the corresponding AppendRelInfo entry indexed by      * child_relid, or NULL if none.  The array itself is not allocated if      * append_rel_list is empty.      */     struct AppendRelInfo **append_rel_array;//先前已介绍,在处理集合操作如UNION ALL时使用      /*      * all_baserels is a Relids set of all base relids (but not "other"      * relids) in the query; that is, the Relids identifier of the final join      * we need to form.  This is computed in make_one_rel, just before we      * start making Paths.      */     Relids      all_baserels;//"base rels"      /*      * nullable_baserels is a Relids set of base relids that are nullable by      * some outer join in the jointree; these are rels that are potentially      * nullable below the WHERE clause, SELECT targetlist, etc.  This is      * computed in deconstruct_jointree.      */     Relids      nullable_baserels;//Nullable-side端的"base rels"      /*      * join_rel_list is a list of all join-relation RelOptInfos we have      * considered in this planning run.  For small problems we just scan the      * list to do lookups, but when there are many join relations we build a      * hash table for faster lookups.  The hash table is present and valid      * when join_rel_hash is not NULL.  Note that we still maintain the list      * even when using the hash table for lookups; this simplifies life for      * GEQO.      */     List       *join_rel_list;  /* 参与连接的Relation的RelOptInfo链表,list of join-relation RelOptInfos */     struct HTAB *join_rel_hash; /* 可加快链表访问的hash表,optional hashtable for join relations */      /*      * When doing a dynamic-programming-style join search, join_rel_level[k]      * is a list of all join-relation RelOptInfos of level k, and      * join_cur_level is the current level.  New join-relation RelOptInfos are      * automatically added to the join_rel_level[join_cur_level] list.      * join_rel_level is NULL if not in use.      */     List      **join_rel_level; /* RelOptInfo指针链表数组,k层的join存储在[k]中,lists of join-relation RelOptInfos */     int         join_cur_level; /* 当前的join层次,index of list being extended */      List       *init_plans;     /* 查询的初始化计划链表,init SubPlans for query */      List       *cte_plan_ids;   /* CTE子计划ID链表,per-CTE-item list of subplan IDs */      List       *multiexpr_params;   /* List of Lists of Params for MULTIEXPR                                      * subquery outputs */      List       *eq_classes;     /* 活动的等价类链表,list of active EquivalenceClasses */      List       *canon_pathkeys; /* 规范化PathKey链表,list of "canonical" PathKeys */      List       *left_join_clauses;  /* 外连接约束条件链表(左),list of RestrictInfos for mergejoinable                                      * outer join clauses w/nonnullable var on                                      * left */      List       *right_join_clauses; /* 外连接约束条件链表(右),list of RestrictInfos for mergejoinable                                      * outer join clauses w/nonnullable var on                                      * right */      List       *full_join_clauses;  /* 全连接约束条件链表,list of RestrictInfos for mergejoinable                                      * full join clauses */      List       *join_info_list; /* 特殊连接信息链表,list of SpecialJoinInfos */      List       *append_rel_list;    /* AppendRelInfo链表,list of AppendRelInfos */      List       *rowMarks;       /* list of PlanRowMarks */      List       *placeholder_list;   /* PHI链表,list of PlaceHolderInfos */      List       *fkey_list;      /* 外键信息链表,list of ForeignKeyOptInfos */      List       *query_pathkeys; /* uery_planner()要求的PathKeys,desired pathkeys for query_planner() */      List       *group_pathkeys; /* groupClause pathkeys, if any */     List       *window_pathkeys;    /* pathkeys of bottom window, if any */     List       *distinct_pathkeys;  /* distinctClause pathkeys, if any */     List       *sort_pathkeys;  /* sortClause pathkeys, if any */      List       *part_schemes;   /* 已规范化的分区Schema,Canonicalised partition schemes used in the                                  * query. */      List       *initial_rels;   /* 尝试连接的RelOptInfo链表,RelOptInfos we are now trying to join */      /* Use fetch_upper_rel() to get any particular upper rel */     List       *upper_rels[UPPERREL_FINAL + 1]; /* 上层的RelOptInfo链表, upper-rel RelOptInfos */      /* Result tlists chosen by grouping_planner for upper-stage processing */     struct PathTarget *upper_targets[UPPERREL_FINAL + 1];//      /*      * grouping_planner passes back its final processed targetlist here, for      * use in relabeling the topmost tlist of the finished Plan.      */     List       *processed_tlist;//最后需处理的投影列      /* Fields filled during create_plan() for use in setrefs.c */     AttrNumber *grouping_map;   /* for GroupingFunc fixup */     List       *minmax_aggs;    /* List of MinMaxAggInfos */      MemoryContext planner_cxt;  /* 内存上下文,context holding PlannerInfo */      double      total_table_pages;  /* 所有的pages,# of pages in all tables of query */      double      tuple_fraction; /* query_planner输入参数:元组处理比例,tuple_fraction passed to query_planner */     double      limit_tuples;   /* query_planner输入参数:limit_tuples passed to query_planner */      Index       qual_security_level;    /* 表达式的最新安全等级,minimum security_level for quals */     /* Note: qual_security_level is zero if there are no securityQuals */      InheritanceKind inhTargetKind;  /* indicates if the target relation is an                                      * inheritance child or partition or a                                      * partitioned table */     bool        hasJoinRTEs;    /* 存在RTE_JOIN的RTE,true if any RTEs are RTE_JOIN kind */     bool        hasLateralRTEs; /* 存在标记为LATERAL的RTE,true if any RTEs are marked LATERAL */     bool        hasDeletedRTEs; /* 存在已在jointree删除的RTE,true if any RTE was deleted from jointree */     bool        hasHavingQual;  /* 存在Having子句,true if havingQual was non-null */     bool        hasPseudoConstantQuals; /* true if any RestrictInfo has                                          * pseudoconstant = true */     bool        hasRecursion;   /* 递归语句,true if planning a recursive WITH item */      /* These fields are used only when hasRecursion is true: */     int         wt_param_id;    /* PARAM_EXEC ID for the work table */     struct Path *non_recursive_path;    /* a path for non-recursive term */      /* These fields are workspace for createplan.c */     Relids      curOuterRels;   /* outer rels above current node */     List       *curOuterParams; /* not-yet-assigned NestLoopParams */      /* optional private data for join_search_hook, e.g., GEQO */     void       *join_search_private;      /* Does this query modify any partition key columns? */     bool        partColsUpdated; } PlannerInfo; 

RelOptInfo
RelOptInfo结构体贯彻逻辑优化和物理优化过程的始终.
build_base_rel_tlists完善了结构体中attr_needed和reltarget变量,find_lateral_references函数完善了结构体中lateral_vars变量.

 typedef struct RelOptInfo {     NodeTag     type;//节点标识      RelOptKind  reloptkind;//RelOpt类型      /* all relations included in this RelOptInfo */     Relids      relids;         /*Relids(rtindex)集合 set of base relids (rangetable indexes) */      /* size estimates generated by planner */     double      rows;           /*结果元组的估算数量 estimated number of result tuples */      /* per-relation planner control flags */     bool        consider_startup;   /*是否考虑启动成本?是,需要保留启动成本低的路径 keep cheap-startup-cost paths? */     bool        consider_param_startup; /*是否考虑参数化?的路径 ditto, for parameterized paths? */     bool        consider_parallel;  /*是否考虑并行处理路径 consider parallel paths? */      /* default result targetlist for Paths scanning this relation */     struct PathTarget *reltarget;   /*扫描该Relation时默认的结果 list of Vars/Exprs, cost, width */      /* materialization information */     List       *pathlist;       /*访问路径链表 Path structures */     List       *ppilist;        /*路径链表中使用参数化路径进行 ParamPathInfos used in pathlist */     List       *partial_pathlist;   /* partial Paths */     struct Path *cheapest_startup_path;//代价最低的启动路径     struct Path *cheapest_total_path;//代价最低的整体路径     struct Path *cheapest_unique_path;//代价最低的获取唯一值的路径     List       *cheapest_parameterized_paths;//代价最低的参数化?路径链表      /* parameterization information needed for both base rels and join rels */     /* (see also lateral_vars and lateral_referencers) */     Relids      direct_lateral_relids;  /*使用lateral语法,需依赖的Relids rels directly laterally referenced */     Relids      lateral_relids; /* minimum parameterization of rel */      /* information about a base rel (not set for join rels!) */     //reloptkind=RELOPT_BASEREL时使用的数据结构     Index       relid;          /* Relation ID */     Oid         reltablespace;  /* 表空间 containing tablespace */     RTEKind     rtekind;        /* 基表?子查询?还是函数等等?RELATION, SUBQUERY, FUNCTION, etc */     AttrNumber  min_attr;       /* 最小的属性编号 smallest attrno of rel (often <0) */     AttrNumber  max_attr;       /* 最大的属性编号 largest attrno of rel */     Relids     *attr_needed;    /* 数组 array indexed [min_attr .. max_attr] */     int32      *attr_widths;    /* 属性宽度 array indexed [min_attr .. max_attr] */     List       *lateral_vars;   /* 关系依赖的Vars/PHVs LATERAL Vars and PHVs referenced by rel */     Relids      lateral_referencers;    /*依赖该关系的Relids rels that reference me laterally */     List       *indexlist;      /* 该关系的IndexOptInfo链表 list of IndexOptInfo */     List       *statlist;       /* 统计信息链表 list of StatisticExtInfo */     BlockNumber pages;          /* 块数 size estimates derived from pg_class */     double      tuples;         /* 元组数 */     double      allvisfrac;     /* ? */     PlannerInfo *subroot;       /* 如为子查询,存储子查询的root if subquery */     List       *subplan_params; /* 如为子查询,存储子查询的参数 if subquery */     int         rel_parallel_workers;   /* 并行执行,需要多少个workers? wanted number of parallel workers */      /* Information about foreign tables and foreign joins */     //FWD相关信息     Oid         serverid;       /* identifies server for the table or join */     Oid         userid;         /* identifies user to check access as */     bool        useridiscurrent;    /* join is only valid for current user */     /* use "struct FdwRoutine" to avoid including fdwapi.h here */     struct FdwRoutine *fdwroutine;     void       *fdw_private;      /* cache space for remembering if we have proven this relation unique */     //已知的,可保证唯一的Relids链表     List       *unique_for_rels;    /* known unique for these other relid                                      * set(s) */     List       *non_unique_for_rels;    /* 已知的,不唯一的Relids链表 known not unique for these set(s) */      /* used by various scans and joins: */     List       *baserestrictinfo;   /* 如为基本关系,存储约束条件 RestrictInfo structures (if base rel) */     QualCost    baserestrictcost;   /* 解析约束表达式的成本? cost of evaluating the above */     Index       baserestrict_min_security;  /* 最低安全等级 min security_level found in                                              * baserestrictinfo */     List       *joininfo;       /* 连接语句的约束条件信息 RestrictInfo structures for join clauses                                  * involving this rel */     bool        has_eclass_joins;   /* 是否存在等价类连接? T means joininfo is incomplete */      /* used by partitionwise joins: */     bool        consider_partitionwise_join;    /* 分区? consider partitionwise                                                  * join paths? (if                                                  * partitioned rel) */     Relids      top_parent_relids;  /* Relids of topmost parents (if "other"                                      * rel) */      /* used for partitioned relations */     //分区表使用     PartitionScheme part_scheme;    /* 分区的schema Partitioning scheme. */     int         nparts;         /* 分区数 number of partitions */     struct PartitionBoundInfoData *boundinfo;   /* 分区边界信息 Partition bounds */     List       *partition_qual; /* 分区约束 partition constraint */     struct RelOptInfo **part_rels;  /* 分区的RelOptInfo数组 Array of RelOptInfos of partitions,                                      * stored in the same order of bounds */     List      **partexprs;      /* 非空分区键表达式 Non-nullable partition key expressions. */     List      **nullable_partexprs; /* 可为空的分区键表达式 Nullable partition key expressions. */     List       *partitioned_child_rels; /* RT Indexes链表 List of RT indexes. */ } RelOptInfo;

二、源码解读

基本概念
PlaceHolder
PlaceHolder即占位符,常用于减少SQL的parse过程提高性能.
如JDBC中常用的PreparedStatement:

String sql = "select * from t_dwxx where dwbh = ? and dwmc = ?";PreparedStatement pstmt = connection.preparestatement(sql);pstmt.setstring(1,'1001');pstmt.setstring(2,'测试');resultset rs = ps.executequery(); 

可以认为,其中的?所代表的是占位符.

在psql中,使用set命令定义变量,在SQL语句中使用占位符:

testdb=# \set v1 '\'1001\''testdb=# select * from t_dwxx where dwbh = :v1;   dwmc    | dwbh |        dwdz        -----------+------+-------------------- X有限公司 | 1001 | 广东省广州市荔湾区(1 row)

build_base_rel_tlists

/********************************* build_base_rel_tlists ******************//*  * build_base_rel_tlists  *    Add targetlist entries for each var needed in the query's final tlist  *    (and HAVING clause, if any) to the appropriate base relations.  *   * 把最终的投影列信息添加到合适的"base rels"中.  *  * We mark such vars as needed by "relation 0" to ensure that they will  * propagate up through all join plan steps.  */ void build_base_rel_tlists(PlannerInfo *root, List *final_tlist) {     List       *tlist_vars = pull_var_clause((Node *) final_tlist,                                              PVC_RECURSE_AGGREGATES |                                              PVC_RECURSE_WINDOWFUNCS |                                              PVC_INCLUDE_PLACEHOLDERS);//获取投影列      if (tlist_vars != NIL)     {         //添加到相应的Relation's targetlist(如不存在)         //标记其为连接或者最终输出所需要         add_vars_to_targetlist(root, tlist_vars, bms_make_singleton(0), true);         list_free(tlist_vars);     }      /*      * If there's a HAVING clause, we'll need the Vars it uses, too.  Note      * that HAVING can contain Aggrefs but not WindowFuncs.      */     if (root->parse->havingQual)//如存在Having子句,顶层的Having是在查询语句的后期才执行,需保留需要的Vars     {         List       *having_vars = pull_var_clause(root->parse->havingQual,                                                   PVC_RECURSE_AGGREGATES |                                                   PVC_INCLUDE_PLACEHOLDERS);          if (having_vars != NIL)         {             add_vars_to_targetlist(root, having_vars,                                    bms_make_singleton(0), true);             list_free(having_vars);         }     } } /*  * add_vars_to_targetlist  *    For each variable appearing in the list, add it to the owning  *    relation's targetlist if not already present, and mark the variable  *    as being needed for the indicated join (or for final output if  *    where_needed includes "relation 0").  *  *    The list may also contain PlaceHolderVars.  These don't necessarily  *    have a single owning relation; we keep their attr_needed info in  *    root->placeholder_list instead.  If create_new_ph is true, it's OK  *    to create new PlaceHolderInfos; otherwise, the PlaceHolderInfos must  *    already exist, and we should only update their ph_needed.  (This should  *    be true before deconstruct_jointree begins, and false after that.)  */ void add_vars_to_targetlist(PlannerInfo *root, List *vars,                        Relids where_needed, bool create_new_ph) {     ListCell   *temp;      Assert(!bms_is_empty(where_needed));      foreach(temp, vars)     {         Node       *node = (Node *) lfirst(temp);          if (IsA(node, Var))         {             Var        *var = (Var *) node;//属性Var             RelOptInfo *rel = find_base_rel(root, var->varno);//找到相应的RelOptInfo             int         attno = var->varattno;//属性编号              if (bms_is_subset(where_needed, rel->relids))//where_needed是否rel的子集?                 continue;//是,继续循环             Assert(attno >= rel->min_attr && attno <= rel->max_attr);             attno -= rel->min_attr;             if (rel->attr_needed[attno] == NULL)             {                 /* Variable not yet requested, so add to rel's targetlist */                 /* XXX is copyObject necessary here? */                 rel->reltarget->exprs = lappend(rel->reltarget->exprs,                                                 copyObject(var));//TODO,添加到rel->reltarget->exprs 中                 /* reltarget cost and width will be computed later */             }             rel->attr_needed[attno] = bms_add_members(rel->attr_needed[attno],                                                       where_needed);//where_needed添加到bitmapset中         }         else if (IsA(node, PlaceHolderVar))         {             PlaceHolderVar *phv = (PlaceHolderVar *) node;             PlaceHolderInfo *phinfo = find_placeholder_info(root, phv,                                                             create_new_ph);              phinfo->ph_needed = bms_add_members(phinfo->ph_needed,                                                 where_needed);//添加PHV         }         else             elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));     } }

find_placeholders_in_jointree

/********************************* find_placeholders_in_jointree ******************/ /*  * find_placeholders_in_jointree  *      Search the jointree for PlaceHolderVars, and build PlaceHolderInfos  *  * 搜索jointree中的PHV,并且构建PHInfos  *  * We don't need to look at the targetlist because build_base_rel_tlists()  * will already have made entries for any PHVs in the tlist.  *  * This is called before we begin deconstruct_jointree.  Once we begin  * deconstruct_jointree, all active placeholders must be present in  * root->placeholder_list, because make_outerjoininfo and  * update_placeholder_eval_levels require this info to be available  * while we crawl up the join tree.  */ void find_placeholders_in_jointree(PlannerInfo *root) {     /* We need do nothing if the query contains no PlaceHolderVars */     if (root->glob->lastPHId != 0)     {         /* Start recursion at top of jointree */         Assert(root->parse->jointree != NULL &&                IsA(root->parse->jointree, FromExpr));         find_placeholders_recurse(root, (Node *) root->parse->jointree);//递归处理     } }  /*  * find_placeholders_recurse  *    One recursion level of find_placeholders_in_jointree.  *  * jtnode is the current jointree node to examine.  */ static void find_placeholders_recurse(PlannerInfo *root, Node *jtnode) {     if (jtnode == NULL)         return;     if (IsA(jtnode, RangeTblRef))//RTR     {         /* No quals to deal with here */     }     else if (IsA(jtnode, FromExpr))//FromExpr     {         FromExpr   *f = (FromExpr *) jtnode;         ListCell   *l;          /*          * First, recurse to handle child joins.          */         foreach(l, f->fromlist)         {             find_placeholders_recurse(root, lfirst(l));         }          /*          * Now process the top-level quals.          */         find_placeholders_in_expr(root, f->quals);//在表达式中搜索     }     else if (IsA(jtnode, JoinExpr))//JoinExpr     {         JoinExpr   *j = (JoinExpr *) jtnode;          /*          * First, recurse to handle child joins.          */         find_placeholders_recurse(root, j->larg);         find_placeholders_recurse(root, j->rarg);          /* Process the qual clauses */         find_placeholders_in_expr(root, j->quals);//在表达式中搜索     }     else         elog(ERROR, "unrecognized node type: %d",              (int) nodeTag(jtnode)); }  /*  * find_placeholders_in_expr  *      Find all PlaceHolderVars in the given expression, and create  *      PlaceHolderInfo entries for them.  */ static void find_placeholders_in_expr(PlannerInfo *root, Node *expr) {     List       *vars;     ListCell   *vl;      /*      * pull_var_clause does more than we need here, but it'll do and it's      * convenient to use.      */     vars = pull_var_clause(expr,                            PVC_RECURSE_AGGREGATES |                            PVC_RECURSE_WINDOWFUNCS |                            PVC_INCLUDE_PLACEHOLDERS);//遍历Vars,得到PH链表     foreach(vl, vars)     {         PlaceHolderVar *phv = (PlaceHolderVar *) lfirst(vl);          /* Ignore any plain Vars */         if (!IsA(phv, PlaceHolderVar))             continue;          /* Create a PlaceHolderInfo entry if there's not one already */         (void) find_placeholder_info(root, phv, true);//创建PHInfo     }     list_free(vars); } /*  * find_placeholder_info  *      Fetch the PlaceHolderInfo for the given PHV  *  * If the PlaceHolderInfo doesn't exist yet, create it if create_new_ph is  * true, else throw an error.  *  * This is separate from make_placeholder_expr because subquery pullup has  * to make PlaceHolderVars for expressions that might not be used at all in  * the upper query, or might not remain after const-expression simplification.  * We build PlaceHolderInfos only for PHVs that are still present in the  * simplified query passed to query_planner().  *  * Note: this should only be called after query_planner() has started.  Also,  * create_new_ph must not be true after deconstruct_jointree begins, because  * make_outerjoininfo assumes that we already know about all placeholders.  */ PlaceHolderInfo * find_placeholder_info(PlannerInfo *root, PlaceHolderVar *phv,                       bool create_new_ph) {     PlaceHolderInfo *phinfo;//结果     Relids      rels_used;     ListCell   *lc;      /* if this ever isn't true, we'd need to be able to look in parent lists */     Assert(phv->phlevelsup == 0);      foreach(lc, root->placeholder_list)//已存在,返回     {         phinfo = (PlaceHolderInfo *) lfirst(lc);         if (phinfo->phid == phv->phid)             return phinfo;     }      /* Not found, so create it */     if (!create_new_ph)         elog(ERROR, "too late to create a new PlaceHolderInfo");      phinfo = makeNode(PlaceHolderInfo);//构建PHInfo      phinfo->phid = phv->phid;     phinfo->ph_var = copyObject(phv);      /*      * Any referenced rels that are outside the PHV's syntactic scope are      * LATERAL references, which should be included in ph_lateral but not in      * ph_eval_at.  If no referenced rels are within the syntactic scope,      * force evaluation at the syntactic location.      */     rels_used = pull_varnos((Node *) phv->phexpr);     phinfo->ph_lateral = bms_difference(rels_used, phv->phrels);     if (bms_is_empty(phinfo->ph_lateral))         phinfo->ph_lateral = NULL;  /* make it exactly NULL if empty */     phinfo->ph_eval_at = bms_int_members(rels_used, phv->phrels);     /* If no contained vars, force evaluation at syntactic location */     if (bms_is_empty(phinfo->ph_eval_at))     {         phinfo->ph_eval_at = bms_copy(phv->phrels);         Assert(!bms_is_empty(phinfo->ph_eval_at));     }     /* ph_eval_at may change later, see update_placeholder_eval_levels */     phinfo->ph_needed = NULL;   /* initially it's unused */     /* for the moment, estimate width using just the datatype info */     phinfo->ph_width = get_typavgwidth(exprType((Node *) phv->phexpr),                                        exprTypmod((Node *) phv->phexpr));      root->placeholder_list = lappend(root->placeholder_list, phinfo);//添加到优化器信息中      /*      * The PHV's contained expression may contain other, lower-level PHVs.  We      * now know we need to get those into the PlaceHolderInfo list, too, so we      * may as well do that immediately.      */     find_placeholders_in_expr(root, (Node *) phinfo->ph_var->phexpr);//如存在子表达式,递归进去      return phinfo; }

find_lateral_references

/************************* find_lateral_references ******************************/ /*  * find_lateral_references  *    For each LATERAL subquery, extract all its references to Vars and  *    PlaceHolderVars of the current query level, and make sure those values  *    will be available for evaluation of the subquery.  *  * 对于LATERAL子查询,获取当前查询层次的Vars&PHVars,并且确保这些值在解析子查询时是可用的  *  * While later planning steps ensure that the Var/PHV source rels are on the  * outside of nestloops relative to the LATERAL subquery, we also need to  * ensure that the Vars/PHVs propagate up to the nestloop join level; this  * means setting suitable where_needed values for them.  *  * Note that this only deals with lateral references in unflattened LATERAL  * subqueries.  When we flatten a LATERAL subquery, its lateral references  * become plain Vars in the parent query, but they may have to be wrapped in  * PlaceHolderVars if they need to be forced NULL by outer joins that don't  * also null the LATERAL subquery.  That's all handled elsewhere.  *  * This has to run before deconstruct_jointree, since it might result in  * creation of PlaceHolderInfos.  */ void find_lateral_references(PlannerInfo *root) {     Index       rti;      /* We need do nothing if the query contains no LATERAL RTEs */     if (!root->hasLateralRTEs)         return;      /*      * Examine all baserels (the rel array has been set up by now).      */     for (rti = 1; rti < root->simple_rel_array_size; rti++)//遍历RelOptInfo     {         RelOptInfo *brel = root->simple_rel_array[rti];          /* there may be empty slots corresponding to non-baserel RTEs */         if (brel == NULL)             continue;          Assert(brel->relid == rti); /* sanity check on array */          /*          * This bit is less obvious than it might look.  We ignore appendrel          * otherrels and consider only their parent baserels.  In a case where          * a LATERAL-containing UNION ALL subquery was pulled up, it is the          * otherrel that is actually going to be in the plan.  However, we          * want to mark all its lateral references as needed by the parent,          * because it is the parent's relid that will be used for join          * planning purposes.  And the parent's RTE will contain all the          * lateral references we need to know, since the pulled-up member is          * nothing but a copy of parts of the original RTE's subquery.  We          * could visit the parent's children instead and transform their          * references back to the parent's relid, but it would be much more          * complicated for no real gain.  (Important here is that the child          * members have not yet received any processing beyond being pulled          * up.)  Similarly, in appendrels created by inheritance expansion,          * it's sufficient to look at the parent relation.          */          /* ignore RTEs that are "other rels" */         if (brel->reloptkind != RELOPT_BASEREL)             continue;          extract_lateral_references(root, brel, rti);//获取LATERAL依赖     } }  static void extract_lateral_references(PlannerInfo *root, RelOptInfo *brel, Index rtindex) {     RangeTblEntry *rte = root->simple_rte_array[rtindex];//相应的RTE     List       *vars;     List       *newvars;     Relids      where_needed;     ListCell   *lc;      /* No cross-references are possible if it's not LATERAL */     if (!rte->lateral)//非LATERAL,退出         return;      /* Fetch the appropriate variables */     //获取相应的Vars     if (rte->rtekind == RTE_RELATION)//基表         vars = pull_vars_of_level((Node *) rte->tablesample, 0);     else if (rte->rtekind == RTE_SUBQUERY)//子查询         vars = pull_vars_of_level((Node *) rte->subquery, 1);     else if (rte->rtekind == RTE_FUNCTION)//函数         vars = pull_vars_of_level((Node *) rte->functions, 0);     else if (rte->rtekind == RTE_TABLEFUNC)//TABLEFUNC         vars = pull_vars_of_level((Node *) rte->tablefunc, 0);     else if (rte->rtekind == RTE_VALUES)//VALUES         vars = pull_vars_of_level((Node *) rte->values_lists, 0);     else     {         Assert(false);         return;                 /* keep compiler quiet */     }      if (vars == NIL)         return;                 /* nothing to do */      /* Copy each Var (or PlaceHolderVar) and adjust it to match our level */     newvars = NIL;     foreach(lc, vars)//遍历Vars     {         Node       *node = (Node *) lfirst(lc);          node = copyObject(node);         if (IsA(node, Var))//Var         {             Var        *var = (Var *) node;              /* Adjustment is easy since it's just one node */             var->varlevelsup = 0;         }         else if (IsA(node, PlaceHolderVar))//PHVar         {             PlaceHolderVar *phv = (PlaceHolderVar *) node;             int         levelsup = phv->phlevelsup;              /* Have to work harder to adjust the contained expression too */             if (levelsup != 0)                 IncrementVarSublevelsUp(node, -levelsup, 0);//调整其中的表达式              /*              * If we pulled the PHV out of a subquery RTE, its expression              * needs to be preprocessed.  subquery_planner() already did this              * for level-zero PHVs in function and values RTEs, though.              */             if (levelsup > 0)                 phv->phexpr = preprocess_phv__expression(root, phv->phexpr);//预处理PHVar表达式         }         else             Assert(false);         newvars = lappend(newvars, node);//添加到新的结果Vars中     }      list_free(vars);      /*      * We mark the Vars as being "needed" at the LATERAL RTE.  This is a bit      * of a cheat: a more formal approach would be to mark each one as needed      * at the join of the LATERAL RTE with its source RTE.  But it will work,      * and it's much less tedious than computing a separate where_needed for      * each Var.      */     where_needed = bms_make_singleton(rtindex);//获取Rel编号      /*      * Push Vars into their source relations' targetlists, and PHVs into      * root->placeholder_list.      */     add_vars_to_targetlist(root, newvars, where_needed, true);//添加到相应的Rel中      /* Remember the lateral references for create_lateral_join_info */     brel->lateral_vars = newvars;//RelOptInfo赋值 } /*  * pull_vars_of_level  *      Create a list of all Vars (and PlaceHolderVars) referencing the  *      specified query level in the given parsetree.  *  * Caution: the Vars are not copied, only linked into the list.  */ List * pull_vars_of_level(Node *node, int levelsup) {     pull_vars_context context;      context.vars = NIL;     context.sublevels_up = levelsup;      /*      * Must be prepared to start with a Query or a bare expression tree; if      * it's a Query, we don't want to increment sublevels_up.      */     query_or_expression_tree_walker(node,                                     pull_vars_walker,                                     (void *) &context,                                     0);//调用XX_walker函数遍历      return context.vars; }  static bool pull_vars_walker(Node *node, pull_vars_context *context)//遍历函数 {     if (node == NULL)         return false;     if (IsA(node, Var))     {         Var        *var = (Var *) node;          if (var->varlevelsup == context->sublevels_up)             context->vars = lappend(context->vars, var);         return false;     }     if (IsA(node, PlaceHolderVar))     {         PlaceHolderVar *phv = (PlaceHolderVar *) node;          if (phv->phlevelsup == context->sublevels_up)             context->vars = lappend(context->vars, phv);         /* we don't want to look into the contained expression */         return false;     }     if (IsA(node, Query))     {         /* Recurse into RTE subquery or not-yet-planned sublink subquery */         bool        result;          context->sublevels_up++;         result = query_tree_walker((Query *) node, pull_vars_walker,                                    (void *) context, 0);         context->sublevels_up--;         return result;     }     return expression_tree_walker(node, pull_vars_walker,                                   (void *) context); }

公共部分

/************************* 公共 ******************************/ /*  * pull_var_clause  *    Recursively pulls all Var nodes from an expression clause.  *  * 递归的方式推送(pull)所有的Var节点  *  *    Aggrefs are handled according to these bits in 'flags':  *      PVC_INCLUDE_AGGREGATES      include Aggrefs in output list  *      PVC_RECURSE_AGGREGATES      recurse into Aggref arguments  *      neither flag                throw error if Aggref found  *    Vars within an Aggref's expression are included in the result only  *    when PVC_RECURSE_AGGREGATES is specified.  *  *    WindowFuncs are handled according to these bits in 'flags':  *      PVC_INCLUDE_WINDOWFUNCS     include WindowFuncs in output list  *      PVC_RECURSE_WINDOWFUNCS     recurse into WindowFunc arguments  *      neither flag                throw error if WindowFunc found  *    Vars within a WindowFunc's expression are included in the result only  *    when PVC_RECURSE_WINDOWFUNCS is specified.  *  *    PlaceHolderVars are handled according to these bits in 'flags':  *      PVC_INCLUDE_PLACEHOLDERS    include PlaceHolderVars in output list  *      PVC_RECURSE_PLACEHOLDERS    recurse into PlaceHolderVar arguments  *      neither flag                throw error if PlaceHolderVar found  *    Vars within a PHV's expression are included in the result only  *    when PVC_RECURSE_PLACEHOLDERS is specified.  *  *    GroupingFuncs are treated mostly like Aggrefs, and so do not need  *    their own flag bits.  *  *    CurrentOfExpr nodes are ignored in all cases.  *  *    Upper-level vars (with varlevelsup > 0) should not be seen here,  *    likewise for upper-level Aggrefs and PlaceHolderVars.  *  *    Returns list of nodes found.  Note the nodes themselves are not  *    copied, only referenced.  *  * Does not examine subqueries, therefore must only be used after reduction  * of sublinks to subplans!  */ List * pull_var_clause(Node *node, int flags) {     pull_var_clause_context context;//上下文      //互斥选项检测     /* Assert that caller has not specified inconsistent flags */     Assert((flags & (PVC_INCLUDE_AGGREGATES | PVC_RECURSE_AGGREGATES))            != (PVC_INCLUDE_AGGREGATES | PVC_RECURSE_AGGREGATES));     Assert((flags & (PVC_INCLUDE_WINDOWFUNCS | PVC_RECURSE_WINDOWFUNCS))            != (PVC_INCLUDE_WINDOWFUNCS | PVC_RECURSE_WINDOWFUNCS));     Assert((flags & (PVC_INCLUDE_PLACEHOLDERS | PVC_RECURSE_PLACEHOLDERS))            != (PVC_INCLUDE_PLACEHOLDERS | PVC_RECURSE_PLACEHOLDERS));      context.varlist = NIL;     context.flags = flags;      pull_var_clause_walker(node, &context);//调用XX_walker函数遍历,结果保存在context.varlist中     return context.varlist; }  static bool pull_var_clause_walker(Node *node, pull_var_clause_context *context) {     if (node == NULL)         return false;     if (IsA(node, Var))//Var类型     {         if (((Var *) node)->varlevelsup != 0)//非本级Var             elog(ERROR, "Upper-level Var found where not expected");         context->varlist = lappend(context->varlist, node);//添加到结果链表中         return false;     }     else if (IsA(node, Aggref))//聚合     {         if (((Aggref *) node)->agglevelsup != 0)             elog(ERROR, "Upper-level Aggref found where not expected");         if (context->flags & PVC_INCLUDE_AGGREGATES)//包含聚合         {             context->varlist = lappend(context->varlist, node);//添加到结果             /* we do NOT descend into the contained expression */             return false;         }         else if (context->flags & PVC_RECURSE_AGGREGATES)//递归搜索         {             /* fall through to recurse into the aggregate's arguments */         }         else             elog(ERROR, "Aggref found where not expected");     }     else if (IsA(node, GroupingFunc))//分组     {         if (((GroupingFunc *) node)->agglevelsup != 0)             elog(ERROR, "Upper-level GROUPING found where not expected");         if (context->flags & PVC_INCLUDE_AGGREGATES)//包含标记         {             context->varlist = lappend(context->varlist, node);             /* we do NOT descend into the contained expression */             return false;         }         else if (context->flags & PVC_RECURSE_AGGREGATES)//递归标记         {             /*              * We do NOT descend into the contained expression, even if the              * caller asked for it, because we never actually evaluate it -              * the result is driven entirely off the associated GROUP BY              * clause, so we never need to extract the actual Vars here.              */             return false;//直接返回,需与GROUP BY语句一起         }         else             elog(ERROR, "GROUPING found where not expected");     }     else if (IsA(node, WindowFunc))//窗口函数     {         /* WindowFuncs have no levelsup field to check ... */         if (context->flags & PVC_INCLUDE_WINDOWFUNCS)//包含标记         {             context->varlist = lappend(context->varlist, node);             /* we do NOT descend into the contained expressions */             return false;         }         else if (context->flags & PVC_RECURSE_WINDOWFUNCS)//递归标记         {             /* fall through to recurse into the windowfunc's arguments */         }         else             elog(ERROR, "WindowFunc found where not expected");     }     else if (IsA(node, PlaceHolderVar))//PH     {         if (((PlaceHolderVar *) node)->phlevelsup != 0)             elog(ERROR, "Upper-level PlaceHolderVar found where not expected");         if (context->flags & PVC_INCLUDE_PLACEHOLDERS)         {             context->varlist = lappend(context->varlist, node);             /* we do NOT descend into the contained expression */             return false;         }         else if (context->flags & PVC_RECURSE_PLACEHOLDERS)         {             /* fall through to recurse into the placeholder's expression */         }         else             elog(ERROR, "PlaceHolderVar found where not expected");     }     return expression_tree_walker(node, pull_var_clause_walker,                                   (void *) context);//表达式解析 } 

三、跟踪分析

重点考察root->simple_rel_array[n]->attr_needed、root->simple_rel_array[n]->reltarget、root->placeholder_list、root->simple_rel_array[n]->lateral_vars.
启动gdb跟踪

(gdb) b build_base_rel_tlistsBreakpoint 1 at 0x76551b: file initsplan.c, line 153.(gdb) cContinuing.Breakpoint 1, build_base_rel_tlists (root=0x171ae40, final_tlist=0x1734750) at initsplan.c:153153     List       *tlist_vars = pull_var_clause((Node *) final_tlist,(gdb) 

final_tlist是最终的输出列(投影列),一共有5个,分别是t_dwxx.dwmc/dwbh/dwdz,t_grxx.grbh,t_jfxx.je

(gdb) p *final_tlist$1 = {type = T_List, length = 5, head = 0x1734730, tail = 0x1734a60}(gdb) p *(Node *)final_tlist->head->data.ptr_value$2 = {type = T_TargetEntry}(gdb) p *(TargetEntry *)final_tlist->head->data.ptr_value$3 = {xpr = {type = T_TargetEntry}, expr = 0x17346e0, resno = 1, resname = 0x171a5a0 "dwmc", ressortgroupref = 0,   resorigtbl = 16394, resorigcol = 1, resjunk = false}

跟踪函数build_base_rel_tlists

(gdb) n158     if (tlist_vars != NIL)(gdb) 160         add_vars_to_targetlist(root, tlist_vars, bms_make_singleton(0), true);#5个Vars(gdb) p *tlist_vars$4 = {type = T_List, length = 5, head = 0x17369e0, tail = 0x1737cb8}(gdb) p *(Var *)tlist_vars->head->data.ptr_value$5 = {xpr = {type = T_Var}, varno = 1, varattno = 1, vartype = 1043, vartypmod = 104, varcollid = 100, varlevelsup = 0,   varnoold = 1, varoattno = 1, location = 7}

执行函数build_base_rel_tlists,检查final_rel->attr_needed和final_rel->reltarget

(gdb) finishRun till exit from #0  build_base_rel_tlists (root=0x171ae40, final_tlist=0x1734750) at initsplan.c:160query_planner (root=0x171ae40, tlist=0x1734750, qp_callback=0x76e97d , qp_extra=0x7ffe20fc33d0)    at planmain.c:152152     find_placeholders_in_jointree(root);

检查root内存结构

(gdb) p *root$15 = {type = T_PlannerInfo, parse = 0x1711680, glob = 0x1732118, query_level = 1, parent_root = 0x0, plan_params = 0x0,   outer_params = 0x0, simple_rel_array = 0x1736578, simple_rel_array_size = 6, simple_rte_array = 0x17365c8,   all_baserels = 0x0, nullable_baserels = 0x0, join_rel_list = 0x0, join_rel_hash = 0x0, join_rel_level = 0x0,   join_cur_level = 0, init_plans = 0x0, cte_plan_ids = 0x0, multiexpr_params = 0x0, eq_classes = 0x0, canon_pathkeys = 0x0,   left_join_clauses = 0x0, right_join_clauses = 0x0, full_join_clauses = 0x0, join_info_list = 0x0, append_rel_list = 0x0,   rowMarks = 0x0, placeholder_list = 0x0, fkey_list = 0x0, query_pathkeys = 0x0, group_pathkeys = 0x0,   window_pathkeys = 0x0, distinct_pathkeys = 0x0, sort_pathkeys = 0x0, part_schemes = 0x0, initial_rels = 0x0,   upper_rels = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, upper_targets = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},   processed_tlist = 0x1734750, grouping_map = 0x0, minmax_aggs = 0x0, planner_cxt = 0x165a040, total_table_pages = 0,   tuple_fraction = 0, limit_tuples = -1, qual_security_level = 0, inhTargetKind = INHKIND_NONE, hasJoinRTEs = true,   hasLateralRTEs = true, hasDeletedRTEs = false, hasHavingQual = false, hasPseudoConstantQuals = false,   hasRecursion = false, wt_param_id = -1, non_recursive_path = 0x0, curOuterRels = 0x0, curOuterParams = 0x0,   join_search_private = 0x0, partColsUpdated = false}

RelOptInfo数组,注意数组的第0(下标)个元素为NULL(无用),有用的元素下标从1开始:
第1个元素是基础关系(相应的RTE=t_dwxx),第2个元素为NULL(相应的RTE=子查询),第3个元素为基础关系(相应的RTE=t_grxx),第4个元素为基础关系(相应的RTE=t_jfxx),第5个元素为NULL(相应的RTE=连接)

(gdb) p *root->simple_rel_array[0]Cannot access memory at address 0x0(gdb) p *root->simple_rel_array[1]$31 = {type = T_RelOptInfo, reloptkind = RELOPT_BASEREL, relids = 0x1736828, rows = 0, consider_startup = false,   consider_param_startup = false, consider_parallel = false, reltarget = 0x1736840, pathlist = 0x0, ppilist = 0x0,   partial_pathlist = 0x0, cheapest_startup_path = 0x0, cheapest_total_path = 0x0, cheapest_unique_path = 0x0,   cheapest_parameterized_paths = 0x0, direct_lateral_relids = 0x0, lateral_relids = 0x0, relid = 1, reltablespace = 0,   rtekind = RTE_RELATION, min_attr = -7, max_attr = 3, attr_needed = 0x1736890, attr_widths = 0x1736920,   lateral_vars = 0x0, lateral_referencers = 0x0, indexlist = 0x1736cc8, statlist = 0x0, pages = 1, tuples = 3,   allvisfrac = 0, subroot = 0x0, subplan_params = 0x0, rel_parallel_workers = -1, serverid = 0, userid = 0,   useridiscurrent = false, fdwroutine = 0x0, fdw_private = 0x0, unique_for_rels = 0x0, non_unique_for_rels = 0x0,   baserestrictinfo = 0x0, baserestrictcost = {startup = 0, per_tuple = 0}, baserestrict_min_security = 4294967295,   joininfo = 0x0, has_eclass_joins = false, top_parent_relids = 0x0, part_scheme = 0x0, nparts = 0, boundinfo = 0x0,   partition_qual = 0x0, part_rels = 0x0, partexprs = 0x0, nullable_partexprs = 0x0, partitioned_child_rels = 0x0}(gdb) p *root->simple_rel_array[2]Cannot access memory at address 0x0(gdb) p *root->simple_rel_array[3]$32 = {type = T_RelOptInfo, reloptkind = RELOPT_BASEREL, relids = 0x17377f8, rows = 0, consider_startup = false,   consider_param_startup = false, consider_parallel = false, reltarget = 0x1737810, pathlist = 0x0, ppilist = 0x0,   partial_pathlist = 0x0, cheapest_startup_path = 0x0, cheapest_total_path = 0x0, cheapest_unique_path = 0x0,   cheapest_parameterized_paths = 0x0, direct_lateral_relids = 0x0, lateral_relids = 0x0, relid = 3, reltablespace = 0,   rtekind = RTE_RELATION, min_attr = -7, max_attr = 5, attr_needed = 0x1737860, attr_widths = 0x17378f0,   lateral_vars = 0x0, lateral_referencers = 0x0, indexlist = 0x0, statlist = 0x0, pages = 10, tuples = 400, allvisfrac = 0,   subroot = 0x0, subplan_params = 0x0, rel_parallel_workers = -1, serverid = 0, userid = 0, useridiscurrent = false,   fdwroutine = 0x0, fdw_private = 0x0, unique_for_rels = 0x0, non_unique_for_rels = 0x0, baserestrictinfo = 0x0,   baserestrictcost = {startup = 0, per_tuple = 0}, baserestrict_min_security = 4294967295, joininfo = 0x0,   has_eclass_joins = false, top_parent_relids = 0x0, part_scheme = 0x0, nparts = 0, boundinfo = 0x0, partition_qual = 0x0,   part_rels = 0x0, partexprs = 0x0, nullable_partexprs = 0x0, partitioned_child_rels = 0x0}(gdb) p *root->simple_rel_array[4]$33 = {type = T_RelOptInfo, reloptkind = RELOPT_BASEREL, relids = 0x1737b50, rows = 0, consider_startup = false,   consider_param_startup = false, consider_parallel = false, reltarget = 0x1737b68, pathlist = 0x0, ppilist = 0x0,   partial_pathlist = 0x0, cheapest_startup_path = 0x0, cheapest_total_path = 0x0, cheapest_unique_path = 0x0,   cheapest_parameterized_paths = 0x0, direct_lateral_relids = 0x0, lateral_relids = 0x0, relid = 4, reltablespace = 0,   rtekind = RTE_RELATION, min_attr = -7, max_attr = 3, attr_needed = 0x1737bb8, attr_widths = 0x1737c48,   lateral_vars = 0x0, lateral_referencers = 0x0, indexlist = 0x0, statlist = 0x0, pages = 10, tuples = 720, allvisfrac = 0,   subroot = 0x0, subplan_params = 0x0, rel_parallel_workers = -1, serverid = 0, userid = 0, useridiscurrent = false,   fdwroutine = 0x0, fdw_private = 0x0, unique_for_rels = 0x0, non_unique_for_rels = 0x0, baserestrictinfo = 0x0,   baserestrictcost = {startup = 0, per_tuple = 0}, baserestrict_min_security = 4294967295, joininfo = 0x0,   has_eclass_joins = false, top_parent_relids = 0x0, part_scheme = 0x0, nparts = 0, boundinfo = 0x0, partition_qual = 0x0,   part_rels = 0x0, partexprs = 0x0, nullable_partexprs = 0x0, partitioned_child_rels = 0x0}(gdb) p *root->simple_rel_array[5]Cannot access memory at address 0x0

查看root->simple_rel_array[n]->attr_needed、root->simple_rel_array[n]->reltarget的内存结构,以第1个元素为例:

#attr_needed(类型为Relids)为NULL(gdb) p *root->simple_rel_array[1]->attr_needed$47 = (Relids) 0x0#reltarget->exprs为3个Var的链表(gdb) p *root->simple_rel_array[1]->reltarget->exprs$38 = {type = T_List, length = 3, head = 0x1737d40, tail = 0x1737e80}(gdb) p *(Var *)root->simple_rel_array[1]->reltarget->exprs->head->data.ptr_value$40 = {xpr = {type = T_Var}, varno = 1, varattno = 1, vartype = 1043, vartypmod = 104, varcollid = 100, varlevelsup = 0,   varnoold = 1, varoattno = 1, location = 7}

继续执行,调用函数find_placeholders_in_jointree

152     find_placeholders_in_jointree(root);(gdb) stepfind_placeholders_in_jointree (root=0x171ae40) at placeholder.c:148148     if (root->glob->lastPHId != 0)(gdb) n155 }(gdb) n154     find_lateral_references(root);#链表为NULL#psql中的:v1似乎不是占位符,理解有偏差//TODO...(gdb) p root->placeholder_list$48 = (List *) 0x0

下面调用子函数find_lateral_references,由于RelOptInfo中的3个不为NULL的元素对应的lateral均为FALSE(只有子查询的lateral为true,但子查询对应的RelOptInfo为NULL),因此root->simple_rel_array[1/3/4/5]->lateral_vars均为NULL

四、参考资料

initsplan.c

0