共计 118045 个字符,预计需要花费 296 分钟才能阅读完成。
自动写代码机器人,免费开通
PostgreSQL 中 Insert 语句如何使用,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面丸趣 TV 小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。
一、源码解读
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
看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注丸趣 TV 行业资讯频道,感谢您对丸趣 TV 的支持。
向 AI 问一下细节