From c291203ca3cde3b10e7a8962df2c1ccc737a9e6f Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 8 Nov 2007 21:49:48 +0000 Subject: [PATCH] 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. --- src/backend/nodes/outfuncs.c | 3 +- src/backend/optimizer/path/equivclass.c | 14 +- src/backend/optimizer/path/pathkeys.c | 247 +++++++++++++++--------- src/backend/optimizer/plan/createplan.c | 169 +++++++++------- src/backend/optimizer/util/tlist.c | 33 ++-- src/include/nodes/relation.h | 8 +- src/include/optimizer/paths.h | 5 +- src/include/optimizer/tlist.h | 4 +- 8 files changed, 300 insertions(+), 183 deletions(-) diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 005879b8c9..fc4f7d2dac 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * 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 * 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_below_outer_join); WRITE_BOOL_FIELD(ec_broken); + WRITE_UINT_FIELD(ec_sortref); } static void diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c index fc862438ff..18c6ff9368 100644 --- a/src/backend/optimizer/path/equivclass.c +++ b/src/backend/optimizer/path/equivclass.c @@ -10,7 +10,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * 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_below_outer_join = below_outer_join; ec->ec_broken = false; + ec->ec_sortref = 0; ec->ec_merged = NULL; em1 = add_eq_member(ec, item1, item1_relids, false, item1_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 * 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; * since it never causes merging it does not invalidate any existing ECs * or PathKeys. @@ -367,7 +371,8 @@ EquivalenceClass * get_eclass_for_sort_expr(PlannerInfo *root, Expr *expr, Oid expr_datatype, - List *opfamilies) + List *opfamilies, + Index sortref) { EquivalenceClass *newec; EquivalenceMember *newem; @@ -382,7 +387,9 @@ get_eclass_for_sort_expr(PlannerInfo *root, EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1); 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)) continue; @@ -423,6 +430,7 @@ get_eclass_for_sort_expr(PlannerInfo *root, newec->ec_has_volatile = contain_volatile_functions((Node *) expr); newec->ec_below_outer_join = false; newec->ec_broken = false; + newec->ec_sortref = sortref; newec->ec_merged = NULL; newem = add_eq_member(newec, expr, pull_varnos((Node *) expr), false, expr_datatype); diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index f96d7bb555..846fe78ee6 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -11,7 +11,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * 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, Expr *expr, Oid ordering_op, bool nulls_first, + Index sortref, bool canonicalize); static Var *find_indexkey_var(PlannerInfo *root, RelOptInfo *rel, AttrNumber varattno); @@ -233,6 +234,9 @@ canonicalize_pathkeys(PlannerInfo *root, List *pathkeys) * a PathKey. If canonicalize = true, the result is a "canonical" * 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 * been performed, but FALSE if we haven't done EquivalenceClass merging yet. */ @@ -240,6 +244,7 @@ static PathKey * make_pathkey_from_sortinfo(PlannerInfo *root, Expr *expr, Oid ordering_op, bool nulls_first, + Index sortref, bool canonicalize) { Oid opfamily, @@ -303,7 +308,8 @@ make_pathkey_from_sortinfo(PlannerInfo *root, } /* 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 */ if (canonicalize) @@ -525,6 +531,7 @@ build_index_pathkeys(PlannerInfo *root, indexkey, sortop, nulls_first, + 0, true); /* Add to list unless redundant */ @@ -597,102 +604,161 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel, PathKey *sub_pathkey = (PathKey *) lfirst(i); EquivalenceClass *sub_eclass = sub_pathkey->pk_eclass; PathKey *best_pathkey = NULL; - int best_score = -1; - 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) + if (sub_eclass->ec_has_volatile) { - EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j); - Expr *sub_expr = sub_member->em_expr; - Expr *rtarg; - ListCell *k; - /* - * We handle two cases: the sub_pathkey key can be either an exact - * match for a targetlist entry, or a RelabelType of a targetlist - * entry. (The latter case is worth extra code because it arises - * frequently in connection with varchar fields.) + * 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. */ - if (IsA(sub_expr, RelabelType)) - rtarg = ((RelabelType *) sub_expr)->arg; - else - rtarg = NULL; - - foreach(k, sub_tlist) + 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) { - TargetEntry *tle = (TargetEntry *) lfirst(k); + /* We can represent this sub_pathkey */ + EquivalenceMember *sub_member; Expr *outer_expr; EquivalenceClass *outer_ec; - PathKey *outer_pk; - int score; - /* resjunk items aren't visible to outer query */ - if (tle->resjunk) - continue; + 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; - if (equal(tle->expr, sub_expr)) - { - /* Exact match */ - outer_expr = (Expr *) - makeVar(rel->relid, - tle->resno, - exprType((Node *) tle->expr), - exprTypmod((Node *) tle->expr), - 0); - } - else if (rtarg && equal(tle->expr, rtarg)) + foreach(j, sub_eclass->ec_members) + { + EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j); + Expr *sub_expr = sub_member->em_expr; + Expr *sub_stripped; + ListCell *k; + + /* + * We handle two cases: the sub_pathkey key can be either an + * exact match for a targetlist entry, or it could match after + * stripping RelabelType nodes. (We need that case since + * make_pathkey_from_sortinfo could add or remove RelabelType.) + */ + sub_stripped = sub_expr; + while (sub_stripped && IsA(sub_stripped, RelabelType)) + sub_stripped = ((RelabelType *) sub_stripped)->arg; + + foreach(k, sub_tlist) { - /* Match after discarding RelabelType */ - outer_expr = (Expr *) - makeVar(rel->relid, - tle->resno, - exprType((Node *) tle->expr), - exprTypmod((Node *) tle->expr), - 0); - outer_expr = (Expr *) - makeRelabelType((Expr *) outer_expr, - ((RelabelType *) sub_expr)->resulttype, - ((RelabelType *) sub_expr)->resulttypmod, - ((RelabelType *) sub_expr)->relabelformat); - } - else - continue; + TargetEntry *tle = (TargetEntry *) lfirst(k); + Expr *outer_expr; + EquivalenceClass *outer_ec; + PathKey *outer_pk; + int score; - /* Found a representation for this sub_pathkey */ - outer_ec = get_eclass_for_sort_expr(root, - outer_expr, - sub_member->em_datatype, - sub_eclass->ec_opfamilies); - outer_pk = make_canonical_pathkey(root, - outer_ec, - sub_pathkey->pk_opfamily, - sub_pathkey->pk_strategy, - sub_pathkey->pk_nulls_first); - /* score = # of equivalence peers */ - score = list_length(outer_ec->ec_members) - 1; - /* +1 if it matches the proper query_pathkeys item */ - if (retvallen < outer_query_keys && - list_nth(root->query_pathkeys, retvallen) == outer_pk) - score++; - if (score > best_score) - { - best_pathkey = outer_pk; - best_score = score; + /* resjunk items aren't visible to outer query */ + if (tle->resjunk) + continue; + + if (equal(tle->expr, sub_expr)) + { + /* Exact match */ + outer_expr = (Expr *) + makeVar(rel->relid, + tle->resno, + exprType((Node *) tle->expr), + exprTypmod((Node *) tle->expr), + 0); + } + 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 */ + outer_expr = (Expr *) + makeVar(rel->relid, + tle->resno, + exprType((Node *) tle->expr), + exprTypmod((Node *) tle->expr), + 0); + if (exprType((Node *) outer_expr) != + exprType((Node *) sub_expr)) + outer_expr = (Expr *) + makeRelabelType(outer_expr, + exprType((Node *) sub_expr), + -1, + COERCE_DONTCARE); + } + else + continue; + } + + /* Found a representation for this sub_pathkey */ + outer_ec = get_eclass_for_sort_expr(root, + outer_expr, + sub_member->em_datatype, + sub_eclass->ec_opfamilies, + 0); + outer_pk = make_canonical_pathkey(root, + outer_ec, + sub_pathkey->pk_opfamily, + sub_pathkey->pk_strategy, + sub_pathkey->pk_nulls_first); + /* score = # of equivalence peers */ + score = list_length(outer_ec->ec_members) - 1; + /* +1 if it matches the proper query_pathkeys item */ + if (retvallen < outer_query_keys && + list_nth(root->query_pathkeys, retvallen) == outer_pk) + score++; + if (score > best_score) + { + best_pathkey = outer_pk; + best_score = score; + } } } } @@ -795,6 +861,7 @@ make_pathkeys_for_sortclauses(PlannerInfo *root, sortkey, sortcl->sortop, sortcl->nulls_first, + sortcl->tleSortGroupRef, canonicalize); /* Canonical form eliminates redundant ordering keys */ @@ -843,12 +910,14 @@ cache_mergeclause_eclasses(PlannerInfo *root, RestrictInfo *restrictinfo) get_eclass_for_sort_expr(root, (Expr *) get_leftop(clause), lefttype, - restrictinfo->mergeopfamilies); + restrictinfo->mergeopfamilies, + 0); restrictinfo->right_ec = get_eclass_for_sort_expr(root, (Expr *) get_rightop(clause), righttype, - restrictinfo->mergeopfamilies); + restrictinfo->mergeopfamilies, + 0); } else Assert(restrictinfo->right_ec != NULL); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index becb8255e8..e2b46f970c 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -10,7 +10,7 @@ * * * 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,103 +2730,124 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, foreach(i, pathkeys) { PathKey *pathkey = (PathKey *) lfirst(i); + EquivalenceClass *ec = pathkey->pk_eclass; TargetEntry *tle = NULL; Oid pk_datatype = InvalidOid; Oid sortop; ListCell *j; - /* - * 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 - * might be cheapest to execute? (For example, int4lt is likely much - * cheaper to execute than numericlt, but both might appear in the - * same equivalence class...) Not clear that we ever will have an - * interesting choice in practice, so it may not matter. - */ - foreach(j, pathkey->pk_eclass->ec_members) + if (ec->ec_has_volatile) { - EquivalenceMember *em = (EquivalenceMember *) lfirst(j); - - if (em->em_is_const || em->em_is_child) - 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. + * 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. */ - tle = tlist_member_ignore_relabel((Node *) em->em_expr, tlist); - if (tle) - { - pk_datatype = em->em_datatype; - break; /* found expr already in tlist */ - } + 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; } - if (!tle) + else { - /* No matching tlist item; look for a computable expression */ - Expr *sortexpr = NULL; - - foreach(j, pathkey->pk_eclass->ec_members) + /* + * 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 + * might be cheapest to execute? (For example, int4lt is likely + * much cheaper to execute than numericlt, but both might appear + * in the same equivalence class...) Not clear that we ever will + * have an interesting choice in practice, so it may not matter. + */ + foreach(j, ec->ec_members) { EquivalenceMember *em = (EquivalenceMember *) lfirst(j); - List *exprvars; - ListCell *k; if (em->em_is_const || em->em_is_child) continue; - sortexpr = em->em_expr; - exprvars = pull_var_clause((Node *) sortexpr, false); - foreach(k, exprvars) + + tle = tlist_member((Node *) em->em_expr, tlist); + if (tle) { - if (!tlist_member_ignore_relabel(lfirst(k), tlist)) - break; + pk_datatype = em->em_datatype; + break; /* found expr already in tlist */ } - list_free(exprvars); - if (!k) + + /* + * 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. + */ + tle = tlist_member_ignore_relabel((Node *) em->em_expr, tlist); + if (tle) { pk_datatype = em->em_datatype; - break; /* found usable expression */ + break; /* found expr already in tlist */ } } - if (!j) - elog(ERROR, "could not find pathkey item to sort"); - /* - * Do we need to insert a Result node? - */ - if (!is_projection_capable_plan(lefttree)) + if (!tle) { - /* copy needed so we don't modify input's tlist below */ - tlist = copyObject(tlist); - lefttree = (Plan *) make_result(root, tlist, NULL, lefttree); - } + /* No matching tlist item; look for a computable expression */ + Expr *sortexpr = NULL; - /* - * 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 */ + foreach(j, ec->ec_members) + { + EquivalenceMember *em = (EquivalenceMember *) lfirst(j); + List *exprvars; + ListCell *k; + + if (em->em_is_const || em->em_is_child) + continue; + sortexpr = em->em_expr; + exprvars = pull_var_clause((Node *) sortexpr, false); + 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 (!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); + } + + /* + * 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 */ + } } /* diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c index e58fe7dc7f..d2ac14cfa1 100644 --- a/src/backend/optimizer/util/tlist.c +++ b/src/backend/optimizer/util/tlist.c @@ -8,7 +8,7 @@ * * * 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) return tlist; } + /* - * 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. + * get_sortgroupref_tle + * Find the targetlist entry matching the given SortGroupRef index, + * and return it. */ TargetEntry * -get_sortgroupclause_tle(SortClause *sortClause, - List *targetList) +get_sortgroupref_tle(Index sortref, List *targetList) { - Index refnumber = sortClause->tleSortGroupRef; ListCell *l; foreach(l, targetList) { TargetEntry *tle = (TargetEntry *) lfirst(l); - if (tle->ressortgroupref == refnumber) + if (tle->ressortgroupref == sortref) return tle; } @@ -158,6 +154,21 @@ get_sortgroupclause_tle(SortClause *sortClause, 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 * Find the targetlist entry matching the given SortClause diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 3890352022..db6eef2096 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * 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 * volatile functions are never considered mergejoinable. We mark such * 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 * 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_below_outer_join; /* equivalence applies below an OJ */ 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 */ } EquivalenceClass; diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index cbde0c7b9a..cf589b48af 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * 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, Expr *expr, Oid expr_datatype, - List *opfamilies); + List *opfamilies, + Index sortref); extern void generate_base_implied_equalities(PlannerInfo *root); extern List *generate_join_implied_equalities(PlannerInfo *root, RelOptInfo *joinrel, diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h index bb127cbce2..515339363a 100644 --- a/src/include/optimizer/tlist.h +++ b/src/include/optimizer/tlist.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * 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 *add_to_flat_tlist(List *tlist, List *vars); +extern TargetEntry *get_sortgroupref_tle(Index sortref, + List *targetList); extern TargetEntry *get_sortgroupclause_tle(SortClause *sortClause, List *targetList); extern Node *get_sortgroupclause_expr(SortClause *sortClause, -- 2.40.0