xml地图|网站地图|网站标签 [设为首页] [加入收藏]

PostgreSQL 源码解读(54)- 查询语句#39(make_one_rel函数#4-生成子查询访谈路径)

本节介绍了standard_planner函数中的fetch_upper_rel和get_cheapest_fractional_path函数,其中fetch_upper_rel函数创设用以代表查询优化器生成的终极关系(用RelOptInfo数据协会意味着),get_cheapest_fractional_path函数依据输入的RelOptInfo数据组织找到花费低于的拜访路线。

本节所向披靡介绍make_one_rel函数中的set_base_rel_sizes及其子函数。在set_base_rel_sizes函数调用进度中,如RTE为子查询,则生成子查询的探访路线,通过调用函数set_subquery_pathlist实现。

本节大约介绍了动态规划算法达成(standard_join_search)中的join_search_one_level->make_join_rel函数,该函数创立三个rels连接所生成的RelOptInfo,并在RelOptInfo中加多访问路线新闻。

风流洒脱、源码解读

fetch_upper_rel从优化器音信中的upper_rels数据中获取用以代表查询优化器生成的尾声关系.

//--------------------------------------------------------------------------- fetch_upper_rel/* * fetch_upper_rel * Build a RelOptInfo describing some post-scan/join query processing, * or return a pre-existing one if somebody already built it. * 构建RelOptInfo数据结构,用以表示post-scan/join查询过程.如已存在,则直接返回. * * An "upper" relation is identified by an UpperRelationKind and a Relids set. * The meaning of the Relids set is not specified here, and very likely will * vary for different relation kinds. * 一个“上级”关系是由一个UpperRelationKind和一个Relids集合来标识。 * 在这里没有指定Relids集合的含义,并且很可能会因不同的关系类型而不同。 * * Most of the fields in an upper-level RelOptInfo are not used and are not * set here (though makeNode should ensure they're zeroes). We basically only * care about fields that are of interest to add_path() and set_cheapest(). * 上层RelOptInfo中的大多数字段都没有使用,也没有在这里设置(尽管makeNode应该确保它们是NULL)。 * 基本上只关心add_path()和set_cheap()函数所感兴趣的字段。 */RelOptInfo *fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids){ RelOptInfo *upperrel; ListCell *lc; /* * For the moment, our indexing data structure is just a List for each * relation kind. If we ever get so many of one kind that this stops * working well, we can improve it. No code outside this function should * assume anything about how to find a particular upperrel. * 目前,我们已索引的数据结构只是每个关系类型的链表。 * 这个函数之外的任何代码都不应该假定如何找到一个特定的上层关系。 */ /* If we already made this upperrel for the query, return it */ //如果已经构造了该查询的上层关系,直接返回 foreach(lc, root->upper_rels[kind]) { upperrel = (RelOptInfo *) lfirst; if (bms_equal(upperrel->relids, relids)) return upperrel; } upperrel = makeNode(RelOptInfo); upperrel->reloptkind = RELOPT_UPPER_REL; upperrel->relids = bms_copy; /* cheap startup cost is interesting iff not all tuples to be retrieved */ //低廉的启动成本在较少的元组返回的情况是比较让人关心的. upperrel->consider_startup = (root->tuple_fraction > 0);//如非全部返回元组,则需要考虑启动成本 upperrel->consider_param_startup = false; upperrel->consider_parallel = false; /* 以后可能会有变化;might get changed later */ upperrel->reltarget = create_empty_pathtarget(); upperrel->pathlist = NIL; upperrel->cheapest_startup_path = NULL; upperrel->cheapest_total_path = NULL; upperrel->cheapest_unique_path = NULL; upperrel->cheapest_parameterized_paths = NIL; root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel); return upperrel;}

get_cheapest_fractional_pathget_cheapest_fractional_path通过对RelOptInfo中的拜候路线两两比较,获取资金最低的拜见路线.

