Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit c291203

Browse files
committed
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.
1 parent 1be0601 commit c291203

File tree

8 files changed

+300
-183
lines changed

8 files changed

+300
-183
lines changed

src/backend/nodes/outfuncs.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.315 2007/10/11 18:05:27 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.316 2007/11/08 21:49:47 tgl Exp $
1212
*
1313
* NOTES
1414
* Every node type that can appear in stored rules' parsetrees *must*
@@ -1405,6 +1405,7 @@ _outEquivalenceClass(StringInfo str, EquivalenceClass *node)
14051405
WRITE_BOOL_FIELD(ec_has_volatile);
14061406
WRITE_BOOL_FIELD(ec_below_outer_join);
14071407
WRITE_BOOL_FIELD(ec_broken);
1408+
WRITE_UINT_FIELD(ec_sortref);
14081409
}
14091410

14101411
static void

src/backend/optimizer/path/equivclass.c

+11-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* Portions Copyright (c) 1994, Regents of the University of California
1111
*
1212
* IDENTIFICATION
13-
* $PostgreSQL: pgsql/src/backend/optimizer/path/equivclass.c,v 1.3 2007/07/07 20:46:45 tgl Exp $
13+
* $PostgreSQL: pgsql/src/backend/optimizer/path/equivclass.c,v 1.4 2007/11/08 21:49:47 tgl Exp $
1414
*
1515
*-------------------------------------------------------------------------
1616
*/
@@ -294,6 +294,7 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
294294
ec->ec_has_volatile = false;
295295
ec->ec_below_outer_join = below_outer_join;
296296
ec->ec_broken = false;
297+
ec->ec_sortref = 0;
297298
ec->ec_merged = NULL;
298299
em1 = add_eq_member(ec, item1, item1_relids, false, item1_type);
299300
em2 = add_eq_member(ec, item2, item2_relids, false, item2_type);
@@ -354,6 +355,9 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
354355
* class it is a member of; if none, build a new single-member
355356
* EquivalenceClass for it.
356357
*
358+
* sortref is the SortGroupRef of the originating SortClause, if any,
359+
* or zero if not.
360+
*
357361
* This can be used safely both before and after EquivalenceClass merging;
358362
* since it never causes merging it does not invalidate any existing ECs
359363
* or PathKeys.
@@ -367,7 +371,8 @@ EquivalenceClass *
367371
get_eclass_for_sort_expr(PlannerInfo *root,
368372
Expr *expr,
369373
Oid expr_datatype,
370-
List *opfamilies)
374+
List *opfamilies,
375+
Index sortref)
371376
{
372377
EquivalenceClass *newec;
373378
EquivalenceMember *newem;
@@ -382,7 +387,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
382387
EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
383388
ListCell *lc2;
384389

385-
/* we allow matching to a volatile EC here */
390+
/* Never match to a volatile EC */
391+
if (cur_ec->ec_has_volatile)
392+
continue;
386393

387394
if (!equal(opfamilies, cur_ec->ec_opfamilies))
388395
continue;
@@ -423,6 +430,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
423430
newec->ec_has_volatile = contain_volatile_functions((Node *) expr);
424431
newec->ec_below_outer_join = false;
425432
newec->ec_broken = false;
433+
newec->ec_sortref = sortref;
426434
newec->ec_merged = NULL;
427435
newem = add_eq_member(newec, expr, pull_varnos((Node *) expr),
428436
false, expr_datatype);

src/backend/optimizer/path/pathkeys.c

+158-89
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
* Portions Copyright (c) 1994, Regents of the University of California
1212
*
1313
* IDENTIFICATION
14-
* $PostgreSQL: pgsql/src/backend/optimizer/path/pathkeys.c,v 1.88 2007/11/08 19:25:37 tgl Exp $
14+
* $PostgreSQL: pgsql/src/backend/optimizer/path/pathkeys.c,v 1.89 2007/11/08 21:49:47 tgl Exp $
1515
*
1616
*-------------------------------------------------------------------------
1717
*/
@@ -46,6 +46,7 @@ static bool pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys);
4646
static PathKey *make_pathkey_from_sortinfo(PlannerInfo *root,
4747
Expr *expr, Oid ordering_op,
4848
bool nulls_first,
49+
Index sortref,
4950
bool canonicalize);
5051
static Var *find_indexkey_var(PlannerInfo *root, RelOptInfo *rel,
5152
AttrNumber varattno);
@@ -233,13 +234,17 @@ canonicalize_pathkeys(PlannerInfo *root, List *pathkeys)
233234
* a PathKey. If canonicalize = true, the result is a "canonical"
234235
* PathKey, otherwise not. (But note it might be redundant anyway.)
235236
*
237+
* If the PathKey is being generated from a SortClause, sortref should be
238+
* the SortClause's SortGroupRef; otherwise zero.
239+
*
236240
* canonicalize should always be TRUE after EquivalenceClass merging has
237241
* been performed, but FALSE if we haven't done EquivalenceClass merging yet.
238242
*/
239243
static PathKey *
240244
make_pathkey_from_sortinfo(PlannerInfo *root,
241245
Expr *expr, Oid ordering_op,
242246
bool nulls_first,
247+
Index sortref,
243248
bool canonicalize)
244249
{
245250
Oid opfamily,
@@ -303,7 +308,8 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
303308
}
304309

