Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions mysql-test/main/having.result
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,61 @@ DROP TABLE t1;
DROP VIEW v1;
SET optimizer_trace= @save_optimizer_trace;
#
# MDEV-40779 DISTINCT with HAVING on AVG() returns no rows when
# the last group fails HAVING
#
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1),(2),(3);
SELECT DISTINCT -AVG(a) FROM t1 GROUP BY a HAVING AVG(a) < 2;
-AVG(a)
-1.0000
SELECT DISTINCT -AVG(a), AVG(a) FROM t1 GROUP BY a HAVING AVG(a) < 3;
-AVG(a) AVG(a)
-1.0000 1.0000
-2.0000 2.0000
SELECT DISTINCT -AVG(a) FROM t1 GROUP BY a ORDER BY AVG(a) DESC;
-AVG(a)
-3.0000
-2.0000
-1.0000
DROP TABLE t1;
CREATE TABLE t1 (a INT, b INT);
INSERT INTO t1 VALUES (1,1),(1,3),(2,2),(2,8);
SELECT DISTINCT -STD(b) FROM t1 GROUP BY a HAVING VARIANCE(b) < 2;
-STD(b)
-1.0000
SELECT DISTINCT -STD(b) FROM t1 GROUP BY a ORDER BY STD(b) DESC;
-STD(b)
-3.0000
-1.0000
DROP TABLE t1;
# The value of a DOUBLE aggregate is not rounded to its decimals
CREATE TABLE t1 (a INT, b INT, c DOUBLE(10,2));
INSERT INTO t1 VALUES (1,1,0),(1,2,1),(1,3,1),(2,5,5);
SELECT DISTINCT CONCAT('v',VAR_POP(b)) FROM t1 GROUP BY a
HAVING VAR_POP(b) <> '0.6667';
CONCAT('v',VAR_POP(b))
v0.0000
v0.6667
SELECT DISTINCT CONCAT('v',AVG(c)) FROM t1 GROUP BY a
HAVING AVG(c) <> '0.666667';
CONCAT('v',AVG(c))
v0.666667
v5.000000
DROP TABLE t1;
# A BIT stored function over an aggregate, used as a HAVING alias
CREATE TABLE t1 (c INT);
INSERT INTO t1 VALUES (1),(1),(2),(2),(3),(3);
CREATE FUNCTION fbit(x INT) RETURNS BIT(8) RETURN x;
SELECT SQL_BIG_RESULT c, fbit(COUNT(*) + 63) AS d FROM t1 GROUP BY c
HAVING d > 64;
c d
1 A
2 A
3 A
DROP FUNCTION fbit;
DROP TABLE t1;
#
# End of 10.11 tests
#
#
Expand Down
49 changes: 49 additions & 0 deletions mysql-test/main/having.test
Original file line number Diff line number Diff line change
Expand Up @@ -1075,6 +1075,55 @@ EXECUTE stmt;
DROP TABLE t1;
DROP VIEW v1;
SET optimizer_trace= @save_optimizer_trace;

--echo #
--echo # MDEV-40779 DISTINCT with HAVING on AVG() returns no rows when
--echo # the last group fails HAVING
--echo #

CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1),(2),(3);

SELECT DISTINCT -AVG(a) FROM t1 GROUP BY a HAVING AVG(a) < 2;
--sorted_result
SELECT DISTINCT -AVG(a), AVG(a) FROM t1 GROUP BY a HAVING AVG(a) < 3;
SELECT DISTINCT -AVG(a) FROM t1 GROUP BY a ORDER BY AVG(a) DESC;

DROP TABLE t1;

CREATE TABLE t1 (a INT, b INT);
INSERT INTO t1 VALUES (1,1),(1,3),(2,2),(2,8);

SELECT DISTINCT -STD(b) FROM t1 GROUP BY a HAVING VARIANCE(b) < 2;
SELECT DISTINCT -STD(b) FROM t1 GROUP BY a ORDER BY STD(b) DESC;

DROP TABLE t1;

--echo # The value of a DOUBLE aggregate is not rounded to its decimals
CREATE TABLE t1 (a INT, b INT, c DOUBLE(10,2));
INSERT INTO t1 VALUES (1,1,0),(1,2,1),(1,3,1),(2,5,5);

--sorted_result
SELECT DISTINCT CONCAT('v',VAR_POP(b)) FROM t1 GROUP BY a
HAVING VAR_POP(b) <> '0.6667';
--sorted_result
SELECT DISTINCT CONCAT('v',AVG(c)) FROM t1 GROUP BY a
HAVING AVG(c) <> '0.666667';

DROP TABLE t1;

