Commit 2dcf451b authored by ingo@mysql.com's avatar ingo@mysql.com

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.
parent b0e6db14
...@@ -231,18 +231,19 @@ explain select * from t1 where a='aaad'; ...@@ -231,18 +231,19 @@ explain select * from t1 where a='aaad';
id select_type table type possible_keys key key_len ref rows Extra 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 1 Using where
insert into t1 select * from t1; insert into t1 select * from t1;
flush tables;
explain select * from t1 where a='aaaa'; explain select * from t1 where a='aaaa';
id select_type table type possible_keys key key_len ref rows Extra 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'; explain select * from t1 where a='aaab';
id select_type table type possible_keys key key_len ref rows Extra 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'; explain select * from t1 where a='aaac';
id select_type table type possible_keys key key_len ref rows Extra 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'; explain select * from t1 where a='aaad';
id select_type table type possible_keys key key_len ref rows Extra 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; flush tables;
explain select * from t1 where a='aaaa'; explain select * from t1 where a='aaaa';
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
...@@ -261,16 +262,16 @@ delete from t1; ...@@ -261,16 +262,16 @@ delete from t1;
insert into t1 select * from t2; insert into t1 select * from t2;
explain select * from t1 where a='aaaa'; explain select * from t1 where a='aaaa';
id select_type table type possible_keys key key_len ref rows Extra 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'; explain select * from t1 where a='aaab';
id select_type table type possible_keys key key_len ref rows Extra 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'; explain select * from t1 where a='aaac';
id select_type table type possible_keys key key_len ref rows Extra 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'; explain select * from t1 where a='aaad';
id select_type table type possible_keys key key_len ref rows Extra 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; drop table t1, t2;
create table t1 ( create table t1 (
id int unsigned not null primary key auto_increment, id int unsigned not null primary key auto_increment,
...@@ -345,15 +346,15 @@ insert into t3 select name, name from t1; ...@@ -345,15 +346,15 @@ insert into t3 select name, name from t1;
show index from t3; show index from t3;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment 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 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; show index from t3;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment 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 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; 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 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 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; drop table t1, t2, t3;
create temporary table t1 ( a int, index (a) ) engine=memory; create temporary table t1 ( a int, index (a) ) engine=memory;
insert into t1 values (1),(2),(3),(4),(5); insert into t1 values (1),(2),(3),(4),(5);
......
...@@ -169,6 +169,8 @@ explain select * from t1 where a='aaac'; ...@@ -169,6 +169,8 @@ explain select * from t1 where a='aaac';
explain select * from t1 where a='aaad'; explain select * from t1 where a='aaad';
insert into t1 select * from t1; 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='aaaa';
explain select * from t1 where a='aaab'; explain select * from t1 where a='aaab';
explain select * from t1 where a='aaac'; explain select * from t1 where a='aaac';
......
...@@ -60,7 +60,15 @@ int ha_heap::open(const char *name, int mode, uint test_if_locked) ...@@ -60,7 +60,15 @@ int ha_heap::open(const char *name, int mode, uint test_if_locked)
{ {
/* Initialize variables for the opened table */ /* Initialize variables for the opened table */
set_keys_for_scanning(); 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); return (file ? 0 : 1);
} }
...@@ -112,6 +120,8 @@ void ha_heap::update_key_stats() ...@@ -112,6 +120,8 @@ void ha_heap::update_key_stats()
} }
} }
records_changed= 0; 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) int ha_heap::write_row(byte * buf)
...@@ -125,7 +135,7 @@ int ha_heap::write_row(byte * buf) ...@@ -125,7 +135,7 @@ int ha_heap::write_row(byte * buf)
res= heap_write(file,buf); res= heap_write(file,buf);
if (!res && ++records_changed*HEAP_STATS_UPDATE_THRESHOLD > if (!res && ++records_changed*HEAP_STATS_UPDATE_THRESHOLD >
file->s->records) file->s->records)
update_key_stats(); key_stats_ok= FALSE;
return res; return res;
} }
...@@ -138,7 +148,7 @@ int ha_heap::update_row(const byte * old_data, byte * new_data) ...@@ -138,7 +148,7 @@ int ha_heap::update_row(const byte * old_data, byte * new_data)
res= heap_update(file,old_data,new_data); res= heap_update(file,old_data,new_data);
if (!res && ++records_changed*HEAP_STATS_UPDATE_THRESHOLD > if (!res && ++records_changed*HEAP_STATS_UPDATE_THRESHOLD >
file->s->records) file->s->records)
update_key_stats(); key_stats_ok= FALSE;
return res; return res;
} }
...@@ -149,7 +159,7 @@ int ha_heap::delete_row(const byte * buf) ...@@ -149,7 +159,7 @@ int ha_heap::delete_row(const byte * buf)
res= heap_delete(file,buf); res= heap_delete(file,buf);
if (!res && table->tmp_table == NO_TMP_TABLE && if (!res && table->tmp_table == NO_TMP_TABLE &&
++records_changed*HEAP_STATS_UPDATE_THRESHOLD > file->s->records) ++records_changed*HEAP_STATS_UPDATE_THRESHOLD > file->s->records)
update_key_stats(); key_stats_ok= FALSE;
return res; return res;
} }
...@@ -262,6 +272,13 @@ void ha_heap::info(uint flag) ...@@ -262,6 +272,13 @@ void ha_heap::info(uint flag)
delete_length= info.deleted * info.reclength; delete_length= info.deleted * info.reclength;
if (flag & HA_STATUS_AUTO) if (flag & HA_STATUS_AUTO)
auto_increment_value= info.auto_increment; 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) int ha_heap::extra(enum ha_extra_function operation)
...@@ -273,7 +290,7 @@ int ha_heap::delete_all_rows() ...@@ -273,7 +290,7 @@ int ha_heap::delete_all_rows()
{ {
heap_clear(file); heap_clear(file);
if (table->tmp_table == NO_TMP_TABLE) if (table->tmp_table == NO_TMP_TABLE)
update_key_stats(); key_stats_ok= FALSE;
return 0; return 0;
} }
...@@ -433,7 +450,11 @@ ha_rows ha_heap::records_in_range(uint inx, key_range *min_key, ...@@ -433,7 +450,11 @@ ha_rows ha_heap::records_in_range(uint inx, key_range *min_key,
max_key->flag != HA_READ_AFTER_KEY) max_key->flag != HA_READ_AFTER_KEY)
return HA_POS_ERROR; // Can only use exact keys return HA_POS_ERROR; // Can only use exact keys
else 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]; return key->rec_per_key[key->key_parts-1];
}
} }
......
...@@ -29,8 +29,10 @@ class ha_heap: public handler ...@@ -29,8 +29,10 @@ class ha_heap: public handler
key_map btree_keys; key_map btree_keys;
/* number of records changed since last statistics update */ /* number of records changed since last statistics update */
uint records_changed; uint records_changed;
bool key_stats_ok;
public: 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() {} ~ha_heap() {}
const char *table_type() const { return "HEAP"; } const char *table_type() const { return "HEAP"; }
const char *index_type(uint inx) const char *index_type(uint inx)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment