Commit ce0be732 authored by igor@olga.mysql.com's avatar igor@olga.mysql.com
Browse files

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.  
parent 2d803761
Loading
Loading
Loading
Loading
+61 −0
Original line number Diff line number Diff line
@@ -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;
+19 −0
Original line number Diff line number Diff line
@@ -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
+25 −0
Original line number Diff line number Diff line
@@ -590,6 +590,31 @@ class Item_func_max :public Item_func_min_max
};


/* 
  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;
+27 −1
Original line number Diff line number Diff line
@@ -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)