]> granicus.if.org Git - postgresql/commitdiff
Don't remove surplus columns from GROUP BY for inheritance parents
authorDavid Rowley <drowley@postgresql.org>
Wed, 3 Jul 2019 11:45:25 +0000 (23:45 +1200)
committerDavid Rowley <drowley@postgresql.org>
Wed, 3 Jul 2019 11:45:25 +0000 (23:45 +1200)
d4c3a156c added code to remove columns that were not part of a table's
PRIMARY KEY constraint from the GROUP BY clause when all the primary key
columns were present in the group by.  This is fine to do since we know
that there will only be one row per group coming from this relation.
However, the logic failed to consider inheritance parent relations.  These
can have child relations without a primary key, but even if they did, they
could duplicate one of the parent's rows or one from another child
relation.  In this case, those additional GROUP BY columns are required.

Fix this by disabling the optimization for inheritance parent tables.
In v11 and beyond, partitioned tables are fine since partitions cannot
overlap and before v11 partitioned tables could not have a primary key.

Reported-by: Manuel Rigger
Discussion: http://postgr.es/m/CA+u7OA7VLKf_vEr6kLF3MnWSA9LToJYncgpNX2tQ-oWzYCBQAw@mail.gmail.com
Backpatch-through: 9.6

src/backend/optimizer/plan/planner.c
src/test/regress/expected/aggregates.out
src/test/regress/sql/aggregates.sql

index eadc04222efcf1bf9bce261fac8d8702e57401b5..401299e542043e5b4c6b7721ec850da74f4f8021 100644 (file)
@@ -3124,6 +3124,14 @@ remove_useless_groupby_columns(PlannerInfo *root)
                if (rte->rtekind != RTE_RELATION)
                        continue;
 
+               /*
+                * We must skip inheritance parent tables as some of the child rels
+                * may cause duplicate rows.  This cannot happen with partitioned
+                * tables, however.
+                */
+               if (rte->inh && rte->relkind != RELKIND_PARTITIONED_TABLE)
+                       continue;
+
                /* Nothing to do unless this rel has multiple Vars in GROUP BY */
                relattnos = groupbyattnos[relid];
                if (bms_membership(relattnos) != BMS_MULTIPLE)
index 2e5ce8cc32bb8189206c6a2c751d2b3e99e18b40..ef8eec3fbf2cdd5c0cd1718ee84f5add6c2e312b 100644 (file)
@@ -1147,9 +1147,52 @@ explain (costs off) select * from t3 group by a,b,c;
    ->  Seq Scan on t3
 (3 rows)
 
-drop table t1;
+create temp table t1c () inherits (t1);
+-- Ensure we don't remove any columns when t1 has a child table
+explain (costs off) select * from t1 group by a,b,c,d;
+             QUERY PLAN              
+-------------------------------------
+ HashAggregate
+   Group Key: t1.a, t1.b, t1.c, t1.d
+   ->  Append
+         ->  Seq Scan on t1
+         ->  Seq Scan on t1c
+(5 rows)
+
+-- Okay to remove columns if we're only querying the parent.
+explain (costs off) select * from only t1 group by a,b,c,d;
+      QUERY PLAN      
+----------------------
+ HashAggregate
+   Group Key: a, b
+   ->  Seq Scan on t1
+(3 rows)
+
+create temp table p_t1 (
+  a int,
+  b int,
+  c int,
+  d int,
+  primary key(a,b)
+) partition by list(a);
+create temp table p_t1_1 partition of p_t1 for values in(1);
+create temp table p_t1_2 partition of p_t1 for values in(2);
+-- Ensure we can remove non-PK columns for partitioned tables.
+explain (costs off) select * from p_t1 group by a,b,c,d;
+           QUERY PLAN            
+---------------------------------
+ HashAggregate
+   Group Key: p_t1_1.a, p_t1_1.b
+   ->  Append
+         ->  Seq Scan on p_t1_1
+         ->  Seq Scan on p_t1_2
+(5 rows)
+
+drop table t1 cascade;
+NOTICE:  drop cascades to table t1c
 drop table t2;
 drop table t3;
+drop table p_t1;
 --
 -- Test combinations of DISTINCT and/or ORDER BY
 --
index ca0d5e66b2a2193e6e5a4c8c3bf33fb241d771fe..17fb256aec5cc62d1933d26a96a1e8e9d776e0aa 100644 (file)
@@ -406,9 +406,31 @@ group by t1.a,t1.b,t1.c,t1.d,t2.x,t2.z;
 -- Cannot optimize when PK is deferrable
 explain (costs off) select * from t3 group by a,b,c;
 
-drop table t1;
+create temp table t1c () inherits (t1);
+
+-- Ensure we don't remove any columns when t1 has a child table
+explain (costs off) select * from t1 group by a,b,c,d;
+
+-- Okay to remove columns if we're only querying the parent.
+explain (costs off) select * from only t1 group by a,b,c,d;
+
+create temp table p_t1 (
+  a int,
+  b int,
+  c int,
+  d int,
+  primary key(a,b)
+) partition by list(a);
+create temp table p_t1_1 partition of p_t1 for values in(1);
+create temp table p_t1_2 partition of p_t1 for values in(2);
+
+-- Ensure we can remove non-PK columns for partitioned tables.
+explain (costs off) select * from p_t1 group by a,b,c,d;
+
+drop table t1 cascade;
 drop table t2;
 drop table t3;
+drop table p_t1;
 
 --
 -- Test combinations of DISTINCT and/or ORDER BY