summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorigor@olga.mysql.com <>2007-04-29 16:04:43 -0700
committerigor@olga.mysql.com <>2007-04-29 16:04:43 -0700
commitce0be732d08b4966115486191fc37af5e1478b00 (patch)
treead655f83591da9e2ddefe8be314cbb25a66a1ab8
parent2d8037610c7c767e698b66bfb85f2a021d9e124b (diff)
downloadmariadb-git-ce0be732d08b4966115486191fc37af5e1478b00.tar.gz
Fixed bug #24856: the result set of a ROLLUP query with DISTINCT could lack
some rollup rows (rows with NULLs for grouping attributes) if GROUP BY list contained constant expressions. This happened because the results of constant expressions were not put in the temporary table used for duplicate elimination. In fact a constant item from the GROUP BY list of a ROLLUP query can be replaced for an Item_null_result object when a rollup row is produced . Now the JOIN::rollup_init function wraps any constant item referenced in the GROYP BY list of a ROLLUP query into an Item_func object of a special class that is never detected as constant item. This ensures creation of fields for such constant items in temporary tables and guarantees right results when the result of the rollup operation first has to be written into a temporary table, e.g. in the cases when duplicate elimination is required.
-rw-r--r--mysql-test/r/olap.result61
-rw-r--r--mysql-test/t/olap.test19
-rw-r--r--sql/item_func.h25
-rw-r--r--sql/sql_select.cc28
4 files changed, 132 insertions, 1 deletions
diff --git a/mysql-test/r/olap.result b/mysql-test/r/olap.result
index 74b7570ea2a..0300fc1759e 100644
--- a/mysql-test/r/olap.result
+++ b/mysql-test/r/olap.result
@@ -556,3 +556,64 @@ x a sum(b)
2006-07-01 NULL 11
NULL NULL 11
drop table t1;
+CREATE TABLE t1 (a int, b int);
+INSERT INTO t1
+VALUES (2,10),(3,30),(2,40),(1,10),(2,30),(1,20),(2,10);
+SELECT a, SUM(b) FROM t1 GROUP BY a WITH ROLLUP;
+a SUM(b)
+1 30
+2 90
+3 30
+NULL 150
+SELECT DISTINCT a, SUM(b) FROM t1 GROUP BY a WITH ROLLUP;
+a SUM(b)
+1 30
+2 90
+3 30
+NULL 150
+SELECT a, b, COUNT(*) FROM t1 GROUP BY a,b WITH ROLLUP;
+a b COUNT(*)
+1 10 1
+1 20 1
+1 NULL 2
+2 10 2
+2 30 1
+2 40 1
+2 NULL 4
+3 30 1
+3 NULL 1
+NULL NULL 7
+SELECT DISTINCT a, b, COUNT(*) FROM t1 GROUP BY a,b WITH ROLLUP;
+a b COUNT(*)
+1 10 1
+1 20 1
+1 NULL 2
+2 10 2
+2 30 1
+2 40 1
+2 NULL 4
+3 30 1
+3 NULL 1
+NULL NULL 7
+SELECT 'x', a, SUM(b) FROM t1 GROUP BY 1,2 WITH ROLLUP;
+x a SUM(b)
+x 1 30
+x 2 90
+x 3 30
+x NULL 150
+NULL NULL 150
+SELECT DISTINCT 'x', a, SUM(b) FROM t1 GROUP BY 1,2 WITH ROLLUP;
+x a SUM(b)
+x 1 30
+x 2 90
+x 3 30
+x NULL 150
+NULL NULL 150
+SELECT DISTINCT 'x', a, SUM(b) FROM t1 GROUP BY 1,2 WITH ROLLUP;
+x a SUM(b)
+x 1 30
+x 2 90
+x 3 30
+x NULL 150
+NULL NULL 150
+DROP TABLE t1;
diff --git a/mysql-test/t/olap.test b/mysql-test/t/olap.test
index 683e1402678..99db3874883 100644
--- a/mysql-test/t/olap.test
+++ b/mysql-test/t/olap.test
@@ -281,4 +281,23 @@ select left(a,10), a, sum(b) from t1 group by 1,2 with rollup;
select left(a,10) x, a, sum(b) from t1 group by x,a with rollup;
drop table t1;
+#
+# Bug #20825: ROLLUP by const item in a query with DISTINCT
+#
+
+CREATE TABLE t1 (a int, b int);
+INSERT INTO t1
+ VALUES (2,10),(3,30),(2,40),(1,10),(2,30),(1,20),(2,10);
+
+SELECT a, SUM(b) FROM t1 GROUP BY a WITH ROLLUP;
+SELECT DISTINCT a, SUM(b) FROM t1 GROUP BY a WITH ROLLUP;
+SELECT a, b, COUNT(*) FROM t1 GROUP BY a,b WITH ROLLUP;
+SELECT DISTINCT a, b, COUNT(*) FROM t1 GROUP BY a,b WITH ROLLUP;
+
+SELECT 'x', a, SUM(b) FROM t1 GROUP BY 1,2 WITH ROLLUP;
+SELECT DISTINCT 'x', a, SUM(b) FROM t1 GROUP BY 1,2 WITH ROLLUP;
+SELECT DISTINCT 'x', a, SUM(b) FROM t1 GROUP BY 1,2 WITH ROLLUP;
+
+DROP TABLE t1;
+
# End of 4.1 tests
diff --git a/sql/item_func.h b/sql/item_func.h
index 467b88eda76..ebe3a589aa1 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -590,6 +590,31 @@ public:
};
+/*
+ Objects of this class are used for ROLLUP queries to wrap up
+ each constant item referred to in GROUP BY list.
+*/
+
+class Item_func_rollup_const :public Item_func
+{
+public:
+ Item_func_rollup_const(Item *a) :Item_func(a)
+ { name= a->name; }
+ double val() { return args[0]->val(); }
+ longlong val_int() { return args[0]->val_int(); }
+ String *val_str(String *str) { return args[0]->val_str(str); }
+ const char *func_name() const { return "rollup_const"; }
+ bool const_item() const { return 0; }
+ Item_result result_type() const { return args[0]->result_type(); }
+ void fix_length_and_dec()
+ {
+ collation= args[0]->collation;
+ max_length= args[0]->max_length;
+ decimals=args[0]->decimals;
+ }
+};
+
+
class Item_func_length :public Item_int_func
{
String value;
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index af3ad782ee3..36a15841065 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -9754,7 +9754,7 @@ bool JOIN::rollup_init()
for (j=0 ; j < fields_list.elements ; j++)
rollup.fields[i].push_back(rollup.null_items[i]);
}
- List_iterator_fast<Item> it(all_fields);
+ List_iterator<Item> it(all_fields);
Item *item;
while ((item= it++))
{
@@ -9767,6 +9767,32 @@ bool JOIN::rollup_init()
{
item->maybe_null= 1;
found_in_group= 1;
+ if (item->const_item())
+ {
+ /*
+ For ROLLUP queries each constant item referenced in GROUP BY list
+ is wrapped up into an Item_func object yielding the same value
+ as the constant item. The objects of the wrapper class are never
+ considered as constant items and besides they inherit all
+ properties of the Item_result_field class.
+ This wrapping allows us to ensure writing constant items
+ into temporary tables whenever the result of the ROLLUP
+ operation has to be written into a temporary table, e.g. when
+ ROLLUP is used together with DISTINCT in the SELECT list.
+ Usually when creating temporary tables for a intermidiate
+ result we do not include fields for constant expressions.
+ */
+ Item* new_item= new Item_func_rollup_const(item);
+ if (!new_item)
+ return 1;
+ new_item->fix_fields(thd,0, (Item **) 0);
+ thd->change_item_tree(it.ref(), new_item);
+ for (ORDER *tmp= group_tmp; tmp; tmp= tmp->next)
+ {
+ if (*tmp->item == item)
+ thd->change_item_tree(tmp->item, new_item);
+ }
+ }
}
}
if (item->type() == Item::FUNC_ITEM && !found_in_group)