Apply my original fix for Taiki Yamaguchi's bug report about DISTINCT MAX().
authorTom Lane
Mon, 31 Mar 2008 16:59:26 +0000 (16:59 +0000)
committerTom Lane
Mon, 31 Mar 2008 16:59:26 +0000 (16:59 +0000)
Add some regression tests for plausible failures in this area.

src/backend/optimizer/path/equivclass.c
src/backend/optimizer/plan/planagg.c
src/include/optimizer/paths.h
src/test/regress/expected/aggregates.out
src/test/regress/sql/aggregates.sql

index 1bc6d15a3e83eaa1e70de86eb553e7436e3ec44c..b289ea6e65b37c998305cade838cd2aaf89d5f77 100644 (file)
@@ -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.9 2008/01/09 20:42:27 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/path/equivclass.c,v 1.10 2008/03/31 16:59:26 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1638,6 +1638,44 @@ add_child_rel_equivalences(PlannerInfo *root,
 }
 
 
+/*
+ * mutate_eclass_expressions
+ *   Apply an expression tree mutator to all expressions stored in
+ *   equivalence classes.
+ *
+ * This is a bit of a hack ... it's currently needed only by planagg.c,
+ * which needs to do a global search-and-replace of MIN/MAX Aggrefs
+ * after eclasses are already set up.  Without changing the eclasses too,
+ * subsequent matching of ORDER BY clauses would fail.
+ *
+ * Note that we assume the mutation won't affect relation membership or any
+ * other properties we keep track of (which is a bit bogus, but by the time
+ * planagg.c runs, it no longer matters).  Also we must be called in the
+ * main planner memory context.
+ */
+void
+mutate_eclass_expressions(PlannerInfo *root,
+                         Node *(*mutator) (),
+                         void *context)
+{
+   ListCell   *lc1;
+
+   foreach(lc1, root->eq_classes)
+   {
+       EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
+       ListCell   *lc2;
+
+       foreach(lc2, cur_ec->ec_members)
+       {
+           EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+
+           cur_em->em_expr = (Expr *)
+               mutator((Node *) cur_em->em_expr, context);
+       }
+   }
+}
+
+
 /*
  * find_eclass_clauses_for_index_join
  *   Create joinclauses usable for a nestloop-with-inner-indexscan
index e472e48ccb5aa5bbc5802d2bd347262be9bb3639..5bb92111f6065035a9758f81014cac061c743c54 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.36 2008/01/01 19:45:50 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.37 2008/03/31 16:59:26 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -187,6 +187,18 @@ optimize_minmax_aggregates(PlannerInfo *root, List *tlist, Path *best_path)
    hqual = replace_aggs_with_params_mutator(parse->havingQual,
                                             &aggs_list);
 
+   /*
+    * We have to replace Aggrefs with Params in equivalence classes too,
+    * else ORDER BY or DISTINCT on an optimized aggregate will fail.
+    *
+    * Note: at some point it might become necessary to mutate other
+    * data structures too, such as the query's sortClause or distinctClause.
+    * Right now, those won't be examined after this point.
+    */
+   mutate_eclass_expressions(root,
+                             replace_aggs_with_params_mutator,
+                             &aggs_list);
+
    /*
     * Generate the output plan --- basically just a Result
     */
index f3e50c5cbf96a40fb332c2fd1965092290b89ead..81e8089df169812fd758261dc8dbb52e1db9488c 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/paths.h,v 1.103 2008/01/01 19:45:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/paths.h,v 1.104 2008/03/31 16:59:26 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -127,6 +127,9 @@ extern void add_child_rel_equivalences(PlannerInfo *root,
                           AppendRelInfo *appinfo,
                           RelOptInfo *parent_rel,
                           RelOptInfo *child_rel);
+extern void mutate_eclass_expressions(PlannerInfo *root,
+                                     Node *(*mutator) (),
+                                     void *context);
 extern List *find_eclass_clauses_for_index_join(PlannerInfo *root,
                                   RelOptInfo *rel,
                                   Relids outer_relids);
index 74635479e486e8c8c473c2e7513311eee883d617..ae65314166f06c753e210ee5b377b9faa943dc51 100644 (file)
@@ -484,3 +484,36 @@ from int4_tbl;
  -2147483647 |  0
 (5 rows)
 
+-- check some cases that were handled incorrectly in 8.3.0
+select distinct max(unique2) from tenk1;
+ max  
+------
+ 9999
+(1 row)
+
+select max(unique2) from tenk1 order by 1;
+ max  
+------
+ 9999
+(1 row)
+
+select max(unique2) from tenk1 order by max(unique2);
+ max  
+------
+ 9999
+(1 row)
+
+select max(unique2) from tenk1 order by max(unique2)+1;
+ max  
+------
+ 9999
+(1 row)
+
+select max(unique2), generate_series(1,3) as g from tenk1 order by g desc;
+ max  | g 
+------+---
+ 9999 | 3
+ 9999 | 2
+ 9999 | 1
+(3 rows)
+
index 890aa8dea02d583d3112f46f778000091036494b..afa997e7ea7e8d0a608e9665386fcc47cc9d623d 100644 (file)
@@ -217,3 +217,10 @@ select min(tenthous) from tenk1 where thousand = 33;
 -- check parameter propagation into an indexscan subquery
 select f1, (select min(unique1) from tenk1 where unique1 > f1) AS gt
 from int4_tbl;
+
+-- check some cases that were handled incorrectly in 8.3.0
+select distinct max(unique2) from tenk1;
+select max(unique2) from tenk1 order by 1;
+select max(unique2) from tenk1 order by max(unique2);
+select max(unique2) from tenk1 order by max(unique2)+1;
+select max(unique2), generate_series(1,3) as g from tenk1 order by g desc;