Bug #54511 Assertion failed: cache != 0L in file sql_select.cc::sub_select_cache on HAVING
Submitted: 15 Jun 2010 11:23 Modified: 20 Nov 2010 23:23
Reporter: John Embretsen Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: Optimizer Severity:S1 (Critical)
Version:mysql-next-mr-opt-backporting, 6.0.14 OS:Any
Assigned to: Jørgen Løland CPU Architecture:Any

[15 Jun 2010 11:23] John Embretsen
Description:
While executing a complex query with a HAVING clause and multiple subqueries,
mysqld crashed as follows:

Assertion failed: cache != 0L, file /export/home/tmp/je159969/mysql-dev/bzr-repos/mysql-next-mr-opt-backporting-custom/sql/sql_select.cc, line 16573
100615 10:52:12 - mysqld got signal 6 ;

[12] _assert(0x0, 0x0, 0x0, 0x0, 0x0, 0x0), at 0xfffffd7fff1fdc95 
[13] sub_select_cache(join = 0x3b09e40, join_tab = 0x3a99498, end_of_records = false), line 16573 in "sql_select.cc"
[14] evaluate_join_record(join = 0x3b09e40, join_tab = 0x3a991e8, error = 0), line 17092 in "sql_select.cc"
[15] sub_select(join = 0x3b09e40, join_tab = 0x3a991e8, end_of_records = false), line 16796 in "sql_select.cc"
[16] do_select(join = 0x3b09e40, fields = (nil), table = 0x3a9bd90, procedure = (nil)), line 16334 in "sql_select.cc"
[17] JOIN::exec(this = 0x3a8f620), line 2649 in "sql_select.cc"
[18] subselect_single_select_engine::exec(this = 0x3a9e6e8), line 2320 in "item_subselect.cc"
[19] Item_subselect::exec(this = 0x3a9e5a0), line 295 in "item_subselect.cc"
[20] Item_in_subselect::exec(this = 0x3a9e5a0), line 345 in "item_subselect.cc"
[21] Item_in_subselect::val_bool(this = 0x3a9e5a0), line 941 in "item_subselect.cc"
[22] Item::val_bool_result(this = 0x3a9e5a0), line 851 in "item.h"
[23] Item_in_optimizer::val_int(this = 0x3b561c0), line 1837 in "item_cmpfunc.cc"
[24] return_zero_rows(join = 0x3b35fb0, result = 0x3a9e7d8, tables = 0x39b8710, fields = CLASS, send_row = true, select_options = 2147748608ULL, info = 0x1b7eb38 "Impossible HAVING noticed after reading const tables", having = 0x3b561c0), line 11292 in "sql_select.cc"
[25] JOIN::exec(this = 0x3b35fb0), line 2570 in "sql_select.cc"
[26] mysql_select(thd = 0x39ac580, rref_pointer_array = 0x39ae5c0, tables = 0x39b8710, wild_num = 0, fields = CLASS, conds = 0x3a58dc8, og_num = 0, order = (nil), group = (nil), having = 0x3a9e5a0, proc_param = (nil), select_options = 2147748608ULL, result = 0x3a9e7d8, unit = 0x39addb8, select_lex = 0x39ae3d8), line 3257 in "sql_select.cc"
[27] handle_select(thd = 0x39ac580, lex = 0x39add10, result = 0x3a9e7d8, setup_tables_done_option = 0), line 301 in "sql_select.cc"
[28] execute_sqlcom_select(thd = 0x39ac580, all_tables = 0x39b8710), line 4779 in "sql_parse.cc"
[29] mysql_execute_command(thd = 0x39ac580), line 2260 in "sql_parse.cc"
[30] mysql_parse(thd = 0x39ac580, inBuf = 0x39b75d0 "SELECT    COUNT(  table1 . `col_varchar_nokey` ) AS field1, COUNT(  table1 . `pk` ) AS field2 FROM ( B AS table1 INNER JOIN C AS table2 ON (table2 . `pk` = table1 . `col_int_key`  ) ) WHERE (  ( table2 . `col_varchar_nokey` , table1 . `col_varchar_key` )  IN ( SELECT 'x' , 'd' UNION  SELECT 'n' , 'k' ) ) AND table1 . `col_varchar_key`  IN ('z', 'b', 'w')  HAVING  ( 'f', 'l' )  IN ( SELECT   SUBQUERY2_t1 . `col_varchar_nokey` AS SUBQUERY2_field1 , SUBQUERY2_t1 . `col_varchar_key` AS SUBQUERY2_field2 FROM ( C" ..., length = 780U, parser_state = 0xfffffd7fff03cab8), line 5808 in "sql_parse.cc"
[31] dispatch_command(command = COM_QUERY, thd = 0x39ac580, packet = 0x39b35a1 "  SELECT    COUNT(  table1 . `col_varchar_nokey` ) AS field1, COUNT(  table1 . `pk` ) AS field2 FROM ( B AS table1 INNER JOIN C AS table2 ON (table2 . `pk` = table1 . `col_int_key`  ) ) WHERE (  ( table2 . `col_varchar_nokey` , table1 . `col_varchar_key` )  IN ( SELECT 'x' , 'd' UNION  SELECT 'n' , 'k' ) ) AND table1 . `col_varchar_key`  IN ('z', 'b', 'w')  HAVING  ( 'f', 'l' )  IN ( SELECT   SUBQUERY2_t1 . `col_varchar_nokey` AS SUBQUERY2_field1 , SUBQUERY2_t1 . `col_varchar_key` AS SUBQUERY2_field2 FROM (" ..., packet_length = 783U), line 1085 in "sql_parse.cc"
[32] do_command(thd = 0x39ac580), line 771 in "sql_parse.cc"
[33] do_handle_one_connection(thd_arg = 0x39ac580), line 1188 in "sql_connect.cc"
[34] handle_one_connection(arg = 0x39ac580), line 1127 in "sql_connect.cc"

