summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan/createplan.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/plan/createplan.c')
-rw-r--r--src/backend/optimizer/plan/createplan.c381
1 files changed, 194 insertions, 187 deletions
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 94140d304f7..38b040ffff3 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -152,12 +152,17 @@ static Sort *make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
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);
@@ -706,6 +711,8 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
/* 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,
@@ -733,6 +740,8 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
/* 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,
@@ -2695,11 +2704,8 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol)
ListCell *indexpr_item;
/*
- * Remove any PlaceHolderVars or binary-compatible relabeling of the
- * indexkey (this must match logic in match_index_to_operand()).
+ * Remove any binary-compatible relabeling of the indexkey
*/
- while (IsA(node, PlaceHolderVar))
- node = (Node *) ((PlaceHolderVar *) node)->phexpr;
if (IsA(node, RelabelType))
node = (Node *) ((RelabelType *) node)->arg;
@@ -3516,55 +3522,6 @@ make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
}
/*
- * add_sort_column --- utility subroutine for building sort info arrays
- *
- * We need this routine because the same column might be selected more than
- * once as a sort key column; if so, the extra mentions are redundant.
- *
- * Caller is assumed to have allocated the arrays large enough for the
- * max possible number of columns. Return value is the new column count.
- */
-static int
-add_sort_column(AttrNumber colIdx, Oid sortOp, Oid coll, bool nulls_first,
- int numCols, AttrNumber *sortColIdx,
- Oid *sortOperators, Oid *collations, 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.
- *
- * We could probably consider sort keys with the same sortop and
- * different collations to be redundant too, but for the moment treat
- * them as not redundant. This will be needed if we ever support
- * collations with different notions of equality.
- */
- if (sortColIdx[i] == colIdx &&
- sortOperators[numCols] == sortOp &&
- collations[numCols] == coll)
- {
- /* Already sorting by this col, so extra sort key is useless */
- return numCols;
- }
- }
-
- /* Add the column */
- sortColIdx[numCols] = colIdx;
- sortOperators[numCols] = sortOp;
- collations[numCols] = coll;
- nullsFirst[numCols] = nulls_first;
- return numCols + 1;
-}
-
-/*
* prepare_sort_from_pathkeys
* Prepare to sort according to given pathkeys
*
@@ -3573,8 +3530,10 @@ add_sort_column(AttrNumber colIdx, Oid sortOp, Oid coll, bool nulls_first,
* plan targetlist if needed to add resjunk sort columns.
*
* Input parameters:
- * 'lefttree' is the node which yields input tuples
+ * 'lefttree' is the plan node which yields input tuples
* 'pathkeys' is the list of pathkeys by which the result is to be sorted
+ * 'relids' identifies the child relation being sorted, if any
+ * 'reqColIdx' is NULL or an array of required sort key column numbers
* 'adjust_tlist_in_place' is TRUE if lefttree must be modified in-place
*
* We must convert the pathkey information into arrays of sort key column
@@ -3582,6 +3541,14 @@ add_sort_column(AttrNumber colIdx, Oid sortOp, Oid coll, bool nulls_first,
* 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/MergeAppend node itself won't
@@ -3596,6 +3563,8 @@ add_sort_column(AttrNumber colIdx, Oid sortOp, Oid coll, bool nulls_first,
*/
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,
@@ -3626,6 +3595,7 @@ prepare_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;
@@ -3645,16 +3615,41 @@ prepare_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
@@ -3663,9 +3658,36 @@ prepare_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;
/*
* We shouldn't be trying to sort by an equivalence class that
@@ -3675,91 +3697,56 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
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.
- * 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)
- 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 usable expression */
- }
- }
- if (!j)
- elog(ERROR, "could not find pathkey item to sort");
-
- /*
- * Do we need to insert a Result node?
- */
- if (!adjust_tlist_in_place &&
- !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);
- }
+ /* 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;
+ /* 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 */
}
/*
@@ -3775,23 +3762,14 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
pathkey->pk_strategy, pk_datatype, pk_datatype,
pathkey->pk_opfamily);
- /*
- * 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.
- */
- numsortkeys = add_sort_column(tle->resno,
- sortop,
- pathkey->pk_eclass->ec_collation,
- pathkey->pk_nulls_first,
- numsortkeys,
- sortColIdx, sortOperators,
- collations, nullsFirst);
+ /* 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++;
}
- Assert(numsortkeys > 0);
-
/* Return results */
*p_numsortkeys = numsortkeys;
*p_sortColIdx = sortColIdx;
@@ -3803,6 +3781,57 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
}
/*
+ * find_ec_member_for_tle
+ * Locate an EquivalenceClass member matching the given TLE, if any
+ *
+ * Child EC members are ignored unless they match 'relids'.
+ */
+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;
+
+ /*
+ * Ignore child members unless they match the rel being sorted.
+ */
+ 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;
+ }
+
+ return NULL;
+}
+
+/*
* make_sort_from_pathkeys
* Create sort plan to sort according to given pathkeys
*
@@ -3823,6 +3852,8 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
/* Compute sort column info, and adjust lefttree as needed */
lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
+ NULL,
+ NULL,
false,
&numsortkeys,
&sortColIdx,
@@ -3854,9 +3885,7 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree)
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));
@@ -3864,27 +3893,18 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree)
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,
- exprCollation((Node *) tle->expr),
- sortcl->nulls_first,
- numsortkeys,
- sortColIdx, sortOperators,
- collations, 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, collations,
nullsFirst, -1.0);
@@ -3910,7 +3930,6 @@ make_sort_from_groupcols(PlannerInfo *root,
Plan *lefttree)
{
List *sub_tlist = lefttree->targetlist;
- int grpno = 0;
ListCell *l;
int numsortkeys;
AttrNumber *sortColIdx;
@@ -3918,9 +3937,7 @@ make_sort_from_groupcols(PlannerInfo *root,
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));
@@ -3928,28 +3945,18 @@ make_sort_from_groupcols(PlannerInfo *root,
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,
- exprCollation((Node *) tle->expr),
- grpcl->nulls_first,
- numsortkeys,
- sortColIdx, sortOperators,
- collations, 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, collations,
nullsFirst, -1.0);