Commit f121994d authored by unknown's avatar unknown
Browse files

Fixed bug #21390: wrong estimate of rows after elimination of

const tables. This resulted in choosing extremely inefficient
execution plans in same cases when distribution of data in
joined were skewed (see the customer test case for the bug).



mysql-test/r/select.result:
  Added a test case for bug #21390: wrong estimate of rows
  after elimination of const tables.
  Includded a test case that checks the code added by the patch
  that handles outer joins with no matches after substitution of
  a const table in an efficient way.
mysql-test/t/select.test:
  Added a test case for bug #21390: wrong estimate of rows
  after elimination of const tables.
  Included a test case that checks the code added by the patch
  that handles outer joins with no matches after substitution of
  a const table in an efficient way.
sql/sql_select.cc:
  Fixed bug #21390: wrong estimate of rows after elimination of
  const tables. This resulted in choosing extremely inefficient
  execution plans in same cases when distribution of data in
  joined were skewed (see the customer test case for the bug).
  Also added the code to handle outer joins with no matches after
  substitution of a const table in an efficient way. 
  Corrected calculation of the null rejecting key conditions.
parent 84a65393
Loading
Loading
Loading
Loading
+40 −2
Original line number Diff line number Diff line
@@ -2328,9 +2328,9 @@ explain select * from t1 left join t2 on id1 = id2 left join t3 on id1 = id3
left join t4 on id3 = id4 where id2 = 1 or id4 = 1;
id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
1	SIMPLE	t3	system	NULL	NULL	NULL	NULL	0	const row not found
1	SIMPLE	t4	const	id4	NULL	NULL	NULL	1	
1	SIMPLE	t1	ALL	NULL	NULL	NULL	NULL	2	
1	SIMPLE	t2	ALL	NULL	NULL	NULL	NULL	1	
1	SIMPLE	t4	ALL	id4	NULL	NULL	NULL	1	Using where
1	SIMPLE	t2	ALL	NULL	NULL	NULL	NULL	1	Using where
select * from t1 left join t2 on id1 = id2 left join t3 on id1 = id3
left join t4 on id3 = id4 where id2 = 1 or id4 = 1;
id1	id2	id3	id4	id44
@@ -3479,3 +3479,41 @@ Warning 1466 Leading spaces are removed from name ' a '
execute stmt;
a 
1
CREATE TABLE t1 (a int NOT NULL PRIMARY KEY, b int NOT NULL);
INSERT INTO t1 VALUES (1,1), (2,2), (3,3), (4,4);
CREATE TABLE t2 (c int NOT NULL, INDEX idx(c));
INSERT INTO t2 VALUES
(1), (1), (1), (1), (1), (1), (1), (1),
(2), (2), (2), (2),
(3), (3),
(4);
EXPLAIN SELECT b FROM t1, t2 WHERE b=c AND a=1;
id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
1	SIMPLE	t1	const	PRIMARY	PRIMARY	4	const	1	
1	SIMPLE	t2	ref	idx	idx	4	const	7	Using index
EXPLAIN SELECT b FROM t1, t2 WHERE b=c AND a=4;
id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
1	SIMPLE	t1	const	PRIMARY	PRIMARY	4	const	1	
1	SIMPLE	t2	ref	idx	idx	4	const	1	Using index
DROP TABLE t1, t2;
CREATE TABLE t1 (id int NOT NULL PRIMARY KEY, a int);
INSERT INTO t1 VALUES (1,2), (2,NULL), (3,2);
CREATE TABLE t2 (b int, c INT, INDEX idx1(b));
INSERT INTO t2 VALUES (2,1), (3,2);
CREATE TABLE t3 (d int,  e int, INDEX idx1(d));
INSERT INTO t3 VALUES (2,10), (2,20), (1,30), (2,40), (2,50);
EXPLAIN
SELECT * FROM t1 LEFT JOIN t2 ON t2.b=t1.a INNER JOIN t3 ON t3.d=t1.id
WHERE t1.id=2;
id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
1	SIMPLE	t1	const	PRIMARY	PRIMARY	4	const	1	
1	SIMPLE	t2	const	idx1	NULL	NULL	NULL	1	
1	SIMPLE	t3	ref	idx1	idx1	5	const	3	Using where
SELECT * FROM t1 LEFT JOIN t2 ON t2.b=t1.a INNER JOIN t3 ON t3.d=t1.id
WHERE t1.id=2;
id	a	b	c	d	e
2	NULL	NULL	NULL	2	10
2	NULL	NULL	NULL	2	20
2	NULL	NULL	NULL	2	40
2	NULL	NULL	NULL	2	50
DROP TABLE t1,t2,t3;
+41 −0
Original line number Diff line number Diff line
@@ -2957,3 +2957,44 @@ SELECT 0.9888889889 * 1.011111411911;
#
prepare stmt from 'select 1 as " a "';
execute stmt;

