X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Foptimizer%2Fplan%2Fcreateplan.c;h=066685c3c7b44b2c5f21bc53ae88621ab689b728;hb=97c39498e5ca9208d3de5a443a2282923619bf91;hp=3024ff9f7880525f436fdf7397bd4196e31281dd;hpb=8c314b9853c2fbb85c041d4761426f25a9d63972;p=postgresql diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 3024ff9f78..066685c3c7 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -5,7 +5,7 @@ * Planning is complete, we just need to convert the selected * Path into a Plan. * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -24,6 +24,7 @@ #include "catalog/pg_class.h" #include "foreign/fdwapi.h" #include "miscadmin.h" +#include "nodes/extensible.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" @@ -34,13 +35,13 @@ #include "optimizer/planmain.h" #include "optimizer/planner.h" #include "optimizer/predtest.h" -#include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "optimizer/subselect.h" #include "optimizer/tlist.h" #include "optimizer/var.h" #include "parser/parse_clause.h" #include "parser/parsetree.h" +#include "partitioning/partprune.h" #include "utils/lsyscache.h" @@ -62,10 +63,14 @@ * any sortgrouprefs specified in its pathtarget, with appropriate * ressortgroupref labels. This is passed down by parent nodes such as Sort * and Group, which need these values to be available in their inputs. + * + * CP_IGNORE_TLIST specifies that the caller plans to replace the targetlist, + * and therefore it doesn't matter a bit what target list gets generated. */ -#define CP_EXACT_TLIST 0x0001 /* Plan must return specified tlist */ -#define CP_SMALL_TLIST 0x0002 /* Prefer narrower tlists */ -#define CP_LABEL_TLIST 0x0004 /* tlist must contain sortgrouprefs */ +#define CP_EXACT_TLIST 0x0001 /* Plan must return specified tlist */ +#define CP_SMALL_TLIST 0x0002 /* Prefer narrower tlists */ +#define CP_LABEL_TLIST 0x0004 /* tlist must contain sortgrouprefs */ +#define CP_IGNORE_TLIST 0x0008 /* caller will replace tlist */ static Plan *create_plan_recurse(PlannerInfo *root, Path *best_path, @@ -81,13 +86,16 @@ static Plan *create_join_plan(PlannerInfo *root, JoinPath *best_path); static Plan *create_append_plan(PlannerInfo *root, AppendPath *best_path); static Plan *create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path); static Result *create_result_plan(PlannerInfo *root, ResultPath *best_path); +static ProjectSet *create_project_set_plan(PlannerInfo *root, ProjectSetPath *best_path); static Material *create_material_plan(PlannerInfo *root, MaterialPath *best_path, int flags); static Plan *create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags); static Gather *create_gather_plan(PlannerInfo *root, GatherPath *best_path); -static Plan *create_projection_plan(PlannerInfo *root, ProjectionPath *best_path); -static Plan *inject_projection_plan(Plan *subplan, List *tlist); +static Plan *create_projection_plan(PlannerInfo *root, + ProjectionPath *best_path, + int flags); +static Plan *inject_projection_plan(Plan *subplan, List *tlist, bool parallel_safe); static Sort *create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags); static Group *create_group_plan(PlannerInfo *root, GroupPath *best_path); static Unique *create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, @@ -99,15 +107,6 @@ static WindowAgg *create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_p static SetOp *create_setop_plan(PlannerInfo *root, SetOpPath *best_path, int flags); static RecursiveUnion *create_recursiveunion_plan(PlannerInfo *root, RecursiveUnionPath *best_path); -static void get_column_info_for_window(PlannerInfo *root, WindowClause *wc, - List *tlist, - int numSortCols, AttrNumber *sortColIdx, - int *partNumCols, - AttrNumber **partColIdx, - Oid **partOperators, - int *ordNumCols, - AttrNumber **ordColIdx, - Oid **ordOperators); static LockRows *create_lockrows_plan(PlannerInfo *root, LockRowsPath *best_path, int flags); static ModifyTable *create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path); @@ -124,6 +123,7 @@ static BitmapHeapScan *create_bitmap_scan_plan(PlannerInfo *root, List *tlist, List *scan_clauses); static Plan *create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, List **qual, List **indexqual, List **indexECs); +static void bitmap_subplan_mark_shared(Plan *plan); static TidScan *create_tidscan_plan(PlannerInfo *root, TidPath *best_path, List *tlist, List *scan_clauses); static SubqueryScan *create_subqueryscan_plan(PlannerInfo *root, @@ -133,8 +133,12 @@ static FunctionScan *create_functionscan_plan(PlannerInfo *root, Path *best_path List *tlist, List *scan_clauses); static ValuesScan *create_valuesscan_plan(PlannerInfo *root, Path *best_path, List *tlist, List *scan_clauses); +static TableFuncScan *create_tablefuncscan_plan(PlannerInfo *root, Path *best_path, + List *tlist, List *scan_clauses); static CteScan *create_ctescan_plan(PlannerInfo *root, Path *best_path, List *tlist, List *scan_clauses); +static NamedTuplestoreScan *create_namedtuplestorescan_plan(PlannerInfo *root, + Path *best_path, List *tlist, List *scan_clauses); static WorkTableScan *create_worktablescan_plan(PlannerInfo *root, Path *best_path, List *tlist, List *scan_clauses); static ForeignScan *create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, @@ -189,11 +193,16 @@ static FunctionScan *make_functionscan(List *qptlist, List *qpqual, Index scanrelid, List *functions, bool funcordinality); static ValuesScan *make_valuesscan(List *qptlist, List *qpqual, Index scanrelid, List *values_lists); +static TableFuncScan *make_tablefuncscan(List *qptlist, List *qpqual, + Index scanrelid, TableFunc *tablefunc); static CteScan *make_ctescan(List *qptlist, List *qpqual, Index scanrelid, int ctePlanId, int cteParam); +static NamedTuplestoreScan *make_namedtuplestorescan(List *qptlist, List *qpqual, + Index scanrelid, char *enrname); static WorkTableScan *make_worktablescan(List *qptlist, List *qpqual, Index scanrelid, int wtParam); -static Append *make_append(List *appendplans, List *tlist); +static Append *make_append(List *appendplans, int first_partial_plan, + List *tlist, PartitionPruneInfo *partpruneinfo); static RecursiveUnion *make_recursive_union(List *tlist, Plan *lefttree, Plan *righttree, @@ -205,18 +214,16 @@ static BitmapOr *make_bitmap_or(List *bitmapplans); static NestLoop *make_nestloop(List *tlist, List *joinclauses, List *otherclauses, List *nestParams, Plan *lefttree, Plan *righttree, - JoinType jointype); + JoinType jointype, bool inner_unique); static HashJoin *make_hashjoin(List *tlist, List *joinclauses, List *otherclauses, List *hashclauses, Plan *lefttree, Plan *righttree, - JoinType jointype); + JoinType jointype, bool inner_unique); static Hash *make_hash(Plan *lefttree, Oid skewTable, AttrNumber skewColumn, - bool skewInherit, - Oid skewColType, - int32 skewColTypmod); + bool skewInherit); static MergeJoin *make_mergejoin(List *tlist, List *joinclauses, List *otherclauses, List *mergeclauses, @@ -225,7 +232,8 @@ static MergeJoin *make_mergejoin(List *tlist, int *mergestrategies, bool *mergenullsfirst, Plan *lefttree, Plan *righttree, - JoinType jointype); + JoinType jointype, bool inner_unique, + bool skip_mark_restore); static Sort *make_sort(Plan *lefttree, int numCols, AttrNumber *sortColIdx, Oid *sortOperators, Oid *collations, bool *nullsFirst); @@ -241,21 +249,18 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys, static EquivalenceMember *find_ec_member_for_tle(EquivalenceClass *ec, TargetEntry *tle, Relids relids); -static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys); -static Sort *make_sort_from_sortclauses(List *sortcls, Plan *lefttree); +static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, + Relids relids); static Sort *make_sort_from_groupcols(List *groupcls, AttrNumber *grpColIdx, Plan *lefttree); static Material *make_material(Plan *lefttree); -static Agg *make_agg(List *tlist, List *qual, AggStrategy aggstrategy, - bool combineStates, bool finalizeAggs, - int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators, - List *groupingSets, List *chain, - double dNumGroups, Plan *lefttree); static WindowAgg *make_windowagg(List *tlist, Index winref, int partNumCols, AttrNumber *partColIdx, Oid *partOperators, int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators, int frameOptions, Node *startOffset, Node *endOffset, + Oid startInRangeFunc, Oid endInRangeFunc, + Oid inRangeColl, bool inRangeAsc, bool inRangeNullsFirst, Plan *lefttree); static Group *make_group(List *tlist, List *qual, int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators, @@ -264,19 +269,22 @@ static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList); static Unique *make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols); static Gather *make_gather(List *qptlist, List *qpqual, - int nworkers, bool single_copy, Plan *subplan); + int nworkers, int rescan_param, bool single_copy, Plan *subplan); static SetOp *make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree, List *distinctList, AttrNumber flagColIdx, int firstFlag, long numGroups); static LockRows *make_lockrows(Plan *lefttree, List *rowMarks, int epqParam); -static Limit *make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount); static Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan); +static ProjectSet *make_project_set(List *tlist, Plan *subplan); static ModifyTable *make_modifytable(PlannerInfo *root, CmdType operation, bool canSetTag, - Index nominalRelation, - List *resultRelations, List *subplans, + Index nominalRelation, Index rootRelation, + bool partColsUpdated, + List *resultRelations, List *subplans, List *subroots, List *withCheckOptionLists, List *returningLists, List *rowMarks, OnConflictExpr *onconflict, int epqParam); +static GatherMerge *create_gather_merge_plan(PlannerInfo *root, + GatherMergePath *best_path); /* @@ -321,16 +329,13 @@ create_plan(PlannerInfo *root, Path *best_path) /* * Attach any initPlans created in this query level to the topmost plan - * node. (The initPlans could actually 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.) + * 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); - /* Update parallel safety information if needed. */ - if (!best_path->parallel_safe) - root->glob->wholePlanParallelSafe = false; - /* Check we successfully assigned all NestLoopParams to plan nodes */ if (root->curOuterParams != NIL) elog(ERROR, "failed to assign all NestLoopParams to plan nodes"); @@ -353,6 +358,9 @@ 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: @@ -363,9 +371,11 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags) 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); @@ -388,12 +398,13 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags) if (IsA(best_path, ProjectionPath)) { plan = create_projection_plan(root, - (ProjectionPath *) best_path); + (ProjectionPath *) best_path, + flags); } else if (IsA(best_path, MinMaxAggPath)) { plan = (Plan *) create_minmaxagg_plan(root, - (MinMaxAggPath *) best_path); + (MinMaxAggPath *) best_path); } else { @@ -402,6 +413,10 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags) (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, @@ -411,7 +426,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags) if (IsA(best_path, UpperUniquePath)) { plan = (Plan *) create_upper_unique_plan(root, - (UpperUniquePath *) best_path, + (UpperUniquePath *) best_path, flags); } else @@ -438,7 +453,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags) case T_Agg: if (IsA(best_path, GroupingSetsPath)) plan = create_groupingsets_plan(root, - (GroupingSetsPath *) best_path); + (GroupingSetsPath *) best_path); else { Assert(IsA(best_path, AggPath)); @@ -448,7 +463,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags) break; case T_WindowAgg: plan = (Plan *) create_windowagg_plan(root, - (WindowAggPath *) best_path); + (WindowAggPath *) best_path); break; case T_SetOp: plan = (Plan *) create_setop_plan(root, @@ -457,7 +472,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags) break; case T_RecursiveUnion: plan = (Plan *) create_recursiveunion_plan(root, - (RecursiveUnionPath *) best_path); + (RecursiveUnionPath *) best_path); break; case T_LockRows: plan = (Plan *) create_lockrows_plan(root, @@ -466,13 +481,17 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags) break; case T_ModifyTable: plan = (Plan *) create_modifytable_plan(root, - (ModifyTablePath *) best_path); + (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); @@ -500,8 +519,24 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags) * Extract the relevant restriction clauses from the parent relation. The * executor must apply all these restrictions during the scan, except for * pseudoconstants which we'll take care of below. + * + * If this is a plain indexscan or index-only scan, we need not consider + * restriction clauses that are implied by the index's predicate, so use + * indrestrictinfo not baserestrictinfo. Note that we can't do that for + * bitmap indexscans, since there's not necessarily a single index + * involved; but it doesn't matter since create_bitmap_scan_plan() will be + * able to get rid of such clauses anyway via predicate proof. */ - scan_clauses = rel->baserestrictinfo; + switch (best_path->pathtype) + { + case T_IndexScan: + case T_IndexOnlyScan: + scan_clauses = castNode(IndexPath, best_path)->indexinfo->indrestrictinfo; + break; + default: + scan_clauses = rel->baserestrictinfo; + break; + } /* * If this is a parameterized scan, we also need to enforce all the join @@ -527,15 +562,28 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags) * only those Vars actually needed by the query), we prefer to generate a * tlist containing all Vars in order. This will allow the executor to * optimize away projection of the table tuples, if possible. + * + * But if the caller is going to ignore our tlist anyway, then don't + * bother generating one at all. We use an exact equality test here, so + * that this only applies when CP_IGNORE_TLIST is the only flag set. */ - if (use_physical_tlist(root, best_path, flags)) + if (flags == CP_IGNORE_TLIST) + { + tlist = NULL; + } + else if (use_physical_tlist(root, best_path, flags)) { if (best_path->pathtype == T_IndexOnlyScan) { /* For index-only scan, the preferred tlist is the index's */ tlist = copyObject(((IndexPath *) best_path)->indexinfo->indextlist); - /* Transfer any sortgroupref data to the replacement tlist */ - apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget); + + /* + * Transfer sortgroupref data to the replacement tlist, if + * requested (use_physical_tlist checked that this will work). + */ + if (flags & CP_LABEL_TLIST) + apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget); } else { @@ -547,8 +595,9 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags) } else { - /* Transfer any sortgroupref data to the replacement tlist */ - apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget); + /* As above, transfer sortgroupref data to replacement tlist */ + if (flags & CP_LABEL_TLIST) + apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget); } } } @@ -591,7 +640,7 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags) case T_BitmapHeapScan: plan = (Plan *) create_bitmap_scan_plan(root, - (BitmapHeapPath *) best_path, + (BitmapHeapPath *) best_path, tlist, scan_clauses); break; @@ -605,7 +654,7 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags) case T_SubqueryScan: plan = (Plan *) create_subqueryscan_plan(root, - (SubqueryScanPath *) best_path, + (SubqueryScanPath *) best_path, tlist, scan_clauses); break; @@ -617,6 +666,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags) scan_clauses); break; + case T_TableFuncScan: + plan = (Plan *) create_tablefuncscan_plan(root, + best_path, + tlist, + scan_clauses); + break; + case T_ValuesScan: plan = (Plan *) create_valuesscan_plan(root, best_path, @@ -631,6 +687,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags) scan_clauses); break; + case T_NamedTuplestoreScan: + plan = (Plan *) create_namedtuplestorescan_plan(root, + best_path, + tlist, + scan_clauses); + break; + case T_WorkTableScan: plan = (Plan *) create_worktablescan_plan(root, best_path, @@ -731,11 +794,12 @@ use_physical_tlist(PlannerInfo *root, Path *path, int flags) /* * We can do this for real relation scans, subquery scans, function scans, - * values scans, and CTE scans (but not for, eg, joins). + * tablefunc scans, values scans, and CTE scans (but not for, eg, joins). */ if (rel->rtekind != RTE_RELATION && rel->rtekind != RTE_SUBQUERY && rel->rtekind != RTE_FUNCTION && + rel->rtekind != RTE_TABLEFUNC && rel->rtekind != RTE_VALUES && rel->rtekind != RTE_CTE) return false; @@ -748,6 +812,24 @@ use_physical_tlist(PlannerInfo *root, Path *path, int flags) if (rel->reloptkind != RELOPT_BASEREL) return false; + /* + * Also, don't do it to a CustomPath; the premise that we're extracting + * columns from a simple physical tuple is unlikely to hold for those. + * (When it does make sense, the custom path creator can set up the path's + * pathtarget that way.) + */ + if (IsA(path, CustomPath)) + return false; + + /* + * If a bitmap scan's tlist is empty, keep it as-is. This may allow the + * executor to skip heap page fetches, and in any case, the benefit of + * using a physical tlist instead would be minimal. + */ + if (IsA(path, BitmapHeapPath) && + path->pathtarget->exprs == NIL) + return false; + /* * Can't do it if any system columns or whole-row Vars are requested. * (This could possibly be fixed but would take some fragile assumptions @@ -777,10 +859,14 @@ use_physical_tlist(PlannerInfo *root, Path *path, int flags) * to emit any sort/group columns that are not simple Vars. (If they are * simple Vars, they should appear in the physical tlist, and * apply_pathtarget_labeling_to_tlist will take care of getting them - * labeled again.) + * labeled again.) We also have to check that no two sort/group columns + * are the same Var, else that element of the physical tlist would need + * conflicting ressortgroupref labels. */ if ((flags & CP_LABEL_TLIST) && path->pathtarget->sortgrouprefs) { + Bitmapset *sortgroupatts = NULL; + i = 0; foreach(lc, path->pathtarget->exprs) { @@ -789,7 +875,14 @@ use_physical_tlist(PlannerInfo *root, Path *path, int flags) if (path->pathtarget->sortgrouprefs[i]) { if (expr && IsA(expr, Var)) - /* okay */ ; + { + int attno = ((Var *) expr)->varattno; + + attno -= FirstLowInvalidHeapAttributeNumber; + if (bms_is_member(attno, sortgroupatts)) + return false; + sortgroupatts = bms_add_member(sortgroupatts, attno); + } else return false; } @@ -858,6 +951,9 @@ create_gating_plan(PlannerInfo *root, Path *path, Plan *plan, */ copy_plan_costsize(gplan, plan); + /* Gating quals could be unsafe, so better use the Path's safety flag */ + gplan->parallel_safe = path->parallel_safe; + return gplan; } @@ -913,7 +1009,7 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path) if (get_loc_restrictinfo(best_path) != NIL) set_qpqual((Plan) plan, list_concat(get_qpqual((Plan) plan), - get_actual_clauses(get_loc_restrictinfo(best_path)))); + get_actual_clauses(get_loc_restrictinfo(best_path)))); #endif return plan; @@ -933,6 +1029,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path) List *tlist = build_path_tlist(root, &best_path->path); List *subplans = NIL; ListCell *subpaths; + RelOptInfo *rel = best_path->path.parent; + PartitionPruneInfo *partpruneinfo = NULL; /* * The subpaths list could be empty, if every child was proven empty by @@ -970,6 +1068,38 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path) subplans = lappend(subplans, subplan); } + /* + * If any quals exist, they may be useful to perform further partition + * pruning during execution. Gather information needed by the executor to + * do partition pruning. + */ + if (enable_partition_pruning && + rel->reloptkind == RELOPT_BASEREL && + best_path->partitioned_rels != NIL) + { + List *prunequal; + + prunequal = extract_actual_clauses(rel->baserestrictinfo, false); + + if (best_path->path.param_info) + { + List *prmquals = best_path->path.param_info->ppi_clauses; + + prmquals = extract_actual_clauses(prmquals, false); + prmquals = (List *) replace_nestloop_params(root, + (Node *) prmquals); + + prunequal = list_concat(prunequal, prmquals); + } + + if (prunequal != NIL) + partpruneinfo = + make_partition_pruneinfo(root, rel, + best_path->subpaths, + best_path->partitioned_rels, + prunequal); + } + /* * XXX ideally, if there's just one child, we'd not bother to generate an * Append node but just return the single child. At the moment this does @@ -977,7 +1107,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path) * parent-rel Vars it'll be asked to emit. */ - plan = make_append(subplans, tlist); + plan = make_append(subplans, best_path->first_partial_path, + tlist, partpruneinfo); copy_generic_path_info(&plan->plan, (Path *) best_path); @@ -1000,6 +1131,8 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path) List *pathkeys = best_path->path.pathkeys; List *subplans = NIL; ListCell *subpaths; + RelOptInfo *rel = best_path->path.parent; + PartitionPruneInfo *partpruneinfo = NULL; /* * We don't have the actual creation of the MergeAppend node split out @@ -1085,7 +1218,40 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path) subplans = lappend(subplans, subplan); } + /* + * If any quals exist, they may be useful to perform further partition + * pruning during execution. Gather information needed by the executor to + * do partition pruning. + */ + if (enable_partition_pruning && + rel->reloptkind == RELOPT_BASEREL && + best_path->partitioned_rels != NIL) + { + List *prunequal; + + prunequal = extract_actual_clauses(rel->baserestrictinfo, false); + + if (best_path->path.param_info) + { + + List *prmquals = best_path->path.param_info->ppi_clauses; + + prmquals = extract_actual_clauses(prmquals, false); + prmquals = (List *) replace_nestloop_params(root, + (Node *) prmquals); + + prunequal = list_concat(prunequal, prmquals); + } + + if (prunequal != NIL) + partpruneinfo = make_partition_pruneinfo(root, rel, + best_path->subpaths, + best_path->partitioned_rels, + prunequal); + } + node->mergeplans = subplans; + node->part_prune_info = partpruneinfo; return (Plan *) node; } @@ -1117,6 +1283,31 @@ create_result_plan(PlannerInfo *root, ResultPath *best_path) return plan; } +/* + * create_project_set_plan + * Create a ProjectSet plan for 'best_path'. + * + * Returns a Plan node. + */ +static ProjectSet * +create_project_set_plan(PlannerInfo *root, ProjectSetPath *best_path) +{ + ProjectSet *plan; + Plan *subplan; + List *tlist; + + /* Since we intend to project, we don't need to constrain child tlist */ + subplan = create_plan_recurse(root, best_path->subpath, 0); + + tlist = build_path_tlist(root, &best_path->path); + + plan = make_project_set(tlist, subplan); + + copy_generic_path_info(&plan->plan, (Path *) best_path); + + return plan; +} + /* * create_material_plan * Create a Material plan for 'best_path' and (recursively) plans @@ -1200,7 +1391,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags) foreach(l, uniq_exprs) { - Node *uniqexpr = lfirst(l); + Expr *uniqexpr = lfirst(l); TargetEntry *tle; tle = tlist_member(uniqexpr, newtlist); @@ -1216,19 +1407,10 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags) } } + /* Use change_plan_targetlist in case we need to insert a Result node */ if (newitems || best_path->umethod == UNIQUE_PATH_SORT) - { - /* - * If the top plan node can't do projections and its existing target - * list isn't already what we need, we need to add a Result node to - * help it along. - */ - if (!is_projection_capable_plan(subplan) && - !tlist_same_exprs(newtlist, subplan->targetlist)) - subplan = inject_projection_plan(subplan, newtlist); - else - subplan->targetlist = newtlist; - } + subplan = change_plan_targetlist(subplan, newtlist, + best_path->path.parallel_safe); /* * Build control information showing which subplan output columns are to @@ -1243,7 +1425,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags) groupColPos = 0; foreach(l, uniq_exprs) { - Node *uniqexpr = lfirst(l); + Expr *uniqexpr = lfirst(l); TargetEntry *tle; tle = tlist_member(uniqexpr, newtlist); @@ -1283,8 +1465,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags) plan = (Plan *) make_agg(build_path_tlist(root, &best_path->path), NIL, AGG_HASHED, - false, - true, + AGGSPLIT_SIMPLE, numGroupCols, groupColIdx, groupOperators, @@ -1320,7 +1501,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags) * for the IN clause operators' RHS datatype. */ eqop = get_equality_op_for_ordering_op(sortop, NULL); - if (!OidIsValid(eqop)) /* shouldn't happen */ + if (!OidIsValid(eqop)) /* shouldn't happen */ elog(ERROR, "could not find equality operator for ordering operator %u", sortop); @@ -1360,13 +1541,20 @@ create_gather_plan(PlannerInfo *root, GatherPath *best_path) { Gather *gather_plan; Plan *subplan; + List *tlist; - /* Must insist that all children return the same tlist */ + /* + * Although the Gather node can project, we prefer to push down such work + * to its child node, so demand an exact tlist from the child. + */ subplan = create_plan_recurse(root, best_path->subpath, CP_EXACT_TLIST); - gather_plan = make_gather(subplan->targetlist, + tlist = build_path_tlist(root, &best_path->path); + + gather_plan = make_gather(tlist, NIL, - best_path->path.parallel_degree, + best_path->num_workers, + SS_assign_special_param(root), best_path->single_copy, subplan); @@ -1378,48 +1566,153 @@ create_gather_plan(PlannerInfo *root, GatherPath *best_path) return gather_plan; } +/* + * create_gather_merge_plan + * + * Create a Gather Merge plan for 'best_path' and (recursively) + * plans for its subpaths. + */ +static GatherMerge * +create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path) +{ + GatherMerge *gm_plan; + Plan *subplan; + List *pathkeys = best_path->path.pathkeys; + List *tlist = build_path_tlist(root, &best_path->path); + + /* As with Gather, it's best to project away columns in the workers. */ + subplan = create_plan_recurse(root, best_path->subpath, CP_EXACT_TLIST); + + /* Create a shell for a GatherMerge plan. */ + gm_plan = makeNode(GatherMerge); + gm_plan->plan.targetlist = tlist; + gm_plan->num_workers = best_path->num_workers; + copy_generic_path_info(&gm_plan->plan, &best_path->path); + + /* Assign the rescan Param. */ + gm_plan->rescan_param = SS_assign_special_param(root); + + /* Gather Merge is pointless with no pathkeys; use Gather instead. */ + Assert(pathkeys != NIL); + + /* Compute sort column info, and adjust subplan's tlist as needed */ + subplan = prepare_sort_from_pathkeys(subplan, pathkeys, + best_path->subpath->parent->relids, + gm_plan->sortColIdx, + false, + &gm_plan->numCols, + &gm_plan->sortColIdx, + &gm_plan->sortOperators, + &gm_plan->collations, + &gm_plan->nullsFirst); + + + /* Now, insert a Sort node if subplan isn't sufficiently ordered */ + if (!pathkeys_contained_in(pathkeys, best_path->subpath->pathkeys)) + subplan = (Plan *) make_sort(subplan, gm_plan->numCols, + gm_plan->sortColIdx, + gm_plan->sortOperators, + gm_plan->collations, + gm_plan->nullsFirst); + + /* Now insert the subplan under GatherMerge. */ + gm_plan->plan.lefttree = subplan; + + /* use parallel mode for parallel plans. */ + root->glob->parallelModeNeeded = true; + + return gm_plan; +} + /* * create_projection_plan * - * Create a Result node to do a projection step and (recursively) plans - * for its subpaths. + * Create a plan tree to do a projection step and (recursively) plans + * for its subpaths. We may need a Result node for the projection, + * but sometimes we can just let the subplan do the work. */ static Plan * -create_projection_plan(PlannerInfo *root, ProjectionPath *best_path) +create_projection_plan(PlannerInfo *root, ProjectionPath *best_path, int flags) { Plan *plan; Plan *subplan; List *tlist; + bool needs_result_node = false; - /* Since we intend to project, we don't need to constrain child tlist */ - subplan = create_plan_recurse(root, best_path->subpath, 0); - - tlist = build_path_tlist(root, &best_path->path); + /* + * Convert our subpath to a Plan and determine whether we need a Result + * node. + * + * In most cases where we don't need to project, creation_projection_path + * will have set dummypp, but not always. First, some createplan.c + * routines change the tlists of their nodes. (An example is that + * create_merge_append_plan might add resjunk sort columns to a + * MergeAppend.) Second, create_projection_path has no way of knowing + * what path node will be placed on top of the projection path and + * therefore can't predict whether it will require an exact tlist. For + * both of these reasons, we have to recheck here. + */ + if (use_physical_tlist(root, &best_path->path, flags)) + { + /* + * Our caller doesn't really care what tlist we return, so we don't + * actually need to project. However, we may still need to ensure + * proper sortgroupref labels, if the caller cares about those. + */ + subplan = create_plan_recurse(root, best_path->subpath, 0); + tlist = subplan->targetlist; + if (flags & CP_LABEL_TLIST) + apply_pathtarget_labeling_to_tlist(tlist, + best_path->path.pathtarget); + } + else if (is_projection_capable_path(best_path->subpath)) + { + /* + * Our caller requires that we return the exact tlist, but no separate + * result node is needed because the subpath is projection-capable. + * Tell create_plan_recurse that we're going to ignore the tlist it + * produces. + */ + subplan = create_plan_recurse(root, best_path->subpath, + CP_IGNORE_TLIST); + tlist = build_path_tlist(root, &best_path->path); + } + else + { + /* + * It looks like we need a result node, unless by good fortune the + * requested tlist is exactly the one the child wants to produce. + */ + subplan = create_plan_recurse(root, best_path->subpath, 0); + tlist = build_path_tlist(root, &best_path->path); + needs_result_node = !tlist_same_exprs(tlist, subplan->targetlist); + } /* - * Although the ProjectionPath node wouldn't have been made unless its - * pathtarget is different from the subpath's, it can still happen that - * the constructed tlist matches the subplan's. (An example is that - * MergeAppend doesn't project, so we would have thought that we needed a - * projection to attach resjunk sort columns to its output ... but - * create_merge_append_plan might have added those same resjunk sort - * columns to both MergeAppend and its children.) So, if the desired - * tlist is the same expression-wise as the subplan's, just jam it in - * there. We'll have charged for a Result that doesn't actually appear in - * the plan, but that's better than having a Result we don't need. + * If we make a different decision about whether to include a Result node + * than create_projection_path did, we'll have made slightly wrong cost + * estimates; but label the plan with the cost estimates we actually used, + * not "corrected" ones. (XXX this could be cleaned up if we moved more + * of the sortcolumn setup logic into Path creation, but that would add + * expense to creating Paths we might end up not using.) */ - if (tlist_same_exprs(tlist, subplan->targetlist)) + if (!needs_result_node) { + /* Don't need a separate Result, just assign tlist to subplan */ plan = subplan; plan->targetlist = tlist; - /* Adjust cost to match what we thought during planning */ + /* Label plan with the estimated costs we actually used */ plan->startup_cost = best_path->path.startup_cost; plan->total_cost = best_path->path.total_cost; - /* ... but be careful not to munge subplan's parallel-aware flag */ + plan->plan_rows = best_path->path.rows; + plan->plan_width = best_path->path.pathtarget->width; + plan->parallel_safe = best_path->path.parallel_safe; + /* ... but don't change subplan's parallel_aware flag */ } else { + /* We need a Result node */ plan = (Plan *) make_result(tlist, NULL, subplan); copy_generic_path_info(plan, (Path *) best_path); @@ -1435,9 +1728,12 @@ create_projection_plan(PlannerInfo *root, ProjectionPath *best_path) * This is used in a few places where we decide on-the-fly that we need a * projection step as part of the tree generated for some Path node. * We should try to get rid of this in favor of doing it more honestly. + * + * One reason it's ugly is we have to be told the right parallel_safe marking + * to apply (since the tlist might be unsafe even if the child plan is safe). */ static Plan * -inject_projection_plan(Plan *subplan, List *tlist) +inject_projection_plan(Plan *subplan, List *tlist, bool parallel_safe) { Plan *plan; @@ -1451,10 +1747,45 @@ inject_projection_plan(Plan *subplan, List *tlist) * consistent not more so. Hence, just copy the subplan's cost. */ copy_plan_costsize(plan, subplan); + plan->parallel_safe = parallel_safe; return plan; } +/* + * change_plan_targetlist + * Externally available wrapper for inject_projection_plan. + * + * This is meant for use by FDW plan-generation functions, which might + * want to adjust the tlist computed by some subplan tree. In general, + * a Result node is needed to compute the new tlist, but we can optimize + * some cases. + * + * In most cases, tlist_parallel_safe can just be passed as the parallel_safe + * flag of the FDW's own Path node. + */ +Plan * +change_plan_targetlist(Plan *subplan, List *tlist, bool tlist_parallel_safe) +{ + /* + * If the top plan node can't do projections and its existing target list + * isn't already what we need, we need to add a Result node to help it + * along. + */ + if (!is_projection_capable_plan(subplan) && + !tlist_same_exprs(tlist, subplan->targetlist)) + subplan = inject_projection_plan(subplan, tlist, + subplan->parallel_safe && + tlist_parallel_safe); + else + { + /* Else we can just replace the plan node's tlist */ + subplan->targetlist = tlist; + subplan->parallel_safe &= tlist_parallel_safe; + } + return subplan; +} + /* * create_sort_plan * @@ -1475,7 +1806,15 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags) subplan = create_plan_recurse(root, best_path->subpath, flags | CP_SMALL_TLIST); - plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys); + /* + * make_sort_from_pathkeys() indirectly calls find_ec_member_for_tle(), + * which will ignore any child EC members that don't belong to the given + * relids. Thus, if this sort path is based on a child relation, we must + * pass its relids. + */ + plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys, + IS_OTHER_REL(best_path->subpath->parent) ? + best_path->path.parent->relids : NULL); copy_generic_path_info(&plan->plan, (Path *) best_path); @@ -1573,8 +1912,7 @@ create_agg_plan(PlannerInfo *root, AggPath *best_path) plan = make_agg(tlist, quals, best_path->aggstrategy, - false, - true, + best_path->aggsplit, list_length(best_path->groupClause), extract_grouping_cols(best_path->groupClause, subplan->targetlist), @@ -1640,20 +1978,15 @@ create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path) { Agg *plan; Plan *subplan; - AttrNumber *groupColIdx = best_path->groupColIdx; - List *rollup_groupclauses = best_path->rollup_groupclauses; - List *rollup_lists = best_path->rollup_lists; + List *rollups = best_path->rollups; AttrNumber *grouping_map; int maxref; List *chain; - int i; - ListCell *lc, - *lc2; + ListCell *lc; /* Shouldn't get here without grouping sets */ Assert(root->parse->groupingSets); - Assert(rollup_lists != NIL); - Assert(list_length(rollup_lists) == list_length(rollup_groupclauses)); + Assert(rollups != NIL); /* * Agg can project, so no need to be terribly picky about child tlist, but @@ -1662,8 +1995,9 @@ create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path) subplan = create_plan_recurse(root, best_path->subpath, CP_LABEL_TLIST); /* - * Compute the mapping from tleSortGroupRef to column index. First, - * identify max SortGroupRef in groupClause, for array sizing. + * Compute the mapping from tleSortGroupRef to column index in the child's + * tlist. First, identify max SortGroupRef in groupClause, for array + * sizing. */ maxref = 0; foreach(lc, root->parse->groupClause) @@ -1676,12 +2010,13 @@ create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path) grouping_map = (AttrNumber *) palloc0((maxref + 1) * sizeof(AttrNumber)); - i = 0; + /* Now look up the column numbers in the child's tlist */ foreach(lc, root->parse->groupClause) { SortGroupClause *gc = (SortGroupClause *) lfirst(lc); + TargetEntry *tle = get_sortgroupclause_tle(gc, subplan->targetlist); - grouping_map[gc->tleSortGroupRef] = groupColIdx[i++]; + grouping_map[gc->tleSortGroupRef] = tle->resno; } /* @@ -1692,7 +2027,7 @@ create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path) * create_modifytable_plan). Fortunately we can't be because there would * never be grouping in an UPDATE/DELETE; but let's Assert that. */ - Assert(!root->hasInheritedTarget); + Assert(root->inhTargetKind == INHKIND_NONE); Assert(root->grouping_map == NULL); root->grouping_map = grouping_map; @@ -1703,74 +2038,86 @@ create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path) * costs will be shown by EXPLAIN. */ chain = NIL; - if (list_length(rollup_groupclauses) > 1) + if (list_length(rollups) > 1) { - forboth(lc, rollup_groupclauses, lc2, rollup_lists) + ListCell *lc2 = lnext(list_head(rollups)); + bool is_first_sort = ((RollupData *) linitial(rollups))->is_hashed; + + for_each_cell(lc, lc2) { - List *groupClause = (List *) lfirst(lc); - List *gsets = (List *) lfirst(lc2); + RollupData *rollup = lfirst(lc); AttrNumber *new_grpColIdx; - Plan *sort_plan; + Plan *sort_plan = NULL; Plan *agg_plan; + AggStrategy strat; - /* We want to iterate over all but the last rollup list elements */ - if (lnext(lc) == NULL) - break; + new_grpColIdx = remap_groupColIdx(root, rollup->groupClause); + + if (!rollup->is_hashed && !is_first_sort) + { + sort_plan = (Plan *) + make_sort_from_groupcols(rollup->groupClause, + new_grpColIdx, + subplan); + } - new_grpColIdx = remap_groupColIdx(root, groupClause); + if (!rollup->is_hashed) + is_first_sort = false; - sort_plan = (Plan *) - make_sort_from_groupcols(groupClause, - new_grpColIdx, - subplan); + if (rollup->is_hashed) + strat = AGG_HASHED; + else if (list_length(linitial(rollup->gsets)) == 0) + strat = AGG_PLAIN; + else + strat = AGG_SORTED; agg_plan = (Plan *) make_agg(NIL, NIL, - AGG_SORTED, - false, - true, - list_length((List *) linitial(gsets)), + strat, + AGGSPLIT_SIMPLE, + list_length((List *) linitial(rollup->gsets)), new_grpColIdx, - extract_grouping_ops(groupClause), - gsets, + extract_grouping_ops(rollup->groupClause), + rollup->gsets, NIL, - 0, /* numGroups not needed */ + rollup->numGroups, sort_plan); /* - * Nuke stuff we don't need to avoid bloating debug output. + * Remove stuff we don't need to avoid bloating debug output. */ - sort_plan->targetlist = NIL; - sort_plan->lefttree = NULL; + if (sort_plan) + { + sort_plan->targetlist = NIL; + sort_plan->lefttree = NULL; + } chain = lappend(chain, agg_plan); } } /* - * Now make the final Agg node + * Now make the real Agg node */ { - List *groupClause = (List *) llast(rollup_groupclauses); - List *gsets = (List *) llast(rollup_lists); + RollupData *rollup = linitial(rollups); AttrNumber *top_grpColIdx; int numGroupCols; - top_grpColIdx = remap_groupColIdx(root, groupClause); + top_grpColIdx = remap_groupColIdx(root, rollup->groupClause); - numGroupCols = list_length((List *) linitial(gsets)); + numGroupCols = list_length((List *) linitial(rollup->gsets)); plan = make_agg(build_path_tlist(root, &best_path->path), best_path->qual, - (numGroupCols > 0) ? AGG_SORTED : AGG_PLAIN, - false, - true, + best_path->aggstrategy, + AGGSPLIT_SIMPLE, numGroupCols, top_grpColIdx, - extract_grouping_ops(groupClause), - gsets, + extract_grouping_ops(rollup->groupClause), + rollup->gsets, chain, - 0, /* numGroups not needed */ + rollup->numGroups, subplan); /* Copy cost data from Path to Plan */ @@ -1819,6 +2166,7 @@ create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path) plan->plan_rows = 1; plan->plan_width = mminfo->path->pathtarget->width; plan->parallel_aware = false; + plan->parallel_safe = mminfo->path->parallel_safe; /* Convert the plan into an InitPlan in the outer query. */ SS_make_initplan_from_plan(root, subroot, plan, mminfo->param); @@ -1841,7 +2189,7 @@ create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path) * create_modifytable_plan). Fortunately we can't be because there would * never be aggregates in an UPDATE/DELETE; but let's Assert that. */ - Assert(!root->hasInheritedTarget); + Assert(root->inhTargetKind == INHKIND_NONE); Assert(root->minmax_aggs == NIL); root->minmax_aggs = best_path->mmaggregates; @@ -1859,19 +2207,17 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path) { WindowAgg *plan; WindowClause *wc = best_path->winclause; + int numPart = list_length(wc->partitionClause); + int numOrder = list_length(wc->orderClause); Plan *subplan; List *tlist; - int numsortkeys; - AttrNumber *sortColIdx; - Oid *sortOperators; - Oid *collations; - bool *nullsFirst; int partNumCols; AttrNumber *partColIdx; Oid *partOperators; int ordNumCols; AttrNumber *ordColIdx; Oid *ordOperators; + ListCell *lc; /* * WindowAgg can project, so no need to be terribly picky about child @@ -1882,32 +2228,43 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path) tlist = build_path_tlist(root, &best_path->path); /* - * We shouldn't need to actually sort, but it's convenient to use - * prepare_sort_from_pathkeys to identify the input's sort columns. + * Convert SortGroupClause lists into arrays of attr indexes and equality + * operators, as wanted by executor. (Note: in principle, it's possible + * to drop some of the sort columns, if they were proved redundant by + * pathkey logic. However, it doesn't seem worth going out of our way to + * optimize such cases. In any case, we must *not* remove the ordering + * column for RANGE OFFSET cases, as the executor needs that for in_range + * tests even if it's known to be equal to some partitioning column.) */ - subplan = prepare_sort_from_pathkeys(subplan, - best_path->winpathkeys, - NULL, - NULL, - false, - &numsortkeys, - &sortColIdx, - &sortOperators, - &collations, - &nullsFirst); - - /* Now deconstruct that into partition and ordering portions */ - get_column_info_for_window(root, - wc, - subplan->targetlist, - numsortkeys, - sortColIdx, - &partNumCols, - &partColIdx, - &partOperators, - &ordNumCols, - &ordColIdx, - &ordOperators); + partColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numPart); + partOperators = (Oid *) palloc(sizeof(Oid) * numPart); + + partNumCols = 0; + foreach(lc, wc->partitionClause) + { + SortGroupClause *sgc = (SortGroupClause *) lfirst(lc); + TargetEntry *tle = get_sortgroupclause_tle(sgc, subplan->targetlist); + + Assert(OidIsValid(sgc->eqop)); + partColIdx[partNumCols] = tle->resno; + partOperators[partNumCols] = sgc->eqop; + partNumCols++; + } + + ordColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numOrder); + ordOperators = (Oid *) palloc(sizeof(Oid) * numOrder); + + ordNumCols = 0; + foreach(lc, wc->orderClause) + { + SortGroupClause *sgc = (SortGroupClause *) lfirst(lc); + TargetEntry *tle = get_sortgroupclause_tle(sgc, subplan->targetlist); + + Assert(OidIsValid(sgc->eqop)); + ordColIdx[ordNumCols] = tle->resno; + ordOperators[ordNumCols] = sgc->eqop; + ordNumCols++; + } /* And finally we can make the WindowAgg node */ plan = make_windowagg(tlist, @@ -1921,6 +2278,11 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path) wc->frameOptions, wc->startOffset, wc->endOffset, + wc->startInRangeFunc, + wc->endInRangeFunc, + wc->inRangeColl, + wc->inRangeAsc, + wc->inRangeNullsFirst, subplan); copy_generic_path_info(&plan->plan, (Path *) best_path); @@ -1928,112 +2290,6 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path) return plan; } -/* - * get_column_info_for_window - * Get the partitioning/ordering column numbers and equality operators - * for a WindowAgg node. - * - * This depends on the behavior of planner.c's make_pathkeys_for_window! - * - * We are given the target WindowClause and an array of the input column - * numbers associated with the resulting pathkeys. In the easy case, there - * are the same number of pathkey columns as partitioning + ordering columns - * and we just have to copy some data around. However, it's possible that - * some of the original partitioning + ordering columns were eliminated as - * redundant during the transformation to pathkeys. (This can happen even - * though the parser gets rid of obvious duplicates. A typical scenario is a - * window specification "PARTITION BY x ORDER BY y" coupled with a clause - * "WHERE x = y" that causes the two sort columns to be recognized as - * redundant.) In that unusual case, we have to work a lot harder to - * determine which keys are significant. - * - * The method used here is a bit brute-force: add the sort columns to a list - * one at a time and note when the resulting pathkey list gets longer. But - * it's a sufficiently uncommon case that a faster way doesn't seem worth - * the amount of code refactoring that'd be needed. - */ -static void -get_column_info_for_window(PlannerInfo *root, WindowClause *wc, List *tlist, - int numSortCols, AttrNumber *sortColIdx, - int *partNumCols, - AttrNumber **partColIdx, - Oid **partOperators, - int *ordNumCols, - AttrNumber **ordColIdx, - Oid **ordOperators) -{ - int numPart = list_length(wc->partitionClause); - int numOrder = list_length(wc->orderClause); - - if (numSortCols == numPart + numOrder) - { - /* easy case */ - *partNumCols = numPart; - *partColIdx = sortColIdx; - *partOperators = extract_grouping_ops(wc->partitionClause); - *ordNumCols = numOrder; - *ordColIdx = sortColIdx + numPart; - *ordOperators = extract_grouping_ops(wc->orderClause); - } - else - { - List *sortclauses; - List *pathkeys; - int scidx; - ListCell *lc; - - /* first, allocate what's certainly enough space for the arrays */ - *partNumCols = 0; - *partColIdx = (AttrNumber *) palloc(numPart * sizeof(AttrNumber)); - *partOperators = (Oid *) palloc(numPart * sizeof(Oid)); - *ordNumCols = 0; - *ordColIdx = (AttrNumber *) palloc(numOrder * sizeof(AttrNumber)); - *ordOperators = (Oid *) palloc(numOrder * sizeof(Oid)); - sortclauses = NIL; - pathkeys = NIL; - scidx = 0; - foreach(lc, wc->partitionClause) - { - SortGroupClause *sgc = (SortGroupClause *) lfirst(lc); - List *new_pathkeys; - - sortclauses = lappend(sortclauses, sgc); - new_pathkeys = make_pathkeys_for_sortclauses(root, - sortclauses, - tlist); - if (list_length(new_pathkeys) > list_length(pathkeys)) - { - /* this sort clause is actually significant */ - (*partColIdx)[*partNumCols] = sortColIdx[scidx++]; - (*partOperators)[*partNumCols] = sgc->eqop; - (*partNumCols)++; - pathkeys = new_pathkeys; - } - } - foreach(lc, wc->orderClause) - { - SortGroupClause *sgc = (SortGroupClause *) lfirst(lc); - List *new_pathkeys; - - sortclauses = lappend(sortclauses, sgc); - new_pathkeys = make_pathkeys_for_sortclauses(root, - sortclauses, - tlist); - if (list_length(new_pathkeys) > list_length(pathkeys)) - { - /* this sort clause is actually significant */ - (*ordColIdx)[*ordNumCols] = sortColIdx[scidx++]; - (*ordOperators)[*ordNumCols] = sgc->eqop; - (*ordNumCols)++; - pathkeys = new_pathkeys; - } - } - /* complain if we didn't eat exactly the right number of sort cols */ - if (scidx != numSortCols) - elog(ERROR, "failed to deconstruct sort operators into partitioning/ordering operators"); - } -} - /* * create_setop_plan * @@ -2174,8 +2430,11 @@ create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path) best_path->operation, best_path->canSetTag, best_path->nominalRelation, + best_path->rootRelation, + best_path->partColsUpdated, best_path->resultRelations, subplans, + best_path->subroots, best_path->withCheckOptionLists, best_path->returningLists, best_path->rowMarks, @@ -2378,41 +2637,23 @@ create_indexscan_plan(PlannerInfo *root, * first input contains only immutable functions, so we have to check * that.) * - * We can also discard quals that are implied by a partial index's - * predicate, but only in a plain SELECT; when scanning a target relation - * of UPDATE/DELETE/SELECT FOR UPDATE, we must leave such quals in the - * plan so that they'll be properly rechecked by EvalPlanQual testing. - * * Note: if you change this bit of code you should also look at * extract_nonindex_conditions() in costsize.c. */ qpqual = NIL; foreach(l, scan_clauses) { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); + RestrictInfo *rinfo = lfirst_node(RestrictInfo, l); - Assert(IsA(rinfo, RestrictInfo)); if (rinfo->pseudoconstant) continue; /* we may drop pseudoconstants here */ if (list_member_ptr(indexquals, rinfo)) continue; /* simple duplicate */ if (is_redundant_derived_clause(rinfo, indexquals)) continue; /* derived from same EquivalenceClass */ - if (!contain_mutable_functions((Node *) rinfo->clause)) - { - List *clausel = list_make1(rinfo->clause); - - if (predicate_implied_by(clausel, indexquals)) - continue; /* provably implied by indexquals */ - if (best_path->indexinfo->indpred) - { - if (baserelid != root->parse->resultRelation && - get_plan_rowmark(root->rowMarks, baserelid) == NULL) - if (predicate_implied_by(clausel, - best_path->indexinfo->indpred)) - continue; /* implied by index predicate */ - } - } + if (!contain_mutable_functions((Node *) rinfo->clause) && + predicate_implied_by(list_make1(rinfo->clause), indexquals, false)) + continue; /* provably implied by indexquals */ qpqual = lappend(qpqual, rinfo); } @@ -2469,7 +2710,8 @@ create_indexscan_plan(PlannerInfo *root, exprtype, pathkey->pk_strategy); if (!OidIsValid(sortop)) - elog(ERROR, "failed to find sort operator for ORDER BY expression"); + elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", + pathkey->pk_strategy, exprtype, exprtype, pathkey->pk_opfamily); indexorderbyops = lappend_oid(indexorderbyops, sortop); } } @@ -2482,7 +2724,7 @@ create_indexscan_plan(PlannerInfo *root, indexoid, fixed_indexquals, fixed_indexorderbys, - best_path->indexinfo->indextlist, + best_path->indexinfo->indextlist, best_path->indexscandir); else scan_plan = (Scan *) make_indexscan(tlist, @@ -2530,6 +2772,9 @@ create_bitmap_scan_plan(PlannerInfo *root, &bitmapqualorig, &indexquals, &indexECs); + if (best_path->path.parallel_aware) + bitmap_subplan_mark_shared(bitmapqualplan); + /* * The qpqual list must contain all restrictions not automatically handled * by the index, other than pseudoconstant clauses which will be handled @@ -2549,32 +2794,28 @@ create_bitmap_scan_plan(PlannerInfo *root, * redundant with any top-level indexqual by virtue of being generated * from the same EC. After that, try predicate_implied_by(). * - * Unlike create_indexscan_plan(), we need take no special thought here - * for partial index predicates; this is because the predicate conditions - * are already listed in bitmapqualorig and indexquals. Bitmap scans have - * to do it that way because predicate conditions need to be rechecked if - * the scan becomes lossy, so they have to be included in bitmapqualorig. + * Unlike create_indexscan_plan(), the predicate_implied_by() test here is + * useful for getting rid of qpquals that are implied by index predicates, + * because the predicate conditions are included in the "indexquals" + * returned by create_bitmap_subplan(). Bitmap scans have to do it that + * way because predicate conditions need to be rechecked if the scan + * becomes lossy, so they have to be included in bitmapqualorig. */ qpqual = NIL; foreach(l, scan_clauses) { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); + RestrictInfo *rinfo = lfirst_node(RestrictInfo, l); Node *clause = (Node *) rinfo->clause; - Assert(IsA(rinfo, RestrictInfo)); if (rinfo->pseudoconstant) continue; /* we may drop pseudoconstants here */ if (list_member(indexquals, clause)) continue; /* simple duplicate */ if (rinfo->parent_ec && list_member_ptr(indexECs, rinfo->parent_ec)) continue; /* derived from same EquivalenceClass */ - if (!contain_mutable_functions(clause)) - { - List *clausel = list_make1(clause); - - if (predicate_implied_by(clausel, indexquals)) - continue; /* provably implied by indexquals */ - } + if (!contain_mutable_functions(clause) && + predicate_implied_by(list_make1(clause), indexquals, false)) + continue; /* provably implied by indexquals */ qpqual = lappend(qpqual, rinfo); } @@ -2682,6 +2923,7 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, clamp_row_est(apath->bitmapselectivity * apath->path.parent->tuples); plan->plan_width = 0; /* meaningless */ plan->parallel_aware = false; + plan->parallel_safe = apath->path.parallel_safe; *qual = subquals; *indexqual = subindexquals; *indexECs = subindexECs; @@ -2743,8 +2985,9 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, plan->total_cost = opath->path.total_cost; plan->plan_rows = clamp_row_est(opath->bitmapselectivity * opath->path.parent->tuples); - plan->plan_width = 0; /* meaningless */ + plan->plan_width = 0; /* meaningless */ plan->parallel_aware = false; + plan->parallel_safe = opath->path.parallel_safe; } /* @@ -2774,9 +3017,9 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, ListCell *l; /* Use the regular indexscan plan build machinery... */ - iscan = (IndexScan *) create_indexscan_plan(root, ipath, - NIL, NIL, false); - Assert(IsA(iscan, IndexScan)); + iscan = castNode(IndexScan, + create_indexscan_plan(root, ipath, + NIL, NIL, false)); /* then convert to a bitmap indexscan */ plan = (Plan *) make_bitmap_indexscan(iscan->scan.scanrelid, iscan->indexid, @@ -2789,6 +3032,7 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, clamp_row_est(ipath->indexselectivity * ipath->path.parent->tuples); plan->plan_width = 0; /* meaningless */ plan->parallel_aware = false; + plan->parallel_safe = ipath->path.parallel_safe; *qual = get_actual_clauses(ipath->indexclauses); *indexqual = get_actual_clauses(ipath->indexquals); foreach(l, ipath->indexinfo->indpred) @@ -2801,7 +3045,8 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, * the conditions that got pushed into the bitmapqual. Avoid * generating redundant conditions. */ - if (!predicate_implied_by(list_make1(pred), ipath->indexclauses)) + if (!predicate_implied_by(list_make1(pred), ipath->indexclauses, + false)) { *qual = lappend(*qual, pred); *indexqual = lappend(*indexqual, pred); @@ -2838,18 +3083,72 @@ create_tidscan_plan(PlannerInfo *root, TidPath *best_path, TidScan *scan_plan; Index scan_relid = best_path->path.parent->relid; List *tidquals = best_path->tidquals; - List *ortidquals; /* it should be a base rel... */ Assert(scan_relid > 0); Assert(best_path->path.parent->rtekind == RTE_RELATION); + /* + * The qpqual list must contain all restrictions not enforced by the + * tidquals list. Since tidquals has OR semantics, we have to be careful + * about matching it up to scan_clauses. It's convenient to handle the + * single-tidqual case separately from the multiple-tidqual case. In the + * single-tidqual case, we look through the scan_clauses while they are + * still in RestrictInfo form, and drop any that are redundant with the + * tidqual. + * + * In normal cases simple pointer equality checks will be enough to spot + * duplicate RestrictInfos, so we try that first. + * + * Another common case is that a scan_clauses entry is generated from the + * same EquivalenceClass as some tidqual, and is therefore redundant with + * it, though not equal. + * + * Unlike indexpaths, we don't bother with predicate_implied_by(); the + * number of cases where it could win are pretty small. + */ + if (list_length(tidquals) == 1) + { + List *qpqual = NIL; + ListCell *l; + + foreach(l, scan_clauses) + { + RestrictInfo *rinfo = lfirst_node(RestrictInfo, l); + + if (rinfo->pseudoconstant) + continue; /* we may drop pseudoconstants here */ + if (list_member_ptr(tidquals, rinfo)) + continue; /* simple duplicate */ + if (is_redundant_derived_clause(rinfo, tidquals)) + continue; /* derived from same EquivalenceClass */ + qpqual = lappend(qpqual, rinfo); + } + scan_clauses = qpqual; + } + /* Sort clauses into best execution order */ scan_clauses = order_qual_clauses(root, scan_clauses); - /* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */ + /* Reduce RestrictInfo lists to bare expressions; ignore pseudoconstants */ + tidquals = extract_actual_clauses(tidquals, false); scan_clauses = extract_actual_clauses(scan_clauses, false); + /* + * If we have multiple tidquals, it's more convenient to remove duplicate + * scan_clauses after stripping the RestrictInfos. In this situation, + * because the tidquals represent OR sub-clauses, they could not have come + * from EquivalenceClasses so we don't have to worry about matching up + * non-identical clauses. On the other hand, because tidpath.c will have + * extracted those sub-clauses from some OR clause and built its own list, + * we will certainly not have pointer equality to any scan clause. So + * convert the tidquals list to an explicit OR clause and see if we can + * match it via equal() to any scan clause. + */ + if (list_length(tidquals) > 1) + scan_clauses = list_difference(scan_clauses, + list_make1(make_orclause(tidquals))); + /* Replace any outer-relation variables with nestloop params */ if (best_path->path.param_info) { @@ -2859,15 +3158,6 @@ create_tidscan_plan(PlannerInfo *root, TidPath *best_path, replace_nestloop_params(root, (Node *) scan_clauses); } - /* - * Remove any clauses that are TID quals. This is a bit tricky since the - * tidquals list has implicit OR semantics. - */ - ortidquals = tidquals; - if (list_length(ortidquals) > 1) - ortidquals = list_make1(make_orclause(ortidquals)); - scan_clauses = list_difference(scan_clauses, ortidquals); - scan_plan = make_tidscan(tlist, scan_clauses, scan_relid, @@ -2971,6 +3261,49 @@ create_functionscan_plan(PlannerInfo *root, Path *best_path, return scan_plan; } +/* + * create_tablefuncscan_plan + * Returns a tablefuncscan plan for the base relation scanned by 'best_path' + * with restriction clauses 'scan_clauses' and targetlist 'tlist'. + */ +static TableFuncScan * +create_tablefuncscan_plan(PlannerInfo *root, Path *best_path, + List *tlist, List *scan_clauses) +{ + TableFuncScan *scan_plan; + Index scan_relid = best_path->parent->relid; + RangeTblEntry *rte; + TableFunc *tablefunc; + + /* it should be a function base rel... */ + Assert(scan_relid > 0); + rte = planner_rt_fetch(scan_relid, root); + Assert(rte->rtekind == RTE_TABLEFUNC); + tablefunc = rte->tablefunc; + + /* Sort clauses into best execution order */ + scan_clauses = order_qual_clauses(root, scan_clauses); + + /* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */ + scan_clauses = extract_actual_clauses(scan_clauses, false); + + /* Replace any outer-relation variables with nestloop params */ + if (best_path->param_info) + { + scan_clauses = (List *) + replace_nestloop_params(root, (Node *) scan_clauses); + /* The function expressions could contain nestloop params, too */ + tablefunc = (TableFunc *) replace_nestloop_params(root, (Node *) tablefunc); + } + + scan_plan = make_tablefuncscan(tlist, scan_clauses, scan_relid, + tablefunc); + + copy_generic_path_info(&scan_plan->scan.plan, best_path); + + return scan_plan; +} + /* * create_valuesscan_plan * Returns a valuesscan plan for the base relation scanned by 'best_path' @@ -3108,6 +3441,45 @@ create_ctescan_plan(PlannerInfo *root, Path *best_path, return scan_plan; } +/* + * create_namedtuplestorescan_plan + * Returns a tuplestorescan plan for the base relation scanned by + * 'best_path' with restriction clauses 'scan_clauses' and targetlist + * 'tlist'. + */ +static NamedTuplestoreScan * +create_namedtuplestorescan_plan(PlannerInfo *root, Path *best_path, + List *tlist, List *scan_clauses) +{ + NamedTuplestoreScan *scan_plan; + Index scan_relid = best_path->parent->relid; + RangeTblEntry *rte; + + Assert(scan_relid > 0); + rte = planner_rt_fetch(scan_relid, root); + Assert(rte->rtekind == RTE_NAMEDTUPLESTORE); + + /* Sort clauses into best execution order */ + scan_clauses = order_qual_clauses(root, scan_clauses); + + /* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */ + scan_clauses = extract_actual_clauses(scan_clauses, false); + + /* Replace any outer-relation variables with nestloop params */ + if (best_path->param_info) + { + scan_clauses = (List *) + replace_nestloop_params(root, (Node *) scan_clauses); + } + + scan_plan = make_namedtuplestorescan(tlist, scan_clauses, scan_relid, + rte->enrname); + + copy_generic_path_info(&scan_plan->scan.plan, best_path); + + return scan_plan; +} + /* * create_worktablescan_plan * Returns a worktablescan plan for the base relation scanned by 'best_path' @@ -3144,7 +3516,7 @@ create_worktablescan_plan(PlannerInfo *root, Path *best_path, if (!cteroot) /* shouldn't happen */ elog(ERROR, "bad levelsup for CTE \"%s\"", rte->ctename); } - if (cteroot->wt_param_id < 0) /* shouldn't happen */ + if (cteroot->wt_param_id < 0) /* shouldn't happen */ elog(ERROR, "could not find param ID for CTE \"%s\"", rte->ctename); /* Sort clauses into best execution order */ @@ -3228,23 +3600,29 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; - /* Likewise, copy the relids that are represented by this foreign scan */ - scan_plan->fs_relids = best_path->path.parent->relids; + /* + * Likewise, copy the relids that are represented by this foreign scan. An + * upper rel doesn't have relids set, but it covers all the base relations + * participating in the underlying scan, so use root's all_baserels. + */ + if (rel->reloptkind == RELOPT_UPPER_REL) + scan_plan->fs_relids = root->all_baserels; + else + scan_plan->fs_relids = best_path->path.parent->relids; /* - * If a join between foreign relations was pushed down, remember it. The - * push-down safety of the join depends upon the server and user mapping - * being same. That can change between planning and execution time, in which - * case the plan should be invalidated. + * If this is a foreign join, and to make it valid to push down we had to + * assume that the current user is the same as some user explicitly named + * in the query, mark the finished plan as depending on the current user. */ - if (scan_relid == 0) - root->glob->hasForeignJoin = true; + if (rel->useridiscurrent) + root->glob->dependsOnRole = true; /* * Replace any outer-relation variables with nestloop params in the qual, * fdw_exprs and fdw_recheck_quals expressions. We do this last so that - * the FDW doesn't have to be involved. (Note that parts of fdw_exprs - * or fdw_recheck_quals could have come from join clauses, so doing this + * the FDW doesn't have to be involved. (Note that parts of fdw_exprs or + * fdw_recheck_quals could have come from join clauses, so doing this * beforehand on the scan_clauses wouldn't work.) We assume * fdw_scan_tlist contains no such variables. */ @@ -3265,8 +3643,8 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, * 0, but there can be no Var with relid 0 in the rel's targetlist or the * restriction clauses, so we skip this in that case. Note that any such * columns in base relations that were joined are assumed to be contained - * in fdw_scan_tlist.) This is a bit of a kluge and might go away someday, - * so we intentionally leave it out of the API presented to FDWs. + * in fdw_scan_tlist.) This is a bit of a kluge and might go away + * someday, so we intentionally leave it out of the API presented to FDWs. */ scan_plan->fsSystemCol = false; if (scan_relid > 0) @@ -3280,7 +3658,7 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, * Note: we must look at rel's targetlist, not the attr_needed data, * because attr_needed isn't computed for inheritance child rels. */ - pull_varattnos((Node *) rel->reltarget.exprs, scan_relid, &attrs_used); + pull_varattnos((Node *) rel->reltarget->exprs, scan_relid, &attrs_used); /* Add all the attributes used by restriction clauses. */ foreach(lc, rel->baserestrictinfo) @@ -3339,13 +3717,13 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path, * Invoke custom plan provider to create the Plan node represented by the * CustomPath. */ - cplan = (CustomScan *) best_path->methods->PlanCustomPath(root, - rel, - best_path, - tlist, - scan_clauses, - custom_plans); - Assert(IsA(cplan, CustomScan)); + cplan = castNode(CustomScan, + best_path->methods->PlanCustomPath(root, + rel, + best_path, + tlist, + scan_clauses, + custom_plans)); /* * Copy cost data from Path to Plan; no need to make custom-plan providers @@ -3421,6 +3799,7 @@ create_nestloop_plan(PlannerInfo *root, if (IS_OUTER_JOIN(best_path->jointype)) { extract_actual_join_clauses(joinrestrictclauses, + best_path->path.parent->relids, &joinclauses, &otherclauses); } else @@ -3462,7 +3841,7 @@ create_nestloop_plan(PlannerInfo *root, bms_overlap(((PlaceHolderVar *) nlp->paramval)->phrels, outerrelids) && bms_is_subset(find_placeholder_info(root, - (PlaceHolderVar *) nlp->paramval, + (PlaceHolderVar *) nlp->paramval, false)->ph_eval_at, outerrelids)) { @@ -3480,7 +3859,8 @@ create_nestloop_plan(PlannerInfo *root, nestParams, outer_plan, inner_plan, - best_path->jointype); + best_path->jointype, + best_path->inner_unique); copy_generic_path_info(&join_plan->join.plan, &best_path->path); @@ -3505,10 +3885,14 @@ create_mergejoin_plan(PlannerInfo *root, Oid *mergecollations; int *mergestrategies; bool *mergenullsfirst; + PathKey *opathkey; + EquivalenceClass *opeclass; int i; ListCell *lc; ListCell *lop; ListCell *lip; + Path *outer_path = best_path->jpath.outerjoinpath; + Path *inner_path = best_path->jpath.innerjoinpath; /* * MergeJoin can project, so we don't have to demand exact tlists from the @@ -3517,10 +3901,10 @@ create_mergejoin_plan(PlannerInfo *root, * necessary. */ outer_plan = create_plan_recurse(root, best_path->jpath.outerjoinpath, - (best_path->outersortkeys != NIL) ? CP_SMALL_TLIST : 0); + (best_path->outersortkeys != NIL) ? CP_SMALL_TLIST : 0); inner_plan = create_plan_recurse(root, best_path->jpath.innerjoinpath, - (best_path->innersortkeys != NIL) ? CP_SMALL_TLIST : 0); + (best_path->innersortkeys != NIL) ? CP_SMALL_TLIST : 0); /* Sort join qual clauses into best execution order */ /* NB: do NOT reorder the mergeclauses */ @@ -3531,6 +3915,7 @@ create_mergejoin_plan(PlannerInfo *root, if (IS_OUTER_JOIN(best_path->jpath.jointype)) { extract_actual_join_clauses(joinclauses, + best_path->jpath.path.parent->relids, &joinclauses, &otherclauses); } else @@ -3565,15 +3950,17 @@ create_mergejoin_plan(PlannerInfo *root, * outer_is_left status. */ mergeclauses = get_switched_clauses(best_path->path_mergeclauses, - best_path->jpath.outerjoinpath->parent->relids); + best_path->jpath.outerjoinpath->parent->relids); /* * Create explicit sort nodes for the outer and inner paths if necessary. */ if (best_path->outersortkeys) { + Relids outer_relids = outer_path->parent->relids; Sort *sort = make_sort_from_pathkeys(outer_plan, - best_path->outersortkeys); + best_path->outersortkeys, + outer_relids); label_sort_with_costsize(root, sort, -1.0); outer_plan = (Plan *) sort; @@ -3584,8 +3971,10 @@ create_mergejoin_plan(PlannerInfo *root, if (best_path->innersortkeys) { + Relids inner_relids = inner_path->parent->relids; Sort *sort = make_sort_from_pathkeys(inner_plan, - best_path->innersortkeys); + best_path->innersortkeys, + inner_relids); label_sort_with_costsize(root, sort, -1.0); inner_plan = (Plan *) sort; @@ -3617,7 +4006,8 @@ create_mergejoin_plan(PlannerInfo *root, * Compute the opfamily/collation/strategy/nullsfirst arrays needed by the * executor. The information is in the pathkeys for the two inputs, but * we need to be careful about the possibility of mergeclauses sharing a - * pathkey (compare find_mergeclauses_for_pathkeys()). + * pathkey, as well as the possibility that the inner pathkeys are not in + * an order matching the mergeclauses. */ nClauses = list_length(mergeclauses); Assert(nClauses == list_length(best_path->path_mergeclauses)); @@ -3626,22 +4016,21 @@ create_mergejoin_plan(PlannerInfo *root, mergestrategies = (int *) palloc(nClauses * sizeof(int)); mergenullsfirst = (bool *) palloc(nClauses * sizeof(bool)); + opathkey = NULL; + opeclass = NULL; lop = list_head(outerpathkeys); lip = list_head(innerpathkeys); i = 0; foreach(lc, best_path->path_mergeclauses) { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); + RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc); EquivalenceClass *oeclass; EquivalenceClass *ieclass; - PathKey *opathkey; - PathKey *ipathkey; - EquivalenceClass *opeclass; - EquivalenceClass *ipeclass; - ListCell *l2; + PathKey *ipathkey = NULL; + EquivalenceClass *ipeclass = NULL; + bool first_inner_match = false; /* fetch outer/inner eclass from mergeclause */ - Assert(IsA(rinfo, RestrictInfo)); if (rinfo->outer_is_left) { oeclass = rinfo->left_ec; @@ -3656,104 +4045,96 @@ create_mergejoin_plan(PlannerInfo *root, Assert(ieclass != NULL); /* - * For debugging purposes, we check that the eclasses match the paths' - * pathkeys. In typical cases the merge clauses are one-to-one with - * the pathkeys, but when dealing with partially redundant query - * conditions, we might have clauses that re-reference earlier path - * keys. The case that we need to reject is where a pathkey is - * entirely skipped over. + * We must identify the pathkey elements associated with this clause + * by matching the eclasses (which should give a unique match, since + * the pathkey lists should be canonical). In typical cases the merge + * clauses are one-to-one with the pathkeys, but when dealing with + * partially redundant query conditions, things are more complicated. + * + * lop and lip reference the first as-yet-unmatched pathkey elements. + * If they're NULL then all pathkey elements have been matched. * - * lop and lip reference the first as-yet-unused pathkey elements; - * it's okay to match them, or any element before them. If they're - * NULL then we have found all pathkey elements to be used. + * The ordering of the outer pathkeys should match the mergeclauses, + * by construction (see find_mergeclauses_for_outer_pathkeys()). There + * could be more than one mergeclause for the same outer pathkey, but + * no pathkey may be entirely skipped over. */ - if (lop) + if (oeclass != opeclass) /* multiple matches are not interesting */ { + /* doesn't match the current opathkey, so must match the next */ + if (lop == NULL) + elog(ERROR, "outer pathkeys do not match mergeclauses"); opathkey = (PathKey *) lfirst(lop); opeclass = opathkey->pk_eclass; - if (oeclass == opeclass) - { - /* fast path for typical case */ - lop = lnext(lop); - } - else - { - /* redundant clauses ... must match something before lop */ - foreach(l2, outerpathkeys) - { - if (l2 == lop) - break; - opathkey = (PathKey *) lfirst(l2); - opeclass = opathkey->pk_eclass; - if (oeclass == opeclass) - break; - } - if (oeclass != opeclass) - elog(ERROR, "outer pathkeys do not match mergeclauses"); - } - } - else - { - /* redundant clauses ... must match some already-used pathkey */ - opathkey = NULL; - opeclass = NULL; - foreach(l2, outerpathkeys) - { - opathkey = (PathKey *) lfirst(l2); - opeclass = opathkey->pk_eclass; - if (oeclass == opeclass) - break; - } - if (l2 == NULL) + lop = lnext(lop); + if (oeclass != opeclass) elog(ERROR, "outer pathkeys do not match mergeclauses"); } + /* + * The inner pathkeys likewise should not have skipped-over keys, but + * it's possible for a mergeclause to reference some earlier inner + * pathkey if we had redundant pathkeys. For example we might have + * mergeclauses like "o.a = i.x AND o.b = i.y AND o.c = i.x". The + * implied inner ordering is then "ORDER BY x, y, x", but the pathkey + * mechanism drops the second sort by x as redundant, and this code + * must cope. + * + * It's also possible for the implied inner-rel ordering to be like + * "ORDER BY x, y, x DESC". We still drop the second instance of x as + * redundant; but this means that the sort ordering of a redundant + * inner pathkey should not be considered significant. So we must + * detect whether this is the first clause matching an inner pathkey. + */ if (lip) { ipathkey = (PathKey *) lfirst(lip); ipeclass = ipathkey->pk_eclass; if (ieclass == ipeclass) { - /* fast path for typical case */ + /* successful first match to this inner pathkey */ lip = lnext(lip); - } - else - { - /* redundant clauses ... must match something before lip */ - foreach(l2, innerpathkeys) - { - if (l2 == lip) - break; - ipathkey = (PathKey *) lfirst(l2); - ipeclass = ipathkey->pk_eclass; - if (ieclass == ipeclass) - break; - } - if (ieclass != ipeclass) - elog(ERROR, "inner pathkeys do not match mergeclauses"); + first_inner_match = true; } } - else + if (!first_inner_match) { - /* redundant clauses ... must match some already-used pathkey */ - ipathkey = NULL; - ipeclass = NULL; + /* redundant clause ... must match something before lip */ + ListCell *l2; + foreach(l2, innerpathkeys) { + if (l2 == lip) + break; ipathkey = (PathKey *) lfirst(l2); ipeclass = ipathkey->pk_eclass; if (ieclass == ipeclass) break; } - if (l2 == NULL) + if (ieclass != ipeclass) elog(ERROR, "inner pathkeys do not match mergeclauses"); } - /* pathkeys should match each other too (more debugging) */ + /* + * The pathkeys should always match each other as to opfamily and + * collation (which affect equality), but if we're considering a + * redundant inner pathkey, its sort ordering might not match. In + * such cases we may ignore the inner pathkey's sort ordering and use + * the outer's. (In effect, we're lying to the executor about the + * sort direction of this inner column, but it does not matter since + * the run-time row comparisons would only reach this column when + * there's equality for the earlier column containing the same eclass. + * There could be only one value in this column for the range of inner + * rows having a given value in the earlier column, so it does not + * matter which way we imagine this column to be ordered.) But a + * non-redundant inner pathkey had better match outer's ordering too. + */ if (opathkey->pk_opfamily != ipathkey->pk_opfamily || - opathkey->pk_eclass->ec_collation != ipathkey->pk_eclass->ec_collation || - opathkey->pk_strategy != ipathkey->pk_strategy || - opathkey->pk_nulls_first != ipathkey->pk_nulls_first) + opathkey->pk_eclass->ec_collation != ipathkey->pk_eclass->ec_collation) + elog(ERROR, "left and right pathkeys do not match in mergejoin"); + if (first_inner_match && + (opathkey->pk_strategy != ipathkey->pk_strategy || + opathkey->pk_nulls_first != ipathkey->pk_nulls_first)) elog(ERROR, "left and right pathkeys do not match in mergejoin"); /* OK, save info for executor */ @@ -3783,7 +4164,9 @@ create_mergejoin_plan(PlannerInfo *root, mergenullsfirst, outer_plan, inner_plan, - best_path->jpath.jointype); + best_path->jpath.jointype, + best_path->jpath.inner_unique, + best_path->skip_mark_restore); /* Costs of sort and material steps are included in path cost already */ copy_generic_path_info(&join_plan->join.plan, &best_path->jpath.path); @@ -3806,8 +4189,6 @@ create_hashjoin_plan(PlannerInfo *root, Oid skewTable = InvalidOid; AttrNumber skewColumn = InvalidAttrNumber; bool skewInherit = false; - Oid skewColType = InvalidOid; - int32 skewColTypmod = -1; /* * HashJoin can project, so we don't have to demand exact tlists from the @@ -3817,7 +4198,7 @@ create_hashjoin_plan(PlannerInfo *root, * that we don't put extra data in the outer batch files. */ outer_plan = create_plan_recurse(root, best_path->jpath.outerjoinpath, - (best_path->num_batches > 1) ? CP_SMALL_TLIST : 0); + (best_path->num_batches > 1) ? CP_SMALL_TLIST : 0); inner_plan = create_plan_recurse(root, best_path->jpath.innerjoinpath, CP_SMALL_TLIST); @@ -3831,6 +4212,7 @@ create_hashjoin_plan(PlannerInfo *root, if (IS_OUTER_JOIN(best_path->jpath.jointype)) { extract_actual_join_clauses(joinclauses, + best_path->jpath.path.parent->relids, &joinclauses, &otherclauses); } else @@ -3864,7 +4246,7 @@ create_hashjoin_plan(PlannerInfo *root, * on the left. */ hashclauses = get_switched_clauses(best_path->path_hashclauses, - best_path->jpath.outerjoinpath->parent->relids); + best_path->jpath.outerjoinpath->parent->relids); /* * If there is a single join clause and we can identify the outer variable @@ -3894,8 +4276,6 @@ create_hashjoin_plan(PlannerInfo *root, skewTable = rte->relid; skewColumn = var->varattno; skewInherit = rte->inh; - skewColType = var->vartype; - skewColTypmod = var->vartypmod; } } } @@ -3906,9 +4286,7 @@ create_hashjoin_plan(PlannerInfo *root, hash_plan = make_hash(inner_plan, skewTable, skewColumn, - skewInherit, - skewColType, - skewColTypmod); + skewInherit); /* * Set Hash node's startup & total costs equal to total cost of input @@ -3917,13 +4295,25 @@ create_hashjoin_plan(PlannerInfo *root, copy_plan_costsize(&hash_plan->plan, inner_plan); hash_plan->plan.startup_cost = hash_plan->plan.total_cost; + /* + * If parallel-aware, the executor will also need an estimate of the total + * number of rows expected from all participants so that it can size the + * shared hash table. + */ + if (best_path->jpath.path.parallel_aware) + { + hash_plan->plan.parallel_aware = true; + hash_plan->rows_total = best_path->inner_rows_total; + } + join_plan = make_hashjoin(tlist, joinclauses, otherclauses, hashclauses, outer_plan, (Plan *) hash_plan, - best_path->jpath.jointype); + best_path->jpath.jointype, + best_path->jpath.inner_unique); copy_generic_path_info(&join_plan->join.plan, &best_path->jpath.path); @@ -4007,8 +4397,8 @@ replace_nestloop_params_mutator(Node *node, PlannerInfo *root) * rels, and then grab its PlaceHolderInfo to tell for sure. */ if (!bms_overlap(phv->phrels, root->curOuterRels) || - !bms_is_subset(find_placeholder_info(root, phv, false)->ph_eval_at, - root->curOuterRels)) + !bms_is_subset(find_placeholder_info(root, phv, false)->ph_eval_at, + root->curOuterRels)) { /* * We can't replace the whole PHV, but we might still need to @@ -4136,7 +4526,7 @@ process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params) /* No, so add it */ nlp = makeNode(NestLoopParam); nlp->paramno = pitem->paramId; - nlp->paramval = copyObject(phv); + nlp->paramval = (Var *) copyObject(phv); root->curOuterParams = lappend(root->curOuterParams, nlp); } } @@ -4176,12 +4566,10 @@ fix_indexqual_references(PlannerInfo *root, IndexPath *index_path) forboth(lcc, index_path->indexquals, lci, index_path->indexqualcols) { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(lcc); + RestrictInfo *rinfo = lfirst_node(RestrictInfo, lcc); int indexcol = lfirst_int(lci); Node *clause; - Assert(IsA(rinfo, RestrictInfo)); - /* * Replace any outer-relation variables with nestloop params. * @@ -4415,7 +4803,7 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol) } } - /* Ooops... */ + /* Oops... */ elog(ERROR, "index key does not match expected index column"); return NULL; /* keep compiler quiet */ } @@ -4479,21 +4867,32 @@ get_switched_clauses(List *clauses, Relids outerrelids) * plan node, sort the list into the order we want to check the quals * in at runtime. * + * When security barrier quals are used in the query, we may have quals with + * different security levels in the list. Quals of lower security_level + * must go before quals of higher security_level, except that we can grant + * exceptions to move up quals that are leakproof. When security level + * doesn't force the decision, we prefer to order clauses by estimated + * execution cost, cheapest first. + * * Ideally the order should be driven by a combination of execution cost and * selectivity, but it's not immediately clear how to account for both, * and given the uncertainty of the estimates the reliability of the decisions - * would be doubtful anyway. So we just order by estimated per-tuple cost, - * being careful not to change the order when (as is often the case) the - * estimates are identical. + * would be doubtful anyway. So we just order by security level then + * estimated per-tuple cost, being careful not to change the order when + * (as is often the case) the estimates are identical. * * Although this will work on either bare clauses or RestrictInfos, it's * much faster to apply it to RestrictInfos, since it can re-use cost - * information that is cached in RestrictInfos. + * information that is cached in RestrictInfos. XXX in the bare-clause + * case, we are also not able to apply security considerations. That is + * all right for the moment, because the bare-clause case doesn't occur + * anywhere that barrier quals could be present, but it would be better to + * get rid of it. * * Note: some callers pass lists that contain entries that will later be * removed; this is the easiest way to let this routine see RestrictInfos - * instead of bare clauses. It's OK because we only sort by cost, but - * a cost/selectivity combination would likely do the wrong thing. + * instead of bare clauses. This is another reason why trying to consider + * selectivity in the ordering would likely do the wrong thing. */ static List * order_qual_clauses(PlannerInfo *root, List *clauses) @@ -4502,6 +4901,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses) { Node *clause; Cost cost; + Index security_level; } QualItem; int nitems = list_length(clauses); QualItem *items; @@ -4527,6 +4927,27 @@ order_qual_clauses(PlannerInfo *root, List *clauses) cost_qual_eval_node(&qcost, clause, root); items[i].clause = clause; items[i].cost = qcost.per_tuple; + if (IsA(clause, RestrictInfo)) + { + RestrictInfo *rinfo = (RestrictInfo *) clause; + + /* + * If a clause is leakproof, it doesn't have to be constrained by + * its nominal security level. If it's also reasonably cheap + * (here defined as 10X cpu_operator_cost), pretend it has + * security_level 0, which will allow it to go in front of + * more-expensive quals of lower security levels. Of course, that + * will also force it to go in front of cheaper quals of its own + * security level, which is not so great, but we can alleviate + * that risk by applying the cost limit cutoff. + */ + if (rinfo->leakproof && items[i].cost < 10 * cpu_operator_cost) + items[i].security_level = 0; + else + items[i].security_level = rinfo->security_level; + } + else + items[i].security_level = 0; i++; } @@ -4543,9 +4964,13 @@ order_qual_clauses(PlannerInfo *root, List *clauses) /* insert newitem into the already-sorted subarray */ for (j = i; j > 0; j--) { - if (newitem.cost >= items[j - 1].cost) + QualItem *olditem = &items[j - 1]; + + if (newitem.security_level > olditem->security_level || + (newitem.security_level == olditem->security_level && + newitem.cost >= olditem->cost)) break; - items[j] = items[j - 1]; + items[j] = *olditem; } items[j] = newitem; } @@ -4561,7 +4986,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses) /* * Copy cost and size info from a Path node to the Plan node created from it. * The executor usually won't use this info, but it's needed by EXPLAIN. - * Also copy the parallel-aware flag, which the executor *will* use. + * Also copy the parallel-related flags, which the executor *will* use. */ static void copy_generic_path_info(Plan *dest, Path *src) @@ -4571,6 +4996,7 @@ copy_generic_path_info(Plan *dest, Path *src) dest->plan_rows = src->rows; dest->plan_width = src->pathtarget->width; dest->parallel_aware = src->parallel_aware; + dest->parallel_safe = src->parallel_safe; } /* @@ -4586,6 +5012,8 @@ copy_plan_costsize(Plan *dest, Plan *src) dest->plan_width = src->plan_width; /* Assume the inserted node is not parallel-aware. */ dest->parallel_aware = false; + /* Assume the inserted node is parallel-safe, if child plan is. */ + dest->parallel_safe = src->parallel_safe; } /* @@ -4615,8 +5043,31 @@ label_sort_with_costsize(PlannerInfo *root, Sort *plan, double limit_tuples) plan->plan.plan_rows = lefttree->plan_rows; plan->plan.plan_width = lefttree->plan_width; plan->plan.parallel_aware = false; + plan->plan.parallel_safe = lefttree->parallel_safe; } +/* + * bitmap_subplan_mark_shared + * Set isshared flag in bitmap subplan so that it will be created in + * shared memory. + */ +static void +bitmap_subplan_mark_shared(Plan *plan) +{ + if (IsA(plan, BitmapAnd)) + bitmap_subplan_mark_shared( + linitial(((BitmapAnd *) plan)->bitmapplans)); + else if (IsA(plan, BitmapOr)) + { + ((BitmapOr *) plan)->isshared = true; + bitmap_subplan_mark_shared( + linitial(((BitmapOr *) plan)->bitmapplans)); + } + else if (IsA(plan, BitmapIndexScan)) + ((BitmapIndexScan *) plan)->isshared = true; + else + elog(ERROR, "unrecognized node type: %d", nodeTag(plan)); +} /***************************************************************************** * @@ -4826,6 +5277,25 @@ make_functionscan(List *qptlist, return node; } +static TableFuncScan * +make_tablefuncscan(List *qptlist, + List *qpqual, + Index scanrelid, + TableFunc *tablefunc) +{ + TableFuncScan *node = makeNode(TableFuncScan); + Plan *plan = &node->scan.plan; + + plan->targetlist = qptlist; + plan->qual = qpqual; + plan->lefttree = NULL; + plan->righttree = NULL; + node->scan.scanrelid = scanrelid; + node->tablefunc = tablefunc; + + return node; +} + static ValuesScan * make_valuesscan(List *qptlist, List *qpqual, @@ -4866,6 +5336,26 @@ make_ctescan(List *qptlist, return node; } +static NamedTuplestoreScan * +make_namedtuplestorescan(List *qptlist, + List *qpqual, + Index scanrelid, + char *enrname) +{ + NamedTuplestoreScan *node = makeNode(NamedTuplestoreScan); + Plan *plan = &node->scan.plan; + + /* cost should be inserted by caller */ + plan->targetlist = qptlist; + plan->qual = qpqual; + plan->lefttree = NULL; + plan->righttree = NULL; + node->scan.scanrelid = scanrelid; + node->enrname = enrname; + + return node; +} + static WorkTableScan * make_worktablescan(List *qptlist, List *qpqual, @@ -4904,6 +5394,7 @@ make_foreignscan(List *qptlist, plan->lefttree = outer_plan; plan->righttree = NULL; node->scan.scanrelid = scanrelid; + node->operation = CMD_SELECT; /* fs_server will be filled in by create_foreignscan_plan */ node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; @@ -4919,7 +5410,8 @@ make_foreignscan(List *qptlist, } static Append * -make_append(List *appendplans, List *tlist) +make_append(List *appendplans, int first_partial_plan, + List *tlist, PartitionPruneInfo *partpruneinfo) { Append *node = makeNode(Append); Plan *plan = &node->plan; @@ -4929,7 +5421,8 @@ make_append(List *appendplans, List *tlist) plan->lefttree = NULL; plan->righttree = NULL; node->appendplans = appendplans; - + node->first_partial_plan = first_partial_plan; + node->part_prune_info = partpruneinfo; return node; } @@ -5022,7 +5515,8 @@ make_nestloop(List *tlist, List *nestParams, Plan *lefttree, Plan *righttree, - JoinType jointype) + JoinType jointype, + bool inner_unique) { NestLoop *node = makeNode(NestLoop); Plan *plan = &node->join.plan; @@ -5032,6 +5526,7 @@ make_nestloop(List *tlist, plan->lefttree = lefttree; plan->righttree = righttree; node->join.jointype = jointype; + node->join.inner_unique = inner_unique; node->join.joinqual = joinclauses; node->nestParams = nestParams; @@ -5045,7 +5540,8 @@ make_hashjoin(List *tlist, List *hashclauses, Plan *lefttree, Plan *righttree, - JoinType jointype) + JoinType jointype, + bool inner_unique) { HashJoin *node = makeNode(HashJoin); Plan *plan = &node->join.plan; @@ -5056,6 +5552,7 @@ make_hashjoin(List *tlist, plan->righttree = righttree; node->hashclauses = hashclauses; node->join.jointype = jointype; + node->join.inner_unique = inner_unique; node->join.joinqual = joinclauses; return node; @@ -5065,9 +5562,7 @@ static Hash * make_hash(Plan *lefttree, Oid skewTable, AttrNumber skewColumn, - bool skewInherit, - Oid skewColType, - int32 skewColTypmod) + bool skewInherit) { Hash *node = makeNode(Hash); Plan *plan = &node->plan; @@ -5080,8 +5575,6 @@ make_hash(Plan *lefttree, node->skewTable = skewTable; node->skewColumn = skewColumn; node->skewInherit = skewInherit; - node->skewColType = skewColType; - node->skewColTypmod = skewColTypmod; return node; } @@ -5097,7 +5590,9 @@ make_mergejoin(List *tlist, bool *mergenullsfirst, Plan *lefttree, Plan *righttree, - JoinType jointype) + JoinType jointype, + bool inner_unique, + bool skip_mark_restore) { MergeJoin *node = makeNode(MergeJoin); Plan *plan = &node->join.plan; @@ -5106,12 +5601,14 @@ make_mergejoin(List *tlist, plan->qual = otherclauses; plan->lefttree = lefttree; plan->righttree = righttree; + node->skip_mark_restore = skip_mark_restore; node->mergeclauses = mergeclauses; node->mergeFamilies = mergefamilies; node->mergeCollations = mergecollations; node->mergeStrategies = mergestrategies; node->mergeNullsFirst = mergenullsfirst; node->join.jointype = jointype; + node->join.inner_unique = inner_unique; node->join.joinqual = joinclauses; return node; @@ -5148,16 +5645,16 @@ make_sort(Plan *lefttree, int numCols, * prepare_sort_from_pathkeys * Prepare to sort according to given pathkeys * - * This is used to set up for both Sort and MergeAppend nodes. It calculates - * the executor's representation of the sort key information, and adjusts the - * plan targetlist if needed to add resjunk sort columns. + * This is used to set up for Sort, MergeAppend, and Gather Merge nodes. It + * calculates the executor's representation of the sort key information, and + * adjusts the plan targetlist if needed to add resjunk sort columns. * * Input parameters: * 'lefttree' is the plan node which yields input tuples * 'pathkeys' is the list of pathkeys by which the result is to be sorted * 'relids' identifies the child relation being sorted, if any * 'reqColIdx' is NULL or an array of required sort key column numbers - * 'adjust_tlist_in_place' is TRUE if lefttree must be modified in-place + * 'adjust_tlist_in_place' is true if lefttree must be modified in-place * * We must convert the pathkey information into arrays of sort key column * numbers, sort operator OIDs, collation OIDs, and nulls-first flags, @@ -5165,8 +5662,9 @@ make_sort(Plan *lefttree, int numCols, * the output parameters *p_numsortkeys etc. * * When looking for matches to an EquivalenceClass's members, we will only - * consider child EC members if they match 'relids'. This protects against - * possible incorrect matches to child expressions that contain no Vars. + * consider child EC members if they belong to given 'relids'. This protects + * against possible incorrect matches to child expressions that contain no + * Vars. * * If reqColIdx isn't NULL then it contains sort key column numbers that * we should match. This is used when making child plans for a MergeAppend; @@ -5174,10 +5672,10 @@ make_sort(Plan *lefttree, int numCols, * * If the pathkeys include expressions that aren't simple Vars, we will * usually need to add resjunk items to the input plan's targetlist to - * compute these expressions, since the Sort/MergeAppend node itself won't + * compute these expressions, since a Sort or MergeAppend node itself won't * do any such calculations. If the input plan type isn't one that can do * projections, this means adding a Result node just to do the projection. - * However, the caller can pass adjust_tlist_in_place = TRUE to force the + * However, the caller can pass adjust_tlist_in_place = true to force the * lefttree tlist to be modified in-place regardless of whether the node type * can project --- we use this for fixing the tlist of MergeAppend itself. * @@ -5301,7 +5799,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys, * that we treat Aggrefs as if they were variables; this is * necessary when attempting to sort the output from an Agg node * for use in a WindowFunc (since grouping_planner will have - * treated the Aggrefs as variables, too). + * treated the Aggrefs as variables, too). Likewise, if we find a + * WindowFunc in a sort expression, treat it as a variable. */ Expr *sortexpr = NULL; @@ -5320,16 +5819,17 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys, continue; /* - * Ignore child members unless they match the rel being + * Ignore child members unless they belong to the rel being * sorted. */ if (em->em_is_child && - !bms_equal(em->em_relids, relids)) + !bms_is_subset(em->em_relids, relids)) continue; sortexpr = em->em_expr; exprvars = pull_var_clause((Node *) sortexpr, - PVC_INCLUDE_AGGREGATES, + PVC_INCLUDE_AGGREGATES | + PVC_INCLUDE_WINDOWFUNCS | PVC_INCLUDE_PLACEHOLDERS); foreach(k, exprvars) { @@ -5354,7 +5854,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys, { /* copy needed so we don't modify input's tlist below */ tlist = copyObject(tlist); - lefttree = inject_projection_plan(lefttree, tlist); + lefttree = inject_projection_plan(lefttree, tlist, + lefttree->parallel_safe); } /* Don't bother testing is_projection_capable_plan again */ @@ -5368,7 +5869,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys, NULL, true); tlist = lappend(tlist, tle); - lefttree->targetlist = tlist; /* just in case NIL before */ + lefttree->targetlist = tlist; /* just in case NIL before */ } /* @@ -5380,7 +5881,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys, pk_datatype, pathkey->pk_strategy); if (!OidIsValid(sortop)) /* should not happen */ - elog(ERROR, "could not find member %d(%u,%u) of opfamily %u", + elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", pathkey->pk_strategy, pk_datatype, pk_datatype, pathkey->pk_opfamily); @@ -5406,7 +5907,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys, * find_ec_member_for_tle * Locate an EquivalenceClass member matching the given TLE, if any * - * Child EC members are ignored unless they match 'relids'. + * Child EC members are ignored unless they belong to given 'relids'. */ static EquivalenceMember * find_ec_member_for_tle(EquivalenceClass *ec, @@ -5434,10 +5935,10 @@ find_ec_member_for_tle(EquivalenceClass *ec, continue; /* - * Ignore child members unless they match the rel being sorted. + * Ignore child members unless they belong to the rel being sorted. */ if (em->em_is_child && - !bms_equal(em->em_relids, relids)) + !bms_is_subset(em->em_relids, relids)) continue; /* Match if same expression (after stripping relabel) */ @@ -5458,9 +5959,10 @@ find_ec_member_for_tle(EquivalenceClass *ec, * * 'lefttree' is the node which yields input tuples * 'pathkeys' is the list of pathkeys by which the result is to be sorted + * 'relids' is the set of relations required by prepare_sort_from_pathkeys() */ static Sort * -make_sort_from_pathkeys(Plan *lefttree, List *pathkeys) +make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids) { int numsortkeys; AttrNumber *sortColIdx; @@ -5470,7 +5972,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys) /* Compute sort column info, and adjust lefttree as needed */ lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys, - NULL, + relids, NULL, false, &numsortkeys, @@ -5492,7 +5994,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys) * 'sortcls' is a list of SortGroupClauses * 'lefttree' is the node which yields input tuples */ -static Sort * +Sort * make_sort_from_sortclauses(List *sortcls, Plan *lefttree) { List *sub_tlist = lefttree->targetlist; @@ -5612,6 +6114,16 @@ materialize_finished_plan(Plan *subplan) matplan = (Plan *) make_material(subplan); + /* + * XXX horrid kluge: if there are any initPlans attached to the subplan, + * move them up to the Material node, which is now effectively the top + * plan node in its query level. This prevents failure in + * SS_finalize_plan(), which see for comments. We don't bother adjusting + * the subplan's cost estimate for this. + */ + matplan->initPlan = subplan->initPlan; + subplan->initPlan = NIL; + /* Set cost data */ cost_material(&matpath, subplan->startup_cost, @@ -5623,14 +6135,14 @@ materialize_finished_plan(Plan *subplan) matplan->plan_rows = subplan->plan_rows; matplan->plan_width = subplan->plan_width; matplan->parallel_aware = false; + matplan->parallel_safe = subplan->parallel_safe; return matplan; } -static Agg * +Agg * make_agg(List *tlist, List *qual, - AggStrategy aggstrategy, - bool combineStates, bool finalizeAggs, + AggStrategy aggstrategy, AggSplit aggsplit, int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators, List *groupingSets, List *chain, double dNumGroups, Plan *lefttree) @@ -5643,12 +6155,12 @@ make_agg(List *tlist, List *qual, numGroups = (long) Min(dNumGroups, (double) LONG_MAX); node->aggstrategy = aggstrategy; - node->combineStates = combineStates; - node->finalizeAggs = finalizeAggs; + node->aggsplit = aggsplit; node->numCols = numGroupCols; node->grpColIdx = grpColIdx; node->grpOperators = grpOperators; node->numGroups = numGroups; + node->aggParams = NULL; /* SS_finalize_plan() will fill this */ node->groupingSets = groupingSets; node->chain = chain; @@ -5665,6 +6177,8 @@ make_windowagg(List *tlist, Index winref, int partNumCols, AttrNumber *partColIdx, Oid *partOperators, int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators, int frameOptions, Node *startOffset, Node *endOffset, + Oid startInRangeFunc, Oid endInRangeFunc, + Oid inRangeColl, bool inRangeAsc, bool inRangeNullsFirst, Plan *lefttree) { WindowAgg *node = makeNode(WindowAgg); @@ -5680,6 +6194,11 @@ make_windowagg(List *tlist, Index winref, node->frameOptions = frameOptions; node->startOffset = startOffset; node->endOffset = endOffset; + node->startInRangeFunc = startInRangeFunc; + node->endInRangeFunc = endInRangeFunc; + node->inRangeColl = inRangeColl; + node->inRangeAsc = inRangeAsc; + node->inRangeNullsFirst = inRangeNullsFirst; plan->targetlist = tlist; plan->lefttree = lefttree; @@ -5848,7 +6367,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols) pk_datatype, BTEqualStrategyNumber); if (!OidIsValid(eqop)) /* should not happen */ - elog(ERROR, "could not find member %d(%u,%u) of opfamily %u", + elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", BTEqualStrategyNumber, pk_datatype, pk_datatype, pathkey->pk_opfamily); @@ -5869,6 +6388,7 @@ static Gather * make_gather(List *qptlist, List *qpqual, int nworkers, + int rescan_param, bool single_copy, Plan *subplan) { @@ -5880,8 +6400,10 @@ make_gather(List *qptlist, plan->lefttree = subplan; plan->righttree = NULL; node->num_workers = nworkers; + node->rescan_param = rescan_param; node->single_copy = single_copy; - node->invisible = false; + node->invisible = false; + node->initParam = NULL; return node; } @@ -5913,7 +6435,6 @@ make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree, * convert SortGroupClause list into arrays of attr indexes and equality * operators, as wanted by executor */ - Assert(numCols > 0); dupColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols); dupOperators = (Oid *) palloc(sizeof(Oid) * numCols); @@ -5965,7 +6486,7 @@ make_lockrows(Plan *lefttree, List *rowMarks, int epqParam) * make_limit * Build a Limit plan node */ -static Limit * +Limit * make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount) { Limit *node = makeNode(Limit); @@ -6003,6 +6524,25 @@ make_result(List *tlist, return node; } +/* + * make_project_set + * Build a ProjectSet plan node + */ +static ProjectSet * +make_project_set(List *tlist, + Plan *subplan) +{ + ProjectSet *node = makeNode(ProjectSet); + Plan *plan = &node->plan; + + plan->targetlist = tlist; + plan->qual = NIL; + plan->lefttree = subplan; + plan->righttree = NULL; + + return node; +} + /* * make_modifytable * Build a ModifyTable plan node @@ -6010,17 +6550,21 @@ make_result(List *tlist, static ModifyTable * make_modifytable(PlannerInfo *root, CmdType operation, bool canSetTag, - Index nominalRelation, - List *resultRelations, List *subplans, + Index nominalRelation, Index rootRelation, + 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 || @@ -6035,8 +6579,11 @@ make_modifytable(PlannerInfo *root, node->operation = operation; node->canSetTag = canSetTag; node->nominalRelation = nominalRelation; + node->rootRelation = rootRelation; + 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) { @@ -6074,12 +6621,15 @@ make_modifytable(PlannerInfo *root, * construct private plan data, and accumulate it all into a list. */ fdw_private_list = NIL; + direct_modify_plans = NULL; i = 0; - foreach(lc, resultRelations) + 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 @@ -6088,16 +6638,16 @@ make_modifytable(PlannerInfo *root, * 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 < root->simple_rel_array_size && - root->simple_rel_array[rti] != NULL) + if (rti < subroot->simple_rel_array_size && + subroot->simple_rel_array[rti] != NULL) { - RelOptInfo *resultRel = root->simple_rel_array[rti]; + RelOptInfo *resultRel = subroot->simple_rel_array[rti]; fdwroutine = resultRel->fdwroutine; } else { - RangeTblEntry *rte = planner_rt_fetch(rti, root); + RangeTblEntry *rte = planner_rt_fetch(rti, subroot); Assert(rte->rtekind == RTE_RELATION); if (rte->relkind == RELKIND_FOREIGN_TABLE) @@ -6106,15 +6656,35 @@ make_modifytable(PlannerInfo *root, 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(root, node, rti, i); + 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; } @@ -6149,6 +6719,15 @@ is_projection_capable_path(Path *path) * projection to its dummy path. */ return IS_DUMMY_PATH(path); + case T_ProjectSet: + + /* + * Although ProjectSet certainly projects, say "no" because we + * don't want the planner to randomly replace its tlist with + * something else; the SRFs have to stay at top level. This might + * get relaxed later. + */ + return false; default: break; } @@ -6177,6 +6756,15 @@ is_projection_capable_plan(Plan *plan) case T_MergeAppend: case T_RecursiveUnion: return false; + case T_ProjectSet: + + /* + * Although ProjectSet certainly projects, say "no" because we + * don't want the planner to randomly replace its tlist with + * something else; the SRFs have to stay at top level. This might + * get relaxed later. + */ + return false; default: break; }