X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Foptimizer%2Fplan%2Fcreateplan.c;h=030f420c90eb37946ee333250de54af61d9b82d7;hb=46c508fbcf98ac334f1e831d21021d731c882fbb;hp=5c35f77ec2d67d9c400c9a07724d5b6e36209bd0;hpb=3f56ca1d4985bd61af329474a3c654a1eb360c47;p=postgresql diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 5c35f77ec2..030f420c90 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -5,12 +5,12 @@ * Planning is complete, we just need to convert the selected * Path into a Plan. * - * Portions Copyright (c) 1996-2010, 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.272 2010/02/19 21:49:10 tgl Exp $ + * src/backend/optimizer/plan/createplan.c * *------------------------------------------------------------------------- */ @@ -20,14 +20,20 @@ #include #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" @@ -35,6 +41,7 @@ #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(PlannerInfo *root, RelOptInfo *rel); @@ -42,18 +49,19 @@ 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 **indexqual); + 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, @@ -66,13 +74,21 @@ 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 *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); @@ -80,7 +96,13 @@ static void copy_plan_costsize(Plan *dest, Plan *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); @@ -93,7 +115,8 @@ static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid, 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, @@ -103,7 +126,7 @@ static WorkTableScan *make_worktablescan(List *qptlist, List *qpqual, 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, @@ -121,20 +144,35 @@ 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. * @@ -151,10 +189,43 @@ create_plan(PlannerInfo *root, Path *best_path) { 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: @@ -162,13 +233,9 @@ create_plan(PlannerInfo *root, Path *best_path) case T_ValuesScan: case T_CteScan: case T_WorkTableScan: + case T_ForeignScan: plan = create_scan_plan(root, best_path); break; - case T_Join: - /* this is only used for no-op joins */ - Assert(IsA(best_path, NoOpPath)); - plan = create_plan(root, ((NoOpPath *) best_path)->subpath); - break; case T_HashJoin: case T_MergeJoin: case T_NestLoop: @@ -179,6 +246,10 @@ create_plan(PlannerInfo *root, Path *best_path) 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); @@ -223,14 +294,33 @@ create_scan_plan(PlannerInfo *root, Path *best_path) */ 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 @@ -238,6 +328,16 @@ create_scan_plan(PlannerInfo *root, Path *best_path) */ 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: @@ -251,7 +351,16 @@ create_scan_plan(PlannerInfo *root, Path *best_path) 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: @@ -303,6 +412,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path) 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); @@ -418,6 +534,7 @@ disuse_physical_tlist(Plan *plan, Path *path) { case T_SeqScan: case T_IndexScan: + case T_IndexOnlyScan: case T_BitmapHeapScan: case T_TidScan: case T_SubqueryScan: @@ -425,6 +542,7 @@ disuse_physical_tlist(Plan *plan, Path *path) case T_ValuesScan: case T_CteScan: case T_WorkTableScan: + case T_ForeignScan: plan->targetlist = build_relation_tlist(path->parent); break; default: @@ -482,9 +600,16 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path) Plan *outer_plan; Plan *inner_plan; Plan *plan; + Relids saveOuterRels = root->curOuterRels; + + outer_plan = create_plan_recurse(root, best_path->outerjoinpath); - 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) { @@ -501,6 +626,10 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path) 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, @@ -576,7 +705,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path) { Path *subpath = (Path *) lfirst(subpaths); - subplans = lappend(subplans, create_plan(root, subpath)); + subplans = lappend(subplans, create_plan_recurse(root, subpath)); } plan = make_append(subplans, tlist); @@ -584,6 +713,107 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path) 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'. @@ -621,7 +851,7 @@ create_material_plan(PlannerInfo *root, MaterialPath *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); @@ -655,7 +885,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) 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) @@ -742,7 +972,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) 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. @@ -772,11 +1002,11 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) build_relation_tlist(best_path->path.parent), NIL, AGG_HASHED, + NULL, numGroupCols, groupColIdx, groupOperators, numGroups, - 0, subplan); } else @@ -789,6 +1019,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) { Oid in_oper = lfirst_oid(l); Oid sortop; + Oid eqop; TargetEntry *tle; SortGroupClause *sortcl; @@ -796,15 +1027,29 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) 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++; } @@ -813,7 +1058,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) } /* 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; } @@ -848,6 +1093,13 @@ create_seqscan_plan(PlannerInfo *root, Path *best_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); + } + scan_plan = make_seqscan(tlist, scan_clauses, scan_relid); @@ -862,24 +1114,28 @@ create_seqscan_plan(PlannerInfo *root, Path *best_path, * 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); @@ -893,39 +1149,39 @@ create_indexscan_plan(PlannerInfo *root, /* * 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 @@ -941,20 +1197,22 @@ create_indexscan_plan(PlannerInfo *root, 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_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); @@ -966,18 +1224,47 @@ create_indexscan_plan(PlannerInfo *root, /* 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; } @@ -997,6 +1284,7 @@ create_bitmap_scan_plan(PlannerInfo *root, Plan *bitmapqualplan; List *bitmapqualorig; List *indexquals; + List *indexECs; List *qpqual; ListCell *l; BitmapHeapScan *scan_plan; @@ -1007,66 +1295,63 @@ create_bitmap_scan_plan(PlannerInfo *root, /* Process the bitmapqual tree into a Plan tree and qual lists */ bitmapqualplan = create_bitmap_subplan(root, best_path->bitmapqual, - &bitmapqualorig, &indexquals); - - /* 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 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 + * 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 and indexquals. Bitmap scans have * to do it that way because predicate conditions need to be rechecked if - * the scan becomes lossy. + * 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; + Assert(IsA(rinfo, RestrictInfo)); + if (rinfo->pseudoconstant) + continue; /* we may drop pseudoconstants here */ if (list_member(indexquals, clause)) - continue; + continue; /* simple duplicate */ + if (rinfo->parent_ec && list_member_ptr(indexECs, rinfo->parent_ec)) + continue; /* derived from same EquivalenceClass */ if (!contain_mutable_functions(clause)) { List *clausel = list_make1(clause); if (predicate_implied_by(clausel, indexquals)) - continue; + 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 @@ -1075,6 +1360,19 @@ create_bitmap_scan_plan(PlannerInfo *root, */ 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, @@ -1083,8 +1381,6 @@ create_bitmap_scan_plan(PlannerInfo *root, 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; } @@ -1101,12 +1397,20 @@ create_bitmap_scan_plan(PlannerInfo *root, * 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 **indexqual) + List **qual, List **indexqual, List **indexECs) { Plan *plan; @@ -1116,6 +1420,7 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, List *subplans = NIL; List *subquals = NIL; List *subindexquals = NIL; + List *subindexECs = NIL; ListCell *l; /* @@ -1130,12 +1435,16 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, Plan *subplan; List *subqual; List *subindexqual; + List *subindexEC; subplan = create_bitmap_subplan(root, (Path *) lfirst(l), - &subqual, &subindexqual); + &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; @@ -1145,6 +1454,7 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, plan->plan_width = 0; /* meaningless */ *qual = subquals; *indexqual = subindexquals; + *indexECs = subindexECs; } else if (IsA(bitmapqual, BitmapOrPath)) { @@ -1170,9 +1480,11 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, Plan *subplan; List *subqual; List *subindexqual; + List *subindexEC; subplan = create_bitmap_subplan(root, (Path *) lfirst(l), - &subqual, &subindexqual); + &subqual, &subindexqual, + &subindexEC); subplans = lappend(subplans, subplan); if (subqual == NIL) const_true_subqual = true; @@ -1221,15 +1533,19 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, *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, @@ -1258,6 +1574,15 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, *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 { @@ -1279,6 +1604,7 @@ create_tidscan_plan(PlannerInfo *root, TidPath *best_path, { TidScan *scan_plan; Index scan_relid = best_path->path.parent->relid; + List *tidquals = best_path->tidquals; List *ortidquals; /* it should be a base rel... */ @@ -1291,11 +1617,20 @@ create_tidscan_plan(PlannerInfo *root, TidPath *best_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->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); @@ -1303,7 +1638,7 @@ create_tidscan_plan(PlannerInfo *root, TidPath *best_path, scan_plan = make_tidscan(tlist, scan_clauses, scan_relid, - best_path->tidquals); + tidquals); copy_path_costsize(&scan_plan->scan.plan, &best_path->path); @@ -1332,12 +1667,19 @@ create_subqueryscan_plan(PlannerInfo *root, Path *best_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->subrowmark); + best_path->parent->subplan); copy_path_costsize(&scan_plan->scan.plan, best_path); @@ -1356,11 +1698,13 @@ create_functionscan_plan(PlannerInfo *root, Path *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); @@ -1368,11 +1712,21 @@ create_functionscan_plan(PlannerInfo *root, Path *best_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); + /* 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); @@ -1391,11 +1745,13 @@ create_valuesscan_plan(PlannerInfo *root, Path *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); @@ -1403,8 +1759,18 @@ create_valuesscan_plan(PlannerInfo *root, Path *best_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); + /* 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); @@ -1489,6 +1855,13 @@ create_ctescan_plan(PlannerInfo *root, Path *best_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); + } + scan_plan = make_ctescan(tlist, scan_clauses, scan_relid, plan_id, cte_param_id); @@ -1542,6 +1915,13 @@ create_worktablescan_plan(PlannerInfo *root, Path *best_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); + } + scan_plan = make_worktablescan(tlist, scan_clauses, scan_relid, cteroot->wt_param_id); @@ -1550,6 +1930,80 @@ create_worktablescan_plan(PlannerInfo *root, Path *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; +} + /***************************************************************************** * @@ -1563,21 +2017,16 @@ create_nestloop_plan(PlannerInfo *root, 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; - NestLoop *join_plan; - - /* - * If the inner path is a nestloop inner indexscan, it might be using some - * of the join quals as index quals, in which case we don't have to check - * them again at the join node. Remove any join quals that are redundant. - */ - joinrestrictclauses = - select_nonredundant_join_clauses(root, - joinrestrictclauses, - best_path->innerjoinpath); + Relids outerrelids; + List *nestParams; + ListCell *cell; + ListCell *prev; + ListCell *next; /* Sort join qual clauses into best execution order */ joinrestrictclauses = order_qual_clauses(root, joinrestrictclauses); @@ -1596,9 +2045,54 @@ create_nestloop_plan(PlannerInfo *root, 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); @@ -1622,6 +2116,7 @@ create_mergejoin_plan(PlannerInfo *root, List *innerpathkeys; int nClauses; Oid *mergefamilies; + Oid *mergecollations; int *mergestrategies; bool *mergenullsfirst; MergeJoin *join_plan; @@ -1655,6 +2150,18 @@ create_mergejoin_plan(PlannerInfo *root, 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 @@ -1694,8 +2201,8 @@ create_mergejoin_plan(PlannerInfo *root, innerpathkeys = best_path->jpath.innerjoinpath->pathkeys; /* - * If specified, add a materialize node to shield the inner plan from - * the need to handle mark/restore. + * If specified, add a materialize node to shield the inner plan from the + * need to handle mark/restore. */ if (best_path->materialize_inner) { @@ -1704,7 +2211,7 @@ create_mergejoin_plan(PlannerInfo *root, /* * We assume the materialize will not spill to disk, and therefore * charge just cpu_operator_cost per tuple. (Keep this estimate in - * sync with cost_mergejoin.) + * sync with final_cost_mergejoin.) */ copy_plan_costsize(matplan, inner_plan); matplan->total_cost += cpu_operator_cost * matplan->plan_rows; @@ -1713,14 +2220,15 @@ create_mergejoin_plan(PlannerInfo *root, } /* - * 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)); @@ -1754,9 +2262,9 @@ create_mergejoin_plan(PlannerInfo *root, Assert(ieclass != NULL); /* - * For debugging purposes, we check that the eclasses match the - * paths' pathkeys. In typical cases the merge clauses are one-to-one - * with the pathkeys, but when dealing with partially redundant query + * 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. @@ -1849,21 +2357,23 @@ create_mergejoin_plan(PlannerInfo *root, /* 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. + * 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. */ /* @@ -1874,6 +2384,7 @@ create_mergejoin_plan(PlannerInfo *root, otherclauses, mergeclauses, mergefamilies, + mergecollations, mergestrategies, mergenullsfirst, outer_plan, @@ -1929,6 +2440,18 @@ create_hashjoin_plan(PlannerInfo *root, 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. @@ -2006,52 +2529,237 @@ create_hashjoin_plan(PlannerInfo *root, * *****************************************************************************/ +/* + * 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)) { @@ -2062,41 +2770,61 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path) /* * 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)) @@ -2105,19 +2833,19 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path) /* 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; + /* 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", @@ -2129,22 +2857,77 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path) 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. * - * This is exported because planagg.c needs it. + * 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. */ -Node * -fix_indexqual_operand(Node *node, IndexOptInfo *index) +static Node * +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; @@ -2155,53 +2938,56 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index) 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 */ } @@ -2239,6 +3025,8 @@ get_switched_clauses(List *clauses, Relids outerrelids) 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. */ @@ -2343,7 +3131,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses) /* * Copy cost and size info from a Path node to the Plan node created from it. - * The executor 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) @@ -2352,7 +3140,7 @@ 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 @@ -2366,9 +3154,7 @@ copy_path_costsize(Plan *dest, Path *src) /* * 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) @@ -2424,6 +3210,8 @@ make_indexscan(List *qptlist, Oid indexid, List *indexqual, List *indexqualorig, + List *indexorderby, + List *indexorderbyorig, ScanDirection indexscandir) { IndexScan *node = makeNode(IndexScan); @@ -2438,6 +3226,36 @@ make_indexscan(List *qptlist, 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; @@ -2510,9 +3328,7 @@ SubqueryScan * make_subqueryscan(List *qptlist, List *qpqual, Index scanrelid, - Plan *subplan, - List *subrtable, - List *subrowmark) + Plan *subplan) { SubqueryScan *node = makeNode(SubqueryScan); Plan *plan = &node->scan.plan; @@ -2531,8 +3347,6 @@ make_subqueryscan(List *qptlist, plan->righttree = NULL; node->scan.scanrelid = scanrelid; node->subplan = subplan; - node->subrtable = subrtable; - node->subrowmark = subrowmark; return node; } @@ -2544,7 +3358,8 @@ make_functionscan(List *qptlist, Node *funcexpr, List *funccolnames, List *funccoltypes, - List *funccoltypmods) + List *funccoltypmods, + List *funccolcollations) { FunctionScan *node = makeNode(FunctionScan); Plan *plan = &node->scan.plan; @@ -2559,6 +3374,7 @@ make_functionscan(List *qptlist, node->funccolnames = funccolnames; node->funccoltypes = funccoltypes; node->funccoltypmods = funccoltypmods; + node->funccolcollations = funccolcollations; return node; } @@ -2625,6 +3441,30 @@ make_worktablescan(List *qptlist, return node; } +ForeignScan * +make_foreignscan(List *qptlist, + List *qpqual, + Index scanrelid, + List *fdw_exprs, + List *fdw_private) +{ + ForeignScan *node = makeNode(ForeignScan); + Plan *plan = &node->scan.plan; + + /* 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->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, List *tlist) { @@ -2637,6 +3477,12 @@ make_append(List *appendplans, List *tlist) * 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; @@ -2756,6 +3602,7 @@ static NestLoop * make_nestloop(List *tlist, List *joinclauses, List *otherclauses, + List *nestParams, Plan *lefttree, Plan *righttree, JoinType jointype) @@ -2770,6 +3617,7 @@ make_nestloop(List *tlist, plan->righttree = righttree; node->join.jointype = jointype; node->join.joinqual = joinclauses; + node->nestParams = nestParams; return node; } @@ -2836,6 +3684,7 @@ make_mergejoin(List *tlist, List *otherclauses, List *mergeclauses, Oid *mergefamilies, + Oid *mergecollations, int *mergestrategies, bool *mergenullsfirst, Plan *lefttree, @@ -2852,6 +3701,7 @@ make_mergejoin(List *tlist, plan->righttree = righttree; node->mergeclauses = mergeclauses; node->mergeFamilies = mergefamilies; + node->mergeCollations = mergecollations; node->mergeStrategies = mergestrategies; node->mergeNullsFirst = mergenullsfirst; node->join.jointype = jointype; @@ -2863,13 +3713,14 @@ make_mergejoin(List *tlist, /* * 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); @@ -2881,6 +3732,8 @@ make_sort(PlannerInfo *root, Plan *lefttree, int numCols, 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; @@ -2891,80 +3744,69 @@ make_sort(PlannerInfo *root, Plan *lefttree, int numCols, 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. + * 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. * - * 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 - * - * '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; /* @@ -2973,6 +3815,7 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, 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; @@ -2981,6 +3824,7 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, { PathKey *pathkey = (PathKey *) lfirst(i); EquivalenceClass *ec = pathkey->pk_eclass; + EquivalenceMember *em; TargetEntry *tle = NULL; Oid pk_datatype = InvalidOid; Oid sortop; @@ -3000,16 +3844,40 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, 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 @@ -3018,87 +3886,96 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, * 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, - 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 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 */ } /* @@ -3114,23 +3991,108 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, 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); } /* @@ -3148,38 +4110,32 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree) 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); } /* @@ -3202,44 +4158,36 @@ make_sort_from_groupcols(PlannerInfo *root, 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 * @@ -3295,9 +4243,9 @@ materialize_finished_plan(Plan *subplan) 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); @@ -3313,7 +4261,7 @@ make_agg(PlannerInfo *root, List *tlist, List *qual, 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, @@ -3336,8 +4284,8 @@ make_agg(PlannerInfo *root, List *tlist, List *qual, * anything for Aggref nodes; this is okay since they are really * comparable to Vars. * - * See notes in grouping_planner about why only make_agg, make_windowagg - * and make_group 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) { @@ -3346,10 +4294,7 @@ make_agg(PlannerInfo *root, List *tlist, List *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; @@ -3361,7 +4306,7 @@ make_agg(PlannerInfo *root, List *tlist, List *qual, WindowAgg * make_windowagg(PlannerInfo *root, List *tlist, - int numWindowFuncs, Index winref, + List *windowFuncs, Index winref, int partNumCols, AttrNumber *partColIdx, Oid *partOperators, int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators, int frameOptions, Node *startOffset, Node *endOffset, @@ -3370,7 +4315,6 @@ make_windowagg(PlannerInfo *root, List *tlist, WindowAgg *node = makeNode(WindowAgg); Plan *plan = &node->plan; Path windowagg_path; /* dummy for result of cost_windowagg */ - QualCost qual_cost; node->winref = winref; node->partNumCols = partNumCols; @@ -3385,7 +4329,7 @@ make_windowagg(PlannerInfo *root, List *tlist, copy_plan_costsize(plan, lefttree); /* only care about copying size */ cost_windowagg(&windowagg_path, root, - numWindowFuncs, partNumCols, ordNumCols, + windowFuncs, partNumCols, ordNumCols, lefttree->startup_cost, lefttree->total_cost, lefttree->plan_rows); @@ -3395,13 +4339,10 @@ make_windowagg(PlannerInfo *root, List *tlist, /* * We also need to account for the cost of evaluation of the tlist. * - * See notes in grouping_planner about why only make_agg, make_windowagg - * and make_group 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. */ - 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->targetlist = tlist; plan->lefttree = lefttree; @@ -3452,8 +4393,8 @@ make_group(PlannerInfo *root, * lower plan level and will only be copied by the Group node. Worth * fixing? * - * See notes in grouping_planner about why only make_agg, make_windowagg - * and make_group 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) { @@ -3462,10 +4403,7 @@ make_group(PlannerInfo *root, 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; @@ -3751,13 +4689,14 @@ make_result(PlannerInfo *root, * 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 + * 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, List *resultRelations, +make_modifytable(CmdType operation, bool canSetTag, + List *resultRelations, List *subplans, List *returningLists, List *rowMarks, int epqParam) { @@ -3781,7 +4720,7 @@ make_modifytable(CmdType operation, List *resultRelations, { Plan *subplan = (Plan *) lfirst(subnode); - if (subnode == list_head(subplans)) /* first node? */ + 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; @@ -3795,19 +4734,13 @@ make_modifytable(CmdType operation, List *resultRelations, node->plan.lefttree = NULL; node->plan.righttree = NULL; node->plan.qual = NIL; - - /* - * Set up the visible plan targetlist as being the same as the first - * RETURNING list. This is for the use of EXPLAIN; the executor won't - * pay any attention to the targetlist. - */ - if (returningLists) - node->plan.targetlist = copyObject(linitial(returningLists)); - else - node->plan.targetlist = 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; @@ -3835,6 +4768,7 @@ is_projection_capable_plan(Plan *plan) case T_Limit: case T_ModifyTable: case T_Append: + case T_MergeAppend: case T_RecursiveUnion: return false; default: