千家信息网

PostgreSQL中set_base_rel_pathlists函数有什么作用

发表于:2026-10-31 作者:千家信息网编辑
千家信息网最后更新 2026年10月31日,这篇文章主要介绍"PostgreSQL中set_base_rel_pathlists函数有什么作用",在日常操作中,相信很多人在PostgreSQL中set_base_rel_pathlists函数有
千家信息网最后更新 2026年10月31日PostgreSQL中set_base_rel_pathlists函数有什么作用

这篇文章主要介绍"PostgreSQL中set_base_rel_pathlists函数有什么作用",在日常操作中,相信很多人在PostgreSQL中set_base_rel_pathlists函数有什么作用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"PostgreSQL中set_base_rel_pathlists函数有什么作用"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

set_base_rel_pathlists函数的目的是为每一个base rel找出所有可用的访问路径(包括顺序扫描和所有可用的索引),每一个可用的路径都会添加到pathlist链表中。

一、数据结构

RelOptInfo

 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 */     //FDW相关信息     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;   /* 是否存在等价类连接? True意味着joininfo并不完整,,T means joininfo is incomplete */      /* used by partitionwise joins: */       //是否尝试partitionwise连接,这是PG 11的一个新特性.     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;

Cost相关
注意:实际使用的参数值通过系统配置文件定义,而不是这里的常量定义!

/*  * The cost estimate produced by cost_qual_eval() includes both a one-time  * (startup) cost, and a per-tuple cost.  */ typedef struct QualCost {     Cost        startup;        /* 启动成本,one-time cost */     Cost        per_tuple;      /* 每个元组的成本,per-evaluation cost */ } QualCost; typedef double Cost; /* execution cost (in page-access units) */ /* defaults for costsize.c's Cost parameters */ /* NB: cost-estimation code should use the variables, not these constants! */ /* 注意:实际值通过系统配置文件定义,而不是这里的常量定义! */ /* If you change these, update backend/utils/misc/postgresql.sample.conf */ #define DEFAULT_SEQ_PAGE_COST  1.0       //顺序扫描page的成本 #define DEFAULT_RANDOM_PAGE_COST  4.0      //随机扫描page的成本 #define DEFAULT_CPU_TUPLE_COST  0.01     //处理一个元组的CPU成本 #define DEFAULT_CPU_INDEX_TUPLE_COST 0.005   //处理一个索引元组的CPU成本 #define DEFAULT_CPU_OPERATOR_COST  0.0025    //执行一次操作或函数的CPU成本 #define DEFAULT_PARALLEL_TUPLE_COST 0.1    //并行执行,从一个worker传输一个元组到另一个worker的成本 #define DEFAULT_PARALLEL_SETUP_COST  1000.0  //构建并行执行环境的成本  #define DEFAULT_EFFECTIVE_CACHE_SIZE  524288    /*先前已有介绍, measured in pages */ double      seq_page_cost = DEFAULT_SEQ_PAGE_COST; double      random_page_cost = DEFAULT_RANDOM_PAGE_COST; double      cpu_tuple_cost = DEFAULT_CPU_TUPLE_COST; double      cpu_index_tuple_cost = DEFAULT_CPU_INDEX_TUPLE_COST; double      cpu_operator_cost = DEFAULT_CPU_OPERATOR_COST; double      parallel_tuple_cost = DEFAULT_PARALLEL_TUPLE_COST; double      parallel_setup_cost = DEFAULT_PARALLEL_SETUP_COST;  int         effective_cache_size = DEFAULT_EFFECTIVE_CACHE_SIZE;  Cost        disable_cost = 1.0e10;//1后面10个0,通过设置一个巨大的成本,让优化器自动放弃此路径  int         max_parallel_workers_per_gather = 2;//每次gather使用的worker数

IndexClauseSet
用于收集匹配索引的的条件语句

 /* Data structure for collecting qual clauses that match an index */ typedef struct {     bool        nonempty;       /* True if lists are not all empty */     /* Lists of RestrictInfos, one per index column */     List       *indexclauses[INDEX_MAX_KEYS]; } IndexClauseSet;

二、源码解读