305310
/* Now find or create a matching EquivalenceClass */
306-
eclass = get_eclass_for_sort_expr(root, expr, opcintype, opfamilies);
311+
eclass = get_eclass_for_sort_expr(root, expr, opcintype, opfamilies,
312+
sortref);
307313

308314
/* And finally we can find or create a PathKey node */
309315
if (canonicalize)
@@ -525,6 +531,7 @@ build_index_pathkeys(PlannerInfo *root,
525531
indexkey,
526532
sortop,
527533
nulls_first,
534+
0,
528535
true);
529536

530537
/* Add to list unless redundant */
@@ -597,102 +604,161 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
597604
PathKey *sub_pathkey = (PathKey *) lfirst(i);
598605
EquivalenceClass *sub_eclass = sub_pathkey->pk_eclass;
599606
PathKey *best_pathkey = NULL;
600-
int best_score = -1;
601-
ListCell *j;
602607

603-
/*
604-
* The sub_pathkey's EquivalenceClass could contain multiple elements
605-
* (representing knowledge that multiple items are effectively equal).
606-
* Each element might match none, one, or more of the output columns
607-
* that are visible to the outer query. This means we may have
608-
* multiple possible representations of the sub_pathkey in the context
609-
* of the outer query. Ideally we would generate them all and put
610-
* them all into an EC of the outer query, thereby propagating
611-
* equality knowledge up to the outer query. Right now we cannot do
612-
* so, because the outer query's EquivalenceClasses are already frozen
613-
* when this is called. Instead we prefer the one that has the highest
614-
* "score" (number of EC peers, plus one if it matches the outer
615-
* query_pathkeys). This is the most likely to be useful in the outer
616-
* query.
617-
*/
618-
foreach(j, sub_eclass->ec_members)
608+
if (sub_eclass->ec_has_volatile)
619609
{
620-
EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
621-
Expr *sub_expr = sub_member->em_expr;
622-
Expr *rtarg;
623-
ListCell *k;
624-
625610
/*
626-
* We handle two cases: the sub_pathkey key can be either an exact
627-
* match for a targetlist entry, or a RelabelType of a targetlist
628-
* entry. (The latter case is worth extra code because it arises
629-
* frequently in connection with varchar fields.)
611+
* If the sub_pathkey's EquivalenceClass is volatile, then it must
612+
* have come from an ORDER BY clause, and we have to match it to
613+
* that same targetlist entry.
630614
*/
631-
if (IsA(sub_expr, RelabelType))
632-
rtarg = ((RelabelType *) sub_expr)->arg;
633-
else
634-
rtarg = NULL;
635-
636-
foreach(k, sub_tlist)
615+
TargetEntry *tle;
616+
617+
if (sub_eclass->ec_sortref == 0) /* can't happen */
618+
elog(ERROR, "volatile EquivalenceClass has no sortref");
619+
tle = get_sortgroupref_tle(sub_eclass->ec_sortref, sub_tlist);
620+
Assert(tle);
621+
/* resjunk items aren't visible to outer query */
622+
if (!tle->resjunk)
637623
{
638-
TargetEntry *tle = (TargetEntry *) lfirst(k);
624+
/* We can represent this sub_pathkey */
625+
EquivalenceMember *sub_member;
639626
Expr *outer_expr;
640627
EquivalenceClass *outer_ec;
641-
PathKey *outer_pk;
642-
int score;
643628

644-
/* resjunk items aren't visible to outer query */
645-
if (tle->resjunk)
646-
continue;
629+
Assert(list_length(sub_eclass->ec_members) == 1);
630+
sub_member = (EquivalenceMember *) linitial(sub_eclass->ec_members);
631+
outer_expr = (Expr *)
632+
makeVar(rel->relid,
633+
tle->resno,
634+
exprType((Node *) tle->expr),
635+
exprTypmod((Node *) tle->expr),
636+
0);
637+
outer_ec =
638+
get_eclass_for_sort_expr(root,
639+
outer_expr,
640+
sub_member->em_datatype,
641+
sub_eclass->ec_opfamilies,
642+
0);
643+
best_pathkey =
644+
make_canonical_pathkey(root,
645+
outer_ec,
646+
sub_pathkey->pk_opfamily,
647+
sub_pathkey->pk_strategy,
648+
sub_pathkey->pk_nulls_first);
649+
}
650+
}
651+
else
652+
{
653+
/*
654+
* Otherwise, the sub_pathkey's EquivalenceClass could contain
655+
* multiple elements (representing knowledge that multiple items
656+
* are effectively equal). Each element might match none, one, or
657+
* more of the output columns that are visible to the outer
658+
* query. This means we may have multiple possible representations
659+
* of the sub_pathkey in the context of the outer query. Ideally
660+
* we would generate them all and put them all into an EC of the
661+
* outer query, thereby propagating equality knowledge up to the
662+
* outer query. Right now we cannot do so, because the outer
663+
* query's EquivalenceClasses are already frozen when this is
664+
* called. Instead we prefer the one that has the highest "score"
665+
* (number of EC peers, plus one if it matches the outer
666+
* query_pathkeys). This is the most likely to be useful in the
667+
* outer query.
668+
*/
669+
int best_score = -1;
670+
ListCell *j;
647671

648-
if (equal(tle->expr, sub_expr))
649-
{
650-
/* Exact match */
651-
outer_expr = (Expr *)
652-
makeVar(rel->relid,
653-
tle->resno,
654-
exprType((Node *) tle->expr),
655-
exprTypmod((Node *) tle->expr),
656-
0);
657-
}
658-
else if (rtarg && equal(tle->expr, rtarg))
672+
foreach(j, sub_eclass->ec_members)
673+
{
674+
EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
675+
Expr *sub_expr = sub_member->em_expr;
676+
Expr *sub_stripped;
677+
ListCell *k;
678+
679+
/*
680+
* We handle two cases: the sub_pathkey key can be either an
681+
* exact match for a targetlist entry, or it could match after
682+
* stripping RelabelType nodes. (We need that case since
683+
* make_pathkey_from_sortinfo could add or remove RelabelType.)
684+
*/
685+
sub_stripped = sub_expr;
686+
while (sub_stripped && IsA(sub_stripped, RelabelType))
687+
sub_stripped = ((RelabelType *) sub_stripped)->arg;
688+
689+
foreach(k, sub_tlist)
659690
{
660-
/* Match after discarding RelabelType */
661-
outer_expr = (Expr *)
662-
makeVar(rel->relid,
663-
tle->resno,
664-
exprType((Node *) tle->expr),
665-
exprTypmod((Node *) tle->expr),
666-
0);
667-
outer_expr = (Expr *)
668-
makeRelabelType((Expr *) outer_expr,
669-
((RelabelType *) sub_expr)->resulttype,
670-
((RelabelType *) sub_expr)->resulttypmod,
671-
((RelabelType *) sub_expr)->relabelformat);
672-
}
673-
else
674-
continue;
691+
TargetEntry *tle = (TargetEntry *) lfirst(k);
692+
Expr *outer_expr;
693+
EquivalenceClass *outer_ec;
694+
PathKey *outer_pk;
695+
int score;
675696

676-
/* Found a representation for this sub_pathkey */
677-
outer_ec = get_eclass_for_sort_expr(root,
678-
outer_expr,
679-
sub_member->em_datatype,
680-
sub_eclass->ec_opfamilies);
681-
outer_pk = make_canonical_pathkey(root,
682-
outer_ec,
683-
sub_pathkey->pk_opfamily,
684-
sub_pathkey->pk_strategy,
685-
sub_pathkey->pk_nulls_first);
686-
/* score = # of equivalence peers */
687-
score = list_length(outer_ec->ec_members) - 1;
688-
/* +1 if it matches the proper query_pathkeys item */
689-
if (retvallen < outer_query_keys &&
690-
list_nth(root->query_pathkeys, retvallen) == outer_pk)
691-
score++;
692-
if (score > best_score)
693-
{
694-
best_pathkey = outer_pk;
695-
best_score = score;
697+
/* resjunk items aren't visible to outer query */
698+
if (tle->resjunk)
699+
continue;
700+
701+
if (equal(tle->expr, sub_expr))
702+
{
703+
/* Exact match */
704+
outer_expr = (Expr *)
705+
makeVar(rel->relid,
706+
tle->resno,
707+
exprType((Node *) tle->expr),
708+
exprTypmod((Node *) tle->expr),
709+
0);
710+
}
711+
else
712+
{
713+
Expr *tle_stripped;
714+
715+
tle_stripped = tle->expr;
716+
while (tle_stripped && IsA(tle_stripped, RelabelType))
717+
tle_stripped = ((RelabelType *) tle_stripped)->arg;
718+
719+
if (equal(tle_stripped, sub_stripped))
720+
{
721+
/* Match after discarding RelabelType */
722+
outer_expr = (Expr *)
723+
makeVar(rel->relid,
724+
tle->resno,
725+
exprType((Node *) tle->expr),
726+
exprTypmod((Node *) tle->expr),
727+
0);
728+
if (exprType((Node *) outer_expr) !=
729+
exprType((Node *) sub_expr))
730+
outer_expr = (Expr *)
731+
makeRelabelType(outer_expr,
732+
exprType((Node *) sub_expr),
733+
-1,
734+
COERCE_DONTCARE);
735+
}
736+
else
737+
continue;
738+
}
739+
740+
/* Found a representation for this sub_pathkey */
741+
outer_ec = get_eclass_for_sort_expr(root,
742+
outer_expr,
743+
sub_member->em_datatype,
744+
sub_eclass->ec_opfamilies,
745+
0);
746+
outer_pk = make_canonical_pathkey(root,
747+
outer_ec,
748+
sub_pathkey->pk_opfamily,
749+
sub_pathkey->pk_strategy,
750+
sub_pathkey->pk_nulls_first);
751+
/* score = # of equivalence peers */
752+
score = list_length(outer_ec->ec_members) - 1;
753+
/* +1 if it matches the proper query_pathkeys item */
754+
if (retvallen < outer_query_keys &&
755+
list_nth(root->query_pathkeys, retvallen) == outer_pk)
756+
score++;
757+
if (score > best_score)
758+
{
759+
best_pathkey = outer_pk;
760+
best_score = score;
761+
}
696762
}
697763
}
698764
}
@@ -795,6 +861,7 @@ make_pathkeys_for_sortclauses(PlannerInfo *root,
795861
sortkey,
796862
sortcl->sortop,
797863
sortcl->nulls_first,
864+
sortcl->tleSortGroupRef,
798865
canonicalize);
799866

800867
/* Canonical form eliminates redundant ordering keys */
@@ -843,12 +910,14 @@ cache_mergeclause_eclasses(PlannerInfo *root, RestrictInfo *restrictinfo)
843910
get_eclass_for_sort_expr(root,
844911
(Expr *) get_leftop(clause),
845912
lefttype,
846-
restrictinfo->mergeopfamilies);
913+
restrictinfo->mergeopfamilies,
914+
0);
847915
restrictinfo->right_ec =
848916
get_eclass_for_sort_expr(root,
849917
(Expr *) get_rightop(clause),
850918
righttype,
851-
restrictinfo->mergeopfamilies);
919+
restrictinfo->mergeopfamilies,
920+
0);
852921
}
853922
else
854923
Assert(restrictinfo->right_ec != NULL);

0 commit comments

Comments
 (0)