Commit a7e66efc authored by unknown's avatar unknown
Browse files

Bug#10178 - failure to find a row in heap table by concurrent UPDATEs

Moved the key statistics update to info().
The table is not locked in open(). This made wrong stats possible.

No test case for the test suite.
This happens only with heavy concurrency.
A test script is added to the bug report.


mysql-test/r/heap_hash.result:
  Bug#10178 - failure to find a row in heap table by concurrent UPDATEs
  Updated test results to reflect the new  statistics behaviour.
mysql-test/t/heap_hash.test:
  Bug#10178 - failure to find a row in heap table by concurrent UPDATEs
  Added a FLUSH TABLES to avoid statistics differences between normal 
  and ps-protocol tests.
sql/ha_heap.cc:
  Bug#10178 - failure to find a row in heap table by concurrent UPDATEs
  Moved the key statistics update to info().
  The table is not locked in open(). This made wrong stats possible.
sql/ha_heap.h:
  Bug#10178 - failure to find a row in heap table by concurrent UPDATEs
  Added an element to track the validity of the key statistics.
parent 5aa793f7
Loading
Loading
Loading
Loading
+12 −11
Original line number Diff line number Diff line
@@ -231,18 +231,19 @@ explain select * from t1 where a='aaad';
id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
1	SIMPLE	t1	ref	a	a	8	const	1	Using where
insert into t1 select * from t1;
flush tables;
explain select * from t1 where a='aaaa';
id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
1	SIMPLE	t1	ref	a	a	8	const	1	Using where
1	SIMPLE	t1	ref	a	a	8	const	2	Using where
explain select * from t1 where a='aaab';
id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
1	SIMPLE	t1	ref	a	a	8	const	1	Using where
1	SIMPLE	t1	ref	a	a	8	const	2	Using where
explain select * from t1 where a='aaac';
id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
1	SIMPLE	t1	ref	a	a	8	const	1	Using where
1	SIMPLE	t1	ref	a	a	8	const	2	Using where
explain select * from t1 where a='aaad';
id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
1	SIMPLE	t1	ref	a	a	8	const	1	Using where
1	SIMPLE	t1	ref	a	a	8	const	2	Using where
flush tables;
explain select * from t1 where a='aaaa';
id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
@@ -261,16 +262,16 @@ delete from t1;
insert into t1 select * from t2;
explain select * from t1 where a='aaaa';
id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
1	SIMPLE	t1	ref	a	a	8	const	1	Using where
1	SIMPLE	t1	ref	a	a	8	const	2	Using where
explain select * from t1 where a='aaab';
id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
1	SIMPLE	t1	ref	a	a	8	const	1	Using where
1	SIMPLE	t1	ref	a	a	8	const	2	Using where
explain select * from t1 where a='aaac';
id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
1	SIMPLE	t1	ref	a	a	8	const	1	Using where
1	SIMPLE	t1	ref	a	a	8	const	2	Using where
explain select * from t1 where a='aaad';
id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
1	SIMPLE	t1	ref	a	a	8	const	1	Using where
1	SIMPLE	t1	ref	a	a	8	const	2	Using where
drop table t1, t2;
create table t1 (
id int unsigned not null primary key auto_increment, 
@@ -345,15 +346,15 @@ insert into t3 select name, name from t1;
show index from t3;
Table	Non_unique	Key_name	Seq_in_index	Column_name	Collation	Cardinality	Sub_part	Packed	Null	Index_type	Comment
t3	1	a	1	a	NULL	NULL	NULL	NULL		HASH	
t3	1	a	2	b	NULL	15	NULL	NULL		HASH	
t3	1	a	2	b	NULL	13	NULL	NULL		HASH	
show index from t3;
Table	Non_unique	Key_name	Seq_in_index	Column_name	Collation	Cardinality	Sub_part	Packed	Null	Index_type	Comment
t3	1	a	1	a	NULL	NULL	NULL	NULL		HASH	
t3	1	a	2	b	NULL	15	NULL	NULL		HASH	
t3	1	a	2	b	NULL	13	NULL	NULL		HASH	
explain select * from t1 ignore key(btree_idx), t3 where t1.name='matt' and t3.a = concat('',t1.name) and t3.b=t1.name;
id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
1	SIMPLE	t1	ref	heap_idx	heap_idx	20	const	7	Using where
1	SIMPLE	t3	ref	a	a	40	func,const	6	Using where
1	SIMPLE	t3	ref	a	a	40	func,const	7	Using where
drop table t1, t2, t3;
create temporary table t1 ( a int, index (a) ) engine=memory;
insert into t1 values (1),(2),(3),(4),(5);
+2 −0
Original line number Diff line number Diff line
@@ -169,6 +169,8 @@ explain select * from t1 where a='aaac';
explain select * from t1 where a='aaad';
insert into t1 select * from t1;

# avoid statistics differences between normal and ps-protocol tests
flush tables;
explain select * from t1 where a='aaaa';
explain select * from t1 where a='aaab';
explain select * from t1 where a='aaac';
+26 −5
Original line number Diff line number Diff line
@@ -60,7 +60,15 @@ int ha_heap::open(const char *name, int mode, uint test_if_locked)
  {
    /* Initialize variables for the opened table */
    set_keys_for_scanning();
    update_key_stats();
    /*
      We cannot run update_key_stats() here because we do not have a
      lock on the table. The 'records' count might just be changed
      temporarily at this moment and we might get wrong statistics (Bug
      #10178). Instead we request for update. This will be done in
      ha_heap::info(), which is always called before key statistics are
      used.
    */
    key_stats_ok= FALSE;
  }
  return (file ? 0 : 1);
}
@@ -112,6 +120,8 @@ void ha_heap::update_key_stats()
    }
  }
  records_changed= 0;
  /* At the end of update_key_stats() we can proudly claim they are OK. */
  key_stats_ok= TRUE;
}

