Commit 7977a0c8 authored by igor@rurik.mysql.com's avatar igor@rurik.mysql.com

Fixed bug #14927.

A query with a group by and having clauses could return a wrong
result set if the having condition contained a constant conjunct 
evaluated to FALSE.
It happened because the pushdown condition for table with
grouping columns lost its constant conjuncts.
Pushdown conditions are always built by the function make_cond_for_table
that ignores constant conjuncts. This is apparently not correct when
constant false conjuncts are present.
parent ceef1105
...@@ -141,3 +141,20 @@ SUM(a) ...@@ -141,3 +141,20 @@ SUM(a)
6 6
4 4
DROP TABLE t1; DROP TABLE t1;
CREATE TABLE t1 (a int);
INSERT INTO t1 VALUES (1), (2), (1), (3), (2), (1);
SELECT a FROM t1 GROUP BY a HAVING a > 1;
a
2
3
SELECT a FROM t1 GROUP BY a HAVING 1 != 1 AND a > 1;
a
SELECT 0 AS x, a FROM t1 GROUP BY x,a HAVING x=1 AND a > 1;
x a
EXPLAIN SELECT a FROM t1 GROUP BY a HAVING 1 != 1 AND a > 1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible HAVING
EXPLAIN SELECT 0 AS x, a FROM t1 GROUP BY x,a HAVING x=1 AND a > 1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible HAVING
DROP table t1;
...@@ -135,4 +135,20 @@ SELECT SUM(a) FROM t1 GROUP BY a HAVING SUM(a); ...@@ -135,4 +135,20 @@ SELECT SUM(a) FROM t1 GROUP BY a HAVING SUM(a);
DROP TABLE t1; DROP TABLE t1;
#
# Bug #14927: HAVING clause containing constant false conjunct
#
CREATE TABLE t1 (a int);
INSERT INTO t1 VALUES (1), (2), (1), (3), (2), (1);
SELECT a FROM t1 GROUP BY a HAVING a > 1;
SELECT a FROM t1 GROUP BY a HAVING 1 != 1 AND a > 1;
SELECT 0 AS x, a FROM t1 GROUP BY x,a HAVING x=1 AND a > 1;
EXPLAIN SELECT a FROM t1 GROUP BY a HAVING 1 != 1 AND a > 1;
EXPLAIN SELECT 0 AS x, a FROM t1 GROUP BY x,a HAVING x=1 AND a > 1;
DROP table t1;
# End of 4.1 tests # End of 4.1 tests
...@@ -1074,6 +1074,7 @@ void st_select_lex::init_query() ...@@ -1074,6 +1074,7 @@ void st_select_lex::init_query()
item_list.empty(); item_list.empty();
join= 0; join= 0;
where= 0; where= 0;
having= 0;
olap= UNSPECIFIED_OLAP_TYPE; olap= UNSPECIFIED_OLAP_TYPE;
having_fix_field= 0; having_fix_field= 0;
resolve_mode= NOMATTER_MODE; resolve_mode= NOMATTER_MODE;
...@@ -1081,6 +1082,7 @@ void st_select_lex::init_query() ...@@ -1081,6 +1082,7 @@ void st_select_lex::init_query()
ref_pointer_array= 0; ref_pointer_array= 0;
select_n_having_items= 0; select_n_having_items= 0;
prep_where= 0; prep_where= 0;
prep_having= 0;
subquery_in_having= explicit_limit= 0; subquery_in_having= explicit_limit= 0;
parsing_place= NO_MATTER; parsing_place= NO_MATTER;
is_item_list_lookup= 0; is_item_list_lookup= 0;
......
...@@ -422,6 +422,7 @@ public: ...@@ -422,6 +422,7 @@ public:
char *db, *db1, *table1, *db2, *table2; /* For outer join using .. */ char *db, *db1, *table1, *db2, *table2; /* For outer join using .. */
Item *where, *having; /* WHERE & HAVING clauses */ Item *where, *having; /* WHERE & HAVING clauses */
Item *prep_where; /* saved WHERE clause for prepared statement processing */ Item *prep_where; /* saved WHERE clause for prepared statement processing */
Item *prep_having;/* saved HAVING clause for prepared statement processing */
enum olap_type olap; enum olap_type olap;
SQL_LIST table_list, group_list; /* FROM & GROUP BY clauses */ SQL_LIST table_list, group_list; /* FROM & GROUP BY clauses */
List<Item> item_list; /* list of fields & expressions */ List<Item> item_list; /* list of fields & expressions */
......
...@@ -1667,10 +1667,11 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, ...@@ -1667,10 +1667,11 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
for (; sl; sl= sl->next_select_in_list()) for (; sl; sl= sl->next_select_in_list())
{ {
/* /*
Save WHERE clause pointers, because they may be changed Save WHERE, HAVING clause pointers, because they may be changed
during query optimisation. during query optimisation.
*/ */
sl->prep_where= sl->where; sl->prep_where= sl->where;
sl->prep_having= sl->having;
/* /*
Switch off a temporary flag that prevents evaluation of Switch off a temporary flag that prevents evaluation of
subqueries in statement prepare. subqueries in statement prepare.
...@@ -1696,13 +1697,18 @@ static void reset_stmt_for_execute(Prepared_statement *stmt) ...@@ -1696,13 +1697,18 @@ static void reset_stmt_for_execute(Prepared_statement *stmt)
/* remove option which was put by mysql_explain_union() */ /* remove option which was put by mysql_explain_union() */
sl->options&= ~SELECT_DESCRIBE; sl->options&= ~SELECT_DESCRIBE;
/* /*
Copy WHERE clause pointers to avoid damaging they by optimisation Copy WHERE, HAVING clause pointers to avoid damaging they by optimisation
*/ */
if (sl->prep_where) if (sl->prep_where)
{ {
sl->where= sl->prep_where->copy_andor_structure(thd); sl->where= sl->prep_where->copy_andor_structure(thd);
sl->where->cleanup(); sl->where->cleanup();
} }
if (sl->prep_having)
{
sl->having= sl->prep_having->copy_andor_structure(thd);
sl->having->cleanup();
}
DBUG_ASSERT(sl->join == 0); DBUG_ASSERT(sl->join == 0);
ORDER *order; ORDER *order;
/* Fix GROUP list */ /* Fix GROUP list */
......
...@@ -501,12 +501,24 @@ JOIN::optimize() ...@@ -501,12 +501,24 @@ JOIN::optimize()
DBUG_RETURN(1); DBUG_RETURN(1);
} }
if (cond_value == Item::COND_FALSE || {
(!unit->select_limit_cnt && !(select_options & OPTION_FOUND_ROWS))) Item::cond_result having_value;
{ /* Impossible cond */ having= optimize_cond(thd, having, &having_value);
zero_result_cause= "Impossible WHERE"; if (thd->net.report_error)
error= 0; {
DBUG_RETURN(0); error= 1;
DBUG_PRINT("error",("Error from optimize_cond"));
DBUG_RETURN(1);
}
if (cond_value == Item::COND_FALSE || having_value == Item::COND_FALSE ||
(!unit->select_limit_cnt && !(select_options & OPTION_FOUND_ROWS)))
{ /* Impossible cond */
zero_result_cause= having_value == Item::COND_FALSE ?
"Impossible HAVING" : "Impossible WHERE";
error= 0;
DBUG_RETURN(0);
}
} }
/* Optimize count(*), min() and max() */ /* Optimize count(*), min() and max() */
...@@ -4612,10 +4624,8 @@ optimize_cond(THD *thd, COND *conds, Item::cond_result *cond_value) ...@@ -4612,10 +4624,8 @@ optimize_cond(THD *thd, COND *conds, Item::cond_result *cond_value)
DBUG_EXECUTE("info", print_where(conds, "after remove");); DBUG_EXECUTE("info", print_where(conds, "after remove"););
} }
else else
{
*cond_value= Item::COND_TRUE; *cond_value= Item::COND_TRUE;
select->prep_where= 0;
}
DBUG_RETURN(conds); DBUG_RETURN(conds);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment