美文网首页PostgreSQL
PostgreSQL 源码解读(14)- Insert语句(如何

PostgreSQL 源码解读(14)- Insert语句(如何

作者: EthanHe | 来源:发表于2018-08-10 17:27 被阅读36次

    本文简单介绍了PG插入数据部分的源码,主要内容包括如何构造PlannedStmt数据结构的实现逻辑,以及其他相关的数据结构,构造PlannedStmt的主函数standard_planner位于planner.c文件中。
    Planned中文直译意思是“已被规划过的”,PlannedStmt意思是已被规划过的Statement(SQL语句),这个数据结构中的元素以及该数据结构的构造是理解后续执行SQL语句的关键。

    一、源码解读

    standard_planner函数,生成PlannedStmt,其中最重要的信息是可用于后续执行SQL语句的planTree.

     PlannedStmt *
     standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
     {
         PlannedStmt *result;//返回结果
         PlannerGlobal *glob;//全局的Plan信息-Global information for planning/optimization
         double      tuple_fraction;//
         PlannerInfo *root;//每个Query的Plan信息-Per-query information for planning/optimization
         RelOptInfo *final_rel;//Plan中的每个Relation信息-Per-relation information for planning/optimization
         Path       *best_path;//最优路径
         Plan       *top_plan;//最上层的Plan
         ListCell   *lp,//临时变量
                    *lr;
     
         /*
          * Set up global state for this planner invocation.  This data is needed
          * across all levels of sub-Query that might exist in the given command,
          * so we keep it in a separate struct that's linked to by each per-Query
          * PlannerInfo.
          */
         glob = makeNode(PlannerGlobal);//构建PlannerGlobal
       //初始化参数
         glob->boundParams = boundParams;
         glob->subplans = NIL;
         glob->subroots = NIL;
         glob->rewindPlanIDs = NULL;
         glob->finalrtable = NIL;
         glob->finalrowmarks = NIL;
         glob->resultRelations = NIL;
         glob->nonleafResultRelations = NIL;
         glob->rootResultRelations = NIL;
         glob->relationOids = NIL;
         glob->invalItems = NIL;
         glob->paramExecTypes = NIL;
         glob->lastPHId = 0;
         glob->lastRowMarkId = 0;
         glob->lastPlanNodeId = 0;
         glob->transientPlan = false;
         glob->dependsOnRole = false;
     
         /*
          * Assess whether it's feasible to use parallel mode for this query. We
          * can't do this in a standalone backend, or if the command will try to
          * modify any data, or if this is a cursor operation, or if GUCs are set
          * to values that don't permit parallelism, or if parallel-unsafe
          * functions are present in the query tree.
          *
          * (Note that we do allow CREATE TABLE AS, SELECT INTO, and CREATE
          * MATERIALIZED VIEW to use parallel plans, but this is safe only because
          * the command is writing into a completely new table which workers won't
          * be able to see.  If the workers could see the table, the fact that
          * group locking would cause them to ignore the leader's heavyweight
          * relation extension lock and GIN page locks would make this unsafe.
          * We'll have to fix that somehow if we want to allow parallel inserts in
          * general; updates and deletes have additional problems especially around
          * combo CIDs.)
          *
          * For now, we don't try to use parallel mode if we're running inside a
          * parallel worker.  We might eventually be able to relax this
          * restriction, but for now it seems best not to have parallel workers
          * trying to create their own parallel workers.
          *
          * We can't use parallelism in serializable mode because the predicate
          * locking code is not parallel-aware.  It's not catastrophic if someone
          * tries to run a parallel plan in serializable mode; it just won't get
          * any workers and will run serially.  But it seems like a good heuristic
          * to assume that the same serialization level will be in effect at plan
          * time and execution time, so don't generate a parallel plan if we're in
          * serializable mode.
          */
         if ((cursorOptions & CURSOR_OPT_PARALLEL_OK) != 0 &&
             IsUnderPostmaster &&
             parse->commandType == CMD_SELECT &&
             !parse->hasModifyingCTE &&
             max_parallel_workers_per_gather > 0 &&
             !IsParallelWorker() &&
             !IsolationIsSerializable())//并行模式的判断
         {
             /* all the cheap tests pass, so scan the query tree */
             glob->maxParallelHazard = max_parallel_hazard(parse);
             glob->parallelModeOK = (glob->maxParallelHazard != PROPARALLEL_UNSAFE);
         }
         else
         {
             /* skip the query tree scan, just assume it's unsafe */
             glob->maxParallelHazard = PROPARALLEL_UNSAFE;
             glob->parallelModeOK = false;
         }
     
         /*
          * glob->parallelModeNeeded is normally set to false here and changed to
          * true during plan creation if a Gather or Gather Merge plan is actually
          * created (cf. create_gather_plan, create_gather_merge_plan).
          *
          * However, if force_parallel_mode = on or force_parallel_mode = regress,
          * then we impose parallel mode whenever it's safe to do so, even if the
          * final plan doesn't use parallelism.  It's not safe to do so if the
          * query contains anything parallel-unsafe; parallelModeOK will be false
          * in that case.  Note that parallelModeOK can't change after this point.
          * Otherwise, everything in the query is either parallel-safe or
          * parallel-restricted, and in either case it should be OK to impose
          * parallel-mode restrictions.  If that ends up breaking something, then
          * either some function the user included in the query is incorrectly
          * labelled as parallel-safe or parallel-restricted when in reality it's
          * parallel-unsafe, or else the query planner itself has a bug.
          */
         glob->parallelModeNeeded = glob->parallelModeOK &&
             (force_parallel_mode != FORCE_PARALLEL_OFF);
     
         /* Determine what fraction of the plan is likely to be scanned */
         if (cursorOptions & CURSOR_OPT_FAST_PLAN)
         {
             /*
              * We have no real idea how many tuples the user will ultimately FETCH
              * from a cursor, but it is often the case that he doesn't want 'em
              * all, or would prefer a fast-start plan anyway so that he can
              * process some of the tuples sooner.  Use a GUC parameter to decide
              * what fraction to optimize for.
              */
             tuple_fraction = cursor_tuple_fraction;//使用GUC 参数
     
             /*
              * We document cursor_tuple_fraction as simply being a fraction, which
              * means the edge cases 0 and 1 have to be treated specially here.  We
              * convert 1 to 0 ("all the tuples") and 0 to a very small fraction.
              */
             if (tuple_fraction >= 1.0)
                 tuple_fraction = 0.0;
             else if (tuple_fraction <= 0.0)
                 tuple_fraction = 1e-10;
         }
         else
         {
             /* Default assumption is we need all the tuples */
             tuple_fraction = 0.0;
         }
     
         /* primary planning entry point (may recurse for subqueries) */
         root = subquery_planner(glob, parse, NULL,
                                 false, tuple_fraction);//获取PlannerInfo根节点
     
         /* Select best Path and turn it into a Plan */
         final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL);//获取顶层的RelOptInfo
         best_path = get_cheapest_fractional_path(final_rel, tuple_fraction);//选择最佳路径
     
         top_plan = create_plan(root, best_path);//生成执行计划
     
         /*
          * If creating a plan for a scrollable cursor, make sure it can run
          * backwards on demand.  Add a Material node at the top at need.
          */
         if (cursorOptions & CURSOR_OPT_SCROLL)
         {
             if (!ExecSupportsBackwardScan(top_plan))
                 top_plan = materialize_finished_plan(top_plan);
         }
     
         /*
          * Optionally add a Gather node for testing purposes, provided this is
          * actually a safe thing to do.
          */
         if (force_parallel_mode != FORCE_PARALLEL_OFF && top_plan->parallel_safe)
         {
             Gather     *gather = makeNode(Gather);
     
             /*
              * If there are any initPlans attached to the formerly-top plan node,
              * move them up to the Gather node; same as we do for Material node in
              * materialize_finished_plan.
              */
             gather->plan.initPlan = top_plan->initPlan;
             top_plan->initPlan = NIL;
     
             gather->plan.targetlist = top_plan->targetlist;
             gather->plan.qual = NIL;
             gather->plan.lefttree = top_plan;
             gather->plan.righttree = NULL;
             gather->num_workers = 1;
             gather->single_copy = true;
             gather->invisible = (force_parallel_mode == FORCE_PARALLEL_REGRESS);
     
             /*
              * Since this Gather has no parallel-aware descendants to signal to,
              * we don't need a rescan Param.
              */
             gather->rescan_param = -1;
     
             /*
              * Ideally we'd use cost_gather here, but setting up dummy path data
              * to satisfy it doesn't seem much cleaner than knowing what it does.
              */
             gather->plan.startup_cost = top_plan->startup_cost +
                 parallel_setup_cost;
             gather->plan.total_cost = top_plan->total_cost +
                 parallel_setup_cost + parallel_tuple_cost * top_plan->plan_rows;
             gather->plan.plan_rows = top_plan->plan_rows;
             gather->plan.plan_width = top_plan->plan_width;
             gather->plan.parallel_aware = false;
             gather->plan.parallel_safe = false;
     
             /* use parallel mode for parallel plans. */
             root->glob->parallelModeNeeded = true;
     
             top_plan = &gather->plan;
         }
     
         /*
          * If any Params were generated, run through the plan tree and compute
          * each plan node's extParam/allParam sets.  Ideally we'd merge this into
          * set_plan_references' tree traversal, but for now it has to be separate
          * because we need to visit subplans before not after main plan.
          */
         if (glob->paramExecTypes != NIL)
         {
             Assert(list_length(glob->subplans) == list_length(glob->subroots));
             forboth(lp, glob->subplans, lr, glob->subroots)
             {
                 Plan       *subplan = (Plan *) lfirst(lp);
                 PlannerInfo *subroot = lfirst_node(PlannerInfo, lr);
     
                 SS_finalize_plan(subroot, subplan);
             }
             SS_finalize_plan(root, top_plan);
         }
     
         /* final cleanup of the plan */
         Assert(glob->finalrtable == NIL);
         Assert(glob->finalrowmarks == NIL);
         Assert(glob->resultRelations == NIL);
         Assert(glob->nonleafResultRelations == NIL);
         Assert(glob->rootResultRelations == NIL);
         top_plan = set_plan_references(root, top_plan);
         /* ... and the subplans (both regular subplans and initplans) */
         Assert(list_length(glob->subplans) == list_length(glob->subroots));
         forboth(lp, glob->subplans, lr, glob->subroots)
         {
             Plan       *subplan = (Plan *) lfirst(lp);
             PlannerInfo *subroot = lfirst_node(PlannerInfo, lr);
     
             lfirst(lp) = set_plan_references(subroot, subplan);
         }
     
         /* build the PlannedStmt result */
         result = makeNode(PlannedStmt);
     
         result->commandType = parse->commandType;//命令类型
         result->queryId = parse->queryId;
         result->hasReturning = (parse->returningList != NIL);
         result->hasModifyingCTE = parse->hasModifyingCTE;
         result->canSetTag = parse->canSetTag;
         result->transientPlan = glob->transientPlan;
         result->dependsOnRole = glob->dependsOnRole;
         result->parallelModeNeeded = glob->parallelModeNeeded;
         result->planTree = top_plan;//执行计划(这是后续执行SQL使用到的最重要的地方)
         result->rtable = glob->finalrtable;
         result->resultRelations = glob->resultRelations;
         result->nonleafResultRelations = glob->nonleafResultRelations;
         result->rootResultRelations = glob->rootResultRelations;
         result->subplans = glob->subplans;
         result->rewindPlanIDs = glob->rewindPlanIDs;
         result->rowMarks = glob->finalrowmarks;
         result->relationOids = glob->relationOids;
         result->invalItems = glob->invalItems;
         result->paramExecTypes = glob->paramExecTypes;
         /* utilityStmt should be null, but we might as well copy it */
         result->utilityStmt = parse->utilityStmt;
         result->stmt_location = parse->stmt_location;
         result->stmt_len = parse->stmt_len;
     
         result->jitFlags = PGJIT_NONE;
         if (jit_enabled && jit_above_cost >= 0 &&
             top_plan->total_cost > jit_above_cost)
         {
             result->jitFlags |= PGJIT_PERFORM;
     
             /*
              * Decide how much effort should be put into generating better code.
              */
             if (jit_optimize_above_cost >= 0 &&
                 top_plan->total_cost > jit_optimize_above_cost)
                 result->jitFlags |= PGJIT_OPT3;
             if (jit_inline_above_cost >= 0 &&
                 top_plan->total_cost > jit_inline_above_cost)
                 result->jitFlags |= PGJIT_INLINE;
     
             /*
              * Decide which operations should be JITed.
              */
             if (jit_expressions)
                 result->jitFlags |= PGJIT_EXPR;
             if (jit_tuple_deforming)
                 result->jitFlags |= PGJIT_DEFORM;
         }
     
         return result;
     }
     
    

    二、基础信息

    standard_planner函数使用的数据结构、宏定义以及依赖的函数等。
    数据结构/宏定义
    1、PlannerGlobal

    /*----------
      * PlannerGlobal
      *      Global information for planning/optimization
      *
      * PlannerGlobal holds state for an entire planner invocation; this state
      * is shared across all levels of sub-Queries that exist in the command being
      * planned.
      *----------
      */
     typedef struct PlannerGlobal
     {
       NodeTag     type;
       ParamListInfo boundParams;  /* Param values provided to planner() */
       List       *subplans;       /* Plans for SubPlan nodes */
       List       *subroots;       /* PlannerInfos for SubPlan nodes */
       Bitmapset  *rewindPlanIDs;  /* indices of subplans that require REWIND */
       List       *finalrtable;    /* "flat" rangetable for executor */
       List       *finalrowmarks;  /* "flat" list of PlanRowMarks */
       List       *resultRelations;    /* "flat" list of integer RT indexes */
       List       *nonleafResultRelations; /* "flat" list of integer RT indexes */
       List       *rootResultRelations;    /* "flat" list of integer RT indexes */
       List       *relationOids;   /* OIDs of relations the plan depends on */
       List       *invalItems;     /* other dependencies, as PlanInvalItems */
       List       *paramExecTypes; /* type OIDs for PARAM_EXEC Params */
       Index       lastPHId;       /* highest PlaceHolderVar ID assigned */
       Index       lastRowMarkId;  /* highest PlanRowMark ID assigned */
       int         lastPlanNodeId; /* highest plan node ID assigned */
       bool        transientPlan;  /* redo plan when TransactionXmin changes? */
       bool        dependsOnRole;  /* is plan specific to current role? */
       bool        parallelModeOK; /* parallel mode potentially OK? */
       bool        parallelModeNeeded; /* parallel mode actually required? */
       char        maxParallelHazard;  /* worst PROPARALLEL hazard level */
     } PlannerGlobal;
     
    

    2、PlannerInfo

     /*----------
      * PlannerInfo
      *      Per-query information for planning/optimization
      *
      * This struct is conventionally called "root" in all the planner routines.
      * It holds links to all of the planner's working state, in addition to the
      * original Query.  Note that at present the planner extensively modifies
      * the passed-in Query data structure; someday that should stop.
      *----------
      */
     struct AppendRelInfo;
     
     typedef struct PlannerInfo
     {
         NodeTag     type;
     
         Query      *parse;          /* the Query being planned */
     
         PlannerGlobal *glob;        /* global info for current planner run */
     
         Index       query_level;    /* 1 at the outermost Query */
     
         struct PlannerInfo *parent_root;    /* NULL at outermost Query */
     
         /*
          * plan_params contains the expressions that this query level needs to
          * make available to a lower query level that is currently being planned.
          * outer_params contains the paramIds of PARAM_EXEC Params that outer
          * query levels will make available to this query level.
          */
         List       *plan_params;    /* list of PlannerParamItems, see below */
         Bitmapset  *outer_params;
     
         /*
          * simple_rel_array holds pointers to "base rels" and "other rels" (see
          * comments for RelOptInfo for more info).  It is indexed by rangetable
          * index (so entry 0 is always wasted).  Entries can be NULL when an RTE
          * does not correspond to a base relation, such as a join RTE or an
          * unreferenced view RTE; or if the RelOptInfo hasn't been made yet.
          */
         struct RelOptInfo **simple_rel_array;   /* All 1-rel RelOptInfos */
         int         simple_rel_array_size;  /* allocated size of array */
     
         /*
          * simple_rte_array is the same length as simple_rel_array and holds
          * pointers to the associated rangetable entries.  This lets us avoid
          * rt_fetch(), which can be a bit slow once large inheritance sets have
          * been expanded.
          */
         RangeTblEntry **simple_rte_array;   /* rangetable as an array */
     
         /*
          * append_rel_array is the same length as the above arrays, and holds
          * pointers to the corresponding AppendRelInfo entry indexed by
          * child_relid, or NULL if none.  The array itself is not allocated if
          * append_rel_list is empty.
          */
         struct AppendRelInfo **append_rel_array;
     
         /*
          * all_baserels is a Relids set of all base relids (but not "other"
          * relids) in the query; that is, the Relids identifier of the final join
          * we need to form.  This is computed in make_one_rel, just before we
          * start making Paths.
          */
         Relids      all_baserels;
     
         /*
          * nullable_baserels is a Relids set of base relids that are nullable by
          * some outer join in the jointree; these are rels that are potentially
          * nullable below the WHERE clause, SELECT targetlist, etc.  This is
          * computed in deconstruct_jointree.
          */
         Relids      nullable_baserels;
     
         /*
          * join_rel_list is a list of all join-relation RelOptInfos we have
          * considered in this planning run.  For small problems we just scan the
          * list to do lookups, but when there are many join relations we build a
          * hash table for faster lookups.  The hash table is present and valid
          * when join_rel_hash is not NULL.  Note that we still maintain the list
          * even when using the hash table for lookups; this simplifies life for
          * GEQO.
          */
         List       *join_rel_list;  /* list of join-relation RelOptInfos */
         struct HTAB *join_rel_hash; /* optional hashtable for join relations */
     
         /*
          * When doing a dynamic-programming-style join search, join_rel_level[k]
          * is a list of all join-relation RelOptInfos of level k, and
          * join_cur_level is the current level.  New join-relation RelOptInfos are
          * automatically added to the join_rel_level[join_cur_level] list.
          * join_rel_level is NULL if not in use.
          */
         List      **join_rel_level; /* lists of join-relation RelOptInfos */
         int         join_cur_level; /* index of list being extended */
     
         List       *init_plans;     /* init SubPlans for query */
     
         List       *cte_plan_ids;   /* per-CTE-item list of subplan IDs */
     
         List       *multiexpr_params;   /* List of Lists of Params for MULTIEXPR
                                          * subquery outputs */
     
         List       *eq_classes;     /* list of active EquivalenceClasses */
     
         List       *canon_pathkeys; /* list of "canonical" PathKeys */
     
         List       *left_join_clauses;  /* list of RestrictInfos for mergejoinable
                                          * outer join clauses w/nonnullable var on
                                          * left */
     
         List       *right_join_clauses; /* list of RestrictInfos for mergejoinable
                                          * outer join clauses w/nonnullable var on
                                          * right */
     
         List       *full_join_clauses;  /* list of RestrictInfos for mergejoinable
                                          * full join clauses */
     
         List       *join_info_list; /* list of SpecialJoinInfos */
     
         List       *append_rel_list;    /* list of AppendRelInfos */
     
         List       *rowMarks;       /* list of PlanRowMarks */
     
         List       *placeholder_list;   /* list of PlaceHolderInfos */
     
         List       *fkey_list;      /* list of ForeignKeyOptInfos */
     
         List       *query_pathkeys; /* desired pathkeys for query_planner() */
     
         List       *group_pathkeys; /* groupClause pathkeys, if any */
         List       *window_pathkeys;    /* pathkeys of bottom window, if any */
         List       *distinct_pathkeys;  /* distinctClause pathkeys, if any */
         List       *sort_pathkeys;  /* sortClause pathkeys, if any */
     
         List       *part_schemes;   /* Canonicalised partition schemes used in the
                                      * query. */
     
         List       *initial_rels;   /* RelOptInfos we are now trying to join */
     
         /* Use fetch_upper_rel() to get any particular upper rel */
         List       *upper_rels[UPPERREL_FINAL + 1]; /* upper-rel RelOptInfos */
     
         /* Result tlists chosen by grouping_planner for upper-stage processing */
         struct PathTarget *upper_targets[UPPERREL_FINAL + 1];//参见UpperRelationKind
     
         /*
          * grouping_planner passes back its final processed targetlist here, for
          * use in relabeling the topmost tlist of the finished Plan.
          */
         List       *processed_tlist;
     
         /* Fields filled during create_plan() for use in setrefs.c */
         AttrNumber *grouping_map;   /* for GroupingFunc fixup */
         List       *minmax_aggs;    /* List of MinMaxAggInfos */
     
         MemoryContext planner_cxt;  /* context holding PlannerInfo */
     
         double      total_table_pages;  /* # of pages in all tables of query */
     
         double      tuple_fraction; /* tuple_fraction passed to query_planner */
         double      limit_tuples;   /* limit_tuples passed to query_planner */
     
         Index       qual_security_level;    /* minimum security_level for quals */
         /* Note: qual_security_level is zero if there are no securityQuals */
     
         InheritanceKind inhTargetKind;  /* indicates if the target relation is an
                                          * inheritance child or partition or a
                                          * partitioned table */
         bool        hasJoinRTEs;    /* true if any RTEs are RTE_JOIN kind */
         bool        hasLateralRTEs; /* true if any RTEs are marked LATERAL */
         bool        hasDeletedRTEs; /* true if any RTE was deleted from jointree */
         bool        hasHavingQual;  /* true if havingQual was non-null */
         bool        hasPseudoConstantQuals; /* true if any RestrictInfo has
                                              * pseudoconstant = true */
         bool        hasRecursion;   /* true if planning a recursive WITH item */
     
         /* These fields are used only when hasRecursion is true: */
         int         wt_param_id;    /* PARAM_EXEC ID for the work table */
         struct Path *non_recursive_path;    /* a path for non-recursive term */
     
         /* These fields are workspace for createplan.c */
         Relids      curOuterRels;   /* outer rels above current node */
         List       *curOuterParams; /* not-yet-assigned NestLoopParams */
     
         /* optional private data for join_search_hook, e.g., GEQO */
         void       *join_search_private;
     
         /* Does this query modify any partition key columns? */
         bool        partColsUpdated;
     } PlannerInfo;
     
     /*
      * This enum identifies the different types of "upper" (post-scan/join)
      * relations that we might deal with during planning.
      */
     typedef enum UpperRelationKind
     {
         UPPERREL_SETOP,             /* result of UNION/INTERSECT/EXCEPT, if any */
         UPPERREL_PARTIAL_GROUP_AGG, /* result of partial grouping/aggregation, if
                                      * any */
         UPPERREL_GROUP_AGG,         /* result of grouping/aggregation, if any */
         UPPERREL_WINDOW,            /* result of window functions, if any */
         UPPERREL_DISTINCT,          /* result of "SELECT DISTINCT", if any */
         UPPERREL_ORDERED,           /* result of ORDER BY, if any */
         UPPERREL_FINAL              /* result of any remaining top-level actions */
         /* NB: UPPERREL_FINAL must be last enum entry; it's used to size arrays */
     } UpperRelationKind;
     
    
    

    3、RangeTblEntry

     /*--------------------
      * RangeTblEntry -
      *    A range table is a List of RangeTblEntry nodes.
      *
      *    A range table entry may represent a plain relation, a sub-select in
      *    FROM, or the result of a JOIN clause.  (Only explicit JOIN syntax
      *    produces an RTE, not the implicit join resulting from multiple FROM
      *    items.  This is because we only need the RTE to deal with SQL features
      *    like outer joins and join-output-column aliasing.)  Other special
      *    RTE types also exist, as indicated by RTEKind.
      *
      *    Note that we consider RTE_RELATION to cover anything that has a pg_class
      *    entry.  relkind distinguishes the sub-cases.
      *
      *    alias is an Alias node representing the AS alias-clause attached to the
      *    FROM expression, or NULL if no clause.
      *
      *    eref is the table reference name and column reference names (either
      *    real or aliases).  Note that system columns (OID etc) are not included
      *    in the column list.
      *    eref->aliasname is required to be present, and should generally be used
      *    to identify the RTE for error messages etc.
      *
      *    In RELATION RTEs, the colnames in both alias and eref are indexed by
      *    physical attribute number; this means there must be colname entries for
      *    dropped columns.  When building an RTE we insert empty strings ("") for
      *    dropped columns.  Note however that a stored rule may have nonempty
      *    colnames for columns dropped since the rule was created (and for that
      *    matter the colnames might be out of date due to column renamings).
      *    The same comments apply to FUNCTION RTEs when a function's return type
      *    is a named composite type.
      *
      *    In JOIN RTEs, the colnames in both alias and eref are one-to-one with
      *    joinaliasvars entries.  A JOIN RTE will omit columns of its inputs when
      *    those columns are known to be dropped at parse time.  Again, however,
      *    a stored rule might contain entries for columns dropped since the rule
      *    was created.  (This is only possible for columns not actually referenced
      *    in the rule.)  When loading a stored rule, we replace the joinaliasvars
      *    items for any such columns with null pointers.  (We can't simply delete
      *    them from the joinaliasvars list, because that would affect the attnums
      *    of Vars referencing the rest of the list.)
      *
      *    inh is true for relation references that should be expanded to include
      *    inheritance children, if the rel has any.  This *must* be false for
      *    RTEs other than RTE_RELATION entries.
      *
      *    inFromCl marks those range variables that are listed in the FROM clause.
      *    It's false for RTEs that are added to a query behind the scenes, such
      *    as the NEW and OLD variables for a rule, or the subqueries of a UNION.
      *    This flag is not used anymore during parsing, since the parser now uses
      *    a separate "namespace" data structure to control visibility, but it is
      *    needed by ruleutils.c to determine whether RTEs should be shown in
      *    decompiled queries.
      *
      *    requiredPerms and checkAsUser specify run-time access permissions
      *    checks to be performed at query startup.  The user must have *all*
      *    of the permissions that are OR'd together in requiredPerms (zero
      *    indicates no permissions checking).  If checkAsUser is not zero,
      *    then do the permissions checks using the access rights of that user,
      *    not the current effective user ID.  (This allows rules to act as
      *    setuid gateways.)  Permissions checks only apply to RELATION RTEs.
      *
      *    For SELECT/INSERT/UPDATE permissions, if the user doesn't have
      *    table-wide permissions then it is sufficient to have the permissions
      *    on all columns identified in selectedCols (for SELECT) and/or
      *    insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may
      *    have all 3).  selectedCols, insertedCols and updatedCols are bitmapsets,
      *    which cannot have negative integer members, so we subtract
      *    FirstLowInvalidHeapAttributeNumber from column numbers before storing
      *    them in these fields.  A whole-row Var reference is represented by
      *    setting the bit for InvalidAttrNumber.
      *
      *    securityQuals is a list of security barrier quals (boolean expressions),
      *    to be tested in the listed order before returning a row from the
      *    relation.  It is always NIL in parser output.  Entries are added by the
      *    rewriter to implement security-barrier views and/or row-level security.
      *    Note that the planner turns each boolean expression into an implicitly
      *    AND'ed sublist, as is its usual habit with qualification expressions.
      *--------------------
      */
     typedef enum RTEKind
     {
         RTE_RELATION,               /* ordinary relation reference */
         RTE_SUBQUERY,               /* subquery in FROM */
         RTE_JOIN,                   /* join */
         RTE_FUNCTION,               /* function in FROM */
         RTE_TABLEFUNC,              /* TableFunc(.., column list) */
         RTE_VALUES,                 /* VALUES (<exprlist>), (<exprlist>), ... */
         RTE_CTE,                    /* common table expr (WITH list element) */
         RTE_NAMEDTUPLESTORE         /* tuplestore, e.g. for AFTER triggers */
     } RTEKind;
     
     typedef struct RangeTblEntry
     {
         NodeTag     type;
     
         RTEKind     rtekind;        /* see above */
     
         /*
          * XXX the fields applicable to only some rte kinds should be merged into
          * a union.  I didn't do this yet because the diffs would impact a lot of
          * code that is being actively worked on.  FIXME someday.
          */
     
         /*
          * Fields valid for a plain relation RTE (else zero):
          *
          * As a special case, RTE_NAMEDTUPLESTORE can also set relid to indicate
          * that the tuple format of the tuplestore is the same as the referenced
          * relation.  This allows plans referencing AFTER trigger transition
          * tables to be invalidated if the underlying table is altered.
          */
         Oid         relid;          /* OID of the relation */
         char        relkind;        /* relation kind (see pg_class.relkind) */
         struct TableSampleClause *tablesample;  /* sampling info, or NULL */
     
         /*
          * Fields valid for a subquery RTE (else NULL):
          */
         Query      *subquery;       /* the sub-query */
         bool        security_barrier;   /* is from security_barrier view? */
     
         /*
          * Fields valid for a join RTE (else NULL/zero):
          *
          * joinaliasvars is a list of (usually) Vars corresponding to the columns
          * of the join result.  An alias Var referencing column K of the join
          * result can be replaced by the K'th element of joinaliasvars --- but to
          * simplify the task of reverse-listing aliases correctly, we do not do
          * that until planning time.  In detail: an element of joinaliasvars can
          * be a Var of one of the join's input relations, or such a Var with an
          * implicit coercion to the join's output column type, or a COALESCE
          * expression containing the two input column Vars (possibly coerced).
          * Within a Query loaded from a stored rule, it is also possible for
          * joinaliasvars items to be null pointers, which are placeholders for
          * (necessarily unreferenced) columns dropped since the rule was made.
          * Also, once planning begins, joinaliasvars items can be almost anything,
          * as a result of subquery-flattening substitutions.
          */
         JoinType    jointype;       /* type of join */
         List       *joinaliasvars;  /* list of alias-var expansions */
     
         /*
          * Fields valid for a function RTE (else NIL/zero):
          *
          * When funcordinality is true, the eref->colnames list includes an alias
          * for the ordinality column.  The ordinality column is otherwise
          * implicit, and must be accounted for "by hand" in places such as
          * expandRTE().
          */
         List       *functions;      /* list of RangeTblFunction nodes */
         bool        funcordinality; /* is this called WITH ORDINALITY? */
     
         /*
          * Fields valid for a TableFunc RTE (else NULL):
          */
         TableFunc  *tablefunc;
     
         /*
          * Fields valid for a values RTE (else NIL):
          */
         List       *values_lists;   /* list of expression lists */
     
         /*
          * Fields valid for a CTE RTE (else NULL/zero):
          */
         char       *ctename;        /* name of the WITH list item */
         Index       ctelevelsup;    /* number of query levels up */
         bool        self_reference; /* is this a recursive self-reference? */
     
         /*
          * Fields valid for table functions, values, CTE and ENR RTEs (else NIL):
          *
          * We need these for CTE RTEs so that the types of self-referential
          * columns are well-defined.  For VALUES RTEs, storing these explicitly
          * saves having to re-determine the info by scanning the values_lists. For
          * ENRs, we store the types explicitly here (we could get the information
          * from the catalogs if 'relid' was supplied, but we'd still need these
          * for TupleDesc-based ENRs, so we might as well always store the type
          * info here).
          *
          * For ENRs only, we have to consider the possibility of dropped columns.
          * A dropped column is included in these lists, but it will have zeroes in
          * all three lists (as well as an empty-string entry in eref).  Testing
          * for zero coltype is the standard way to detect a dropped column.
          */
         List       *coltypes;       /* OID list of column type OIDs */
         List       *coltypmods;     /* integer list of column typmods */
         List       *colcollations;  /* OID list of column collation OIDs */
     
         /*
          * Fields valid for ENR RTEs (else NULL/zero):
          */
         char       *enrname;        /* name of ephemeral named relation */
         double      enrtuples;      /* estimated or actual from caller */
     
         /*
          * Fields valid in all RTEs:
          */
         Alias      *alias;          /* user-written alias clause, if any */
         Alias      *eref;           /* expanded reference names */
         bool        lateral;        /* subquery, function, or values is LATERAL? */
         bool        inh;            /* inheritance requested? */
         bool        inFromCl;       /* present in FROM clause? */
         AclMode     requiredPerms;  /* bitmask of required access permissions */
         Oid         checkAsUser;    /* if valid, check access as this role */
         Bitmapset  *selectedCols;   /* columns needing SELECT permission */
         Bitmapset  *insertedCols;   /* columns needing INSERT permission */
         Bitmapset  *updatedCols;    /* columns needing UPDATE permission */
         List       *securityQuals;  /* security barrier quals to apply, if any */
     } RangeTblEntry;
     
    

    4、TargetEntry

     /*--------------------
      * TargetEntry -
      *     a target entry (used in query target lists)
      *
      * Strictly speaking, a TargetEntry isn't an expression node (since it can't
      * be evaluated by ExecEvalExpr).  But we treat it as one anyway, since in
      * very many places it's convenient to process a whole query targetlist as a
      * single expression tree.
      *
      * In a SELECT's targetlist, resno should always be equal to the item's
      * ordinal position (counting from 1).  However, in an INSERT or UPDATE
      * targetlist, resno represents the attribute number of the destination
      * column for the item; so there may be missing or out-of-order resnos.
      * It is even legal to have duplicated resnos; consider
      *      UPDATE table SET arraycol[1] = ..., arraycol[2] = ..., ...
      * The two meanings come together in the executor, because the planner
      * transforms INSERT/UPDATE tlists into a normalized form with exactly
      * one entry for each column of the destination table.  Before that's
      * happened, however, it is risky to assume that resno == position.
      * Generally get_tle_by_resno() should be used rather than list_nth()
      * to fetch tlist entries by resno, and only in SELECT should you assume
      * that resno is a unique identifier.
      *
      * resname is required to represent the correct column name in non-resjunk
      * entries of top-level SELECT targetlists, since it will be used as the
      * column title sent to the frontend.  In most other contexts it is only
      * a debugging aid, and may be wrong or even NULL.  (In particular, it may
      * be wrong in a tlist from a stored rule, if the referenced column has been
      * renamed by ALTER TABLE since the rule was made.  Also, the planner tends
      * to store NULL rather than look up a valid name for tlist entries in
      * non-toplevel plan nodes.)  In resjunk entries, resname should be either
      * a specific system-generated name (such as "ctid") or NULL; anything else
      * risks confusing ExecGetJunkAttribute!
      *
      * ressortgroupref is used in the representation of ORDER BY, GROUP BY, and
      * DISTINCT items.  Targetlist entries with ressortgroupref=0 are not
      * sort/group items.  If ressortgroupref>0, then this item is an ORDER BY,
      * GROUP BY, and/or DISTINCT target value.  No two entries in a targetlist
      * may have the same nonzero ressortgroupref --- but there is no particular
      * meaning to the nonzero values, except as tags.  (For example, one must
      * not assume that lower ressortgroupref means a more significant sort key.)
      * The order of the associated SortGroupClause lists determine the semantics.
      *
      * resorigtbl/resorigcol identify the source of the column, if it is a
      * simple reference to a column of a base table (or view).  If it is not
      * a simple reference, these fields are zeroes.
      *
      * If resjunk is true then the column is a working column (such as a sort key)
      * that should be removed from the final output of the query.  Resjunk columns
      * must have resnos that cannot duplicate any regular column's resno.  Also
      * note that there are places that assume resjunk columns come after non-junk
      * columns.
      *--------------------
      */
     typedef struct TargetEntry
     {
         Expr        xpr;
         Expr       *expr;           /* expression to evaluate */
         AttrNumber  resno;          /* attribute number (see notes above) */
         char       *resname;        /* name of the column (could be NULL) */
         Index       ressortgroupref;    /* nonzero if referenced by a sort/group
                                          * clause */
         Oid         resorigtbl;     /* OID of column's source table */
         AttrNumber  resorigcol;     /* column's number in source table */
         bool        resjunk;        /* set to true to eliminate the attribute from
                                      * final target list */
     } TargetEntry;
      
    

    5、RelOptInfo

     /*----------
      * RelOptInfo
      *      Per-relation information for planning/optimization
      *
      * For planning purposes, a "base rel" is either a plain relation (a table)
      * or the output of a sub-SELECT or function that appears in the range table.
      * In either case it is uniquely identified by an RT index.  A "joinrel"
      * is the joining of two or more base rels.  A joinrel is identified by
      * the set of RT indexes for its component baserels.  We create RelOptInfo
      * nodes for each baserel and joinrel, and store them in the PlannerInfo's
      * simple_rel_array and join_rel_list respectively.
      *
      * Note that there is only one joinrel for any given set of component
      * baserels, no matter what order we assemble them in; so an unordered
      * set is the right datatype to identify it with.
      *
      * We also have "other rels", which are like base rels in that they refer to
      * single RT indexes; but they are not part of the join tree, and are given
      * a different RelOptKind to identify them.
      * Currently the only kind of otherrels are those made for member relations
      * of an "append relation", that is an inheritance set or UNION ALL subquery.
      * An append relation has a parent RTE that is a base rel, which represents
      * the entire append relation.  The member RTEs are otherrels.  The parent
      * is present in the query join tree but the members are not.  The member
      * RTEs and otherrels are used to plan the scans of the individual tables or
      * subqueries of the append set; then the parent baserel is given Append
      * and/or MergeAppend paths comprising the best paths for the individual
      * member rels.  (See comments for AppendRelInfo for more information.)
      *
      * At one time we also made otherrels to represent join RTEs, for use in
      * handling join alias Vars.  Currently this is not needed because all join
      * alias Vars are expanded to non-aliased form during preprocess_expression.
      *
      * We also have relations representing joins between child relations of
      * different partitioned tables. These relations are not added to
      * join_rel_level lists as they are not joined directly by the dynamic
      * programming algorithm.
      *
      * There is also a RelOptKind for "upper" relations, which are RelOptInfos
      * that describe post-scan/join processing steps, such as aggregation.
      * Many of the fields in these RelOptInfos are meaningless, but their Path
      * fields always hold Paths showing ways to do that processing step.
      *
      * Lastly, there is a RelOptKind for "dead" relations, which are base rels
      * that we have proven we don't need to join after all.
      *
      * Parts of this data structure are specific to various scan and join
      * mechanisms.  It didn't seem worth creating new node types for them.
      *
      *      relids - Set of base-relation identifiers; it is a base relation
      *              if there is just one, a join relation if more than one
      *      rows - estimated number of tuples in the relation after restriction
      *             clauses have been applied (ie, output rows of a plan for it)
      *      consider_startup - true if there is any value in keeping plain paths for
      *                         this rel on the basis of having cheap startup cost
      *      consider_param_startup - the same for parameterized paths
      *      reltarget - Default Path output tlist for this rel; normally contains
      *                  Var and PlaceHolderVar nodes for the values we need to
      *                  output from this relation.
      *                  List is in no particular order, but all rels of an
      *                  appendrel set must use corresponding orders.
      *                  NOTE: in an appendrel child relation, may contain
      *                  arbitrary expressions pulled up from a subquery!
      *      pathlist - List of Path nodes, one for each potentially useful
      *                 method of generating the relation
      *      ppilist - ParamPathInfo nodes for parameterized Paths, if any
      *      cheapest_startup_path - the pathlist member with lowest startup cost
      *          (regardless of ordering) among the unparameterized paths;
      *          or NULL if there is no unparameterized path
      *      cheapest_total_path - the pathlist member with lowest total cost
      *          (regardless of ordering) among the unparameterized paths;
      *          or if there is no unparameterized path, the path with lowest
      *          total cost among the paths with minimum parameterization
      *      cheapest_unique_path - for caching cheapest path to produce unique
      *          (no duplicates) output from relation; NULL if not yet requested
      *      cheapest_parameterized_paths - best paths for their parameterizations;
      *          always includes cheapest_total_path, even if that's unparameterized
      *      direct_lateral_relids - rels this rel has direct LATERAL references to
      *      lateral_relids - required outer rels for LATERAL, as a Relids set
      *          (includes both direct and indirect lateral references)
      *
      * If the relation is a base relation it will have these fields set:
      *
      *      relid - RTE index (this is redundant with the relids field, but
      *              is provided for convenience of access)
      *      rtekind - copy of RTE's rtekind field
      *      min_attr, max_attr - range of valid AttrNumbers for rel
      *      attr_needed - array of bitmapsets indicating the highest joinrel
      *              in which each attribute is needed; if bit 0 is set then
      *              the attribute is needed as part of final targetlist
      *      attr_widths - cache space for per-attribute width estimates;
      *                    zero means not computed yet
      *      lateral_vars - lateral cross-references of rel, if any (list of
      *                     Vars and PlaceHolderVars)
      *      lateral_referencers - relids of rels that reference this one laterally
      *              (includes both direct and indirect lateral references)
      *      indexlist - list of IndexOptInfo nodes for relation's indexes
      *                  (always NIL if it's not a table)
      *      pages - number of disk pages in relation (zero if not a table)
      *      tuples - number of tuples in relation (not considering restrictions)
      *      allvisfrac - fraction of disk pages that are marked all-visible
      *      subroot - PlannerInfo for subquery (NULL if it's not a subquery)
      *      subplan_params - list of PlannerParamItems to be passed to subquery
      *
      *      Note: for a subquery, tuples and subroot are not set immediately
      *      upon creation of the RelOptInfo object; they are filled in when
      *      set_subquery_pathlist processes the object.
      *
      *      For otherrels that are appendrel members, these fields are filled
      *      in just as for a baserel, except we don't bother with lateral_vars.
      *
      * If the relation is either a foreign table or a join of foreign tables that
      * all belong to the same foreign server and are assigned to the same user to
      * check access permissions as (cf checkAsUser), these fields will be set:
      *
      *      serverid - OID of foreign server, if foreign table (else InvalidOid)
      *      userid - OID of user to check access as (InvalidOid means current user)
      *      useridiscurrent - we've assumed that userid equals current user
      *      fdwroutine - function hooks for FDW, if foreign table (else NULL)
      *      fdw_private - private state for FDW, if foreign table (else NULL)
      *
      * Two fields are used to cache knowledge acquired during the join search
      * about whether this rel is provably unique when being joined to given other
      * relation(s), ie, it can have at most one row matching any given row from
      * that join relation.  Currently we only attempt such proofs, and thus only
      * populate these fields, for base rels; but someday they might be used for
      * join rels too:
      *
      *      unique_for_rels - list of Relid sets, each one being a set of other
      *                  rels for which this one has been proven unique
      *      non_unique_for_rels - list of Relid sets, each one being a set of
      *                  other rels for which we have tried and failed to prove
      *                  this one unique
      *
      * The presence of the following fields depends on the restrictions
      * and joins that the relation participates in:
      *
      *      baserestrictinfo - List of RestrictInfo nodes, containing info about
      *                  each non-join qualification clause in which this relation
      *                  participates (only used for base rels)
      *      baserestrictcost - Estimated cost of evaluating the baserestrictinfo
      *                  clauses at a single tuple (only used for base rels)
      *      baserestrict_min_security - Smallest security_level found among
      *                  clauses in baserestrictinfo
      *      joininfo  - List of RestrictInfo nodes, containing info about each
      *                  join clause in which this relation participates (but
      *                  note this excludes clauses that might be derivable from
      *                  EquivalenceClasses)
      *      has_eclass_joins - flag that EquivalenceClass joins are possible
      *
      * Note: Keeping a restrictinfo list in the RelOptInfo is useful only for
      * base rels, because for a join rel the set of clauses that are treated as
      * restrict clauses varies depending on which sub-relations we choose to join.
      * (For example, in a 3-base-rel join, a clause relating rels 1 and 2 must be
      * treated as a restrictclause if we join {1} and {2 3} to make {1 2 3}; but
      * if we join {1 2} and {3} then that clause will be a restrictclause in {1 2}
      * and should not be processed again at the level of {1 2 3}.)  Therefore,
      * the restrictinfo list in the join case appears in individual JoinPaths
      * (field joinrestrictinfo), not in the parent relation.  But it's OK for
      * the RelOptInfo to store the joininfo list, because that is the same
      * for a given rel no matter how we form it.
      *
      * We store baserestrictcost in the RelOptInfo (for base relations) because
      * we know we will need it at least once (to price the sequential scan)
      * and may need it multiple times to price index scans.
      *
      * If the relation is partitioned, these fields will be set:
      *
      *      part_scheme - Partitioning scheme of the relation
      *      nparts - Number of partitions
      *      boundinfo - Partition bounds
      *      partition_qual - Partition constraint if not the root
      *      part_rels - RelOptInfos for each partition
      *      partexprs, nullable_partexprs - Partition key expressions
      *      partitioned_child_rels - RT indexes of unpruned partitions of
      *                               this relation that are partitioned tables
      *                               themselves, in hierarchical order
      *
      * Note: A base relation always has only one set of partition keys, but a join
      * relation may have as many sets of partition keys as the number of relations
      * being joined. partexprs and nullable_partexprs are arrays containing
      * part_scheme->partnatts elements each. Each of these elements is a list of
      * partition key expressions.  For a base relation each list in partexprs
      * contains only one expression and nullable_partexprs is not populated. For a
      * join relation, partexprs and nullable_partexprs contain partition key
      * expressions from non-nullable and nullable relations resp. Lists at any
      * given position in those arrays together contain as many elements as the
      * number of joining relations.
      *----------
      */
     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;
     
         /* all relations included in this RelOptInfo */
         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;   /* 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;  /* rels directly laterally referenced */
         Relids      lateral_relids; /* minimum parameterization of rel */
     
         /* information about a base rel (not set for join rels!) */
         Index       relid;
         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;   /* LATERAL Vars and PHVs referenced by rel */
         Relids      lateral_referencers;    /* rels that reference me laterally */
         List       *indexlist;      /* list of IndexOptInfo */
         List       *statlist;       /* list of StatisticExtInfo */
         BlockNumber pages;          /* size estimates derived from pg_class */
         double      tuples;
         double      allvisfrac;
         PlannerInfo *subroot;       /* if subquery */
         List       *subplan_params; /* if subquery */
         int         rel_parallel_workers;   /* wanted number of parallel workers */
     
         /* Information about foreign tables and foreign joins */
         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 */
         List       *unique_for_rels;    /* known unique for these other relid
                                          * set(s) */
         List       *non_unique_for_rels;    /* 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 "other" relations */
         Relids      top_parent_relids;  /* Relids of topmost parents */
     
         /* used for partitioned relations */
         PartitionScheme part_scheme;    /* Partitioning scheme. */
         int         nparts;         /* number of partitions */
         struct PartitionBoundInfoData *boundinfo;   /* Partition bounds */
         List       *partition_qual; /* partition constraint */
         struct RelOptInfo **part_rels;  /* 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; /* List of RT indexes. */
     } RelOptInfo;
     
    

    6、Path

     /*
      * Type "Path" is used as-is for sequential-scan paths, as well as some other
      * simple plan types that we don't need any extra information in the path for.
      * For other path types it is the first component of a larger struct.
      *
      * "pathtype" is the NodeTag of the Plan node we could build from this Path.
      * It is partially redundant with the Path's NodeTag, but allows us to use
      * the same Path type for multiple Plan types when there is no need to
      * distinguish the Plan type during path processing.
      *
      * "parent" identifies the relation this Path scans, and "pathtarget"
      * describes the precise set of output columns the Path would compute.
      * In simple cases all Paths for a given rel share the same targetlist,
      * which we represent by having path->pathtarget equal to parent->reltarget.
      *
      * "param_info", if not NULL, links to a ParamPathInfo that identifies outer
      * relation(s) that provide parameter values to each scan of this path.
      * That means this path can only be joined to those rels by means of nestloop
      * joins with this path on the inside.  Also note that a parameterized path
      * is responsible for testing all "movable" joinclauses involving this rel
      * and the specified outer rel(s).
      *
      * "rows" is the same as parent->rows in simple paths, but in parameterized
      * paths and UniquePaths it can be less than parent->rows, reflecting the
      * fact that we've filtered by extra join conditions or removed duplicates.
      *
      * "pathkeys" is a List of PathKey nodes (see above), describing the sort
      * ordering of the path's output rows.
      */
     typedef struct Path
     {
         NodeTag     type;
     
         NodeTag     pathtype;       /* tag identifying scan/join method */
     
         RelOptInfo *parent;         /* the relation this path can build */
         PathTarget *pathtarget;     /* list of Vars/Exprs, cost, width */
     
         ParamPathInfo *param_info;  /* parameterization info, or NULL if none */
     
         bool        parallel_aware; /* engage parallel-aware logic? */
         bool        parallel_safe;  /* OK to use as part of parallel plan? */
         int         parallel_workers;   /* desired # of workers; 0 = not parallel */
     
         /* estimated size/costs for path (see costsize.c for more info) */
         double      rows;           /* estimated number of result tuples */
         Cost        startup_cost;   /* cost expended before fetching any tuples */
         Cost        total_cost;     /* total cost (assuming all tuples fetched) */
     
         List       *pathkeys;       /* sort ordering of path's output */
         /* pathkeys is a List of PathKey nodes; see above */
     } Path;
     
    /*
      * PathKeys
      *
      * The sort ordering of a path is represented by a list of PathKey nodes.
      * An empty list implies no known ordering.  Otherwise the first item
      * represents the primary sort key, the second the first secondary sort key,
      * etc.  The value being sorted is represented by linking to an
      * EquivalenceClass containing that value and including pk_opfamily among its
      * ec_opfamilies.  The EquivalenceClass tells which collation to use, too.
      * This is a convenient method because it makes it trivial to detect
      * equivalent and closely-related orderings. (See optimizer/README for more
      * information.)
      *
      * Note: pk_strategy is either BTLessStrategyNumber (for ASC) or
      * BTGreaterStrategyNumber (for DESC).  We assume that all ordering-capable
      * index types will use btree-compatible strategy numbers.
      */
     typedef struct PathKey
     {
         NodeTag     type;
     
         EquivalenceClass *pk_eclass;    /* the value that is ordered */
         Oid         pk_opfamily;    /* btree opfamily defining the ordering */
         int         pk_strategy;    /* sort direction (ASC or DESC) */
         bool        pk_nulls_first; /* do NULLs come before normal values? */
     } PathKey;
     
     
     
     /*
      * PathTarget
      *
      * This struct contains what we need to know during planning about the
      * targetlist (output columns) that a Path will compute.  Each RelOptInfo
      * includes a default PathTarget, which its individual Paths may simply
      * reference.  However, in some cases a Path may compute outputs different
      * from other Paths, and in that case we make a custom PathTarget for it.
      * For example, an indexscan might return index expressions that would
      * otherwise need to be explicitly calculated.  (Note also that "upper"
      * relations generally don't have useful default PathTargets.)
      *
      * exprs contains bare expressions; they do not have TargetEntry nodes on top,
      * though those will appear in finished Plans.
      *
      * sortgrouprefs[] is an array of the same length as exprs, containing the
      * corresponding sort/group refnos, or zeroes for expressions not referenced
      * by sort/group clauses.  If sortgrouprefs is NULL (which it generally is in
      * RelOptInfo.reltarget targets; only upper-level Paths contain this info),
      * we have not identified sort/group columns in this tlist.  This allows us to
      * deal with sort/group refnos when needed with less expense than including
      * TargetEntry nodes in the exprs list.
      */
     typedef struct PathTarget
     {
         NodeTag     type;
         List       *exprs;          /* list of expressions to be computed */
         Index      *sortgrouprefs;  /* corresponding sort/group refnos, or 0 */
         QualCost    cost;           /* cost of evaluating the expressions */
         int         width;          /* estimated avg width of result tuples */
     } PathTarget;
     
     /* Convenience macro to get a sort/group refno from a PathTarget */
     #define get_pathtarget_sortgroupref(target, colno) \
         ((target)->sortgrouprefs ? (target)->sortgrouprefs[colno] : (Index) 0)
    
    
    

    7、ModifyTable

     /* ----------------
      *   ModifyTable node -
      *      Apply rows produced by subplan(s) to result table(s),
      *      by inserting, updating, or deleting.
      *
      * Note that rowMarks and epqParam are presumed to be valid for all the
      * subplan(s); they can't contain any info that varies across subplans.
      * ----------------
      */
     typedef struct ModifyTable
     {
         Plan        plan;
         CmdType     operation;      /* INSERT, UPDATE, or DELETE */
         bool        canSetTag;      /* do we set the command tag/es_processed? */
         Index       nominalRelation;    /* Parent RT index for use of EXPLAIN */
         /* RT indexes of non-leaf tables in a partition tree */
         List       *partitioned_rels;
         bool        partColsUpdated;    /* some part key in hierarchy updated */
         List       *resultRelations;    /* integer list of RT indexes */
         int         resultRelIndex; /* index of first resultRel in plan's list */
         int         rootResultRelIndex; /* index of the partitioned table root */
         List       *plans;          /* plan(s) producing source data */
         List       *withCheckOptionLists;   /* per-target-table WCO lists */
         List       *returningLists; /* per-target-table RETURNING tlists */
         List       *fdwPrivLists;   /* per-target-table FDW private data lists */
         Bitmapset  *fdwDirectModifyPlans;   /* indices of FDW DM plans */
         List       *rowMarks;       /* PlanRowMarks (non-locking only) */
         int         epqParam;       /* ID of Param for EvalPlanQual re-eval */
         OnConflictAction onConflictAction;  /* ON CONFLICT action */
         List       *arbiterIndexes; /* List of ON CONFLICT arbiter index OIDs  */
         List       *onConflictSet;  /* SET for INSERT ON CONFLICT DO UPDATE */
         Node       *onConflictWhere;    /* WHERE for ON CONFLICT UPDATE */
         Index       exclRelRTI;     /* RTI of the EXCLUDED pseudo relation */
         List       *exclRelTlist;   /* tlist of the EXCLUDED pseudo relation */
     } ModifyTable;
    

    依赖的函数
    1、grouping_planner

    /*--------------------
      * grouping_planner
      *    Perform planning steps related to grouping, aggregation, etc.
      *
      * This function adds all required top-level processing to the scan/join
      * Path(s) produced by query_planner.
      *
      * If inheritance_update is true, we're being called from inheritance_planner
      * and should not include a ModifyTable step in the resulting Path(s).
      * (inheritance_planner will create a single ModifyTable node covering all the
      * target tables.)
      *
      * tuple_fraction is the fraction of tuples we expect will be retrieved.
      * tuple_fraction is interpreted as follows:
      *    0: expect all tuples to be retrieved (normal case)
      *    0 < tuple_fraction < 1: expect the given fraction of tuples available
      *      from the plan to be retrieved
      *    tuple_fraction >= 1: tuple_fraction is the absolute number of tuples
      *      expected to be retrieved (ie, a LIMIT specification)
      *
      * Returns nothing; the useful output is in the Paths we attach to the
      * (UPPERREL_FINAL, NULL) upperrel in *root.  In addition,
      * root->processed_tlist contains the final processed targetlist.
      *
      * Note that we have not done set_cheapest() on the final rel; it's convenient
      * to leave this to the caller.
      *--------------------
      */
     static void
     grouping_planner(PlannerInfo *root, bool inheritance_update,
                      double tuple_fraction)
     {
         Query      *parse = root->parse;
         List       *tlist;
         int64       offset_est = 0;
         int64       count_est = 0;
         double      limit_tuples = -1.0;
         bool        have_postponed_srfs = false;
         PathTarget *final_target;
         List       *final_targets;
         List       *final_targets_contain_srfs;
         bool        final_target_parallel_safe;
         RelOptInfo *current_rel;
         RelOptInfo *final_rel;
         ListCell   *lc;
     
         /* Tweak caller-supplied tuple_fraction if have LIMIT/OFFSET */
         if (parse->limitCount || parse->limitOffset)
         {
             tuple_fraction = preprocess_limit(root, tuple_fraction,
                                               &offset_est, &count_est);
     
             /*
              * If we have a known LIMIT, and don't have an unknown OFFSET, we can
              * estimate the effects of using a bounded sort.
              */
             if (count_est > 0 && offset_est >= 0)
                 limit_tuples = (double) count_est + (double) offset_est;
         }
     
         /* Make tuple_fraction accessible to lower-level routines */
         root->tuple_fraction = tuple_fraction;
     
         if (parse->setOperations)
         {
             /*
              * If there's a top-level ORDER BY, assume we have to fetch all the
              * tuples.  This might be too simplistic given all the hackery below
              * to possibly avoid the sort; but the odds of accurate estimates here
              * are pretty low anyway.  XXX try to get rid of this in favor of
              * letting plan_set_operations generate both fast-start and
              * cheapest-total paths.
              */
             if (parse->sortClause)
                 root->tuple_fraction = 0.0;
     
             /*
              * Construct Paths for set operations.  The results will not need any
              * work except perhaps a top-level sort and/or LIMIT.  Note that any
              * special work for recursive unions is the responsibility of
              * plan_set_operations.
              */
             current_rel = plan_set_operations(root);
     
             /*
              * We should not need to call preprocess_targetlist, since we must be
              * in a SELECT query node.  Instead, use the targetlist returned by
              * plan_set_operations (since this tells whether it returned any
              * resjunk columns!), and transfer any sort key information from the
              * original tlist.
              */
             Assert(parse->commandType == CMD_SELECT);
     
             tlist = root->processed_tlist;  /* from plan_set_operations */
     
             /* for safety, copy processed_tlist instead of modifying in-place */
             tlist = postprocess_setop_tlist(copyObject(tlist), parse->targetList);
     
             /* Save aside the final decorated tlist */
             root->processed_tlist = tlist;
     
             /* Also extract the PathTarget form of the setop result tlist */
             final_target = current_rel->cheapest_total_path->pathtarget;
     
             /* And check whether it's parallel safe */
             final_target_parallel_safe =
                 is_parallel_safe(root, (Node *) final_target->exprs);
     
             /* The setop result tlist couldn't contain any SRFs */
             Assert(!parse->hasTargetSRFs);
             final_targets = final_targets_contain_srfs = NIL;
     
             /*
              * Can't handle FOR [KEY] UPDATE/SHARE here (parser should have
              * checked already, but let's make sure).
              */
             if (parse->rowMarks)
                 ereport(ERROR,
                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 /*------
                   translator: %s is a SQL row locking clause such as FOR UPDATE */
                          errmsg("%s is not allowed with UNION/INTERSECT/EXCEPT",
                                 LCS_asString(linitial_node(RowMarkClause,
                                                            parse->rowMarks)->strength))));
     
             /*
              * Calculate pathkeys that represent result ordering requirements
              */
             Assert(parse->distinctClause == NIL);
             root->sort_pathkeys = make_pathkeys_for_sortclauses(root,
                                                                 parse->sortClause,
                                                                 tlist);
         }
         else
         {
             /* No set operations, do regular planning */
             PathTarget *sort_input_target;
             List       *sort_input_targets;
             List       *sort_input_targets_contain_srfs;
             bool        sort_input_target_parallel_safe;
             PathTarget *grouping_target;
             List       *grouping_targets;
             List       *grouping_targets_contain_srfs;
             bool        grouping_target_parallel_safe;
             PathTarget *scanjoin_target;
             List       *scanjoin_targets;
             List       *scanjoin_targets_contain_srfs;
             bool        scanjoin_target_parallel_safe;
             bool        scanjoin_target_same_exprs;
             bool        have_grouping;
             AggClauseCosts agg_costs;
             WindowFuncLists *wflists = NULL;
             List       *activeWindows = NIL;
             grouping_sets_data *gset_data = NULL;
             standard_qp_extra qp_extra;
     
             /* A recursive query should always have setOperations */
             Assert(!root->hasRecursion);
     
             /* Preprocess grouping sets and GROUP BY clause, if any */
             if (parse->groupingSets)
             {
                 gset_data = preprocess_grouping_sets(root);
             }
             else
             {
                 /* Preprocess regular GROUP BY clause, if any */
                 if (parse->groupClause)
                     parse->groupClause = preprocess_groupclause(root, NIL);
             }
     
             /* Preprocess targetlist */
             tlist = preprocess_targetlist(root);
     
             /*
              * We are now done hacking up the query's targetlist.  Most of the
              * remaining planning work will be done with the PathTarget
              * representation of tlists, but save aside the full representation so
              * that we can transfer its decoration (resnames etc) to the topmost
              * tlist of the finished Plan.
              */
             root->processed_tlist = tlist;
     
             /*
              * Collect statistics about aggregates for estimating costs, and mark
              * all the aggregates with resolved aggtranstypes.  We must do this
              * before slicing and dicing the tlist into various pathtargets, else
              * some copies of the Aggref nodes might escape being marked with the
              * correct transtypes.
              *
              * Note: currently, we do not detect duplicate aggregates here.  This
              * may result in somewhat-overestimated cost, which is fine for our
              * purposes since all Paths will get charged the same.  But at some
              * point we might wish to do that detection in the planner, rather
              * than during executor startup.
              */
             MemSet(&agg_costs, 0, sizeof(AggClauseCosts));
             if (parse->hasAggs)
             {
                 get_agg_clause_costs(root, (Node *) tlist, AGGSPLIT_SIMPLE,
                                      &agg_costs);
                 get_agg_clause_costs(root, parse->havingQual, AGGSPLIT_SIMPLE,
                                      &agg_costs);
             }
     
             /*
              * Locate any window functions in the tlist.  (We don't need to look
              * anywhere else, since expressions used in ORDER BY will be in there
              * too.)  Note that they could all have been eliminated by constant
              * folding, in which case we don't need to do any more work.
              */
             if (parse->hasWindowFuncs)
             {
                 wflists = find_window_functions((Node *) tlist,
                                                 list_length(parse->windowClause));
                 if (wflists->numWindowFuncs > 0)
                     activeWindows = select_active_windows(root, wflists);
                 else
                     parse->hasWindowFuncs = false;
             }
     
             /*
              * Preprocess MIN/MAX aggregates, if any.  Note: be careful about
              * adding logic between here and the query_planner() call.  Anything
              * that is needed in MIN/MAX-optimizable cases will have to be
              * duplicated in planagg.c.
              */
             if (parse->hasAggs)
                 preprocess_minmax_aggregates(root, tlist);
     
             /*
              * Figure out whether there's a hard limit on the number of rows that
              * query_planner's result subplan needs to return.  Even if we know a
              * hard limit overall, it doesn't apply if the query has any
              * grouping/aggregation operations, or SRFs in the tlist.
              */
             if (parse->groupClause ||
                 parse->groupingSets ||
                 parse->distinctClause ||
                 parse->hasAggs ||
                 parse->hasWindowFuncs ||
                 parse->hasTargetSRFs ||
                 root->hasHavingQual)
                 root->limit_tuples = -1.0;
             else
                 root->limit_tuples = limit_tuples;
     
             /* Set up data needed by standard_qp_callback */
             qp_extra.tlist = tlist;
             qp_extra.activeWindows = activeWindows;
             qp_extra.groupClause = (gset_data
                                     ? (gset_data->rollups ? linitial_node(RollupData, gset_data->rollups)->groupClause : NIL)
                                     : parse->groupClause);
     
             /*
              * Generate the best unsorted and presorted paths for the scan/join
              * portion of this Query, ie the processing represented by the
              * FROM/WHERE clauses.  (Note there may not be any presorted paths.)
              * We also generate (in standard_qp_callback) pathkey representations
              * of the query's sort clause, distinct clause, etc.
              */
             current_rel = query_planner(root, tlist,
                                         standard_qp_callback, &qp_extra);
     
             /*
              * Convert the query's result tlist into PathTarget format.
              *
              * Note: it's desirable to not do this till after query_planner(),
              * because the target width estimates can use per-Var width numbers
              * that were obtained within query_planner().
              */
             final_target = create_pathtarget(root, tlist);
             final_target_parallel_safe =
                 is_parallel_safe(root, (Node *) final_target->exprs);
     
             /*
              * If ORDER BY was given, consider whether we should use a post-sort
              * projection, and compute the adjusted target for preceding steps if
              * so.
              */
             if (parse->sortClause)
             {
                 sort_input_target = make_sort_input_target(root,
                                                            final_target,
                                                            &have_postponed_srfs);
                 sort_input_target_parallel_safe =
                     is_parallel_safe(root, (Node *) sort_input_target->exprs);
             }
             else
             {
                 sort_input_target = final_target;
                 sort_input_target_parallel_safe = final_target_parallel_safe;
             }
     
             /*
              * If we have window functions to deal with, the output from any
              * grouping step needs to be what the window functions want;
              * otherwise, it should be sort_input_target.
              */
             if (activeWindows)
             {
                 grouping_target = make_window_input_target(root,
                                                            final_target,
                                                            activeWindows);
                 grouping_target_parallel_safe =
                     is_parallel_safe(root, (Node *) grouping_target->exprs);
             }
             else
             {
                 grouping_target = sort_input_target;
                 grouping_target_parallel_safe = sort_input_target_parallel_safe;
             }
     
             /*
              * If we have grouping or aggregation to do, the topmost scan/join
              * plan node must emit what the grouping step wants; otherwise, it
              * should emit grouping_target.
              */
             have_grouping = (parse->groupClause || parse->groupingSets ||
                              parse->hasAggs || root->hasHavingQual);
             if (have_grouping)
             {
                 scanjoin_target = make_group_input_target(root, final_target);
                 scanjoin_target_parallel_safe =
                     is_parallel_safe(root, (Node *) grouping_target->exprs);
             }
             else
             {
                 scanjoin_target = grouping_target;
                 scanjoin_target_parallel_safe = grouping_target_parallel_safe;
             }
     
             /*
              * If there are any SRFs in the targetlist, we must separate each of
              * these PathTargets into SRF-computing and SRF-free targets.  Replace
              * each of the named targets with a SRF-free version, and remember the
              * list of additional projection steps we need to add afterwards.
              */
             if (parse->hasTargetSRFs)
             {
                 /* final_target doesn't recompute any SRFs in sort_input_target */
                 split_pathtarget_at_srfs(root, final_target, sort_input_target,
                                          &final_targets,
                                          &final_targets_contain_srfs);
                 final_target = linitial_node(PathTarget, final_targets);
                 Assert(!linitial_int(final_targets_contain_srfs));
                 /* likewise for sort_input_target vs. grouping_target */
                 split_pathtarget_at_srfs(root, sort_input_target, grouping_target,
                                          &sort_input_targets,
                                          &sort_input_targets_contain_srfs);
                 sort_input_target = linitial_node(PathTarget, sort_input_targets);
                 Assert(!linitial_int(sort_input_targets_contain_srfs));
                 /* likewise for grouping_target vs. scanjoin_target */
                 split_pathtarget_at_srfs(root, grouping_target, scanjoin_target,
                                          &grouping_targets,
                                          &grouping_targets_contain_srfs);
                 grouping_target = linitial_node(PathTarget, grouping_targets);
                 Assert(!linitial_int(grouping_targets_contain_srfs));
                 /* scanjoin_target will not have any SRFs precomputed for it */
                 split_pathtarget_at_srfs(root, scanjoin_target, NULL,
                                          &scanjoin_targets,
                                          &scanjoin_targets_contain_srfs);
                 scanjoin_target = linitial_node(PathTarget, scanjoin_targets);
                 Assert(!linitial_int(scanjoin_targets_contain_srfs));
             }
             else
             {
                 /* initialize lists; for most of these, dummy values are OK */
                 final_targets = final_targets_contain_srfs = NIL;
                 sort_input_targets = sort_input_targets_contain_srfs = NIL;
                 grouping_targets = grouping_targets_contain_srfs = NIL;
                 scanjoin_targets = list_make1(scanjoin_target);
                 scanjoin_targets_contain_srfs = NIL;
             }
     
             /* Apply scan/join target. */
             scanjoin_target_same_exprs = list_length(scanjoin_targets) == 1
                 && equal(scanjoin_target->exprs, current_rel->reltarget->exprs);
             apply_scanjoin_target_to_paths(root, current_rel, scanjoin_targets,
                                            scanjoin_targets_contain_srfs,
                                            scanjoin_target_parallel_safe,
                                            scanjoin_target_same_exprs);
     
             /*
              * Save the various upper-rel PathTargets we just computed into
              * root->upper_targets[].  The core code doesn't use this, but it
              * provides a convenient place for extensions to get at the info.  For
              * consistency, we save all the intermediate targets, even though some
              * of the corresponding upperrels might not be needed for this query.
              */
             root->upper_targets[UPPERREL_FINAL] = final_target;
             root->upper_targets[UPPERREL_WINDOW] = sort_input_target;
             root->upper_targets[UPPERREL_GROUP_AGG] = grouping_target;
     
             /*
              * If we have grouping and/or aggregation, consider ways to implement
              * that.  We build a new upperrel representing the output of this
              * phase.
              */
             if (have_grouping)
             {
                 current_rel = create_grouping_paths(root,
                                                     current_rel,
                                                     grouping_target,
                                                     grouping_target_parallel_safe,
                                                     &agg_costs,
                                                     gset_data);
                 /* Fix things up if grouping_target contains SRFs */
                 if (parse->hasTargetSRFs)
                     adjust_paths_for_srfs(root, current_rel,
                                           grouping_targets,
                                           grouping_targets_contain_srfs);
             }
     
             /*
              * If we have window functions, consider ways to implement those.  We
              * build a new upperrel representing the output of this phase.
              */
             if (activeWindows)
             {
                 current_rel = create_window_paths(root,
                                                   current_rel,
                                                   grouping_target,
                                                   sort_input_target,
                                                   sort_input_target_parallel_safe,
                                                   tlist,
                                                   wflists,
                                                   activeWindows);
                 /* Fix things up if sort_input_target contains SRFs */
                 if (parse->hasTargetSRFs)
                     adjust_paths_for_srfs(root, current_rel,
                                           sort_input_targets,
                                           sort_input_targets_contain_srfs);
             }
     
             /*
              * If there is a DISTINCT clause, consider ways to implement that. We
              * build a new upperrel representing the output of this phase.
              */
             if (parse->distinctClause)
             {
                 current_rel = create_distinct_paths(root,
                                                     current_rel);
             }
         }                           /* end of if (setOperations) */
     
         /*
          * If ORDER BY was given, consider ways to implement that, and generate a
          * new upperrel containing only paths that emit the correct ordering and
          * project the correct final_target.  We can apply the original
          * limit_tuples limit in sort costing here, but only if there are no
          * postponed SRFs.
          */
         if (parse->sortClause)
         {
             current_rel = create_ordered_paths(root,
                                                current_rel,
                                                final_target,
                                                final_target_parallel_safe,
                                                have_postponed_srfs ? -1.0 :
                                                limit_tuples);
             /* Fix things up if final_target contains SRFs */
             if (parse->hasTargetSRFs)
                 adjust_paths_for_srfs(root, current_rel,
                                       final_targets,
                                       final_targets_contain_srfs);
         }
     
         /*
          * Now we are prepared to build the final-output upperrel.
          */
         final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL);
     
         /*
          * If the input rel is marked consider_parallel and there's nothing that's
          * not parallel-safe in the LIMIT clause, then the final_rel can be marked
          * consider_parallel as well.  Note that if the query has rowMarks or is
          * not a SELECT, consider_parallel will be false for every relation in the
          * query.
          */
         if (current_rel->consider_parallel &&
             is_parallel_safe(root, parse->limitOffset) &&
             is_parallel_safe(root, parse->limitCount))
             final_rel->consider_parallel = true;
     
         /*
          * If the current_rel belongs to a single FDW, so does the final_rel.
          */
         final_rel->serverid = current_rel->serverid;
         final_rel->userid = current_rel->userid;
         final_rel->useridiscurrent = current_rel->useridiscurrent;
         final_rel->fdwroutine = current_rel->fdwroutine;
     
         /*
          * Generate paths for the final_rel.  Insert all surviving paths, with
          * LockRows, Limit, and/or ModifyTable steps added if needed.
          */
         foreach(lc, current_rel->pathlist)
         {
             Path       *path = (Path *) lfirst(lc);
     
             /*
              * If there is a FOR [KEY] UPDATE/SHARE clause, add the LockRows node.
              * (Note: we intentionally test parse->rowMarks not root->rowMarks
              * here.  If there are only non-locking rowmarks, they should be
              * handled by the ModifyTable node instead.  However, root->rowMarks
              * is what goes into the LockRows node.)
              */
             if (parse->rowMarks)
             {
                 path = (Path *) create_lockrows_path(root, final_rel, path,
                                                      root->rowMarks,
                                                      SS_assign_special_param(root));
             }
     
             /*
              * If there is a LIMIT/OFFSET clause, add the LIMIT node.
              */
             if (limit_needed(parse))
             {
                 path = (Path *) create_limit_path(root, final_rel, path,
                                                   parse->limitOffset,
                                                   parse->limitCount,
                                                   offset_est, count_est);
             }
     
             /*
              * If this is an INSERT/UPDATE/DELETE, and we're not being called from
              * inheritance_planner, add the ModifyTable node.
              */
             if (parse->commandType != CMD_SELECT && !inheritance_update)
             {
                 List       *withCheckOptionLists;
                 List       *returningLists;
                 List       *rowMarks;
     
                 /*
                  * Set up the WITH CHECK OPTION and RETURNING lists-of-lists, if
                  * needed.
                  */
                 if (parse->withCheckOptions)
                     withCheckOptionLists = list_make1(parse->withCheckOptions);
                 else
                     withCheckOptionLists = NIL;
     
                 if (parse->returningList)
                     returningLists = list_make1(parse->returningList);
                 else
                     returningLists = NIL;
     
                 /*
                  * If there was a FOR [KEY] UPDATE/SHARE clause, the LockRows node
                  * will have dealt with fetching non-locked marked rows, else we
                  * need to have ModifyTable do that.
                  */
                 if (parse->rowMarks)
                     rowMarks = NIL;
                 else
                     rowMarks = root->rowMarks;
     
                 path = (Path *)
                     create_modifytable_path(root, final_rel,
                                             parse->commandType,
                                             parse->canSetTag,
                                             parse->resultRelation,
                                             NIL,
                                             false,
                                             list_make1_int(parse->resultRelation),
                                             list_make1(path),
                                             list_make1(root),
                                             withCheckOptionLists,
                                             returningLists,
                                             rowMarks,
                                             parse->onConflict,
                                             SS_assign_special_param(root));
             }
     
             /* And shove it into final_rel */
             add_path(final_rel, path);
         }
     
         /*
          * Generate partial paths for final_rel, too, if outer query levels might
          * be able to make use of them.
          */
         if (final_rel->consider_parallel && root->query_level > 1 &&
             !limit_needed(parse))
         {
             Assert(!parse->rowMarks && parse->commandType == CMD_SELECT);
             foreach(lc, current_rel->partial_pathlist)
             {
                 Path       *partial_path = (Path *) lfirst(lc);
     
                 add_partial_path(final_rel, partial_path);
             }
         }
     
         /*
          * If there is an FDW that's responsible for all baserels of the query,
          * let it consider adding ForeignPaths.
          */
         if (final_rel->fdwroutine &&
             final_rel->fdwroutine->GetForeignUpperPaths)
             final_rel->fdwroutine->GetForeignUpperPaths(root, UPPERREL_FINAL,
                                                         current_rel, final_rel,
                                                         NULL);
     
         /* Let extensions possibly add some more paths */
         if (create_upper_paths_hook)
             (*create_upper_paths_hook) (root, UPPERREL_FINAL,
                                         current_rel, final_rel, NULL);
     
         /* Note: currently, we leave it to callers to do set_cheapest() */
     }
    
     /*
      * query_planner
      *    Generate a path (that is, a simplified plan) for a basic query,
      *    which may involve joins but not any fancier features.
      *
      * Since query_planner does not handle the toplevel processing (grouping,
      * sorting, etc) it cannot select the best path by itself.  Instead, it
      * returns the RelOptInfo for the top level of joining, and the caller
      * (grouping_planner) can choose among the surviving paths for the rel.
      *
      * root describes the query to plan
      * tlist is the target list the query should produce
      *      (this is NOT necessarily root->parse->targetList!)
      * qp_callback is a function to compute query_pathkeys once it's safe to do so
      * qp_extra is optional extra data to pass to qp_callback
      *
      * Note: the PlannerInfo node also includes a query_pathkeys field, which
      * tells query_planner the sort order that is desired in the final output
      * plan.  This value is *not* available at call time, but is computed by
      * qp_callback once we have completed merging the query's equivalence classes.
      * (We cannot construct canonical pathkeys until that's done.)
      */
     RelOptInfo *
     query_planner(PlannerInfo *root, List *tlist,
                   query_pathkeys_callback qp_callback, void *qp_extra)
     {
         Query      *parse = root->parse;
         List       *joinlist;
         RelOptInfo *final_rel;
         Index       rti;
         double      total_pages;
     
         /*
          * If the query has an empty join tree, then it's something easy like
          * "SELECT 2+2;" or "INSERT ... VALUES()".  Fall through quickly.
          */
         if (parse->jointree->fromlist == NIL)
         {
             /* We need a dummy joinrel to describe the empty set of baserels */
             final_rel = build_empty_join_rel(root);
     
             /*
              * If query allows parallelism in general, check whether the quals are
              * parallel-restricted.  (We need not check final_rel->reltarget
              * because it's empty at this point.  Anything parallel-restricted in
              * the query tlist will be dealt with later.)
              */
             if (root->glob->parallelModeOK)
                 final_rel->consider_parallel =
                     is_parallel_safe(root, parse->jointree->quals);
     
             /* The only path for it is a trivial Result path */
             add_path(final_rel, (Path *)
                      create_result_path(root, final_rel,
                                         final_rel->reltarget,
                                         (List *) parse->jointree->quals));
     
             /* Select cheapest path (pretty easy in this case...) */
             set_cheapest(final_rel);
     
             /*
              * We still are required to call qp_callback, in case it's something
              * like "SELECT 2+2 ORDER BY 1".
              */
             root->canon_pathkeys = NIL;
             (*qp_callback) (root, qp_extra);
     
             return final_rel;
      }
    
       /*
      * create_modifytable_path
      *    Creates a pathnode that represents performing INSERT/UPDATE/DELETE mods
      *
      * 'rel' is the parent relation associated with the result
      * 'operation' is the operation type
      * 'canSetTag' is true if we set the command tag/es_processed
      * 'nominalRelation' is the parent RT index for use of EXPLAIN
      * 'partitioned_rels' is an integer list of RT indexes of non-leaf tables in
      *      the partition tree, if this is an UPDATE/DELETE to a partitioned table.
      *      Otherwise NIL.
      * 'partColsUpdated' is true if any partitioning columns are being updated,
      *      either from the target relation or a descendent partitioned table.
      * 'resultRelations' is an integer list of actual RT indexes of target rel(s)
      * 'subpaths' is a list of Path(s) producing source data (one per rel)
      * 'subroots' is a list of PlannerInfo structs (one per rel)
      * 'withCheckOptionLists' is a list of WCO lists (one per rel)
      * 'returningLists' is a list of RETURNING tlists (one per rel)
      * 'rowMarks' is a list of PlanRowMarks (non-locking only)
      * 'onconflict' is the ON CONFLICT clause, or NULL
      * 'epqParam' is the ID of Param for EvalPlanQual re-eval
      */
     ModifyTablePath *
     create_modifytable_path(PlannerInfo *root, RelOptInfo *rel,
                             CmdType operation, bool canSetTag,
                             Index nominalRelation, List *partitioned_rels,
                             bool partColsUpdated,
                             List *resultRelations, List *subpaths,
                             List *subroots,
                             List *withCheckOptionLists, List *returningLists,
                             List *rowMarks, OnConflictExpr *onconflict,
                             int epqParam)
     {
         ModifyTablePath *pathnode = makeNode(ModifyTablePath);
         double      total_size;
         ListCell   *lc;
     
         Assert(list_length(resultRelations) == list_length(subpaths));
         Assert(list_length(resultRelations) == list_length(subroots));
         Assert(withCheckOptionLists == NIL ||
                list_length(resultRelations) == list_length(withCheckOptionLists));
         Assert(returningLists == NIL ||
                list_length(resultRelations) == list_length(returningLists));
     
         pathnode->path.pathtype = T_ModifyTable;
         pathnode->path.parent = rel;
         /* pathtarget is not interesting, just make it minimally valid */
         pathnode->path.pathtarget = rel->reltarget;
         /* For now, assume we are above any joins, so no parameterization */
         pathnode->path.param_info = NULL;
         pathnode->path.parallel_aware = false;
         pathnode->path.parallel_safe = false;
         pathnode->path.parallel_workers = 0;
         pathnode->path.pathkeys = NIL;
     
         /*
          * Compute cost & rowcount as sum of subpath costs & rowcounts.
          *
          * Currently, we don't charge anything extra for the actual table
          * modification work, nor for the WITH CHECK OPTIONS or RETURNING
          * expressions if any.  It would only be window dressing, since
          * ModifyTable is always a top-level node and there is no way for the
          * costs to change any higher-level planning choices.  But we might want
          * to make it look better sometime.
          */
         pathnode->path.startup_cost = 0;
         pathnode->path.total_cost = 0;
         pathnode->path.rows = 0;
         total_size = 0;
         foreach(lc, subpaths)
         {
             Path       *subpath = (Path *) lfirst(lc);
     
             if (lc == list_head(subpaths))  /* first node? */
                 pathnode->path.startup_cost = subpath->startup_cost;
             pathnode->path.total_cost += subpath->total_cost;
             pathnode->path.rows += subpath->rows;
             total_size += subpath->pathtarget->width * subpath->rows;
         }
     
         /*
          * Set width to the average width of the subpath outputs.  XXX this is
          * totally wrong: we should report zero if no RETURNING, else an average
          * of the RETURNING tlist widths.  But it's what happened historically,
          * and improving it is a task for another day.
          */
         if (pathnode->path.rows > 0)
             total_size /= pathnode->path.rows;
         pathnode->path.pathtarget->width = rint(total_size);
     
         pathnode->operation = operation;
         pathnode->canSetTag = canSetTag;
         pathnode->nominalRelation = nominalRelation;
         pathnode->partitioned_rels = list_copy(partitioned_rels);
         pathnode->partColsUpdated = partColsUpdated;
         pathnode->resultRelations = resultRelations;
         pathnode->subpaths = subpaths;
         pathnode->subroots = subroots;
         pathnode->withCheckOptionLists = withCheckOptionLists;
         pathnode->returningLists = returningLists;
         pathnode->rowMarks = rowMarks;
         pathnode->onconflict = onconflict;
         pathnode->epqParam = epqParam;
     
         return pathnode;
     }
     
    

    2、subquery_planner

     /*--------------------
      * subquery_planner
      *    Invokes the planner on a subquery.  We recurse to here for each
      *    sub-SELECT found in the query tree.
      *
      * glob is the global state for the current planner run.
      * parse is the querytree produced by the parser & rewriter.
      * parent_root is the immediate parent Query's info (NULL at the top level).
      * hasRecursion is true if this is a recursive WITH query.
      * tuple_fraction is the fraction of tuples we expect will be retrieved.
      * tuple_fraction is interpreted as explained for grouping_planner, below.
      *
      * Basically, this routine does the stuff that should only be done once
      * per Query object.  It then calls grouping_planner.  At one time,
      * grouping_planner could be invoked recursively on the same Query object;
      * that's not currently true, but we keep the separation between the two
      * routines anyway, in case we need it again someday.
      *
      * subquery_planner will be called recursively to handle sub-Query nodes
      * found within the query's expressions and rangetable.
      *
      * Returns the PlannerInfo struct ("root") that contains all data generated
      * while planning the subquery.  In particular, the Path(s) attached to
      * the (UPPERREL_FINAL, NULL) upperrel represent our conclusions about the
      * cheapest way(s) to implement the query.  The top level will select the
      * best Path and pass it through createplan.c to produce a finished Plan.
      *--------------------
      */
    /*
    输入:
      glob-PlannerGlobal
      parse-Query结构体指针
      parent_root-父PlannerInfo Root节点
      hasRecursion-是否递归?
      tuple_fraction-扫描Tuple比例
    输出:
      PlannerInfo指针
    */
     PlannerInfo *
     subquery_planner(PlannerGlobal *glob, Query *parse,
                      PlannerInfo *parent_root,
                      bool hasRecursion, double tuple_fraction)
     {
         PlannerInfo *root;//返回值
         List       *newWithCheckOptions;//
         List       *newHaving;//Having子句
         bool        hasOuterJoins;//是否存在Outer Join?
         RelOptInfo *final_rel;//
         ListCell   *l;//临时变量
     
         /* Create a PlannerInfo data structure for this subquery */
         root = makeNode(PlannerInfo);//构造返回值
         root->parse = parse;
         root->glob = glob;
         root->query_level = parent_root ? parent_root->query_level + 1 : 1;
         root->parent_root = parent_root;
         root->plan_params = NIL;
         root->outer_params = NULL;
         root->planner_cxt = CurrentMemoryContext;
         root->init_plans = NIL;
         root->cte_plan_ids = NIL;
         root->multiexpr_params = NIL;
         root->eq_classes = NIL;
         root->append_rel_list = NIL;
         root->rowMarks = NIL;
         memset(root->upper_rels, 0, sizeof(root->upper_rels));
         memset(root->upper_targets, 0, sizeof(root->upper_targets));
         root->processed_tlist = NIL;
         root->grouping_map = NULL;
         root->minmax_aggs = NIL;
         root->qual_security_level = 0;
         root->inhTargetKind = INHKIND_NONE;
         root->hasRecursion = hasRecursion;
         if (hasRecursion)
             root->wt_param_id = SS_assign_special_param(root);
         else
             root->wt_param_id = -1;
         root->non_recursive_path = NULL;
         root->partColsUpdated = false;
     
         /*
          * If there is a WITH list, process each WITH query and build an initplan
          * SubPlan structure for it.
          */
         if (parse->cteList)
             SS_process_ctes(root);//With 语句
     
         /*
          * Look for ANY and EXISTS SubLinks in WHERE and JOIN/ON clauses, and try
          * to transform them into joins.  Note that this step does not descend
          * into subqueries; if we pull up any subqueries below, their SubLinks are
          * processed just before pulling them up.
          */
         if (parse->hasSubLinks)
             pull_up_sublinks(root); //转换ANY/EXISTS为JOIN
     
         /*
          * Scan the rangetable for set-returning functions, and inline them if
          * possible (producing subqueries that might get pulled up next).
          * Recursion issues here are handled in the same way as for SubLinks.
          */
         inline_set_returning_functions(root);
     
         /*
          * Check to see if any subqueries in the jointree can be merged into this
          * query.
          */
         pull_up_subqueries(root);//
     
         /*
          * If this is a simple UNION ALL query, flatten it into an appendrel. We
          * do this now because it requires applying pull_up_subqueries to the leaf
          * queries of the UNION ALL, which weren't touched above because they
          * weren't referenced by the jointree (they will be after we do this).
          */
         if (parse->setOperations)
             flatten_simple_union_all(root);
     
         /*
          * Detect whether any rangetable entries are RTE_JOIN kind; if not, we can
          * avoid the expense of doing flatten_join_alias_vars().  Also check for
          * outer joins --- if none, we can skip reduce_outer_joins().  And check
          * for LATERAL RTEs, too.  This must be done after we have done
          * pull_up_subqueries(), of course.
          */
         root->hasJoinRTEs = false;
         root->hasLateralRTEs = false;
         hasOuterJoins = false;
         foreach(l, parse->rtable)
         {
             RangeTblEntry *rte = lfirst_node(RangeTblEntry, l);
     
             if (rte->rtekind == RTE_JOIN)
             {
                 root->hasJoinRTEs = true;
                 if (IS_OUTER_JOIN(rte->jointype))
                     hasOuterJoins = true;
             }
             if (rte->lateral)
                 root->hasLateralRTEs = true;
         }
     
         /*
          * Preprocess RowMark information.  We need to do this after subquery
          * pullup (so that all non-inherited RTEs are present) and before
          * inheritance expansion (so that the info is available for
          * expand_inherited_tables to examine and modify).
          */
         preprocess_rowmarks(root);
     
         /*
          * Expand any rangetable entries that are inheritance sets into "append
          * relations".  This can add entries to the rangetable, but they must be
          * plain base relations not joins, so it's OK (and marginally more
          * efficient) to do it after checking for join RTEs.  We must do it after
          * pulling up subqueries, else we'd fail to handle inherited tables in
          * subqueries.
          */
         expand_inherited_tables(root);
     
         /*
          * Set hasHavingQual to remember if HAVING clause is present.  Needed
          * because preprocess_expression will reduce a constant-true condition to
          * an empty qual list ... but "HAVING TRUE" is not a semantic no-op.
          */
         root->hasHavingQual = (parse->havingQual != NULL);
     
         /* Clear this flag; might get set in distribute_qual_to_rels */
         root->hasPseudoConstantQuals = false;
     
         /*
          * Do expression preprocessing on targetlist and quals, as well as other
          * random expressions in the querytree.  Note that we do not need to
          * handle sort/group expressions explicitly, because they are actually
          * part of the targetlist.
          */
         parse->targetList = (List *)
             preprocess_expression(root, (Node *) parse->targetList,
                                   EXPRKIND_TARGET);
     
         /* Constant-folding might have removed all set-returning functions */
         if (parse->hasTargetSRFs)
             parse->hasTargetSRFs = expression_returns_set((Node *) parse->targetList);
     
         newWithCheckOptions = NIL;
         foreach(l, parse->withCheckOptions)
         {
             WithCheckOption *wco = lfirst_node(WithCheckOption, l);
     
             wco->qual = preprocess_expression(root, wco->qual,
                                               EXPRKIND_QUAL);
             if (wco->qual != NULL)
                 newWithCheckOptions = lappend(newWithCheckOptions, wco);
         }
         parse->withCheckOptions = newWithCheckOptions;
     
         parse->returningList = (List *)
             preprocess_expression(root, (Node *) parse->returningList,
                                   EXPRKIND_TARGET);
     
         preprocess_qual_conditions(root, (Node *) parse->jointree);
     
         parse->havingQual = preprocess_expression(root, parse->havingQual,
                                                   EXPRKIND_QUAL);
     
         foreach(l, parse->windowClause)
         {
             WindowClause *wc = lfirst_node(WindowClause, l);
     
             /* partitionClause/orderClause are sort/group expressions */
             wc->startOffset = preprocess_expression(root, wc->startOffset,
                                                     EXPRKIND_LIMIT);
             wc->endOffset = preprocess_expression(root, wc->endOffset,
                                                   EXPRKIND_LIMIT);
         }
     
         parse->limitOffset = preprocess_expression(root, parse->limitOffset,
                                                    EXPRKIND_LIMIT);
         parse->limitCount = preprocess_expression(root, parse->limitCount,
                                                   EXPRKIND_LIMIT);
     
         if (parse->onConflict)
         {
             parse->onConflict->arbiterElems = (List *)
                 preprocess_expression(root,
                                       (Node *) parse->onConflict->arbiterElems,
                                       EXPRKIND_ARBITER_ELEM);
             parse->onConflict->arbiterWhere =
                 preprocess_expression(root,
                                       parse->onConflict->arbiterWhere,
                                       EXPRKIND_QUAL);
             parse->onConflict->onConflictSet = (List *)
                 preprocess_expression(root,
                                       (Node *) parse->onConflict->onConflictSet,
                                       EXPRKIND_TARGET);
             parse->onConflict->onConflictWhere =
                 preprocess_expression(root,
                                       parse->onConflict->onConflictWhere,
                                       EXPRKIND_QUAL);
             /* exclRelTlist contains only Vars, so no preprocessing needed */
         }
     
         root->append_rel_list = (List *)
             preprocess_expression(root, (Node *) root->append_rel_list,
                                   EXPRKIND_APPINFO);
     
         /* Also need to preprocess expressions within RTEs */
         foreach(l, parse->rtable)
         {
             RangeTblEntry *rte = lfirst_node(RangeTblEntry, l);
             int         kind;
             ListCell   *lcsq;
     
             if (rte->rtekind == RTE_RELATION)
             {
                 if (rte->tablesample)
                     rte->tablesample = (TableSampleClause *)
                         preprocess_expression(root,
                                               (Node *) rte->tablesample,
                                               EXPRKIND_TABLESAMPLE);
             }
             else if (rte->rtekind == RTE_SUBQUERY)
             {
                 /*
                  * We don't want to do all preprocessing yet on the subquery's
                  * expressions, since that will happen when we plan it.  But if it
                  * contains any join aliases of our level, those have to get
                  * expanded now, because planning of the subquery won't do it.
                  * That's only possible if the subquery is LATERAL.
                  */
                 if (rte->lateral && root->hasJoinRTEs)
                     rte->subquery = (Query *)
                         flatten_join_alias_vars(root, (Node *) rte->subquery);
             }
             else if (rte->rtekind == RTE_FUNCTION)
             {
                 /* Preprocess the function expression(s) fully */
                 kind = rte->lateral ? EXPRKIND_RTFUNC_LATERAL : EXPRKIND_RTFUNC;
                 rte->functions = (List *)
                     preprocess_expression(root, (Node *) rte->functions, kind);
             }
             else if (rte->rtekind == RTE_TABLEFUNC)
             {
                 /* Preprocess the function expression(s) fully */
                 kind = rte->lateral ? EXPRKIND_TABLEFUNC_LATERAL : EXPRKIND_TABLEFUNC;
                 rte->tablefunc = (TableFunc *)
                     preprocess_expression(root, (Node *) rte->tablefunc, kind);
             }
             else if (rte->rtekind == RTE_VALUES)
             {
                 /* Preprocess the values lists fully */
                 kind = rte->lateral ? EXPRKIND_VALUES_LATERAL : EXPRKIND_VALUES;
                 rte->values_lists = (List *)
                     preprocess_expression(root, (Node *) rte->values_lists, kind);
             }
     
             /*
              * Process each element of the securityQuals list as if it were a
              * separate qual expression (as indeed it is).  We need to do it this
              * way to get proper canonicalization of AND/OR structure.  Note that
              * this converts each element into an implicit-AND sublist.
              */
             foreach(lcsq, rte->securityQuals)
             {
                 lfirst(lcsq) = preprocess_expression(root,
                                                      (Node *) lfirst(lcsq),
                                                      EXPRKIND_QUAL);
             }
         }
     
         /*
          * Now that we are done preprocessing expressions, and in particular done
          * flattening join alias variables, get rid of the joinaliasvars lists.
          * They no longer match what expressions in the rest of the tree look
          * like, because we have not preprocessed expressions in those lists (and
          * do not want to; for example, expanding a SubLink there would result in
          * a useless unreferenced subplan).  Leaving them in place simply creates
          * a hazard for later scans of the tree.  We could try to prevent that by
          * using QTW_IGNORE_JOINALIASES in every tree scan done after this point,
          * but that doesn't sound very reliable.
          */
         if (root->hasJoinRTEs)
         {
             foreach(l, parse->rtable)
             {
                 RangeTblEntry *rte = lfirst_node(RangeTblEntry, l);
     
                 rte->joinaliasvars = NIL;
             }
         }
     
         /*
          * In some cases we may want to transfer a HAVING clause into WHERE. We
          * cannot do so if the HAVING clause contains aggregates (obviously) or
          * volatile functions (since a HAVING clause is supposed to be executed
          * only once per group).  We also can't do this if there are any nonempty
          * grouping sets; moving such a clause into WHERE would potentially change
          * the results, if any referenced column isn't present in all the grouping
          * sets.  (If there are only empty grouping sets, then the HAVING clause
          * must be degenerate as discussed below.)
          *
          * Also, it may be that the clause is so expensive to execute that we're
          * better off doing it only once per group, despite the loss of
          * selectivity.  This is hard to estimate short of doing the entire
          * planning process twice, so we use a heuristic: clauses containing
          * subplans are left in HAVING.  Otherwise, we move or copy the HAVING
          * clause into WHERE, in hopes of eliminating tuples before aggregation
          * instead of after.
          *
          * If the query has explicit grouping then we can simply move such a
          * clause into WHERE; any group that fails the clause will not be in the
          * output because none of its tuples will reach the grouping or
          * aggregation stage.  Otherwise we must have a degenerate (variable-free)
          * HAVING clause, which we put in WHERE so that query_planner() can use it
          * in a gating Result node, but also keep in HAVING to ensure that we
          * don't emit a bogus aggregated row. (This could be done better, but it
          * seems not worth optimizing.)
          *
          * Note that both havingQual and parse->jointree->quals are in
          * implicitly-ANDed-list form at this point, even though they are declared
          * as Node *.
          */
         newHaving = NIL;
         foreach(l, (List *) parse->havingQual)
         {
             Node       *havingclause = (Node *) lfirst(l);
     
             if ((parse->groupClause && parse->groupingSets) ||
                 contain_agg_clause(havingclause) ||
                 contain_volatile_functions(havingclause) ||
                 contain_subplans(havingclause))
             {
                 /* keep it in HAVING */
                 newHaving = lappend(newHaving, havingclause);
             }
             else if (parse->groupClause && !parse->groupingSets)
             {
                 /* move it to WHERE */
                 parse->jointree->quals = (Node *)
                     lappend((List *) parse->jointree->quals, havingclause);
             }
             else
             {
                 /* put a copy in WHERE, keep it in HAVING */
                 parse->jointree->quals = (Node *)
                     lappend((List *) parse->jointree->quals,
                             copyObject(havingclause));
                 newHaving = lappend(newHaving, havingclause);
             }
         }
         parse->havingQual = (Node *) newHaving;
     
         /* Remove any redundant GROUP BY columns */
         remove_useless_groupby_columns(root);
     
         /*
          * If we have any outer joins, try to reduce them to plain inner joins.
          * This step is most easily done after we've done expression
          * preprocessing.
          */
         if (hasOuterJoins)
             reduce_outer_joins(root);
     
         /*
          * Do the main planning.  If we have an inherited target relation, that
          * needs special processing, else go straight to grouping_planner.
          */
         if (parse->resultRelation &&
             rt_fetch(parse->resultRelation, parse->rtable)->inh)
             inheritance_planner(root);
         else
             grouping_planner(root, false, tuple_fraction);
     
         /*
          * Capture the set of outer-level param IDs we have access to, for use in
          * extParam/allParam calculations later.
          */
         SS_identify_outer_params(root);
     
         /*
          * If any initPlans were created in this query level, adjust the surviving
          * Paths' costs and parallel-safety flags to account for them.  The
          * initPlans won't actually get attached to the plan tree till
          * create_plan() runs, but we must include their effects now.
          */
         final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL);
         SS_charge_for_initplans(root, final_rel);
     
         /*
          * Make sure we've identified the cheapest Path for the final rel.  (By
          * doing this here not in grouping_planner, we include initPlan costs in
          * the decision, though it's unlikely that will change anything.)
          */
         set_cheapest(final_rel);
     
         return root;
     }
     
    

    3.create_plan

     /*
      * create_plan
      *    Creates the access plan for a query by recursively processing the
      *    desired tree of pathnodes, starting at the node 'best_path'.  For
      *    every pathnode found, we create a corresponding plan node containing
      *    appropriate id, target list, and qualification information.
      *
      *    The tlists and quals in the plan tree are still in planner format,
      *    ie, Vars still correspond to the parser's numbering.  This will be
      *    fixed later by setrefs.c.
      *
      *    best_path is the best access path
      *
      *    Returns a Plan tree.
      */
     Plan *
     create_plan(PlannerInfo *root, Path *best_path)
     {
         Plan       *plan;
     
         /* plan_params should not be in use in current query level */
         Assert(root->plan_params == NIL);
     
         /* Initialize this module's private workspace in PlannerInfo */
         root->curOuterRels = NULL;
         root->curOuterParams = NIL;
     
         /* Recursively process the path tree, demanding the correct tlist result */
         plan = create_plan_recurse(root, best_path, CP_EXACT_TLIST);
     
         /*
          * Make sure the topmost plan node's targetlist exposes the original
          * column names and other decorative info.  Targetlists generated within
          * the planner don't bother with that stuff, but we must have it on the
          * top-level tlist seen at execution time.  However, ModifyTable plan
          * nodes don't have a tlist matching the querytree targetlist.
          */
         if (!IsA(plan, ModifyTable))
             apply_tlist_labeling(plan->targetlist, root->processed_tlist);
     
         /*
          * Attach any initPlans created in this query level to the topmost plan
          * node.  (In principle the initplans could go in any plan node at or
          * above where they're referenced, but there seems no reason to put them
          * any lower than the topmost node for the query level.  Also, see
          * comments for SS_finalize_plan before you try to change this.)
          */
         SS_attach_initplans(root, plan);
     
         /* Check we successfully assigned all NestLoopParams to plan nodes */
         if (root->curOuterParams != NIL)
             elog(ERROR, "failed to assign all NestLoopParams to plan nodes");
     
         /*
          * Reset plan_params to ensure param IDs used for nestloop params are not
          * re-used later
          */
         root->plan_params = NIL;
     
         return plan;
     }
    
    /*
      * create_plan_recurse
      *    Recursive guts of create_plan().
      */
     static Plan *
     create_plan_recurse(PlannerInfo *root, Path *best_path, int flags)
     {
         Plan       *plan;
     
         /* Guard against stack overflow due to overly complex plans */
         check_stack_depth();
     
         switch (best_path->pathtype)
         {
             case T_SeqScan:
             case T_SampleScan:
             case T_IndexScan:
             case T_IndexOnlyScan:
             case T_BitmapHeapScan:
             case T_TidScan:
             case T_SubqueryScan:
             case T_FunctionScan:
             case T_TableFuncScan:
             case T_ValuesScan:
             case T_CteScan:
             case T_WorkTableScan:
             case T_NamedTuplestoreScan:
             case T_ForeignScan:
             case T_CustomScan:
                 plan = create_scan_plan(root, best_path, flags);
                 break;
             case T_HashJoin:
             case T_MergeJoin:
             case T_NestLoop:
                 plan = create_join_plan(root,
                                         (JoinPath *) best_path);
                 break;
             case T_Append:
                 plan = create_append_plan(root,
                                           (AppendPath *) best_path);
                 break;
             case T_MergeAppend:
                 plan = create_merge_append_plan(root,
                                                 (MergeAppendPath *) best_path);
                 break;
             case T_Result:
                 if (IsA(best_path, ProjectionPath))
                 {
                     plan = create_projection_plan(root,
                                                   (ProjectionPath *) best_path,
                                                   flags);
                 }
                 else if (IsA(best_path, MinMaxAggPath))
                 {
                     plan = (Plan *) create_minmaxagg_plan(root,
                                                           (MinMaxAggPath *) best_path);
                 }
                 else
                 {
                     Assert(IsA(best_path, ResultPath));
                     plan = (Plan *) create_result_plan(root,
                                                        (ResultPath *) best_path);
                 }
                 break;
             case T_ProjectSet:
                 plan = (Plan *) create_project_set_plan(root,
                                                         (ProjectSetPath *) best_path);
                 break;
             case T_Material:
                 plan = (Plan *) create_material_plan(root,
                                                      (MaterialPath *) best_path,
                                                      flags);
                 break;
             case T_Unique:
                 if (IsA(best_path, UpperUniquePath))
                 {
                     plan = (Plan *) create_upper_unique_plan(root,
                                                              (UpperUniquePath *) best_path,
                                                              flags);
                 }
                 else
                 {
                     Assert(IsA(best_path, UniquePath));
                     plan = create_unique_plan(root,
                                               (UniquePath *) best_path,
                                               flags);
                 }
                 break;
             case T_Gather:
                 plan = (Plan *) create_gather_plan(root,
                                                    (GatherPath *) best_path);
                 break;
             case T_Sort:
                 plan = (Plan *) create_sort_plan(root,
                                                  (SortPath *) best_path,
                                                  flags);
                 break;
             case T_Group:
                 plan = (Plan *) create_group_plan(root,
                                                   (GroupPath *) best_path);
                 break;
             case T_Agg:
                 if (IsA(best_path, GroupingSetsPath))
                     plan = create_groupingsets_plan(root,
                                                     (GroupingSetsPath *) best_path);
                 else
                 {
                     Assert(IsA(best_path, AggPath));
                     plan = (Plan *) create_agg_plan(root,
                                                     (AggPath *) best_path);
                 }
                 break;
             case T_WindowAgg:
                 plan = (Plan *) create_windowagg_plan(root,
                                                       (WindowAggPath *) best_path);
                 break;
             case T_SetOp:
                 plan = (Plan *) create_setop_plan(root,
                                                   (SetOpPath *) best_path,
                                                   flags);
                 break;
             case T_RecursiveUnion:
                 plan = (Plan *) create_recursiveunion_plan(root,
                                                            (RecursiveUnionPath *) best_path);
                 break;
             case T_LockRows:
                 plan = (Plan *) create_lockrows_plan(root,
                                                      (LockRowsPath *) best_path,
                                                      flags);
                 break;
             case T_ModifyTable:
                 plan = (Plan *) create_modifytable_plan(root,
                                                         (ModifyTablePath *) best_path);
                 break;
             case T_Limit:
                 plan = (Plan *) create_limit_plan(root,
                                                   (LimitPath *) best_path,
                                                   flags);
                 break;
             case T_GatherMerge:
                 plan = (Plan *) create_gather_merge_plan(root,
                                                          (GatherMergePath *) best_path);
                 break;
             default:
                 elog(ERROR, "unrecognized node type: %d",
                      (int) best_path->pathtype);
                 plan = NULL;        /* keep compiler quiet */
                 break;
         }
     
         return plan;
     }
    
     /*
      * create_modifytable_plan
      *    Create a ModifyTable plan for 'best_path'.
      *
      *    Returns a Plan node.
      */
     static ModifyTable *
     create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path)
     {
         ModifyTable *plan;
         List       *subplans = NIL;
         ListCell   *subpaths,
                    *subroots;
     
         /* Build the plan for each input path */
         forboth(subpaths, best_path->subpaths,
                 subroots, best_path->subroots)
         {
             Path       *subpath = (Path *) lfirst(subpaths);
             PlannerInfo *subroot = (PlannerInfo *) lfirst(subroots);
             Plan       *subplan;
     
             /*
              * In an inherited UPDATE/DELETE, reference the per-child modified
              * subroot while creating Plans from Paths for the child rel.  This is
              * a kluge, but otherwise it's too hard to ensure that Plan creation
              * functions (particularly in FDWs) don't depend on the contents of
              * "root" matching what they saw at Path creation time.  The main
              * downside is that creation functions for Plans that might appear
              * below a ModifyTable cannot expect to modify the contents of "root"
              * and have it "stick" for subsequent processing such as setrefs.c.
              * That's not great, but it seems better than the alternative.
              */
             subplan = create_plan_recurse(subroot, subpath, CP_EXACT_TLIST);
     
             /* Transfer resname/resjunk labeling, too, to keep executor happy */
             apply_tlist_labeling(subplan->targetlist, subroot->processed_tlist);
     
             subplans = lappend(subplans, subplan);
         }
     
         plan = make_modifytable(root,
                                 best_path->operation,
                                 best_path->canSetTag,
                                 best_path->nominalRelation,
                                 best_path->partitioned_rels,
                                 best_path->partColsUpdated,
                                 best_path->resultRelations,
                                 subplans,
                                 best_path->subroots,
                                 best_path->withCheckOptionLists,
                                 best_path->returningLists,
                                 best_path->rowMarks,
                                 best_path->onconflict,
                                 best_path->epqParam);
     
         copy_generic_path_info(&plan->plan, &best_path->path);
     
         return plan;
     }
    
     /*
      * make_modifytable
      *    Build a ModifyTable plan node
      */
     static ModifyTable *
     make_modifytable(PlannerInfo *root,
                      CmdType operation, bool canSetTag,
                      Index nominalRelation, List *partitioned_rels,
                      bool partColsUpdated,
                      List *resultRelations, List *subplans, List *subroots,
                      List *withCheckOptionLists, List *returningLists,
                      List *rowMarks, OnConflictExpr *onconflict, int epqParam)
     {
         ModifyTable *node = makeNode(ModifyTable);
         List       *fdw_private_list;
         Bitmapset  *direct_modify_plans;
         ListCell   *lc;
         ListCell   *lc2;
         int         i;
     
         Assert(list_length(resultRelations) == list_length(subplans));
         Assert(list_length(resultRelations) == list_length(subroots));
         Assert(withCheckOptionLists == NIL ||
                list_length(resultRelations) == list_length(withCheckOptionLists));
         Assert(returningLists == NIL ||
                list_length(resultRelations) == list_length(returningLists));
     
         node->plan.lefttree = NULL;
         node->plan.righttree = NULL;
         node->plan.qual = NIL;
         /* setrefs.c will fill in the targetlist, if needed */
         node->plan.targetlist = NIL;
     
         node->operation = operation;
         node->canSetTag = canSetTag;
         node->nominalRelation = nominalRelation;
         node->partitioned_rels = flatten_partitioned_rels(partitioned_rels);
         node->partColsUpdated = partColsUpdated;
         node->resultRelations = resultRelations;
         node->resultRelIndex = -1;  /* will be set correctly in setrefs.c */
         node->rootResultRelIndex = -1;  /* will be set correctly in setrefs.c */
         node->plans = subplans;
         if (!onconflict)
         {
             node->onConflictAction = ONCONFLICT_NONE;
             node->onConflictSet = NIL;
             node->onConflictWhere = NULL;
             node->arbiterIndexes = NIL;
             node->exclRelRTI = 0;
             node->exclRelTlist = NIL;
         }
         else
         {
             node->onConflictAction = onconflict->action;
             node->onConflictSet = onconflict->onConflictSet;
             node->onConflictWhere = onconflict->onConflictWhere;
     
             /*
              * If a set of unique index inference elements was provided (an
              * INSERT...ON CONFLICT "inference specification"), then infer
              * appropriate unique indexes (or throw an error if none are
              * available).
              */
             node->arbiterIndexes = infer_arbiter_indexes(root);
     
             node->exclRelRTI = onconflict->exclRelIndex;
             node->exclRelTlist = onconflict->exclRelTlist;
         }
         node->withCheckOptionLists = withCheckOptionLists;
         node->returningLists = returningLists;
         node->rowMarks = rowMarks;
         node->epqParam = epqParam;
     
         /*
          * For each result relation that is a foreign table, allow the FDW to
          * construct private plan data, and accumulate it all into a list.
          */
         fdw_private_list = NIL;
         direct_modify_plans = NULL;
         i = 0;
         forboth(lc, resultRelations, lc2, subroots)
         {
             Index       rti = lfirst_int(lc);
             PlannerInfo *subroot = lfirst_node(PlannerInfo, lc2);
             FdwRoutine *fdwroutine;
             List       *fdw_private;
             bool        direct_modify;
     
             /*
              * If possible, we want to get the FdwRoutine from our RelOptInfo for
              * the table.  But sometimes we don't have a RelOptInfo and must get
              * it the hard way.  (In INSERT, the target relation is not scanned,
              * so it's not a baserel; and there are also corner cases for
              * updatable views where the target rel isn't a baserel.)
              */
             if (rti < subroot->simple_rel_array_size &&
                 subroot->simple_rel_array[rti] != NULL)
             {
                 RelOptInfo *resultRel = subroot->simple_rel_array[rti];
     
                 fdwroutine = resultRel->fdwroutine;
             }
             else
             {
                 RangeTblEntry *rte = planner_rt_fetch(rti, subroot);
     
                 Assert(rte->rtekind == RTE_RELATION);
                 if (rte->relkind == RELKIND_FOREIGN_TABLE)
                     fdwroutine = GetFdwRoutineByRelId(rte->relid);
                 else
                     fdwroutine = NULL;
             }
     
             /*
              * Try to modify the foreign table directly if (1) the FDW provides
              * callback functions needed for that, (2) there are no row-level
              * triggers on the foreign table, and (3) there are no WITH CHECK
              * OPTIONs from parent views.
              */
             direct_modify = false;
             if (fdwroutine != NULL &&
                 fdwroutine->PlanDirectModify != NULL &&
                 fdwroutine->BeginDirectModify != NULL &&
                 fdwroutine->IterateDirectModify != NULL &&
                 fdwroutine->EndDirectModify != NULL &&
                 withCheckOptionLists == NIL &&
                 !has_row_triggers(subroot, rti, operation))
                 direct_modify = fdwroutine->PlanDirectModify(subroot, node, rti, i);
             if (direct_modify)
                 direct_modify_plans = bms_add_member(direct_modify_plans, i);
     
             if (!direct_modify &&
                 fdwroutine != NULL &&
                 fdwroutine->PlanForeignModify != NULL)
                 fdw_private = fdwroutine->PlanForeignModify(subroot, node, rti, i);
             else
                 fdw_private = NIL;
             fdw_private_list = lappend(fdw_private_list, fdw_private);
             i++;
         }
         node->fdwPrivLists = fdw_private_list;
         node->fdwDirectModifyPlans = direct_modify_plans;
     
         return node;
     }
     
    

    三、跟踪分析

    插入测试数据:

    testdb=# insert into t_insert values(1000,'I am test','I am test','I am test');
    (挂起)
    

    启动gdb,跟踪调试:

    standard_planner

    [root@localhost ~]# gdb -p 1610
    GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-100.el7
    Copyright (C) 2013 Free Software Foundation, Inc.
    ...
    #跟踪进入subquery_planner(见后)
    (gdb) n
    409   final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL);
    (gdb) 
    410   best_path = get_cheapest_fractional_path(final_rel, tuple_fraction);
    #最优路径,INSERT语句,Plan为T_ModifyTable
    (gdb) p *best_path
    $51 = {type = T_ModifyTablePath, pathtype = T_ModifyTable, parent = 0x21c40a0, pathtarget = 0x21c42b0, 
      param_info = 0x0, parallel_aware = false, parallel_safe = false, parallel_workers = 0, rows = 1, 
      startup_cost = 0, total_cost = 0.01, pathkeys = 0x0}
    (gdb) 
    412   top_plan = create_plan(root, best_path);
    (gdb) step
    create_plan (root=0x21c2cb0, best_path=0x219dd88) at createplan.c:323
    323   root->curOuterRels = NULL;
    (gdb) n
    324   root->curOuterParams = NIL;
    (gdb) 
    327   plan = create_plan_recurse(root, best_path, CP_EXACT_TLIST);
    (gdb) 
    336   if (!IsA(plan, ModifyTable))
    #plan可用于后续的执行
    (gdb) p *plan
    $53 = {type = T_ModifyTable, startup_cost = 0, total_cost = 0.01, plan_rows = 1, plan_width = 298, 
      parallel_aware = false, parallel_safe = false, plan_node_id = 0, targetlist = 0x0, qual = 0x0, 
      lefttree = 0x0, righttree = 0x0, initPlan = 0x0, extParam = 0x0, allParam = 0x0}
    
    

    subquery_planner

    [root@localhost ~]# gdb -p 1610
    GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-100.el7
    Copyright (C) 2013 Free Software Foundation, Inc.
    ...
    (gdb) b subquery_planner
    Breakpoint 1 at 0x76a0bb: file planner.c, line 606.
    (gdb) c
    Continuing.
    ...
    Breakpoint 1, subquery_planner (glob=0x21c2c20, parse=0x219de98, parent_root=0x0, hasRecursion=false, 
        tuple_fraction=0) at planner.c:606
    606     root = makeNode(PlannerInfo);
    #输入参数
    #1,glob
    (gdb) p *glob
    $1 = {type = T_PlannerGlobal, boundParams = 0x0, subplans = 0x0, subroots = 0x0, rewindPlanIDs = 0x0, 
      finalrtable = 0x0, finalrowmarks = 0x0, resultRelations = 0x0, nonleafResultRelations = 0x0, 
      rootResultRelations = 0x0, relationOids = 0x0, invalItems = 0x0, paramExecTypes = 0x0, lastPHId = 0, 
      lastRowMarkId = 0, lastPlanNodeId = 0, transientPlan = false, dependsOnRole = false, 
      parallelModeOK = false, parallelModeNeeded = false, maxParallelHazard = 117 'u'}
    #2,parse
    #Query结构体
    (gdb) p *parse
    $2 = {type = T_Query, commandType = CMD_INSERT, querySource = QSRC_ORIGINAL, queryId = 0, canSetTag = true, 
      utilityStmt = 0x0, resultRelation = 1, hasAggs = false, hasWindowFuncs = false, hasTargetSRFs = false, 
      hasSubLinks = false, hasDistinctOn = false, hasRecursive = false, hasModifyingCTE = false, 
      hasForUpdate = false, hasRowSecurity = false, cteList = 0x0, rtable = 0x219e2b8, jointree = 0x21c2aa0, 
      targetList = 0x21c2b20, override = OVERRIDING_NOT_SET, onConflict = 0x0, returningList = 0x0, 
      groupClause = 0x0, groupingSets = 0x0, havingQual = 0x0, windowClause = 0x0, distinctClause = 0x0, 
      sortClause = 0x0, limitOffset = 0x0, limitCount = 0x0, rowMarks = 0x0, setOperations = 0x0, 
      constraintDeps = 0x0, withCheckOptions = 0x0, stmt_location = 0, stmt_len = 69}
    #targetList中的元素为TargetEntry *
    #在insert语句中,是数据表列
    (gdb) p *(parse->targetList)
    $3 = {type = T_List, length = 4, head = 0x21c2b00, tail = 0x21c2b90}
    (gdb)  p *((TargetEntry *)(parse->targetList->head->data.ptr_value))
    $4 = {xpr = {type = T_TargetEntry}, expr = 0x219e5e8, resno = 1, resname = 0x219e338 "id", 
      ressortgroupref = 0, resorigtbl = 0, resorigcol = 0, resjunk = false}
    #rtable中的元素是RangeTblEntry *
    #在insert操作中,是数据表
    (gdb)  p *(parse->rtable)
    $5 = {type = T_List, length = 1, head = 0x219e298, tail = 0x219e298}
    (gdb) p *((RangeTblEntry *)(parse->rtable->head->data.ptr_value))
    $6 = {type = T_RangeTblEntry, rtekind = RTE_RELATION, relid = 26731, relkind = 114 'r', tablesample = 0x0, 
      subquery = 0x0, security_barrier = false, jointype = JOIN_INNER, joinaliasvars = 0x0, functions = 0x0, 
      funcordinality = false, tablefunc = 0x0, values_lists = 0x0, ctename = 0x0, ctelevelsup = 0, 
      self_reference = false, coltypes = 0x0, coltypmods = 0x0, colcollations = 0x0, enrname = 0x0, 
      enrtuples = 0, alias = 0x0, eref = 0x219e0b8, lateral = false, inh = false, inFromCl = false, 
      requiredPerms = 1, checkAsUser = 0, selectedCols = 0x0, insertedCols = 0x21c2938, updatedCols = 0x0, 
      securityQuals = 0x0}
    (gdb) p *(((RangeTblEntry *)(parse->rtable->head->data.ptr_value))->insertedCols)
    $7 = {nwords = 1, words = 0x21c293c}
    #3,parent_root
    (gdb) p *parent_root
    Cannot access memory at address 0x0
    #4,hasRecursion
    (gdb) p hasRecursion
    $9 = false
    #5,tuple_fraction
    (gdb) p tuple_fraction
    $10 = 0
    ...
    639     if (parse->cteList)
    (gdb) 
    648     if (parse->hasSubLinks)
    (gdb) 
    656     inline_set_returning_functions(root);
    (gdb) 
    662     pull_up_subqueries(root);
    (gdb) 
    670     if (parse->setOperations)
    (gdb) 
    680     root->hasJoinRTEs = false;
    (gdb) 
    681     root->hasLateralRTEs = false;
    682     hasOuterJoins = false;
    (gdb) 
    683     foreach(l, parse->rtable)
    (gdb) 
    685         RangeTblEntry *rte = lfirst_node(RangeTblEntry, l);
    (gdb) 
    687         if (rte->rtekind == RTE_JOIN)
    (gdb) p *rte
    $11 = {type = T_RangeTblEntry, rtekind = RTE_RELATION, relid = 26731, relkind = 114 'r', tablesample = 0x0, 
      subquery = 0x0, security_barrier = false, jointype = JOIN_INNER, joinaliasvars = 0x0, functions = 0x0, 
      funcordinality = false, tablefunc = 0x0, values_lists = 0x0, ctename = 0x0, ctelevelsup = 0, 
      self_reference = false, coltypes = 0x0, coltypmods = 0x0, colcollations = 0x0, enrname = 0x0, 
      enrtuples = 0, alias = 0x0, eref = 0x219e0b8, lateral = false, inh = false, inFromCl = false, 
      requiredPerms = 1, checkAsUser = 0, selectedCols = 0x0, insertedCols = 0x21c2938, updatedCols = 0x0, 
      securityQuals = 0x0}
    ...
    731     parse->targetList = (List *)
    (gdb) 
    736     if (parse->hasTargetSRFs)
    (gdb) p *((TargetEntry *)(parse->targetList->head->data.ptr_value))
    $12 = {xpr = {type = T_TargetEntry}, expr = 0x21c3110, resno = 1, resname = 0x219e338 "id", 
      ressortgroupref = 0, resorigtbl = 0, resorigcol = 0, resjunk = false}
    (gdb) p *(((TargetEntry *)(parse->targetList->head->data.ptr_value))->expr)
    $13 = {type = T_Const}
    ...
    #进入grouping_planner函数,此函数生成root->upper_rels & upper_targets
    #注意upper_rels,grouping_planner函数执行完毕,upper_rels最后一个元素会填入相应的值
    (gdb) p *root
    $22 = {type = T_PlannerInfo, parse = 0x219de98, glob = 0x21c2c20, query_level = 1, parent_root = 0x0, 
      plan_params = 0x0, outer_params = 0x0, simple_rel_array = 0x0, simple_rel_array_size = 0, 
      simple_rte_array = 0x0, all_baserels = 0x0, nullable_baserels = 0x0, join_rel_list = 0x0, 
      join_rel_hash = 0x0, join_rel_level = 0x0, join_cur_level = 0, init_plans = 0x0, cte_plan_ids = 0x0, 
      multiexpr_params = 0x0, eq_classes = 0x0, canon_pathkeys = 0x0, left_join_clauses = 0x0, 
      right_join_clauses = 0x0, full_join_clauses = 0x0, join_info_list = 0x0, append_rel_list = 0x0, 
      rowMarks = 0x0, placeholder_list = 0x0, fkey_list = 0x0, query_pathkeys = 0x0, group_pathkeys = 0x0, 
      window_pathkeys = 0x0, distinct_pathkeys = 0x0, sort_pathkeys = 0x0, part_schemes = 0x0, 
      initial_rels = 0x0, upper_rels = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, upper_targets = {0x0, 0x0, 0x0, 0x0, 
        0x0, 0x0, 0x0}, processed_tlist = 0x0, grouping_map = 0x0, minmax_aggs = 0x0, planner_cxt = 0x219cde0, 
      total_table_pages = 0, tuple_fraction = 0, limit_tuples = 0, qual_security_level = 0, 
      inhTargetKind = INHKIND_NONE, hasJoinRTEs = false, hasLateralRTEs = false, hasDeletedRTEs = false, 
      hasHavingQual = false, hasPseudoConstantQuals = false, hasRecursion = false, wt_param_id = -1, 
      non_recursive_path = 0x0, curOuterRels = 0x0, curOuterParams = 0x0, join_search_private = 0x0, 
      partColsUpdated = false}
    (gdb) p inheritance_update
    $23 = false
    (gdb) p inheritance_update
    $24 = false
    (gdb) p tuple_fraction
    $25 = 0
    (gdb) 
    ...
    (gdb) 
    1808      tlist = preprocess_targetlist(root);
    (gdb) p *root
    $27 = {type = T_PlannerInfo, parse = 0x219de98, glob = 0x21c2c20, query_level = 1, parent_root = 0x0, 
      plan_params = 0x0, outer_params = 0x0, simple_rel_array = 0x0, simple_rel_array_size = 0, 
      simple_rte_array = 0x0, all_baserels = 0x0, nullable_baserels = 0x0, join_rel_list = 0x0, 
      join_rel_hash = 0x0, join_rel_level = 0x0, join_cur_level = 0, init_plans = 0x0, cte_plan_ids = 0x0, 
      multiexpr_params = 0x0, eq_classes = 0x0, canon_pathkeys = 0x0, left_join_clauses = 0x0, 
      right_join_clauses = 0x0, full_join_clauses = 0x0, join_info_list = 0x0, append_rel_list = 0x0, 
      rowMarks = 0x0, placeholder_list = 0x0, fkey_list = 0x0, query_pathkeys = 0x0, group_pathkeys = 0x0, 
      window_pathkeys = 0x0, distinct_pathkeys = 0x0, sort_pathkeys = 0x0, part_schemes = 0x0, 
      initial_rels = 0x0, upper_rels = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, upper_targets = {0x0, 0x0, 0x0, 0x0, 
        0x0, 0x0, 0x0}, processed_tlist = 0x21c39e0, grouping_map = 0x0, minmax_aggs = 0x0, 
      planner_cxt = 0x219cde0, total_table_pages = 0, tuple_fraction = 0, limit_tuples = 0, 
      qual_security_level = 0, inhTargetKind = INHKIND_NONE, hasJoinRTEs = false, hasLateralRTEs = false, 
      hasDeletedRTEs = false, hasHavingQual = false, hasPseudoConstantQuals = false, hasRecursion = false, 
      wt_param_id = -1, non_recursive_path = 0x0, curOuterRels = 0x0, curOuterParams = 0x0, 
      join_search_private = 0x0, partColsUpdated = false}
    #processed_tlist中的元素为TargetEntry *,也就是字段Column
    (gdb) p *(root->processed_tlist)
    $28 = {type = T_List, length = 4, head = 0x21c39c0, tail = 0x21c3a50}
    (gdb) p *(root->processed_tlist->head)
    $29 = {data = {ptr_value = 0x21c30c0, int_value = 35401920, oid_value = 35401920}, next = 0x21c3a10}
    (gdb) p *(TargetEntry *)(root->processed_tlist->head.data->ptr_value)
    $30 = {xpr = {type = T_TargetEntry}, expr = 0x21c3110, resno = 1, resname = 0x219e338 "id", 
      ressortgroupref = 0, resorigtbl = 0, resorigcol = 0, resjunk = false}
    ...
    2026      root->upper_targets[UPPERREL_FINAL] = final_target;
    (gdb) 
    2027      root->upper_targets[UPPERREL_WINDOW] = sort_input_target;
    (gdb) 
    2028      root->upper_targets[UPPERREL_GROUP_AGG] = grouping_target;
    (gdb) 
    2035      if (have_grouping)
    (gdb) p *final_target
    $45 = {type = T_PathTarget, exprs = 0x21c3ee0, sortgrouprefs = 0x21c3ea0, cost = {startup = 0, 
    ...
    (gdb) 
    2197          create_modifytable_path(root, final_rel,
    (gdb) 
    2200                      parse->resultRelation,
    (gdb) p *root
    $49 = {type = T_PlannerInfo, parse = 0x219de98, glob = 0x21c2c20, query_level = 1, parent_root = 0x0, 
      plan_params = 0x0, outer_params = 0x0, simple_rel_array = 0x0, simple_rel_array_size = 0, 
      simple_rte_array = 0x0, all_baserels = 0x0, nullable_baserels = 0x0, join_rel_list = 0x21c3cf0, 
      join_rel_hash = 0x0, join_rel_level = 0x0, join_cur_level = 0, init_plans = 0x0, cte_plan_ids = 0x0, 
      multiexpr_params = 0x0, eq_classes = 0x0, canon_pathkeys = 0x0, left_join_clauses = 0x0, 
      right_join_clauses = 0x0, full_join_clauses = 0x0, join_info_list = 0x0, append_rel_list = 0x0, 
      rowMarks = 0x0, placeholder_list = 0x0, fkey_list = 0x0, query_pathkeys = 0x0, group_pathkeys = 0x0, 
      window_pathkeys = 0x0, distinct_pathkeys = 0x0, sort_pathkeys = 0x0, part_schemes = 0x0, 
      initial_rels = 0x0, upper_rels = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x21c4320}, upper_targets = {0x0, 0x0, 
        0x21c3e50, 0x21c3e50, 0x0, 0x0, 0x21c3e50}, processed_tlist = 0x21c39e0, grouping_map = 0x0, 
      minmax_aggs = 0x0, planner_cxt = 0x219cde0, total_table_pages = 0, tuple_fraction = 0, limit_tuples = -1, 
      qual_security_level = 0, inhTargetKind = INHKIND_NONE, hasJoinRTEs = false, hasLateralRTEs = false, 
      hasDeletedRTEs = false, hasHavingQual = false, hasPseudoConstantQuals = false, hasRecursion = false, 
      wt_param_id = -1, non_recursive_path = 0x0, curOuterRels = 0x0, curOuterParams = 0x0, 
      join_search_private = 0x0, partColsUpdated = false}
    (gdb) finish
    Run till exit from #0  grouping_planner (root=0x21c2cb0, inheritance_update=false, tuple_fraction=0)
        at planner.c:2200
    subquery_planner (glob=0x21c2c20, parse=0x219de98, parent_root=0x0, hasRecursion=false, tuple_fraction=0)
        at planner.c:972
    #退出grouping_planner函数
    ...
    #最终的返回值
    #INSERT VALUES语句相对比较简单,没有复杂的JOIN/WITH/HAVING/GROUP等语句,这里只是简单的返回一个root节点
    (gdb) p *root
    $17 = {type = T_PlannerInfo, parse = 0x219de98, glob = 0x21c2c20, query_level = 1, parent_root = 0x0, 
      plan_params = 0x0, outer_params = 0x0, simple_rel_array = 0x0, simple_rel_array_size = 0, 
      simple_rte_array = 0x0, all_baserels = 0x0, nullable_baserels = 0x0, join_rel_list = 0x21c3cf0, 
      join_rel_hash = 0x0, join_rel_level = 0x0, join_cur_level = 0, init_plans = 0x0, cte_plan_ids = 0x0, 
      multiexpr_params = 0x0, eq_classes = 0x0, canon_pathkeys = 0x0, left_join_clauses = 0x0, 
      right_join_clauses = 0x0, full_join_clauses = 0x0, join_info_list = 0x0, append_rel_list = 0x0, 
      rowMarks = 0x0, placeholder_list = 0x0, fkey_list = 0x0, query_pathkeys = 0x0, group_pathkeys = 0x0, 
      window_pathkeys = 0x0, distinct_pathkeys = 0x0, sort_pathkeys = 0x0, part_schemes = 0x0, 
      initial_rels = 0x0, upper_rels = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x21c4320}, upper_targets = {0x0, 0x0, 
        0x21c3e50, 0x21c3e50, 0x0, 0x0, 0x21c3e50}, processed_tlist = 0x21c39e0, grouping_map = 0x0, 
      minmax_aggs = 0x0, planner_cxt = 0x219cde0, total_table_pages = 0, tuple_fraction = 0, limit_tuples = -1, 
      qual_security_level = 0, inhTargetKind = INHKIND_NONE, hasJoinRTEs = false, hasLateralRTEs = false, 
      hasDeletedRTEs = false, hasHavingQual = false, hasPseudoConstantQuals = false, hasRecursion = false, 
      wt_param_id = -1, non_recursive_path = 0x0, curOuterRels = 0x0, curOuterParams = 0x0, 
      join_search_private = 0x0, partColsUpdated = false}
    

    四、小结

    1.重要的数据结构:PlannedStmt/PlannerGlobal/PlannerInfo/RelOptInfo/Path
    2.重要的函数:subquery_planner/grouping_planner/create_plan

    相关文章

      网友评论

        本文标题:PostgreSQL 源码解读(14)- Insert语句(如何

        本文链接:https://www.haomeiwen.com/subject/hrekbftx.html