Bug #1990 Optimizer selects a wrong a non-optimal plan for queries containing IS NULL
Submitted: 1 Dec 2003 12:02 Modified: 12 Dec 2003 8:39
Reporter: Igor Babaev Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server Severity:S3 (Non-critical)
Version:4.1.1 OS:Any (all)
Assigned to: Igor Babaev CPU Architecture:Any

[1 Dec 2003 12:02] Igor Babaev
Description:
The optimizer selects a non-optimal execution plan for some queries    
containing IS NULL predicate for a non-nullable attribute. 
This case can be reproduced for version 4.0.17 as well.    
      

How to repeat:
Run the following mysql commands to create and the populate the case database:  
  
create table t1 (a int not null, b int not null, undex idx(a));  
insert into t1 values  
  (1,1), (2,2), (3,3), (4,4), (5,5), (6,6),  
  (7,7), (8,8), (9,9), (10,10), (11,11), (12,12);  
  
Then run:  
  explain select * from t1 where a between 2 and 3 or b is null;  
  
You'll get:  
+-------+------+---------------+------+---------+------+------+-------------+  
| table | type | possible_keys | key  | key_len | ref  | rows | Extra       |  
+-------+------+---------------+------+---------+------+------+-------------+  
| t1    | ALL  | idx           | NULL |    NULL | NULL |   12 | Using where |  
+-------+------+---------------+------+---------+------+------+-------------+  
    
This is not the best plan as b is a non-nullable column and the IS NULL  
predicate can be removed from the query.   
When we remove the predicate we have:   
mysql> explain select * from t1 where a between 2 and 3;  
+-------+-------+---------------+------+---------+------+------+-------------+  
| table | type  | possible_keys | key  | key_len | ref  | rows | Extra       |  
+-------+-------+---------------+------+---------+------+------+-------------+  
| t1    | range | idx           | idx  |       4 | NULL |    2 | Using where |  
+-------+-------+---------------+------+---------+------+------+-------------+  
   

Suggested fix:
This wrong choice of the optimizer happens due to an incostistent state of 
used_table_cache and const_item_cache (both of then are 0) in Item_func_isnull 
objects for IS NULL predicates containing a non-nullable attribute. 
Item_func_isnull::update_used_tables is to be fixed:  
virtual void update_used_tables() 
  { 
    if (!args[0]->maybe_null) 
    { 
      used_tables_cache= 0;			/* is always false */ 
      const_item_cache= 1;     /* line to be added for this fix */ 
      cached_value= (longlong) 0; 
    } 
    else 
    { 
      args[0]->update_used_tables(); 
      if (!(used_tables_cache=args[0]->used_tables())) 
      { 
	/* Remember if the value is always NULL or never NULL */ 
	cached_value= (longlong) args[0]->is_null(); 
      } 
    }
[12 Dec 2003 8:39] Igor Babaev
Fixed in 4.1.2