Commit a400e7fe authored by unknown's avatar unknown

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.


mysql-test/r/having.result:
  Added A test case for bug #14927.
mysql-test/t/having.test:
  Added A test case for bug #14927.
sql/sql_lex.cc:
  Fixed bug #14927.
  Initialized fields for having conditions in  st_select_lex::init_query().
sql/sql_lex.h:
  Fixed bug #14927.
  Added a field to restore having condititions for execution in SP and PS.
sql/sql_prepare.cc:
  Fixed bug #14927.
  Added code to restore havinf conditions for execution in SP and PS.
sql/sql_select.cc:
  Fixed bug #14927.
  Performed evaluation of constant expressions in having clauses.
  If the having condition contains a constant conjunct that is always false
  an empty result set is returned after the optimization phase.
  In this case the corresponding EXPLAIN command now returns 
  "Impossible HAVING" in the last column.
parent 97deac8b
...@@ -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;
......
...@@ -421,6 +421,7 @@ class st_select_lex: public st_select_lex_node ...@@ -421,6 +421,7 @@ class st_select_lex: public st_select_lex_node
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 */
......
...@@ -1665,10 +1665,11 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, ...@@ -1665,10 +1665,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.
...@@ -1694,13 +1695,18 @@ static void reset_stmt_for_execute(Prepared_statement *stmt) ...@@ -1694,13 +1695,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,18 @@ JOIN::optimize() ...@@ -501,12 +501,18 @@ 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";
error= 0; if (cond_value == Item::COND_FALSE || having_value == Item::COND_FALSE ||
DBUG_RETURN(0); (!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() */
...@@ -4611,10 +4617,8 @@ optimize_cond(THD *thd, COND *conds, Item::cond_result *cond_value) ...@@ -4611,10 +4617,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