set_base_rel_pathlists函数遍历RelOptInfo数组,为每一个Rel构造访问路径,先前已介绍了顺序扫描的成本估算,本节介绍索引扫描的成本估算(函数:create_index_paths),通过调用set_plain_rel_pathlist->create_index_paths函数实现.

 /*  * set_plain_rel_pathlist  *    Build access paths for a plain relation (no subquery, no inheritance)  */ static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) {     Relids      required_outer;      //...      /* 索引扫描,Consider index scans */     create_index_paths(root, rel);      /* TID扫描,Consider TID scans */     create_tidscan_paths(root, rel); }

create_index_paths
create_index_paths函数生成Relation所有可能被选中的索引访问路径,详见源码注释.

 /*  * create_index_paths()  *    Generate all interesting index paths for the given relation.  *    Candidate paths are added to the rel's pathlist (using add_path).  *    生成Relation所有可能被选中的索引访问路径.  *    Paths通过add_path方法加入到RelOptInfo的pathlist链表中.  *  * To be considered for an index scan, an index must match one or more  * restriction clauses or join clauses from the query's qual condition,  * or match the query's ORDER BY condition, or have a predicate that  * matches the query's qual condition.  * 使用索引扫描的前提是:1.索引必须匹配一个或多个限制条件或连接条件,或者  * 2.匹配查询的ORDER BY排序条件,或者3.匹配查询条件的谓词(部分/条件索引)  *  * There are two basic kinds of index scans.  A "plain" index scan uses  * only restriction clauses (possibly none at all) in its indexqual,  * so it can be applied in any context.  A "parameterized" index scan uses  * join clauses (plus restriction clauses, if available) in its indexqual.  * When joining such a scan to one of the relations supplying the other  * variables used in its indexqual, the parameterized scan must appear as  * the inner relation of a nestloop join; it can't be used on the outer side,  * nor in a merge or hash join.  In that context, values for the other rels'  * attributes are available and fixed during any one scan of the indexpath.  * 有两种基本的索引扫描类型,一种是"plain"索引扫描,只使用限制条件(或者什么都  *  没有),这种扫描方法适用于任何场景.另外一种是"parameterized"扫描,使用连接条件  *  (可能的话,加上限制条件)."parameterized"扫描只能出现在嵌套循环中的内关系中,  *  因为参数由外关系提供.  *  * An IndexPath is generated and submitted to add_path() for each plain or  * parameterized index scan this routine deems potentially interesting for  * the current query.  * IndexPath访问路径通过函数add_path生成并提交.  *  * 输入参数:  * 'rel' is the relation for which we want to generate index paths  * rel是待生成索引范围路径的关系  *  * Note: check_index_predicates() must have been run previously for this rel.  * 注意:函数check_index_predicates在调用此函数前调用  *  * Note: in cases involving LATERAL references in the relation's tlist, it's  * possible that rel->lateral_relids is nonempty.  Currently, we include  * lateral_relids into the parameterization reported for each path, but don't  * take it into account otherwise.  The fact that any such rels *must* be  * available as parameter sources perhaps should influence our choices of  * index quals ... but for now, it doesn't seem worth troubling over.  * In particular, comments below about "unparameterized" paths should be read  * as meaning "unparameterized so far as the indexquals are concerned".  */ void create_index_paths(PlannerInfo *root, RelOptInfo *rel) {     List       *indexpaths;//索引访问路径链表     List       *bitindexpaths;//     List       *bitjoinpaths;     List       *joinorclauses;     IndexClauseSet rclauseset;     IndexClauseSet jclauseset;     IndexClauseSet eclauseset;     ListCell   *lc;      /* Skip the whole mess if no indexes */     if (rel->indexlist == NIL)//不存在索引,退出         return;      /* Bitmap paths are collected and then dealt with at the end */     bitindexpaths = bitjoinpaths = joinorclauses = NIL;//初始赋值      /* Examine each index in turn */     foreach(lc, rel->indexlist)//遍历索引链表     {         IndexOptInfo *index = (IndexOptInfo *) lfirst(lc);//索引信息          /* Protect limited-size array in IndexClauseSets */         Assert(index->ncolumns <= INDEX_MAX_KEYS);          /*          * Ignore partial indexes that do not match the query.          * (generate_bitmap_or_paths() might be able to do something with          * them, but that's of no concern here.)          */         if (index->indpred != NIL && !index->predOK)//部分索引,而且不能使用,不使用此索引             continue;          /*          * Identify the restriction clauses that can match the index.          * 验证索引和条件是否匹配          */         MemSet(&rclauseset, 0, sizeof(rclauseset));         match_restriction_clauses_to_index(rel, index, &rclauseset);          /*          * Build index paths from the restriction clauses.  These will be          * non-parameterized paths.  Plain paths go directly to add_path(),          * bitmap paths are added to bitindexpaths to be handled below.          * 通过限制条件创建非参数化索引访问路径.Plain访问路径通过函数add_path直接添加到RelOptInfo中          * 位图访问路径添加到bitindexpaths链表中,后续再处理          */         get_index_paths(root, rel, index, &rclauseset,                         &bitindexpaths);          /*          * Identify the join clauses that can match the index.  For the moment          * we keep them separate from the restriction clauses.  Note that this          * step finds only "loose" join clauses that have not been merged into          * EquivalenceClasses.  Also, collect join OR clauses for later.          * 验证索引是否与连接条件匹配(连接条件与限制条件相互独立).          * 这一步只是发现未被合并到EC中的"loose"连接条件,在此之后会收集连接中的OR条件          */         MemSet(&jclauseset, 0, sizeof(jclauseset));         match_join_clauses_to_index(root, rel, index,                                     &jclauseset, &joinorclauses);          /*          * Look for EquivalenceClasses that can generate joinclauses matching          * the index.          * 通过EC(等价类)匹配索引,结果存储在eclauseset链表中          */         MemSet(&eclauseset, 0, sizeof(eclauseset));         match_eclass_clauses_to_index(root, index,                                       &eclauseset);          /*          * If we found any plain or eclass join clauses, build parameterized          * index paths using them.          * 如果存在plain或者eclass连接条件,创建参数化索引访问路径          */         if (jclauseset.nonempty || eclauseset.nonempty)             consider_index_join_clauses(root, rel, index,                                         &rclauseset,                                         &jclauseset,                                         &eclauseset,                                         &bitjoinpaths);     }      /*      * Generate BitmapOrPaths for any suitable OR-clauses present in the      * restriction list.  Add these to bitindexpaths.      * 基于RelOptInfo中的限制条件生成BitmapOrPaths访问路径      */     indexpaths = generate_bitmap_or_paths(root, rel,                                           rel->baserestrictinfo, NIL);     bitindexpaths = list_concat(bitindexpaths, indexpaths);//合并到bitindexpaths链表中      /*      * Likewise, generate BitmapOrPaths for any suitable OR-clauses present in      * the joinclause list.  Add these to bitjoinpaths.      * 同样的,基于连接条件joinorclause中的OR语句生成BitmapOrPaths访问路径      */     indexpaths = generate_bitmap_or_paths(root, rel,                                           joinorclauses, rel->baserestrictinfo);     bitjoinpaths = list_concat(bitjoinpaths, indexpaths);//合并到bitjoinpaths链表中      /*      * If we found anything usable, generate a BitmapHeapPath for the most      * promising combination of restriction bitmap index paths.  Note there      * will be only one such path no matter how many indexes exist.  This      * should be sufficient since there's basically only one figure of merit      * (total cost) for such a path.      */     if (bitindexpaths != NIL)//存在位图索引访问路径     {         Path       *bitmapqual;//访问路径         BitmapHeapPath *bpath;//BitmapHeapPath访问路径          bitmapqual = choose_bitmap_and(root, rel, bitindexpaths);//位图表达式路径         bpath = create_bitmap_heap_path(root, rel, bitmapqual,                                         rel->lateral_relids, 1.0, 0);//BitmapHeapPath访问路径         add_path(rel, (Path *) bpath);//添加到RelOptInfo中          /* create a partial bitmap heap path */         if (rel->consider_parallel && rel->lateral_relids == NULL)             create_partial_bitmap_paths(root, rel, bitmapqual);//创建并行访问路径     }      /*      * Likewise, if we found anything usable, generate BitmapHeapPaths for the      * most promising combinations of join bitmap index paths.  Our strategy      * is to generate one such path for each distinct parameterization seen      * among the available bitmap index paths.  This may look pretty      * expensive, but usually there won't be very many distinct      * parameterizations.  (This logic is quite similar to that in      * consider_index_join_clauses, but we're working with whole paths not      * individual clauses.)      */     if (bitjoinpaths != NIL)//bitjoinpaths位图连接访问路径     {         List       *path_outer;//依赖的外部Relids链表         List       *all_path_outers;//依赖的外部路径Relids链表         ListCell   *lc;//临时变量          /*          * path_outer holds the parameterization of each path in bitjoinpaths          * (to save recalculating that several times), while all_path_outers          * holds all distinct parameterization sets.          */         path_outer = all_path_outers = NIL;//初始化变量         foreach(lc, bitjoinpaths)//遍历bitjoinpaths         {             Path       *path = (Path *) lfirst(lc);//访问路径             Relids      required_outer;//依赖的外部Relids              required_outer = get_bitmap_tree_required_outer(path);//             path_outer = lappend(path_outer, required_outer);//添加到链表中             if (!bms_equal_any(required_outer, all_path_outers))//不等,则添加到all_path_outers中                 all_path_outers = lappend(all_path_outers, required_outer);         }          /* Now, for each distinct parameterization set ... */         //对每一个唯一的参数化集合进行处理         foreach(lc, all_path_outers)//遍历all_path_outers         {             Relids      max_outers = (Relids) lfirst(lc);             List       *this_path_set;             Path       *bitmapqual;             Relids      required_outer;             double      loop_count;             BitmapHeapPath *bpath;             ListCell   *lcp;             ListCell   *lco;              /* Identify all the bitmap join paths needing no more than that */             this_path_set = NIL;             forboth(lcp, bitjoinpaths, lco, path_outer)//遍历             {                 Path       *path = (Path *) lfirst(lcp);                 Relids      p_outers = (Relids) lfirst(lco);                  if (bms_is_subset(p_outers, max_outers))//无需依赖其他Relids,添加到this_path_set中                     this_path_set = lappend(this_path_set, path);             }              /*              * Add in restriction bitmap paths, since they can be used              * together with any join paths.              */             this_path_set = list_concat(this_path_set, bitindexpaths);//合并bitindexpaths访问路径              /* Select best AND combination for this parameterization */             bitmapqual = choose_bitmap_and(root, rel, this_path_set);//为此参数化处理选择最好的AND组合              /* And push that path into the mix */             required_outer = get_bitmap_tree_required_outer(bitmapqual);             loop_count = get_loop_count(root, rel->relid, required_outer);             bpath = create_bitmap_heap_path(root, rel, bitmapqual,                                             required_outer, loop_count, 0);//创建索引访问路径             add_path(rel, (Path *) bpath);         }     } }

match_XXX
match_restriction_clauses_to_index函数验证限制条件是否与Index匹配,匹配的条件添加到clauseset中.
match_join_clauses_to_index函数验证连接条件是否与Index匹配,同样的,匹配的条件添加到clauseset中.
match_eclass_clauses_to_index函数验证EC连接条件是否与Index匹配,匹配的条件添加到clauseset中.

//--------------------------------------------------- match_restriction_clauses_to_index /*  * match_restriction_clauses_to_index  *    Identify restriction clauses for the rel that match the index.  *    Matching clauses are added to *clauseset.  *    验证限制条件是否与Index匹配,匹配的条件加入到clauseset中  */ static void match_restriction_clauses_to_index(RelOptInfo *rel, IndexOptInfo *index,                                    IndexClauseSet *clauseset) {     /* We can ignore clauses that are implied by the index predicate */     //忽略部分(条件)索引,直接调用match_clauses_to_index     match_clauses_to_index(index, index->indrestrictinfo, clauseset); } //------------------------------- match_clauses_to_index /*  * match_clauses_to_index  *    Perform match_clause_to_index() for each clause in a list.  *    Matching clauses are added to *clauseset.  */ static void match_clauses_to_index(IndexOptInfo *index,                        List *clauses,                        IndexClauseSet *clauseset) {     ListCell   *lc;//临时变量      foreach(lc, clauses)//遍历限制条件     {         RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);          match_clause_to_index(index, rinfo, clauseset);     } }//--------------------------------------------------- match_join_clauses_to_index /*  * match_join_clauses_to_index  *    Identify join clauses for the rel that match the index.  *    Matching clauses are added to *clauseset.  *    Also, add any potentially usable join OR clauses to *joinorclauses.  *    验证连接条件是否与Index匹配,匹配的条件添加到clauseset中  *    另外,在joinorclauses中添加可能有用的连接条件OR子句  */ static void match_join_clauses_to_index(PlannerInfo *root,                             RelOptInfo *rel, IndexOptInfo *index,                             IndexClauseSet *clauseset,                             List **joinorclauses) {     ListCell   *lc;//临时变量      /* Scan the rel's join clauses */     foreach(lc, rel->joininfo)//遍历连接条件     {         RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);          /* Check if clause can be moved to this rel */         if (!join_clause_is_movable_to(rinfo, rel))             continue;          /* Potentially usable, so see if it matches the index or is an OR */         if (restriction_is_or_clause(rinfo))             *joinorclauses = lappend(*joinorclauses, rinfo);         else             match_clause_to_index(index, rinfo, clauseset);     } }//--------------------------------------------------- match_eclass_clauses_to_index /*  * match_eclass_clauses_to_index  *    Identify EquivalenceClass join clauses for the rel that match the index.  *    Matching clauses are added to *clauseset.  *    验证EC连接条件是否与Index匹配,相匹配的子句加入到clauseset中  */ static void match_eclass_clauses_to_index(PlannerInfo *root, IndexOptInfo *index,                               IndexClauseSet *clauseset) {     int         indexcol;      /* No work if rel is not in any such ECs */     if (!index->rel->has_eclass_joins)//没有ECs,返回         return;      for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++)//遍历索引列     {         ec_member_matches_arg arg;         List       *clauses;          /* Generate clauses, skipping any that join to lateral_referencers */         //生成条件子句链表         arg.index = index;         arg.indexcol = indexcol;         clauses = generate_implied_equalities_for_column(root,                                                          index->rel,                                                          ec_member_matches_indexcol,                                                          (void *) &arg,                                                          index->rel->lateral_referencers);          /*          * We have to check whether the results actually do match the index,          * since for non-btree indexes the EC's equality operators might not          * be in the index opclass (cf ec_member_matches_indexcol).          */         match_clauses_to_index(index, clauses, clauseset);     } }//---------------------------- generate_implied_equalities_for_column /*  * generate_implied_equalities_for_column  *    Create EC-derived joinclauses usable with a specific column.  *    创建可用于特定列的EC衍生连接条件  *  * This is used by indxpath.c to extract potentially indexable joinclauses  * from ECs, and can be used by foreign data wrappers for similar purposes.  * We assume that only expressions in Vars of a single table are of interest,  * but the caller provides a callback function to identify exactly which  * such expressions it would like to know about.  *  * We assume that any given table/index column could appear in only one EC.  * (This should be true in all but the most pathological cases, and if it  * isn't, we stop on the first match anyway.)  Therefore, what we return  * is a redundant list of clauses equating the table/index column to each of  * the other-relation values it is known to be equal to.  Any one of  * these clauses can be used to create a parameterized path, and there  * is no value in using more than one.  (But it *is* worthwhile to create  * a separate parameterized path for each one, since that leads to different  * join orders.)  *  * The caller can pass a Relids set of rels we aren't interested in joining  * to, so as to save the work of creating useless clauses.  */ List * generate_implied_equalities_for_column(PlannerInfo *root,                                        RelOptInfo *rel,                                        ec_matches_callback_type callback,                                        void *callback_arg,                                        Relids prohibited_rels) {     List       *result = NIL;//结果链表     bool        is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);//是否子Relation     Relids      parent_relids;//父Relids     ListCell   *lc1;//变量      /* Indexes are available only on base or "other" member relations. */     Assert(IS_SIMPLE_REL(rel));      /* If it's a child rel, we'll need to know what its parent(s) are */     if (is_child_rel)         parent_relids = find_childrel_parents(root, rel);     else         parent_relids = NULL;   /* not used, but keep compiler quiet */      foreach(lc1, root->eq_classes)//遍历EC     {         EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);//当前的EC         EquivalenceMember *cur_em;//EC成员         ListCell   *lc2;//链表成员          /*          * Won't generate joinclauses if const or single-member (the latter          * test covers the volatile case too)          */         if (cur_ec->ec_has_const || list_length(cur_ec->ec_members) <= 1)             continue;          /*          * No point in searching if rel not mentioned in eclass (but we can't          * tell that for a child rel).          */         if (!is_child_rel &&             !bms_is_subset(rel->relids, cur_ec->ec_relids))             continue;          /*          * Scan members, looking for a match to the target column.  Note that          * child EC members are considered, but only when they belong to the          * target relation.  (Unlike regular members, the same expression          * could be a child member of more than one EC.  Therefore, it's          * potentially order-dependent which EC a child relation's target          * column gets matched to.  This is annoying but it only happens in          * corner cases, so for now we live with just reporting the first          * match.  See also get_eclass_for_sort_expr.)          */         cur_em = NULL;         foreach(lc2, cur_ec->ec_members)//遍历EC的成员         {             cur_em = (EquivalenceMember *) lfirst(lc2);//当前成员             if (bms_equal(cur_em->em_relids, rel->relids) &&                 callback(root, rel, cur_ec, cur_em, callback_arg))//调用ec_member_matches_indexcol函数                 break;//找到匹配的成员,跳出             cur_em = NULL;         }          if (!cur_em)             continue;          /*          * Found our match.  Scan the other EC members and attempt to generate          * joinclauses.          */         foreach(lc2, cur_ec->ec_members)         {             EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2);             Oid         eq_op;             RestrictInfo *rinfo;              if (other_em->em_is_child)//                 continue;       /* 忽略子成员,ignore children here */              /* Make sure it'll be a join to a different rel */             if (other_em == cur_em ||                 bms_overlap(other_em->em_relids, rel->relids))//过滤cur_em                 continue;              /* Forget it if caller doesn't want joins to this rel */             if (bms_overlap(other_em->em_relids, prohibited_rels))                 continue;              /*              * Also, if this is a child rel, avoid generating a useless join              * to its parent rel(s).              */             if (is_child_rel &&                 bms_overlap(parent_relids, other_em->em_relids))                 continue;              eq_op = select_equality_operator(cur_ec,                                              cur_em->em_datatype,                                              other_em->em_datatype);             if (!OidIsValid(eq_op))                 continue;              /* set parent_ec to mark as redundant with other joinclauses */             rinfo = create_join_clause(root, cur_ec, eq_op,                                        cur_em, other_em,                                        cur_ec);//创建连接条件语句              result = lappend(result, rinfo);         }          /*          * If somehow we failed to create any join clauses, we might as well          * keep scanning the ECs for another match.  But if we did make any,          * we're done, because we don't want to return non-redundant clauses.          */         if (result)             break;     }      return result; }//---------------------------- match_clause_to_index /*  * match_clause_to_index  *    Test whether a qual clause can be used with an index.  *  * If the clause is usable, add it to the appropriate list in *clauseset.  * *clauseset must be initialized to zeroes before first call.  *  * Note: in some circumstances we may find the same RestrictInfos coming from  * multiple places.  Defend against redundant outputs by refusing to add a  * clause twice (pointer equality should be a good enough check for this).  *  * Note: it's possible that a badly-defined index could have multiple matching  * columns.  We always select the first match if so; this avoids scenarios  * wherein we get an inflated idea of the index's selectivity by using the  * same clause multiple times with different index columns.  */ static void match_clause_to_index(IndexOptInfo *index,                       RestrictInfo *rinfo,                       IndexClauseSet *clauseset) {     int         indexcol;      /*      * Never match pseudoconstants to indexes.  (Normally a match could not      * happen anyway, since a pseudoconstant clause couldn't contain a Var,      * but what if someone builds an expression index on a constant? It's not      * totally unreasonable to do so with a partial index, either.)      */     if (rinfo->pseudoconstant)         return;      /*      * If clause can't be used as an indexqual because it must wait till after      * some lower-security-level restriction clause, reject it.      */     if (!restriction_is_securely_promotable(rinfo, index->rel))         return;      /* OK, check each index key column for a match */     for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++)     {         if (match_clause_to_indexcol(index,                                      indexcol,                                      rinfo))         {             clauseset->indexclauses[indexcol] =                 list_append_unique_ptr(clauseset->indexclauses[indexcol],                                        rinfo);//赋值             clauseset->nonempty = true;//设置标记             return;         }     } }//------------------- match_clause_to_indexcol /*  * match_clause_to_indexcol()  *    Determines whether a restriction clause matches a column of an index.  *    判断约束条件是否与索引中的某一列匹配  *  *    To match an index normally, the clause:  *    通常来说,匹配索引,子句必须:  *    (1)  must be in the form (indexkey op const) or (const op indexkey);  *         and  *         满足格式:(索引键 操作符 常量) 或者 (常量 操作符 索引键),而且  *    (2)  must contain an operator which is in the same family as the index  *         operator for this column, or is a "special" operator as recognized  *         by match_special_index_operator();  *         and  *         包含一种与索引列同一family的操作符,或者是一种通过            match_special_index_operator方法认定的特殊操作符  *    (3)  must match the collation of the index, if collation is relevant.  *         与索引的排序规则collation匹配  *   *    Our definition of "const" is exceedingly liberal: we allow anything that  *    doesn't involve a volatile function or a Var of the index's relation.  *    In particular, Vars belonging to other relations of the query are  *    accepted here, since a clause of that form can be used in a  *    parameterized indexscan.  It's the responsibility of higher code levels  *    to manage restriction and join clauses appropriately.  *    这里"const"常量的定义非常自由:除了易变函数或索引关系的Var之外的,均视为"const"  *    由于存在参数化索引扫描的可能,因此查询中属于其他Relations的Vars也可以在此出现.  *    调用此函数的代码有责任"合适"的管理限制条件和连接条件.  *  *    Note: we do need to check for Vars of the index's relation on the  *    "const" side of the clause, since clauses like (a.f1 OP (b.f2 OP a.f3))  *    are not processable by a parameterized indexscan on a.f1, whereas  *    something like (a.f1 OP (b.f2 OP c.f3)) is.  *    注意:需要在子句的const部分检查索引关系的Vars,因为子句  *    如(a.f1 OP (b.f2 OP a.f3)不能通过a上的参数化索引扫描进行处理  *  *    Presently, the executor can only deal with indexquals that have the  *    indexkey on the left, so we can only use clauses that have the indexkey  *    on the right if we can commute the clause to put the key on the left.  *    We do not actually do the commuting here, but we check whether a  *    suitable commutator operator is available.  *    目前为止,执行器只能处理索引键在左边的索引表达式,因此只能使用那些可以  *    把索引键变换到左边的条件表达式.在这个函数中不执行变换,但会执行相应的检查.  *  *    If the index has a collation, the clause must have the same collation.  *    For collation-less indexes, we assume it doesn't matter; this is  *    necessary for cases like "hstore ? text", wherein hstore's operators  *    don't care about collation but the clause will get marked with a  *    collation anyway because of the text argument.  (This logic is  *    embodied in the macro IndexCollMatchesExprColl.)  *    如果索引含有排序规则(collation),条件子句必须包含相同的排序规则.  *    对于无collation的索引,假定collation没有任何影响.  *  *    It is also possible to match RowCompareExpr clauses to indexes (but  *    currently, only btree indexes handle this).  In this routine we will  *    report a match if the first column of the row comparison matches the  *    target index column.  This is sufficient to guarantee that some index  *    condition can be constructed from the RowCompareExpr --- whether the  *    remaining columns match the index too is considered in  *    adjust_rowcompare_for_index().  *    RowCompareExpr有可能与索引进行匹配,在这个处理过程中,如果行对比的第一个列  *    与目标索引匹配,那么可以认为是匹配的.  *  *    It is also possible to match ScalarArrayOpExpr clauses to indexes, when  *    the clause is of the form "indexkey op ANY (arrayconst)".  *    如果子句的格式是"indexkey op ANY (arrayconst)",那么匹配ScalarArrayOpExpr  *    也是可能的.  *  *    For boolean indexes, it is also possible to match the clause directly  *    to the indexkey; or perhaps the clause is (NOT indexkey).  *    对于布尔索引,可以直接与索引键进行匹配  *  * 输入参数:  * 'index' is the index of interest.  * index-正在处理的索引  * 'indexcol' is a column number of 'index' (counting from 0).  * indexcol-索引列(从0起算)  * 'rinfo' is the clause to be tested (as a RestrictInfo node).  * rinfo-RestrictInfo Node  *  * Returns true if the clause can be used with this index key.  * 如可以使用索引,则返回T  *  * NOTE:  returns false if clause is an OR or AND clause; it is the  * responsibility of higher-level routines to cope with those.  * 注意:如果条件语句是OR/AND语句,则返回F,由上层处理逻辑处理  */ static bool match_clause_to_indexcol(IndexOptInfo *index,                          int indexcol,                          RestrictInfo *rinfo) {     Expr       *clause = rinfo->clause;//条件语句     Index       index_relid = index->rel->relid;//Index的Relid     Oid         opfamily;//操作符种类     Oid         idxcollation;//索引排序规则     Node       *leftop,//左节点                *rightop;//右节点     Relids      left_relids;//左节点相关Relids     Relids      right_relids;//右节点相关Relids     Oid         expr_op;//表达式操作符的Oid     Oid         expr_coll;//表达式Collation的Oid     bool        plain_op;//是否Plain操作符      Assert(indexcol < index->nkeycolumns);      opfamily = index->opfamily[indexcol];//获取操作符种类     idxcollation = index->indexcollations[indexcol];//获取索引排序规则      /* First check for boolean-index cases. */     if (IsBooleanOpfamily(opfamily))//是否布尔类     {         if (match_boolean_index_clause((Node *) clause, indexcol, index))//是否匹配             return true;//如匹配,返回T     }      /*      * Clause must be a binary opclause, or possibly a ScalarArrayOpExpr      * (which is always binary, by definition).  Or it could be a      * RowCompareExpr, which we pass off to match_rowcompare_to_indexcol().      * Or, if the index supports it, we can handle IS NULL/NOT NULL clauses.      */     if (is_opclause(clause))//OpExpr     {         leftop = get_leftop(clause);         rightop = get_rightop(clause);         if (!leftop || !rightop)             return false;         left_relids = rinfo->left_relids;         right_relids = rinfo->right_relids;         expr_op = ((OpExpr *) clause)->opno;         expr_coll = ((OpExpr *) clause)->inputcollid;         plain_op = true;     }     else if (clause && IsA(clause, ScalarArrayOpExpr))//ScalarArrayOpExpr     {         ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;          /* We only accept ANY clauses, not ALL */         if (!saop->useOr)             return false;         leftop = (Node *) linitial(saop->args);         rightop = (Node *) lsecond(saop->args);         left_relids = NULL;     /* not actually needed */         right_relids = pull_varnos(rightop);         expr_op = saop->opno;         expr_coll = saop->inputcollid;         plain_op = false;     }     else if (clause && IsA(clause, RowCompareExpr))//RowCompareExpr     {         return match_rowcompare_to_indexcol(index, indexcol,                                             opfamily, idxcollation,                                             (RowCompareExpr *) clause);     }     else if (index->amsearchnulls && IsA(clause, NullTest))//NullTest     {         NullTest   *nt = (NullTest *) clause;          if (!nt->argisrow &&             match_index_to_operand((Node *) nt->arg, indexcol, index))             return true;         return false;     }     else         return false;      /*      * Check for clauses of the form: (indexkey operator constant) or      * (constant operator indexkey).  See above notes about const-ness.      * (indexkey operator constant)和(constant operator indexkey)格式的语句      */     //处理:(indexkey operator constant)     if (match_index_to_operand(leftop, indexcol, index) &&         !bms_is_member(index_relid, right_relids) &&         !contain_volatile_functions(rightop))     {         if (IndexCollMatchesExprColl(idxcollation, expr_coll) &&             is_indexable_operator(expr_op, opfamily, true))//排序规则&操作符种类匹配             return true;//返回T          /*          * If we didn't find a member of the index's opfamily, see whether it          * is a "special" indexable operator.          */         if (plain_op &&             match_special_index_operator(clause, opfamily,                                          idxcollation, true))//Plain操作&特殊操作符,返回T             return true;         return false;//否则,返回F     }      //处理(constant operator indexkey)     if (plain_op &&         match_index_to_operand(rightop, indexcol, index) &&         !bms_is_member(index_relid, left_relids) &&         !contain_volatile_functions(leftop))     {         if (IndexCollMatchesExprColl(idxcollation, expr_coll) &&             is_indexable_operator(expr_op, opfamily, false))             return true;          /*          * If we didn't find a member of the index's opfamily, see whether it          * is a "special" indexable operator.          */         if (match_special_index_operator(clause, opfamily,                                          idxcollation, false))             return true;         return false;     }      return false; }

三、跟踪分析

测试脚本如下

select a.*,b.grbh,b.je from t_dwxx a,    lateral (select t1.dwbh,t1.grbh,t2.je      from t_grxx t1           inner join t_jfxx t2 on t1.dwbh = a.dwbh and t1.grbh = t2.grbh) bwhere a.dwbh = '1001'order by b.dwbh;

注意:按先前的分析,SQL语句存在等价类{t_dwxx.dwbh t_grxx.dwbh '1001'}和{t_grxx.grbh t_jfxx.grbh},在构造t_grxx的索引访问路径时,使用等价类构造.

启动gdb,第一个RelOptInfo(对应t_dwxx)有3个Index,第二个RelOptInfo(对应t_grxx)有2个Index(分别是在dwbh和grbh上的索引),第三个RelOptInfo(对应t_jfxx)有1个Index(grbh上的索引),本节以t_jfxx和t_grxx为例进行跟踪分析

...(gdb) cContinuing.Breakpoint 1, create_index_paths (root=0x2714c50, rel=0x2729530) at indxpath.c:242242   if (rel->indexlist == NIL)(gdb) p *(IndexOptInfo *)rel->indexlist->head->data.ptr_value$38 = {type = T_IndexOptInfo, indexoid = 16750, reltablespace = 0, rel = 0x2729530, pages = 276, tuples = 100000,   tree_height = 1, ncolumns = 1, nkeycolumns = 1, indexkeys = 0x2729998, indexcollations = 0x27299b0, opfamily = 0x27299c8,   opcintype = 0x27299e0, sortopfamily = 0x27299c8, reverse_sort = 0x2729a10, nulls_first = 0x2729a28,   canreturn = 0x27299f8, relam = 403, indexprs = 0x0, indpred = 0x0, indextlist = 0x2729ae0, indrestrictinfo = 0x0,   predOK = false, unique = false, immediate = true, hypothetical = false, amcanorderbyop = false, amoptionalkey = true,   amsearcharray = true, amsearchnulls = true, amhasgettuple = true, amhasgetbitmap = true, amcanparallel = true,   amcostestimate = 0x94f0ad }

输入信息是已熟知的root(PlannerInfo)和rel(RelOptInfo).首先进行索引遍历循环

(gdb) cContinuing.Breakpoint 1, create_index_paths (root=0x2714c50, rel=0x2729530) at indxpath.c:242242   if (rel->indexlist == NIL)(gdb) p *(IndexOptInfo *)rel->indexlist->head->data.ptr_value$38 = {type = T_IndexOptInfo, indexoid = 16750, reltablespace = 0, rel = 0x2729530, pages = 276, tuples = 100000,   tree_height = 1, ncolumns = 1, nkeycolumns = 1, indexkeys = 0x2729998, indexcollations = 0x27299b0, opfamily = 0x27299c8,   opcintype = 0x27299e0, sortopfamily = 0x27299c8, reverse_sort = 0x2729a10, nulls_first = 0x2729a28,   canreturn = 0x27299f8, relam = 403, indexprs = 0x0, indpred = 0x0, indextlist = 0x2729ae0, indrestrictinfo = 0x0,   predOK = false, unique = false, immediate = true, hypothetical = false, amcanorderbyop = false, amoptionalkey = true,   amsearcharray = true, amsearchnulls = true, amhasgettuple = true, amhasgetbitmap = true, amcanparallel = true,   amcostestimate = 0x94f0ad }

查询数据字典pg_class,oid=16750相应的索引是idx_t_jfxx_grbh

testdb=# select relname from pg_class where oid=16750;     relname     ----------------- idx_t_jfxx_grbh(1 row)

调用match_restriction_clauses_to_index和match_join_clauses_to_index,子句集合均为NULL

(gdb) match_restriction_clauses_to_index (rel=0x2729530, index=0x2729888, clauseset=0x7fff69cf0890) at indxpath.c:21172117  }(gdb) create_index_paths (root=0x2714c50, rel=0x2729530) at indxpath.c:275275     get_index_paths(root, rel, index, &rclauseset,(gdb) 284     MemSet(&jclauseset, 0, sizeof(jclauseset));(gdb) 285     match_join_clauses_to_index(root, rel, index,(gdb) 292     MemSet(&eclauseset, 0, sizeof(eclauseset));(gdb) 293     match_eclass_clauses_to_index(root, index,(gdb) p rclauseset$2 = {nonempty = false, indexclauses = {0x0 }}(gdb) p joinorclauses$3 = (List *) 0x0(gdb) p jclauseset$4 = {nonempty = false, indexclauses = {0x0 }}

进入match_eclass_clauses_to_index

...268     match_restriction_clauses_to_index(rel, index, &rclauseset);(gdb) stepmatch_restriction_clauses_to_index (rel=0x2724c88, index=0x27254d8, clauseset=0x7fff69cf0890) at indxpath.c:21162116    match_clauses_to_index(index, index->indrestrictinfo, clauseset);

进入generate_implied_equalities_for_column

...(gdb) stepgenerate_implied_equalities_for_column (root=0x2714c50, rel=0x2729530, callback=0x7509b0 ,     callback_arg=0x7fff69cf0620, prohibited_rels=0x0) at equivclass.c:22192219    List     *result = NIL;

等价类信息

...2235      EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);(gdb) 2243      if (cur_ec->ec_has_const || list_length(cur_ec->ec_members) <= 1)(gdb) p *cur_ec$6 = {type = T_EquivalenceClass, ec_opfamilies = 0x272a268, ec_collation = 100, ec_members = 0x272a4a8,   ec_sources = 0x272a3f0, ec_derives = 0x272d2f0, ec_relids = 0x272a470, ec_has_const = false, ec_has_volatile = false,   ec_below_outer_join = false, ec_broken = false, ec_sortref = 0, ec_min_security = 0, ec_max_security = 0, ec_merged = 0x0}

遍历EC的成员后,cur_em不为NULL,查看cur_em内存结构(匹配的成员,即t_jfxx.grbh)

2281      foreach(lc2, cur_ec->ec_members)(gdb) p *cur_em$7 = {type = T_EquivalenceMember, em_expr = 0x2722890, em_relids = 0x272a238, em_nullable_relids = 0x0,   em_is_const = false, em_is_child = false, em_datatype = 25}(gdb) p *cur_em->em_expr$8 = {type = T_RelabelType}(gdb) p *(RelabelType *)cur_em->em_expr$9 = {xpr = {type = T_RelabelType}, arg = 0x2722840, resulttype = 25, resulttypmod = -1, resultcollid = 100,   relabelformat = COERCE_IMPLICIT_CAST, location = -1}(gdb) p *((RelabelType *)cur_em->em_expr)->arg$10 = {type = T_Var}(gdb) p *(Var *)((RelabelType *)cur_em->em_expr)->arg$11 = {xpr = {type = T_Var}, varno = 4, varattno = 1, vartype = 1043, vartypmod = 14, varcollid = 100, varlevelsup = 0,   varnoold = 4, varoattno = 1, location = 168}

再次遍历等价类的成员,得到第一个约束条件(t_jfxx.grbh=t_grxx.grbh)

(gdb) n2314        rinfo = create_join_clause(root, cur_ec, eq_op,(gdb) 2318        result = lappend(result, rinfo);(gdb) p *rinfo$18 = {type = T_RestrictInfo, clause = 0x272d910, is_pushed_down = true, outerjoin_delayed = false, can_join = true,   pseudoconstant = false, leakproof = false, security_level = 0, clause_relids = 0x272db10, required_relids = 0x272d5f0,   outer_relids = 0x0, nullable_relids = 0x0, left_relids = 0x272dae0, right_relids = 0x272daf8, orclause = 0x0,   parent_ec = 0x272a340, eval_cost = {startup = 0, per_tuple = 0.0025000000000000001}, norm_selec = -1, outer_selec = -1,   mergeopfamilies = 0x272db48, left_ec = 0x272a340, right_ec = 0x272a340, left_em = 0x272a4d8, right_em = 0x272a420,   scansel_cache = 0x0, outer_is_left = false, hashjoinoperator = 98, left_bucketsize = -1, right_bucketsize = -1,   left_mcvfreq = -1, right_mcvfreq = -1}(gdb) set $tmp1=(RelabelType *)((OpExpr *)rinfo->clause)->args->head->data.ptr_value(gdb) set $tmp2=(RelabelType *)((OpExpr *)rinfo->clause)->args->head->next->data.ptr_value(gdb) p *(Var *)$tmp1->arg$31 = {xpr = {type = T_Var}, varno = 4, varattno = 1, vartype = 1043, vartypmod = 14, varcollid = 100, varlevelsup = 0,   varnoold = 4, varoattno = 1, location = 168}(gdb) p *(Var *)$tmp2->arg$32 = {xpr = {type = T_Var}, varno = 3, varattno = 2, vartype = 1043, vartypmod = 14, varcollid = 100, varlevelsup = 0,   varnoold = 3, varoattno = 2, location = 158}

获得了结果,返回到match_eclass_clauses_to_index

2281      foreach(lc2, cur_ec->ec_members)(gdb) 2326      if (result)(gdb) 2327        break;(gdb) 2330    return result;(gdb) 2331  }(gdb) match_eclass_clauses_to_index (root=0x2714c50, index=0x2729888, clauseset=0x7fff69cf0670) at indxpath.c:21842184      match_clauses_to_index(index, clauses, clauseset);...

下面再考察t_grxx.dwbh上的索引为例,分析match_clause_to_index

(gdb) cContinuing.Breakpoint 1, create_index_paths (root=0x2714c50, rel=0x2728c38) at indxpath.c:242242   if (rel->indexlist == NIL)(gdb) p *(IndexOptInfo *)rel->indexlist->head->data.ptr_value$39 = {type = T_IndexOptInfo, indexoid = 16752, reltablespace = 0, rel = 0x2728c38, pages = 276, tuples = 100000,   tree_height = 1, ncolumns = 1, nkeycolumns = 1, indexkeys = 0x2729378, indexcollations = 0x2729390, opfamily = 0x27293a8,   opcintype = 0x27293c0, sortopfamily = 0x27293a8, reverse_sort = 0x27293f0, nulls_first = 0x2729408,   canreturn = 0x27293d8, relam = 403, indexprs = 0x0, indpred = 0x0, indextlist = 0x27294e0, indrestrictinfo = 0x272b040,   predOK = false, unique = false, immediate = true, hypothetical = false, amcanorderbyop = false, amoptionalkey = true,   amsearcharray = true, amsearchnulls = true, amhasgettuple = true, amhasgetbitmap = true, amcanparallel = true,   amcostestimate = 0x94f0ad }

oid=16752,对应的object为idx_t_grxx_dwbh

testdb=# select relname from pg_class where oid=16752;     relname     ----------------- idx_t_grxx_dwbh(1 row)

进入IndexOptInfo循环,第一个元素对应的IndexOptInfo为idx_t_grxx_dwbh

249   foreach(lc, rel->indexlist)(gdb) p *rel->indexlist$40 = {type = T_List, length = 2, head = 0x2729510, tail = 0x2729218}(gdb) p *(IndexOptInfo *)rel->indexlist->head->data->ptr_value$42 = {type = T_IndexOptInfo, indexoid = 16752, reltablespace = 0, rel = 0x2728c38, pages = 276, tuples = 100000,   tree_height = 1, ncolumns = 1, nkeycolumns = 1, indexkeys = 0x2729378, indexcollations = 0x2729390, opfamily = 0x27293a8,   opcintype = 0x27293c0, sortopfamily = 0x27293a8, reverse_sort = 0x27293f0, nulls_first = 0x2729408,   canreturn = 0x27293d8, relam = 403, indexprs = 0x0, indpred = 0x0, indextlist = 0x27294e0, indrestrictinfo = 0x272b040,   predOK = false, unique = false, immediate = true, hypothetical = false, amcanorderbyop = false, amoptionalkey = true,   amsearcharray = true, amsearchnulls = true, amhasgettuple = true, amhasgetbitmap = true, amcanparallel = true,   amcostestimate = 0x94f0ad }

一路小跑,进入match_clause_to_indexcol

...(gdb) stepmatch_clause_to_indexcol (index=0x2729268, indexcol=0, rinfo=0x272ae58) at indxpath.c:23302330    Expr     *clause = rinfo->clause;(gdb) n2331    Index   index_relid = index->rel->relid;(gdb) n2344    opfamily = index->opfamily[indexcol];(gdb) 2345    idxcollation = index->indexcollations[indexcol];(gdb) p index_relid$47 = 3(gdb) p opfamily$48 = 1994(gdb)

根据opfamily查询数据字典

testdb=# select * from pg_opfamily where oid=1994; opfmethod | opfname  | opfnamespace | opfowner -----------+----------+--------------+----------       403 | text_ops |           11 |       10(1 row)-- 索引访问方法(btree)testdb=# select * from pg_am where oid=403; amname | amhandler | amtype --------+-----------+-------- btree  | bthandler | i(1 row)

下面进入is_opclause判断分支

(gdb) p idxcollation$49 = 100(gdb) n2360    if (is_opclause(clause))(gdb) 2362      leftop = get_leftop(clause);(gdb) 2363      rightop = get_rightop(clause);(gdb) 2364      if (!leftop || !rightop)(gdb) p *leftop$50 = {type = T_RelabelType}(gdb) p *rightop$51 = {type = T_Const}

限制条件下推后,形成限制条件t_grxx.dwbh = '1001'

#Var:t_grxx.dwbh(gdb) p *(RelabelType *)leftop$56 = {xpr = {type = T_RelabelType}, arg = 0x272ad80, resulttype = 25, resulttypmod = -1, resultcollid = 100,   relabelformat = COERCE_IMPLICIT_CAST, location = -1}#常量:'1001'(gdb) p *(Const *)rightop$57 = {xpr = {type = T_Const}, consttype = 25, consttypmod = -1, constcollid = 100, constlen = -1, constvalue = 41069848,   constisnull = false, constbyval = false, location = 194}

执行相关判断,返回T

(gdb) n2366      left_relids = rinfo->left_relids;(gdb) 2367      right_relids = rinfo->right_relids;(gdb) 2368      expr_op = ((OpExpr *) clause)->opno;(gdb) 2369      expr_coll = ((OpExpr *) clause)->inputcollid;(gdb) 2370      plain_op = true;(gdb) 2409    if (match_index_to_operand(leftop, indexcol, index) &&(gdb) 2410      !bms_is_member(index_relid, right_relids) &&(gdb) 2409    if (match_index_to_operand(leftop, indexcol, index) &&(gdb) 2411      !contain_volatile_functions(rightop))(gdb) 2410      !bms_is_member(index_relid, right_relids) &&(gdb) 2413      if (IndexCollMatchesExprColl(idxcollation, expr_coll) &&(gdb) 2414        is_indexable_operator(expr_op, opfamily, true))(gdb) 2413      if (IndexCollMatchesExprColl(idxcollation, expr_coll) &&(gdb) 2415        return true;

给clauseset变量赋值

(gdb) match_clause_to_index (index=0x2729268, rinfo=0x272ae58, clauseset=0x7fff69cf0890) at indxpath.c:22552255          list_append_unique_ptr(clauseset->indexclauses[indexcol],(gdb) 2254        clauseset->indexclauses[indexcol] =(gdb) 2257        clauseset->nonempty = true;(gdb) 2258        return;(gdb) 2261  }

返回到match_clauses_to_index

(gdb) match_clauses_to_index (index=0x2729268, clauses=0x272b040, clauseset=0x7fff69cf0890) at indxpath.c:22002200    foreach(lc, clauses)

到此,关于"PostgreSQL中set_base_rel_pathlists函数有什么作用"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

索引 条件 路径 函数 成本 参数 处理 限制 操作符 查询 子句 成员 语句 表达式 验证 排序 生成 信息 变量 常量 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 湖北九亚互联网络科技有限公司 西安纸贵互联网科技有限公司 互联网科技财经 互联网与科技发展 高科技与互联网 互联网科技集团 嘟嘟瓜互联网科技 百单互联网科技有限公司 互联网科技大会案例 阿里巴巴算科技企业还是互联网 互联网大会黑科技排名 广州市恰铎互联网科技有限公司 哈尔滨屹滨互联网科技有限公司 黑科技 体验场景 互联网大会 互联网与数字科技关系 工业互联网科技创业 云南互联网科技学费 西安左小互联网科技有限公司 科技互联网专业工作会议发言稿 河南商务圈互联网科技有限公司 互联网科技公司简介 互联网科技深圳 贷财互联网科技 狮子座互联网科技有限公司 甘肃拓世互联网科技有限公司 互联网科技公司招聘 隆华科技工业互联网 烟台灵鸽互联网科技有限公司 雨阳互联网卡科技有限公司 盐城市攀科互联网科技有限公司
0