#include "nodes/plannodes.h"
#include "nodes/relation.h"
Go to the source code of this file.
Typedefs | |
typedef PlannedStmt *(* | planner_hook_type )(Query *parse, int cursorOptions, ParamListInfo boundParams) |
Functions | |
PlannedStmt * | planner (Query *parse, int cursorOptions, ParamListInfo boundParams) |
PlannedStmt * | standard_planner (Query *parse, int cursorOptions, ParamListInfo boundParams) |
Plan * | subquery_planner (PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root, bool hasRecursion, double tuple_fraction, PlannerInfo **subroot) |
void | add_tlist_costs_to_plan (PlannerInfo *root, Plan *plan, List *tlist) |
bool | is_dummy_plan (Plan *plan) |
Expr * | expression_planner (Expr *expr) |
Expr * | preprocess_phv_expression (PlannerInfo *root, Expr *expr) |
bool | plan_cluster_use_sort (Oid tableOid, Oid indexOid) |
Variables | |
PGDLLIMPORT planner_hook_type | planner_hook |
typedef PlannedStmt*(* planner_hook_type)(Query *parse, int cursorOptions, ParamListInfo boundParams) |
void add_tlist_costs_to_plan | ( | PlannerInfo * | root, | |
Plan * | plan, | |||
List * | tlist | |||
) |
Definition at line 1811 of file planner.c.
References cost_qual_eval(), cpu_tuple_cost, QualCost::per_tuple, Plan::plan_rows, QualCost::startup, Plan::startup_cost, tlist_returns_set_rows(), and Plan::total_cost.
Referenced by grouping_planner(), make_agg(), make_group(), make_windowagg(), and optimize_minmax_aggregates().
{ QualCost tlist_cost; double tlist_rows; cost_qual_eval(&tlist_cost, tlist, root); plan->startup_cost += tlist_cost.startup; plan->total_cost += tlist_cost.startup + tlist_cost.per_tuple * plan->plan_rows; tlist_rows = tlist_returns_set_rows(tlist); if (tlist_rows > 1) { /* * We assume that execution costs of the tlist proper were all * accounted for by cost_qual_eval. However, it still seems * appropriate to charge something more for the executor's general * costs of processing the added tuples. The cost is probably less * than cpu_tuple_cost, though, so we arbitrarily use half of that. */ plan->total_cost += plan->plan_rows * (tlist_rows - 1) * cpu_tuple_cost / 2; plan->plan_rows *= tlist_rows; } }
Definition at line 3414 of file planner.c.
References eval_const_expressions(), fix_opfuncids(), and NULL.
Referenced by ATExecAddColumn(), ATPrepAlterColumnType(), BeginCopyFrom(), CheckMutability(), ExecPrepareExpr(), and GetDomainConstraints().
{ Node *result; /* * Convert named-argument function calls, insert default arguments and * simplify constant subexprs */ result = eval_const_expressions(NULL, (Node *) expr); /* Fill in opfuncid values if missing */ fix_opfuncids(result); return (Expr *) result; }
Definition at line 1848 of file planner.c.
References Const::constisnull, Const::constvalue, DatumGetBool, IsA, linitial, and list_length().
Referenced by create_append_plan(), inheritance_planner(), and set_subquery_pathlist().
{ if (IsA(plan, Result)) { List *rcqual = (List *) ((Result *) plan)->resconstantqual; if (list_length(rcqual) == 1) { Const *constqual = (Const *) linitial(rcqual); if (constqual && IsA(constqual, Const)) { if (!constqual->constisnull && !DatumGetBool(constqual->constvalue)) return true; } } } return false; }
Definition at line 3443 of file planner.c.
References build_simple_rel(), Query::commandType, cost_qual_eval(), cost_sort(), create_index_path(), create_seqscan_path(), CurrentMemoryContext, ForwardScanDirection, get_relation_data_width(), PlannerInfo::glob, RelOptInfo::indexlist, IndexOptInfo::indexoid, IndexOptInfo::indexprs, RangeTblEntry::inFromCl, RangeTblEntry::inh, RangeTblEntry::lateral, lfirst, list_make1, maintenance_work_mem, makeNode, NIL, NULL, RelOptInfo::pages, PlannerInfo::parse, IndexPath::path, QualCost::per_tuple, PlannerInfo::planner_cxt, PlannerInfo::query_level, RangeTblEntry::relid, RangeTblEntry::relkind, RELOPT_BASEREL, RelOptInfo::rows, Query::rtable, RangeTblEntry::rtekind, setup_simple_rel_arrays(), QualCost::startup, Path::total_cost, PlannerInfo::total_table_pages, RelOptInfo::tuples, RelOptInfo::width, and PlannerInfo::wt_param_id.
Referenced by copy_heap_data().
{ PlannerInfo *root; Query *query; PlannerGlobal *glob; RangeTblEntry *rte; RelOptInfo *rel; IndexOptInfo *indexInfo; QualCost indexExprCost; Cost comparisonCost; Path *seqScanPath; Path seqScanAndSortPath; IndexPath *indexScanPath; ListCell *lc; /* Set up mostly-dummy planner state */ query = makeNode(Query); query->commandType = CMD_SELECT; glob = makeNode(PlannerGlobal); root = makeNode(PlannerInfo); root->parse = query; root->glob = glob; root->query_level = 1; root->planner_cxt = CurrentMemoryContext; root->wt_param_id = -1; /* Build a minimal RTE for the rel */ rte = makeNode(RangeTblEntry); rte->rtekind = RTE_RELATION; rte->relid = tableOid; rte->relkind = RELKIND_RELATION; /* Don't be too picky. */ rte->lateral = false; rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); /* Build RelOptInfo */ rel = build_simple_rel(root, 1, RELOPT_BASEREL); /* Locate IndexOptInfo for the target index */ indexInfo = NULL; foreach(lc, rel->indexlist) { indexInfo = (IndexOptInfo *) lfirst(lc); if (indexInfo->indexoid == indexOid) break; } /* * It's possible that get_relation_info did not generate an IndexOptInfo * for the desired index; this could happen if it's not yet reached its * indcheckxmin usability horizon, or if it's a system index and we're * ignoring system indexes. In such cases we should tell CLUSTER to not * trust the index contents but use seqscan-and-sort. */ if (lc == NULL) /* not in the list? */ return true; /* use sort */ /* * Rather than doing all the pushups that would be needed to use * set_baserel_size_estimates, just do a quick hack for rows and width. */ rel->rows = rel->tuples; rel->width = get_relation_data_width(tableOid, NULL); root->total_table_pages = rel->pages; /* * Determine eval cost of the index expressions, if any. We need to * charge twice that amount for each tuple comparison that happens during * the sort, since tuplesort.c will have to re-evaluate the index * expressions each time. (XXX that's pretty inefficient...) */ cost_qual_eval(&indexExprCost, indexInfo->indexprs, root); comparisonCost = 2.0 * (indexExprCost.startup + indexExprCost.per_tuple); /* Estimate the cost of seq scan + sort */ seqScanPath = create_seqscan_path(root, rel, NULL); cost_sort(&seqScanAndSortPath, root, NIL, seqScanPath->total_cost, rel->tuples, rel->width, comparisonCost, maintenance_work_mem, -1.0); /* Estimate the cost of index scan */ indexScanPath = create_index_path(root, indexInfo, NIL, NIL, NIL, NIL, NIL, ForwardScanDirection, false, NULL, 1.0); return (seqScanAndSortPath.total_cost < indexScanPath->path.total_cost); }
PlannedStmt* planner | ( | Query * | parse, | |
int | cursorOptions, | |||
ParamListInfo | boundParams | |||
) |
Definition at line 131 of file planner.c.
References planner_hook, and standard_planner().
Referenced by BeginCopy(), and pg_plan_query().
{ PlannedStmt *result; if (planner_hook) result = (*planner_hook) (parse, cursorOptions, boundParams); else result = standard_planner(parse, cursorOptions, boundParams); return result; }
Expr* preprocess_phv_expression | ( | PlannerInfo * | root, | |
Expr * | expr | |||
) |
Definition at line 743 of file planner.c.
References EXPRKIND_PHV, and preprocess_expression().
Referenced by extract_lateral_references().
{ return (Expr *) preprocess_expression(root, (Node *) expr, EXPRKIND_PHV); }
PlannedStmt* standard_planner | ( | Query * | parse, | |
int | cursorOptions, | |||
ParamListInfo | boundParams | |||
) |
Definition at line 143 of file planner.c.
References Assert, PlannerGlobal::boundParams, Query::canSetTag, PlannedStmt::canSetTag, Query::commandType, PlannedStmt::commandType, CURSOR_OPT_FAST_PLAN, CURSOR_OPT_SCROLL, cursor_tuple_fraction, ExecSupportsBackwardScan(), PlannerGlobal::finalrowmarks, PlannerGlobal::finalrtable, forboth, Query::hasModifyingCTE, PlannedStmt::hasModifyingCTE, PlannedStmt::hasReturning, PlannedStmt::invalItems, PlannerGlobal::invalItems, IsA, PlannerGlobal::lastPHId, PlannerGlobal::lastRowMarkId, lfirst, list_length(), makeNode, materialize_finished_plan(), NIL, PlannedStmt::nParamExec, PlannerGlobal::nParamExec, NULL, PlannedStmt::planTree, Query::queryId, PlannedStmt::queryId, PlannedStmt::relationOids, PlannerGlobal::relationOids, PlannedStmt::resultRelations, PlannerGlobal::resultRelations, Query::returningList, PlannedStmt::rewindPlanIDs, PlannerGlobal::rewindPlanIDs, PlannedStmt::rowMarks, PlannedStmt::rtable, set_plan_references(), PlannedStmt::subplans, PlannerGlobal::subplans, subquery_planner(), PlannerGlobal::subroots, PlannedStmt::transientPlan, PlannerGlobal::transientPlan, PlannedStmt::utilityStmt, and Query::utilityStmt.
Referenced by planner().
{ PlannedStmt *result; PlannerGlobal *glob; double tuple_fraction; PlannerInfo *root; Plan *top_plan; ListCell *lp, *lr; /* Cursor options may come from caller or from DECLARE CURSOR stmt */ if (parse->utilityStmt && IsA(parse->utilityStmt, DeclareCursorStmt)) cursorOptions |= ((DeclareCursorStmt *) parse->utilityStmt)->options; /* * 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); glob->boundParams = boundParams; glob->subplans = NIL; glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->relationOids = NIL; glob->invalItems = NIL; glob->nParamExec = 0; glob->lastPHId = 0; glob->lastRowMarkId = 0; glob->transientPlan = false; /* 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; /* * 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) */ top_plan = subquery_planner(glob, parse, NULL, false, tuple_fraction, &root); /* * 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); } /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == 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 = (PlannerInfo *) lfirst(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->planTree = top_plan; result->rtable = glob->finalrtable; result->resultRelations = glob->resultRelations; result->utilityStmt = parse->utilityStmt; result->subplans = glob->subplans; result->rewindPlanIDs = glob->rewindPlanIDs; result->rowMarks = glob->finalrowmarks; result->relationOids = glob->relationOids; result->invalItems = glob->invalItems; result->nParamExec = glob->nParamExec; return result; }
Plan* subquery_planner | ( | PlannerGlobal * | glob, | |
Query * | parse, | |||
PlannerInfo * | parent_root, | |||
bool | hasRecursion, | |||
double | tuple_fraction, | |||
PlannerInfo ** | subroot | |||
) |
Definition at line 289 of file planner.c.
References PlannerInfo::append_rel_list, Query::canSetTag, CMD_SELECT, Query::commandType, contain_agg_clause(), contain_subplans(), contain_volatile_functions(), copyObject(), PlannerInfo::cte_plan_ids, Query::cteList, CurrentMemoryContext, WindowClause::endOffset, PlannerInfo::eq_classes, expand_inherited_tables(), EXPRKIND_LIMIT, EXPRKIND_QUAL, EXPRKIND_RTFUNC_LATERAL, EXPRKIND_VALUES_LATERAL, flatten_join_alias_vars(), flatten_simple_union_all(), RangeTblEntry::funcexpr, PlannerInfo::glob, Query::groupClause, grouping_planner(), PlannerInfo::hasHavingQual, PlannerInfo::hasInheritedTarget, PlannerInfo::hasJoinRTEs, PlannerInfo::hasLateralRTEs, PlannerInfo::hasPseudoConstantQuals, PlannerInfo::hasRecursion, Query::hasSubLinks, Query::havingQual, inheritance_planner(), PlannerInfo::init_plans, inline_set_returning_functions(), IS_OUTER_JOIN, Query::jointree, RangeTblEntry::jointype, lappend(), RangeTblEntry::lateral, lfirst, Query::limitCount, Query::limitOffset, list_length(), list_make1, list_make1_int, make_modifytable(), makeNode, PlannerInfo::non_recursive_plan, PlannerGlobal::nParamExec, PlannerInfo::parent_root, PlannerInfo::parse, PlannerInfo::plan_params, PlannerInfo::planner_cxt, preprocess_expression(), preprocess_qual_conditions(), preprocess_rowmarks(), pull_up_sublinks(), pull_up_subqueries(), FromExpr::quals, PlannerInfo::query_level, reduce_outer_joins(), Query::resultRelation, Query::returningList, Query::rowMarks, PlannerInfo::rowMarks, rt_fetch, Query::rtable, RTE_FUNCTION, RTE_JOIN, RTE_SUBQUERY, RTE_VALUES, RangeTblEntry::rtekind, Query::setOperations, SS_assign_special_param(), SS_finalize_plan(), SS_process_ctes(), WindowClause::startOffset, PlannerGlobal::subplans, RangeTblEntry::subquery, Query::targetList, RangeTblEntry::values_lists, Query::windowClause, and PlannerInfo::wt_param_id.
Referenced by make_subplan(), recurse_set_operations(), set_subquery_pathlist(), SS_process_ctes(), and standard_planner().
{ int num_old_subplans = list_length(glob->subplans); PlannerInfo *root; Plan *plan; List *newHaving; bool hasOuterJoins; 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->planner_cxt = CurrentMemoryContext; root->init_plans = NIL; root->cte_plan_ids = NIL; root->eq_classes = NIL; root->append_rel_list = NIL; root->rowMarks = NIL; root->hasInheritedTarget = false; root->hasRecursion = hasRecursion; if (hasRecursion) root->wt_param_id = SS_assign_special_param(root); else root->wt_param_id = -1; root->non_recursive_plan = NULL; /* * 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); /* * 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); /* * 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. */ parse->jointree = (FromExpr *) pull_up_subqueries(root, (Node *) parse->jointree); /* * 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 = (RangeTblEntry *) lfirst(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); 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 = (WindowClause *) lfirst(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); 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 = (RangeTblEntry *) lfirst(l); int kind; 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 fully */ kind = rte->lateral ? EXPRKIND_RTFUNC_LATERAL : EXPRKIND_RTFUNC; rte->funcexpr = preprocess_expression(root, rte->funcexpr, 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); } } /* * 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). 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 (contain_agg_clause(havingclause) || contain_volatile_functions(havingclause) || contain_subplans(havingclause)) { /* keep it in HAVING */ newHaving = lappend(newHaving, havingclause); } else if (parse->groupClause) { /* 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; /* * 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) plan = inheritance_planner(root); else { plan = grouping_planner(root, tuple_fraction); /* If it's not SELECT, we need a ModifyTable node */ if (parse->commandType != CMD_SELECT) { List *returningLists; List *rowMarks; /* * Set up the RETURNING list-of-lists, if needed. */ 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; plan = (Plan *) make_modifytable(root, parse->commandType, parse->canSetTag, list_make1_int(parse->resultRelation), list_make1(plan), returningLists, rowMarks, SS_assign_special_param(root)); } } /* * If any subplans were generated, or if there are any parameters to worry * about, build initPlan list and extParam/allParam sets for plan nodes, * and attach the initPlans to the top plan node. */ if (list_length(glob->subplans) != num_old_subplans || root->glob->nParamExec > 0) SS_finalize_plan(root, plan, true); /* Return internal info if caller wants it */ if (subroot) *subroot = root; return plan; }
PGDLLIMPORT planner_hook_type planner_hook |