#
# Bug #21390: wrong estimate of rows after elimination of const tables
#

CREATE TABLE t1 (a int NOT NULL PRIMARY KEY, b int NOT NULL);
INSERT INTO t1 VALUES (1,1), (2,2), (3,3), (4,4);

CREATE TABLE t2 (c int NOT NULL, INDEX idx(c));
INSERT INTO t2 VALUES
  (1), (1), (1), (1), (1), (1), (1), (1),
  (2), (2), (2), (2),
  (3), (3),
  (4);

EXPLAIN SELECT b FROM t1, t2 WHERE b=c AND a=1;
EXPLAIN SELECT b FROM t1, t2 WHERE b=c AND a=4;

DROP TABLE t1, t2;

#
# No matches for a join after substitution of a const table
#

CREATE TABLE t1 (id int NOT NULL PRIMARY KEY, a int);
INSERT INTO t1 VALUES (1,2), (2,NULL), (3,2);

CREATE TABLE t2 (b int, c INT, INDEX idx1(b));
INSERT INTO t2 VALUES (2,1), (3,2);

CREATE TABLE t3 (d int,  e int, INDEX idx1(d));
INSERT INTO t3 VALUES (2,10), (2,20), (1,30), (2,40), (2,50);

EXPLAIN
SELECT * FROM t1 LEFT JOIN t2 ON t2.b=t1.a INNER JOIN t3 ON t3.d=t1.id
  WHERE t1.id=2;
SELECT * FROM t1 LEFT JOIN t2 ON t2.b=t1.a INNER JOIN t3 ON t3.d=t1.id
  WHERE t1.id=2;


DROP TABLE t1,t2,t3;
+54 −24
Original line number Diff line number Diff line
@@ -2205,6 +2205,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds,
  int ref_changed;
  do
  {
  more_const_tables_found:
    ref_changed = 0;
    found_ref=0;

@@ -2216,6 +2217,30 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds,
    for (JOIN_TAB **pos=stat_vector+const_count ; (s= *pos) ; pos++)
    {
      table=s->table;

      /* 
        If equi-join condition by a key is null rejecting and after a
        substitution of a const table the key value happens to be null
        then we can state that there are no matches for this equi-join.
      */  
      if ((keyuse= s->keyuse) && *s->on_expr_ref)
      {
        while (keyuse->table == table)
        {
          if (!(keyuse->val->used_tables() & ~join->const_table_map) &&
              keyuse->val->is_null() && keyuse->null_rejecting)
          {
            s->type= JT_CONST;
            mark_as_null_row(table);
            found_const_table_map|= table->map;
	    join->const_table_map|= table->map;
	    set_position(join,const_count++,s,(KEYUSE*) 0);
            goto more_const_tables_found;
           }
	  keyuse++;
        }
      }

      if (s->dependent)				// If dependent on some table
      {
	// All dep. must be constants
@@ -2266,10 +2291,11 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds,
	  } while (keyuse->table == table && keyuse->key == key);

	  if (eq_part.is_prefix(table->key_info[key].key_parts) &&
	      ((table->key_info[key].flags & (HA_NOSAME | HA_END_SPACE_KEY)) ==
	       HA_NOSAME) &&
              !table->fulltext_searched && 
              !table->pos_in_table_list->embedding)
	  {
            if ((table->key_info[key].flags & (HA_NOSAME | HA_END_SPACE_KEY))
                 == HA_NOSAME)
            {
	      if (const_ref == eq_part)
	      {					// Found everything for ref.
@@ -2294,6 +2320,9 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds,
	      else
	        found_ref|= refs;      // Table is const if all refs are const
	    }
            else if (const_ref == eq_part)
              s->const_keys.set_bit(key);
          }
	}
      }
    }
@@ -2696,7 +2725,8 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond,
    We use null_rejecting in add_not_null_conds() to add
    'othertbl.field IS NOT NULL' to tab->select_cond.
  */
  (*key_fields)->null_rejecting= ((cond->functype() == Item_func::EQ_FUNC) &&
  (*key_fields)->null_rejecting= ((cond->functype() == Item_func::EQ_FUNC ||
                                   cond->functype() == Item_func::MULT_EQUAL_FUNC) &&
                                  ((*value)->type() == Item::FIELD_ITEM) &&
                                  ((Item_field*)*value)->field->maybe_null());
  (*key_fields)++;
@@ -3461,7 +3491,7 @@ best_access_path(JOIN *join,
                                                 keyuse->used_tables));
            if (tmp < best_prev_record_reads)
            {
              best_part_found_ref= keyuse->used_tables;
              best_part_found_ref= keyuse->used_tables & ~join->const_table_map;
              best_prev_record_reads= tmp;
            }
            if (rec > keyuse->ref_table_rows)