PostgreSQL中Insert语句如何使用
发表于:2024-11-23 作者:千家信息网编辑
千家信息网最后更新 2024年11月23日,PostgreSQL中Insert语句如何使用,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。一、源码解读standard
千家信息网最后更新 2024年11月23日PostgreSQL中Insert语句如何使用
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 (), ( ), ... */ 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 1610GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-100.el7Copyright (C) 2013 Free Software Foundation, Inc....#跟踪进入subquery_planner(见后)(gdb) n409 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) stepcreate_plan (root=0x21c2cb0, best_path=0x219dd88) at createplan.c:323323 root->curOuterRels = NULL;(gdb) n324 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 1610GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-100.el7Copyright (C) 2013 Free Software Foundation, Inc....(gdb) b subquery_plannerBreakpoint 1 at 0x76a0bb: file planner.c, line 606.(gdb) cContinuing....Breakpoint 1, subquery_planner (glob=0x21c2c20, parse=0x219de98, parent_root=0x0, hasRecursion=false, tuple_fraction=0) at planner.c:606606 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_rootCannot 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) finishRun till exit from #0 grouping_planner (root=0x21c2cb0, inheritance_update=false, tuple_fraction=0) at planner.c:2200subquery_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
看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注行业资讯频道,感谢您对的支持。
函数
语句
数据
信息
结构
重要
元素
参数
数据结构
跟踪
变量
指针
数据表
节点
路径
帮助
生成
输入
复杂
清楚
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
天王殿小说软件开发
维护网络安全处理办法
快板网络安全三句半
浙江现代软件开发报价表
上海服务管理软件开发公司
软件开发集成及安装税率
学校服务器维修视频教程
ps数据库文件怎么打开
下载开源数据库安全性
企业业务系统数据库例子
魔兽世界pvp服务器在哪
工厂5g网络 网络安全
移动网络技术支持有哪些
华科数据库课设
网络安全专家刘杰
软件开发人员工作汇报表
12306数据库设计
富士服务器报警代码OL
汉绣数据库
宠物大战服务器大全
浪潮服务器插电脑显示硬盘拔下来
计算机网络技术月报告
我的世界如何在服务器里面搞32k
李沧区游戏软件开发外包公司
北京惠普服务器维修点云服务器
数据库视图中文乱码
财务人员谈网络安全
深圳交警网络安全管理制度
家庭组建局域网用什么服务器
考研网络安全的专业