* Planning is complete, we just need to convert the selected
* Path into a Plan.
*
- * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.242 2008/08/02 21:32:00 tgl Exp $
+ * src/backend/optimizer/plan/createplan.c
*
*-------------------------------------------------------------------------
*/
#include <math.h>
#include "access/skey.h"
+#include "foreign/fdwapi.h"
+#include "miscadmin.h"
#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
+#include "optimizer/paths.h"
+#include "optimizer/placeholder.h"
#include "optimizer/plancat.h"
#include "optimizer/planmain.h"
+#include "optimizer/planner.h"
#include "optimizer/predtest.h"
#include "optimizer/restrictinfo.h"
+#include "optimizer/subselect.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "parser/parse_clause.h"
-#include "parser/parse_expr.h"
#include "parser/parsetree.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);
static List *build_relation_tlist(RelOptInfo *rel);
-static bool use_physical_tlist(RelOptInfo *rel);
+static bool use_physical_tlist(PlannerInfo *root, RelOptInfo *rel);
static void disuse_physical_tlist(Plan *plan, Path *path);
static Plan *create_gating_plan(PlannerInfo *root, Plan *plan, List *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 SeqScan *create_seqscan_plan(PlannerInfo *root, Path *best_path,
List *tlist, List *scan_clauses);
-static IndexScan *create_indexscan_plan(PlannerInfo *root, IndexPath *best_path,
- List *tlist, List *scan_clauses);
+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,
BitmapHeapPath *best_path,
List *tlist, List *scan_clauses);
static Plan *create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
- List **qual);
+ List **qual, List **indexqual, List **indexECs);
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,
List *tlist, List *scan_clauses);
static ValuesScan *create_valuesscan_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,
+ List *tlist, List *scan_clauses);
+static ForeignScan *create_foreignscan_plan(PlannerInfo *root, ForeignPath *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 List *fix_indexqual_references(List *indexquals, IndexPath *index_path);
-static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index);
+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,
+ List *subplan_params);
+static List *fix_indexqual_references(PlannerInfo *root, IndexPath *index_path);
+static List *fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path);
+static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol);
static List *get_switched_clauses(List *clauses, Relids outerrelids);
static List *order_qual_clauses(PlannerInfo *root, List *clauses);
static void copy_path_costsize(Plan *dest, Path *src);
static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
Oid indexid, List *indexqual, List *indexqualorig,
+ List *indexorderby, List *indexorderbyorig,
ScanDirection indexscandir);
+static IndexOnlyScan *make_indexonlyscan(List *qptlist, List *qpqual,
+ Index scanrelid, Oid indexid,
+ List *indexqual, List *indexorderby,
+ List *indextlist,
+ ScanDirection indexscandir);
static BitmapIndexScan *make_bitmap_indexscan(Index scanrelid, Oid indexid,
List *indexqual,
List *indexqualorig);
List *tidquals);
static FunctionScan *make_functionscan(List *qptlist, List *qpqual,
Index scanrelid, Node *funcexpr, List *funccolnames,
- List *funccoltypes, List *funccoltypmods);
+ List *funccoltypes, List *funccoltypmods,
+ List *funccolcollations);
static ValuesScan *make_valuesscan(List *qptlist, List *qpqual,
Index scanrelid, List *values_lists);
+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 BitmapAnd *make_bitmap_and(List *bitmapplans);
static BitmapOr *make_bitmap_or(List *bitmapplans);
static NestLoop *make_nestloop(List *tlist,
- List *joinclauses, List *otherclauses,
+ List *joinclauses, List *otherclauses, List *nestParams,
Plan *lefttree, Plan *righttree,
JoinType jointype);
static HashJoin *make_hashjoin(List *tlist,
List *hashclauses,
Plan *lefttree, Plan *righttree,
JoinType jointype);
-static Hash *make_hash(Plan *lefttree);
+static Hash *make_hash(Plan *lefttree,
+ Oid skewTable,
+ AttrNumber skewColumn,
+ bool skewInherit,
+ Oid skewColType,
+ int32 skewColTypmod);
static MergeJoin *make_mergejoin(List *tlist,
List *joinclauses, List *otherclauses,
List *mergeclauses,
Oid *mergefamilies,
+ Oid *mergecollations,
int *mergestrategies,
bool *mergenullsfirst,
Plan *lefttree, Plan *righttree,
JoinType jointype);
static Sort *make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
- AttrNumber *sortColIdx, Oid *sortOperators, bool *nullsFirst,
+ AttrNumber *sortColIdx, Oid *sortOperators,
+ Oid *collations, bool *nullsFirst,
double limit_tuples);
+static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
+ Plan *lefttree, List *pathkeys,
+ Relids relids,
+ const AttrNumber *reqColIdx,
+ bool adjust_tlist_in_place,
+ int *p_numsortkeys,
+ AttrNumber **p_sortColIdx,
+ Oid **p_sortOperators,
+ Oid **p_collations,
+ bool **p_nullsFirst);
+static EquivalenceMember *find_ec_member_for_tle(EquivalenceClass *ec,
+ TargetEntry *tle,
+ Relids relids);
static Material *make_material(Plan *lefttree);
/*
* create_plan
- * Creates the access plan for a query by tracing backwards through the
- * desired chain of pathnodes, starting at the node 'best_path'. For
+ * Creates the access plan for a query by recursively processing the
+ * desired tree of pathnodes, starting at the node 'best_path'. For
* every pathnode found, we create a corresponding plan node containing
* appropriate id, target list, and qualification information.
*
{
Plan *plan;
+ /* plan_params should not be in use in current query level */
+ Assert(root->plan_params == NIL);
+
+ /* Initialize this module's private workspace in PlannerInfo */
+ root->curOuterRels = NULL;
+ root->curOuterParams = NIL;
+
+ /* Recursively process the path tree */
+ plan = create_plan_recurse(root, best_path);
+
+ /* Check we successfully assigned all NestLoopParams to plan nodes */
+ if (root->curOuterParams != NIL)
+ elog(ERROR, "failed to assign all NestLoopParams to plan nodes");
+
+ /*
+ * Reset plan_params to ensure param IDs used for nestloop params are not
+ * re-used later
+ */
+ root->plan_params = NIL;
+
+ return plan;
+}
+
+/*
+ * create_plan_recurse
+ * Recursive guts of create_plan().
+ */
+static Plan *
+create_plan_recurse(PlannerInfo *root, Path *best_path)
+{
+ Plan *plan;
+
switch (best_path->pathtype)
{
case T_SeqScan:
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:
plan = create_scan_plan(root, best_path);
break;
case T_HashJoin:
plan = create_append_plan(root,
(AppendPath *) best_path);
break;
+ case T_MergeAppend:
+ plan = create_merge_append_plan(root,
+ (MergeAppendPath *) best_path);
+ break;
case T_Result:
plan = (Plan *) create_result_plan(root,
(ResultPath *) best_path);
* planner.c may replace the tlist we generate here, forcing projection to
* occur.)
*/
- if (use_physical_tlist(rel))
+ if (use_physical_tlist(root, rel))
{
- tlist = build_physical_tlist(root, rel);
- /* if fail because of dropped cols, use regular method */
- if (tlist == NIL)
- tlist = build_relation_tlist(rel);
+ if (best_path->pathtype == T_IndexOnlyScan)
+ {
+ /* For index-only scan, the preferred tlist is the index's */
+ tlist = copyObject(((IndexPath *) best_path)->indexinfo->indextlist);
+ }
+ else
+ {
+ tlist = build_physical_tlist(root, rel);
+ /* if fail because of dropped cols, use regular method */
+ if (tlist == NIL)
+ tlist = build_relation_tlist(rel);
+ }
}
else
+ {
tlist = build_relation_tlist(rel);
+ /*
+ * If it's a parameterized otherrel, there might be lateral references
+ * in the tlist, which need to be replaced with Params. This cannot
+ * happen for regular baserels, though. Note use_physical_tlist()
+ * always fails for otherrels, so we don't need to check this above.
+ */
+ if (rel->reloptkind != RELOPT_BASEREL && best_path->param_info)
+ tlist = (List *) replace_nestloop_params(root, (Node *) tlist);
+ }
+
/*
* Extract the relevant restriction clauses from the parent relation. The
* executor must apply all these restrictions during the scan, except for
*/
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:
plan = (Plan *) create_indexscan_plan(root,
(IndexPath *) best_path,
tlist,
- scan_clauses);
+ scan_clauses,
+ false);
+ break;
+
+ case T_IndexOnlyScan:
+ plan = (Plan *) create_indexscan_plan(root,
+ (IndexPath *) best_path,
+ tlist,
+ scan_clauses,
+ true);
break;
case T_BitmapHeapScan:
scan_clauses);
break;
+ case T_CteScan:
+ plan = (Plan *) create_ctescan_plan(root,
+ best_path,
+ tlist,
+ scan_clauses);
+ break;
+
+ case T_WorkTableScan:
+ plan = (Plan *) create_worktablescan_plan(root,
+ best_path,
+ tlist,
+ scan_clauses);
+ break;
+
+ case T_ForeignScan:
+ plan = (Plan *) create_foreignscan_plan(root,
+ (ForeignPath *) best_path,
+ tlist,
+ scan_clauses);
+ break;
+
default:
elog(ERROR, "unrecognized node type: %d",
(int) best_path->pathtype);
foreach(v, rel->reltargetlist)
{
/* Do we really need to copy here? Not sure */
- Var *var = (Var *) copyObject(lfirst(v));
+ Node *node = (Node *) copyObject(lfirst(v));
- tlist = lappend(tlist, makeTargetEntry((Expr *) var,
+ tlist = lappend(tlist, makeTargetEntry((Expr *) node,
resno,
NULL,
false));
* rather than only those Vars actually referenced.
*/
static bool
-use_physical_tlist(RelOptInfo *rel)
+use_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
{
int i;
+ ListCell *lc;
/*
* We can do this for real relation scans, subquery scans, function scans,
- * and values scans (but not for, eg, joins).
+ * 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_VALUES)
+ rel->rtekind != RTE_VALUES &&
+ rel->rtekind != RTE_CTE)
return false;
/*
return false;
/*
- * Can't do it if any system columns or whole-row Vars are requested,
- * either. (This could possibly be fixed but would take some fragile
- * assumptions in setrefs.c, I think.)
+ * Can't do it if any system columns or whole-row Vars are requested.
+ * (This could possibly be fixed but would take some fragile assumptions
+ * in setrefs.c, I think.)
*/
for (i = rel->min_attr; i <= 0; i++)
{
return false;
}
+ /*
+ * Can't do it if the rel is required to emit any placeholder expressions,
+ * either.
+ */
+ foreach(lc, root->placeholder_list)
+ {
+ PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
+
+ if (bms_nonempty_difference(phinfo->ph_needed, rel->relids) &&
+ bms_is_subset(phinfo->ph_eval_at, rel->relids))
+ return false;
+ }
+
return true;
}
{
case T_SeqScan:
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:
plan->targetlist = build_relation_tlist(path->parent);
break;
default:
Plan *outer_plan;
Plan *inner_plan;
Plan *plan;
+ Relids saveOuterRels = root->curOuterRels;
+
+ outer_plan = create_plan_recurse(root, best_path->outerjoinpath);
- outer_plan = create_plan(root, best_path->outerjoinpath);
- inner_plan = create_plan(root, best_path->innerjoinpath);
+ /* 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);
switch (best_path->path.pathtype)
{
inner_plan);
break;
case T_NestLoop:
+ /* Restore curOuterRels */
+ bms_free(root->curOuterRels);
+ root->curOuterRels = saveOuterRels;
+
plan = (Plan *) create_nestloop_plan(root,
(NestPath *) best_path,
outer_plan,
{
Path *subpath = (Path *) lfirst(subpaths);
- subplans = lappend(subplans, create_plan(root, subpath));
+ subplans = lappend(subplans, create_plan_recurse(root, subpath));
}
- plan = make_append(subplans, false, tlist);
+ plan = make_append(subplans, tlist);
return (Plan *) plan;
}
+/*
+ * create_merge_append_plan
+ * Create a MergeAppend plan for 'best_path' and (recursively) plans
+ * for its subpaths.
+ *
+ * Returns a Plan node.
+ */
+static Plan *
+create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
+{
+ MergeAppend *node = makeNode(MergeAppend);
+ Plan *plan = &node->plan;
+ List *tlist = build_relation_tlist(best_path->path.parent);
+ List *pathkeys = best_path->path.pathkeys;
+ List *subplans = NIL;
+ ListCell *subpaths;
+
+ /*
+ * We don't have the actual creation of the MergeAppend node split out
+ * into a separate make_xxx function. This is because we want to run
+ * prepare_sort_from_pathkeys on it before we do so on the individual
+ * child plans, to make cross-checking the sort info easier.
+ */
+ copy_path_costsize(plan, (Path *) best_path);
+ plan->targetlist = tlist;
+ plan->qual = NIL;
+ plan->lefttree = NULL;
+ plan->righttree = NULL;
+
+ /* Compute sort column info, and adjust MergeAppend's tlist as needed */
+ (void) prepare_sort_from_pathkeys(root, plan, pathkeys,
+ NULL,
+ NULL,
+ true,
+ &node->numCols,
+ &node->sortColIdx,
+ &node->sortOperators,
+ &node->collations,
+ &node->nullsFirst);
+
+ /*
+ * Now prepare the child plans. We must apply prepare_sort_from_pathkeys
+ * even to subplans that don't need an explicit sort, to make sure they
+ * are returning the same sort key columns the MergeAppend expects.
+ */
+ foreach(subpaths, best_path->subpaths)
+ {
+ Path *subpath = (Path *) lfirst(subpaths);
+ Plan *subplan;
+ int numsortkeys;
+ AttrNumber *sortColIdx;
+ Oid *sortOperators;
+ Oid *collations;
+ bool *nullsFirst;
+
+ /* Build the child plan */
+ subplan = create_plan_recurse(root, subpath);
+
+ /* Compute sort column info, and adjust subplan's tlist as needed */
+ subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
+ subpath->parent->relids,
+ node->sortColIdx,
+ false,
+ &numsortkeys,
+ &sortColIdx,
+ &sortOperators,
+ &collations,
+ &nullsFirst);
+
+ /*
+ * Check that we got the same sort key information. We just Assert
+ * that the sortops match, since those depend only on the pathkeys;
+ * but it seems like a good idea to check the sort column numbers
+ * explicitly, to ensure the tlists really do match up.
+ */
+ Assert(numsortkeys == node->numCols);
+ if (memcmp(sortColIdx, node->sortColIdx,
+ numsortkeys * sizeof(AttrNumber)) != 0)
+ elog(ERROR, "MergeAppend child's targetlist doesn't match MergeAppend");
+ Assert(memcmp(sortOperators, node->sortOperators,
+ numsortkeys * sizeof(Oid)) == 0);
+ Assert(memcmp(collations, node->collations,
+ numsortkeys * sizeof(Oid)) == 0);
+ Assert(memcmp(nullsFirst, node->nullsFirst,
+ numsortkeys * sizeof(bool)) == 0);
+
+ /* 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,
+ sortColIdx, sortOperators,
+ collations, nullsFirst,
+ best_path->limit_tuples);
+
+ subplans = lappend(subplans, subplan);
+ }
+
+ node->mergeplans = subplans;
+
+ return (Plan *) node;
+}
+
/*
* create_result_plan
* Create a Result plan for 'best_path'.
Material *plan;
Plan *subplan;
- subplan = create_plan(root, best_path->subpath);
+ subplan = create_plan_recurse(root, best_path->subpath);
/* We don't want any excess columns in the materialized tuples */
disuse_physical_tlist(subplan, best_path->subpath);
{
Plan *plan;
Plan *subplan;
- List *uniq_exprs;
List *in_operators;
+ List *uniq_exprs;
List *newtlist;
int nextresno;
bool newitems;
int groupColPos;
ListCell *l;
- subplan = create_plan(root, best_path->subpath);
+ subplan = create_plan_recurse(root, best_path->subpath);
/* Done if we don't need to do any actual unique-ifying */
if (best_path->umethod == UNIQUE_PATH_NOOP)
return subplan;
- /*----------
- * As constructed, the subplan has a "flat" tlist containing just the
- * Vars needed here and at upper levels. The values we are supposed
- * to unique-ify may be expressions in these variables. We have to
- * add any such expressions to the subplan's tlist.
- *
- * The subplan may have a "physical" tlist if it is a simple scan plan.
- * If we're going to sort, this should be reduced to the regular tlist,
- * so that we don't sort more data than we need to. For hashing, the
- * tlist should be left as-is if we don't need to add any expressions;
- * but if we do have to add expressions, then a projection step will be
- * needed at runtime anyway, so we may as well remove unneeded items.
- * Therefore newtlist starts from build_relation_tlist() not just a
- * copy of the subplan's tlist; and we don't install it into the subplan
- * unless we are sorting or stuff has to be added.
+ /*
+ * As constructed, the subplan has a "flat" tlist containing just the Vars
+ * needed here and at upper levels. The values we are supposed to
+ * unique-ify may be expressions in these variables. We have to add any
+ * such expressions to the subplan's tlist.
*
- * To find the correct list of values to unique-ify, we look in the
- * information saved for IN expressions. If this code is ever used in
- * other scenarios, some other way of finding what to unique-ify will
- * be needed. The IN clause's operators are needed too, since they
- * determine what the meaning of "unique" is in this context.
- *----------
+ * The subplan may have a "physical" tlist if it is a simple scan plan. If
+ * we're going to sort, this should be reduced to the regular tlist, so
+ * that we don't sort more data than we need to. For hashing, the tlist
+ * should be left as-is if we don't need to add any expressions; but if we
+ * do have to add expressions, then a projection step will be needed at
+ * runtime anyway, so we may as well remove unneeded items. Therefore
+ * newtlist starts from build_relation_tlist() not just a copy of the
+ * subplan's tlist; and we don't install it into the subplan unless we are
+ * sorting or stuff has to be added.
*/
- uniq_exprs = NIL; /* just to keep compiler quiet */
- in_operators = NIL;
- foreach(l, root->in_info_list)
- {
- InClauseInfo *ininfo = (InClauseInfo *) lfirst(l);
-
- if (bms_equal(ininfo->righthand, best_path->path.parent->relids))
- {
- uniq_exprs = ininfo->sub_targetlist;
- in_operators = ininfo->in_operators;
- break;
- }
- }
- if (l == NULL) /* fell out of loop? */
- elog(ERROR, "could not find UniquePath in in_info_list");
+ in_operators = best_path->in_operators;
+ uniq_exprs = best_path->uniq_exprs;
/* initialize modified subplan tlist as just the "required" vars */
newtlist = build_relation_tlist(best_path->path.parent);
long numGroups;
Oid *groupOperators;
- numGroups = (long) Min(best_path->rows, (double) LONG_MAX);
+ numGroups = (long) Min(best_path->path.rows, (double) LONG_MAX);
/*
* Get the hashable equality operators for the Agg node to use.
build_relation_tlist(best_path->path.parent),
NIL,
AGG_HASHED,
+ NULL,
numGroupCols,
groupColIdx,
groupOperators,
numGroups,
- 0,
subplan);
}
else
{
Oid in_oper = lfirst_oid(l);
Oid sortop;
+ Oid eqop;
TargetEntry *tle;
SortGroupClause *sortcl;
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 = in_oper;
+ sortcl->eqop = eqop;
sortcl->sortop = sortop;
sortcl->nulls_first = false;
+ sortcl->hashable = false; /* no need to make this accurate */
sortList = lappend(sortList, sortcl);
groupColPos++;
}
}
/* Adjust output size estimate (other fields should be OK already) */
- plan->plan_rows = best_path->rows;
+ plan->plan_rows = best_path->path.rows;
return plan;
}
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
scan_clauses = extract_actual_clauses(scan_clauses, false);
+ /* Replace any outer-relation variables with nestloop params */
+ if (best_path->param_info)
+ {
+ scan_clauses = (List *)
+ replace_nestloop_params(root, (Node *) scan_clauses);
+ }
+
scan_plan = make_seqscan(tlist,
scan_clauses,
scan_relid);
* Returns an indexscan plan for the base relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*
- * The indexquals list of the path contains implicitly-ANDed qual conditions.
- * The list can be empty --- then no index restrictions will be applied during
- * the scan.
+ * We use this for both plain IndexScans and IndexOnlyScans, because the
+ * qual preprocessing work is the same for both. Note that the caller tells
+ * us which to build --- we don't look at best_path->path.pathtype, because
+ * create_bitmap_subplan needs to be able to override the prior decision.
*/
-static IndexScan *
+static Scan *
create_indexscan_plan(PlannerInfo *root,
IndexPath *best_path,
List *tlist,
- List *scan_clauses)
+ List *scan_clauses,
+ bool indexonly)
{
+ Scan *scan_plan;
List *indexquals = best_path->indexquals;
+ List *indexorderbys = best_path->indexorderbys;
Index baserelid = best_path->path.parent->relid;
Oid indexoid = best_path->indexinfo->indexoid;
List *qpqual;
List *stripped_indexquals;
List *fixed_indexquals;
+ List *fixed_indexorderbys;
ListCell *l;
- IndexScan *scan_plan;
/* it should be a base rel... */
Assert(baserelid > 0);
/*
* The executor needs a copy with the indexkey on the left of each clause
- * and with index attr numbers substituted for table ones.
+ * and with index Vars substituted for table ones.
*/
- fixed_indexquals = fix_indexqual_references(indexquals, best_path);
+ fixed_indexquals = fix_indexqual_references(root, best_path);
/*
- * If this is an innerjoin scan, the indexclauses will contain join
- * clauses that are not present in scan_clauses (since the passed-in value
- * is just the rel's baserestrictinfo list). We must add these clauses to
- * scan_clauses to ensure they get checked. In most cases we will remove
- * the join clauses again below, but if a join clause contains a special
- * operator, we need to make sure it gets into the scan_clauses.
- *
- * Note: pointer comparison should be enough to determine RestrictInfo
- * matches.
+ * Likewise fix up index attr references in the ORDER BY expressions.
*/
- if (best_path->isjoininner)
- scan_clauses = list_union_ptr(scan_clauses, best_path->indexclauses);
+ fixed_indexorderbys = fix_indexorderby_references(root, best_path);
/*
* The qpqual list must contain all restrictions not automatically handled
- * by the index. All the predicates in the indexquals will be checked
- * (either by the index itself, or by nodeIndexscan.c), but if there are
- * any "special" operators involved then they must be included in qpqual.
- * The upshot is that qpqual must contain scan_clauses minus whatever
- * appears in indexquals.
+ * by the index, other than pseudoconstant clauses which will be handled
+ * by a separate gating plan node. All the predicates in the indexquals
+ * will be checked (either by the index itself, or by nodeIndexscan.c),
+ * but if there are any "special" operators involved then they must be
+ * included in qpqual. The upshot is that qpqual must contain
+ * scan_clauses minus whatever appears in indexquals.
*
* In normal cases simple pointer equality checks will be enough to spot
- * duplicate RestrictInfos, so we try that first. In some situations
- * (particularly with OR'd index conditions) we may have scan_clauses that
- * are not equal to, but are logically implied by, the index quals; so we
- * also try a predicate_implied_by() check to see if we can discard quals
- * that way. (predicate_implied_by assumes its first input contains only
- * immutable functions, so we have to check that.)
+ * duplicate RestrictInfos, so we try that first.
+ *
+ * Another common case is that a scan_clauses entry is generated from the
+ * same EquivalenceClass as some indexqual, and is therefore redundant
+ * with it, though not equal. (This happens when indxpath.c prefers a
+ * different derived equality than what generate_join_implied_equalities
+ * picked for a parameterized scan's ppi_clauses.)
+ *
+ * In some situations (particularly with OR'd index conditions) we may
+ * have scan_clauses that are not equal to, but are logically implied by,
+ * the index quals; so we also try a predicate_implied_by() check to see
+ * if we can discard quals that way. (predicate_implied_by assumes its
+ * 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
if (rinfo->pseudoconstant)
continue; /* we may drop pseudoconstants here */
if (list_member_ptr(indexquals, rinfo))
- continue;
+ 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;
+ continue; /* provably implied by indexquals */
if (best_path->indexinfo->indpred)
{
if (baserelid != root->parse->resultRelation &&
- get_rowmark(root->parse, baserelid) == NULL)
+ get_parse_rowmark(root->parse, baserelid) == NULL)
if (predicate_implied_by(clausel,
best_path->indexinfo->indpred))
- continue;
+ continue; /* implied by index predicate */
}
}
qpqual = lappend(qpqual, rinfo);
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
qpqual = extract_actual_clauses(qpqual, false);
- /* Finally ready to build the plan node */
- scan_plan = make_indexscan(tlist,
- qpqual,
- baserelid,
- indexoid,
- fixed_indexquals,
- stripped_indexquals,
- best_path->indexscandir);
+ /*
+ * We have to replace any outer-relation variables with nestloop params in
+ * the indexqualorig, qpqual, and indexorderbyorig expressions. A bit
+ * annoying to have to do this separately from the processing in
+ * fix_indexqual_references --- rethink this when generalizing the inner
+ * indexscan support. But note we can't really do this earlier because
+ * it'd break the comparisons to predicates above ... (or would it? Those
+ * wouldn't have outer refs)
+ */
+ if (best_path->path.param_info)
+ {
+ stripped_indexquals = (List *)
+ replace_nestloop_params(root, (Node *) stripped_indexquals);
+ qpqual = (List *)
+ replace_nestloop_params(root, (Node *) qpqual);
+ indexorderbys = (List *)
+ replace_nestloop_params(root, (Node *) indexorderbys);
+ }
- copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
- /* use the indexscan-specific rows estimate, not the parent rel's */
- scan_plan->scan.plan.plan_rows = best_path->rows;
+ /* Finally ready to build the plan node */
+ if (indexonly)
+ scan_plan = (Scan *) make_indexonlyscan(tlist,
+ qpqual,
+ baserelid,
+ indexoid,
+ fixed_indexquals,
+ fixed_indexorderbys,
+ best_path->indexinfo->indextlist,
+ best_path->indexscandir);
+ else
+ scan_plan = (Scan *) make_indexscan(tlist,
+ qpqual,
+ baserelid,
+ indexoid,
+ fixed_indexquals,
+ stripped_indexquals,
+ fixed_indexorderbys,
+ indexorderbys,
+ best_path->indexscandir);
+
+ copy_path_costsize(&scan_plan->plan, &best_path->path);
return scan_plan;
}
Index baserelid = best_path->path.parent->relid;
Plan *bitmapqualplan;
List *bitmapqualorig;
+ List *indexquals;
+ List *indexECs;
List *qpqual;
ListCell *l;
BitmapHeapScan *scan_plan;
Assert(baserelid > 0);
Assert(best_path->path.parent->rtekind == RTE_RELATION);
- /* Process the bitmapqual tree into a Plan tree and qual list */
+ /* Process the bitmapqual tree into a Plan tree and qual lists */
bitmapqualplan = create_bitmap_subplan(root, best_path->bitmapqual,
- &bitmapqualorig);
-
- /* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
- scan_clauses = extract_actual_clauses(scan_clauses, false);
-
- /*
- * If this is a innerjoin scan, the indexclauses will contain join clauses
- * that are not present in scan_clauses (since the passed-in value is just
- * the rel's baserestrictinfo list). We must add these clauses to
- * scan_clauses to ensure they get checked. In most cases we will remove
- * the join clauses again below, but if a join clause contains a special
- * operator, we need to make sure it gets into the scan_clauses.
- */
- if (best_path->isjoininner)
- {
- scan_clauses = list_concat_unique(scan_clauses, bitmapqualorig);
- }
+ &bitmapqualorig, &indexquals,
+ &indexECs);
/*
* The qpqual list must contain all restrictions not automatically handled
- * by the index. All the predicates in the indexquals will be checked
- * (either by the index itself, or by nodeBitmapHeapscan.c), but if there
- * are any "special" operators involved then they must be added to qpqual.
- * The upshot is that qpqual must contain scan_clauses minus whatever
- * appears in bitmapqualorig.
+ * by the index, other than pseudoconstant clauses which will be handled
+ * by a separate gating plan node. All the predicates in the indexquals
+ * will be checked (either by the index itself, or by
+ * nodeBitmapHeapscan.c), but if there are any "special" operators
+ * involved then they must be added to qpqual. The upshot is that qpqual
+ * must contain scan_clauses minus whatever appears in indexquals.
+ *
+ * This loop is similar to the comparable code in create_indexscan_plan(),
+ * but with some differences because it has to compare the scan clauses to
+ * stripped (no RestrictInfos) indexquals. See comments there for more
+ * info.
*
* In normal cases simple equal() checks will be enough to spot duplicate
- * clauses, so we try that first. In some situations (particularly with
- * OR'd index conditions) we may have scan_clauses that are not equal to,
- * but are logically implied by, the index quals; so we also try a
- * predicate_implied_by() check to see if we can discard quals that way.
- * (predicate_implied_by assumes its first input contains only immutable
- * functions, so we have to check that.)
+ * clauses, so we try that first. We next see if the scan clause is
+ * 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. Bitmap scans have to do it that
- * way because predicate conditions need to be rechecked if the scan's
- * bitmap becomes lossy.
+ * 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.
*/
qpqual = NIL;
foreach(l, scan_clauses)
{
- Node *clause = (Node *) lfirst(l);
+ RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
+ Node *clause = (Node *) rinfo->clause;
- if (list_member(bitmapqualorig, clause))
- continue;
+ 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, bitmapqualorig))
- continue;
+ if (predicate_implied_by(clausel, indexquals))
+ continue; /* provably implied by indexquals */
}
- qpqual = lappend(qpqual, clause);
+ qpqual = lappend(qpqual, rinfo);
}
/* Sort clauses into best execution order */
qpqual = order_qual_clauses(root, qpqual);
+ /* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
+ qpqual = extract_actual_clauses(qpqual, false);
+
/*
- * When dealing with special operators, we will at this point
- * have duplicate clauses in qpqual and bitmapqualorig. We may as well
- * drop 'em from bitmapqualorig, since there's no point in making the
- * tests twice.
+ * When dealing with special operators, we will at this point have
+ * duplicate clauses in qpqual and bitmapqualorig. We may as well drop
+ * 'em from bitmapqualorig, since there's no point in making the tests
+ * twice.
*/
bitmapqualorig = list_difference_ptr(bitmapqualorig, qpqual);
+ /*
+ * We have to replace any outer-relation variables with nestloop params in
+ * the qpqual and bitmapqualorig expressions. (This was already done for
+ * expressions attached to plan nodes in the bitmapqualplan tree.)
+ */
+ if (best_path->path.param_info)
+ {
+ qpqual = (List *)
+ replace_nestloop_params(root, (Node *) qpqual);
+ bitmapqualorig = (List *)
+ replace_nestloop_params(root, (Node *) bitmapqualorig);
+ }
+
/* Finally ready to build the plan node */
scan_plan = make_bitmap_heapscan(tlist,
qpqual,
baserelid);
copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
- /* use the indexscan-specific rows estimate, not the parent rel's */
- scan_plan->scan.plan.plan_rows = best_path->rows;
return scan_plan;
}
/*
* Given a bitmapqual tree, generate the Plan tree that implements it
*
- * As a byproduct, we also return in *qual a qual list (in implicit-AND
- * form, without RestrictInfos) describing the generated indexqual
- * conditions, as needed for rechecking heap tuples in lossy cases.
- * This list also includes partial-index predicates, because we have to
- * recheck predicates as well as index conditions if the scan's bitmap
- * becomes lossy.
+ * As byproducts, we also return in *qual and *indexqual the qual lists
+ * (in implicit-AND form, without RestrictInfos) describing the original index
+ * conditions and the generated indexqual conditions. (These are the same in
+ * simple cases, but when special index operators are involved, the former
+ * list includes the special conditions while the latter includes the actual
+ * indexable conditions derived from them.) Both lists include partial-index
+ * predicates, because we have to recheck predicates as well as index
+ * conditions if the bitmap scan becomes lossy.
+ *
+ * In addition, we return a list of EquivalenceClass pointers for all the
+ * top-level indexquals that were possibly-redundantly derived from ECs.
+ * This allows removal of scan_clauses that are redundant with such quals.
+ * (We do not attempt to detect such redundancies for quals that are within
+ * OR subtrees. This could be done in a less hacky way if we returned the
+ * indexquals in RestrictInfo form, but that would be slower and still pretty
+ * messy, since we'd have to build new RestrictInfos in many cases.)
*
* Note: if you find yourself changing this, you probably need to change
* make_restrictinfo_from_bitmapqual too.
*/
static Plan *
create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
- List **qual)
+ List **qual, List **indexqual, List **indexECs)
{
Plan *plan;
BitmapAndPath *apath = (BitmapAndPath *) bitmapqual;
List *subplans = NIL;
List *subquals = NIL;
+ List *subindexquals = NIL;
+ List *subindexECs = NIL;
ListCell *l;
/*
{
Plan *subplan;
List *subqual;
+ List *subindexqual;
+ List *subindexEC;
subplan = create_bitmap_subplan(root, (Path *) lfirst(l),
- &subqual);
+ &subqual, &subindexqual,
+ &subindexEC);
subplans = lappend(subplans, subplan);
subquals = list_concat_unique(subquals, subqual);
+ subindexquals = list_concat_unique(subindexquals, subindexqual);
+ /* Duplicates in indexECs aren't worth getting rid of */
+ subindexECs = list_concat(subindexECs, subindexEC);
}
plan = (Plan *) make_bitmap_and(subplans);
plan->startup_cost = apath->path.startup_cost;
clamp_row_est(apath->bitmapselectivity * apath->path.parent->tuples);
plan->plan_width = 0; /* meaningless */
*qual = subquals;
+ *indexqual = subindexquals;
+ *indexECs = subindexECs;
}
else if (IsA(bitmapqual, BitmapOrPath))
{
BitmapOrPath *opath = (BitmapOrPath *) bitmapqual;
List *subplans = NIL;
List *subquals = NIL;
+ List *subindexquals = NIL;
bool const_true_subqual = false;
+ bool const_true_subindexqual = false;
ListCell *l;
/*
{
Plan *subplan;
List *subqual;
+ List *subindexqual;
+ List *subindexEC;
subplan = create_bitmap_subplan(root, (Path *) lfirst(l),
- &subqual);
+ &subqual, &subindexqual,
+ &subindexEC);
subplans = lappend(subplans, subplan);
if (subqual == NIL)
const_true_subqual = true;
else if (!const_true_subqual)
subquals = lappend(subquals,
make_ands_explicit(subqual));
+ if (subindexqual == NIL)
+ const_true_subindexqual = true;
+ else if (!const_true_subindexqual)
+ subindexquals = lappend(subindexquals,
+ make_ands_explicit(subindexqual));
}
/*
*qual = subquals;
else
*qual = list_make1(make_orclause(subquals));
+ if (const_true_subindexqual)
+ *indexqual = NIL;
+ else if (list_length(subindexquals) <= 1)
+ *indexqual = subindexquals;
+ else
+ *indexqual = list_make1(make_orclause(subindexquals));
+ *indexECs = NIL;
}
else if (IsA(bitmapqual, IndexPath))
{
IndexPath *ipath = (IndexPath *) bitmapqual;
IndexScan *iscan;
+ List *subindexECs;
ListCell *l;
/* Use the regular indexscan plan build machinery... */
- iscan = create_indexscan_plan(root, ipath, NIL, NIL);
+ iscan = (IndexScan *) create_indexscan_plan(root, ipath,
+ NIL, NIL, false);
+ Assert(IsA(iscan, IndexScan));
/* then convert to a bitmap indexscan */
plan = (Plan *) make_bitmap_indexscan(iscan->scan.scanrelid,
iscan->indexid,
clamp_row_est(ipath->indexselectivity * ipath->path.parent->tuples);
plan->plan_width = 0; /* meaningless */
*qual = get_actual_clauses(ipath->indexclauses);
+ *indexqual = get_actual_clauses(ipath->indexquals);
foreach(l, ipath->indexinfo->indpred)
{
Expr *pred = (Expr *) lfirst(l);
* generating redundant conditions.
*/
if (!predicate_implied_by(list_make1(pred), ipath->indexclauses))
+ {
*qual = lappend(*qual, pred);
+ *indexqual = lappend(*indexqual, pred);
+ }
}
+ subindexECs = NIL;
+ foreach(l, ipath->indexquals)
+ {
+ RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
+
+ if (rinfo->parent_ec)
+ subindexECs = lappend(subindexECs, rinfo->parent_ec);
+ }
+ *indexECs = subindexECs;
}
else
{
{
TidScan *scan_plan;
Index scan_relid = best_path->path.parent->relid;
+ List *tidquals = best_path->tidquals;
List *ortidquals;
/* it should be a base rel... */
/* 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->path.param_info)
+ {
+ tidquals = (List *)
+ replace_nestloop_params(root, (Node *) tidquals);
+ scan_clauses = (List *)
+ replace_nestloop_params(root, (Node *) scan_clauses);
+ }
+
/*
* Remove any clauses that are TID quals. This is a bit tricky since the
* tidquals list has implicit OR semantics.
*/
- ortidquals = best_path->tidquals;
+ ortidquals = tidquals;
if (list_length(ortidquals) > 1)
ortidquals = list_make1(make_orclause(ortidquals));
scan_clauses = list_difference(scan_clauses, ortidquals);
scan_plan = make_tidscan(tlist,
scan_clauses,
scan_relid,
- best_path->tidquals);
+ tidquals);
copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
/* 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);
+ process_subquery_nestloop_params(root,
+ best_path->parent->subplan_params);
+ }
+
scan_plan = make_subqueryscan(tlist,
scan_clauses,
scan_relid,
- best_path->parent->subplan,
- best_path->parent->subrtable);
+ best_path->parent->subplan);
copy_path_costsize(&scan_plan->scan.plan, best_path);
FunctionScan *scan_plan;
Index scan_relid = best_path->parent->relid;
RangeTblEntry *rte;
+ Node *funcexpr;
/* it should be a function base rel... */
Assert(scan_relid > 0);
rte = planner_rt_fetch(scan_relid, root);
Assert(rte->rtekind == RTE_FUNCTION);
+ funcexpr = rte->funcexpr;
/* 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 func expression itself could contain nestloop params, too */
+ funcexpr = replace_nestloop_params(root, funcexpr);
+ }
+
scan_plan = make_functionscan(tlist, scan_clauses, scan_relid,
- rte->funcexpr,
+ funcexpr,
rte->eref->colnames,
rte->funccoltypes,
- rte->funccoltypmods);
+ rte->funccoltypmods,
+ rte->funccolcollations);
copy_path_costsize(&scan_plan->scan.plan, best_path);
ValuesScan *scan_plan;
Index scan_relid = best_path->parent->relid;
RangeTblEntry *rte;
+ List *values_lists;
/* it should be a values base rel... */
Assert(scan_relid > 0);
rte = planner_rt_fetch(scan_relid, root);
Assert(rte->rtekind == RTE_VALUES);
+ values_lists = rte->values_lists;
/* 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 values lists could contain nestloop params, too */
+ values_lists = (List *)
+ replace_nestloop_params(root, (Node *) values_lists);
+ }
+
scan_plan = make_valuesscan(tlist, scan_clauses, scan_relid,
- rte->values_lists);
+ values_lists);
copy_path_costsize(&scan_plan->scan.plan, best_path);
return scan_plan;
}
-/*****************************************************************************
- *
- * JOIN METHODS
- *
- *****************************************************************************/
-
-static NestLoop *
-create_nestloop_plan(PlannerInfo *root,
- NestPath *best_path,
- Plan *outer_plan,
- Plan *inner_plan)
+/*
+ * create_ctescan_plan
+ * Returns a ctescan plan for the base relation scanned by 'best_path'
+ * with restriction clauses 'scan_clauses' and targetlist 'tlist'.
+ */
+static CteScan *
+create_ctescan_plan(PlannerInfo *root, Path *best_path,
+ List *tlist, List *scan_clauses)
{
- List *tlist = build_relation_tlist(best_path->path.parent);
- List *joinrestrictclauses = best_path->joinrestrictinfo;
- List *joinclauses;
- List *otherclauses;
- NestLoop *join_plan;
+ CteScan *scan_plan;
+ Index scan_relid = best_path->parent->relid;
+ RangeTblEntry *rte;
+ SubPlan *ctesplan = NULL;
+ int plan_id;
+ int cte_param_id;
+ PlannerInfo *cteroot;
+ Index levelsup;
+ int ndx;
+ ListCell *lc;
+
+ Assert(scan_relid > 0);
+ rte = planner_rt_fetch(scan_relid, root);
+ Assert(rte->rtekind == RTE_CTE);
+ Assert(!rte->self_reference);
- if (IsA(best_path->innerjoinpath, IndexPath))
+ /*
+ * Find the referenced CTE, and locate the SubPlan previously made for it.
+ */
+ levelsup = rte->ctelevelsup;
+ cteroot = root;
+ while (levelsup-- > 0)
{
- /*
- * An index is being used to reduce the number of tuples scanned in
- * the inner relation. If there are join clauses being used with the
- * index, we may remove those join clauses from the list of clauses
- * that have to be checked as qpquals at the join node.
- *
- * We can also remove any join clauses that are redundant with those
- * being used in the index scan; this check is needed because
- * find_eclass_clauses_for_index_join() may emit different clauses
- * than generate_join_implied_equalities() did.
- *
- * We can skip this if the index path is an ordinary indexpath and not
- * a special innerjoin path, since it then wouldn't be using any join
- * clauses.
- */
- IndexPath *innerpath = (IndexPath *) best_path->innerjoinpath;
+ cteroot = cteroot->parent_root;
+ if (!cteroot) /* shouldn't happen */
+ elog(ERROR, "bad levelsup for CTE \"%s\"", rte->ctename);
+ }
+
+ /*
+ * Note: cte_plan_ids can be shorter than cteList, if we are still working
+ * on planning the CTEs (ie, this is a side-reference from another CTE).
+ * So we mustn't use forboth here.
+ */
+ ndx = 0;
+ foreach(lc, cteroot->parse->cteList)
+ {
+ CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
- if (innerpath->isjoininner)
- joinrestrictclauses =
- select_nonredundant_join_clauses(root,
- joinrestrictclauses,
- innerpath->indexclauses);
+ if (strcmp(cte->ctename, rte->ctename) == 0)
+ break;
+ ndx++;
}
- else if (IsA(best_path->innerjoinpath, BitmapHeapPath))
+ if (lc == NULL) /* shouldn't happen */
+ elog(ERROR, "could not find CTE \"%s\"", rte->ctename);
+ if (ndx >= list_length(cteroot->cte_plan_ids))
+ elog(ERROR, "could not find plan for CTE \"%s\"", rte->ctename);
+ plan_id = list_nth_int(cteroot->cte_plan_ids, ndx);
+ Assert(plan_id > 0);
+ foreach(lc, cteroot->init_plans)
{
- /*
- * Same deal for bitmapped index scans.
- *
- * Note: both here and above, we ignore any implicit index
- * restrictions associated with the use of partial indexes. This is
- * OK because we're only trying to prove we can dispense with some
- * join quals; failing to prove that doesn't result in an incorrect
- * plan. It is the right way to proceed because adding more quals to
- * the stuff we got from the original query would just make it harder
- * to detect duplication. (Also, to change this we'd have to be wary
- * of UPDATE/DELETE/SELECT FOR UPDATE target relations; see notes
- * above about EvalPlanQual.)
- */
- BitmapHeapPath *innerpath = (BitmapHeapPath *) best_path->innerjoinpath;
+ ctesplan = (SubPlan *) lfirst(lc);
+ if (ctesplan->plan_id == plan_id)
+ break;
+ }
+ if (lc == NULL) /* shouldn't happen */
+ elog(ERROR, "could not find plan for CTE \"%s\"", rte->ctename);
- if (innerpath->isjoininner)
- {
- List *bitmapclauses;
+ /*
+ * We need the CTE param ID, which is the sole member of the SubPlan's
+ * setParam list.
+ */
+ cte_param_id = linitial_int(ctesplan->setParam);
- bitmapclauses =
- make_restrictinfo_from_bitmapqual(innerpath->bitmapqual,
- true,
- false);
- joinrestrictclauses =
- select_nonredundant_join_clauses(root,
- joinrestrictclauses,
- bitmapclauses);
- }
- }
+ /* Sort clauses into best execution order */
+ scan_clauses = order_qual_clauses(root, scan_clauses);
- /* Sort join qual clauses into best execution order */
- joinrestrictclauses = order_qual_clauses(root, joinrestrictclauses);
+ /* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
+ scan_clauses = extract_actual_clauses(scan_clauses, false);
- /* Get the join qual clauses (in plain expression form) */
- /* Any pseudoconstant clauses are ignored here */
- if (IS_OUTER_JOIN(best_path->jointype))
- {
- extract_actual_join_clauses(joinrestrictclauses,
- &joinclauses, &otherclauses);
- }
- else
+ /* Replace any outer-relation variables with nestloop params */
+ if (best_path->param_info)
{
- /* We can treat all clauses alike for an inner join */
- joinclauses = extract_actual_clauses(joinrestrictclauses, false);
- otherclauses = NIL;
+ scan_clauses = (List *)
+ replace_nestloop_params(root, (Node *) scan_clauses);
}
- join_plan = make_nestloop(tlist,
- joinclauses,
- otherclauses,
- outer_plan,
- inner_plan,
- best_path->jointype);
+ scan_plan = make_ctescan(tlist, scan_clauses, scan_relid,
+ plan_id, cte_param_id);
- copy_path_costsize(&join_plan->join.plan, &best_path->path);
+ copy_path_costsize(&scan_plan->scan.plan, best_path);
- return join_plan;
+ return scan_plan;
}
-static MergeJoin *
-create_mergejoin_plan(PlannerInfo *root,
- MergePath *best_path,
- Plan *outer_plan,
- Plan *inner_plan)
+/*
+ * create_worktablescan_plan
+ * Returns a worktablescan plan for the base relation scanned by 'best_path'
+ * with restriction clauses 'scan_clauses' and targetlist 'tlist'.
+ */
+static WorkTableScan *
+create_worktablescan_plan(PlannerInfo *root, Path *best_path,
+ List *tlist, List *scan_clauses)
{
- List *tlist = build_relation_tlist(best_path->jpath.path.parent);
+ WorkTableScan *scan_plan;
+ Index scan_relid = best_path->parent->relid;
+ RangeTblEntry *rte;
+ Index levelsup;
+ PlannerInfo *cteroot;
+
+ Assert(scan_relid > 0);
+ rte = planner_rt_fetch(scan_relid, root);
+ Assert(rte->rtekind == RTE_CTE);
+ Assert(rte->self_reference);
+
+ /*
+ * We need to find the worktable param ID, which is in the plan level
+ * that's processing the recursive UNION, which is one level *below* where
+ * the CTE comes from.
+ */
+ levelsup = rte->ctelevelsup;
+ if (levelsup == 0) /* shouldn't happen */
+ elog(ERROR, "bad levelsup for CTE \"%s\"", rte->ctename);
+ levelsup--;
+ cteroot = root;
+ while (levelsup-- > 0)
+ {
+ cteroot = cteroot->parent_root;
+ if (!cteroot) /* shouldn't happen */
+ elog(ERROR, "bad levelsup for CTE \"%s\"", rte->ctename);
+ }
+ if (cteroot->wt_param_id < 0) /* shouldn't happen */
+ elog(ERROR, "could not find param ID for CTE \"%s\"", rte->ctename);
+
+ /* Sort clauses into best execution order */
+ scan_clauses = order_qual_clauses(root, scan_clauses);
+
+ /* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
+ scan_clauses = extract_actual_clauses(scan_clauses, false);
+
+ /* Replace any outer-relation variables with nestloop params */
+ if (best_path->param_info)
+ {
+ scan_clauses = (List *)
+ replace_nestloop_params(root, (Node *) scan_clauses);
+ }
+
+ scan_plan = make_worktablescan(tlist, scan_clauses, scan_relid,
+ cteroot->wt_param_id);
+
+ copy_path_costsize(&scan_plan->scan.plan, best_path);
+
+ return scan_plan;
+}
+
+/*
+ * create_foreignscan_plan
+ * Returns a foreignscan plan for the base relation scanned by 'best_path'
+ * with restriction clauses 'scan_clauses' and targetlist 'tlist'.
+ */
+static ForeignScan *
+create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
+ List *tlist, List *scan_clauses)
+{
+ ForeignScan *scan_plan;
+ RelOptInfo *rel = best_path->path.parent;
+ Index scan_relid = rel->relid;
+ RangeTblEntry *rte;
+ int i;
+
+ /* it should be a base rel... */
+ Assert(scan_relid > 0);
+ Assert(rel->rtekind == RTE_RELATION);
+ rte = planner_rt_fetch(scan_relid, root);
+ Assert(rte->rtekind == RTE_RELATION);
+
+ /*
+ * Sort clauses into best execution order. We do this first since the FDW
+ * might have more info than we do and wish to adjust the ordering.
+ */
+ scan_clauses = order_qual_clauses(root, scan_clauses);
+
+ /*
+ * Let the FDW perform its processing on the restriction clauses and
+ * generate the plan node. Note that the FDW might remove restriction
+ * clauses that it intends to execute remotely, or even add more (if it
+ * has selected some join clauses for remote use but also wants them
+ * rechecked locally).
+ */
+ scan_plan = rel->fdwroutine->GetForeignPlan(root, rel, rte->relid,
+ best_path,
+ tlist, scan_clauses);
+
+ /* Copy cost data from Path to Plan; no need to make FDW do this */
+ copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
+
+ /*
+ * Replace any outer-relation variables with nestloop params in the qual
+ * and fdw_exprs expressions. We do this last so that the FDW doesn't
+ * have to be involved. (Note that parts of fdw_exprs could have come
+ * from join clauses, so doing this beforehand on the scan_clauses
+ * wouldn't work.)
+ */
+ if (best_path->path.param_info)
+ {
+ scan_plan->scan.plan.qual = (List *)
+ replace_nestloop_params(root, (Node *) scan_plan->scan.plan.qual);
+ scan_plan->fdw_exprs = (List *)
+ replace_nestloop_params(root, (Node *) scan_plan->fdw_exprs);
+ }
+
+ /*
+ * 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.
+ */
+ scan_plan->fsSystemCol = false;
+ for (i = rel->min_attr; i < 0; i++)
+ {
+ if (!bms_is_empty(rel->attr_needed[i - rel->min_attr]))
+ {
+ scan_plan->fsSystemCol = true;
+ break;
+ }
+ }
+
+ return scan_plan;
+}
+
+
+/*****************************************************************************
+ *
+ * JOIN METHODS
+ *
+ *****************************************************************************/
+
+static NestLoop *
+create_nestloop_plan(PlannerInfo *root,
+ NestPath *best_path,
+ Plan *outer_plan,
+ Plan *inner_plan)
+{
+ NestLoop *join_plan;
+ List *tlist = build_relation_tlist(best_path->path.parent);
+ List *joinrestrictclauses = best_path->joinrestrictinfo;
+ List *joinclauses;
+ List *otherclauses;
+ Relids outerrelids;
+ List *nestParams;
+ ListCell *cell;
+ ListCell *prev;
+ ListCell *next;
+
+ /* Sort join qual clauses into best execution order */
+ joinrestrictclauses = order_qual_clauses(root, joinrestrictclauses);
+
+ /* Get the join qual clauses (in plain expression form) */
+ /* Any pseudoconstant clauses are ignored here */
+ if (IS_OUTER_JOIN(best_path->jointype))
+ {
+ extract_actual_join_clauses(joinrestrictclauses,
+ &joinclauses, &otherclauses);
+ }
+ else
+ {
+ /* We can treat all clauses alike for an inner join */
+ joinclauses = extract_actual_clauses(joinrestrictclauses, false);
+ otherclauses = NIL;
+ }
+
+ /* Replace any outer-relation variables with nestloop params */
+ if (best_path->path.param_info)
+ {
+ joinclauses = (List *)
+ replace_nestloop_params(root, (Node *) joinclauses);
+ otherclauses = (List *)
+ replace_nestloop_params(root, (Node *) otherclauses);
+ }
+
+ /*
+ * Identify any nestloop parameters that should be supplied by this join
+ * node, and move them from root->curOuterParams to the nestParams list.
+ */
+ outerrelids = best_path->outerjoinpath->parent->relids;
+ nestParams = NIL;
+ prev = NULL;
+ for (cell = list_head(root->curOuterParams); cell; cell = next)
+ {
+ NestLoopParam *nlp = (NestLoopParam *) lfirst(cell);
+
+ next = lnext(cell);
+ if (IsA(nlp->paramval, Var) &&
+ bms_is_member(nlp->paramval->varno, outerrelids))
+ {
+ root->curOuterParams = list_delete_cell(root->curOuterParams,
+ cell, prev);
+ nestParams = lappend(nestParams, nlp);
+ }
+ else if (IsA(nlp->paramval, PlaceHolderVar) &&
+ bms_overlap(((PlaceHolderVar *) nlp->paramval)->phrels,
+ outerrelids) &&
+ bms_is_subset(find_placeholder_info(root,
+ (PlaceHolderVar *) nlp->paramval,
+ false)->ph_eval_at,
+ outerrelids))
+ {
+ root->curOuterParams = list_delete_cell(root->curOuterParams,
+ cell, prev);
+ nestParams = lappend(nestParams, nlp);
+ }
+ else
+ prev = cell;
+ }
+
+ join_plan = make_nestloop(tlist,
+ joinclauses,
+ otherclauses,
+ nestParams,
+ outer_plan,
+ inner_plan,
+ best_path->jointype);
+
+ copy_path_costsize(&join_plan->join.plan, &best_path->path);
+
+ return join_plan;
+}
+
+static MergeJoin *
+create_mergejoin_plan(PlannerInfo *root,
+ MergePath *best_path,
+ Plan *outer_plan,
+ Plan *inner_plan)
+{
+ List *tlist = build_relation_tlist(best_path->jpath.path.parent);
List *joinclauses;
List *otherclauses;
List *mergeclauses;
List *innerpathkeys;
int nClauses;
Oid *mergefamilies;
+ Oid *mergecollations;
int *mergestrategies;
bool *mergenullsfirst;
MergeJoin *join_plan;
int i;
- EquivalenceClass *lastoeclass;
- EquivalenceClass *lastieclass;
- PathKey *opathkey;
- PathKey *ipathkey;
ListCell *lc;
ListCell *lop;
ListCell *lip;
mergeclauses = get_actual_clauses(best_path->path_mergeclauses);
joinclauses = list_difference(joinclauses, mergeclauses);
+ /*
+ * Replace any outer-relation variables with nestloop params. There
+ * should not be any in the mergeclauses.
+ */
+ if (best_path->jpath.path.param_info)
+ {
+ joinclauses = (List *)
+ replace_nestloop_params(root, (Node *) joinclauses);
+ otherclauses = (List *)
+ replace_nestloop_params(root, (Node *) otherclauses);
+ }
+
/*
* Rearrange mergeclauses, if needed, so that the outer variable is always
* on the left; mark the mergeclause restrictinfos with correct
best_path->jpath.outerjoinpath->parent->relids);
/*
- * Create explicit sort nodes for the outer and inner join paths if
- * necessary. The sort cost was already accounted for in the path. Make
- * sure there are no excess columns in the inputs if sorting.
+ * 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)
{
innerpathkeys = best_path->jpath.innerjoinpath->pathkeys;
/*
- * If inner plan is a sort that is expected to spill to disk, add a
- * materialize node to shield it from the need to handle mark/restore.
- * This will allow it to perform the last merge pass on-the-fly, while in
- * most cases not requiring the materialize to spill to disk.
- *
- * XXX really, Sort oughta do this for itself, probably, to avoid the
- * overhead of a separate plan node.
+ * If specified, add a materialize node to shield the inner plan from the
+ * need to handle mark/restore.
*/
- if (IsA(inner_plan, Sort) &&
- sort_exceeds_work_mem((Sort *) inner_plan))
+ if (best_path->materialize_inner)
{
Plan *matplan = (Plan *) make_material(inner_plan);
/*
* We assume the materialize will not spill to disk, and therefore
- * charge just cpu_tuple_cost per tuple.
+ * charge just cpu_operator_cost per tuple. (Keep this estimate in
+ * sync with final_cost_mergejoin.)
*/
copy_plan_costsize(matplan, inner_plan);
- matplan->total_cost += cpu_tuple_cost * matplan->plan_rows;
+ matplan->total_cost += cpu_operator_cost * matplan->plan_rows;
inner_plan = matplan;
}
/*
- * Compute the opfamily/strategy/nullsfirst arrays needed by the executor.
- * The information is in the pathkeys for the two inputs, but we need to
- * be careful about the possibility of mergeclauses sharing a pathkey
- * (compare find_mergeclauses_for_pathkeys()).
+ * Compute the opfamily/collation/strategy/nullsfirst arrays needed by the
+ * executor. The information is in the pathkeys for the two inputs, but
+ * we need to be careful about the possibility of mergeclauses sharing a
+ * pathkey (compare find_mergeclauses_for_pathkeys()).
*/
nClauses = list_length(mergeclauses);
Assert(nClauses == list_length(best_path->path_mergeclauses));
mergefamilies = (Oid *) palloc(nClauses * sizeof(Oid));
+ mergecollations = (Oid *) palloc(nClauses * sizeof(Oid));
mergestrategies = (int *) palloc(nClauses * sizeof(int));
mergenullsfirst = (bool *) palloc(nClauses * sizeof(bool));
- lastoeclass = NULL;
- lastieclass = NULL;
- opathkey = NULL;
- ipathkey = NULL;
lop = list_head(outerpathkeys);
lip = list_head(innerpathkeys);
i = 0;
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
EquivalenceClass *oeclass;
EquivalenceClass *ieclass;
+ PathKey *opathkey;
+ PathKey *ipathkey;
+ EquivalenceClass *opeclass;
+ EquivalenceClass *ipeclass;
+ ListCell *l2;
/* fetch outer/inner eclass from mergeclause */
Assert(IsA(rinfo, RestrictInfo));
Assert(oeclass != NULL);
Assert(ieclass != NULL);
- /* should match current or next pathkeys */
- /* we check this carefully for debugging reasons */
- if (oeclass != lastoeclass)
+ /*
+ * For debugging purposes, we check that the eclasses match the paths'
+ * pathkeys. In typical cases the merge clauses are one-to-one with
+ * the pathkeys, but when dealing with partially redundant query
+ * conditions, we might have clauses that re-reference earlier path
+ * keys. The case that we need to reject is where a pathkey is
+ * entirely skipped over.
+ *
+ * lop and lip reference the first as-yet-unused pathkey elements;
+ * it's okay to match them, or any element before them. If they're
+ * NULL then we have found all pathkey elements to be used.
+ */
+ if (lop)
{
- if (!lop)
- elog(ERROR, "too few pathkeys for mergeclauses");
opathkey = (PathKey *) lfirst(lop);
- lop = lnext(lop);
- lastoeclass = opathkey->pk_eclass;
- if (oeclass != lastoeclass)
- elog(ERROR, "outer pathkeys do not match mergeclause");
+ opeclass = opathkey->pk_eclass;
+ if (oeclass == opeclass)
+ {
+ /* fast path for typical case */
+ lop = lnext(lop);
+ }
+ else
+ {
+ /* redundant clauses ... must match something before lop */
+ foreach(l2, outerpathkeys)
+ {
+ if (l2 == lop)
+ break;
+ opathkey = (PathKey *) lfirst(l2);
+ opeclass = opathkey->pk_eclass;
+ if (oeclass == opeclass)
+ break;
+ }
+ if (oeclass != opeclass)
+ elog(ERROR, "outer pathkeys do not match mergeclauses");
+ }
+ }
+ else
+ {
+ /* redundant clauses ... must match some already-used pathkey */
+ opathkey = NULL;
+ opeclass = NULL;
+ foreach(l2, outerpathkeys)
+ {
+ opathkey = (PathKey *) lfirst(l2);
+ opeclass = opathkey->pk_eclass;
+ if (oeclass == opeclass)
+ break;
+ }
+ if (l2 == NULL)
+ elog(ERROR, "outer pathkeys do not match mergeclauses");
}
- if (ieclass != lastieclass)
+
+ if (lip)
{
- if (!lip)
- elog(ERROR, "too few pathkeys for mergeclauses");
ipathkey = (PathKey *) lfirst(lip);
- lip = lnext(lip);
- lastieclass = ipathkey->pk_eclass;
- if (ieclass != lastieclass)
- elog(ERROR, "inner pathkeys do not match mergeclause");
+ ipeclass = ipathkey->pk_eclass;
+ if (ieclass == ipeclass)
+ {
+ /* fast path for typical case */
+ lip = lnext(lip);
+ }
+ else
+ {
+ /* redundant clauses ... must match something before lip */
+ foreach(l2, innerpathkeys)
+ {
+ if (l2 == lip)
+ break;
+ ipathkey = (PathKey *) lfirst(l2);
+ ipeclass = ipathkey->pk_eclass;
+ if (ieclass == ipeclass)
+ break;
+ }
+ if (ieclass != ipeclass)
+ elog(ERROR, "inner pathkeys do not match mergeclauses");
+ }
+ }
+ else
+ {
+ /* redundant clauses ... must match some already-used pathkey */
+ ipathkey = NULL;
+ ipeclass = NULL;
+ foreach(l2, innerpathkeys)
+ {
+ ipathkey = (PathKey *) lfirst(l2);
+ ipeclass = ipathkey->pk_eclass;
+ if (ieclass == ipeclass)
+ break;
+ }
+ if (l2 == NULL)
+ elog(ERROR, "inner pathkeys do not match mergeclauses");
}
+
/* pathkeys should match each other too (more debugging) */
if (opathkey->pk_opfamily != ipathkey->pk_opfamily ||
+ opathkey->pk_eclass->ec_collation != ipathkey->pk_eclass->ec_collation ||
opathkey->pk_strategy != ipathkey->pk_strategy ||
opathkey->pk_nulls_first != ipathkey->pk_nulls_first)
elog(ERROR, "left and right pathkeys do not match in mergejoin");
/* OK, save info for executor */
mergefamilies[i] = opathkey->pk_opfamily;
+ mergecollations[i] = opathkey->pk_eclass->ec_collation;
mergestrategies[i] = opathkey->pk_strategy;
mergenullsfirst[i] = opathkey->pk_nulls_first;
i++;
}
+ /*
+ * Note: it is not an error if we have additional pathkey elements (i.e.,
+ * lop or lip isn't NULL here). The input paths might be better-sorted
+ * than we need for the current mergejoin.
+ */
/*
* Now we can build the mergejoin node.
otherclauses,
mergeclauses,
mergefamilies,
+ mergecollations,
mergestrategies,
mergenullsfirst,
outer_plan,
inner_plan,
best_path->jpath.jointype);
+ /* Costs of sort and material steps are included in path cost already */
copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path);
return join_plan;
List *joinclauses;
List *otherclauses;
List *hashclauses;
+ Oid skewTable = InvalidOid;
+ AttrNumber skewColumn = InvalidAttrNumber;
+ bool skewInherit = false;
+ Oid skewColType = InvalidOid;
+ int32 skewColTypmod = -1;
HashJoin *join_plan;
Hash *hash_plan;
hashclauses = get_actual_clauses(best_path->path_hashclauses);
joinclauses = list_difference(joinclauses, hashclauses);
+ /*
+ * Replace any outer-relation variables with nestloop params. There
+ * should not be any in the hashclauses.
+ */
+ if (best_path->jpath.path.param_info)
+ {
+ joinclauses = (List *)
+ replace_nestloop_params(root, (Node *) joinclauses);
+ otherclauses = (List *)
+ replace_nestloop_params(root, (Node *) otherclauses);
+ }
+
/*
* Rearrange hashclauses, if needed, so that the outer variable is always
* on the left.
/* We don't want any excess columns in the hashed tuples */
disuse_physical_tlist(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(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
+ * skew optimization. (Note: in principle we could do skew optimization
+ * with multiple join clauses, but we'd have to be able to determine the
+ * most common combinations of outer values, which we don't currently have
+ * enough stats for.)
+ */
+ if (list_length(hashclauses) == 1)
+ {
+ OpExpr *clause = (OpExpr *) linitial(hashclauses);
+ Node *node;
+
+ Assert(is_opclause(clause));
+ node = (Node *) linitial(clause->args);
+ if (IsA(node, RelabelType))
+ node = (Node *) ((RelabelType *) node)->arg;
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
+ RangeTblEntry *rte;
+
+ rte = root->simple_rte_array[var->varno];
+ if (rte->rtekind == RTE_RELATION)
+ {
+ skewTable = rte->relid;
+ skewColumn = var->varattno;
+ skewInherit = rte->inh;
+ skewColType = var->vartype;
+ skewColTypmod = var->vartypmod;
+ }
+ }
+ }
+
/*
* Build the hash node and hash join node.
*/
- hash_plan = make_hash(inner_plan);
+ hash_plan = make_hash(inner_plan,
+ skewTable,
+ skewColumn,
+ skewInherit,
+ skewColType,
+ skewColTypmod);
join_plan = make_hashjoin(tlist,
joinclauses,
otherclauses,
*
*****************************************************************************/
+/*
+ * replace_nestloop_params
+ * Replace outer-relation Vars and PlaceHolderVars in the given expression
+ * with nestloop Params
+ *
+ * All Vars and PlaceHolderVars belonging to the relation(s) identified by
+ * root->curOuterRels are replaced by Params, and entries are added to
+ * root->curOuterParams if not already present.
+ */
+static Node *
+replace_nestloop_params(PlannerInfo *root, Node *expr)
+{
+ /* No setup needed for tree walk, so away we go */
+ return replace_nestloop_params_mutator(expr, root);
+}
+
+static Node *
+replace_nestloop_params_mutator(Node *node, PlannerInfo *root)
+{
+ if (node == NULL)
+ return NULL;
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
+ Param *param;
+ NestLoopParam *nlp;
+ ListCell *lc;
+
+ /* Upper-level Vars should be long gone at this point */
+ Assert(var->varlevelsup == 0);
+ /* If not to be replaced, we can just return the Var unmodified */
+ if (!bms_is_member(var->varno, root->curOuterRels))
+ return node;
+ /* Create a Param representing the Var */
+ param = assign_nestloop_param_var(root, var);
+ /* Is this param already listed in root->curOuterParams? */
+ foreach(lc, root->curOuterParams)
+ {
+ nlp = (NestLoopParam *) lfirst(lc);
+ if (nlp->paramno == param->paramid)
+ {
+ Assert(equal(var, nlp->paramval));
+ /* Present, so we can just return the Param */
+ return (Node *) param;
+ }
+ }
+ /* No, so add it */
+ nlp = makeNode(NestLoopParam);
+ nlp->paramno = param->paramid;
+ nlp->paramval = var;
+ root->curOuterParams = lappend(root->curOuterParams, nlp);
+ /* And return the replacement Param */
+ return (Node *) param;
+ }
+ if (IsA(node, PlaceHolderVar))
+ {
+ PlaceHolderVar *phv = (PlaceHolderVar *) node;
+ Param *param;
+ NestLoopParam *nlp;
+ ListCell *lc;
+
+ /* Upper-level PlaceHolderVars should be long gone at this point */
+ Assert(phv->phlevelsup == 0);
+
+ /*
+ * If not to be replaced, just return the PlaceHolderVar unmodified.
+ * We use bms_overlap as a cheap/quick test to see if the PHV might be
+ * evaluated in the outer rels, and then grab its PlaceHolderInfo to
+ * tell for sure.
+ */
+ if (!bms_overlap(phv->phrels, root->curOuterRels))
+ return node;
+ if (!bms_is_subset(find_placeholder_info(root, phv, false)->ph_eval_at,
+ root->curOuterRels))
+ return node;
+ /* Create a Param representing the PlaceHolderVar */
+ param = assign_nestloop_param_placeholdervar(root, phv);
+ /* Is this param already listed in root->curOuterParams? */
+ foreach(lc, root->curOuterParams)
+ {
+ nlp = (NestLoopParam *) lfirst(lc);
+ if (nlp->paramno == param->paramid)
+ {
+ Assert(equal(phv, nlp->paramval));
+ /* Present, so we can just return the Param */
+ return (Node *) param;
+ }
+ }
+ /* No, so add it */
+ nlp = makeNode(NestLoopParam);
+ nlp->paramno = param->paramid;
+ nlp->paramval = (Var *) phv;
+ root->curOuterParams = lappend(root->curOuterParams, nlp);
+ /* And return the replacement Param */
+ return (Node *) param;
+ }
+ return expression_tree_mutator(node,
+ replace_nestloop_params_mutator,
+ (void *) root);
+}
+
+/*
+ * process_subquery_nestloop_params
+ * Handle params of a parameterized subquery that need to be fed
+ * from an outer nestloop.
+ *
+ * Currently, that would be *all* params that a subquery in FROM has demanded
+ * from the current query level, since they must be LATERAL references.
+ *
+ * The subplan's references to the outer variables are already represented
+ * as PARAM_EXEC Params, so we need not modify the subplan here. What we
+ * do need to do is add entries to root->curOuterParams to signal the parent
+ * nestloop plan node that it must provide these values.
+ */
+static void
+process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params)
+{
+ ListCell *ppl;
+
+ foreach(ppl, subplan_params)
+ {
+ PlannerParamItem *pitem = (PlannerParamItem *) lfirst(ppl);
+
+ if (IsA(pitem->item, Var))
+ {
+ Var *var = (Var *) pitem->item;
+ NestLoopParam *nlp;
+ ListCell *lc;
+
+ /* If not from a nestloop outer rel, complain */
+ if (!bms_is_member(var->varno, root->curOuterRels))
+ elog(ERROR, "non-LATERAL parameter required by subquery");
+ /* Is this param already listed in root->curOuterParams? */
+ foreach(lc, root->curOuterParams)
+ {
+ nlp = (NestLoopParam *) lfirst(lc);
+ if (nlp->paramno == pitem->paramId)
+ {
+ Assert(equal(var, nlp->paramval));
+ /* Present, so nothing to do */
+ break;
+ }
+ }
+ if (lc == NULL)
+ {
+ /* No, so add it */
+ nlp = makeNode(NestLoopParam);
+ nlp->paramno = pitem->paramId;
+ nlp->paramval = copyObject(var);
+ root->curOuterParams = lappend(root->curOuterParams, nlp);
+ }
+ }
+ else if (IsA(pitem->item, PlaceHolderVar))
+ {
+ PlaceHolderVar *phv = (PlaceHolderVar *) pitem->item;
+ NestLoopParam *nlp;
+ ListCell *lc;
+
+ /* If not from a nestloop outer rel, complain */
+ if (!bms_is_subset(find_placeholder_info(root, phv, false)->ph_eval_at,
+ root->curOuterRels))
+ elog(ERROR, "non-LATERAL parameter required by subquery");
+ /* Is this param already listed in root->curOuterParams? */
+ foreach(lc, root->curOuterParams)
+ {
+ nlp = (NestLoopParam *) lfirst(lc);
+ if (nlp->paramno == pitem->paramId)
+ {
+ Assert(equal(phv, nlp->paramval));
+ /* Present, so nothing to do */
+ break;
+ }
+ }
+ if (lc == NULL)
+ {
+ /* No, so add it */
+ nlp = makeNode(NestLoopParam);
+ nlp->paramno = pitem->paramId;
+ nlp->paramval = copyObject(phv);
+ root->curOuterParams = lappend(root->curOuterParams, nlp);
+ }
+ }
+ else
+ elog(ERROR, "unexpected type of subquery parameter");
+ }
+}
+
/*
* fix_indexqual_references
* Adjust indexqual clauses to the form the executor's indexqual
* machinery needs.
*
- * We have three tasks here:
+ * We have four tasks here:
* * Remove RestrictInfo nodes from the input clauses.
+ * * Replace any outer-relation Var or PHV nodes with nestloop Params.
+ * (XXX eventually, that responsibility should go elsewhere?)
* * Index keys must be represented by Var nodes with varattno set to the
* index's attribute number, not the attribute number in the original rel.
* * If the index key is on the right, commute the clause to put it on the
* left.
*
- * The result is a modified copy of the indexquals list --- the
+ * The result is a modified copy of the path's indexquals list --- the
* original is not changed. Note also that the copy shares no substructure
* with the original; this is needed in case there is a subplan in it (we need
* two separate copies of the subplan tree, or things will go awry).
*/
static List *
-fix_indexqual_references(List *indexquals, IndexPath *index_path)
+fix_indexqual_references(PlannerInfo *root, IndexPath *index_path)
{
IndexOptInfo *index = index_path->indexinfo;
List *fixed_indexquals;
- ListCell *l;
+ ListCell *lcc,
+ *lci;
fixed_indexquals = NIL;
- /*
- * For each qual clause, commute if needed to put the indexkey operand on
- * the left, and then fix its varattno. (We do not need to change the
- * other side of the clause.)
- */
- foreach(l, indexquals)
+ forboth(lcc, index_path->indexquals, lci, index_path->indexqualcols)
{
- RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
- Expr *clause;
+ RestrictInfo *rinfo = (RestrictInfo *) lfirst(lcc);
+ int indexcol = lfirst_int(lci);
+ Node *clause;
Assert(IsA(rinfo, RestrictInfo));
/*
- * Make a copy that will become the fixed clause.
+ * Replace any outer-relation variables with nestloop params.
*
- * We used to try to do a shallow copy here, but that fails if there
- * is a subplan in the arguments of the opclause. So just do a full
- * copy.
+ * This also makes a copy of the clause, so it's safe to modify it
+ * in-place below.
*/
- clause = (Expr *) copyObject((Node *) rinfo->clause);
+ clause = replace_nestloop_params(root, (Node *) rinfo->clause);
if (IsA(clause, OpExpr))
{
/*
* Check to see if the indexkey is on the right; if so, commute
- * the clause. The indexkey should be the side that refers to
+ * the clause. The indexkey should be the side that refers to
* (only) the base relation.
*/
if (!bms_equal(rinfo->left_relids, index->rel->relids))
CommuteOpExpr(op);
/*
- * Now, determine which index attribute this is and change the
- * indexkey operand as needed.
+ * Now replace the indexkey expression with an index Var.
*/
linitial(op->args) = fix_indexqual_operand(linitial(op->args),
- index);
+ index,
+ indexcol);
}
else if (IsA(clause, RowCompareExpr))
{
RowCompareExpr *rc = (RowCompareExpr *) clause;
- ListCell *lc;
+ Expr *newrc;
+ List *indexcolnos;
+ bool var_on_left;
+ ListCell *lca,
+ *lcai;
+
+ /*
+ * Re-discover which index columns are used in the rowcompare.
+ */
+ newrc = adjust_rowcompare_for_index(rc,
+ index,
+ indexcol,
+ &indexcolnos,
+ &var_on_left);
+
+ /*
+ * Trouble if adjust_rowcompare_for_index thought the
+ * RowCompareExpr didn't match the index as-is; the clause should
+ * have gone through that routine already.
+ */
+ if (newrc != (Expr *) rc)
+ elog(ERROR, "inconsistent results from adjust_rowcompare_for_index");
/*
* Check to see if the indexkey is on the right; if so, commute
- * the clause. The indexkey should be the side that refers to
- * (only) the base relation.
+ * the clause.
*/
- if (!bms_overlap(pull_varnos(linitial(rc->largs)),
- index->rel->relids))
+ if (!var_on_left)
CommuteRowCompareExpr(rc);
/*
- * For each column in the row comparison, determine which index
- * attribute this is and change the indexkey operand as needed.
+ * Now replace the indexkey expressions with index Vars.
*/
- foreach(lc, rc->largs)
+ Assert(list_length(rc->largs) == list_length(indexcolnos));
+ forboth(lca, rc->largs, lcai, indexcolnos)
{
- lfirst(lc) = fix_indexqual_operand(lfirst(lc),
- index);
+ lfirst(lca) = fix_indexqual_operand(lfirst(lca),
+ index,
+ lfirst_int(lcai));
}
}
else if (IsA(clause, ScalarArrayOpExpr))
/* Never need to commute... */
- /*
- * Determine which index attribute this is and change the
- * indexkey operand as needed.
- */
+ /* Replace the indexkey expression with an index Var. */
linitial(saop->args) = fix_indexqual_operand(linitial(saop->args),
- index);
+ index,
+ indexcol);
}
else if (IsA(clause, NullTest))
{
NullTest *nt = (NullTest *) clause;
- Assert(nt->nulltesttype == IS_NULL);
+ /* Replace the indexkey expression with an index Var. */
nt->arg = (Expr *) fix_indexqual_operand((Node *) nt->arg,
- index);
+ index,
+ indexcol);
}
else
elog(ERROR, "unsupported indexqual type: %d",
return fixed_indexquals;
}
+/*
+ * fix_indexorderby_references
+ * Adjust indexorderby clauses to the form the executor's index
+ * machinery needs.
+ *
+ * This is a simplified version of fix_indexqual_references. The input does
+ * not have RestrictInfo nodes, and we assume that indxpath.c already
+ * commuted the clauses to put the index keys on the left. Also, we don't
+ * bother to support any cases except simple OpExprs, since nothing else
+ * is allowed for ordering operators.
+ */
+static List *
+fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path)
+{
+ IndexOptInfo *index = index_path->indexinfo;
+ List *fixed_indexorderbys;
+ ListCell *lcc,
+ *lci;
+
+ fixed_indexorderbys = NIL;
+
+ forboth(lcc, index_path->indexorderbys, lci, index_path->indexorderbycols)
+ {
+ Node *clause = (Node *) lfirst(lcc);
+ int indexcol = lfirst_int(lci);
+
+ /*
+ * Replace any outer-relation variables with nestloop params.
+ *
+ * This also makes a copy of the clause, so it's safe to modify it
+ * in-place below.
+ */
+ clause = replace_nestloop_params(root, clause);
+
+ if (IsA(clause, OpExpr))
+ {
+ OpExpr *op = (OpExpr *) clause;
+
+ if (list_length(op->args) != 2)
+ elog(ERROR, "indexorderby clause is not binary opclause");
+
+ /*
+ * Now replace the indexkey expression with an index Var.
+ */
+ linitial(op->args) = fix_indexqual_operand(linitial(op->args),
+ index,
+ indexcol);
+ }
+ else
+ elog(ERROR, "unsupported indexorderby type: %d",
+ (int) nodeTag(clause));
+
+ fixed_indexorderbys = lappend(fixed_indexorderbys, clause);
+ }
+
+ return fixed_indexorderbys;
+}
+
+/*
+ * fix_indexqual_operand
+ * Convert an indexqual expression to a Var referencing the index column.
+ *
+ * We represent index keys by Var nodes having varno == INDEX_VAR and varattno
+ * equal to the index's attribute number (index column position).
+ *
+ * Most of the code here is just for sanity cross-checking that the given
+ * expression actually matches the index column it's claimed to.
+ */
static Node *
-fix_indexqual_operand(Node *node, IndexOptInfo *index)
+fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol)
{
- /*
- * We represent index keys by Var nodes having the varno of the base table
- * but varattno equal to the index's attribute number (index column
- * position). This is a bit hokey ... would be cleaner to use a
- * special-purpose node type that could not be mistaken for a regular Var.
- * But it will do for now.
- */
Var *result;
int pos;
ListCell *indexpr_item;
if (IsA(node, RelabelType))
node = (Node *) ((RelabelType *) node)->arg;
- if (IsA(node, Var) &&
- ((Var *) node)->varno == index->rel->relid)
- {
- /* Try to match against simple index columns */
- int varatt = ((Var *) node)->varattno;
+ Assert(indexcol >= 0 && indexcol < index->ncolumns);
- if (varatt != 0)
+ if (index->indexkeys[indexcol] != 0)
+ {
+ /* It's a simple index column */
+ if (IsA(node, Var) &&
+ ((Var *) node)->varno == index->rel->relid &&
+ ((Var *) node)->varattno == index->indexkeys[indexcol])
{
- for (pos = 0; pos < index->ncolumns; pos++)
- {
- if (index->indexkeys[pos] == varatt)
- {
- result = (Var *) copyObject(node);
- result->varattno = pos + 1;
- return (Node *) result;
- }
- }
+ result = (Var *) copyObject(node);
+ result->varno = INDEX_VAR;
+ result->varattno = indexcol + 1;
+ return (Node *) result;
}
+ else
+ elog(ERROR, "index key does not match expected index column");
}
- /* Try to match against index expressions */
+ /* It's an index expression, so find and cross-check the expression */
indexpr_item = list_head(index->indexprs);
for (pos = 0; pos < index->ncolumns; pos++)
{
if (index->indexkeys[pos] == 0)
{
- Node *indexkey;
-
if (indexpr_item == NULL)
elog(ERROR, "too few entries in indexprs list");
- indexkey = (Node *) lfirst(indexpr_item);
- if (indexkey && IsA(indexkey, RelabelType))
- indexkey = (Node *) ((RelabelType *) indexkey)->arg;
- if (equal(node, indexkey))
+ if (pos == indexcol)
{
- /* Found a match */
- result = makeVar(index->rel->relid, pos + 1,
- exprType(lfirst(indexpr_item)), -1,
- 0);
- return (Node *) result;
+ Node *indexkey;
+
+ indexkey = (Node *) lfirst(indexpr_item);
+ if (indexkey && IsA(indexkey, RelabelType))
+ indexkey = (Node *) ((RelabelType *) indexkey)->arg;
+ if (equal(node, indexkey))
+ {
+ result = makeVar(INDEX_VAR, indexcol + 1,
+ exprType(lfirst(indexpr_item)), -1,
+ exprCollation(lfirst(indexpr_item)),
+ 0);
+ return (Node *) result;
+ }
+ else
+ elog(ERROR, "index key does not match expected index column");
}
indexpr_item = lnext(indexpr_item);
}
}
/* Ooops... */
- elog(ERROR, "node is not an index attribute");
+ elog(ERROR, "index key does not match expected index column");
return NULL; /* keep compiler quiet */
}
temp->opfuncid = InvalidOid;
temp->opresulttype = clause->opresulttype;
temp->opretset = clause->opretset;
+ temp->opcollid = clause->opcollid;
+ temp->inputcollid = clause->inputcollid;
temp->args = list_copy(clause->args);
+ temp->location = clause->location;
/* Commute it --- note this modifies the temp node in-place. */
CommuteOpExpr(temp);
t_list = lappend(t_list, temp);
/*
* Copy cost and size info from a Path node to the Plan node created from it.
- * The executor won't use this info, but it's needed by EXPLAIN.
+ * The executor usually won't use this info, but it's needed by EXPLAIN.
*/
static void
copy_path_costsize(Plan *dest, Path *src)
{
dest->startup_cost = src->startup_cost;
dest->total_cost = src->total_cost;
- dest->plan_rows = src->parent->rows;
+ dest->plan_rows = src->rows;
dest->plan_width = src->parent->width;
}
else
/*
* Copy cost and size info from a lower plan node to an inserted node.
- * This is not critical, since the decisions have already been made,
- * but it helps produce more reasonable-looking EXPLAIN output.
- * (Some callers alter the info after copying it.)
+ * (Most callers alter the info after copying it.)
*/
static void
copy_plan_costsize(Plan *dest, Plan *src)
Oid indexid,
List *indexqual,
List *indexqualorig,
+ List *indexorderby,
+ List *indexorderbyorig,
ScanDirection indexscandir)
{
IndexScan *node = makeNode(IndexScan);
node->indexid = indexid;
node->indexqual = indexqual;
node->indexqualorig = indexqualorig;
+ node->indexorderby = indexorderby;
+ node->indexorderbyorig = indexorderbyorig;
+ node->indexorderdir = indexscandir;
+
+ return node;
+}
+
+static IndexOnlyScan *
+make_indexonlyscan(List *qptlist,
+ List *qpqual,
+ Index scanrelid,
+ Oid indexid,
+ List *indexqual,
+ List *indexorderby,
+ List *indextlist,
+ ScanDirection indexscandir)
+{
+ IndexOnlyScan *node = makeNode(IndexOnlyScan);
+ Plan *plan = &node->scan.plan;
+
+ /* cost should be inserted by caller */
+ plan->targetlist = qptlist;
+ plan->qual = qpqual;
+ plan->lefttree = NULL;
+ plan->righttree = NULL;
+ node->scan.scanrelid = scanrelid;
+ node->indexid = indexid;
+ node->indexqual = indexqual;
+ node->indexorderby = indexorderby;
+ node->indextlist = indextlist;
node->indexorderdir = indexscandir;
return node;
make_subqueryscan(List *qptlist,
List *qpqual,
Index scanrelid,
- Plan *subplan,
- List *subrtable)
+ Plan *subplan)
{
SubqueryScan *node = makeNode(SubqueryScan);
Plan *plan = &node->scan.plan;
plan->lefttree = NULL;
plan->righttree = NULL;
node->scan.scanrelid = scanrelid;
- node->subplan = subplan;
- node->subrtable = subrtable;
+ node->subplan = subplan;
+
+ return node;
+}
+
+static FunctionScan *
+make_functionscan(List *qptlist,
+ List *qpqual,
+ Index scanrelid,
+ Node *funcexpr,
+ List *funccolnames,
+ List *funccoltypes,
+ List *funccoltypmods,
+ List *funccolcollations)
+{
+ FunctionScan *node = makeNode(FunctionScan);
+ Plan *plan = &node->scan.plan;
+
+ /* cost should be inserted by caller */
+ plan->targetlist = qptlist;
+ plan->qual = qpqual;
+ plan->lefttree = NULL;
+ plan->righttree = NULL;
+ node->scan.scanrelid = scanrelid;
+ node->funcexpr = funcexpr;
+ node->funccolnames = funccolnames;
+ node->funccoltypes = funccoltypes;
+ node->funccoltypmods = funccoltypmods;
+ node->funccolcollations = funccolcollations;
+
+ return node;
+}
+
+static ValuesScan *
+make_valuesscan(List *qptlist,
+ List *qpqual,
+ Index scanrelid,
+ List *values_lists)
+{
+ ValuesScan *node = makeNode(ValuesScan);
+ Plan *plan = &node->scan.plan;
+
+ /* cost should be inserted by caller */
+ plan->targetlist = qptlist;
+ plan->qual = qpqual;
+ plan->lefttree = NULL;
+ plan->righttree = NULL;
+ node->scan.scanrelid = scanrelid;
+ node->values_lists = values_lists;
+
+ return node;
+}
+
+static CteScan *
+make_ctescan(List *qptlist,
+ List *qpqual,
+ Index scanrelid,
+ int ctePlanId,
+ int cteParam)
+{
+ CteScan *node = makeNode(CteScan);
+ Plan *plan = &node->scan.plan;
+
+ /* cost should be inserted by caller */
+ plan->targetlist = qptlist;
+ plan->qual = qpqual;
+ plan->lefttree = NULL;
+ plan->righttree = NULL;
+ node->scan.scanrelid = scanrelid;
+ node->ctePlanId = ctePlanId;
+ node->cteParam = cteParam;
return node;
}
-static FunctionScan *
-make_functionscan(List *qptlist,
- List *qpqual,
- Index scanrelid,
- Node *funcexpr,
- List *funccolnames,
- List *funccoltypes,
- List *funccoltypmods)
+static WorkTableScan *
+make_worktablescan(List *qptlist,
+ List *qpqual,
+ Index scanrelid,
+ int wtParam)
{
- FunctionScan *node = makeNode(FunctionScan);
+ WorkTableScan *node = makeNode(WorkTableScan);
Plan *plan = &node->scan.plan;
/* cost should be inserted by caller */
plan->lefttree = NULL;
plan->righttree = NULL;
node->scan.scanrelid = scanrelid;
- node->funcexpr = funcexpr;
- node->funccolnames = funccolnames;
- node->funccoltypes = funccoltypes;
- node->funccoltypmods = funccoltypmods;
+ node->wtParam = wtParam;
return node;
}
-static ValuesScan *
-make_valuesscan(List *qptlist,
- List *qpqual,
- Index scanrelid,
- List *values_lists)
+ForeignScan *
+make_foreignscan(List *qptlist,
+ List *qpqual,
+ Index scanrelid,
+ List *fdw_exprs,
+ List *fdw_private)
{
- ValuesScan *node = makeNode(ValuesScan);
+ ForeignScan *node = makeNode(ForeignScan);
Plan *plan = &node->scan.plan;
- /* cost should be inserted by caller */
+ /* cost will be filled in by create_foreignscan_plan */
plan->targetlist = qptlist;
plan->qual = qpqual;
plan->lefttree = NULL;
plan->righttree = NULL;
node->scan.scanrelid = scanrelid;
- node->values_lists = values_lists;
+ node->fdw_exprs = fdw_exprs;
+ node->fdw_private = fdw_private;
+ /* fsSystemCol will be filled in by create_foreignscan_plan */
+ node->fsSystemCol = false;
return node;
}
Append *
-make_append(List *appendplans, bool isTarget, List *tlist)
+make_append(List *appendplans, List *tlist)
{
Append *node = makeNode(Append);
Plan *plan = &node->plan;
* 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->lefttree = NULL;
plan->righttree = NULL;
node->appendplans = appendplans;
- node->isTarget = isTarget;
+
+ return node;
+}
+
+RecursiveUnion *
+make_recursive_union(List *tlist,
+ Plan *lefttree,
+ Plan *righttree,
+ int wtParam,
+ List *distinctList,
+ long numGroups)
+{
+ RecursiveUnion *node = makeNode(RecursiveUnion);
+ Plan *plan = &node->plan;
+ int numCols = list_length(distinctList);
+
+ cost_recursive_union(plan, lefttree, righttree);
+
+ plan->targetlist = tlist;
+ plan->qual = NIL;
+ plan->lefttree = lefttree;
+ plan->righttree = righttree;
+ node->wtParam = wtParam;
+
+ /*
+ * convert SortGroupClause list into arrays of attr indexes and equality
+ * operators, as wanted by executor
+ */
+ node->numCols = numCols;
+ if (numCols > 0)
+ {
+ int keyno = 0;
+ AttrNumber *dupColIdx;
+ Oid *dupOperators;
+ ListCell *slitem;
+
+ dupColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
+ dupOperators = (Oid *) palloc(sizeof(Oid) * numCols);
+
+ foreach(slitem, distinctList)
+ {
+ SortGroupClause *sortcl = (SortGroupClause *) lfirst(slitem);
+ TargetEntry *tle = get_sortgroupclause_tle(sortcl,
+ plan->targetlist);
+
+ dupColIdx[keyno] = tle->resno;
+ dupOperators[keyno] = sortcl->eqop;
+ Assert(OidIsValid(dupOperators[keyno]));
+ keyno++;
+ }
+ node->dupColIdx = dupColIdx;
+ node->dupOperators = dupOperators;
+ }
+ node->numGroups = numGroups;
return node;
}
make_nestloop(List *tlist,
List *joinclauses,
List *otherclauses,
+ List *nestParams,
Plan *lefttree,
Plan *righttree,
JoinType jointype)
plan->righttree = righttree;
node->join.jointype = jointype;
node->join.joinqual = joinclauses;
+ node->nestParams = nestParams;
return node;
}
}
static Hash *
-make_hash(Plan *lefttree)
+make_hash(Plan *lefttree,
+ Oid skewTable,
+ AttrNumber skewColumn,
+ bool skewInherit,
+ Oid skewColType,
+ int32 skewColTypmod)
{
Hash *node = makeNode(Hash);
Plan *plan = &node->plan;
plan->lefttree = lefttree;
plan->righttree = NULL;
+ node->skewTable = skewTable;
+ node->skewColumn = skewColumn;
+ node->skewInherit = skewInherit;
+ node->skewColType = skewColType;
+ node->skewColTypmod = skewColTypmod;
+
return node;
}
List *otherclauses,
List *mergeclauses,
Oid *mergefamilies,
+ Oid *mergecollations,
int *mergestrategies,
bool *mergenullsfirst,
Plan *lefttree,
plan->righttree = righttree;
node->mergeclauses = mergeclauses;
node->mergeFamilies = mergefamilies;
+ node->mergeCollations = mergecollations;
node->mergeStrategies = mergestrategies;
node->mergeNullsFirst = mergenullsfirst;
node->join.jointype = jointype;
/*
* make_sort --- basic routine to build a Sort plan node
*
- * Caller must have built the sortColIdx, sortOperators, and nullsFirst
- * arrays already. limit_tuples is as for cost_sort (in particular, pass
- * -1 if no limit)
+ * 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,
- AttrNumber *sortColIdx, Oid *sortOperators, bool *nullsFirst,
+ AttrNumber *sortColIdx, Oid *sortOperators,
+ Oid *collations, bool *nullsFirst,
double limit_tuples)
{
Sort *node = makeNode(Sort);
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;
node->numCols = numCols;
node->sortColIdx = sortColIdx;
node->sortOperators = sortOperators;
+ node->collations = collations;
node->nullsFirst = nullsFirst;
return node;
}
/*
- * add_sort_column --- utility subroutine for building sort info arrays
+ * prepare_sort_from_pathkeys
+ * Prepare to sort according to given pathkeys
*
- * We need this routine because the same column might be selected more than
- * once as a sort key column; if so, the extra mentions are redundant.
- *
- * Caller is assumed to have allocated the arrays large enough for the
- * max possible number of columns. Return value is the new column count.
- */
-static int
-add_sort_column(AttrNumber colIdx, Oid sortOp, bool nulls_first,
- int numCols, AttrNumber *sortColIdx,
- Oid *sortOperators, bool *nullsFirst)
-{
- int i;
-
- Assert(OidIsValid(sortOp));
-
- for (i = 0; i < numCols; i++)
- {
- /*
- * Note: we check sortOp because it's conceivable that "ORDER BY foo
- * USING <, foo USING <<<" is not redundant, if <<< distinguishes
- * values that < considers equal. We need not check nulls_first
- * however because a lower-order column with the same sortop but
- * opposite nulls direction is redundant.
- */
- if (sortColIdx[i] == colIdx &&
- sortOperators[numCols] == sortOp)
- {
- /* Already sorting by this col, so extra sort key is useless */
- return numCols;
- }
- }
-
- /* Add the column */
- sortColIdx[numCols] = colIdx;
- sortOperators[numCols] = sortOp;
- nullsFirst[numCols] = nulls_first;
- return numCols + 1;
-}
-
-/*
- * make_sort_from_pathkeys
- * Create sort plan 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.
*
- * 'lefttree' is the node which yields input tuples
+ * Input parameters:
+ * 'lefttree' is the plan node which yields input tuples
* 'pathkeys' is the list of pathkeys by which the result is to be sorted
- * 'limit_tuples' is the bound on the number of output tuples;
- * -1 if no bound
+ * 'relids' identifies the child relation being sorted, if any
+ * 'reqColIdx' is NULL or an array of required sort key column numbers
+ * 'adjust_tlist_in_place' is TRUE if lefttree must be modified in-place
*
* We must convert the pathkey information into arrays of sort key column
- * numbers and sort operator OIDs.
+ * numbers, sort operator OIDs, collation OIDs, and nulls-first flags,
+ * which is the representation the executor wants. These are returned into
+ * the output parameters *p_numsortkeys etc.
+ *
+ * When looking for matches to an EquivalenceClass's members, we will only
+ * consider child EC members if they match 'relids'. This protects against
+ * possible incorrect matches to child expressions that contain no Vars.
+ *
+ * If reqColIdx isn't NULL then it contains sort key column numbers that
+ * we should match. This is used when making child plans for a MergeAppend;
+ * it's an error if we can't match the columns.
*
* 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 node itself won't do it).
- * If the input plan type isn't one that can do projections, this means
- * adding a Result node just to do the projection.
+ * compute these expressions, since the Sort/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
+ * lefttree tlist to be modified in-place regardless of whether the node type
+ * can project --- we use this for fixing the tlist of MergeAppend itself.
+ *
+ * Returns the node which is to be the input to the Sort (either lefttree,
+ * or a Result stacked atop lefttree).
*/
-Sort *
-make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
- double limit_tuples)
+static Plan *
+prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+ Relids relids,
+ const AttrNumber *reqColIdx,
+ bool adjust_tlist_in_place,
+ int *p_numsortkeys,
+ AttrNumber **p_sortColIdx,
+ Oid **p_sortOperators,
+ Oid **p_collations,
+ bool **p_nullsFirst)
{
List *tlist = lefttree->targetlist;
ListCell *i;
int numsortkeys;
AttrNumber *sortColIdx;
Oid *sortOperators;
+ Oid *collations;
bool *nullsFirst;
/*
numsortkeys = list_length(pathkeys);
sortColIdx = (AttrNumber *) palloc(numsortkeys * sizeof(AttrNumber));
sortOperators = (Oid *) palloc(numsortkeys * sizeof(Oid));
+ collations = (Oid *) palloc(numsortkeys * sizeof(Oid));
nullsFirst = (bool *) palloc(numsortkeys * sizeof(bool));
numsortkeys = 0;
{
PathKey *pathkey = (PathKey *) lfirst(i);
EquivalenceClass *ec = pathkey->pk_eclass;
+ EquivalenceMember *em;
TargetEntry *tle = NULL;
Oid pk_datatype = InvalidOid;
Oid sortop;
Assert(list_length(ec->ec_members) == 1);
pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
}
+ else if (reqColIdx != NULL)
+ {
+ /*
+ * If we are given a sort column number to match, only consider
+ * the single TLE at that position. It's possible that there is
+ * no such TLE, in which case fall through and generate a resjunk
+ * targetentry (we assume this must have happened in the parent
+ * plan as well). If there is a TLE but it doesn't match the
+ * pathkey's EC, we do the same, which is probably the wrong thing
+ * but we'll leave it to caller to complain about the mismatch.
+ */
+ tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
+ if (tle)
+ {
+ em = find_ec_member_for_tle(ec, tle, relids);
+ if (em)
+ {
+ /* found expr at right place in tlist */
+ pk_datatype = em->em_datatype;
+ }
+ else
+ tle = NULL;
+ }
+ }
else
{
/*
* Otherwise, we can sort by any non-constant expression listed in
- * the pathkey's EquivalenceClass. For now, we take the first one
- * that corresponds to an available item in the tlist. If there
- * isn't any, use the first one that is an expression in the
- * input's vars. (The non-const restriction only matters if the
- * EC is below_outer_join; but if it isn't, it won't contain
- * consts anyway, else we'd have discarded the pathkey as
+ * the pathkey's EquivalenceClass. For now, we take the first
+ * tlist item found in the EC. If there's no match, we'll generate
+ * a resjunk entry using the first EC member that is an expression
+ * in the input's vars. (The non-const restriction only matters
+ * if the EC is below_outer_join; but if it isn't, it won't
+ * contain consts anyway, else we'd have discarded the pathkey as
* redundant.)
*
* XXX if we have a choice, is there any way of figuring out which
* in the same equivalence class...) Not clear that we ever will
* have an interesting choice in practice, so it may not matter.
*/
+ foreach(j, tlist)
+ {
+ tle = (TargetEntry *) lfirst(j);
+ em = find_ec_member_for_tle(ec, tle, relids);
+ if (em)
+ {
+ /* found expr already in tlist */
+ pk_datatype = em->em_datatype;
+ break;
+ }
+ tle = NULL;
+ }
+ }
+
+ if (!tle)
+ {
+ /*
+ * No matching tlist item; look for a computable expression. Note
+ * 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).
+ */
+ Expr *sortexpr = NULL;
+
foreach(j, ec->ec_members)
{
EquivalenceMember *em = (EquivalenceMember *) lfirst(j);
+ List *exprvars;
+ ListCell *k;
- if (em->em_is_const || em->em_is_child)
+ /*
+ * We shouldn't be trying to sort by an equivalence class that
+ * contains a constant, so no need to consider such cases any
+ * further.
+ */
+ if (em->em_is_const)
continue;
- tle = tlist_member((Node *) em->em_expr, tlist);
- if (tle)
- {
- pk_datatype = em->em_datatype;
- break; /* found expr already in tlist */
- }
-
/*
- * We can also use it if the pathkey expression is a relabel
- * of the tlist entry, or vice versa. This is needed for
- * binary-compatible cases (cf. make_pathkey_from_sortinfo).
- * We prefer an exact match, though, so we do the basic search
- * first.
+ * Ignore child members unless they match the rel being
+ * sorted.
*/
- tle = tlist_member_ignore_relabel((Node *) em->em_expr, tlist);
- if (tle)
+ if (em->em_is_child &&
+ !bms_equal(em->em_relids, relids))
+ continue;
+
+ sortexpr = em->em_expr;
+ exprvars = pull_var_clause((Node *) sortexpr,
+ PVC_INCLUDE_AGGREGATES,
+ PVC_INCLUDE_PLACEHOLDERS);
+ foreach(k, exprvars)
+ {
+ if (!tlist_member_ignore_relabel(lfirst(k), tlist))
+ break;
+ }
+ list_free(exprvars);
+ if (!k)
{
pk_datatype = em->em_datatype;
- break; /* found expr already in tlist */
+ break; /* found usable expression */
}
}
+ if (!j)
+ elog(ERROR, "could not find pathkey item to sort");
- if (!tle)
+ /*
+ * Do we need to insert a Result node?
+ */
+ if (!adjust_tlist_in_place &&
+ !is_projection_capable_plan(lefttree))
{
- /* No matching tlist item; look for a computable expression */
- Expr *sortexpr = NULL;
-
- foreach(j, ec->ec_members)
- {
- EquivalenceMember *em = (EquivalenceMember *) lfirst(j);
- List *exprvars;
- ListCell *k;
-
- if (em->em_is_const || em->em_is_child)
- continue;
- sortexpr = em->em_expr;
- exprvars = pull_var_clause((Node *) sortexpr, false);
- foreach(k, exprvars)
- {
- if (!tlist_member_ignore_relabel(lfirst(k), tlist))
- break;
- }
- list_free(exprvars);
- if (!k)
- {
- pk_datatype = em->em_datatype;
- break; /* found usable expression */
- }
- }
- if (!j)
- elog(ERROR, "could not find pathkey item to sort");
+ /* copy needed so we don't modify input's tlist below */
+ tlist = copyObject(tlist);
+ lefttree = (Plan *) make_result(root, tlist, NULL,
+ lefttree);
+ }
- /*
- * Do we need to insert a Result node?
- */
- if (!is_projection_capable_plan(lefttree))
- {
- /* copy needed so we don't modify input's tlist below */
- tlist = copyObject(tlist);
- lefttree = (Plan *) make_result(root, tlist, NULL,
- lefttree);
- }
+ /* Don't bother testing is_projection_capable_plan again */
+ adjust_tlist_in_place = true;
- /*
- * Add resjunk entry to input's tlist
- */
- tle = makeTargetEntry(sortexpr,
- list_length(tlist) + 1,
- NULL,
- true);
- tlist = lappend(tlist, tle);
- lefttree->targetlist = tlist; /* just in case NIL before */
- }
+ /*
+ * Add resjunk entry to input's tlist
+ */
+ tle = makeTargetEntry(sortexpr,
+ list_length(tlist) + 1,
+ NULL,
+ true);
+ tlist = lappend(tlist, tle);
+ lefttree->targetlist = tlist; /* just in case NIL before */
}
/*
pathkey->pk_strategy, pk_datatype, pk_datatype,
pathkey->pk_opfamily);
+ /* Add the column to the sort arrays */
+ sortColIdx[numsortkeys] = tle->resno;
+ sortOperators[numsortkeys] = sortop;
+ collations[numsortkeys] = ec->ec_collation;
+ nullsFirst[numsortkeys] = pathkey->pk_nulls_first;
+ numsortkeys++;
+ }
+
+ /* Return results */
+ *p_numsortkeys = numsortkeys;
+ *p_sortColIdx = sortColIdx;
+ *p_sortOperators = sortOperators;
+ *p_collations = collations;
+ *p_nullsFirst = nullsFirst;
+
+ return lefttree;
+}
+
+/*
+ * find_ec_member_for_tle
+ * Locate an EquivalenceClass member matching the given TLE, if any
+ *
+ * Child EC members are ignored unless they match 'relids'.
+ */
+static EquivalenceMember *
+find_ec_member_for_tle(EquivalenceClass *ec,
+ TargetEntry *tle,
+ Relids relids)
+{
+ Expr *tlexpr;
+ ListCell *lc;
+
+ /* We ignore binary-compatible relabeling on both ends */
+ tlexpr = tle->expr;
+ while (tlexpr && IsA(tlexpr, RelabelType))
+ tlexpr = ((RelabelType *) tlexpr)->arg;
+
+ foreach(lc, ec->ec_members)
+ {
+ EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+ Expr *emexpr;
+
+ /*
+ * We shouldn't be trying to sort by an equivalence class that
+ * contains a constant, so no need to consider such cases any further.
+ */
+ if (em->em_is_const)
+ continue;
+
/*
- * The column might already be selected as a sort key, if the pathkeys
- * contain duplicate entries. (This can happen in scenarios where
- * multiple mergejoinable clauses mention the same var, for example.)
- * So enter it only once in the sort arrays.
+ * Ignore child members unless they match the rel being sorted.
*/
- numsortkeys = add_sort_column(tle->resno,
- sortop,
- pathkey->pk_nulls_first,
- numsortkeys,
- sortColIdx, sortOperators, nullsFirst);
+ if (em->em_is_child &&
+ !bms_equal(em->em_relids, relids))
+ continue;
+
+ /* Match if same expression (after stripping relabel) */
+ emexpr = em->em_expr;
+ while (emexpr && IsA(emexpr, RelabelType))
+ emexpr = ((RelabelType *) emexpr)->arg;
+
+ if (equal(emexpr, tlexpr))
+ return em;
}
- Assert(numsortkeys > 0);
+ return NULL;
+}
+
+/*
+ * make_sort_from_pathkeys
+ * Create sort plan to sort according to given pathkeys
+ *
+ * '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)
+{
+ int numsortkeys;
+ AttrNumber *sortColIdx;
+ Oid *sortOperators;
+ Oid *collations;
+ bool *nullsFirst;
+ /* Compute sort column info, and adjust lefttree as needed */
+ lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
+ NULL,
+ NULL,
+ false,
+ &numsortkeys,
+ &sortColIdx,
+ &sortOperators,
+ &collations,
+ &nullsFirst);
+
+ /* Now build the Sort node */
return make_sort(root, lefttree, numsortkeys,
- sortColIdx, sortOperators, nullsFirst, limit_tuples);
+ sortColIdx, sortOperators, collations,
+ nullsFirst, limit_tuples);
}
/*
int numsortkeys;
AttrNumber *sortColIdx;
Oid *sortOperators;
+ Oid *collations;
bool *nullsFirst;
- /*
- * We will need at most list_length(sortcls) sort columns; possibly less
- */
+ /* Convert list-ish representation to arrays wanted by executor */
numsortkeys = list_length(sortcls);
sortColIdx = (AttrNumber *) palloc(numsortkeys * sizeof(AttrNumber));
sortOperators = (Oid *) palloc(numsortkeys * sizeof(Oid));
+ collations = (Oid *) palloc(numsortkeys * sizeof(Oid));
nullsFirst = (bool *) palloc(numsortkeys * sizeof(bool));
numsortkeys = 0;
-
foreach(l, sortcls)
{
SortGroupClause *sortcl = (SortGroupClause *) lfirst(l);
TargetEntry *tle = get_sortgroupclause_tle(sortcl, sub_tlist);
- /*
- * Check for the possibility of duplicate order-by clauses --- the
- * parser should have removed 'em, but no point in sorting
- * redundantly.
- */
- numsortkeys = add_sort_column(tle->resno, sortcl->sortop,
- sortcl->nulls_first,
- numsortkeys,
- sortColIdx, sortOperators, nullsFirst);
+ sortColIdx[numsortkeys] = tle->resno;
+ sortOperators[numsortkeys] = sortcl->sortop;
+ collations[numsortkeys] = exprCollation((Node *) tle->expr);
+ nullsFirst[numsortkeys] = sortcl->nulls_first;
+ numsortkeys++;
}
- Assert(numsortkeys > 0);
-
return make_sort(root, lefttree, numsortkeys,
- sortColIdx, sortOperators, nullsFirst, -1.0);
+ sortColIdx, sortOperators, collations,
+ nullsFirst, -1.0);
}
/*
Plan *lefttree)
{
List *sub_tlist = lefttree->targetlist;
- int grpno = 0;
ListCell *l;
int numsortkeys;
AttrNumber *sortColIdx;
Oid *sortOperators;
+ Oid *collations;
bool *nullsFirst;
- /*
- * We will need at most list_length(groupcls) sort columns; possibly less
- */
+ /* Convert list-ish representation to arrays wanted by executor */
numsortkeys = list_length(groupcls);
sortColIdx = (AttrNumber *) palloc(numsortkeys * sizeof(AttrNumber));
sortOperators = (Oid *) palloc(numsortkeys * sizeof(Oid));
+ collations = (Oid *) palloc(numsortkeys * sizeof(Oid));
nullsFirst = (bool *) palloc(numsortkeys * sizeof(bool));
numsortkeys = 0;
-
foreach(l, groupcls)
{
SortGroupClause *grpcl = (SortGroupClause *) lfirst(l);
- TargetEntry *tle = get_tle_by_resno(sub_tlist, grpColIdx[grpno]);
+ TargetEntry *tle = get_tle_by_resno(sub_tlist, grpColIdx[numsortkeys]);
- /*
- * Check for the possibility of duplicate group-by clauses --- the
- * parser should have removed 'em, but no point in sorting
- * redundantly.
- */
- numsortkeys = add_sort_column(tle->resno, grpcl->sortop,
- grpcl->nulls_first,
- numsortkeys,
- sortColIdx, sortOperators, nullsFirst);
- grpno++;
+ sortColIdx[numsortkeys] = tle->resno;
+ sortOperators[numsortkeys] = grpcl->sortop;
+ collations[numsortkeys] = exprCollation((Node *) tle->expr);
+ nullsFirst[numsortkeys] = grpcl->nulls_first;
+ numsortkeys++;
}
- Assert(numsortkeys > 0);
-
return make_sort(root, lefttree, numsortkeys,
- sortColIdx, sortOperators, nullsFirst, -1.0);
+ sortColIdx, sortOperators, collations,
+ nullsFirst, -1.0);
}
static Material *
/* Set cost data */
cost_material(&matpath,
+ subplan->startup_cost,
subplan->total_cost,
subplan->plan_rows,
subplan->plan_width);
Agg *
make_agg(PlannerInfo *root, List *tlist, List *qual,
- AggStrategy aggstrategy,
+ AggStrategy aggstrategy, const AggClauseCosts *aggcosts,
int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators,
- long numGroups, int numAggs,
+ long numGroups,
Plan *lefttree)
{
Agg *node = makeNode(Agg);
copy_plan_costsize(plan, lefttree); /* only care about copying size */
cost_agg(&agg_path, root,
- aggstrategy, numAggs,
+ aggstrategy, aggcosts,
numGroupCols, numGroups,
lefttree->startup_cost,
lefttree->total_cost,
* anything for Aggref nodes; this is okay since they are really
* comparable to Vars.
*
- * See notes in grouping_planner about why this routine and make_group are
- * the only ones in this file that worry about tlist eval cost.
+ * 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)
{
plan->total_cost += qual_cost.startup;
plan->total_cost += qual_cost.per_tuple * plan->plan_rows;
}
- cost_qual_eval(&qual_cost, tlist, 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;
return node;
}
+WindowAgg *
+make_windowagg(PlannerInfo *root, List *tlist,
+ List *windowFuncs, Index winref,
+ int partNumCols, AttrNumber *partColIdx, Oid *partOperators,
+ int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators,
+ int frameOptions, Node *startOffset, Node *endOffset,
+ Plan *lefttree)
+{
+ WindowAgg *node = makeNode(WindowAgg);
+ Plan *plan = &node->plan;
+ Path windowagg_path; /* dummy for result of cost_windowagg */
+
+ node->winref = winref;
+ node->partNumCols = partNumCols;
+ node->partColIdx = partColIdx;
+ node->partOperators = partOperators;
+ node->ordNumCols = ordNumCols;
+ node->ordColIdx = ordColIdx;
+ node->ordOperators = ordOperators;
+ node->frameOptions = frameOptions;
+ 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;
+ /* WindowAgg nodes never have a qual clause */
+ plan->qual = NIL;
+
+ return node;
+}
+
Group *
make_group(PlannerInfo *root,
List *tlist,
* lower plan level and will only be copied by the Group node. Worth
* fixing?
*
- * See notes in grouping_planner about why this routine and make_agg are
- * the only ones in this file that worry about tlist eval cost.
+ * 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)
{
plan->total_cost += qual_cost.startup;
plan->total_cost += qual_cost.per_tuple * plan->plan_rows;
}
- cost_qual_eval(&qual_cost, tlist, 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;
* already be sorted accordingly.
*/
SetOp *
-make_setop(SetOpCmd cmd, Plan *lefttree,
- List *distinctList, AttrNumber flagColIdx)
+make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree,
+ List *distinctList, AttrNumber flagColIdx, int firstFlag,
+ long numGroups, double outputRows)
{
SetOp *node = makeNode(SetOp);
Plan *plan = &node->plan;
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 * plan->plan_rows * numCols;
-
- /*
- * We make the unsupported assumption that there will be 10% as many
- * tuples out as in. Any way to do better?
- */
- plan->plan_rows *= 0.1;
- if (plan->plan_rows < 1)
- plan->plan_rows = 1;
+ plan->total_cost += cpu_operator_cost * lefttree->plan_rows * numCols;
plan->targetlist = lefttree->targetlist;
plan->qual = NIL;
}
node->cmd = cmd;
+ node->strategy = strategy;
node->numCols = numCols;
node->dupColIdx = dupColIdx;
node->dupOperators = dupOperators;
node->flagColIdx = flagColIdx;
+ node->firstFlag = firstFlag;
+ node->numGroups = numGroups;
+
+ return node;
+}
+
+/*
+ * make_lockrows
+ * Build a LockRows plan node
+ */
+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;
+ plan->righttree = NULL;
+
+ node->rowMarks = rowMarks;
+ node->epqParam = epqParam;
return node;
}
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 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 *
+make_modifytable(CmdType operation, bool canSetTag,
+ List *resultRelations,
+ List *subplans, List *returningLists,
+ List *rowMarks, int epqParam)
+{
+ ModifyTable *node = makeNode(ModifyTable);
+ Plan *plan = &node->plan;
+ double total_size;
+ ListCell *subnode;
+
+ Assert(list_length(resultRelations) == list_length(subplans));
+ 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;
+ /* setrefs.c will fill in the targetlist, if needed */
+ node->plan.targetlist = NIL;
+
+ node->operation = operation;
+ node->canSetTag = canSetTag;
+ node->resultRelations = resultRelations;
+ node->resultRelIndex = -1; /* will be set correctly in setrefs.c */
+ node->plans = subplans;
+ node->returningLists = returningLists;
+ node->rowMarks = rowMarks;
+ node->epqParam = epqParam;
+
+ return node;
+}
+
/*
* is_projection_capable_plan
* Check whether a given Plan node is able to do projection.
case T_Sort:
case T_Unique:
case T_SetOp:
+ case T_LockRows:
case T_Limit:
+ case T_ModifyTable:
case T_Append:
+ case T_MergeAppend:
+ case T_RecursiveUnion:
return false;
default:
break;