int ha_heap::write_row(byte * buf)
@@ -125,7 +135,7 @@ int ha_heap::write_row(byte * buf)
  res= heap_write(file,buf);
  if (!res && ++records_changed*HEAP_STATS_UPDATE_THRESHOLD > 
              file->s->records)
    update_key_stats();
    key_stats_ok= FALSE;
  return res;
}

@@ -138,7 +148,7 @@ int ha_heap::update_row(const byte * old_data, byte * new_data)
  res= heap_update(file,old_data,new_data);
  if (!res && ++records_changed*HEAP_STATS_UPDATE_THRESHOLD > 
              file->s->records)
    update_key_stats();
    key_stats_ok= FALSE;
  return res;
}

@@ -149,7 +159,7 @@ int ha_heap::delete_row(const byte * buf)
  res= heap_delete(file,buf);
  if (!res && table->tmp_table == NO_TMP_TABLE && 
      ++records_changed*HEAP_STATS_UPDATE_THRESHOLD > file->s->records)
    update_key_stats();
    key_stats_ok= FALSE;
  return res;
}

@@ -262,6 +272,13 @@ void ha_heap::info(uint flag)
  delete_length= info.deleted * info.reclength;
  if (flag & HA_STATUS_AUTO)
    auto_increment_value= info.auto_increment;
  /*
    If info() is called for the first time after open(), we will still
    have to update the key statistics. Hoping that a table lock is now
    in place.
  */
  if (! key_stats_ok)
    update_key_stats();
}

int ha_heap::extra(enum ha_extra_function operation)
@@ -273,7 +290,7 @@ int ha_heap::delete_all_rows()
{
  heap_clear(file);
  if (table->tmp_table == NO_TMP_TABLE)
    update_key_stats();
    key_stats_ok= FALSE;
  return 0;
}

@@ -433,8 +450,12 @@ ha_rows ha_heap::records_in_range(uint inx, key_range *min_key,
      max_key->flag != HA_READ_AFTER_KEY)
    return HA_POS_ERROR;			// Can only use exact keys
  else
  {
    /* Assert that info() did run. We need current statistics here. */
    DBUG_ASSERT(key_stats_ok);
    return key->rec_per_key[key->key_parts-1];
  }
}


int ha_heap::create(const char *name, TABLE *table_arg,
+3 −1
Original line number Diff line number Diff line
@@ -29,8 +29,10 @@ class ha_heap: public handler
  key_map btree_keys;
  /* number of records changed since last statistics update */
  uint    records_changed;
  bool    key_stats_ok;
public:
  ha_heap(TABLE *table): handler(table), file(0), records_changed(0) {}
  ha_heap(TABLE *table): handler(table), file(0), records_changed(0),
      key_stats_ok(0) {}
  ~ha_heap() {}
  const char *table_type() const { return "HEAP"; }
  const char *index_type(uint inx)