PostgreSQL中Insert语句如何使用,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。
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
看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。