//--------------------------------------------------------------------------- get_cheapest_fractional_path/* * get_cheapest_fractional_path * Find the cheapest path for retrieving a specified fraction of all * the tuples expected to be returned by the given relation. * 给定关系,找到成本最低的访问路径,该路径返回返回预期设定元组数。 * * We interpret tuple_fraction the same way as grouping_planner. * 我们用grouping_planner函数来获取tuple_fraction。 * * We assume set_cheapest() has been run on the given rel. * 假定set_cheapest()函数已在给定的关系上执行. */Path *get_cheapest_fractional_path(RelOptInfo *rel, double tuple_fraction){ Path *best_path = rel->cheapest_total_path; ListCell *l; /* If all tuples will be retrieved, just return the cheapest-total path */ //如果需要检索所有元组,则返回总成本最低的访问路径 if (tuple_fraction <= 0.0) return best_path; /* Convert absolute # of tuples to a fraction; no need to clamp to 0..1 */ //根据比例给出需返回行数 if (tuple_fraction >= 1.0 && best_path->rows > 0) tuple_fraction /= best_path->rows; foreach(l, rel->pathlist) { Path *path =  lfirst; //best_path的成本比path要低或相同,保留 if (path == rel->cheapest_total_path || compare_fractional_path_costs(best_path, path, tuple_fraction) <= 0) continue; //否则,选择新的访问路径 best_path = path; } return best_path;}//------------------------------------------------------ compare_path_costs /* * compare_path_fractional_costs * Return -1, 0, or +1 according as path1 is cheaper, the same cost, * or more expensive than path2 for fetching the specified fraction * of the total tuples. * 返回值: * -1:相对于path2,path1成本更低; * 0:与path2成本相同 * 1:比path2成本更高 * * If fraction is <= 0 or > 1, we interpret it as 1, ie, we select the * path with the cheaper total_cost. * 如果fraction≤0或者大于1,则选择总成本最低的访问路径 */ int compare_fractional_path_costs(Path *path1, Path *path2, double fraction) { Cost cost1, cost2; if (fraction <= 0.0 || fraction >= 1.0) return compare_path_costs(path1, path2, TOTAL_COST); cost1 = path1->startup_cost + fraction * (path1->total_cost - path1->startup_cost); cost2 = path2->startup_cost + fraction * (path2->total_cost - path2->startup_cost); if (cost1 < cost2) return -1; if (cost1 > cost2) return +1; return 0; }//--------------------------------------- compare_path_costs /* * compare_path_costs * Return -1, 0, or +1 according as path1 is cheaper, the same cost, * or more expensive than path2 for the specified criterion. * 给定标准,返回比较结果. * 返回值: * -1:相对于path2,path1成本更低; * 0:与path2成本相同 * 1:比path2成本更高 */ int compare_path_costs(Path *path1, Path *path2, CostSelector criterion) { if (criterion == STARTUP_COST)//启动成本 { if (path1->startup_cost < path2->startup_cost) return -1; if (path1->startup_cost > path2->startup_cost) return +1; /* * If paths have the same startup cost (not at all unlikely), order * them by total cost. */ if (path1->total_cost < path2->total_cost) return -1; if (path1->total_cost > path2->total_cost) return +1; } else//总成本 { if (path1->total_cost < path2->total_cost) return -1; if (path1->total_cost > path2->total_cost) return +1; /* * If paths have the same total cost, order them by startup cost. */ if (path1->startup_cost < path2->startup_cost) return -1; if (path1->startup_cost > path2->startup_cost) return +1; } return 0; }

make_one_rel源代码:

风度翩翩、数据布局

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  ->reloptkind == RELOPT_BASEREL ||  ->reloptkind == RELOPT_OTHER_MEMBER_REL) /* Is the given relation a join relation? */ #define IS_JOIN_REL  ->reloptkind == RELOPT_JOINREL ||  ->reloptkind == RELOPT_OTHER_JOINREL) /* Is the given relation an upper relation? */ #define IS_UPPER_REL  ->reloptkind == RELOPT_UPPER_REL ||  ->reloptkind == RELOPT_OTHER_UPPER_REL) /* Is the given relation an "other" relation? */ #define IS_OTHER_REL  ->reloptkind == RELOPT_OTHER_MEMBER_REL ||  ->reloptkind == RELOPT_OTHER_JOINREL ||  ->reloptkind == RELOPT_OTHER_UPPER_REL) typedef struct RelOptInfo { NodeTag type;//节点标识 RelOptKind reloptkind;//RelOpt类型 /* all relations included in this RelOptInfo */ Relids relids; /*Relids集合 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 */ List *non_unique_for_rels; /* 已知的,不唯一的Relids链表 known not unique for these set */ /* 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;

二、参考资料

allpaths.cPG Document:Query Planning

 RelOptInfo * make_one_rel(PlannerInfo *root, List *joinlist) { //... /* * Compute size estimates and consider_parallel flags for each base rel, * then generate access paths. */ set_base_rel_sizes;//估算Relation的Size并且设置consider_parallel标记 //... }

二、源码解读

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 //位置是否调换 { 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 { 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 //已存在 { /* * 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,  restrictlist) && is_parallel_safe(root,  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; /* * 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  return hentry->join_rel; } else//线性 { ListCell *l; foreach(l, root->join_rel_list) { RelOptInfo *rel = (RelOptInfo *) lfirst; 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; } 

本文由永利澳门平台发布于计算机资讯,转载请注明出处:PostgreSQL 源码解读(54)- 查询语句#39(make_one_rel函数#4-生成子查询访谈路径)

您可能还会对下面的文章感兴趣: