[MDEV-32201] join_init_read_record() wrongly issues HA_ERR_OUT_OF_MEM Created: 2023-09-19  Updated: 2023-10-23

Status: Confirmed
Project: MariaDB Server
Component/s: Optimizer
Affects Version/s: 10.5
Fix Version/s: 10.5

Type: Bug Priority: Major
Reporter: Vladislav Lesin Assignee: Sergei Petrunia
Resolution: Unresolved Votes: 0
Labels: None

Issue Links:
Problem/Incident
is caused by MDEV-13751 Interrupted SELECT fails with 1030: '... Closed
Relates
relates to MDEV-25163 Rowid filter does not process storage... Closed

 Description   
  1. The following code reports HA_ERR_OUT_OF_MEM even if tab->select->quick->reset() returns meaningful error code.

    int join_init_read_record(JOIN_TAB *tab)                                        
    {                                                                               
    ...                                                                             
      if (tab->select && tab->select->quick && tab->select->quick->reset())         
      {                                                                             
        /* Ensures error status is propagated back to client */                     
        report_error(tab->table,                                                    
                     tab->join->thd->killed ? HA_ERR_QUERY_INTERRUPTED : HA_ERR_OUT_OF_MEM);
        return 1;                                                                   
      }                                                                             
    ...                                                                             
    }
    

The case I analysed is the following.
In the following stack trace row_merge_is_index_usable() returns false.

#0  row_merge_is_index_usable (trx=0x3d872403d218, index=0x68c324040588) at /data/MDEV-24035/10.5-MDEV-25163-OOM-debug/storage/innobase/row/row0merge.cc:4299
#1  0x00005653842cc4ad in ha_innobase::change_active_index (this=0x4d38000b8c60, keynr=2) at /data/MDEV-24035/10.5-MDEV-25163-OOM-debug/storage/innobase/handler/ha_innodb.cc:9036
#2  0x000056538418f555 in handler::ha_index_init (sorted=true, idx=2, this=0x4d38000b8c60) at /data/MDEV-24035/10.5-MDEV-25163-OOM-debug/sql/opt_range.cc:12644
#3  QUICK_RANGE_SELECT::reset (this=0x2bdb3c08a640) at /data/MDEV-24035/10.5-MDEV-25163-OOM-debug/sql/opt_range.cc:12614
#4  0x0000565383eb6585 in join_init_read_record (tab=0x7d0c680128e0) at /data/MDEV-24035/10.5-MDEV-25163-OOM-debug/sql/sql_select.cc:22140
#5  join_init_read_record (tab=0x7d0c680128e0) at /data/MDEV-24035/10.5-MDEV-25163-OOM-debug/sql/sql_select.cc:22123
#6  0x0000565383ea0d1a in sub_select (join=0x7d0c680109c8, join_tab=0x7d0c680128e0, end_of_records=<optimized out>)
    at /data/MDEV-24035/10.5-MDEV-25163-OOM-debug/sql/sql_select.cc:21189

Then, ha_innobase::change_active_index() converts it to HA_ERR_TABLE_DEF_CHANGED:

int                                                                             
ha_innobase::change_active_index(...)                                           
{                                                                               
...                                                                             
        m_prebuilt->index_usable = row_merge_is_index_usable(                   
                m_prebuilt->trx, m_prebuilt->index);                            
                                                                                
        if (!m_prebuilt->index_usable) {                                        
                if (m_prebuilt->index->is_corrupted()) {                        
...                                                                             
                } else {                                                        
                        push_warning_printf(                                    
                                m_user_thd, Sql_condition::WARN_LEVEL_WARN,     
                                HA_ERR_TABLE_DEF_CHANGED,                       
                                "InnoDB: insufficient history for index %u",    
                                keynr);                                         
                }                                                               
                                                                                
                /* The caller seems to ignore this.  Thus, we must check        
                this again in row_search_for_mysql(). */                        
                DBUG_RETURN(convert_error_code_to_mysql(DB_MISSING_HISTORY,     
                                0, NULL));                                      
        }                                                                       
...                                                                             
}                                                                               
                                                                                
static int                                                                      
convert_error_code_to_mysql(...)                                                
{                                                                               
        switch (error) {                                                        
        ...                                                                     
        case DB_MISSING_HISTORY:                                                
                return(HA_ERR_TABLE_DEF_CHANGED);                               
        ...                                                                     
        }                                                                       
...                                                                             
}

QUICK_RANGE_SELECT::reset() returns it to join_init_read_record(). But join_init_read_record() interprets the returned value as boolean and emits HA_ERR_OUT_OF_MEM for any errors except HA_ERR_QUERY_INTERRUPTED.

Possible fix: if returned value of tab->select->quick->reset() is not null, return it instead of HA_ERR_OUT_OF_MEM.



 Comments   
Comment by Sergei Petrunia [ 2023-09-19 ]

The code with HA_ERR_OUT_OF_MEM looks odd.
It was added by this patch: https://github.com/MariaDB/server/commit/55c5448ab7030f458f6e06f380c9f605c1d8d3ba#diff-16fd8e507582e428d933bc7ee3984394eab544410785220b536ab964d4e9e084R19474

commit 55c5448ab7030f458f6e06f380c9f605c1d8d3ba
Author: Sergei Golubchik <serg@mariadb.org>
Date:   Fri Sep 15 16:43:06 2017 +0200
 
    MDEV-13751 Interrupted SELECT fails with 1030: 'Got error 1 "Operation not permitted" from storage engine MyISAM'
    
    quick select returns 1, not proper HA_ERR_xxx error code,
    so don't send it to handler::print_error().

which uses HA_ERR_OUT_OF_MEM as lets-just-invent-some-error.

Comment by Sergei Petrunia [ 2023-09-19 ]

This was discovered when working on MDEV-25163

Generated at Thu Feb 08 10:29:36 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.