mirror of
https://github.com/postgres/postgres.git
synced 2025-05-24 00:03:23 -04:00
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.
This commit is contained in:
parent
1be0601681
commit
c291203ca3
@ -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)
|
|||||||
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 @@
|
|||||||
* 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,
|
|||||||
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,
|
|||||||
* 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 *
|
|||||||
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,
|
|||||||
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,
|
|||||||
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 @@
|
|||||||
* 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);
|
|||||||
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)
|
|||||||
* 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 *
|
|||||||
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,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 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,
|
|||||||
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,
|
|||||||
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;
|
||||||
|
|
||||||
|
if (sub_eclass->ec_has_volatile)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If the sub_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.
|
||||||
|
*/
|
||||||
|
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;
|
int best_score = -1;
|
||||||
ListCell *j;
|
ListCell *j;
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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,
|
|||||||
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)
|
|||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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,
|
|||||||
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)
|
||||||
|
{
|
||||||
/*
|
/*
|
||||||
* We can sort by any non-constant expression listed in the pathkey's
|
* If the pathkey's EquivalenceClass is volatile, then it must
|
||||||
* EquivalenceClass. For now, we take the first one that corresponds
|
* have come from an ORDER BY clause, and we have to match it to
|
||||||
* to an available item in the tlist. If there isn't any, use the first
|
* that same targetlist entry.
|
||||||
* 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
|
if (ec->ec_sortref == 0) /* can't happen */
|
||||||
* isn't, it won't contain consts anyway, else we'd have discarded
|
elog(ERROR, "volatile EquivalenceClass has no sortref");
|
||||||
* the pathkey as redundant.)
|
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
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
* 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,
|
|||||||
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,
|
|||||||
{
|
{
|
||||||
/* 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,
|
|||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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,6 +131,29 @@ add_to_flat_tlist(List *tlist, List *vars)
|
|||||||
return tlist;
|
return tlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get_sortgroupref_tle
|
||||||
|
* Find the targetlist entry matching the given SortGroupRef index,
|
||||||
|
* and return it.
|
||||||
|
*/
|
||||||
|
TargetEntry *
|
||||||
|
get_sortgroupref_tle(Index sortref, List *targetList)
|
||||||
|
{
|
||||||
|
ListCell *l;
|
||||||
|
|
||||||
|
foreach(l, targetList)
|
||||||
|
{
|
||||||
|
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
||||||
|
|
||||||
|
if (tle->ressortgroupref == sortref)
|
||||||
|
return tle;
|
||||||
|
}
|
||||||
|
|
||||||
|
elog(ERROR, "ORDER/GROUP BY expression not found in targetlist");
|
||||||
|
return NULL; /* keep compiler quiet */
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get_sortgroupclause_tle
|
* get_sortgroupclause_tle
|
||||||
* Find the targetlist entry matching the given SortClause
|
* Find the targetlist entry matching the given SortClause
|
||||||
@ -143,19 +166,7 @@ TargetEntry *
|
|||||||
get_sortgroupclause_tle(SortClause *sortClause,
|
get_sortgroupclause_tle(SortClause *sortClause,
|
||||||
List *targetList)
|
List *targetList)
|
||||||
{
|
{
|
||||||
Index refnumber = sortClause->tleSortGroupRef;
|
return get_sortgroupref_tle(sortClause->tleSortGroupRef, targetList);
|
||||||
ListCell *l;
|
|
||||||
|
|
||||||
foreach(l, targetList)
|
|
||||||
{
|
|
||||||
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
|
||||||
|
|
||||||
if (tle->ressortgroupref == refnumber)
|
|
||||||
return tle;
|
|
||||||
}
|
|
||||||
|
|
||||||
elog(ERROR, "ORDER/GROUP BY expression not found in targetlist");
|
|
||||||
return NULL; /* keep compiler quiet */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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
|
|||||||
* 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
|
|||||||
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 @@
|
|||||||
* 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);
|
|||||||
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 @@
|
|||||||
* 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);
|
|||||||
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,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user