mirror of
https://github.com/postgres/postgres.git
synced 2025-07-11 10:01:57 +03:00
Revisit handling of UNION ALL subqueries with non-Var output columns.
In commit57664ed25e
I tried to fix a bug reported by Teodor Sigaev by making non-simple-Var output columns distinct (by wrapping their expressions with dummy PlaceHolderVar nodes). This did not work too well. Commitb28ffd0fcc
fixed some ensuing problems with matching to child indexes, but per a recent report from Claus Stadler, constraint exclusion of UNION ALL subqueries was still broken, because constant-simplification didn't handle the injected PlaceHolderVars well either. On reflection, the original patch was quite misguided: there is no reason to expect that EquivalenceClass child members will be distinct. So instead of trying to make them so, we should ensure that we can cope with the situation when they're not. Accordingly, this patch reverts the code changes in the above-mentioned commits (though the regression test cases they added stay). Instead, I've added assorted defenses to make sure that duplicate EC child members don't cause any problems. Teodor's original problem ("MergeAppend child's targetlist doesn't match MergeAppend") is addressed more directly by revising prepare_sort_from_pathkeys to let the parent MergeAppend's sort list guide creation of each child's sort list. In passing, get rid of add_sort_column; as far as I can tell, testing for duplicate sort keys at this stage is dead code. Certainly it doesn't trigger often enough to be worth expending cycles on in ordinary queries. And keeping the test would've greatly complicated the new logic in prepare_sort_from_pathkeys, because comparing pathkey list entries against a previous output array requires that we not skip any entries in the list. Back-patch to 9.1, like the previous patches. The only known issue in this area that wasn't caused by the ill-advised previous patches was the MergeAppend planning failure, which of course is not relevant before 9.1. It's possible that we need some of the new defenses against duplicate child EC entries in older branches, but until there's some clear evidence of that I'm going to refrain from back-patching further.
This commit is contained in:
@ -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;
|
||||
|
||||
@ -3515,55 +3521,6 @@ make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
/* 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 */
|
||||
}
|
||||
|
||||
/*
|
||||
@ -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;
|
||||
@ -3802,6 +3780,57 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
|
||||
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;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
Reference in New Issue
Block a user