diff options
author | Tom Lane | 2008-03-31 16:59:33 +0000 |
---|---|---|
committer | Tom Lane | 2008-03-31 16:59:33 +0000 |
commit | 0973c9a3a815ef191088eaacb6486b1f928db6ce (patch) | |
tree | 9cd80d46b183aff012199de168a8bfcf6a351540 | |
parent | 5a087a86bef9c39436ac7915537d06fe344baff1 (diff) |
Apply my original fix for Taiki Yamaguchi's bug report about DISTINCT MAX().
Add some regression tests for plausible failures in this area.
-rw-r--r-- | src/backend/optimizer/path/equivclass.c | 38 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planagg.c | 12 | ||||
-rw-r--r-- | src/include/optimizer/paths.h | 3 | ||||
-rw-r--r-- | src/test/regress/expected/aggregates.out | 33 | ||||
-rw-r--r-- | src/test/regress/sql/aggregates.sql | 7 |
5 files changed, 93 insertions, 0 deletions
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c index ce4aa5ed61..764d2eeb05 100644 --- a/src/backend/optimizer/path/equivclass.c +++ b/src/backend/optimizer/path/equivclass.c @@ -1639,6 +1639,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 * scanning the given inner rel with the specified set of outer rels. diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c index dc4922d797..b4e931c39b 100644 --- a/src/backend/optimizer/plan/planagg.c +++ b/src/backend/optimizer/plan/planagg.c @@ -188,6 +188,18 @@ optimize_minmax_aggregates(PlannerInfo *root, List *tlist, Path *best_path) &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 */ plan = (Plan *) make_result(root, tlist, hqual, NULL); diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index 166dab4d75..443495744b 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -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); diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index 74635479e4..ae65314166 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -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) + diff --git a/src/test/regress/sql/aggregates.sql b/src/test/regress/sql/aggregates.sql index 890aa8dea0..afa997e7ea 100644 --- a/src/test/regress/sql/aggregates.sql +++ b/src/test/regress/sql/aggregates.sql @@ -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; |