博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
PostgreSQL 源码解读(64)- 查询语句#49(make_one_rel函数#14-...
阅读量:2497 次
发布时间:2019-05-11

本文共 34431 字,大约阅读时间需要 114 分钟。

本节大体介绍了动态规划算法实现(standard_join_search)中的join_search_one_level->make_join_rel函数,该函数创建两个rels连接所生成的RelOptInfo,并在RelOptInfo中添加访问路径信息。

一、数据结构

RelOptInfo

typedef enum RelOptKind {     RELOPT_BASEREL,//基本关系(如基表/子查询等)     RELOPT_JOINREL,//连接产生的关系,要注意的是通过连接等方式产生的结果亦可以视为关系     RELOPT_OTHER_MEMBER_REL,     RELOPT_OTHER_JOINREL,     RELOPT_UPPER_REL,//上层的关系     RELOPT_OTHER_UPPER_REL,     RELOPT_DEADREL } RelOptKind;  /*  * Is the given relation a simple relation i.e a base or "other" member  * relation?  */ #define IS_SIMPLE_REL(rel) \     ((rel)->reloptkind == RELOPT_BASEREL || \      (rel)->reloptkind == RELOPT_OTHER_MEMBER_REL)  /* Is the given relation a join relation? */ #define IS_JOIN_REL(rel)    \     ((rel)->reloptkind == RELOPT_JOINREL || \      (rel)->reloptkind == RELOPT_OTHER_JOINREL)  /* Is the given relation an upper relation? */ #define IS_UPPER_REL(rel)   \     ((rel)->reloptkind == RELOPT_UPPER_REL || \      (rel)->reloptkind == RELOPT_OTHER_UPPER_REL)  /* Is the given relation an "other" relation? */ #define IS_OTHER_REL(rel) \     ((rel)->reloptkind == RELOPT_OTHER_MEMBER_REL || \      (rel)->reloptkind == RELOPT_OTHER_JOINREL || \      (rel)->reloptkind == RELOPT_OTHER_UPPER_REL)  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;

二、源码解读

join_search_one_level->...(如make_rels_by_clause_joins)->make_join_rel函数创建两个rels连接所生成的RelOptInfo,并创建访问路径添加到RelOptInfo的pathlist链表中。这里重点介绍make_join_rel函数中的build_join_rel函数,populate_joinrel_with_paths函数下一小节再行介绍.

//---------------------------------------------------- make_join_rel /*  * make_join_rel  *     Find or create a join RelOptInfo that represents the join of  *     the two given rels, and add to it path information for paths  *     created with the two rels as outer and inner rel.  *     (The join rel may already contain paths generated from other  *     pairs of rels that add up to the same set of base rels.)  *     创建两个rels连接所生成的RelOptInfo,并添加访问路径信息.  *     (新产生的rel可能已经包含从相同的两个rels对所生成的的路径)  *  * NB: will return NULL if attempted join is not valid.  This can happen  * when working with outer joins, or with IN or EXISTS clauses that have been  * turned into joins.  * 注意:如果尝试连接失败,则返回NULL.这可能出现在处理外连接或者已转变为连接的IN/EXISTS子句上  */ RelOptInfo * make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2) {     Relids      joinrelids;     SpecialJoinInfo *sjinfo;     bool        reversed;     SpecialJoinInfo sjinfo_data;     RelOptInfo *joinrel;     List       *restrictlist;      /* We should never try to join two overlapping sets of rels. */     Assert(!bms_overlap(rel1->relids, rel2->relids));//两者无交接      /* Construct Relids set that identifies the joinrel. */     joinrelids = bms_union(rel1->relids, rel2->relids);//两个rel涉及的rels      /* Check validity and determine join type. */     if (!join_is_legal(root, rel1, rel2, joinrelids,                        &sjinfo, &reversed))//是否非法     {         /* invalid join path */         bms_free(joinrelids);         return NULL;//返回     }      /* Swap rels if needed to match the join info. */     if (reversed)//位置是否调换     {         RelOptInfo *trel = rel1;          rel1 = rel2;         rel2 = trel;     }      /*      * If it's a plain inner join, then we won't have found anything in      * join_info_list.  Make up a SpecialJoinInfo so that selectivity      * estimation functions will know what's being joined.      * 普通的内连接,不需要使用join_info_list,      * 构造一个SpecialJoinInfo以便告知选择率估算函数已连接      */     if (sjinfo == NULL)     {         sjinfo = &sjinfo_data;         sjinfo->type = T_SpecialJoinInfo;         sjinfo->min_lefthand = rel1->relids;         sjinfo->min_righthand = rel2->relids;         sjinfo->syn_lefthand = rel1->relids;         sjinfo->syn_righthand = rel2->relids;         sjinfo->jointype = JOIN_INNER;         /* we don't bother trying to make the remaining fields valid */         sjinfo->lhs_strict = false;         sjinfo->delay_upper_joins = false;         sjinfo->semi_can_btree = false;         sjinfo->semi_can_hash = false;         sjinfo->semi_operators = NIL;         sjinfo->semi_rhs_exprs = NIL;     }      /*      * Find or build the join RelOptInfo, and compute the restrictlist that      * goes with this particular joining.      * 创建连接生成的新关系RelOptInfo,并为此连接生成限制条件链表      */     joinrel = build_join_rel(root, joinrelids, rel1, rel2, sjinfo,                              &restrictlist);      /*      * If we've already proven this join is empty, we needn't consider any      * more paths for it.      */     if (is_dummy_rel(joinrel))     {         bms_free(joinrelids);         return joinrel;     }      /* Add paths to the join relation. */     //为连接生成的新关系构造访问路径     populate_joinrel_with_paths(root, rel1, rel2, joinrel, sjinfo,                                 restrictlist);      bms_free(joinrelids);//释放资源      return joinrel;//返回joinrel } //-------------------------------------------------------------------- build_join_rel /*  * build_join_rel  *    Returns relation entry corresponding to the union of two given rels,  *    creating a new relation entry if none already exists.  *    给定两个rels,创建并返回对应这两个rels连接生成的新的Relation  *   * 'joinrelids' is the Relids set that uniquely identifies the join  * 'outer_rel' and 'inner_rel' are relation nodes for the relations to be  *      joined  * 'sjinfo': join context info  * 'restrictlist_ptr': result variable.  If not NULL, *restrictlist_ptr  *      receives the list of RestrictInfo nodes that apply to this  *      particular pair of joinable relations.  * joinrelids-与此连接相关的所有relids  * outer_rel和inner_rel-构成连接的外表(驱动表)和内表   * sjinfo-连接上下文信息  * restrictlist_ptr-存储结果的变量,如为非NULL值,该指针指向RestrictInfo(约束条件)节点链表  *  * restrictlist_ptr makes the routine's API a little grotty, but it saves  * duplicated calculation of the restrictlist...  */ RelOptInfo * build_join_rel(PlannerInfo *root,                Relids joinrelids,                RelOptInfo *outer_rel,                RelOptInfo *inner_rel,                SpecialJoinInfo *sjinfo,                List **restrictlist_ptr) {     RelOptInfo *joinrel;     List       *restrictlist;      /* This function should be used only for join between parents. */     Assert(!IS_OTHER_REL(outer_rel) && !IS_OTHER_REL(inner_rel));      /*      * See if we already have a joinrel for this set of base rels.      * 这些基础rels所构成的连接是否已存在?      */     joinrel = find_join_rel(root, joinrelids);      if (joinrel)//已存在     {         /*          * Yes, so we only need to figure the restrictlist for this particular          * pair of component relations.          */         if (restrictlist_ptr)             *restrictlist_ptr = build_joinrel_restrictlist(root,                                                            joinrel,                                                            outer_rel,                                                            inner_rel);//如已存在约束条件,则找出相应的信息即可         return joinrel;//返回     }      /*      * Nope, so make one.      * 没有,则构造之      */     joinrel = makeNode(RelOptInfo);     joinrel->reloptkind = RELOPT_JOINREL;     joinrel->relids = bms_copy(joinrelids);     joinrel->rows = 0;     /* cheap startup cost is interesting iff not all tuples to be retrieved */     joinrel->consider_startup = (root->tuple_fraction > 0);     joinrel->consider_param_startup = false;     joinrel->consider_parallel = false;     joinrel->reltarget = create_empty_pathtarget();     joinrel->pathlist = NIL;     joinrel->ppilist = NIL;     joinrel->partial_pathlist = NIL;     joinrel->cheapest_startup_path = NULL;     joinrel->cheapest_total_path = NULL;     joinrel->cheapest_unique_path = NULL;     joinrel->cheapest_parameterized_paths = NIL;     /* init direct_lateral_relids from children; we'll finish it up below */     joinrel->direct_lateral_relids =         bms_union(outer_rel->direct_lateral_relids,                   inner_rel->direct_lateral_relids);     joinrel->lateral_relids = min_join_parameterization(root, joinrel->relids,                                                         outer_rel, inner_rel);     joinrel->relid = 0;         /* indicates not a baserel */     joinrel->rtekind = RTE_JOIN;//RTE_JOIN     joinrel->min_attr = 0;     joinrel->max_attr = 0;     joinrel->attr_needed = NULL;     joinrel->attr_widths = NULL;     joinrel->lateral_vars = NIL;     joinrel->lateral_referencers = NULL;     joinrel->indexlist = NIL;     joinrel->statlist = NIL;     joinrel->pages = 0;     joinrel->tuples = 0;     joinrel->allvisfrac = 0;     joinrel->subroot = NULL;     joinrel->subplan_params = NIL;     joinrel->rel_parallel_workers = -1;     joinrel->serverid = InvalidOid;     joinrel->userid = InvalidOid;     joinrel->useridiscurrent = false;     joinrel->fdwroutine = NULL;     joinrel->fdw_private = NULL;     joinrel->unique_for_rels = NIL;     joinrel->non_unique_for_rels = NIL;     joinrel->baserestrictinfo = NIL;     joinrel->baserestrictcost.startup = 0;     joinrel->baserestrictcost.per_tuple = 0;     joinrel->baserestrict_min_security = UINT_MAX;     joinrel->joininfo = NIL;     joinrel->has_eclass_joins = false;     joinrel->consider_partitionwise_join = false; /* might get changed later */     joinrel->top_parent_relids = NULL;     joinrel->part_scheme = NULL;     joinrel->nparts = 0;     joinrel->boundinfo = NULL;     joinrel->partition_qual = NIL;     joinrel->part_rels = NULL;     joinrel->partexprs = NULL;     joinrel->nullable_partexprs = NULL;     joinrel->partitioned_child_rels = NIL;      /* 设置FDW的相关信息,Compute information relevant to the foreign relations. */     set_foreign_rel_properties(joinrel, outer_rel, inner_rel);      /*      * Create a new tlist containing just the vars that need to be output from      * this join (ie, are needed for higher joinclauses or final output).      *      * 创建一个新的投影列链表,只包含该连接的输出      * NOTE: the tlist order for a join rel will depend on which pair of outer      * and inner rels we first try to build it from.  But the contents should      * be the same regardless.      */     build_joinrel_tlist(root, joinrel, outer_rel);//连接外表     build_joinrel_tlist(root, joinrel, inner_rel);//连接内表     add_placeholders_to_joinrel(root, joinrel, outer_rel, inner_rel);//添加PHV      /*      * add_placeholders_to_joinrel also took care of adding the ph_lateral      * sets of any PlaceHolderVars computed here to direct_lateral_relids, so      * now we can finish computing that.  This is much like the computation of      * the transitively-closed lateral_relids in min_join_parameterization,      * except that here we *do* have to consider the added PHVs.      * add_placeholders_to_joinrel函数将这里产生的PlaceHolderVars的ph_lateral集合      * 添加到direct_lateral_relids中以完成最终的处理。      * 这非常类似于min_join_parameterization的transtivelyclosed lateral al_relid的计算,      * 只是这里需要考虑添加的phv。      */     joinrel->direct_lateral_relids =         bms_del_members(joinrel->direct_lateral_relids, joinrel->relids);     if (bms_is_empty(joinrel->direct_lateral_relids))         joinrel->direct_lateral_relids = NULL;      /*      * Construct restrict and join clause lists for the new joinrel. (The      * caller might or might not need the restrictlist, but I need it anyway      * for set_joinrel_size_estimates().)      * 为新产生的joinrel构造约束和连接条件链表      */     restrictlist = build_joinrel_restrictlist(root, joinrel,                                               outer_rel, inner_rel);//构建限制条件链表     if (restrictlist_ptr)         *restrictlist_ptr = restrictlist;     build_joinrel_joinlist(joinrel, outer_rel, inner_rel);//构建连接条件链表      /*      * This is also the right place to check whether the joinrel has any      * pending EquivalenceClass joins.      * 判断是否存在等价类EC      */     joinrel->has_eclass_joins = has_relevant_eclass_joinclause(root, joinrel);      /* S存储分区信息,tore the partition information. */     build_joinrel_partition_info(joinrel, outer_rel, inner_rel, restrictlist,                                  sjinfo->jointype);      /*      * 估算joinrel的大小,Set estimates of the joinrel's size.      */     set_joinrel_size_estimates(root, joinrel, outer_rel, inner_rel,                                sjinfo, restrictlist);      /*      * Set the consider_parallel flag if this joinrel could potentially be      * scanned within a parallel worker.  If this flag is false for either      * inner_rel or outer_rel, then it must be false for the joinrel also.      * Even if both are true, there might be parallel-restricted expressions      * in the targetlist or quals.      * 设置consider_parallel标记,如joinrel可以并行扫描的话      *      * Note that if there are more than two rels in this relation, they could      * be divided between inner_rel and outer_rel in any arbitrary way.  We      * assume this doesn't matter, because we should hit all the same baserels      * and joinclauses while building up to this joinrel no matter which we      * take; therefore, we should make the same decision here however we get      * here.      */     if (inner_rel->consider_parallel && outer_rel->consider_parallel &&         is_parallel_safe(root, (Node *) restrictlist) &&         is_parallel_safe(root, (Node *) joinrel->reltarget->exprs))         joinrel->consider_parallel = true;      /* Add the joinrel to the PlannerInfo. */     add_join_rel(root, joinrel);//添加到优化器信息中      /*      * Also, if dynamic-programming join search is active, add the new joinrel      * to the appropriate sublist.  Note: you might think the Assert on number      * of members should be for equality, but some of the level 1 rels might      * have been joinrels already, so we can only assert <=.      * 添加到合适的链表中root->join_rel_levep[j]      */     if (root->join_rel_level)     {         Assert(root->join_cur_level > 0);         Assert(root->join_cur_level <= bms_num_members(joinrel->relids));         root->join_rel_level[root->join_cur_level] =             lappend(root->join_rel_level[root->join_cur_level], joinrel);//加入到链表中     }      return joinrel; }//----------------------------------------------- find_join_rel /*  * find_join_rel  *    Returns relation entry corresponding to 'relids' (a set of RT indexes),  *    or NULL if none exists.  This is for join relations.  *    返回对应relids(RT indexes的集合)的RelOptInfo,如无则返回NULL.  */ RelOptInfo * find_join_rel(PlannerInfo *root, Relids relids) {     /*      * Switch to using hash lookup when list grows "too long".  The threshold      * is arbitrary and is known only here.      * 如链表过长,则改用hash查找      */     if (!root->join_rel_hash && list_length(root->join_rel_list) > 32)         build_join_rel_hash(root);      /*      * Use either hashtable lookup or linear search, as appropriate.      * 使用hash表查找或者线性搜索      *      * Note: the seemingly redundant hashkey variable is used to avoid taking      * the address of relids; unless the compiler is exceedingly smart, doing      * so would force relids out of a register and thus probably slow down the      * list-search case.      */     if (root->join_rel_hash)//hash     {         Relids      hashkey = relids;         JoinHashEntry *hentry;          hentry = (JoinHashEntry *) hash_search(root->join_rel_hash,                                                &hashkey,                                                HASH_FIND,                                                NULL);         if (hentry)             return hentry->join_rel;     }     else//线性     {         ListCell   *l;          foreach(l, root->join_rel_list)         {             RelOptInfo *rel = (RelOptInfo *) lfirst(l);              if (bms_equal(rel->relids, relids))                 return rel;         }     }      return NULL; } //----------------------------------------------- build_joinrel_restrictlist /*  * build_joinrel_restrictlist  * build_joinrel_joinlist  *    These routines build lists of restriction and join clauses for a  *    join relation from the joininfo lists of the relations it joins.  *    从关系的joininfo链表中建立限制条件和连接条件链表  *      *    These routines are separate because the restriction list must be  *    built afresh for each pair of input sub-relations we consider, whereas  *    the join list need only be computed once for any join RelOptInfo.  *    The join list is fully determined by the set of rels making up the  *    joinrel, so we should get the same results (up to ordering) from any  *    candidate pair of sub-relations.  But the restriction list is whatever  *    is not handled in the sub-relations, so it depends on which  *    sub-relations are considered.  *    这些处理过程是独立的,因为限制条件链表必须为所考虑的每一对输入子关系重新构建,  *    而连接条件链表只需要为任何连接RelOptInfo计算一次即可。  *    连接链表完全由组成joinrel的一组rels决定,  *    因此从任何子关系的候选对中都应该得到相同的结果(直到排序过程)。  *    但是限制条件链表是子关系中没有处理的内容,所以它取决于考虑的子关系。     *  *    If a join clause from an input relation refers to base rels still not  *    present in the joinrel, then it is still a join clause for the joinrel;  *    we put it into the joininfo list for the joinrel.  Otherwise,  *    the clause is now a restrict clause for the joined relation, and we  *    return it to the caller of build_joinrel_restrictlist() to be stored in  *    join paths made from this pair of sub-relations.  (It will not need to  *    be considered further up the join tree.)  *    如果构成连接关系中的连接条件子句指向的base rels不在joinrel中,  *    那么它仍然是joinrel的连接条件子句;这些信息会放到joinrel的joininfo链表中。  *    否则,如果条件子句现在是连接关系的限制子句,  *    那么将它返回给build_joinrel_restrictlist()的调用方,将其存储在由这对子关系构成的连接路径中。  *    (它不需要被认为位于连接树的更上层。)  *  *    In many case we will find the same RestrictInfos in both input  *    relations' joinlists, so be careful to eliminate duplicates.  *    Pointer equality should be a sufficient test for dups, since all  *    the various joinlist entries ultimately refer to RestrictInfos  *    pushed into them by distribute_restrictinfo_to_rels().  *    在许多情况下,在两个关系的连接列表中可以发现相同的RestrictInfos,因此要小心排除重复。  *    指针相等的判断应该是对重复值的充分测试,因为所有的joinlist条目最终  *    都指向distribute_restrictinfo_to_rels()推入的RestrictInfos。  *  * 'joinrel' is a join relation node,连接新产生的关系  * 'outer_rel' and 'inner_rel' are a pair of relations that can be joined  *      to form joinrel.连接的外表和内表  *  * build_joinrel_restrictlist() returns a list of relevant restrictinfos,  * whereas build_joinrel_joinlist() stores its results in the joinrel's  * joininfo list.  One or the other must accept each given clause!  * build_joinrel_restrictlist()返回相关限制条件的链表,  * 而build_joinrel_joinlist()把结果存储在joinrel的joininfo链表中  *  * NB: Formerly, we made deep(!) copies of each input RestrictInfo to pass  * up to the join relation.  I believe this is no longer necessary, because  * RestrictInfo nodes are no longer context-dependent.  Instead, just include  * the original nodes in the lists made for the join relation.  */ static List * build_joinrel_restrictlist(PlannerInfo *root,                            RelOptInfo *joinrel,                            RelOptInfo *outer_rel,                            RelOptInfo *inner_rel) {     List       *result;      /*      * Collect all the clauses that syntactically belong at this level,      * eliminating any duplicates (important since we will see many of the      * same clauses arriving from both input relations).      * 收集语法上属于该级别的所有限制条件子句,消除任何重复(这很重要,因为存在来自两个关系的相同子句)。      */     result = subbuild_joinrel_restrictlist(joinrel, outer_rel->joininfo, NIL);     result = subbuild_joinrel_restrictlist(joinrel, inner_rel->joininfo, result);      /*      * Add on any clauses derived from EquivalenceClasses.  These cannot be      * redundant with the clauses in the joininfo lists, so don't bother      * checking.      * 添加来自EC的条件子句.      */     result = list_concat(result,                          generate_join_implied_equalities(root,                                                           joinrel->relids,                                                           outer_rel->relids,                                                           inner_rel));      return result; }   static void build_joinrel_joinlist(RelOptInfo *joinrel,                        RelOptInfo *outer_rel,                        RelOptInfo *inner_rel) {     List       *result;      /*      * Collect all the clauses that syntactically belong above this level,      * eliminating any duplicates (important since we will see many of the      * same clauses arriving from both input relations).      * 收集语法上属于该级别的所有连接条件子句,消除任何重复(这很重要,因为存在来自两个关系的相同子句)。      */     result = subbuild_joinrel_joinlist(joinrel, outer_rel->joininfo, NIL);     result = subbuild_joinrel_joinlist(joinrel, inner_rel->joininfo, result);      joinrel->joininfo = result; }

三、跟踪分析

测试表和数据继续沿用上一节创建的表和数据,使用的SQL语句如下:

testdb=# explain verbose select a.*,b.c1,c.c2,d.c2,e.c1,f.c2from a inner join b on a.c1=b.c1,c,d,e inner join f on e.c1 = f.c1 and e.c1 < 100where a.c1=f.c1 and b.c1=c.c1 and c.c1 = d.c1 and d.c1 = e.c1;                                                QUERY PLAN                                                ---------------------------------------------------------------------------------------------------------- Nested Loop  (cost=101.17..2218.24 rows=2 width=42)   Output: a.c1, a.c2, b.c1, c.c2, d.c2, e.c1, f.c2   Join Filter: (a.c1 = b.c1)   ->  Hash Join  (cost=3.25..196.75 rows=100 width=22)         Output: a.c1, a.c2, c.c2, c.c1         Hash Cond: (c.c1 = a.c1)         ->  Seq Scan on public.c  (cost=0.00..155.00 rows=10000 width=12)               Output: c.c1, c.c2         ->  Hash  (cost=2.00..2.00 rows=100 width=10)               Output: a.c1, a.c2               ->  Seq Scan on public.a  (cost=0.00..2.00 rows=100 width=10)                     Output: a.c1, a.c2   ->  Materialize  (cost=97.92..2014.00 rows=5 width=32)         Output: b.c1, d.c2, d.c1, e.c1, f.c2, f.c1         ->  Hash Join  (cost=97.92..2013.97 rows=5 width=32)               Output: b.c1, d.c2, d.c1, e.c1, f.c2, f.c1               Hash Cond: (f.c1 = b.c1)               ->  Seq Scan on public.f  (cost=0.00..1541.00 rows=100000 width=13)                     Output: f.c1, f.c2               ->  Hash  (cost=97.86..97.86 rows=5 width=19)                     Output: b.c1, d.c2, d.c1, e.c1                     ->  Hash Join  (cost=78.10..97.86 rows=5 width=19)                           Output: b.c1, d.c2, d.c1, e.c1                           Hash Cond: (b.c1 = e.c1)                           ->  Seq Scan on public.b  (cost=0.00..16.00 rows=1000 width=4)                                 Output: b.c1, b.c2                           ->  Hash  (cost=78.04..78.04 rows=5 width=15)                                 Output: d.c2, d.c1, e.c1                                 ->  Hash Join  (cost=73.24..78.04 rows=5 width=15)                                       Output: d.c2, d.c1, e.c1                                       Hash Cond: (d.c1 = e.c1)                                       ->  Seq Scan on public.d  (cost=0.00..4.00 rows=200 width=11)                                             Output: d.c1, d.c2                                       ->  Hash  (cost=72.00..72.00 rows=99 width=4)                                             Output: e.c1                                             ->  Seq Scan on public.e  (cost=0.00..72.00 rows=99 width=4)                                                   Output: e.c1                                                   Filter: (e.c1 < 100)(38 rows)

优化器选择了2 rels + 4 rels的连接模式,跟踪重点考察bushy plans的执行情况.

启动gdb,设置断点,只考察level=6的情况

(gdb) b join_search_one_levelBreakpoint 2 at 0x7b0289: file joinrels.c, line 67.(gdb) cContinuing....(gdb) cContinuing.Breakpoint 2, join_search_one_level (root=0x241ca38, level=6) at joinrels.c:6767      List      **joinrels = root->join_rel_level;

完成5(rels)+1(rels)的调用

(gdb) b joinrels.c:142Breakpoint 3 at 0x7b03c4: file joinrels.c, line 142.(gdb) cContinuing.Breakpoint 3, join_search_one_level (root=0x241ca38, level=6) at joinrels.c:142142     for (k = 2;; k++)

查看root->join_rel_level[6]

(gdb) p *root->join_rel_level[6]$1 = {type = T_List, length = 1, head = 0x24c8468, tail = 0x24c8468}

查看该链表中的RelOptInfo

(gdb) set $roi=(RelOptInfo *)root->join_rel_level[6]->head->data.ptr_value(gdb) p *$roi$3 = {type = T_RelOptInfo, reloptkind = RELOPT_JOINREL, relids = 0x1eb8330, rows = 2, consider_startup = false,   consider_param_startup = false, consider_parallel = true, reltarget = 0x1f25ac8, pathlist = 0x1f25f80, 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 = 0, reltablespace = 0,   rtekind = RTE_JOIN, min_attr = 0, max_attr = 0, attr_needed = 0x0, attr_widths = 0x0, lateral_vars = 0x0,   lateral_referencers = 0x0, indexlist = 0x0, statlist = 0x0, pages = 0, tuples = 0, 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}

查看该RelOptInfo的pathlist

(gdb) p *$roi->pathlist$4 = {type = T_List, length = 1, head = 0x1f25f60, tail = 0x1f25f60}(gdb) p *(Node *)$roi->pathlist->head->data.ptr_value$5 = {type = T_NestPath}(gdb) set $np=(NestPath *)$roi->pathlist->head->data.ptr_value(gdb) p *(NestPath *)$np$5 = {path = {type = T_NestPath, pathtype = T_NestLoop, parent = 0x1f258b8, pathtarget = 0x1f25ac8, param_info = 0x0,     parallel_aware = false, parallel_safe = true, parallel_workers = 0, rows = 2, startup_cost = 290.57499999999999,     total_cost = 2216.1374999999998, pathkeys = 0x0}, jointype = JOIN_INNER, inner_unique = false,   outerjoinpath = 0x1f07c00, innerjoinpath = 0x1f27c40, joinrestrictinfo = 0x1f27e60}

查看该连接的外表和内部访问路径

(gdb) p *$np->outerjoinpath$6 = {type = T_Path, pathtype = T_SeqScan, parent = 0x1e228e8, pathtarget = 0x1f04bc0, param_info = 0x0,   parallel_aware = false, parallel_safe = true, parallel_workers = 0, rows = 100, startup_cost = 0, total_cost = 2,   pathkeys = 0x0}(gdb) p *$np->innerjoinpath$7 = {type = T_MaterialPath, pathtype = T_Material, parent = 0x1ebb538, pathtarget = 0x1ebb748, param_info = 0x0,   parallel_aware = false, parallel_safe = true, parallel_workers = 0, rows = 5, startup_cost = 290.57499999999999,   total_cost = 2206.6500000000001, pathkeys = 0x0}

下面开始尝试bushy plans,即(2/4 rels+ 4/2 rels)或(3 rels + 3 rels)模式,重点考察ac + bdef这种组合

(gdb) b joinrels.c:156Breakpoint 3 at 0x7557df: file joinrels.c, line 156.(gdb) cContinuing.Breakpoint 3, join_search_one_level (root=0x1e214b8, level=6) at joinrels.c:164164             if (old_rel->joininfo == NIL && !old_rel->has_eclass_joins &&(gdb) p *old_rel->relids->words$13 = 18

进入make_join_rel函数

173             for_each_cell(r2, other_rels)(gdb) 175                 RelOptInfo *new_rel = (RelOptInfo *) lfirst(r2);(gdb) 177                 if (!bms_overlap(old_rel->relids, new_rel->relids))(gdb) 184                     if (have_relevant_joinclause(root, old_rel, new_rel) ||(gdb) 187                         (void) make_join_rel(root, old_rel, new_rel);(gdb) stepmake_join_rel (root=0x1e214b8, rel1=0x1f079f0, rel2=0x1e96520) at joinrels.c:681681     joinrelids = bms_union(rel1->relids, rel2->relids);

进入build_join_rel函数,相应的RelOptInfo已存在,返回

(gdb) 728     joinrel = build_join_rel(root, joinrelids, rel1, rel2, sjinfo,(gdb) stepbuild_join_rel (root=0x1e214b8, joinrelids=0x1e401d8, outer_rel=0x1f079f0, inner_rel=0x1e96520, sjinfo=0x7fff247e18a0,     restrictlist_ptr=0x7fff247e1898) at relnode.c:498498     joinrel = find_join_rel(root, joinrelids);500     if (joinrel)(gdb) n506         if (restrictlist_ptr)(gdb) 507             *restrictlist_ptr = build_joinrel_restrictlist(root,(gdb) 511         return joinrel;

执行populate_joinrel_with_paths,该函数执行后再次查看外表和内部访问路径,变成了HashPath + MaterialPath的组合,具体的变化,下一节再行介绍.

...(gdb) 742     populate_joinrel_with_paths(root, rel1, rel2, joinrel, sjinfo,(gdb) n745     bms_free(joinrelids);(gdb) set $roi=(RelOptInfo *)root->join_rel_level[6]->head->data.ptr_value(gdb) set $np=(NestPath *)$roi->pathlist->head->data.ptr_value(gdb) p *$np->outerjoinpath$30 = {type = T_HashPath, pathtype = T_HashJoin, parent = 0x1f079f0, pathtarget = 0x1e41128, param_info = 0x0,   parallel_aware = false, parallel_safe = true, parallel_workers = 0, rows = 100, startup_cost = 3.25, total_cost = 196.75,   pathkeys = 0x0}(gdb) p *$np->innerjoinpath$31 = {type = T_MaterialPath, pathtype = T_Material, parent = 0x1e96520, pathtarget = 0x1e96730, param_info = 0x0,   parallel_aware = false, parallel_safe = true, parallel_workers = 0, rows = 5, startup_cost = 97.962499999999991,   total_cost = 2014.0375000000001, pathkeys = 0x0}

DONE!

四、参考资料

allpaths.c

cost.h
costsize.c
PG Document:Query Planning

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/6906/viewspace-2374836/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/6906/viewspace-2374836/

你可能感兴趣的文章
小程序丨页面去掉转发按钮
查看>>
centos6.4安装flashcache
查看>>
树、二叉树基础
查看>>
指针数组,数组指针,指针函数,函数指针,二级指针详解
查看>>
判断浏览器类型和版本
查看>>
七, 表查询 一
查看>>
ContentProvider备份短信,以xml文件存储
查看>>
file-loader及url-loader的使用
查看>>
熟悉常用的HBase操作,编写MapReduce作业
查看>>
关于clipboard插件的使用问题
查看>>
Pycharm使用中背景颜色和更改项目的Python版本
查看>>
关情纸尾-----UIKit基础-简述KVC和KVO
查看>>
[Poj]1905——二分
查看>>
Makefile 使用总结
查看>>
控制反转
查看>>
jmm 和线程安全
查看>>
pyplot的文本显示
查看>>
20140710总结
查看>>
前端巧用localStorage做“缓存”,减少HTTP请求次数
查看>>
echarts图表中饼状图的指示线和百分比
查看>>