* Planning is complete, we just need to convert the selected
* Path into a Plan.
*
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
#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"
static Plan *create_append_plan(PlannerInfo *root, AppendPath *best_path);
static Plan *create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path);
static Result *create_result_plan(PlannerInfo *root, ResultPath *best_path);
+static ProjectSet *create_project_set_plan(PlannerInfo *root, ProjectSetPath *best_path);
static Material *create_material_plan(PlannerInfo *root, MaterialPath *best_path,
int flags);
static Plan *create_unique_plan(PlannerInfo *root, UniquePath *best_path,
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,
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,
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,
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);
/*
/*
* Attach any initPlans created in this query level to the topmost plan
- * node. (The initPlans could actually go in any plan node at or above
- * where they're referenced, but there seems no reason to put them any
- * lower than the topmost node for the query level.)
+ * node. (In principle the initplans could go in any plan node at or
+ * above where they're referenced, but there seems no reason to put them
+ * any lower than the topmost node for the query level. Also, see
+ * comments for SS_finalize_plan before you try to change this.)
*/
SS_attach_initplans(root, plan);
- /* Update parallel safety information if needed. */
- if (!best_path->parallel_safe)
- root->glob->wholePlanParallelSafe = false;
-
/* Check we successfully assigned all NestLoopParams to plan nodes */
if (root->curOuterParams != NIL)
elog(ERROR, "failed to assign all NestLoopParams to plan nodes");
case T_TidScan:
case T_SubqueryScan:
case T_FunctionScan:
+ case T_TableFuncScan:
case T_ValuesScan:
case T_CteScan:
case T_WorkTableScan:
(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,
(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);
* Extract the relevant restriction clauses from the parent relation. The
* executor must apply all these restrictions during the scan, except for
* pseudoconstants which we'll take care of below.
+ *
+ * If this is a plain indexscan or index-only scan, we need not consider
+ * restriction clauses that are implied by the index's predicate, so use
+ * indrestrictinfo not baserestrictinfo. Note that we can't do that for
+ * bitmap indexscans, since there's not necessarily a single index
+ * involved; but it doesn't matter since create_bitmap_scan_plan() will be
+ * able to get rid of such clauses anyway via predicate proof.
*/
- scan_clauses = rel->baserestrictinfo;
+ switch (best_path->pathtype)
+ {
+ case T_IndexScan:
+ case T_IndexOnlyScan:
+ scan_clauses = castNode(IndexPath, best_path)->indexinfo->indrestrictinfo;
+ break;
+ default:
+ scan_clauses = rel->baserestrictinfo;
+ break;
+ }
/*
* If this is a parameterized scan, we also need to enforce all the join
{
/* For index-only scan, the preferred tlist is the index's */
tlist = copyObject(((IndexPath *) best_path)->indexinfo->indextlist);
- /* Transfer any sortgroupref data to the replacement tlist */
- apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget);
+
+ /*
+ * Transfer 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
{
}
else
{
- /* Transfer any sortgroupref data to the replacement tlist */
- apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget);
+ /* As above, transfer sortgroupref data to replacement tlist */
+ if (!gating_clauses)
+ apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget);
}
}
}
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,
/*
* 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;
* to emit any sort/group columns that are not simple Vars. (If they are
* simple Vars, they should appear in the physical tlist, and
* apply_pathtarget_labeling_to_tlist will take care of getting them
- * labeled again.)
+ * labeled again.) We also have to check that no two sort/group columns
+ * are the same Var, else that element of the physical tlist would need
+ * conflicting ressortgroupref labels.
*/
if ((flags & CP_LABEL_TLIST) && path->pathtarget->sortgrouprefs)
{
+ Bitmapset *sortgroupatts = NULL;
+
i = 0;
foreach(lc, path->pathtarget->exprs)
{
if (path->pathtarget->sortgrouprefs[i])
{
if (expr && IsA(expr, Var))
- /* okay */ ;
+ {
+ int attno = ((Var *) expr)->varattno;
+
+ attno -= FirstLowInvalidHeapAttributeNumber;
+ if (bms_is_member(attno, sortgroupatts))
+ return false;
+ sortgroupatts = bms_add_member(sortgroupatts, attno);
+ }
else
return false;
}
return plan;
}
+/*
+ * create_project_set_plan
+ * Create a ProjectSet plan for 'best_path'.
+ *
+ * Returns a Plan node.
+ */
+static ProjectSet *
+create_project_set_plan(PlannerInfo *root, ProjectSetPath *best_path)
+{
+ ProjectSet *plan;
+ Plan *subplan;
+ List *tlist;
+
+ /* Since we intend to project, we don't need to constrain child tlist */
+ subplan = create_plan_recurse(root, best_path->subpath, 0);
+
+ tlist = build_path_tlist(root, &best_path->path);
+
+ plan = make_project_set(tlist, subplan);
+
+ copy_generic_path_info(&plan->plan, (Path *) best_path);
+
+ return plan;
+}
+
/*
* create_material_plan
* Create a Material plan for 'best_path' and (recursively) plans
plan = (Plan *) make_agg(build_path_tlist(root, &best_path->path),
NIL,
AGG_HASHED,
- false,
- true,
- false,
+ AGGSPLIT_SIMPLE,
numGroupCols,
groupColIdx,
groupOperators,
gather_plan = make_gather(tlist,
NIL,
- best_path->path.parallel_degree,
+ best_path->path.parallel_workers,
best_path->single_copy,
subplan);
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 Result node to do a projection step and (recursively) plans
- * for its subpaths.
+ * Create a plan tree to do a projection step and (recursively) plans
+ * for its subpaths. We may need a Result node for the projection,
+ * but sometimes we can just let the subplan do the work.
*/
static Plan *
create_projection_plan(PlannerInfo *root, ProjectionPath *best_path)
tlist = build_path_tlist(root, &best_path->path);
/*
- * We might not really need a Result node here. There are several ways
- * that this can happen. For example, MergeAppend doesn't project, so we
- * would have thought that we needed a projection to attach resjunk sort
- * columns to its output ... but create_merge_append_plan might have
- * added those same resjunk sort columns to both MergeAppend and its
- * children. Alternatively, apply_projection_to_path might have created
- * a projection path as the subpath of a Gather node even though the
- * subpath was projection-capable. So, if the subpath is capable of
- * projection or the desired tlist is the same expression-wise as the
- * subplan's, just jam it in there. We'll have charged for a Result that
- * doesn't actually appear in the plan, but that's better than having a
- * Result we don't need.
+ * 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;
- /* Adjust cost to match what we thought during planning */
+ /* Label plan with the estimated costs we actually used */
plan->startup_cost = best_path->path.startup_cost;
plan->total_cost = best_path->path.total_cost;
+ 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);
plan = make_agg(tlist, quals,
best_path->aggstrategy,
- best_path->combineStates,
- best_path->finalizeAggs,
- best_path->serialStates,
+ best_path->aggsplit,
list_length(best_path->groupClause),
extract_grouping_cols(best_path->groupClause,
subplan->targetlist),
agg_plan = (Plan *) make_agg(NIL,
NIL,
AGG_SORTED,
- false,
- true,
- false,
+ AGGSPLIT_SIMPLE,
list_length((List *) linitial(gsets)),
new_grpColIdx,
extract_grouping_ops(groupClause),
plan = make_agg(build_path_tlist(root, &best_path->path),
best_path->qual,
(numGroupCols > 0) ? AGG_SORTED : AGG_PLAIN,
- false,
- true,
- false,
+ AGGSPLIT_SIMPLE,
numGroupCols,
top_grpColIdx,
extract_grouping_ops(groupClause),
* 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);
}
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,
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'
/* Copy foreign server OID; likewise, no need to make FDW do this */
scan_plan->fs_server = rel->serverid;
- /* Likewise, copy the relids that are represented by this foreign scan */
- scan_plan->fs_relids = best_path->path.parent->relids;
+ /*
+ * Likewise, copy the relids that are represented by this foreign scan. An
+ * upper rel doesn't have relids set, but it covers all the base relations
+ * participating in the underlying scan, so use root's all_baserels.
+ */
+ if (rel->reloptkind == RELOPT_UPPER_REL)
+ scan_plan->fs_relids = root->all_baserels;
+ else
+ scan_plan->fs_relids = best_path->path.parent->relids;
/*
- * If a join between foreign relations was pushed down, remember it. The
- * push-down safety of the join depends upon the server and user mapping
- * being same. That can change between planning and execution time, in which
- * case the plan should be invalidated.
+ * If this is a foreign join, and to make it valid to push down we had to
+ * assume that the current user is the same as some user explicitly named
+ * in the query, mark the finished plan as depending on the current user.
*/
- if (scan_relid == 0)
- root->glob->hasForeignJoin = true;
+ if (rel->useridiscurrent)
+ root->glob->dependsOnRole = true;
/*
* Replace any outer-relation variables with nestloop params in the qual,
* fdw_exprs and fdw_recheck_quals expressions. We do this last so that
- * the FDW doesn't have to be involved. (Note that parts of fdw_exprs
- * or fdw_recheck_quals could have come from join clauses, so doing this
+ * the FDW doesn't have to be involved. (Note that parts of fdw_exprs or
+ * fdw_recheck_quals could have come from join clauses, so doing this
* beforehand on the scan_clauses wouldn't work.) We assume
* fdw_scan_tlist contains no such variables.
*/
* 0, but there can be no Var with relid 0 in the rel's targetlist or the
* restriction clauses, so we skip this in that case. Note that any such
* columns in base relations that were joined are assumed to be contained
- * in fdw_scan_tlist.) This is a bit of a kluge and might go away someday,
- * so we intentionally leave it out of the API presented to FDWs.
+ * in fdw_scan_tlist.) This is a bit of a kluge and might go away
+ * someday, so we intentionally leave it out of the API presented to FDWs.
*/
scan_plan->fsSystemCol = false;
if (scan_relid > 0)
* 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
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;
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;
}
plan->plan.parallel_aware = false;
}
+/*
+ * bitmap_subplan_mark_shared
+ * Set isshared flag in bitmap subplan so that it will be created in
+ * shared memory.
+ */
+static void
+bitmap_subplan_mark_shared(Plan *plan)
+{
+ if (IsA(plan, BitmapAnd))
+ bitmap_subplan_mark_shared(
+ linitial(((BitmapAnd *) plan)->bitmapplans));
+ else if (IsA(plan, BitmapOr))
+ ((BitmapOr *) plan)->isshared = true;
+ else if (IsA(plan, BitmapIndexScan))
+ ((BitmapIndexScan *) plan)->isshared = true;
+ else
+ elog(ERROR, "unrecognized node type: %d", nodeTag(plan));
+}
/*****************************************************************************
*
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,
* 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
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,
Agg *
make_agg(List *tlist, List *qual,
- AggStrategy aggstrategy,
- bool combineStates, bool finalizeAggs, bool serialStates,
+ AggStrategy aggstrategy, AggSplit aggsplit,
int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators,
List *groupingSets, List *chain,
double dNumGroups, Plan *lefttree)
numGroups = (long) Min(dNumGroups, (double) LONG_MAX);
node->aggstrategy = aggstrategy;
- node->combineStates = combineStates;
- node->finalizeAggs = finalizeAggs;
- node->serialStates = serialStates;
+ node->aggsplit = aggsplit;
node->numCols = numGroupCols;
node->grpColIdx = grpColIdx;
node->grpOperators = grpOperators;
node->numGroups = numGroups;
+ node->aggParams = NULL; /* SS_finalize_plan() will fill this */
node->groupingSets = groupingSets;
node->chain = chain;
plan->righttree = NULL;
node->num_workers = nworkers;
node->single_copy = single_copy;
- node->invisible = false;
+ node->invisible = false;
return node;
}
return node;
}
+/*
+ * make_project_set
+ * Build a ProjectSet plan node
+ */
+static ProjectSet *
+make_project_set(List *tlist,
+ Plan *subplan)
+{
+ ProjectSet *node = makeNode(ProjectSet);
+ Plan *plan = &node->plan;
+
+ plan->targetlist = tlist;
+ plan->qual = NIL;
+ plan->lefttree = subplan;
+ plan->righttree = NULL;
+
+ return node;
+}
+
/*
* make_modifytable
* Build a ModifyTable plan node
* 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;
}
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;
}