The above was observed on Solaris using mysql-next-mr-opt-backporting revid  jorgen.loland@sun.com-20100614075728-r36zunxrjgnlwyl6

With a slightly older revision on Linux, a related assert was hit, which could be the same issue (roughly the same stacktrace):

mysqld: sql_select.cc:16542: enum_nested_loop_state sub_select_cache(JOIN*, JOIN_TAB*, bool): Assertion `cache != __null' failed.
100615 10:43:09 - mysqld got signal 6 ;

Crash occurs even with 
  optimizer_join_cache_level=1 
and
  optimizer_switch=='default,semijoin=off,materialization=off,mrr=off,index_condition_pushdown=off'

Not reproducible with mysql-next-mr or mysql-5.1-bugteam as of 2010-06-14.
Reproducible with latest mysql-6.0-codebase-bugfixing.

How to repeat:
Using the Random Query Generator ($CODE is path to MySQL binaries):

bzr branch lp:randgen
cd randgen

perl ./runall.pl \
--seed=1276587694 \
--threads=1 \
--grammar=conf/optimizer/optimizer_subquery.yy \
--engine=InnoDB \
--queries=1000000 \
--duration=1200 \
--basedir=$CODE \
--Reporter=Shutdown,Backtrace

Using a mysql client:

DROP TABLE IF EXISTS t1;
DROP TABLE IF EXISTS t2;
DROP TABLE IF EXISTS t3;

CREATE TABLE t1 (
  `pk` int(11) NOT NULL AUTO_INCREMENT,
  `col_int_nokey` int(11) DEFAULT NULL,
  `col_int_key` int(11) DEFAULT NULL,
  `col_varchar_key` varchar(1) DEFAULT NULL,
  `col_varchar_nokey` varchar(1) DEFAULT NULL,
  PRIMARY KEY (`pk`),
  KEY `col_varchar_key` (`col_varchar_key`,`col_int_key`)
) ENGINE=InnoDB;

CREATE TABLE t2 (
  `pk` int(11) NOT NULL AUTO_INCREMENT,
  `col_int_nokey` int(11) DEFAULT NULL,
  `col_int_key` int(11) DEFAULT NULL,
  `col_varchar_key` varchar(1) DEFAULT NULL,
  `col_varchar_nokey` varchar(1) DEFAULT NULL,
  PRIMARY KEY (`pk`),
  KEY `col_varchar_key` (`col_varchar_key`,`col_int_key`)
) ENGINE=InnoDB;
INSERT INTO t2 VALUES (20,4,2,'d','d');
CREATE TABLE t3 (
  `pk` int(11) NOT NULL AUTO_INCREMENT,
  `col_int_nokey` int(11) DEFAULT NULL,
  `col_int_key` int(11) DEFAULT NULL,
  `col_varchar_key` varchar(1) DEFAULT NULL,
  `col_varchar_nokey` varchar(1) DEFAULT NULL,
  PRIMARY KEY (`pk`),
  KEY `col_varchar_key` (`col_varchar_key`,`col_int_key`)
) ENGINE=InnoDB;

SELECT COUNT(  table1 . `col_varchar_nokey` ) AS field1, 
       COUNT(  table1 . `pk` ) AS field2 
FROM 
  ( 
     t1 AS table1 
       INNER JOIN t2 AS table2 ON (table2 . `pk` = table1 . `col_int_key`  ) 
  ) 
WHERE (  ( table2 . `col_varchar_nokey` , table1 . `col_varchar_key` )  
  IN ( 
    SELECT 'x' , 'd' 
    UNION SELECT 'n' , 'k' ) 
  ) 
HAVING  ( 'f', 'l' )  
  IN ( 
SELECT SUBQUERY2_t1 . `col_varchar_nokey` AS SUBQUERY2_field1 , 
       SUBQUERY2_t1 . `col_varchar_key` AS SUBQUERY2_field2 
FROM ( t2 AS SUBQUERY2_t1 
  STRAIGHT_JOIN ( t1 AS SUBQUERY2_t2 
     INNER JOIN t3 AS SUBQUERY2_t3 ON (SUBQUERY2_t3 . `pk` = SUBQUERY2_t2 . `col_int_nokey`  ) 
  ) 
  ON (SUBQUERY2_t3 . `col_varchar_key` = SUBQUERY2_t2 . `col_varchar_key`  ) )  
GROUP BY SUBQUERY2_field1, SUBQUERY2_field2
);

Apologies for not providing a less complex reproducible query at this point. Some automatic simplification attempts end up with a different crash (Bug#52336 or Bug#53933).

Replacing the "HAVING" keyword in the query above with "AND" does not result in a crash.
[15 Jun 2010 12:04] John Embretsen
Running EXPLAIN on the offending query results in the following:

mysql> EXPLAIN SELECT COUNT(  table1 . `col_varchar_nokey` ) AS field1,         COUNT(  table1 . `pk` ) AS field2  FROM    (       t1 AS table1         INNER JOIN t2 AS table2 ON (table2 . `pk` = table1 . `col_int_key`  )    )  WHERE (  ( table2 . `col_varchar_nokey` , table1 . `col_varchar_key` )     IN (      SELECT 'x' , 'd'      UNION SELECT 'n' , 'k' )    )  HAVING  ( 'f', 'l' )     IN (  SELECT SUBQUERY2_t1 . `col_varchar_nokey` AS SUBQUERY2_field1 ,         SUBQUERY2_t1 . `col_varchar_key` AS SUBQUERY2_field2  FROM ( t2 AS SUBQUERY2_t1    STRAIGHT_JOIN ( t1 AS SUBQUERY2_t2       INNER JOIN t3 AS SUBQUERY2_t3 ON (SUBQUERY2_t3 . `pk` = SUBQUERY2_t2 . `col_int_nokey`  )    )    ON (SUBQUERY2_t3 . `col_varchar_key` = SUBQUERY2_t2 . `col_varchar_key`  ) )   GROUP BY SUBQUERY2_field1, SUBQUERY2_field2 )\G;

*************************** 1. row ***************************
           id: 1
  select_type: PRIMARY
        table: NULL
         type: NULL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: NULL
        Extra: Impossible HAVING noticed after reading const tables
*************************** 2. row ***************************
           id: 4
  select_type: DEPENDENT SUBQUERY
        table: SUBQUERY2_t1
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1
        Extra: Using temporary; Using filesort
*************************** 3. row ***************************
           id: 4
  select_type: DEPENDENT SUBQUERY
        table: SUBQUERY2_t2
         type: ALL
possible_keys: col_varchar_key
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1
        Extra: Using where; Using join buffer (BNL, incremental buffers)
*************************** 4. row ***************************
           id: 4
  select_type: DEPENDENT SUBQUERY
        table: SUBQUERY2_t3
         type: eq_ref
possible_keys: PRIMARY,col_varchar_key
          key: PRIMARY
      key_len: 4
          ref: test.SUBQUERY2_t2.col_int_nokey
         rows: 1
        Extra: Using where
*************************** 5. row ***************************
           id: 2
  select_type: DEPENDENT SUBQUERY
        table: NULL
         type: NULL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: NULL
        Extra: No tables used
*************************** 6. row ***************************
           id: 3
  select_type: DEPENDENT UNION
        table: NULL
         type: NULL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: NULL
        Extra: No tables used
*************************** 7. row ***************************
           id: NULL
  select_type: UNION RESULT
        table: <union2,3>
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: NULL
        Extra: 
7 rows in set (0.00 sec)

ERROR: 
No query specified
[21 Jun 2010 6:39] Jørgen Løland
Simple mtr test script that crashes with the same stack trace:

--source include/have_innodb.inc

SET optimizer_switch='materialization=off'; # <- turning mat on removes crash

CREATE TABLE t1 (
  i int(11) NOT NULL
) ENGINE=InnoDB;

CREATE TABLE t2 (
  v varchar(1)
) ENGINE=InnoDB;

CREATE TABLE t3 (
  v varchar(1)
) ENGINE=InnoDB;

SELECT COUNT( i )
FROM t1
HAVING  ( 'c' )  
  IN ( 
    SELECT t2.v
    FROM (t2 JOIN t3)
    );
[21 Jun 2010 6:43] Jørgen Løland
Explain of the crashing query with materialization on and off:

materialization=on
------------------
id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible HAVING noticed after reading const tables
2	SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	1	
2	SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	1	Using join buffer (BNL, regular buffers)

materialization=off (crashes when not explain)
----------------------------------------------
id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible HAVING noticed after reading const tables
2	DEPENDENT SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	1	Using where
2	DEPENDENT SUBQUERY	t3	ALL	NULL	NULL	NULL	NULL	1	Using join buffer (BNL, regular bu
[21 Jun 2010 11:23] Jørgen Løland
bzrfind: Regression source: sergey.glukhov@sun.com-20100319092029-m2t1vgnovw5es2o6

which is a patch for BUG#48916
[21 Jun 2010 11:31] Jørgen Løland
Offending commit: http://lists.mysql.com/commits/103797
[22 Jun 2010 11:26] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/111754

3209 Jorgen Loland	2010-06-22
      BUG#54511 - Assertion failed: cache != 0L in file 
                  sql_select.cc::sub_select_cache on HAVING
      
      In JOIN::optimize(), it is checked if the HAVING clause is 
      impossible, in which case optimization can be stopped since
      the result is an empty set. However, the result of evaluating
      the HAVING clause (HAVING is FALSE) was not saved, and 
      return_zero_rows() therefore had to reevaluate it to see if 
      the query should return zero rows or one row with NULL values. 
      In this bug, reevaluating the HAVING clause caused a crash 
      because the HAVING clause contained a cached subselect which 
      had been freed.
      
      The fix is to store the outcome of the impossible HAVING clause
      by setting having_value to Item::COND_FALSE.
     @ mysql-test/include/subquery_mat.inc
        Test for BUG#45411
     @ mysql-test/r/having.result
        Updated EXPLAIN EXTENDED output to print "having 0" in cases
        where an impossible HAVING clause is detected.
     @ mysql-test/r/subquery_mat.result
        Test for BUG#45411
     @ mysql-test/r/subquery_mat_all.result
        Test for BUG#45411
     @ mysql-test/r/subquery_mat_none.result
        Test for BUG#45411
     @ sql/sql_select.cc
        When JOIN::optimize detects that the HAVING clause is  impossible, set having_value=Item::COND_FALSE and having=NULL.
[24 Jun 2010 11:44] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/112073

3198 Jorgen Loland	2010-06-24
      BUG#54511 - Assertion failed: cache != 0L in file 
                  sql_select.cc::sub_select_cache on HAVING
      
      Reported ASSERT has gone away after the fix for bug 51242 was 
      merged to mysql-next-mr-opt-bugfixing. This patch adds a test
      case for bug 54511 as well as code cleanup that sets 
      st_select_lex::having_value= JOIN::having_value (after 
      evaluating the HAVING clause) and JOIN::having= NULL (if the 
      HAVING clause is known to be false).
     @ mysql-test/include/subquery_mat.inc
        Test for BUG#45411
     @ mysql-test/r/subquery_mat.result
        Test for BUG#45411
     @ mysql-test/r/subquery_mat_all.result
        Test for BUG#45411
     @ mysql-test/r/subquery_mat_none.result
        Test for BUG#45411
     @ sql/sql_select.cc
        Code cleanup: Set st_select_lex::having_value= JOIN::having_value after evaluating JOIN::having by calling remove_eq_conds from JOIN::optimize. Set JOIN::having= NULL if the HAVING clause is known to be false.
[30 Jun 2010 6:28] Jørgen Løland
The problem was apparently due to a bad merge of Gluh's patch to next-mr. The bug has been fixed by a recent merge to the opt-backporting tree.

Two patches will be pushed:
 * A followup patch that removes the duplication of {having|cond}_value in select_lex and JOIN.
 * The test case for code coverage
[30 Jun 2010 6:35] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/112516

3203 Jorgen Loland	2010-06-30
      BUG#54511 - Assertion failed: cache != 0L in file 
                  sql_select.cc::sub_select_cache on HAVING
           
      Bug fixed by Gluh as part of BUG 48916. This revision contains
      a test case for code coverage since the repo in 48916 looks 
      different.
     @ mysql-test/include/subquery_mat.inc
        Test for BUG#54511
     @ mysql-test/r/subquery_mat.result
        Test for BUG#54511
     @ mysql-test/r/subquery_mat_all.result
        Test for BUG#54511
     @ mysql-test/r/subquery_mat_none.result
        Test for BUG#54511
[30 Jun 2010 8:33] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/112526

3275 Jorgen Loland	2010-06-30
      Bug#54511: Assertion failed: cache != 0L in file 
                 sql_select.cc::sub_select_cache on HAVING
      
      Code cleanup followup patch: make JOIN::having_value and 
      cond_value point to select_lex versions of the variables 
      with same name to avoid duplication of information. Also 
      add documentation to having_value and cond_value in 
      st_select_lex and JOIN and to optimize_cond().
     @ sql/sql_lex.h
        Add documentation to st_select_lex::having_value and cond_value
     @ sql/sql_select.cc
        Make JOIN::having_value and cond_value point to select_lex 
        variables with same name and document optimize_cond()
     @ sql/sql_select.h
        Make JOIN::having_value and cond_value point to select_lex 
        variables with same name and document how they work.
[1 Jul 2010 9:36] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/112657

3278 Jorgen Loland	2010-07-01
      Bug#54511: Assertion failed: cache != 0L in file 
                 sql_select.cc::sub_select_cache on HAVING
      
      Code cleanup followup patch: 
       * remove JOIN::having_value and JOIN::cond_value and use 
         select_lex variables with same name instead to avoid 
         duplication of information.
       * Add documentation about the relationship between 
         st_select_lex::{where|having} and JOIN::{conds|having} 
       * Add documentation about the relationship between 
         st_select_lex::{having|cond}_value and 
         JOIN::{conds|having}
     @ sql/sql_lex.h
        Add documentation to st_select_lex::having_value and cond_value
     @ sql/sql_select.cc
        Remove usage of JOIN::having_value and cond_value and use 
        select_lex variables with same name instead to avoid duplicate
        variables. Also documented optimize_cond()
     @ sql/sql_select.h
        Remove variables JOIN::having_value and cond_value and use 
        select_lex variables with same name instead to avoid duplicate
        variables. Also documented the relationship between 
        JOIN::{having|conds}, st_select_lex::{where|having} and 
        st_select_lex::{cond|having}_value.
[23 Jul 2010 12:30] Bugs System
Pushed into mysql-next-mr (revid:alik@sun.com-20100723121929-90e9zemk3jkr2ocy) (version source revid:vasil.dimov@oracle.com-20100531152341-x2d4hma644icamh1) (pib:18)
[4 Aug 2010 8:04] Bugs System
Pushed into mysql-trunk 5.6.1-m4 (revid:alik@ibmvm-20100804080001-bny5271e65xo34ig) (version source revid:vasil.dimov@oracle.com-20100531152341-x2d4hma644icamh1) (merge vers: 5.5.5-m3) (pib:18)
[4 Aug 2010 8:20] Bugs System
Pushed into mysql-trunk 5.6.1-m4 (revid:alik@ibmvm-20100804081533-c1d3rbipo9e8rt1s) (version source revid:vasil.dimov@oracle.com-20100531152341-x2d4hma644icamh1) (merge vers: 5.5.5-m3) (pib:18)
[13 Aug 2010 2:03] Paul DuBois
Changes to test suite. No changelog entry needed.
[16 Aug 2010 6:33] Bugs System
Pushed into mysql-next-mr (revid:alik@sun.com-20100816062819-bluwgdq8q4xysmlg) (version source revid:alik@sun.com-20100816062612-enatdwnv809iw3s9) (pib:20)
[13 Nov 2010 16:11] Bugs System
Pushed into mysql-trunk 5.6.99-m5 (revid:alexander.nozdrin@oracle.com-20101113155825-czmva9kg4n31anmu) (version source revid:vasil.dimov@oracle.com-20100629074804-359l9m9gniauxr94) (merge vers: 5.6.99-m4) (pib:21)