• Dmitry Shulga's avatar
    MDEV-19631: Assertion `0' failed in st_select_lex_unit::optimize or different... · bdba1d46
    Dmitry Shulga authored
    MDEV-19631: Assertion `0' failed in st_select_lex_unit::optimize or different plan upon 2nd execution of PS with EXPLAIN
    
    Second execution of a prepared statement for a query containing a constant
    subquery with union that can be optimized away, could result in server abnormal
    termination for debug build or incorrect result set output for release build.
    
    For example, the following test case crashes a server built with debug on second
    run of the statement EXECUTE stmt
      CREATE TABLE t1 (a INT);
      PREPARE stmt FROM 'EXPLAIN SELECT * FROM t1 HAVING 6 IN ( SELECT 6 UNION SELECT 5 )';
      EXECUTE stmt;
      EXECUTE stmt;
    
    The reason for incorrect result set output or abnormal server termination
    is careless working with the data member fake_select_lex->options inside
    the function mysql_explain_union(). Once the flag SELECT_DESCRIBE is set in
    the data member fake_select_lex->option before calling the methods
      SELECT_LEX_UNIT::prepare/SELECT_LEX_UNIT::execute
    the original value of the option is no longer restored.
    As a consequence, next time the prepared statement is re-executed we have
    the fake_select_lex with the flag SELECT_DESCRIBE set in the data member
    fake_select_lex->option, that is incorrect. In result, the method
      Item_subselect::assigned()
    is not invoked during evaluation of a constant condition (constant subquery
    with union) that being performed on OPTIMIZE phase of query handling.
    
    This leads to the fact that records in the temporary table are not deleted
    before calling
      table->file->ha_enable_indexes(HA_KEY_SWITCH_ALL)
    in the method st_select_lex_unit::optimize().
    In result table->file->ha_enable_indexes(HA_KEY_SWITCH_ALL) returns error
    and DBUG_ASSERT(0) is fired.
    
    Stack trace to the line where the error generated on re-enabling indexes
    for next subselect iteration is below:
    st_select_lex_unit::optimize (at sql_union.cc:954)
      handler::ha_enable_indexes (at handler.cc:4338)
        ha_heap::enable_indexes (at ha_heap.cc:519)
          heap_enable_indexes (at hp_clear.c:164)
    
    The code snippet to clarify raising the error is also listed:
    int heap_enable_indexes(HP_INFO *info)
    {
      int error= 0;
      HP_SHARE *share= info->s;
    
      if (share->data_length || share->index_length)
        error= HA_ERR_CRASHED; <<== set error the value HA_ERR_CRASHED
                                    since share->data_length != 0
    
    To fix this issue the original value of unit->fake_select_lex->options
    has to be saved before setting the flag SELECT_DESCRIBE and restored
    on return from invocation of SELECT_LEX_UNIT::prepare/SELECT_LEX_UNIT::execute
    bdba1d46
ps.test 127 KB