Commit c291203c authored by Tom Lane's avatar Tom Lane

Fix EquivalenceClass code to handle volatile sort expressions in a more

predictable manner; in particular that if you say ORDER BY output-column-ref,
it will in fact sort by that specific column even if there are multiple
syntactic matches.  An example is
	SELECT random() AS a, random() AS b FROM ... ORDER BY b, a;
While the use-case for this might be a bit debatable, it worked as expected
in earlier releases, so we should preserve the behavior for 8.3.  Per my
recent proposal.

While at it, fix convert_subquery_pathkeys() to handle RelabelType stripping
in both directions; it needs this for the same reasons make_sort_from_pathkeys
does.
parent 1be06016
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.315 2007/10/11 18:05:27 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.316 2007/11/08 21:49:47 tgl Exp $
* *
* NOTES * NOTES
* Every node type that can appear in stored rules' parsetrees *must* * Every node type that can appear in stored rules' parsetrees *must*
...@@ -1405,6 +1405,7 @@ _outEquivalenceClass(StringInfo str, EquivalenceClass *node) ...@@ -1405,6 +1405,7 @@ _outEquivalenceClass(StringInfo str, EquivalenceClass *node)
WRITE_BOOL_FIELD(ec_has_volatile); WRITE_BOOL_FIELD(ec_has_volatile);
WRITE_BOOL_FIELD(ec_below_outer_join); WRITE_BOOL_FIELD(ec_below_outer_join);
WRITE_BOOL_FIELD(ec_broken); WRITE_BOOL_FIELD(ec_broken);
WRITE_UINT_FIELD(ec_sortref);
} }
static void static void
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/equivclass.c,v 1.3 2007/07/07 20:46:45 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/path/equivclass.c,v 1.4 2007/11/08 21:49:47 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -294,6 +294,7 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo, ...@@ -294,6 +294,7 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
ec->ec_has_volatile = false; ec->ec_has_volatile = false;
ec->ec_below_outer_join = below_outer_join; ec->ec_below_outer_join = below_outer_join;
ec->ec_broken = false; ec->ec_broken = false;
ec->ec_sortref = 0;
ec->ec_merged = NULL; ec->ec_merged = NULL;
em1 = add_eq_member(ec, item1, item1_relids, false, item1_type); em1 = add_eq_member(ec, item1, item1_relids, false, item1_type);
em2 = add_eq_member(ec, item2, item2_relids, false, item2_type); em2 = add_eq_member(ec, item2, item2_relids, false, item2_type);
...@@ -354,6 +355,9 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids, ...@@ -354,6 +355,9 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
* class it is a member of; if none, build a new single-member * class it is a member of; if none, build a new single-member
* EquivalenceClass for it. * EquivalenceClass for it.
* *
* sortref is the SortGroupRef of the originating SortClause, if any,
* or zero if not.
*
* This can be used safely both before and after EquivalenceClass merging; * This can be used safely both before and after EquivalenceClass merging;
* since it never causes merging it does not invalidate any existing ECs * since it never causes merging it does not invalidate any existing ECs
* or PathKeys. * or PathKeys.
...@@ -367,7 +371,8 @@ EquivalenceClass * ...@@ -367,7 +371,8 @@ EquivalenceClass *
get_eclass_for_sort_expr(PlannerInfo *root, get_eclass_for_sort_expr(PlannerInfo *root,
Expr *expr, Expr *expr,
Oid expr_datatype, Oid expr_datatype,
List *opfamilies) List *opfamilies,
Index sortref)
{ {
EquivalenceClass *newec; EquivalenceClass *newec;
EquivalenceMember *newem; EquivalenceMember *newem;
...@@ -382,7 +387,9 @@ get_eclass_for_sort_expr(PlannerInfo *root, ...@@ -382,7 +387,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1); EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
ListCell *lc2; ListCell *lc2;
/* we allow matching to a volatile EC here */ /* Never match to a volatile EC */
if (cur_ec->ec_has_volatile)
continue;
if (!equal(opfamilies, cur_ec->ec_opfamilies)) if (!equal(opfamilies, cur_ec->ec_opfamilies))
continue; continue;
...@@ -423,6 +430,7 @@ get_eclass_for_sort_expr(PlannerInfo *root, ...@@ -423,6 +430,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
newec->ec_has_volatile = contain_volatile_functions((Node *) expr); newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
newec->ec_below_outer_join = false; newec->ec_below_outer_join = false;
newec->ec_broken = false; newec->ec_broken = false;
newec->ec_sortref = sortref;
newec->ec_merged = NULL; newec->ec_merged = NULL;
newem = add_eq_member(newec, expr, pull_varnos((Node *) expr), newem = add_eq_member(newec, expr, pull_varnos((Node *) expr),
false, expr_datatype); false, expr_datatype);
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/pathkeys.c,v 1.88 2007/11/08 19:25:37 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/path/pathkeys.c,v 1.89 2007/11/08 21:49:47 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -46,6 +46,7 @@ static bool pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys); ...@@ -46,6 +46,7 @@ static bool pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys);
static PathKey *make_pathkey_from_sortinfo(PlannerInfo *root, static PathKey *make_pathkey_from_sortinfo(PlannerInfo *root,
Expr *expr, Oid ordering_op, Expr *expr, Oid ordering_op,
bool nulls_first, bool nulls_first,
Index sortref,
bool canonicalize); bool canonicalize);
static Var *find_indexkey_var(PlannerInfo *root, RelOptInfo *rel, static Var *find_indexkey_var(PlannerInfo *root, RelOptInfo *rel,
AttrNumber varattno); AttrNumber varattno);
...@@ -233,6 +234,9 @@ canonicalize_pathkeys(PlannerInfo *root, List *pathkeys) ...@@ -233,6 +234,9 @@ canonicalize_pathkeys(PlannerInfo *root, List *pathkeys)
* a PathKey. If canonicalize = true, the result is a "canonical" * a PathKey. If canonicalize = true, the result is a "canonical"
* PathKey, otherwise not. (But note it might be redundant anyway.) * PathKey, otherwise not. (But note it might be redundant anyway.)
* *
* If the PathKey is being generated from a SortClause, sortref should be
* the SortClause's SortGroupRef; otherwise zero.
*
* canonicalize should always be TRUE after EquivalenceClass merging has * canonicalize should always be TRUE after EquivalenceClass merging has
* been performed, but FALSE if we haven't done EquivalenceClass merging yet. * been performed, but FALSE if we haven't done EquivalenceClass merging yet.
*/ */
...@@ -240,6 +244,7 @@ static PathKey * ...@@ -240,6 +244,7 @@ static PathKey *
make_pathkey_from_sortinfo(PlannerInfo *root, make_pathkey_from_sortinfo(PlannerInfo *root,
Expr *expr, Oid ordering_op, Expr *expr, Oid ordering_op,
bool nulls_first, bool nulls_first,
Index sortref,
bool canonicalize) bool canonicalize)
{ {
Oid opfamily, Oid opfamily,
...@@ -303,7 +308,8 @@ make_pathkey_from_sortinfo(PlannerInfo *root, ...@@ -303,7 +308,8 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
} }
/* Now find or create a matching EquivalenceClass */ /* Now find or create a matching EquivalenceClass */
eclass = get_eclass_for_sort_expr(root, expr, opcintype, opfamilies); eclass = get_eclass_for_sort_expr(root, expr, opcintype, opfamilies,
sortref);
/* And finally we can find or create a PathKey node */ /* And finally we can find or create a PathKey node */
if (canonicalize) if (canonicalize)
...@@ -525,6 +531,7 @@ build_index_pathkeys(PlannerInfo *root, ...@@ -525,6 +531,7 @@ build_index_pathkeys(PlannerInfo *root,
indexkey, indexkey,
sortop, sortop,
nulls_first, nulls_first,
0,
true); true);
/* Add to list unless redundant */ /* Add to list unless redundant */
...@@ -597,41 +604,87 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel, ...@@ -597,41 +604,87 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
PathKey *sub_pathkey = (PathKey *) lfirst(i); PathKey *sub_pathkey = (PathKey *) lfirst(i);
EquivalenceClass *sub_eclass = sub_pathkey->pk_eclass; EquivalenceClass *sub_eclass = sub_pathkey->pk_eclass;
PathKey *best_pathkey = NULL; PathKey *best_pathkey = NULL;
int best_score = -1;
ListCell *j;
if (sub_eclass->ec_has_volatile)
{
/* /*
* The sub_pathkey's EquivalenceClass could contain multiple elements * If the sub_pathkey's EquivalenceClass is volatile, then it must
* (representing knowledge that multiple items are effectively equal). * have come from an ORDER BY clause, and we have to match it to
* Each element might match none, one, or more of the output columns * that same targetlist entry.
* that are visible to the outer query. This means we may have
* multiple possible representations of the sub_pathkey in the context
* of the outer query. Ideally we would generate them all and put
* them all into an EC of the outer query, thereby propagating
* equality knowledge up to the outer query. Right now we cannot do
* so, because the outer query's EquivalenceClasses are already frozen
* when this is called. Instead we prefer the one that has the highest
* "score" (number of EC peers, plus one if it matches the outer
* query_pathkeys). This is the most likely to be useful in the outer
* query.
*/ */
TargetEntry *tle;
if (sub_eclass->ec_sortref == 0) /* can't happen */
elog(ERROR, "volatile EquivalenceClass has no sortref");
tle = get_sortgroupref_tle(sub_eclass->ec_sortref, sub_tlist);
Assert(tle);
/* resjunk items aren't visible to outer query */
if (!tle->resjunk)
{
/* We can represent this sub_pathkey */
EquivalenceMember *sub_member;
Expr *outer_expr;
EquivalenceClass *outer_ec;
Assert(list_length(sub_eclass->ec_members) == 1);
sub_member = (EquivalenceMember *) linitial(sub_eclass->ec_members);
outer_expr = (Expr *)
makeVar(rel->relid,
tle->resno,
exprType((Node *) tle->expr),
exprTypmod((Node *) tle->expr),
0);
outer_ec =
get_eclass_for_sort_expr(root,
outer_expr,
sub_member->em_datatype,
sub_eclass->ec_opfamilies,
0);
best_pathkey =
make_canonical_pathkey(root,
outer_ec,
sub_pathkey->pk_opfamily,
sub_pathkey->pk_strategy,
sub_pathkey->pk_nulls_first);
}
}
else
{
/*
* Otherwise, the sub_pathkey's EquivalenceClass could contain
* multiple elements (representing knowledge that multiple items
* are effectively equal). Each element might match none, one, or
* more of the output columns that are visible to the outer
* query. This means we may have multiple possible representations
* of the sub_pathkey in the context of the outer query. Ideally
* we would generate them all and put them all into an EC of the
* outer query, thereby propagating equality knowledge up to the
* outer query. Right now we cannot do so, because the outer
* query's EquivalenceClasses are already frozen when this is
* called. Instead we prefer the one that has the highest "score"
* (number of EC peers, plus one if it matches the outer
* query_pathkeys). This is the most likely to be useful in the
* outer query.
*/
int best_score = -1;
ListCell *j;
foreach(j, sub_eclass->ec_members) foreach(j, sub_eclass->ec_members)
{ {
EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j); EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
Expr *sub_expr = sub_member->em_expr; Expr *sub_expr = sub_member->em_expr;
Expr *rtarg; Expr *sub_stripped;
ListCell *k; ListCell *k;
/* /*
* We handle two cases: the sub_pathkey key can be either an exact * We handle two cases: the sub_pathkey key can be either an
* match for a targetlist entry, or a RelabelType of a targetlist * exact match for a targetlist entry, or it could match after
* entry. (The latter case is worth extra code because it arises * stripping RelabelType nodes. (We need that case since
* frequently in connection with varchar fields.) * make_pathkey_from_sortinfo could add or remove RelabelType.)
*/ */
if (IsA(sub_expr, RelabelType)) sub_stripped = sub_expr;
rtarg = ((RelabelType *) sub_expr)->arg; while (sub_stripped && IsA(sub_stripped, RelabelType))
else sub_stripped = ((RelabelType *) sub_stripped)->arg;
rtarg = NULL;
foreach(k, sub_tlist) foreach(k, sub_tlist)
{ {
...@@ -655,7 +708,15 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel, ...@@ -655,7 +708,15 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
exprTypmod((Node *) tle->expr), exprTypmod((Node *) tle->expr),
0); 0);
} }
else if (rtarg && equal(tle->expr, rtarg)) else
{
Expr *tle_stripped;
tle_stripped = tle->expr;
while (tle_stripped && IsA(tle_stripped, RelabelType))
tle_stripped = ((RelabelType *) tle_stripped)->arg;
if (equal(tle_stripped, sub_stripped))
{ {
/* Match after discarding RelabelType */ /* Match after discarding RelabelType */
outer_expr = (Expr *) outer_expr = (Expr *)
...@@ -664,20 +725,24 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel, ...@@ -664,20 +725,24 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
exprType((Node *) tle->expr), exprType((Node *) tle->expr),
exprTypmod((Node *) tle->expr), exprTypmod((Node *) tle->expr),
0); 0);
if (exprType((Node *) outer_expr) !=
exprType((Node *) sub_expr))
outer_expr = (Expr *) outer_expr = (Expr *)
makeRelabelType((Expr *) outer_expr, makeRelabelType(outer_expr,
((RelabelType *) sub_expr)->resulttype, exprType((Node *) sub_expr),
((RelabelType *) sub_expr)->resulttypmod, -1,
((RelabelType *) sub_expr)->relabelformat); COERCE_DONTCARE);
} }
else else
continue; continue;
}
/* Found a representation for this sub_pathkey */ /* Found a representation for this sub_pathkey */
outer_ec = get_eclass_for_sort_expr(root, outer_ec = get_eclass_for_sort_expr(root,
outer_expr, outer_expr,
sub_member->em_datatype, sub_member->em_datatype,
sub_eclass->ec_opfamilies); sub_eclass->ec_opfamilies,
0);
outer_pk = make_canonical_pathkey(root, outer_pk = make_canonical_pathkey(root,
outer_ec, outer_ec,
sub_pathkey->pk_opfamily, sub_pathkey->pk_opfamily,
...@@ -696,6 +761,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel, ...@@ -696,6 +761,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
} }
} }
} }
}
/* /*
* If we couldn't find a representation of this sub_pathkey, we're * If we couldn't find a representation of this sub_pathkey, we're
...@@ -795,6 +861,7 @@ make_pathkeys_for_sortclauses(PlannerInfo *root, ...@@ -795,6 +861,7 @@ make_pathkeys_for_sortclauses(PlannerInfo *root,
sortkey, sortkey,
sortcl->sortop, sortcl->sortop,
sortcl->nulls_first, sortcl->nulls_first,
sortcl->tleSortGroupRef,
canonicalize); canonicalize);
/* Canonical form eliminates redundant ordering keys */ /* Canonical form eliminates redundant ordering keys */
...@@ -843,12 +910,14 @@ cache_mergeclause_eclasses(PlannerInfo *root, RestrictInfo *restrictinfo) ...@@ -843,12 +910,14 @@ cache_mergeclause_eclasses(PlannerInfo *root, RestrictInfo *restrictinfo)
get_eclass_for_sort_expr(root, get_eclass_for_sort_expr(root,
(Expr *) get_leftop(clause), (Expr *) get_leftop(clause),
lefttype, lefttype,
restrictinfo->mergeopfamilies); restrictinfo->mergeopfamilies,
0);
restrictinfo->right_ec = restrictinfo->right_ec =
get_eclass_for_sort_expr(root, get_eclass_for_sort_expr(root,
(Expr *) get_rightop(clause), (Expr *) get_rightop(clause),
righttype, righttype,
restrictinfo->mergeopfamilies); restrictinfo->mergeopfamilies,
0);
} }
else else
Assert(restrictinfo->right_ec != NULL); Assert(restrictinfo->right_ec != NULL);
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.233 2007/11/08 19:25:37 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.234 2007/11/08 21:49:47 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -2730,27 +2730,45 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, ...@@ -2730,27 +2730,45 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
foreach(i, pathkeys) foreach(i, pathkeys)
{ {
PathKey *pathkey = (PathKey *) lfirst(i); PathKey *pathkey = (PathKey *) lfirst(i);
EquivalenceClass *ec = pathkey->pk_eclass;
TargetEntry *tle = NULL; TargetEntry *tle = NULL;
Oid pk_datatype = InvalidOid; Oid pk_datatype = InvalidOid;
Oid sortop; Oid sortop;
ListCell *j; ListCell *j;
if (ec->ec_has_volatile)
{
/*
* If the pathkey's EquivalenceClass is volatile, then it must
* have come from an ORDER BY clause, and we have to match it to
* that same targetlist entry.
*/
if (ec->ec_sortref == 0) /* can't happen */
elog(ERROR, "volatile EquivalenceClass has no sortref");
tle = get_sortgroupref_tle(ec->ec_sortref, tlist);
Assert(tle);
Assert(list_length(ec->ec_members) == 1);
pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
}
else
{
/* /*
* We can sort by any non-constant expression listed in the pathkey's * Otherwise, we can sort by any non-constant expression listed in
* EquivalenceClass. For now, we take the first one that corresponds * the pathkey's EquivalenceClass. For now, we take the first one
* to an available item in the tlist. If there isn't any, use the first * that corresponds to an available item in the tlist. If there
* one that is an expression in the input's vars. (The non-const * isn't any, use the first one that is an expression in the
* restriction only matters if the EC is below_outer_join; but if it * input's vars. (The non-const restriction only matters if the
* isn't, it won't contain consts anyway, else we'd have discarded * EC is below_outer_join; but if it isn't, it won't contain
* the pathkey as redundant.) * 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 * XXX if we have a choice, is there any way of figuring out which
* might be cheapest to execute? (For example, int4lt is likely much * might be cheapest to execute? (For example, int4lt is likely
* cheaper to execute than numericlt, but both might appear in the * much cheaper to execute than numericlt, but both might appear
* same equivalence class...) Not clear that we ever will have an * in the same equivalence class...) Not clear that we ever will
* interesting choice in practice, so it may not matter. * have an interesting choice in practice, so it may not matter.
*/ */
foreach(j, pathkey->pk_eclass->ec_members) foreach(j, ec->ec_members)
{ {
EquivalenceMember *em = (EquivalenceMember *) lfirst(j); EquivalenceMember *em = (EquivalenceMember *) lfirst(j);
...@@ -2778,12 +2796,13 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, ...@@ -2778,12 +2796,13 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
break; /* found expr already in tlist */ break; /* found expr already in tlist */
} }
} }
if (!tle) if (!tle)
{ {
/* No matching tlist item; look for a computable expression */ /* No matching tlist item; look for a computable expression */
Expr *sortexpr = NULL; Expr *sortexpr = NULL;
foreach(j, pathkey->pk_eclass->ec_members) foreach(j, ec->ec_members)
{ {
EquivalenceMember *em = (EquivalenceMember *) lfirst(j); EquivalenceMember *em = (EquivalenceMember *) lfirst(j);
List *exprvars; List *exprvars;
...@@ -2815,7 +2834,8 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, ...@@ -2815,7 +2834,8 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
{ {
/* copy needed so we don't modify input's tlist below */ /* copy needed so we don't modify input's tlist below */
tlist = copyObject(tlist); tlist = copyObject(tlist);
lefttree = (Plan *) make_result(root, tlist, NULL, lefttree); lefttree = (Plan *) make_result(root, tlist, NULL,
lefttree);
} }
/* /*
...@@ -2828,6 +2848,7 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, ...@@ -2828,6 +2848,7 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
tlist = lappend(tlist, tle); tlist = lappend(tlist, tle);
lefttree->targetlist = tlist; /* just in case NIL before */ lefttree->targetlist = tlist; /* just in case NIL before */
} }
}
/* /*
* Look up the correct sort operator from the PathKey's slightly * Look up the correct sort operator from the PathKey's slightly
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/tlist.c,v 1.75 2007/11/08 19:25:37 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/util/tlist.c,v 1.76 2007/11/08 21:49:47 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -131,26 +131,22 @@ add_to_flat_tlist(List *tlist, List *vars) ...@@ -131,26 +131,22 @@ add_to_flat_tlist(List *tlist, List *vars)
return tlist; return tlist;
} }
/* /*
* get_sortgroupclause_tle * get_sortgroupref_tle
* Find the targetlist entry matching the given SortClause * Find the targetlist entry matching the given SortGroupRef index,
* (or GroupClause) by ressortgroupref, and return it. * and return it.
*
* Because GroupClause is typedef'd as SortClause, either kind of
* node can be passed without casting.
*/ */
TargetEntry * TargetEntry *
get_sortgroupclause_tle(SortClause *sortClause, get_sortgroupref_tle(Index sortref, List *targetList)
List *targetList)
{ {
Index refnumber = sortClause->tleSortGroupRef;
ListCell *l; ListCell *l;
foreach(l, targetList) foreach(l, targetList)
{ {
TargetEntry *tle = (TargetEntry *) lfirst(l); TargetEntry *tle = (TargetEntry *) lfirst(l);
if (tle->ressortgroupref == refnumber) if (tle->ressortgroupref == sortref)
return tle; return tle;
} }
...@@ -158,6 +154,21 @@ get_sortgroupclause_tle(SortClause *sortClause, ...@@ -158,6 +154,21 @@ get_sortgroupclause_tle(SortClause *sortClause,
return NULL; /* keep compiler quiet */ return NULL; /* keep compiler quiet */
} }
/*
* get_sortgroupclause_tle
* Find the targetlist entry matching the given SortClause
* (or GroupClause) by ressortgroupref, and return it.
*
* Because GroupClause is typedef'd as SortClause, either kind of
* node can be passed without casting.
*/
TargetEntry *
get_sortgroupclause_tle(SortClause *sortClause,
List *targetList)
{
return get_sortgroupref_tle(sortClause->tleSortGroupRef, targetList);
}
/* /*
* get_sortgroupclause_expr * get_sortgroupclause_expr
* Find the targetlist entry matching the given SortClause * Find the targetlist entry matching the given SortClause
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.147 2007/10/11 18:05:27 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.148 2007/11/08 21:49:48 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -449,7 +449,10 @@ typedef struct IndexOptInfo ...@@ -449,7 +449,10 @@ typedef struct IndexOptInfo
* which is a case that can't arise otherwise since clauses containing * which is a case that can't arise otherwise since clauses containing
* volatile functions are never considered mergejoinable. We mark such * volatile functions are never considered mergejoinable. We mark such
* EquivalenceClasses specially to prevent them from being merged with * EquivalenceClasses specially to prevent them from being merged with
* ordinary EquivalenceClasses. * ordinary EquivalenceClasses. Also, for volatile expressions we have
* to be careful to match the EquivalenceClass to the correct targetlist
* entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
* So we record the SortGroupRef of the originating sort clause.
* *
* We allow equality clauses appearing below the nullable side of an outer join * We allow equality clauses appearing below the nullable side of an outer join
* to form EquivalenceClasses, but these have a slightly different meaning: * to form EquivalenceClasses, but these have a slightly different meaning:
...@@ -472,6 +475,7 @@ typedef struct EquivalenceClass ...@@ -472,6 +475,7 @@ typedef struct EquivalenceClass
bool ec_has_volatile; /* the (sole) member is a volatile expr */ bool ec_has_volatile; /* the (sole) member is a volatile expr */
bool ec_below_outer_join; /* equivalence applies below an OJ */ bool ec_below_outer_join; /* equivalence applies below an OJ */
bool ec_broken; /* failed to generate needed clauses? */ bool ec_broken; /* failed to generate needed clauses? */
Index ec_sortref; /* originating sortclause label, or 0 */
struct EquivalenceClass *ec_merged; /* set if merged into another EC */ struct EquivalenceClass *ec_merged; /* set if merged into another EC */
} EquivalenceClass; } EquivalenceClass;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/optimizer/paths.h,v 1.99 2007/09/26 18:51:51 tgl Exp $ * $PostgreSQL: pgsql/src/include/optimizer/paths.h,v 1.100 2007/11/08 21:49:48 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -115,7 +115,8 @@ extern void reconsider_outer_join_clauses(PlannerInfo *root); ...@@ -115,7 +115,8 @@ extern void reconsider_outer_join_clauses(PlannerInfo *root);
extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root, extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
Expr *expr, Expr *expr,
Oid expr_datatype, Oid expr_datatype,
List *opfamilies); List *opfamilies,
Index sortref);
extern void generate_base_implied_equalities(PlannerInfo *root); extern void generate_base_implied_equalities(PlannerInfo *root);
extern List *generate_join_implied_equalities(PlannerInfo *root, extern List *generate_join_implied_equalities(PlannerInfo *root,
RelOptInfo *joinrel, RelOptInfo *joinrel,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/optimizer/tlist.h,v 1.46 2007/11/08 19:25:37 tgl Exp $ * $PostgreSQL: pgsql/src/include/optimizer/tlist.h,v 1.47 2007/11/08 21:49:48 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -23,6 +23,8 @@ extern TargetEntry *tlist_member_ignore_relabel(Node *node, List *targetlist); ...@@ -23,6 +23,8 @@ extern TargetEntry *tlist_member_ignore_relabel(Node *node, List *targetlist);
extern List *flatten_tlist(List *tlist); extern List *flatten_tlist(List *tlist);
extern List *add_to_flat_tlist(List *tlist, List *vars); extern List *add_to_flat_tlist(List *tlist, List *vars);
extern TargetEntry *get_sortgroupref_tle(Index sortref,
List *targetList);
extern TargetEntry *get_sortgroupclause_tle(SortClause *sortClause, extern TargetEntry *get_sortgroupclause_tle(SortClause *sortClause,
List *targetList); List *targetList);
extern Node *get_sortgroupclause_expr(SortClause *sortClause, extern Node *get_sortgroupclause_expr(SortClause *sortClause,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment