* Planning is complete, we just need to convert the selected
* Path into a Plan.
*
- * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
#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"
#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 "utils/lsyscache.h"
-static Plan *create_plan_recurse(PlannerInfo *root, Path *best_path);
-static Plan *create_scan_plan(PlannerInfo *root, Path *best_path);
+/*
+ * Flag bits that can appear in the flags argument of create_plan_recurse().
+ * These can be OR-ed together.
+ *
+ * CP_EXACT_TLIST specifies that the generated plan node must return exactly
+ * the tlist specified by the path's pathtarget (this overrides both
+ * CP_SMALL_TLIST and CP_LABEL_TLIST, if those are set). Otherwise, the
+ * plan node is allowed to return just the Vars and PlaceHolderVars needed
+ * to evaluate the pathtarget.
+ *
+ * CP_SMALL_TLIST specifies that a narrower tlist is preferred. This is
+ * passed down by parent nodes such as Sort and Hash, which will have to
+ * store the returned tuples.
+ *
+ * CP_LABEL_TLIST specifies that the plan node must return columns matching
+ * 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.
+ */
+#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 */
+
+
+static Plan *create_plan_recurse(PlannerInfo *root, Path *best_path,
+ int flags);
+static Plan *create_scan_plan(PlannerInfo *root, Path *best_path,
+ int flags);
static List *build_path_tlist(PlannerInfo *root, Path *path);
-static bool use_physical_tlist(PlannerInfo *root, RelOptInfo *rel);
-static void disuse_physical_tlist(PlannerInfo *root, Plan *plan, Path *path);
-static Plan *create_gating_plan(PlannerInfo *root, Plan *plan, List *quals);
+static bool use_physical_tlist(PlannerInfo *root, Path *path, int flags);
+static List *get_gating_quals(PlannerInfo *root, List *quals);
+static Plan *create_gating_plan(PlannerInfo *root, Path *path, Plan *plan,
+ List *gating_quals);
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 Material *create_material_plan(PlannerInfo *root, MaterialPath *best_path);
-static Plan *create_unique_plan(PlannerInfo *root, UniquePath *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 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,
+ int flags);
+static Agg *create_agg_plan(PlannerInfo *root, AggPath *best_path);
+static Plan *create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path);
+static Result *create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path);
+static WindowAgg *create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path);
+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);
+static Limit *create_limit_plan(PlannerInfo *root, LimitPath *best_path,
+ int flags);
static SeqScan *create_seqscan_plan(PlannerInfo *root, Path *best_path,
List *tlist, List *scan_clauses);
static SampleScan *create_samplescan_plan(PlannerInfo *root, Path *best_path,
List *tlist, List *scan_clauses);
-static Gather *create_gather_plan(PlannerInfo *root,
- GatherPath *best_path);
static Scan *create_indexscan_plan(PlannerInfo *root, IndexPath *best_path,
List *tlist, List *scan_clauses, bool indexonly);
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, Path *best_path,
+static SubqueryScan *create_subqueryscan_plan(PlannerInfo *root,
+ SubqueryScanPath *best_path,
List *tlist, List *scan_clauses);
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 WorkTableScan *create_worktablescan_plan(PlannerInfo *root, Path *best_path,
static CustomScan *create_customscan_plan(PlannerInfo *root,
CustomPath *best_path,
List *tlist, List *scan_clauses);
-static NestLoop *create_nestloop_plan(PlannerInfo *root, NestPath *best_path,
- Plan *outer_plan, Plan *inner_plan);
-static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path,
- Plan *outer_plan, Plan *inner_plan);
-static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path,
- Plan *outer_plan, Plan *inner_plan);
+static NestLoop *create_nestloop_plan(PlannerInfo *root, NestPath *best_path);
+static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path);
+static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path);
static Node *replace_nestloop_params(PlannerInfo *root, Node *expr);
static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root);
static void process_subquery_nestloop_params(PlannerInfo *root,
static List *order_qual_clauses(PlannerInfo *root, List *clauses);
static void copy_generic_path_info(Plan *dest, Path *src);
static void copy_plan_costsize(Plan *dest, Plan *src);
+static void label_sort_with_costsize(PlannerInfo *root, Sort *plan,
+ double limit_tuples);
static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
static SampleScan *make_samplescan(List *qptlist, List *qpqual, Index scanrelid,
TableSampleClause *tsc);
-static Gather *make_gather(List *qptlist, List *qpqual,
- int nworkers, bool single_copy, Plan *subplan);
static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
Oid indexid, List *indexqual, List *indexqualorig,
List *indexorderby, List *indexorderbyorig,
Index scanrelid);
static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
List *tidquals);
+static SubqueryScan *make_subqueryscan(List *qptlist,
+ List *qpqual,
+ Index scanrelid,
+ Plan *subplan);
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 WorkTableScan *make_worktablescan(List *qptlist, List *qpqual,
Index scanrelid, int wtParam);
+static Append *make_append(List *appendplans, List *tlist);
+static RecursiveUnion *make_recursive_union(List *tlist,
+ Plan *lefttree,
+ Plan *righttree,
+ int wtParam,
+ List *distinctList,
+ long numGroups);
static BitmapAnd *make_bitmap_and(List *bitmapplans);
static BitmapOr *make_bitmap_or(List *bitmapplans);
static NestLoop *make_nestloop(List *tlist,
bool *mergenullsfirst,
Plan *lefttree, Plan *righttree,
JoinType jointype);
-static Sort *make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
+static Sort *make_sort(Plan *lefttree, int numCols,
AttrNumber *sortColIdx, Oid *sortOperators,
- Oid *collations, bool *nullsFirst,
- double limit_tuples);
-static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
- Plan *lefttree, List *pathkeys,
+ Oid *collations, bool *nullsFirst);
+static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
Relids relids,
const AttrNumber *reqColIdx,
bool adjust_tlist_in_place,
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_groupcols(List *groupcls,
+ AttrNumber *grpColIdx,
+ Plan *lefttree);
static Material *make_material(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,
+ Plan *lefttree);
+static Group *make_group(List *tlist, List *qual, int numGroupCols,
+ AttrNumber *grpColIdx, Oid *grpOperators,
+ Plan *lefttree);
+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);
+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 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,
+ List *withCheckOptionLists, List *returningLists,
+ List *rowMarks, OnConflictExpr *onconflict, int epqParam);
+static GatherMerge *create_gather_merge_plan(PlannerInfo *root,
+ GatherMergePath *best_path);
/*
root->curOuterRels = NULL;
root->curOuterParams = NIL;
- /* Recursively process the path tree */
- plan = create_plan_recurse(root, best_path);
+ /* Recursively process the path tree, demanding the correct tlist result */
+ plan = create_plan_recurse(root, best_path, CP_EXACT_TLIST);
+
+ /*
+ * Make sure the topmost plan node's targetlist exposes the original
+ * column names and other decorative info. Targetlists generated within
+ * the planner don't bother with that stuff, but we must have it on the
+ * top-level tlist seen at execution time. However, ModifyTable plan
+ * nodes don't have a tlist matching the querytree targetlist.
+ */
+ if (!IsA(plan, ModifyTable))
+ apply_tlist_labeling(plan->targetlist, root->processed_tlist);
+
+ /*
+ * Attach any initPlans created in this query level to the topmost plan
+ * node. (In principle the initplans could go in any plan node at or
+ * above where they're referenced, but there seems no reason to put them
+ * any lower than the topmost node for the query level. Also, see
+ * comments for SS_finalize_plan before you try to change this.)
+ */
+ SS_attach_initplans(root, plan);
/* Check we successfully assigned all NestLoopParams to plan nodes */
if (root->curOuterParams != NIL)
* Recursive guts of create_plan().
*/
static Plan *
-create_plan_recurse(PlannerInfo *root, Path *best_path)
+create_plan_recurse(PlannerInfo *root, Path *best_path, int flags)
{
Plan *plan;
case T_TidScan:
case T_SubqueryScan:
case T_FunctionScan:
+ case T_TableFuncScan:
case T_ValuesScan:
case T_CteScan:
case T_WorkTableScan:
case T_ForeignScan:
case T_CustomScan:
- plan = create_scan_plan(root, best_path);
+ plan = create_scan_plan(root, best_path, flags);
break;
case T_HashJoin:
case T_MergeJoin:
(MergeAppendPath *) best_path);
break;
case T_Result:
- plan = (Plan *) create_result_plan(root,
- (ResultPath *) best_path);
+ if (IsA(best_path, ProjectionPath))
+ {
+ plan = create_projection_plan(root,
+ (ProjectionPath *) best_path);
+ }
+ else if (IsA(best_path, MinMaxAggPath))
+ {
+ plan = (Plan *) create_minmaxagg_plan(root,
+ (MinMaxAggPath *) best_path);
+ }
+ else
+ {
+ Assert(IsA(best_path, ResultPath));
+ plan = (Plan *) create_result_plan(root,
+ (ResultPath *) best_path);
+ }
+ break;
+ case T_ProjectSet:
+ plan = (Plan *) create_project_set_plan(root,
+ (ProjectSetPath *) best_path);
break;
case T_Material:
plan = (Plan *) create_material_plan(root,
- (MaterialPath *) best_path);
+ (MaterialPath *) best_path,
+ flags);
break;
case T_Unique:
- plan = create_unique_plan(root,
- (UniquePath *) best_path);
+ if (IsA(best_path, UpperUniquePath))
+ {
+ plan = (Plan *) create_upper_unique_plan(root,
+ (UpperUniquePath *) best_path,
+ flags);
+ }
+ else
+ {
+ Assert(IsA(best_path, UniquePath));
+ plan = create_unique_plan(root,
+ (UniquePath *) best_path,
+ flags);
+ }
break;
case T_Gather:
plan = (Plan *) create_gather_plan(root,
(GatherPath *) best_path);
break;
+ case T_Sort:
+ plan = (Plan *) create_sort_plan(root,
+ (SortPath *) best_path,
+ flags);
+ break;
+ case T_Group:
+ plan = (Plan *) create_group_plan(root,
+ (GroupPath *) best_path);
+ break;
+ case T_Agg:
+ if (IsA(best_path, GroupingSetsPath))
+ plan = create_groupingsets_plan(root,
+ (GroupingSetsPath *) best_path);
+ else
+ {
+ Assert(IsA(best_path, AggPath));
+ plan = (Plan *) create_agg_plan(root,
+ (AggPath *) best_path);
+ }
+ break;
+ case T_WindowAgg:
+ plan = (Plan *) create_windowagg_plan(root,
+ (WindowAggPath *) best_path);
+ break;
+ case T_SetOp:
+ plan = (Plan *) create_setop_plan(root,
+ (SetOpPath *) best_path,
+ flags);
+ break;
+ case T_RecursiveUnion:
+ plan = (Plan *) create_recursiveunion_plan(root,
+ (RecursiveUnionPath *) best_path);
+ break;
+ case T_LockRows:
+ plan = (Plan *) create_lockrows_plan(root,
+ (LockRowsPath *) best_path,
+ flags);
+ break;
+ case T_ModifyTable:
+ plan = (Plan *) create_modifytable_plan(root,
+ (ModifyTablePath *) best_path);
+ break;
+ case T_Limit:
+ plan = (Plan *) create_limit_plan(root,
+ (LimitPath *) best_path,
+ flags);
+ break;
+ case T_GatherMerge:
+ plan = (Plan *) create_gather_merge_plan(root,
+ (GatherMergePath *) best_path);
+ break;
default:
elog(ERROR, "unrecognized node type: %d",
(int) best_path->pathtype);
* Create a scan plan for the parent relation of 'best_path'.
*/
static Plan *
-create_scan_plan(PlannerInfo *root, Path *best_path)
+create_scan_plan(PlannerInfo *root, Path *best_path, int flags)
{
RelOptInfo *rel = best_path->parent;
- List *tlist;
List *scan_clauses;
+ List *gating_clauses;
+ List *tlist;
Plan *plan;
+ /*
+ * 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.
+ */
+ 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
+ * clauses available from the outer relation(s).
+ *
+ * For paranoia's sake, don't modify the stored baserestrictinfo list.
+ */
+ if (best_path->param_info)
+ scan_clauses = list_concat(list_copy(scan_clauses),
+ best_path->param_info->ppi_clauses);
+
+ /*
+ * Detect whether we have any pseudoconstant quals to deal with. Then, if
+ * we'll need a gating Result node, it will be able to project, so there
+ * are no requirements on the child's tlist.
+ */
+ gating_clauses = get_gating_quals(root, scan_clauses);
+ if (gating_clauses)
+ flags = 0;
+
/*
* For table scans, rather than using the relation targetlist (which is
* 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. (Note that
- * planner.c may replace the tlist we generate here, forcing projection to
- * occur.)
+ * optimize away projection of the table tuples, if possible.
*/
- if (use_physical_tlist(root, rel))
+ 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, unless
+ * we don't care because the gating Result will handle it.
+ */
+ if (!gating_clauses)
+ apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget);
}
else
{
tlist = build_physical_tlist(root, rel);
- /* if fail because of dropped cols, use regular method */
if (tlist == NIL)
+ {
+ /* Failed because of dropped cols, so use regular method */
tlist = build_path_tlist(root, best_path);
+ }
+ else
+ {
+ /* As above, transfer sortgroupref data to replacement tlist */
+ if (!gating_clauses)
+ apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget);
+ }
}
}
else
tlist = build_path_tlist(root, best_path);
}
- /*
- * 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.
- */
- scan_clauses = rel->baserestrictinfo;
-
- /*
- * If this is a parameterized scan, we also need to enforce all the join
- * clauses available from the outer relation(s).
- *
- * For paranoia's sake, don't modify the stored baserestrictinfo list.
- */
- if (best_path->param_info)
- scan_clauses = list_concat(list_copy(scan_clauses),
- best_path->param_info->ppi_clauses);
-
switch (best_path->pathtype)
{
case T_SeqScan:
case T_SubqueryScan:
plan = (Plan *) create_subqueryscan_plan(root,
- best_path,
+ (SubqueryScanPath *) best_path,
tlist,
scan_clauses);
break;
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,
* gating Result node that evaluates the pseudoconstants as one-time
* quals.
*/
- if (root->hasPseudoConstantQuals)
- plan = create_gating_plan(root, plan, scan_clauses);
+ if (gating_clauses)
+ plan = create_gating_plan(root, best_path, plan, gating_clauses);
return plan;
}
/*
* Build a target list (ie, a list of TargetEntry) for the Path's output.
+ *
+ * This is almost just make_tlist_from_pathtarget(), but we also have to
+ * deal with replacing nestloop params.
*/
static List *
build_path_tlist(PlannerInfo *root, Path *path)
{
- RelOptInfo *rel = path->parent;
List *tlist = NIL;
+ Index *sortgrouprefs = path->pathtarget->sortgrouprefs;
int resno = 1;
ListCell *v;
- foreach(v, rel->reltargetlist)
+ foreach(v, path->pathtarget->exprs)
{
- /* Do we really need to copy here? Not sure */
- Node *node = (Node *) copyObject(lfirst(v));
+ Node *node = (Node *) lfirst(v);
+ TargetEntry *tle;
/*
* If it's a parameterized path, there might be lateral references in
if (path->param_info)
node = replace_nestloop_params(root, node);
- tlist = lappend(tlist, makeTargetEntry((Expr *) node,
- resno,
- NULL,
- false));
+ tle = makeTargetEntry((Expr *) node,
+ resno,
+ NULL,
+ false);
+ if (sortgrouprefs)
+ tle->ressortgroupref = sortgrouprefs[resno - 1];
+
+ tlist = lappend(tlist, tle);
resno++;
}
return tlist;
* rather than only those Vars actually referenced.
*/
static bool
-use_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
+use_physical_tlist(PlannerInfo *root, Path *path, int flags)
{
+ RelOptInfo *rel = path->parent;
int i;
ListCell *lc;
+ /*
+ * Forget it if either exact tlist or small tlist is demanded.
+ */
+ if (flags & (CP_EXACT_TLIST | CP_SMALL_TLIST))
+ return false;
+
/*
* 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;
/*
* Can't do it with inheritance cases either (mainly because Append
- * doesn't project).
+ * doesn't project; this test may be unnecessary now that
+ * create_append_plan instructs its children to return an exact tlist).
*/
if (rel->reloptkind != RELOPT_BASEREL)
return false;
return false;
}
+ /*
+ * Also, can't do it if CP_LABEL_TLIST is specified and path is requested
+ * 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.) 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)
+ {
+ Expr *expr = (Expr *) lfirst(lc);
+
+ if (path->pathtarget->sortgrouprefs[i])
+ {
+ if (expr && IsA(expr, Var))
+ {
+ int attno = ((Var *) expr)->varattno;
+
+ attno -= FirstLowInvalidHeapAttributeNumber;
+ if (bms_is_member(attno, sortgroupatts))
+ return false;
+ sortgroupatts = bms_add_member(sortgroupatts, attno);
+ }
+ else
+ return false;
+ }
+ i++;
+ }
+ }
+
return true;
}
/*
- * disuse_physical_tlist
- * Switch a plan node back to emitting only Vars actually referenced.
+ * get_gating_quals
+ * See if there are pseudoconstant quals in a node's quals list
*
- * If the plan node immediately above a scan would prefer to get only
- * needed Vars and not a physical tlist, it must call this routine to
- * undo the decision made by use_physical_tlist(). Currently, Hash, Sort,
- * and Material nodes want this, so they don't have to store useless columns.
+ * If the node's quals list includes any pseudoconstant quals,
+ * return just those quals.
*/
-static void
-disuse_physical_tlist(PlannerInfo *root, Plan *plan, Path *path)
+static List *
+get_gating_quals(PlannerInfo *root, List *quals)
{
- /* Only need to undo it for path types handled by create_scan_plan() */
- switch (path->pathtype)
- {
- case T_SeqScan:
- case T_SampleScan:
- case T_IndexScan:
- case T_IndexOnlyScan:
- case T_BitmapHeapScan:
- case T_TidScan:
- case T_SubqueryScan:
- case T_FunctionScan:
- case T_ValuesScan:
- case T_CteScan:
- case T_WorkTableScan:
- case T_ForeignScan:
- case T_CustomScan:
- plan->targetlist = build_path_tlist(root, path);
- break;
- default:
- break;
- }
+ /* No need to look if we know there are no pseudoconstants */
+ if (!root->hasPseudoConstantQuals)
+ return NIL;
+
+ /* Sort into desirable execution order while still in RestrictInfo form */
+ quals = order_qual_clauses(root, quals);
+
+ /* Pull out any pseudoconstant quals from the RestrictInfo list */
+ return extract_actual_clauses(quals, true);
}
/*
* create_gating_plan
* Deal with pseudoconstant qual clauses
*
- * If the node's quals list includes any pseudoconstant quals, put them
- * into a gating Result node atop the already-built plan. Otherwise,
- * return the plan as-is.
- *
- * Note that we don't change cost or size estimates when doing gating.
- * The costs of qual eval were already folded into the plan's startup cost.
- * Leaving the size alone amounts to assuming that the gating qual will
- * succeed, which is the conservative estimate for planning upper queries.
- * We certainly don't want to assume the output size is zero (unless the
- * gating qual is actually constant FALSE, and that case is dealt with in
- * clausesel.c). Interpolating between the two cases is silly, because
- * it doesn't reflect what will really happen at runtime, and besides which
- * in most cases we have only a very bad idea of the probability of the gating
- * qual being true.
+ * Add a gating Result node atop the already-built plan.
*/
static Plan *
-create_gating_plan(PlannerInfo *root, Plan *plan, List *quals)
+create_gating_plan(PlannerInfo *root, Path *path, Plan *plan,
+ List *gating_quals)
{
- List *pseudoconstants;
+ Plan *gplan;
- /* Sort into desirable execution order while still in RestrictInfo form */
- quals = order_qual_clauses(root, quals);
+ Assert(gating_quals);
- /* Pull out any pseudoconstant quals from the RestrictInfo list */
- pseudoconstants = extract_actual_clauses(quals, true);
+ /*
+ * Since we need a Result node anyway, always return the path's requested
+ * tlist; that's never a wrong choice, even if the parent node didn't ask
+ * for CP_EXACT_TLIST.
+ */
+ gplan = (Plan *) make_result(build_path_tlist(root, path),
+ (Node *) gating_quals,
+ plan);
- if (!pseudoconstants)
- return plan;
+ /*
+ * Notice that we don't change cost or size estimates when doing gating.
+ * The costs of qual eval were already included in the subplan's cost.
+ * Leaving the size alone amounts to assuming that the gating qual will
+ * succeed, which is the conservative estimate for planning upper queries.
+ * We certainly don't want to assume the output size is zero (unless the
+ * gating qual is actually constant FALSE, and that case is dealt with in
+ * clausesel.c). Interpolating between the two cases is silly, because it
+ * doesn't reflect what will really happen at runtime, and besides which
+ * in most cases we have only a very bad idea of the probability of the
+ * gating qual being true.
+ */
+ copy_plan_costsize(gplan, plan);
- return (Plan *) make_result(root,
- plan->targetlist,
- (Node *) pseudoconstants,
- plan);
+ return gplan;
}
/*
static Plan *
create_join_plan(PlannerInfo *root, JoinPath *best_path)
{
- Plan *outer_plan;
- Plan *inner_plan;
Plan *plan;
- Relids saveOuterRels = root->curOuterRels;
-
- outer_plan = create_plan_recurse(root, best_path->outerjoinpath);
-
- /* For a nestloop, include outer relids in curOuterRels for inner side */
- if (best_path->path.pathtype == T_NestLoop)
- root->curOuterRels = bms_union(root->curOuterRels,
- best_path->outerjoinpath->parent->relids);
-
- inner_plan = create_plan_recurse(root, best_path->innerjoinpath);
+ List *gating_clauses;
switch (best_path->path.pathtype)
{
case T_MergeJoin:
plan = (Plan *) create_mergejoin_plan(root,
- (MergePath *) best_path,
- outer_plan,
- inner_plan);
+ (MergePath *) best_path);
break;
case T_HashJoin:
plan = (Plan *) create_hashjoin_plan(root,
- (HashPath *) best_path,
- outer_plan,
- inner_plan);
+ (HashPath *) best_path);
break;
case T_NestLoop:
- /* Restore curOuterRels */
- bms_free(root->curOuterRels);
- root->curOuterRels = saveOuterRels;
-
plan = (Plan *) create_nestloop_plan(root,
- (NestPath *) best_path,
- outer_plan,
- inner_plan);
+ (NestPath *) best_path);
break;
default:
elog(ERROR, "unrecognized node type: %d",
* gating Result node that evaluates the pseudoconstants as one-time
* quals.
*/
- if (root->hasPseudoConstantQuals)
- plan = create_gating_plan(root, plan, best_path->joinrestrictinfo);
+ gating_clauses = get_gating_quals(root, best_path->joinrestrictinfo);
+ if (gating_clauses)
+ plan = create_gating_plan(root, (Path *) best_path, plan,
+ gating_clauses);
#ifdef NOT_USED
if (best_path->subpaths == NIL)
{
/* Generate a Result plan with constant-FALSE gating qual */
- return (Plan *) make_result(root,
- tlist,
+ Plan *plan;
+
+ plan = (Plan *) make_result(tlist,
(Node *) list_make1(makeBoolConst(false,
false)),
NULL);
+
+ copy_generic_path_info(plan, (Path *) best_path);
+
+ return plan;
}
/* Build the plan for each child */
foreach(subpaths, best_path->subpaths)
{
Path *subpath = (Path *) lfirst(subpaths);
+ Plan *subplan;
- subplans = lappend(subplans, create_plan_recurse(root, subpath));
+ /* Must insist that all children return the same tlist */
+ subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
+
+ subplans = lappend(subplans, subplan);
}
/*
plan = make_append(subplans, tlist);
+ copy_generic_path_info(&plan->plan, (Path *) best_path);
+
return (Plan *) plan;
}
plan->righttree = NULL;
/* Compute sort column info, and adjust MergeAppend's tlist as needed */
- (void) prepare_sort_from_pathkeys(root, plan, pathkeys,
+ (void) prepare_sort_from_pathkeys(plan, pathkeys,
best_path->path.parent->relids,
NULL,
true,
bool *nullsFirst;
/* Build the child plan */
- subplan = create_plan_recurse(root, subpath);
+ /* Must insist that all children return the same tlist */
+ subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
/* Compute sort column info, and adjust subplan's tlist as needed */
- subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
+ subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
subpath->parent->relids,
node->sortColIdx,
false,
/* Now, insert a Sort node if subplan isn't sufficiently ordered */
if (!pathkeys_contained_in(pathkeys, subpath->pathkeys))
- subplan = (Plan *) make_sort(root, subplan, numsortkeys,
+ {
+ Sort *sort = make_sort(subplan, numsortkeys,
sortColIdx, sortOperators,
- collations, nullsFirst,
- best_path->limit_tuples);
+ collations, nullsFirst);
+
+ label_sort_with_costsize(root, sort, best_path->limit_tuples);
+ subplan = (Plan *) sort;
+ }
subplans = lappend(subplans, subplan);
}
/*
* create_result_plan
* Create a Result plan for 'best_path'.
- * This is only used for the case of a query with an empty jointree.
+ * This is only used for degenerate cases, such as a query with an empty
+ * jointree.
*
* Returns a Plan node.
*/
static Result *
create_result_plan(PlannerInfo *root, ResultPath *best_path)
{
+ Result *plan;
List *tlist;
List *quals;
- /* The tlist will be installed later, since we have no RelOptInfo */
- Assert(best_path->path.parent == NULL);
- tlist = NIL;
+ tlist = build_path_tlist(root, &best_path->path);
/* best_path->quals is just bare clauses */
-
quals = order_qual_clauses(root, best_path->quals);
- return make_result(root, tlist, (Node *) quals, NULL);
+ plan = make_result(tlist, (Node *) quals, NULL);
+
+ copy_generic_path_info(&plan->plan, (Path *) 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;
}
/*
* Returns a Plan node.
*/
static Material *
-create_material_plan(PlannerInfo *root, MaterialPath *best_path)
+create_material_plan(PlannerInfo *root, MaterialPath *best_path, int flags)
{
Material *plan;
Plan *subplan;
- subplan = create_plan_recurse(root, best_path->subpath);
-
- /* We don't want any excess columns in the materialized tuples */
- disuse_physical_tlist(root, subplan, best_path->subpath);
+ /*
+ * We don't want any excess columns in the materialized tuples, so request
+ * a smaller tlist. Otherwise, since Material doesn't project, tlist
+ * requirements pass through.
+ */
+ subplan = create_plan_recurse(root, best_path->subpath,
+ flags | CP_SMALL_TLIST);
plan = make_material(subplan);
* Returns a Plan node.
*/
static Plan *
-create_unique_plan(PlannerInfo *root, UniquePath *best_path)
+create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags)
{
Plan *plan;
Plan *subplan;
int groupColPos;
ListCell *l;
- subplan = create_plan_recurse(root, best_path->subpath);
+ /* Unique doesn't project, so tlist requirements pass through */
+ subplan = create_plan_recurse(root, best_path->subpath, flags);
/* Done if we don't need to do any actual unique-ifying */
if (best_path->umethod == UNIQUE_PATH_NOOP)
*/
if (!is_projection_capable_plan(subplan) &&
!tlist_same_exprs(newtlist, subplan->targetlist))
- subplan = (Plan *) make_result(root, newtlist, NULL, subplan);
+ subplan = inject_projection_plan(subplan, newtlist);
else
subplan->targetlist = newtlist;
}
groupColIdx[groupColPos++] = tle->resno;
}
- if (best_path->umethod == UNIQUE_PATH_HASH)
+ if (best_path->umethod == UNIQUE_PATH_HASH)
+ {
+ Oid *groupOperators;
+
+ /*
+ * Get the hashable equality operators for the Agg node to use.
+ * Normally these are the same as the IN clause operators, but if
+ * those are cross-type operators then the equality operators are the
+ * ones for the IN clause operators' RHS datatype.
+ */
+ groupOperators = (Oid *) palloc(numGroupCols * sizeof(Oid));
+ groupColPos = 0;
+ foreach(l, in_operators)
+ {
+ Oid in_oper = lfirst_oid(l);
+ Oid eq_oper;
+
+ if (!get_compatible_hash_operators(in_oper, NULL, &eq_oper))
+ elog(ERROR, "could not find compatible hash operator for operator %u",
+ in_oper);
+ groupOperators[groupColPos++] = eq_oper;
+ }
+
+ /*
+ * Since the Agg node is going to project anyway, we can give it the
+ * minimum output tlist, without any stuff we might have added to the
+ * subplan tlist.
+ */
+ plan = (Plan *) make_agg(build_path_tlist(root, &best_path->path),
+ NIL,
+ AGG_HASHED,
+ AGGSPLIT_SIMPLE,
+ numGroupCols,
+ groupColIdx,
+ groupOperators,
+ NIL,
+ NIL,
+ best_path->path.rows,
+ subplan);
+ }
+ else
+ {
+ List *sortList = NIL;
+ Sort *sort;
+
+ /* Create an ORDER BY list to sort the input compatibly */
+ groupColPos = 0;
+ foreach(l, in_operators)
+ {
+ Oid in_oper = lfirst_oid(l);
+ Oid sortop;
+ Oid eqop;
+ TargetEntry *tle;
+ SortGroupClause *sortcl;
+
+ sortop = get_ordering_op_for_equality_op(in_oper, false);
+ if (!OidIsValid(sortop)) /* shouldn't happen */
+ elog(ERROR, "could not find ordering operator for equality operator %u",
+ in_oper);
+
+ /*
+ * The Unique node will need equality operators. Normally these
+ * are the same as the IN clause operators, but if those are
+ * cross-type operators then the equality operators are the ones
+ * for the IN clause operators' RHS datatype.
+ */
+ eqop = get_equality_op_for_ordering_op(sortop, NULL);
+ if (!OidIsValid(eqop)) /* shouldn't happen */
+ elog(ERROR, "could not find equality operator for ordering operator %u",
+ sortop);
+
+ tle = get_tle_by_resno(subplan->targetlist,
+ groupColIdx[groupColPos]);
+ Assert(tle != NULL);
+
+ sortcl = makeNode(SortGroupClause);
+ sortcl->tleSortGroupRef = assignSortGroupRef(tle,
+ subplan->targetlist);
+ sortcl->eqop = eqop;
+ sortcl->sortop = sortop;
+ sortcl->nulls_first = false;
+ sortcl->hashable = false; /* no need to make this accurate */
+ sortList = lappend(sortList, sortcl);
+ groupColPos++;
+ }
+ sort = make_sort_from_sortclauses(sortList, subplan);
+ label_sort_with_costsize(root, sort, -1.0);
+ plan = (Plan *) make_unique_from_sortclauses((Plan *) sort, sortList);
+ }
+
+ /* Copy cost data from Path to Plan */
+ copy_generic_path_info(plan, &best_path->path);
+
+ return plan;
+}
+
+/*
+ * create_gather_plan
+ *
+ * Create a Gather plan for 'best_path' and (recursively) plans
+ * for its subpaths.
+ */
+static Gather *
+create_gather_plan(PlannerInfo *root, GatherPath *best_path)
+{
+ Gather *gather_plan;
+ Plan *subplan;
+ List *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);
+
+ tlist = build_path_tlist(root, &best_path->path);
+
+ gather_plan = make_gather(tlist,
+ NIL,
+ best_path->path.parallel_workers,
+ best_path->single_copy,
+ subplan);
+
+ copy_generic_path_info(&gather_plan->plan, &best_path->path);
+
+ /* use parallel mode for parallel plans. */
+ root->glob->parallelModeNeeded = true;
+
+ 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);
+
+ /* 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 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)
+{
+ Plan *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);
+
+ /*
+ * We might not really need a Result node here, either because the subplan
+ * can project or because it's returning the right list of expressions
+ * anyway. Usually create_projection_path will have detected that and set
+ * dummypp if we don't need a Result; but its decision can't be final,
+ * because 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.) So we have to recheck here. If we do
+ * arrive at a different answer 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 (is_projection_capable_path(best_path->subpath) ||
+ tlist_same_exprs(tlist, subplan->targetlist))
+ {
+ /* Don't need a separate Result, just assign tlist to subplan */
+ plan = subplan;
+ plan->targetlist = tlist;
+
+ /* 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;
+ plan->plan_rows = best_path->path.rows;
+ plan->plan_width = best_path->path.pathtarget->width;
+ /* ... but be careful not to munge 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);
+ }
+
+ return plan;
+}
+
+/*
+ * inject_projection_plan
+ * Insert a Result node to do a projection step.
+ *
+ * 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.
+ */
+static Plan *
+inject_projection_plan(Plan *subplan, List *tlist)
+{
+ Plan *plan;
+
+ plan = (Plan *) make_result(tlist, NULL, subplan);
+
+ /*
+ * In principle, we should charge tlist eval cost plus cpu_per_tuple per
+ * row for the Result node. But the former has probably been factored in
+ * already and the latter was not accounted for during Path construction,
+ * so being formally correct might just make the EXPLAIN output look less
+ * consistent not more so. Hence, just copy the subplan's cost.
+ */
+ copy_plan_costsize(plan, subplan);
+
+ return plan;
+}
+
+/*
+ * create_sort_plan
+ *
+ * Create a Sort plan for 'best_path' and (recursively) plans
+ * for its subpaths.
+ */
+static Sort *
+create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
+{
+ Sort *plan;
+ Plan *subplan;
+
+ /*
+ * We don't want any excess columns in the sorted tuples, so request a
+ * smaller tlist. Otherwise, since Sort doesn't project, tlist
+ * requirements pass through.
+ */
+ subplan = create_plan_recurse(root, best_path->subpath,
+ flags | CP_SMALL_TLIST);
+
+ plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys);
+
+ copy_generic_path_info(&plan->plan, (Path *) best_path);
+
+ return plan;
+}
+
+/*
+ * create_group_plan
+ *
+ * Create a Group plan for 'best_path' and (recursively) plans
+ * for its subpaths.
+ */
+static Group *
+create_group_plan(PlannerInfo *root, GroupPath *best_path)
+{
+ Group *plan;
+ Plan *subplan;
+ List *tlist;
+ List *quals;
+
+ /*
+ * Group can project, so no need to be terribly picky about child tlist,
+ * but we do need grouping columns to be available
+ */
+ subplan = create_plan_recurse(root, best_path->subpath, CP_LABEL_TLIST);
+
+ tlist = build_path_tlist(root, &best_path->path);
+
+ quals = order_qual_clauses(root, best_path->qual);
+
+ plan = make_group(tlist,
+ quals,
+ list_length(best_path->groupClause),
+ extract_grouping_cols(best_path->groupClause,
+ subplan->targetlist),
+ extract_grouping_ops(best_path->groupClause),
+ subplan);
+
+ copy_generic_path_info(&plan->plan, (Path *) best_path);
+
+ return plan;
+}
+
+/*
+ * create_upper_unique_plan
+ *
+ * Create a Unique plan for 'best_path' and (recursively) plans
+ * for its subpaths.
+ */
+static Unique *
+create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flags)
+{
+ Unique *plan;
+ Plan *subplan;
+
+ /*
+ * Unique doesn't project, so tlist requirements pass through; moreover we
+ * need grouping columns to be labeled.
+ */
+ subplan = create_plan_recurse(root, best_path->subpath,
+ flags | CP_LABEL_TLIST);
+
+ plan = make_unique_from_pathkeys(subplan,
+ best_path->path.pathkeys,
+ best_path->numkeys);
+
+ copy_generic_path_info(&plan->plan, (Path *) best_path);
+
+ return plan;
+}
+
+/*
+ * create_agg_plan
+ *
+ * Create an Agg plan for 'best_path' and (recursively) plans
+ * for its subpaths.
+ */
+static Agg *
+create_agg_plan(PlannerInfo *root, AggPath *best_path)
+{
+ Agg *plan;
+ Plan *subplan;
+ List *tlist;
+ List *quals;
+
+ /*
+ * Agg can project, so no need to be terribly picky about child tlist, but
+ * we do need grouping columns to be available
+ */
+ subplan = create_plan_recurse(root, best_path->subpath, CP_LABEL_TLIST);
+
+ tlist = build_path_tlist(root, &best_path->path);
+
+ quals = order_qual_clauses(root, best_path->qual);
+
+ plan = make_agg(tlist, quals,
+ best_path->aggstrategy,
+ best_path->aggsplit,
+ list_length(best_path->groupClause),
+ extract_grouping_cols(best_path->groupClause,
+ subplan->targetlist),
+ extract_grouping_ops(best_path->groupClause),
+ NIL,
+ NIL,
+ best_path->numGroups,
+ subplan);
+
+ copy_generic_path_info(&plan->plan, (Path *) best_path);
+
+ return plan;
+}
+
+/*
+ * Given a groupclause for a collection of grouping sets, produce the
+ * corresponding groupColIdx.
+ *
+ * root->grouping_map maps the tleSortGroupRef to the actual column position in
+ * the input tuple. So we get the ref from the entries in the groupclause and
+ * look them up there.
+ */
+static AttrNumber *
+remap_groupColIdx(PlannerInfo *root, List *groupClause)
+{
+ AttrNumber *grouping_map = root->grouping_map;
+ AttrNumber *new_grpColIdx;
+ ListCell *lc;
+ int i;
+
+ Assert(grouping_map);
+
+ new_grpColIdx = palloc0(sizeof(AttrNumber) * list_length(groupClause));
+
+ i = 0;
+ foreach(lc, groupClause)
+ {
+ SortGroupClause *clause = lfirst(lc);
+
+ new_grpColIdx[i++] = grouping_map[clause->tleSortGroupRef];
+ }
+
+ return new_grpColIdx;
+}
+
+/*
+ * create_groupingsets_plan
+ * Create a plan for 'best_path' and (recursively) plans
+ * for its subpaths.
+ *
+ * What we emit is an Agg plan with some vestigial Agg and Sort nodes
+ * hanging off the side. The top Agg implements the last grouping set
+ * specified in the GroupingSetsPath, and any additional grouping sets
+ * each give rise to a subsidiary Agg and Sort node in the top Agg's
+ * "chain" list. These nodes don't participate in the plan directly,
+ * but they are a convenient way to represent the required data for
+ * the extra steps.
+ *
+ * Returns a Plan node.
+ */
+static Plan *
+create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path)
+{
+ Agg *plan;
+ Plan *subplan;
+ List *rollup_groupclauses = best_path->rollup_groupclauses;
+ List *rollup_lists = best_path->rollup_lists;
+ AttrNumber *grouping_map;
+ int maxref;
+ List *chain;
+ ListCell *lc,
+ *lc2;
+
+ /* Shouldn't get here without grouping sets */
+ Assert(root->parse->groupingSets);
+ Assert(rollup_lists != NIL);
+ Assert(list_length(rollup_lists) == list_length(rollup_groupclauses));
+
+ /*
+ * Agg can project, so no need to be terribly picky about child tlist, but
+ * we do need grouping columns to be available
+ */
+ subplan = create_plan_recurse(root, best_path->subpath, CP_LABEL_TLIST);
+
+ /*
+ * 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)
+ {
+ SortGroupClause *gc = (SortGroupClause *) lfirst(lc);
+
+ if (gc->tleSortGroupRef > maxref)
+ maxref = gc->tleSortGroupRef;
+ }
+
+ grouping_map = (AttrNumber *) palloc0((maxref + 1) * sizeof(AttrNumber));
+
+ /* 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] = tle->resno;
+ }
+
+ /*
+ * During setrefs.c, we'll need the grouping_map to fix up the cols lists
+ * in GroupingFunc nodes. Save it for setrefs.c to use.
+ *
+ * This doesn't work if we're in an inheritance subtree (see notes in
+ * 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->grouping_map == NULL);
+ root->grouping_map = grouping_map;
+
+ /*
+ * Generate the side nodes that describe the other sort and group
+ * operations besides the top one. Note that we don't worry about putting
+ * accurate cost estimates in the side nodes; only the topmost Agg node's
+ * costs will be shown by EXPLAIN.
+ */
+ chain = NIL;
+ if (list_length(rollup_groupclauses) > 1)
+ {
+ forboth(lc, rollup_groupclauses, lc2, rollup_lists)
+ {
+ List *groupClause = (List *) lfirst(lc);
+ List *gsets = (List *) lfirst(lc2);
+ AttrNumber *new_grpColIdx;
+ Plan *sort_plan;
+ Plan *agg_plan;
+
+ /* We want to iterate over all but the last rollup list elements */
+ if (lnext(lc) == NULL)
+ break;
+
+ new_grpColIdx = remap_groupColIdx(root, groupClause);
+
+ sort_plan = (Plan *)
+ make_sort_from_groupcols(groupClause,
+ new_grpColIdx,
+ subplan);
+
+ agg_plan = (Plan *) make_agg(NIL,
+ NIL,
+ AGG_SORTED,
+ AGGSPLIT_SIMPLE,
+ list_length((List *) linitial(gsets)),
+ new_grpColIdx,
+ extract_grouping_ops(groupClause),
+ gsets,
+ NIL,
+ 0, /* numGroups not needed */
+ sort_plan);
+
+ /*
+ * Nuke stuff we don't need to avoid bloating debug output.
+ */
+ sort_plan->targetlist = NIL;
+ sort_plan->lefttree = NULL;
+
+ chain = lappend(chain, agg_plan);
+ }
+ }
+
+ /*
+ * Now make the final Agg node
+ */
+ {
+ List *groupClause = (List *) llast(rollup_groupclauses);
+ List *gsets = (List *) llast(rollup_lists);
+ AttrNumber *top_grpColIdx;
+ int numGroupCols;
+
+ top_grpColIdx = remap_groupColIdx(root, groupClause);
+
+ numGroupCols = list_length((List *) linitial(gsets));
+
+ plan = make_agg(build_path_tlist(root, &best_path->path),
+ best_path->qual,
+ (numGroupCols > 0) ? AGG_SORTED : AGG_PLAIN,
+ AGGSPLIT_SIMPLE,
+ numGroupCols,
+ top_grpColIdx,
+ extract_grouping_ops(groupClause),
+ gsets,
+ chain,
+ 0, /* numGroups not needed */
+ subplan);
+
+ /* Copy cost data from Path to Plan */
+ copy_generic_path_info(&plan->plan, &best_path->path);
+ }
+
+ return (Plan *) plan;
+}
+
+/*
+ * create_minmaxagg_plan
+ *
+ * Create a Result plan for 'best_path' and (recursively) plans
+ * for its subpaths.
+ */
+static Result *
+create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path)
+{
+ Result *plan;
+ List *tlist;
+ ListCell *lc;
+
+ /* Prepare an InitPlan for each aggregate's subquery. */
+ foreach(lc, best_path->mmaggregates)
+ {
+ MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc);
+ PlannerInfo *subroot = mminfo->subroot;
+ Query *subparse = subroot->parse;
+ Plan *plan;
+
+ /*
+ * Generate the plan for the subquery. We already have a Path, but we
+ * have to convert it to a Plan and attach a LIMIT node above it.
+ * Since we are entering a different planner context (subroot),
+ * recurse to create_plan not create_plan_recurse.
+ */
+ plan = create_plan(subroot, mminfo->path);
+
+ plan = (Plan *) make_limit(plan,
+ subparse->limitOffset,
+ subparse->limitCount);
+
+ /* Must apply correct cost/width data to Limit node */
+ plan->startup_cost = mminfo->path->startup_cost;
+ plan->total_cost = mminfo->pathcost;
+ plan->plan_rows = 1;
+ plan->plan_width = mminfo->path->pathtarget->width;
+ plan->parallel_aware = false;
+
+ /* Convert the plan into an InitPlan in the outer query. */
+ SS_make_initplan_from_plan(root, subroot, plan, mminfo->param);
+ }
+
+ /* Generate the output plan --- basically just a Result */
+ tlist = build_path_tlist(root, &best_path->path);
+
+ plan = make_result(tlist, (Node *) best_path->quals, NULL);
+
+ copy_generic_path_info(&plan->plan, (Path *) best_path);
+
+ /*
+ * During setrefs.c, we'll need to replace references to the Agg nodes
+ * with InitPlan output params. (We can't just do that locally in the
+ * MinMaxAgg node, because path nodes above here may have Agg references
+ * as well.) Save the mmaggregates list to tell setrefs.c to do that.
+ *
+ * This doesn't work if we're in an inheritance subtree (see notes in
+ * 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->minmax_aggs == NIL);
+ root->minmax_aggs = best_path->mmaggregates;
+
+ return plan;
+}
+
+/*
+ * create_windowagg_plan
+ *
+ * Create a WindowAgg plan for 'best_path' and (recursively) plans
+ * for its subpaths.
+ */
+static WindowAgg *
+create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path)
+{
+ WindowAgg *plan;
+ WindowClause *wc = best_path->winclause;
+ 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;
+
+ /*
+ * WindowAgg can project, so no need to be terribly picky about child
+ * tlist, but we do need grouping columns to be available
+ */
+ subplan = create_plan_recurse(root, best_path->subpath, CP_LABEL_TLIST);
+
+ 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.
+ */
+ 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);
+
+ /* And finally we can make the WindowAgg node */
+ plan = make_windowagg(tlist,
+ wc->winref,
+ partNumCols,
+ partColIdx,
+ partOperators,
+ ordNumCols,
+ ordColIdx,
+ ordOperators,
+ wc->frameOptions,
+ wc->startOffset,
+ wc->endOffset,
+ subplan);
+
+ copy_generic_path_info(&plan->plan, (Path *) 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
+ *
+ * Create a SetOp plan for 'best_path' and (recursively) plans
+ * for its subpaths.
+ */
+static SetOp *
+create_setop_plan(PlannerInfo *root, SetOpPath *best_path, int flags)
+{
+ SetOp *plan;
+ Plan *subplan;
+ long numGroups;
+
+ /*
+ * SetOp doesn't project, so tlist requirements pass through; moreover we
+ * need grouping columns to be labeled.
+ */
+ subplan = create_plan_recurse(root, best_path->subpath,
+ flags | CP_LABEL_TLIST);
+
+ /* Convert numGroups to long int --- but 'ware overflow! */
+ numGroups = (long) Min(best_path->numGroups, (double) LONG_MAX);
+
+ plan = make_setop(best_path->cmd,
+ best_path->strategy,
+ subplan,
+ best_path->distinctList,
+ best_path->flagColIdx,
+ best_path->firstFlag,
+ numGroups);
+
+ copy_generic_path_info(&plan->plan, (Path *) best_path);
+
+ return plan;
+}
+
+/*
+ * create_recursiveunion_plan
+ *
+ * Create a RecursiveUnion plan for 'best_path' and (recursively) plans
+ * for its subpaths.
+ */
+static RecursiveUnion *
+create_recursiveunion_plan(PlannerInfo *root, RecursiveUnionPath *best_path)
+{
+ RecursiveUnion *plan;
+ Plan *leftplan;
+ Plan *rightplan;
+ List *tlist;
+ long numGroups;
+
+ /* Need both children to produce same tlist, so force it */
+ leftplan = create_plan_recurse(root, best_path->leftpath, CP_EXACT_TLIST);
+ rightplan = create_plan_recurse(root, best_path->rightpath, CP_EXACT_TLIST);
+
+ tlist = build_path_tlist(root, &best_path->path);
+
+ /* Convert numGroups to long int --- but 'ware overflow! */
+ numGroups = (long) Min(best_path->numGroups, (double) LONG_MAX);
+
+ plan = make_recursive_union(tlist,
+ leftplan,
+ rightplan,
+ best_path->wtParam,
+ best_path->distinctList,
+ numGroups);
+
+ copy_generic_path_info(&plan->plan, (Path *) best_path);
+
+ return plan;
+}
+
+/*
+ * create_lockrows_plan
+ *
+ * Create a LockRows plan for 'best_path' and (recursively) plans
+ * for its subpaths.
+ */
+static LockRows *
+create_lockrows_plan(PlannerInfo *root, LockRowsPath *best_path,
+ int flags)
+{
+ LockRows *plan;
+ Plan *subplan;
+
+ /* LockRows doesn't project, so tlist requirements pass through */
+ subplan = create_plan_recurse(root, best_path->subpath, flags);
+
+ plan = make_lockrows(subplan, best_path->rowMarks, best_path->epqParam);
+
+ copy_generic_path_info(&plan->plan, (Path *) best_path);
+
+ return plan;
+}
+
+/*
+ * create_modifytable_plan
+ * Create a ModifyTable plan for 'best_path'.
+ *
+ * Returns a Plan node.
+ */
+static ModifyTable *
+create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path)
+{
+ ModifyTable *plan;
+ List *subplans = NIL;
+ ListCell *subpaths,
+ *subroots;
+
+ /* Build the plan for each input path */
+ forboth(subpaths, best_path->subpaths,
+ subroots, best_path->subroots)
{
- long numGroups;
- Oid *groupOperators;
-
- numGroups = (long) Min(best_path->path.rows, (double) LONG_MAX);
+ Path *subpath = (Path *) lfirst(subpaths);
+ PlannerInfo *subroot = (PlannerInfo *) lfirst(subroots);
+ Plan *subplan;
/*
- * Get the hashable equality operators for the Agg node to use.
- * Normally these are the same as the IN clause operators, but if
- * those are cross-type operators then the equality operators are the
- * ones for the IN clause operators' RHS datatype.
+ * In an inherited UPDATE/DELETE, reference the per-child modified
+ * subroot while creating Plans from Paths for the child rel. This is
+ * a kluge, but otherwise it's too hard to ensure that Plan creation
+ * functions (particularly in FDWs) don't depend on the contents of
+ * "root" matching what they saw at Path creation time. The main
+ * downside is that creation functions for Plans that might appear
+ * below a ModifyTable cannot expect to modify the contents of "root"
+ * and have it "stick" for subsequent processing such as setrefs.c.
+ * That's not great, but it seems better than the alternative.
*/
- groupOperators = (Oid *) palloc(numGroupCols * sizeof(Oid));
- groupColPos = 0;
- foreach(l, in_operators)
- {
- Oid in_oper = lfirst_oid(l);
- Oid eq_oper;
+ subplan = create_plan_recurse(subroot, subpath, CP_EXACT_TLIST);
- if (!get_compatible_hash_operators(in_oper, NULL, &eq_oper))
- elog(ERROR, "could not find compatible hash operator for operator %u",
- in_oper);
- groupOperators[groupColPos++] = eq_oper;
- }
+ /* Transfer resname/resjunk labeling, too, to keep executor happy */
+ apply_tlist_labeling(subplan->targetlist, subroot->processed_tlist);
- /*
- * Since the Agg node is going to project anyway, we can give it the
- * minimum output tlist, without any stuff we might have added to the
- * subplan tlist.
- */
- plan = (Plan *) make_agg(root,
- build_path_tlist(root, &best_path->path),
- NIL,
- AGG_HASHED,
- NULL,
- numGroupCols,
- groupColIdx,
- groupOperators,
- NIL,
- numGroups,
- subplan);
+ subplans = lappend(subplans, subplan);
}
- else
- {
- List *sortList = NIL;
-
- /* Create an ORDER BY list to sort the input compatibly */
- groupColPos = 0;
- foreach(l, in_operators)
- {
- Oid in_oper = lfirst_oid(l);
- Oid sortop;
- Oid eqop;
- TargetEntry *tle;
- SortGroupClause *sortcl;
-
- sortop = get_ordering_op_for_equality_op(in_oper, false);
- if (!OidIsValid(sortop)) /* shouldn't happen */
- elog(ERROR, "could not find ordering operator for equality operator %u",
- in_oper);
-
- /*
- * The Unique node will need equality operators. Normally these
- * are the same as the IN clause operators, but if those are
- * cross-type operators then the equality operators are the ones
- * for the IN clause operators' RHS datatype.
- */
- eqop = get_equality_op_for_ordering_op(sortop, NULL);
- if (!OidIsValid(eqop)) /* shouldn't happen */
- elog(ERROR, "could not find equality operator for ordering operator %u",
- sortop);
-
- tle = get_tle_by_resno(subplan->targetlist,
- groupColIdx[groupColPos]);
- Assert(tle != NULL);
- sortcl = makeNode(SortGroupClause);
- sortcl->tleSortGroupRef = assignSortGroupRef(tle,
- subplan->targetlist);
- sortcl->eqop = eqop;
- sortcl->sortop = sortop;
- sortcl->nulls_first = false;
- sortcl->hashable = false; /* no need to make this accurate */
- sortList = lappend(sortList, sortcl);
- groupColPos++;
- }
- plan = (Plan *) make_sort_from_sortclauses(root, sortList, subplan);
- plan = (Plan *) make_unique(plan, sortList);
- }
+ plan = make_modifytable(root,
+ best_path->operation,
+ best_path->canSetTag,
+ best_path->nominalRelation,
+ best_path->resultRelations,
+ subplans,
+ best_path->withCheckOptionLists,
+ best_path->returningLists,
+ best_path->rowMarks,
+ best_path->onconflict,
+ best_path->epqParam);
- /* Adjust output size estimate (other fields should be OK already) */
- plan->plan_rows = best_path->path.rows;
+ copy_generic_path_info(&plan->plan, &best_path->path);
return plan;
}
/*
- * create_gather_plan
+ * create_limit_plan
*
- * Create a Gather plan for 'best_path' and (recursively) plans
+ * Create a Limit plan for 'best_path' and (recursively) plans
* for its subpaths.
*/
-static Gather *
-create_gather_plan(PlannerInfo *root, GatherPath *best_path)
+static Limit *
+create_limit_plan(PlannerInfo *root, LimitPath *best_path, int flags)
{
- Gather *gather_plan;
+ Limit *plan;
Plan *subplan;
- subplan = create_plan_recurse(root, best_path->subpath);
-
- gather_plan = make_gather(subplan->targetlist,
- NIL,
- best_path->num_workers,
- best_path->single_copy,
- subplan);
+ /* Limit doesn't project, so tlist requirements pass through */
+ subplan = create_plan_recurse(root, best_path->subpath, flags);
- copy_generic_path_info(&gather_plan->plan, &best_path->path);
+ plan = make_limit(subplan,
+ best_path->limitOffset,
+ best_path->limitCount);
- /* use parallel mode for parallel plans. */
- root->glob->parallelModeNeeded = true;
+ copy_generic_path_info(&plan->plan, (Path *) best_path);
- return gather_plan;
+ return plan;
}
* 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 = castNode(RestrictInfo, lfirst(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))
+ continue; /* provably implied by indexquals */
qpqual = lappend(qpqual, rinfo);
}
&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
* 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 = castNode(RestrictInfo, lfirst(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))
+ continue; /* provably implied by indexquals */
qpqual = lappend(qpqual, rinfo);
}
plan->plan_rows =
clamp_row_est(apath->bitmapselectivity * apath->path.parent->tuples);
plan->plan_width = 0; /* meaningless */
+ plan->parallel_aware = false;
*qual = subquals;
*indexqual = subindexquals;
*indexECs = subindexECs;
plan->plan_rows =
clamp_row_est(opath->bitmapselectivity * opath->path.parent->tuples);
plan->plan_width = 0; /* meaningless */
+ plan->parallel_aware = false;
}
/*
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,
iscan->indexqual,
iscan->indexqualorig);
+ /* and set its cost/width fields appropriately */
plan->startup_cost = 0.0;
plan->total_cost = ipath->indextotalcost;
plan->plan_rows =
clamp_row_est(ipath->indexselectivity * ipath->path.parent->tuples);
plan->plan_width = 0; /* meaningless */
+ plan->parallel_aware = false;
*qual = get_actual_clauses(ipath->indexclauses);
*indexqual = get_actual_clauses(ipath->indexquals);
foreach(l, ipath->indexinfo->indpred)
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*/
static SubqueryScan *
-create_subqueryscan_plan(PlannerInfo *root, Path *best_path,
+create_subqueryscan_plan(PlannerInfo *root, SubqueryScanPath *best_path,
List *tlist, List *scan_clauses)
{
SubqueryScan *scan_plan;
- Index scan_relid = best_path->parent->relid;
+ RelOptInfo *rel = best_path->path.parent;
+ Index scan_relid = rel->relid;
+ Plan *subplan;
/* it should be a subquery base rel... */
Assert(scan_relid > 0);
- Assert(best_path->parent->rtekind == RTE_SUBQUERY);
+ Assert(rel->rtekind == RTE_SUBQUERY);
+
+ /*
+ * Recursively create Plan from Path for subquery. Since we are entering
+ * a different planner context (subroot), recurse to create_plan not
+ * create_plan_recurse.
+ */
+ subplan = create_plan(rel->subroot, best_path->subpath);
/* Sort clauses into best execution order */
scan_clauses = order_qual_clauses(root, scan_clauses);
scan_clauses = extract_actual_clauses(scan_clauses, false);
/* Replace any outer-relation variables with nestloop params */
- if (best_path->param_info)
+ if (best_path->path.param_info)
{
scan_clauses = (List *)
replace_nestloop_params(root, (Node *) scan_clauses);
process_subquery_nestloop_params(root,
- best_path->parent->subplan_params);
+ rel->subplan_params);
}
scan_plan = make_subqueryscan(tlist,
scan_clauses,
scan_relid,
- best_path->parent->subplan);
+ subplan);
- copy_generic_path_info(&scan_plan->scan.plan, best_path);
+ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path);
return scan_plan;
}
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'
RelOptInfo *rel = best_path->path.parent;
Index scan_relid = rel->relid;
Oid rel_oid = InvalidOid;
- Bitmapset *attrs_used = NULL;
Plan *outer_plan = NULL;
- ListCell *lc;
- int i;
Assert(rel->fdwroutine != NULL);
/* transform the child path if any */
if (best_path->fdw_outerpath)
- outer_plan = create_plan_recurse(root, best_path->fdw_outerpath);
+ outer_plan = create_plan_recurse(root, best_path->fdw_outerpath,
+ CP_EXACT_TLIST);
/*
* If we're scanning a base relation, fetch its OID. (Irrelevant if
/* 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 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 (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.
*/
}
/*
- * Detect whether any system columns are requested from rel. This is a
- * bit of a kluge and might go away someday, so we intentionally leave it
- * out of the API presented to FDWs.
- *
- * First, examine all the attributes needed for joins or final output.
- * Note: we must look at reltargetlist, not the attr_needed data, because
- * attr_needed isn't computed for inheritance child rels.
+ * If rel is a base relation, detect whether any system columns are
+ * requested from the rel. (If rel is a join relation, rel->relid will be
+ * 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.
*/
- pull_varattnos((Node *) rel->reltargetlist, rel->relid, &attrs_used);
-
- /* Add all the attributes used by restriction clauses. */
- foreach(lc, rel->baserestrictinfo)
+ scan_plan->fsSystemCol = false;
+ if (scan_relid > 0)
{
- RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+ Bitmapset *attrs_used = NULL;
+ ListCell *lc;
+ int i;
- pull_varattnos((Node *) rinfo->clause, rel->relid, &attrs_used);
- }
+ /*
+ * First, examine all the attributes needed for joins or final output.
+ * 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);
- /* Now, are any system columns requested from rel? */
- scan_plan->fsSystemCol = false;
- for (i = FirstLowInvalidHeapAttributeNumber + 1; i < 0; i++)
- {
- if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber, attrs_used))
+ /* Add all the attributes used by restriction clauses. */
+ foreach(lc, rel->baserestrictinfo)
{
- scan_plan->fsSystemCol = true;
- break;
+ RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+
+ pull_varattnos((Node *) rinfo->clause, scan_relid, &attrs_used);
+ }
+
+ /* Now, are any system columns requested from rel? */
+ for (i = FirstLowInvalidHeapAttributeNumber + 1; i < 0; i++)
+ {
+ if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber, attrs_used))
+ {
+ scan_plan->fsSystemCol = true;
+ break;
+ }
}
- }
- bms_free(attrs_used);
+ bms_free(attrs_used);
+ }
return scan_plan;
}
/* Recursively transform child paths. */
foreach(lc, best_path->custom_paths)
{
- Plan *plan = create_plan_recurse(root, (Path *) lfirst(lc));
+ Plan *plan = create_plan_recurse(root, (Path *) lfirst(lc),
+ CP_EXACT_TLIST);
custom_plans = lappend(custom_plans, plan);
}
* 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
static NestLoop *
create_nestloop_plan(PlannerInfo *root,
- NestPath *best_path,
- Plan *outer_plan,
- Plan *inner_plan)
+ NestPath *best_path)
{
NestLoop *join_plan;
+ Plan *outer_plan;
+ Plan *inner_plan;
List *tlist = build_path_tlist(root, &best_path->path);
List *joinrestrictclauses = best_path->joinrestrictinfo;
List *joinclauses;
List *otherclauses;
Relids outerrelids;
List *nestParams;
+ Relids saveOuterRels = root->curOuterRels;
ListCell *cell;
ListCell *prev;
ListCell *next;
+ /* NestLoop can project, so no need to be picky about child tlists */
+ outer_plan = create_plan_recurse(root, best_path->outerjoinpath, 0);
+
+ /* For a nestloop, include outer relids in curOuterRels for inner side */
+ root->curOuterRels = bms_union(root->curOuterRels,
+ best_path->outerjoinpath->parent->relids);
+
+ inner_plan = create_plan_recurse(root, best_path->innerjoinpath, 0);
+
+ /* Restore curOuterRels */
+ bms_free(root->curOuterRels);
+ root->curOuterRels = saveOuterRels;
+
/* Sort join qual clauses into best execution order */
joinrestrictclauses = order_qual_clauses(root, joinrestrictclauses);
static MergeJoin *
create_mergejoin_plan(PlannerInfo *root,
- MergePath *best_path,
- Plan *outer_plan,
- Plan *inner_plan)
+ MergePath *best_path)
{
+ MergeJoin *join_plan;
+ Plan *outer_plan;
+ Plan *inner_plan;
List *tlist = build_path_tlist(root, &best_path->jpath.path);
List *joinclauses;
List *otherclauses;
Oid *mergecollations;
int *mergestrategies;
bool *mergenullsfirst;
- MergeJoin *join_plan;
int i;
ListCell *lc;
ListCell *lop;
ListCell *lip;
+ /*
+ * MergeJoin can project, so we don't have to demand exact tlists from the
+ * inputs. However, if we're intending to sort an input's result, it's
+ * best to request a small tlist so we aren't sorting more data than
+ * necessary.
+ */
+ outer_plan = create_plan_recurse(root, best_path->jpath.outerjoinpath,
+ (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);
+
/* Sort join qual clauses into best execution order */
/* NB: do NOT reorder the mergeclauses */
joinclauses = order_qual_clauses(root, best_path->jpath.joinrestrictinfo);
/*
* Create explicit sort nodes for the outer and inner paths if necessary.
- * Make sure there are no excess columns in the inputs if sorting.
*/
if (best_path->outersortkeys)
{
- disuse_physical_tlist(root, outer_plan, best_path->jpath.outerjoinpath);
- outer_plan = (Plan *)
- make_sort_from_pathkeys(root,
- outer_plan,
- best_path->outersortkeys,
- -1.0);
+ Sort *sort = make_sort_from_pathkeys(outer_plan,
+ best_path->outersortkeys);
+
+ label_sort_with_costsize(root, sort, -1.0);
+ outer_plan = (Plan *) sort;
outerpathkeys = best_path->outersortkeys;
}
else
if (best_path->innersortkeys)
{
- disuse_physical_tlist(root, inner_plan, best_path->jpath.innerjoinpath);
- inner_plan = (Plan *)
- make_sort_from_pathkeys(root,
- inner_plan,
- best_path->innersortkeys,
- -1.0);
+ Sort *sort = make_sort_from_pathkeys(inner_plan,
+ best_path->innersortkeys);
+
+ label_sort_with_costsize(root, sort, -1.0);
+ inner_plan = (Plan *) sort;
innerpathkeys = best_path->innersortkeys;
}
else
i = 0;
foreach(lc, best_path->path_mergeclauses)
{
- RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+ RestrictInfo *rinfo = castNode(RestrictInfo, lfirst(lc));
EquivalenceClass *oeclass;
EquivalenceClass *ieclass;
PathKey *opathkey;
ListCell *l2;
/* fetch outer/inner eclass from mergeclause */
- Assert(IsA(rinfo, RestrictInfo));
if (rinfo->outer_is_left)
{
oeclass = rinfo->left_ec;
static HashJoin *
create_hashjoin_plan(PlannerInfo *root,
- HashPath *best_path,
- Plan *outer_plan,
- Plan *inner_plan)
+ HashPath *best_path)
{
+ HashJoin *join_plan;
+ Hash *hash_plan;
+ Plan *outer_plan;
+ Plan *inner_plan;
List *tlist = build_path_tlist(root, &best_path->jpath.path);
List *joinclauses;
List *otherclauses;
bool skewInherit = false;
Oid skewColType = InvalidOid;
int32 skewColTypmod = -1;
- HashJoin *join_plan;
- Hash *hash_plan;
+
+ /*
+ * HashJoin can project, so we don't have to demand exact tlists from the
+ * inputs. However, it's best to request a small tlist from the inner
+ * side, so that we aren't storing more data than necessary. Likewise, if
+ * we anticipate batching, request a small tlist from the outer side so
+ * 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);
+
+ inner_plan = create_plan_recurse(root, best_path->jpath.innerjoinpath,
+ CP_SMALL_TLIST);
/* Sort join qual clauses into best execution order */
joinclauses = order_qual_clauses(root, best_path->jpath.joinrestrictinfo);
hashclauses = get_switched_clauses(best_path->path_hashclauses,
best_path->jpath.outerjoinpath->parent->relids);
- /* We don't want any excess columns in the hashed tuples */
- disuse_physical_tlist(root, inner_plan, best_path->jpath.innerjoinpath);
-
- /* If we expect batching, suppress excess columns in outer tuples too */
- if (best_path->num_batches > 1)
- disuse_physical_tlist(root, outer_plan, best_path->jpath.outerjoinpath);
-
/*
* If there is a single join clause and we can identify the outer variable
* as a simple column reference, supply its identity for possible use in
skewInherit,
skewColType,
skewColTypmod);
+
+ /*
+ * Set Hash node's startup & total costs equal to total cost of input
+ * plan; this only affects EXPLAIN display not decisions.
+ */
+ copy_plan_costsize(&hash_plan->plan, inner_plan);
+ hash_plan->plan.startup_cost = hash_plan->plan.total_cost;
+
join_plan = make_hashjoin(tlist,
joinclauses,
otherclauses,
forboth(lcc, index_path->indexquals, lci, index_path->indexqualcols)
{
- RestrictInfo *rinfo = (RestrictInfo *) lfirst(lcc);
+ RestrictInfo *rinfo = castNode(RestrictInfo, lfirst(lcc));
int indexcol = lfirst_int(lci);
Node *clause;
- Assert(IsA(rinfo, RestrictInfo));
-
/*
* Replace any outer-relation variables with nestloop params.
*
}
}
- /* Ooops... */
+ /* Oops... */
elog(ERROR, "index key does not match expected index column");
return NULL; /* keep compiler quiet */
}
* 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)
{
Node *clause;
Cost cost;
+ Index security_level;
} QualItem;
int nitems = list_length(clauses);
QualItem *items;
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++;
}
/* 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;
}
/*
* 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-aware flag, which the executor *will* use.
*/
static void
copy_generic_path_info(Plan *dest, Path *src)
{
- if (src)
- {
- dest->startup_cost = src->startup_cost;
- dest->total_cost = src->total_cost;
- dest->plan_rows = src->rows;
- dest->plan_width = src->parent->width;
- dest->parallel_aware = src->parallel_aware;
- }
- else
- {
- dest->startup_cost = 0;
- dest->total_cost = 0;
- dest->plan_rows = 0;
- dest->plan_width = 0;
- dest->parallel_aware = false;
- }
+ dest->startup_cost = src->startup_cost;
+ dest->total_cost = src->total_cost;
+ dest->plan_rows = src->rows;
+ dest->plan_width = src->pathtarget->width;
+ dest->parallel_aware = src->parallel_aware;
+}
+
+/*
+ * Copy cost and size info from a lower plan node to an inserted node.
+ * (Most callers alter the info after copying it.)
+ */
+static void
+copy_plan_costsize(Plan *dest, Plan *src)
+{
+ dest->startup_cost = src->startup_cost;
+ dest->total_cost = src->total_cost;
+ dest->plan_rows = src->plan_rows;
+ dest->plan_width = src->plan_width;
+ /* Assume the inserted node is not parallel-aware. */
+ dest->parallel_aware = false;
+}
+
+/*
+ * Some places in this file build Sort nodes that don't have a directly
+ * corresponding Path node. The cost of the sort is, or should have been,
+ * included in the cost of the Path node we're working from, but since it's
+ * not split out, we have to re-figure it using cost_sort(). This is just
+ * to label the Sort node nicely for EXPLAIN.
+ *
+ * limit_tuples is as for cost_sort (in particular, pass -1 if no limit)
+ */
+static void
+label_sort_with_costsize(PlannerInfo *root, Sort *plan, double limit_tuples)
+{
+ Plan *lefttree = plan->plan.lefttree;
+ Path sort_path; /* dummy for result of cost_sort */
+
+ cost_sort(&sort_path, root, NIL,
+ lefttree->total_cost,
+ lefttree->plan_rows,
+ lefttree->plan_width,
+ 0.0,
+ work_mem,
+ limit_tuples);
+ plan->plan.startup_cost = sort_path.startup_cost;
+ plan->plan.total_cost = sort_path.total_cost;
+ plan->plan.plan_rows = lefttree->plan_rows;
+ plan->plan.plan_width = lefttree->plan_width;
+ plan->plan.parallel_aware = false;
}
/*
- * Copy cost and size info from a lower plan node to an inserted node.
- * (Most callers alter the info after copying it.)
+ * bitmap_subplan_mark_shared
+ * Set isshared flag in bitmap subplan so that it will be created in
+ * shared memory.
*/
static void
-copy_plan_costsize(Plan *dest, Plan *src)
+bitmap_subplan_mark_shared(Plan *plan)
{
- if (src)
- {
- dest->startup_cost = src->startup_cost;
- dest->total_cost = src->total_cost;
- dest->plan_rows = src->plan_rows;
- dest->plan_width = src->plan_width;
- }
+ if (IsA(plan, BitmapAnd))
+ bitmap_subplan_mark_shared(
+ linitial(((BitmapAnd *) plan)->bitmapplans));
+ else if (IsA(plan, BitmapOr))
+ ((BitmapOr *) plan)->isshared = true;
+ else if (IsA(plan, BitmapIndexScan))
+ ((BitmapIndexScan *) plan)->isshared = true;
else
- {
- dest->startup_cost = 0;
- dest->total_cost = 0;
- dest->plan_rows = 0;
- dest->plan_width = 0;
- }
+ elog(ERROR, "unrecognized node type: %d", nodeTag(plan));
}
-
/*****************************************************************************
*
* PLAN NODE BUILDING ROUTINES
*
- * Some of these are exported because they are called to build plan nodes
- * in contexts where we're not deriving the plan node from a path node.
+ * In general, these functions are not passed the original Path and therefore
+ * leave it to the caller to fill in the cost/width fields from the Path,
+ * typically by calling copy_generic_path_info(). This convention is
+ * somewhat historical, but it does support a few places above where we build
+ * a plan node without having an exactly corresponding Path node. Under no
+ * circumstances should one of these functions do its own cost calculations,
+ * as that would be redundant with calculations done while building Paths.
*
*****************************************************************************/
SeqScan *node = makeNode(SeqScan);
Plan *plan = &node->plan;
- /* cost should be inserted by caller */
plan->targetlist = qptlist;
plan->qual = qpqual;
plan->lefttree = NULL;
SampleScan *node = makeNode(SampleScan);
Plan *plan = &node->scan.plan;
- /* cost should be inserted by caller */
plan->targetlist = qptlist;
plan->qual = qpqual;
plan->lefttree = NULL;
IndexScan *node = makeNode(IndexScan);
Plan *plan = &node->scan.plan;
- /* cost should be inserted by caller */
plan->targetlist = qptlist;
plan->qual = qpqual;
plan->lefttree = NULL;
IndexOnlyScan *node = makeNode(IndexOnlyScan);
Plan *plan = &node->scan.plan;
- /* cost should be inserted by caller */
plan->targetlist = qptlist;
plan->qual = qpqual;
plan->lefttree = NULL;
BitmapIndexScan *node = makeNode(BitmapIndexScan);
Plan *plan = &node->scan.plan;
- /* cost should be inserted by caller */
plan->targetlist = NIL; /* not used */
plan->qual = NIL; /* not used */
plan->lefttree = NULL;
BitmapHeapScan *node = makeNode(BitmapHeapScan);
Plan *plan = &node->scan.plan;
- /* cost should be inserted by caller */
plan->targetlist = qptlist;
plan->qual = qpqual;
plan->lefttree = lefttree;
TidScan *node = makeNode(TidScan);
Plan *plan = &node->scan.plan;
- /* cost should be inserted by caller */
plan->targetlist = qptlist;
plan->qual = qpqual;
plan->lefttree = NULL;
return node;
}
-SubqueryScan *
+static SubqueryScan *
make_subqueryscan(List *qptlist,
List *qpqual,
Index scanrelid,
SubqueryScan *node = makeNode(SubqueryScan);
Plan *plan = &node->scan.plan;
- /*
- * Cost is figured here for the convenience of prepunion.c. Note this is
- * only correct for the case where qpqual is empty; otherwise caller
- * should overwrite cost with a better estimate.
- */
- copy_plan_costsize(plan, subplan);
- plan->total_cost += cpu_tuple_cost * subplan->plan_rows;
-
plan->targetlist = qptlist;
plan->qual = qpqual;
plan->lefttree = NULL;
FunctionScan *node = makeNode(FunctionScan);
Plan *plan = &node->scan.plan;
- /* cost should be inserted by caller */
plan->targetlist = qptlist;
plan->qual = qpqual;
plan->lefttree = NULL;
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,
ValuesScan *node = makeNode(ValuesScan);
Plan *plan = &node->scan.plan;
- /* cost should be inserted by caller */
plan->targetlist = qptlist;
plan->qual = qpqual;
plan->lefttree = NULL;
CteScan *node = makeNode(CteScan);
Plan *plan = &node->scan.plan;
- /* cost should be inserted by caller */
plan->targetlist = qptlist;
plan->qual = qpqual;
plan->lefttree = NULL;
WorkTableScan *node = makeNode(WorkTableScan);
Plan *plan = &node->scan.plan;
- /* cost should be inserted by caller */
plan->targetlist = qptlist;
plan->qual = qpqual;
plan->lefttree = NULL;
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;
return node;
}
-Append *
+static Append *
make_append(List *appendplans, List *tlist)
{
Append *node = makeNode(Append);
Plan *plan = &node->plan;
- double total_size;
- ListCell *subnode;
-
- /*
- * Compute cost as sum of subplan costs. We charge nothing extra for the
- * Append itself, which perhaps is too optimistic, but since it doesn't do
- * any selection or projection, it is a pretty cheap node.
- *
- * If you change this, see also create_append_path(). Also, the size
- * calculations should match set_append_rel_pathlist(). It'd be better
- * not to duplicate all this logic, but some callers of this function
- * aren't working from an appendrel or AppendPath, so there's noplace to
- * copy the data from.
- */
- plan->startup_cost = 0;
- plan->total_cost = 0;
- plan->plan_rows = 0;
- total_size = 0;
- foreach(subnode, appendplans)
- {
- Plan *subplan = (Plan *) lfirst(subnode);
-
- if (subnode == list_head(appendplans)) /* first node? */
- plan->startup_cost = subplan->startup_cost;
- plan->total_cost += subplan->total_cost;
- plan->plan_rows += subplan->plan_rows;
- total_size += subplan->plan_width * subplan->plan_rows;
- }
- if (plan->plan_rows > 0)
- plan->plan_width = rint(total_size / plan->plan_rows);
- else
- plan->plan_width = 0;
plan->targetlist = tlist;
plan->qual = NIL;
return node;
}
-RecursiveUnion *
+static RecursiveUnion *
make_recursive_union(List *tlist,
Plan *lefttree,
Plan *righttree,
Plan *plan = &node->plan;
int numCols = list_length(distinctList);
- cost_recursive_union(plan, lefttree, righttree);
-
plan->targetlist = tlist;
plan->qual = NIL;
plan->lefttree = lefttree;
BitmapAnd *node = makeNode(BitmapAnd);
Plan *plan = &node->plan;
- /* cost should be inserted by caller */
plan->targetlist = NIL;
plan->qual = NIL;
plan->lefttree = NULL;
BitmapOr *node = makeNode(BitmapOr);
Plan *plan = &node->plan;
- /* cost should be inserted by caller */
plan->targetlist = NIL;
plan->qual = NIL;
plan->lefttree = NULL;
NestLoop *node = makeNode(NestLoop);
Plan *plan = &node->join.plan;
- /* cost should be inserted by caller */
plan->targetlist = tlist;
plan->qual = otherclauses;
plan->lefttree = lefttree;
HashJoin *node = makeNode(HashJoin);
Plan *plan = &node->join.plan;
- /* cost should be inserted by caller */
plan->targetlist = tlist;
plan->qual = otherclauses;
plan->lefttree = lefttree;
Hash *node = makeNode(Hash);
Plan *plan = &node->plan;
- copy_plan_costsize(plan, lefttree);
-
- /*
- * For plausibility, make startup & total costs equal total cost of input
- * plan; this only affects EXPLAIN display not decisions.
- */
- plan->startup_cost = plan->total_cost;
plan->targetlist = lefttree->targetlist;
plan->qual = NIL;
plan->lefttree = lefttree;
MergeJoin *node = makeNode(MergeJoin);
Plan *plan = &node->join.plan;
- /* cost should be inserted by caller */
plan->targetlist = tlist;
plan->qual = otherclauses;
plan->lefttree = lefttree;
*
* Caller must have built the sortColIdx, sortOperators, collations, and
* nullsFirst arrays already.
- * limit_tuples is as for cost_sort (in particular, pass -1 if no limit)
*/
static Sort *
-make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
+make_sort(Plan *lefttree, int numCols,
AttrNumber *sortColIdx, Oid *sortOperators,
- Oid *collations, bool *nullsFirst,
- double limit_tuples)
+ Oid *collations, bool *nullsFirst)
{
Sort *node = makeNode(Sort);
Plan *plan = &node->plan;
- Path sort_path; /* dummy for result of cost_sort */
- copy_plan_costsize(plan, lefttree); /* only care about copying size */
- cost_sort(&sort_path, root, NIL,
- lefttree->total_cost,
- lefttree->plan_rows,
- lefttree->plan_width,
- 0.0,
- work_mem,
- limit_tuples);
- plan->startup_cost = sort_path.startup_cost;
- plan->total_cost = sort_path.total_cost;
plan->targetlist = lefttree->targetlist;
plan->qual = NIL;
plan->lefttree = lefttree;
* 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
*
* 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
* or a Result stacked atop lefttree).
*/
static Plan *
-prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
Relids relids,
const AttrNumber *reqColIdx,
bool adjust_tlist_in_place,
* 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;
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)
{
{
/* copy needed so we don't modify input's tlist below */
tlist = copyObject(tlist);
- lefttree = (Plan *) make_result(root, tlist, NULL,
- lefttree);
+ lefttree = inject_projection_plan(lefttree, tlist);
}
/* Don't bother testing is_projection_capable_plan again */
*
* 'lefttree' is the node which yields input tuples
* 'pathkeys' is the list of pathkeys by which the result is to be sorted
- * 'limit_tuples' is the bound on the number of output tuples;
- * -1 if no bound
*/
-Sort *
-make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
- double limit_tuples)
+static Sort *
+make_sort_from_pathkeys(Plan *lefttree, List *pathkeys)
{
int numsortkeys;
AttrNumber *sortColIdx;
bool *nullsFirst;
/* Compute sort column info, and adjust lefttree as needed */
- lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
+ lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
NULL,
NULL,
false,
&nullsFirst);
/* Now build the Sort node */
- return make_sort(root, lefttree, numsortkeys,
- sortColIdx, sortOperators, collations,
- nullsFirst, limit_tuples);
+ return make_sort(lefttree, numsortkeys,
+ sortColIdx, sortOperators,
+ collations, nullsFirst);
}
/*
* 'lefttree' is the node which yields input tuples
*/
Sort *
-make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree)
+make_sort_from_sortclauses(List *sortcls, Plan *lefttree)
{
List *sub_tlist = lefttree->targetlist;
ListCell *l;
numsortkeys++;
}
- return make_sort(root, lefttree, numsortkeys,
- sortColIdx, sortOperators, collations,
- nullsFirst, -1.0);
+ return make_sort(lefttree, numsortkeys,
+ sortColIdx, sortOperators,
+ collations, nullsFirst);
}
/*
* appropriate to the grouping node. So, only the sort ordering info
* is used from the SortGroupClause entries.
*/
-Sort *
-make_sort_from_groupcols(PlannerInfo *root,
- List *groupcls,
+static Sort *
+make_sort_from_groupcols(List *groupcls,
AttrNumber *grpColIdx,
Plan *lefttree)
{
numsortkeys++;
}
- return make_sort(root, lefttree, numsortkeys,
- sortColIdx, sortOperators, collations,
- nullsFirst, -1.0);
+ return make_sort(lefttree, numsortkeys,
+ sortColIdx, sortOperators,
+ collations, nullsFirst);
}
static Material *
Material *node = makeNode(Material);
Plan *plan = &node->plan;
- /* cost should be inserted by caller */
plan->targetlist = lefttree->targetlist;
plan->qual = NIL;
plan->lefttree = lefttree;
* materialize_finished_plan: stick a Material node atop a completed plan
*
* There are a couple of places where we want to attach a Material node
- * after completion of subquery_planner(), without any MaterialPath path.
+ * after completion of create_plan(), without any MaterialPath path.
+ * Those places should probably be refactored someday to do this on the
+ * Path representation, but it's not worth the trouble yet.
*/
Plan *
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,
matplan->total_cost = matpath.total_cost;
matplan->plan_rows = subplan->plan_rows;
matplan->plan_width = subplan->plan_width;
+ matplan->parallel_aware = false;
return matplan;
}
Agg *
-make_agg(PlannerInfo *root, List *tlist, List *qual,
- AggStrategy aggstrategy, const AggClauseCosts *aggcosts,
+make_agg(List *tlist, List *qual,
+ AggStrategy aggstrategy, AggSplit aggsplit,
int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators,
- List *groupingSets,
- long numGroups,
- Plan *lefttree)
+ List *groupingSets, List *chain,
+ double dNumGroups, Plan *lefttree)
{
Agg *node = makeNode(Agg);
Plan *plan = &node->plan;
- Path agg_path; /* dummy for result of cost_agg */
- QualCost qual_cost;
+ long numGroups;
+
+ /* Reduce to long, but 'ware overflow! */
+ numGroups = (long) Min(dNumGroups, (double) LONG_MAX);
node->aggstrategy = aggstrategy;
+ node->aggsplit = aggsplit;
node->numCols = numGroupCols;
node->grpColIdx = grpColIdx;
node->grpOperators = grpOperators;
node->numGroups = numGroups;
-
- copy_plan_costsize(plan, lefttree); /* only care about copying size */
- cost_agg(&agg_path, root,
- aggstrategy, aggcosts,
- numGroupCols, numGroups,
- lefttree->startup_cost,
- lefttree->total_cost,
- lefttree->plan_rows);
- plan->startup_cost = agg_path.startup_cost;
- plan->total_cost = agg_path.total_cost;
-
- /*
- * We will produce a single output tuple if not grouping, and a tuple per
- * group otherwise.
- */
- if (aggstrategy == AGG_PLAIN)
- plan->plan_rows = groupingSets ? list_length(groupingSets) : 1;
- else
- plan->plan_rows = numGroups;
-
+ node->aggParams = NULL; /* SS_finalize_plan() will fill this */
node->groupingSets = groupingSets;
-
- /*
- * We also need to account for the cost of evaluation of the qual (ie, the
- * HAVING clause) and the tlist. Note that cost_qual_eval doesn't charge
- * anything for Aggref nodes; this is okay since they are really
- * comparable to Vars.
- *
- * See notes in add_tlist_costs_to_plan about why only make_agg,
- * make_windowagg and make_group worry about tlist eval cost.
- */
- if (qual)
- {
- cost_qual_eval(&qual_cost, qual, root);
- plan->startup_cost += qual_cost.startup;
- plan->total_cost += qual_cost.startup;
- plan->total_cost += qual_cost.per_tuple * plan->plan_rows;
- }
- add_tlist_costs_to_plan(root, plan, tlist);
+ node->chain = chain;
plan->qual = qual;
plan->targetlist = tlist;
-
plan->lefttree = lefttree;
plan->righttree = NULL;
return node;
}
-WindowAgg *
-make_windowagg(PlannerInfo *root, List *tlist,
- List *windowFuncs, Index winref,
+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,
{
WindowAgg *node = makeNode(WindowAgg);
Plan *plan = &node->plan;
- Path windowagg_path; /* dummy for result of cost_windowagg */
node->winref = winref;
node->partNumCols = partNumCols;
node->startOffset = startOffset;
node->endOffset = endOffset;
- copy_plan_costsize(plan, lefttree); /* only care about copying size */
- cost_windowagg(&windowagg_path, root,
- windowFuncs, partNumCols, ordNumCols,
- lefttree->startup_cost,
- lefttree->total_cost,
- lefttree->plan_rows);
- plan->startup_cost = windowagg_path.startup_cost;
- plan->total_cost = windowagg_path.total_cost;
-
- /*
- * We also need to account for the cost of evaluation of the tlist.
- *
- * See notes in add_tlist_costs_to_plan about why only make_agg,
- * make_windowagg and make_group worry about tlist eval cost.
- */
- add_tlist_costs_to_plan(root, plan, tlist);
-
plan->targetlist = tlist;
plan->lefttree = lefttree;
plan->righttree = NULL;
return node;
}
-Group *
-make_group(PlannerInfo *root,
- List *tlist,
+static Group *
+make_group(List *tlist,
List *qual,
int numGroupCols,
AttrNumber *grpColIdx,
Oid *grpOperators,
- double numGroups,
Plan *lefttree)
{
Group *node = makeNode(Group);
Plan *plan = &node->plan;
- Path group_path; /* dummy for result of cost_group */
- QualCost qual_cost;
node->numCols = numGroupCols;
node->grpColIdx = grpColIdx;
node->grpOperators = grpOperators;
- copy_plan_costsize(plan, lefttree); /* only care about copying size */
- cost_group(&group_path, root,
- numGroupCols, numGroups,
- lefttree->startup_cost,
- lefttree->total_cost,
- lefttree->plan_rows);
- plan->startup_cost = group_path.startup_cost;
- plan->total_cost = group_path.total_cost;
-
- /* One output tuple per estimated result group */
- plan->plan_rows = numGroups;
-
- /*
- * We also need to account for the cost of evaluation of the qual (ie, the
- * HAVING clause) and the tlist.
- *
- * XXX this double-counts the cost of evaluation of any expressions used
- * for grouping, since in reality those will have been evaluated at a
- * lower plan level and will only be copied by the Group node. Worth
- * fixing?
- *
- * See notes in add_tlist_costs_to_plan about why only make_agg,
- * make_windowagg and make_group worry about tlist eval cost.
- */
- if (qual)
- {
- cost_qual_eval(&qual_cost, qual, root);
- plan->startup_cost += qual_cost.startup;
- plan->total_cost += qual_cost.startup;
- plan->total_cost += qual_cost.per_tuple * plan->plan_rows;
- }
- add_tlist_costs_to_plan(root, plan, tlist);
-
plan->qual = qual;
plan->targetlist = tlist;
plan->lefttree = lefttree;
* that should be considered by the Unique filter. The input path must
* already be sorted accordingly.
*/
-Unique *
-make_unique(Plan *lefttree, List *distinctList)
+static Unique *
+make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
{
Unique *node = makeNode(Unique);
Plan *plan = &node->plan;
Oid *uniqOperators;
ListCell *slitem;
- copy_plan_costsize(plan, lefttree);
-
- /*
- * Charge one cpu_operator_cost per comparison per input tuple. We assume
- * all columns get compared at most of the tuples. (XXX probably this is
- * an overestimate.)
- */
- plan->total_cost += cpu_operator_cost * plan->plan_rows * numCols;
-
- /*
- * plan->plan_rows is left as a copy of the input subplan's plan_rows; ie,
- * we assume the filter removes nothing. The caller must alter this if he
- * has a better idea.
- */
-
plan->targetlist = lefttree->targetlist;
plan->qual = NIL;
plan->lefttree = lefttree;
return node;
}
+/*
+ * as above, but use pathkeys to identify the sort columns and semantics
+ */
+static Unique *
+make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+{
+ Unique *node = makeNode(Unique);
+ Plan *plan = &node->plan;
+ int keyno = 0;
+ AttrNumber *uniqColIdx;
+ Oid *uniqOperators;
+ ListCell *lc;
+
+ plan->targetlist = lefttree->targetlist;
+ plan->qual = NIL;
+ plan->lefttree = lefttree;
+ plan->righttree = NULL;
+
+ /*
+ * Convert pathkeys list into arrays of attr indexes and equality
+ * operators, as wanted by executor. This has a lot in common with
+ * prepare_sort_from_pathkeys ... maybe unify sometime?
+ */
+ Assert(numCols >= 0 && numCols <= list_length(pathkeys));
+ uniqColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
+ uniqOperators = (Oid *) palloc(sizeof(Oid) * numCols);
+
+ foreach(lc, pathkeys)
+ {
+ PathKey *pathkey = (PathKey *) lfirst(lc);
+ EquivalenceClass *ec = pathkey->pk_eclass;
+ EquivalenceMember *em;
+ TargetEntry *tle = NULL;
+ Oid pk_datatype = InvalidOid;
+ Oid eqop;
+ ListCell *j;
+
+ /* Ignore pathkeys beyond the specified number of columns */
+ if (keyno >= numCols)
+ break;
+
+ if (ec->ec_has_volatile)
+ {
+ /*
+ * If the pathkey's EquivalenceClass is volatile, then it must
+ * have come from an ORDER BY clause, and we have to match it to
+ * that same targetlist entry.
+ */
+ if (ec->ec_sortref == 0) /* can't happen */
+ elog(ERROR, "volatile EquivalenceClass has no sortref");
+ tle = get_sortgroupref_tle(ec->ec_sortref, plan->targetlist);
+ Assert(tle);
+ Assert(list_length(ec->ec_members) == 1);
+ pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
+ }
+ else
+ {
+ /*
+ * Otherwise, we can use any non-constant expression listed in the
+ * pathkey's EquivalenceClass. For now, we take the first tlist
+ * item found in the EC.
+ */
+ foreach(j, plan->targetlist)
+ {
+ tle = (TargetEntry *) lfirst(j);
+ em = find_ec_member_for_tle(ec, tle, NULL);
+ if (em)
+ {
+ /* found expr already in tlist */
+ pk_datatype = em->em_datatype;
+ break;
+ }
+ tle = NULL;
+ }
+ }
+
+ if (!tle)
+ elog(ERROR, "could not find pathkey item to sort");
+
+ /*
+ * Look up the correct equality operator from the PathKey's slightly
+ * abstracted representation.
+ */
+ eqop = get_opfamily_member(pathkey->pk_opfamily,
+ pk_datatype,
+ pk_datatype,
+ BTEqualStrategyNumber);
+ if (!OidIsValid(eqop)) /* should not happen */
+ elog(ERROR, "could not find member %d(%u,%u) of opfamily %u",
+ BTEqualStrategyNumber, pk_datatype, pk_datatype,
+ pathkey->pk_opfamily);
+
+ uniqColIdx[keyno] = tle->resno;
+ uniqOperators[keyno] = eqop;
+
+ keyno++;
+ }
+
+ node->numCols = numCols;
+ node->uniqColIdx = uniqColIdx;
+ node->uniqOperators = uniqOperators;
+
+ return node;
+}
+
static Gather *
make_gather(List *qptlist,
List *qpqual,
Gather *node = makeNode(Gather);
Plan *plan = &node->plan;
- /* cost should be inserted by caller */
plan->targetlist = qptlist;
plan->qual = qpqual;
plan->lefttree = subplan;
plan->righttree = NULL;
node->num_workers = nworkers;
node->single_copy = single_copy;
+ node->invisible = false;
return node;
}
* items that should be considered by the SetOp filter. The input path must
* already be sorted accordingly.
*/
-SetOp *
+static SetOp *
make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree,
List *distinctList, AttrNumber flagColIdx, int firstFlag,
- long numGroups, double outputRows)
+ long numGroups)
{
SetOp *node = makeNode(SetOp);
Plan *plan = &node->plan;
Oid *dupOperators;
ListCell *slitem;
- copy_plan_costsize(plan, lefttree);
- plan->plan_rows = outputRows;
-
- /*
- * Charge one cpu_operator_cost per comparison per input tuple. We assume
- * all columns get compared at most of the tuples.
- */
- plan->total_cost += cpu_operator_cost * lefttree->plan_rows * numCols;
-
plan->targetlist = lefttree->targetlist;
plan->qual = NIL;
plan->lefttree = lefttree;
* make_lockrows
* Build a LockRows plan node
*/
-LockRows *
+static LockRows *
make_lockrows(Plan *lefttree, List *rowMarks, int epqParam)
{
LockRows *node = makeNode(LockRows);
Plan *plan = &node->plan;
- copy_plan_costsize(plan, lefttree);
-
- /* charge cpu_tuple_cost to reflect locking costs (underestimate?) */
- plan->total_cost += cpu_tuple_cost * plan->plan_rows;
-
plan->targetlist = lefttree->targetlist;
plan->qual = NIL;
plan->lefttree = lefttree;
}
/*
- * Note: offset_est and count_est are passed in to save having to repeat
- * work already done to estimate the values of the limitOffset and limitCount
- * expressions. Their values are as returned by preprocess_limit (0 means
- * "not relevant", -1 means "couldn't estimate"). Keep the code below in sync
- * with that function!
+ * make_limit
+ * Build a Limit plan node
*/
Limit *
-make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount,
- int64 offset_est, int64 count_est)
+make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount)
{
Limit *node = makeNode(Limit);
Plan *plan = &node->plan;
- copy_plan_costsize(plan, lefttree);
-
- /*
- * Adjust the output rows count and costs according to the offset/limit.
- * This is only a cosmetic issue if we are at top level, but if we are
- * building a subquery then it's important to report correct info to the
- * outer planner.
- *
- * When the offset or count couldn't be estimated, use 10% of the
- * estimated number of rows emitted from the subplan.
- */
- if (offset_est != 0)
- {
- double offset_rows;
-
- if (offset_est > 0)
- offset_rows = (double) offset_est;
- else
- offset_rows = clamp_row_est(lefttree->plan_rows * 0.10);
- if (offset_rows > plan->plan_rows)
- offset_rows = plan->plan_rows;
- if (plan->plan_rows > 0)
- plan->startup_cost +=
- (plan->total_cost - plan->startup_cost)
- * offset_rows / plan->plan_rows;
- plan->plan_rows -= offset_rows;
- if (plan->plan_rows < 1)
- plan->plan_rows = 1;
- }
-
- if (count_est != 0)
- {
- double count_rows;
-
- if (count_est > 0)
- count_rows = (double) count_est;
- else
- count_rows = clamp_row_est(lefttree->plan_rows * 0.10);
- if (count_rows > plan->plan_rows)
- count_rows = plan->plan_rows;
- if (plan->plan_rows > 0)
- plan->total_cost = plan->startup_cost +
- (plan->total_cost - plan->startup_cost)
- * count_rows / plan->plan_rows;
- plan->plan_rows = count_rows;
- if (plan->plan_rows < 1)
- plan->plan_rows = 1;
- }
-
plan->targetlist = lefttree->targetlist;
plan->qual = NIL;
plan->lefttree = lefttree;
/*
* make_result
* Build a Result plan node
- *
- * If we have a subplan, assume that any evaluation costs for the gating qual
- * were already factored into the subplan's startup cost, and just copy the
- * subplan cost. If there's no subplan, we should include the qual eval
- * cost. In either case, tlist eval cost is not to be included here.
*/
-Result *
-make_result(PlannerInfo *root,
- List *tlist,
+static Result *
+make_result(List *tlist,
Node *resconstantqual,
Plan *subplan)
{
Result *node = makeNode(Result);
Plan *plan = &node->plan;
- if (subplan)
- copy_plan_costsize(plan, subplan);
- else
- {
- plan->startup_cost = 0;
- plan->total_cost = cpu_tuple_cost;
- plan->plan_rows = 1; /* wrong if we have a set-valued function? */
- plan->plan_width = 0; /* XXX is it worth being smarter? */
- if (resconstantqual)
- {
- QualCost qual_cost;
+ plan->targetlist = tlist;
+ plan->qual = NIL;
+ plan->lefttree = subplan;
+ plan->righttree = NULL;
+ node->resconstantqual = resconstantqual;
- cost_qual_eval(&qual_cost, (List *) resconstantqual, root);
- /* resconstantqual is evaluated once at startup */
- plan->startup_cost += qual_cost.startup + qual_cost.per_tuple;
- plan->total_cost += qual_cost.startup + qual_cost.per_tuple;
- }
- }
+ 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;
- node->resconstantqual = resconstantqual;
return node;
}
/*
* make_modifytable
* Build a ModifyTable plan node
- *
- * Currently, we don't charge anything extra for the actual table modification
- * work, nor for the WITH CHECK OPTIONS or RETURNING expressions if any. It
- * would only be window dressing, since these are always top-level nodes and
- * there is no way for the costs to change any higher-level planning choices.
- * But we might want to make it look better sometime.
*/
-ModifyTable *
+static ModifyTable *
make_modifytable(PlannerInfo *root,
CmdType operation, bool canSetTag,
Index nominalRelation,
List *rowMarks, OnConflictExpr *onconflict, int epqParam)
{
ModifyTable *node = makeNode(ModifyTable);
- Plan *plan = &node->plan;
- double total_size;
List *fdw_private_list;
- ListCell *subnode;
+ Bitmapset *direct_modify_plans;
ListCell *lc;
int i;
Assert(returningLists == NIL ||
list_length(resultRelations) == list_length(returningLists));
- /*
- * Compute cost as sum of subplan costs.
- */
- plan->startup_cost = 0;
- plan->total_cost = 0;
- plan->plan_rows = 0;
- total_size = 0;
- foreach(subnode, subplans)
- {
- Plan *subplan = (Plan *) lfirst(subnode);
-
- if (subnode == list_head(subplans)) /* first node? */
- plan->startup_cost = subplan->startup_cost;
- plan->total_cost += subplan->total_cost;
- plan->plan_rows += subplan->plan_rows;
- total_size += subplan->plan_width * subplan->plan_rows;
- }
- if (plan->plan_rows > 0)
- plan->plan_width = rint(total_size / plan->plan_rows);
- else
- plan->plan_width = 0;
-
node->plan.lefttree = NULL;
node->plan.righttree = NULL;
node->plan.qual = NIL;
* construct private plan data, and accumulate it all into a list.
*/
fdw_private_list = NIL;
+ direct_modify_plans = NULL;
i = 0;
foreach(lc, resultRelations)
{
Index rti = lfirst_int(lc);
FdwRoutine *fdwroutine;
List *fdw_private;
+ bool direct_modify;
/*
* If possible, we want to get the FdwRoutine from our RelOptInfo for
fdwroutine = NULL;
}
+ /*
+ * If the target foreign table has any row-level triggers, we can't
+ * modify the foreign table directly.
+ */
+ direct_modify = false;
if (fdwroutine != NULL &&
+ fdwroutine->PlanDirectModify != NULL &&
+ fdwroutine->BeginDirectModify != NULL &&
+ fdwroutine->IterateDirectModify != NULL &&
+ fdwroutine->EndDirectModify != NULL &&
+ !has_row_triggers(root, rti, operation))
+ direct_modify = fdwroutine->PlanDirectModify(root, 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);
else
i++;
}
node->fdwPrivLists = fdw_private_list;
+ node->fdwDirectModifyPlans = direct_modify_plans;
return node;
}
+/*
+ * is_projection_capable_path
+ * Check whether a given Path node is able to do projection.
+ */
+bool
+is_projection_capable_path(Path *path)
+{
+ /* Most plan types can project, so just list the ones that can't */
+ switch (path->pathtype)
+ {
+ case T_Hash:
+ case T_Material:
+ case T_Sort:
+ case T_Unique:
+ case T_SetOp:
+ case T_LockRows:
+ case T_Limit:
+ case T_ModifyTable:
+ case T_MergeAppend:
+ case T_RecursiveUnion:
+ return false;
+ case T_Append:
+
+ /*
+ * Append can't project, but if it's being used to represent a
+ * dummy path, claim that it can project. This prevents us from
+ * converting a rel from dummy to non-dummy status by applying a
+ * 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;
+ }
+ return true;
+}
+
/*
* is_projection_capable_plan
* Check whether a given Plan node is able to do projection.
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;
}