--echo # A BIT stored function over an aggregate, used as a HAVING alias
CREATE TABLE t1 (c INT);
INSERT INTO t1 VALUES (1),(1),(2),(2),(3),(3);
CREATE FUNCTION fbit(x INT) RETURNS BIT(8) RETURN x;

--sorted_result
SELECT SQL_BIG_RESULT c, fbit(COUNT(*) + 63) AS d FROM t1 GROUP BY c
HAVING d > 64;

DROP FUNCTION fbit;
DROP TABLE t1;

--echo #
--echo # End of 10.11 tests
--echo #
Expand Down
37 changes: 37 additions & 0 deletions mysql-test/main/olap.result
Original file line number Diff line number Diff line change
Expand Up @@ -920,4 +920,41 @@ a
1
NULL
drop table t1;
#
# MDEV-40779: HAVING is not checked for ROLLUP rows with DISTINCT
#
create table t1 (a int);
insert into t1 values (1),(2);
# The total row has a=NULL and fails a<0 and a=2.
select distinct a, a+0 as d from t1 group by a with rollup having a<0;
a d
select distinct a, a+0 as d from t1 group by a with rollup having a=2;
a d
2 2
select distinct a, a+0 as d from t1 group by a with rollup having a is null;
a d
NULL NULL
drop table t1;
#
# MDEV-40779: ROLLUP rows lost with DISTINCT when a group column
# is used only in an expression
#
create table t1 (a int, b int);
insert into t1 values (1,1),(1,2),(2,1);
# The total row has a+0=NULL.
select distinct a+0 from t1 group by a with rollup;
a+0
1
2
NULL
# The subtotal rows have b+0=NULL, the total row has a=NULL too.
select distinct a, b+0 from t1 group by a,b with rollup;
a b+0
1 1
1 2
1 NULL
2 1
2 NULL
NULL NULL
drop table t1;
# End of 11.4 tests
33 changes: 33 additions & 0 deletions mysql-test/main/olap.test
Original file line number Diff line number Diff line change
Expand Up @@ -548,4 +548,37 @@ select distinct a from t1 where a=1 and rand() >= 0 group by a with rollup;

drop table t1;

--echo #
--echo # MDEV-40779: HAVING is not checked for ROLLUP rows with DISTINCT
--echo #

create table t1 (a int);
insert into t1 values (1),(2);

--echo # The total row has a=NULL and fails a<0 and a=2.
select distinct a, a+0 as d from t1 group by a with rollup having a<0;
--sorted_result
select distinct a, a+0 as d from t1 group by a with rollup having a=2;
--sorted_result
select distinct a, a+0 as d from t1 group by a with rollup having a is null;

drop table t1;

--echo #
--echo # MDEV-40779: ROLLUP rows lost with DISTINCT when a group column
--echo # is used only in an expression
--echo #

create table t1 (a int, b int);
insert into t1 values (1,1),(1,2),(2,1);

--echo # The total row has a+0=NULL.
--sorted_result
select distinct a+0 from t1 group by a with rollup;
--echo # The subtotal rows have b+0=NULL, the total row has a=NULL too.
--sorted_result
select distinct a, b+0 from t1 group by a,b with rollup;

drop table t1;

--echo # End of 11.4 tests
6 changes: 3 additions & 3 deletions sql/item.h
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,7 @@ class Tmp_field_src
{
Field *m_field;
Field *m_default_field;
Item_result_field *m_item_result_field;
Item *m_item_result_field;
public:
Tmp_field_src()
:m_field(0),
Expand All @@ -698,10 +698,10 @@ class Tmp_field_src
{ }
Field *field() const { return m_field; }
Field *default_field() const { return m_default_field; }
Item_result_field *item_result_field() const { return m_item_result_field; }
Item *item_result_field() const { return m_item_result_field; }
void set_field(Field *field) { m_field= field; }
void set_default_field(Field *field) { m_default_field= field; }
void set_item_result_field(Item_result_field *item)
void set_item_result_field(Item *item)
{ m_item_result_field= item; }
};

Expand Down
44 changes: 41 additions & 3 deletions sql/item_sum.h
Original file line number Diff line number Diff line change
Expand Up @@ -1543,10 +1543,19 @@ class Item_sum_sp :public Item_sum,
class Item_sum_field :public Item
{
protected:
/* Aggregate state in the temp table that does the grouping */
Field *field;
/*
Column holding the value of this item in a later temp table, such as
the one for DISTINCT. field points into the grouping table's record
buffer, which holds the last group once that table has been read, so
HAVING, ORDER BY and duplicate removal on the later table must read
this column instead.
*/
Field *result_field;
public:
Item_sum_field(THD *thd, Item_sum *item)
:Item(thd), field(item->result_field)
:Item(thd), field(item->result_field), result_field(NULL)
{
name= item->name;
set_maybe_null();
Expand All @@ -1555,12 +1564,41 @@ class Item_sum_field :public Item
unsigned_flag= item->unsigned_flag;
}
table_map used_tables() const override { return (table_map) 1L; }
Field *get_tmp_table_field() override { return result_field; }
Field *create_tmp_field_ex(MEM_ROOT *root, TABLE *table, Tmp_field_src *src,
const Tmp_field_param *param) override
{
return create_tmp_field_ex_simple(root, table, src, param);
Field *result;
/*
Keep every digit of a DOUBLE value. A column with fixed decimals
rounds it to this item's decimals before HAVING and ORDER BY compare
it. Item_sum_variance::create_tmp_field creates its column the same
way when the temp table does not do the grouping.
*/
if (result_type() == REAL_RESULT)
{
if ((result= new (root) Field_double(max_length, maybe_null(), &name,
decimals, true)))
result->init(table);
}
else
result= create_tmp_field_ex_simple(root, table, src, param);
if (result && param->modify_item())
{
result_field= result;
src->set_item_result_field(this);
}
return result;
}
void save_in_result_field(bool no_conversions) override
{
save_in_field(result_field, no_conversions);
}
void cleanup() override
{
Item::cleanup();
result_field= NULL;
}
void save_in_result_field(bool no_conversions) override { DBUG_ASSERT(0); }
bool check_vcol_func_processor(void *arg) override
{
return mark_unsupported_function(name.str, arg, VCOL_IMPOSSIBLE);
Expand Down
26 changes: 18 additions & 8 deletions sql/sql_select.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26516,7 +26516,7 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
{
if (unlikely(join->rollup_write_data((uint) (idx+1),
join_tab->tmp_table_param,
table)))
table, join_tab->having)))
{
DBUG_RETURN(NESTED_LOOP_ERROR);
}
Expand Down Expand Up @@ -30841,6 +30841,7 @@ int JOIN::rollup_send_data(uint idx)
- 1 = First group changed (a)
- 2 = Second group changed (a,b)
@param table reference to temp table
@param having_arg JOIN_TAB::having

@retval
0 ok
Expand All @@ -30849,19 +30850,28 @@ int JOIN::rollup_send_data(uint idx)
*/

int JOIN::rollup_write_data(uint idx, TMP_TABLE_PARAM *tmp_table_param_arg,
TABLE *table_arg)
TABLE *table_arg, Item *having_arg)
{
uint i;
/*
When the optimizer moves HAVING onto the JOIN_TAB, JOIN::having
becomes NULL. ROLLUP rows must then pass the JOIN_TAB condition.
*/
Item *rollup_having= having_arg ? having_arg : having;
for (i= send_group_parts ; i-- > idx ; )
{
/* Get reference pointers to sum functions in place */
copy_ref_ptr_array(ref_ptrs, rollup.ref_pointer_arrays[i]);
if ((!having || having->val_bool()))
if ((!rollup_having || rollup_having->val_bool()))
{
int write_error;
Item *item;
List_iterator_fast<Item> it(rollup.fields[i]);
while ((item= it++))
/*
Store NULL in the temp table columns of the group columns
rolled up at this level. Expressions over a group column are
computed this table is read. The ref array holds the NULL
items of hidden group columns, too.
*/
for (Item *item : rollup.ref_pointer_arrays[i])
{
if (item->type() == Item::NULL_ITEM && item->is_result_field())
item->save_in_result_field(1);
Expand All @@ -30870,11 +30880,11 @@ int JOIN::rollup_write_data(uint idx, TMP_TABLE_PARAM *tmp_table_param_arg,
if (unlikely((write_error=
table_arg->file->ha_write_tmp_row(table_arg->record[0]))))
{
if (create_internal_tmp_table_from_heap(thd, table_arg,
if (create_internal_tmp_table_from_heap(thd, table_arg,
tmp_table_param_arg->start_recinfo,
&tmp_table_param_arg->recinfo,
write_error, 0, NULL))
return 1;
return 1;
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion sql/sql_select.h
Original file line number Diff line number Diff line change
Expand Up @@ -1838,7 +1838,8 @@ class JOIN :public Sql_alloc
bool rollup_make_fields(List<Item> &all_fields, List<Item> &fields,
Item_sum ***func);
int rollup_send_data(uint idx);
int rollup_write_data(uint idx, TMP_TABLE_PARAM *tmp_table_param, TABLE *table);
int rollup_write_data(uint idx, TMP_TABLE_PARAM *tmp_table_param,
TABLE *table, Item *having);
void join_free();
/** Cleanup this JOIN, possibly for reuse */
void cleanup(bool full);
Expand Down