From eccd9969c752c06a89d93fd295796a58601136f8 Mon Sep 17 00:00:00 2001 From: "svoj@may.pils.ru" <> Date: Thu, 17 Aug 2006 21:23:00 +0500 Subject: [PATCH 1/6] BUG#19702 - Using myisampack/myisamchk on a FULLTEXT indexed table results in table corrupt Fulltext key has always two keysegs, thus we need to update FT_SEGS (last) element from seg array in case of compressed table. Also we must update ft2_keyinfo. --- myisam/mi_packrec.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/myisam/mi_packrec.c b/myisam/mi_packrec.c index bd2d162d100..0edb3ac1d5d 100644 --- a/myisam/mi_packrec.c +++ b/myisam/mi_packrec.c @@ -16,7 +16,7 @@ /* Functions to compressed records */ -#include "myisamdef.h" +#include "fulltext.h" #define IS_CHAR ((uint) 32768) /* Bit if char (not offset) in tree */ @@ -228,11 +228,19 @@ my_bool _mi_read_pack_info(MI_INFO *info, pbool fix_keys) { for (i=0 ; i < share->base.keys ; i++) { - share->keyinfo[i].keylength+=(uint16) diff_length; - share->keyinfo[i].minlength+=(uint16) diff_length; - share->keyinfo[i].maxlength+=(uint16) diff_length; - share->keyinfo[i].seg[share->keyinfo[i].keysegs].length= - (uint16) rec_reflength; + MI_KEYDEF *keyinfo= &share->keyinfo[i]; + keyinfo->keylength+= (uint16) diff_length; + keyinfo->minlength+= (uint16) diff_length; + keyinfo->maxlength+= (uint16) diff_length; + keyinfo->seg[keyinfo->flag & HA_FULLTEXT ? + FT_SEGS : keyinfo->keysegs].length= (uint16) rec_reflength; + } + if (share->ft2_keyinfo.seg) + { + MI_KEYDEF *ft2_keyinfo= &share->ft2_keyinfo; + ft2_keyinfo->keylength+= (uint16) diff_length; + ft2_keyinfo->minlength+= (uint16) diff_length; + ft2_keyinfo->maxlength+= (uint16) diff_length; } } From 0b2a9d01ed78c3bb51b95f7d5cb1c8359a865986 Mon Sep 17 00:00:00 2001 From: "istruewing@chilla.local" <> Date: Tue, 29 Aug 2006 20:45:04 +0200 Subject: [PATCH 2/6] Bug#14400 - Query joins wrong rows from table which is subject of "concurrent insert" Better fix by Monty: "The previous bug fix didn't work when using partial keys." --- myisam/mi_rkey.c | 43 +++++++++++++++++++++++++++----------- mysql-test/r/myisam.result | 15 +++++++++++++ mysql-test/t/myisam.test | 17 +++++++++++++++ 3 files changed, 63 insertions(+), 12 deletions(-) diff --git a/myisam/mi_rkey.c b/myisam/mi_rkey.c index 70122288d6c..f051558cae5 100644 --- a/myisam/mi_rkey.c +++ b/myisam/mi_rkey.c @@ -66,6 +66,7 @@ int mi_rkey(MI_INFO *info, byte *buf, int inx, const byte *key, uint key_len, if (fast_mi_readinfo(info)) goto err; + if (share->concurrent_insert) rw_rdlock(&share->key_root_lock[inx]); @@ -77,19 +78,37 @@ int mi_rkey(MI_INFO *info, byte *buf, int inx, const byte *key, uint key_len, if (!_mi_search(info,keyinfo, key_buff, use_key_length, myisam_read_vec[search_flag], info->s->state.key_root[inx])) { - while (info->lastpos >= info->state->data_file_length) + if (info->lastpos >= info->state->data_file_length) { - /* - Skip rows that are inserted by other threads since we got a lock - Note that this can only happen if we are not searching after an - exact key, because the keys are sorted according to position - */ - - if (_mi_search_next(info, keyinfo, info->lastkey, - info->lastkey_length, - myisam_readnext_vec[search_flag], - info->s->state.key_root[inx])) - break; + do + { + uint not_used; + /* + If we are searching for an exact key, abort if we find a bigger + key. + */ + if (search_flag == HA_READ_KEY_EXACT && + (use_key_length == USE_WHOLE_KEY || + _mi_key_cmp(keyinfo->seg, key_buff, info->lastkey, use_key_length, + SEARCH_FIND, ¬_used))) + { + my_errno= HA_ERR_END_OF_FILE; + info->lastpos= HA_OFFSET_ERROR; + break; + } + /* + Skip rows that are inserted by other threads since we got a lock + Note that this can only happen if we are not searching after an + full length exact key, because the keys are sorted + according to position + */ + if (_mi_search_next(info, keyinfo, info->lastkey, + info->lastkey_length, + myisam_readnext_vec[search_flag], + info->s->state.key_root[inx])) + break; + } + while (info->lastpos >= info->state->data_file_length); } } if (share->concurrent_insert) diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index e6df3499eb5..e7c1ad7e344 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -472,3 +472,18 @@ select c1 from t1 order by c1 limit 1; c1 a drop table t1; +create table t1 (a int not null, primary key(a)); +create table t2 (a int not null, b int not null, primary key(a,b)); +insert into t1 values (1),(2),(3),(4),(5),(6); +insert into t2 values (1,1),(2,1); +lock tables t1 read local, t2 read local; +select straight_join * from t1,t2 force index (primary) where t1.a=t2.a; +a a b +1 1 1 +2 2 1 +insert into t2 values(2,0); +select straight_join * from t1,t2 force index (primary) where t1.a=t2.a; +a a b +1 1 1 +2 2 1 +drop table t1,t2; diff --git a/mysql-test/t/myisam.test b/mysql-test/t/myisam.test index a502002d30e..bb8dc30395b 100644 --- a/mysql-test/t/myisam.test +++ b/mysql-test/t/myisam.test @@ -458,3 +458,20 @@ insert into t1 values ('a'), ('b'); select c1 from t1 order by c1 limit 1; drop table t1; +# +# Bug #14400 Join could miss concurrently inserted row +# +create table t1 (a int not null, primary key(a)); +create table t2 (a int not null, b int not null, primary key(a,b)); +insert into t1 values (1),(2),(3),(4),(5),(6); +insert into t2 values (1,1),(2,1); +lock tables t1 read local, t2 read local; +select straight_join * from t1,t2 force index (primary) where t1.a=t2.a; +connect (root,localhost,root,,test,$MASTER_MYPORT,master.sock); +insert into t2 values(2,0); +disconnect root; +connection default; +select straight_join * from t1,t2 force index (primary) where t1.a=t2.a; +drop table t1,t2; + +# end of 4.0 tests From 678e15174eacc04306c09175781e68a462fcc4f7 Mon Sep 17 00:00:00 2001 From: "acurtis/antony@xiphis.org/ltantony.xiphis.org" <> Date: Wed, 30 Aug 2006 13:20:39 -0700 Subject: [PATCH 3/6] Bug#20573 "strict mode: inserts autogenerated auto_increment value bigger than max" Strict mode should fail if autoincrement value is out of range --- include/my_base.h | 4 +++- mysql-test/include/strict_autoinc.inc | 28 ++++++++++++++++++++++ mysql-test/r/strict_autoinc_1myisam.result | 27 +++++++++++++++++++++ mysql-test/r/strict_autoinc_2innodb.result | 27 +++++++++++++++++++++ mysql-test/r/strict_autoinc_3heap.result | 27 +++++++++++++++++++++ mysql-test/r/strict_autoinc_4bdb.result | 27 +++++++++++++++++++++ mysql-test/r/strict_autoinc_5ndb.result | 27 +++++++++++++++++++++ mysql-test/t/strict_autoinc_1myisam.test | 8 +++++++ mysql-test/t/strict_autoinc_2innodb.test | 10 ++++++++ mysql-test/t/strict_autoinc_3heap.test | 8 +++++++ mysql-test/t/strict_autoinc_4bdb.test | 10 ++++++++ mysql-test/t/strict_autoinc_5ndb.test | 10 ++++++++ sql/ha_berkeley.cc | 5 +++- sql/ha_heap.cc | 5 +++- sql/ha_innodb.cc | 3 ++- sql/ha_myisam.cc | 6 ++++- sql/ha_myisammrg.cc | 6 ++++- sql/ha_ndbcluster.cc | 4 +++- sql/handler.cc | 23 ++++++++++++++---- sql/handler.h | 2 +- sql/share/errmsg.txt | 2 ++ 21 files changed, 256 insertions(+), 13 deletions(-) create mode 100644 mysql-test/include/strict_autoinc.inc create mode 100644 mysql-test/r/strict_autoinc_1myisam.result create mode 100644 mysql-test/r/strict_autoinc_2innodb.result create mode 100644 mysql-test/r/strict_autoinc_3heap.result create mode 100644 mysql-test/r/strict_autoinc_4bdb.result create mode 100644 mysql-test/r/strict_autoinc_5ndb.result create mode 100644 mysql-test/t/strict_autoinc_1myisam.test create mode 100644 mysql-test/t/strict_autoinc_2innodb.test create mode 100644 mysql-test/t/strict_autoinc_3heap.test create mode 100644 mysql-test/t/strict_autoinc_4bdb.test create mode 100644 mysql-test/t/strict_autoinc_5ndb.test diff --git a/include/my_base.h b/include/my_base.h index d8a0e15ccbe..cffe1c3caf4 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -357,8 +357,10 @@ enum ha_base_keytype { #define HA_ERR_TABLE_DEF_CHANGED 159 /* The table changed in storage engine */ #define HA_ERR_TABLE_NEEDS_UPGRADE 160 /* The table changed in storage engine */ #define HA_ERR_TABLE_READONLY 161 /* The table is not writable */ +#define HA_ERR_AUTOINC_READ_FAILED 162/* Failed to get the next autoinc value */ +#define HA_ERR_AUTOINC_ERANGE 163 /* Failed to set the row autoinc value */ -#define HA_ERR_LAST 161 /*Copy last error nr.*/ +#define HA_ERR_LAST 163 /*Copy last error nr.*/ /* Add error numbers before HA_ERR_LAST and change it accordingly. */ #define HA_ERR_ERRORS (HA_ERR_LAST - HA_ERR_FIRST + 1) diff --git a/mysql-test/include/strict_autoinc.inc b/mysql-test/include/strict_autoinc.inc new file mode 100644 index 00000000000..6960440f3a7 --- /dev/null +++ b/mysql-test/include/strict_autoinc.inc @@ -0,0 +1,28 @@ +# +# Test for strict-mode autoincrement +# + +set @org_mode=@@sql_mode; +eval create table t1 +( + `a` tinyint(4) NOT NULL auto_increment, + primary key (`a`) +) engine = $type ; +set @@sql_mode='strict_all_tables'; +--error ER_WARN_DATA_OUT_OF_RANGE +insert into t1 values(1000); +select count(*) from t1; + +set auto_increment_increment=1000; +set auto_increment_offset=700; +--error ER_WARN_DATA_OUT_OF_RANGE +insert into t1 values(null); +select count(*) from t1; + +set @@sql_mode=@org_mode; +insert into t1 values(null); +select * from t1; + +drop table t1; + +# End of test diff --git a/mysql-test/r/strict_autoinc_1myisam.result b/mysql-test/r/strict_autoinc_1myisam.result new file mode 100644 index 00000000000..5d3c2698cda --- /dev/null +++ b/mysql-test/r/strict_autoinc_1myisam.result @@ -0,0 +1,27 @@ +set @org_mode=@@sql_mode; +create table t1 +( +`a` tinyint(4) NOT NULL auto_increment, +primary key (`a`) +) engine = 'MYISAM' ; +set @@sql_mode='strict_all_tables'; +insert into t1 values(1000); +ERROR 22003: Out of range value adjusted for column 'a' at row 1 +select count(*) from t1; +count(*) +0 +set auto_increment_increment=1000; +set auto_increment_offset=700; +insert into t1 values(null); +ERROR 22003: Out of range value adjusted for column 'a' at row 1 +select count(*) from t1; +count(*) +0 +set @@sql_mode=@org_mode; +insert into t1 values(null); +Warnings: +Warning 1264 Out of range value adjusted for column 'a' at row 1 +select * from t1; +a +127 +drop table t1; diff --git a/mysql-test/r/strict_autoinc_2innodb.result b/mysql-test/r/strict_autoinc_2innodb.result new file mode 100644 index 00000000000..f1936ff4de3 --- /dev/null +++ b/mysql-test/r/strict_autoinc_2innodb.result @@ -0,0 +1,27 @@ +set @org_mode=@@sql_mode; +create table t1 +( +`a` tinyint(4) NOT NULL auto_increment, +primary key (`a`) +) engine = 'InnoDB' ; +set @@sql_mode='strict_all_tables'; +insert into t1 values(1000); +ERROR 22003: Out of range value adjusted for column 'a' at row 1 +select count(*) from t1; +count(*) +0 +set auto_increment_increment=1000; +set auto_increment_offset=700; +insert into t1 values(null); +ERROR 22003: Out of range value adjusted for column 'a' at row 1 +select count(*) from t1; +count(*) +0 +set @@sql_mode=@org_mode; +insert into t1 values(null); +Warnings: +Warning 1264 Out of range value adjusted for column 'a' at row 1 +select * from t1; +a +127 +drop table t1; diff --git a/mysql-test/r/strict_autoinc_3heap.result b/mysql-test/r/strict_autoinc_3heap.result new file mode 100644 index 00000000000..aa0be270ac5 --- /dev/null +++ b/mysql-test/r/strict_autoinc_3heap.result @@ -0,0 +1,27 @@ +set @org_mode=@@sql_mode; +create table t1 +( +`a` tinyint(4) NOT NULL auto_increment, +primary key (`a`) +) engine = 'MEMORY' ; +set @@sql_mode='strict_all_tables'; +insert into t1 values(1000); +ERROR 22003: Out of range value adjusted for column 'a' at row 1 +select count(*) from t1; +count(*) +0 +set auto_increment_increment=1000; +set auto_increment_offset=700; +insert into t1 values(null); +ERROR 22003: Out of range value adjusted for column 'a' at row 1 +select count(*) from t1; +count(*) +0 +set @@sql_mode=@org_mode; +insert into t1 values(null); +Warnings: +Warning 1264 Out of range value adjusted for column 'a' at row 1 +select * from t1; +a +127 +drop table t1; diff --git a/mysql-test/r/strict_autoinc_4bdb.result b/mysql-test/r/strict_autoinc_4bdb.result new file mode 100644 index 00000000000..73683b645e2 --- /dev/null +++ b/mysql-test/r/strict_autoinc_4bdb.result @@ -0,0 +1,27 @@ +set @org_mode=@@sql_mode; +create table t1 +( +`a` tinyint(4) NOT NULL auto_increment, +primary key (`a`) +) engine = 'BDB' ; +set @@sql_mode='strict_all_tables'; +insert into t1 values(1000); +ERROR 22003: Out of range value adjusted for column 'a' at row 1 +select count(*) from t1; +count(*) +0 +set auto_increment_increment=1000; +set auto_increment_offset=700; +insert into t1 values(null); +ERROR 22003: Out of range value adjusted for column 'a' at row 1 +select count(*) from t1; +count(*) +0 +set @@sql_mode=@org_mode; +insert into t1 values(null); +Warnings: +Warning 1264 Out of range value adjusted for column 'a' at row 1 +select * from t1; +a +127 +drop table t1; diff --git a/mysql-test/r/strict_autoinc_5ndb.result b/mysql-test/r/strict_autoinc_5ndb.result new file mode 100644 index 00000000000..d0d62d05b32 --- /dev/null +++ b/mysql-test/r/strict_autoinc_5ndb.result @@ -0,0 +1,27 @@ +set @org_mode=@@sql_mode; +create table t1 +( +`a` tinyint(4) NOT NULL auto_increment, +primary key (`a`) +) engine = 'NDB' ; +set @@sql_mode='strict_all_tables'; +insert into t1 values(1000); +ERROR 22003: Out of range value adjusted for column 'a' at row 1 +select count(*) from t1; +count(*) +0 +set auto_increment_increment=1000; +set auto_increment_offset=700; +insert into t1 values(null); +ERROR 22003: Out of range value adjusted for column 'a' at row 1 +select count(*) from t1; +count(*) +0 +set @@sql_mode=@org_mode; +insert into t1 values(null); +Warnings: +Warning 1264 Out of range value adjusted for column 'a' at row 1 +select * from t1; +a +127 +drop table t1; diff --git a/mysql-test/t/strict_autoinc_1myisam.test b/mysql-test/t/strict_autoinc_1myisam.test new file mode 100644 index 00000000000..d9ecce30974 --- /dev/null +++ b/mysql-test/t/strict_autoinc_1myisam.test @@ -0,0 +1,8 @@ +# +# Bug#20573 Strict mode auto-increment +# + +let $type= 'MYISAM' ; +--source include/strict_autoinc.inc + +# end of test diff --git a/mysql-test/t/strict_autoinc_2innodb.test b/mysql-test/t/strict_autoinc_2innodb.test new file mode 100644 index 00000000000..83dfe950938 --- /dev/null +++ b/mysql-test/t/strict_autoinc_2innodb.test @@ -0,0 +1,10 @@ +-- source include/have_innodb.inc + +# +# Bug#20573 Strict mode auto-increment +# + +let $type= 'InnoDB' ; +--source include/strict_autoinc.inc + +# end of test diff --git a/mysql-test/t/strict_autoinc_3heap.test b/mysql-test/t/strict_autoinc_3heap.test new file mode 100644 index 00000000000..f266ecdfda2 --- /dev/null +++ b/mysql-test/t/strict_autoinc_3heap.test @@ -0,0 +1,8 @@ +# +# Bug#20573 Strict mode auto-increment +# + +let $type= 'MEMORY' ; +--source include/strict_autoinc.inc + +# end of test diff --git a/mysql-test/t/strict_autoinc_4bdb.test b/mysql-test/t/strict_autoinc_4bdb.test new file mode 100644 index 00000000000..10d6bfd41e7 --- /dev/null +++ b/mysql-test/t/strict_autoinc_4bdb.test @@ -0,0 +1,10 @@ +-- source include/have_bdb.inc + +# +# Bug#20573 Strict mode auto-increment +# + +let $type= 'BDB' ; +--source include/strict_autoinc.inc + +# end of test diff --git a/mysql-test/t/strict_autoinc_5ndb.test b/mysql-test/t/strict_autoinc_5ndb.test new file mode 100644 index 00000000000..9e2090fddef --- /dev/null +++ b/mysql-test/t/strict_autoinc_5ndb.test @@ -0,0 +1,10 @@ +-- source include/have_ndb.inc + +# +# Bug#20573 Strict mode auto-increment +# + +let $type= 'NDB' ; +--source include/strict_autoinc.inc + +# end of test diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc index 72af402a0dc..4209bc93d30 100644 --- a/sql/ha_berkeley.cc +++ b/sql/ha_berkeley.cc @@ -953,7 +953,10 @@ int ha_berkeley::write_row(byte * record) if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) table->timestamp_field->set_time(); if (table->next_number_field && record == table->record[0]) - update_auto_increment(); + { + if ((error= update_auto_increment())) + DBUG_RETURN(error); + } if ((error=pack_row(&row, record,1))) DBUG_RETURN(error); /* purecov: inspected */ diff --git a/sql/ha_heap.cc b/sql/ha_heap.cc index 79d4575ff1b..3aaa0287098 100644 --- a/sql/ha_heap.cc +++ b/sql/ha_heap.cc @@ -176,7 +176,10 @@ int ha_heap::write_row(byte * buf) if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) table->timestamp_field->set_time(); if (table->next_number_field && buf == table->record[0]) - update_auto_increment(); + { + if ((res= update_auto_increment())) + return res; + } res= heap_write(file,buf); if (!res && (++records_changed*HEAP_STATS_UPDATE_THRESHOLD > file->s->records)) diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index c56be6376d0..4e73d4fb285 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -3252,7 +3252,8 @@ no_commit: /* We must use the handler code to update the auto-increment value to be sure that we increment it correctly. */ - update_auto_increment(); + if ((error= update_auto_increment())) + goto func_exit; auto_inc_used = 1; } diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index 128cc191434..f35d7efce0b 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -316,7 +316,11 @@ int ha_myisam::write_row(byte * buf) or a new row, then update the auto_increment value in the record. */ if (table->next_number_field && buf == table->record[0]) - update_auto_increment(); + { + int error; + if ((error= update_auto_increment())) + return error; + } return mi_write(file,buf); } diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc index 0b6e05fcbd4..cb11d9b0452 100644 --- a/sql/ha_myisammrg.cc +++ b/sql/ha_myisammrg.cc @@ -139,7 +139,11 @@ int ha_myisammrg::write_row(byte * buf) if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) table->timestamp_field->set_time(); if (table->next_number_field && buf == table->record[0]) - update_auto_increment(); + { + int error; + if ((error= update_auto_increment())) + return error; + } return myrg_write(file,buf); } diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 5d6fe5f984f..d8f33394e1a 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -2130,9 +2130,11 @@ int ha_ndbcluster::write_row(byte *record) if (has_auto_increment) { THD *thd= table->in_use; + int error; m_skip_auto_increment= FALSE; - update_auto_increment(); + if ((error= update_auto_increment())) + DBUG_RETURN(error); /* Ensure that handler is always called for auto_increment values */ thd->next_insert_id= 0; m_skip_auto_increment= !auto_increment_column_changed; diff --git a/sql/handler.cc b/sql/handler.cc index b0051b02d91..d29ae5e4026 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -429,6 +429,8 @@ static int ha_init_errors(void) SETMSG(HA_ERR_TABLE_DEF_CHANGED, ER(ER_TABLE_DEF_CHANGED)); SETMSG(HA_ERR_TABLE_NEEDS_UPGRADE, ER(ER_TABLE_NEEDS_UPGRADE)); SETMSG(HA_ERR_TABLE_READONLY, ER(ER_OPEN_AS_READONLY)); + SETMSG(HA_ERR_AUTOINC_READ_FAILED, ER(ER_AUTOINC_READ_FAILED)); + SETMSG(HA_ERR_AUTOINC_ERANGE, ER(ER_WARN_DATA_OUT_OF_RANGE)); /* Register the error messages for use with my_error(). */ return my_error_register(errmsgs, HA_ERR_FIRST, HA_ERR_LAST); @@ -1542,7 +1544,10 @@ prev_insert_id(ulonglong nr, struct system_variables *variables) RETURN 0 ok - 1 get_auto_increment() was called and returned ~(ulonglong) 0 + HA_ERR_AUTOINC_READ_FAILED + get_auto_increment() was called and returned ~(ulonglong) 0 + HA_ERR_AUTOINC_ERANGE + storing value in field caused strict mode failure. IMPLEMENTATION @@ -1586,13 +1591,12 @@ prev_insert_id(ulonglong nr, struct system_variables *variables) thd->next_insert_id is cleared after it's been used for a statement. */ -bool handler::update_auto_increment() +int handler::update_auto_increment() { ulonglong nr; THD *thd= table->in_use; struct system_variables *variables= &thd->variables; bool auto_increment_field_not_null; - bool result= 0; DBUG_ENTER("handler::update_auto_increment"); /* @@ -1616,7 +1620,7 @@ bool handler::update_auto_increment() if (!(nr= thd->next_insert_id)) { if ((nr= get_auto_increment()) == ~(ulonglong) 0) - result= 1; // Mark failure + DBUG_RETURN(HA_ERR_AUTOINC_READ_FAILED); // Mark failure if (variables->auto_increment_increment != 1) nr= next_insert_id(nr-1, variables); @@ -1636,6 +1640,7 @@ bool handler::update_auto_increment() if (likely(!table->next_number_field->store((longlong) nr, TRUE))) thd->insert_id((ulonglong) nr); else + if (thd->killed != THD::KILL_BAD_DATA) /* did we fail strict mode? */ { /* overflow of the field; we'll use the max value, however we try to @@ -1646,6 +1651,8 @@ bool handler::update_auto_increment() if (unlikely(table->next_number_field->store((longlong) nr, TRUE))) thd->insert_id(nr= table->next_number_field->val_int()); } + else + DBUG_RETURN(HA_ERR_AUTOINC_ERANGE); /* We can't set next_insert_id if the auto-increment key is not the @@ -1666,7 +1673,7 @@ bool handler::update_auto_increment() /* Mark that we generated a new value */ auto_increment_column_changed=1; - DBUG_RETURN(result); + DBUG_RETURN(0); } /* @@ -1864,6 +1871,12 @@ void handler::print_error(int error, myf errflag) case HA_ERR_TABLE_READONLY: textno= ER_OPEN_AS_READONLY; break; + case HA_ERR_AUTOINC_READ_FAILED: + textno= ER_AUTOINC_READ_FAILED; + break; + case HA_ERR_AUTOINC_ERANGE: + textno= ER_WARN_DATA_OUT_OF_RANGE; + break; default: { /* The error was "unknown" to this function. diff --git a/sql/handler.h b/sql/handler.h index 44de0cc715a..051f5bd31bf 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -565,7 +565,7 @@ public: virtual ~handler(void) { /* TODO: DBUG_ASSERT(inited == NONE); */ } int ha_open(const char *name, int mode, int test_if_locked); void adjust_next_insert_id_after_explicit_value(ulonglong nr); - bool update_auto_increment(); + int update_auto_increment(); virtual void print_error(int error, myf errflag); virtual bool get_error_message(int error, String *buf); uint get_dup_key(int error); diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index e90f54608e1..94e0d6e0db5 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5623,3 +5623,5 @@ ER_NO_TRIGGERS_ON_SYSTEM_SCHEMA eng "Triggers can not be created on system tables" ER_REMOVED_SPACES eng "Leading spaces are removed from name '%s'" +ER_AUTOINC_READ_FAILED + eng "Failed to read auto-increment value from storage engine" From 39c0f0a4844dbcc5782ae0240ba409ee6edd4899 Mon Sep 17 00:00:00 2001 From: "istruewing@chilla.local" <> Date: Thu, 7 Sep 2006 15:39:31 +0200 Subject: [PATCH 4/6] Bug#14400 - Query joins wrong rows from table which is subject of "concurrent insert" Additional fix for full keys and test case. --- myisam/mi_rkey.c | 38 ++++++++++++++++++++++---------------- mysql-test/r/myisam.result | 13 +++++++++++++ mysql-test/t/myisam.test | 18 ++++++++++++++++++ 3 files changed, 53 insertions(+), 16 deletions(-) diff --git a/myisam/mi_rkey.c b/myisam/mi_rkey.c index f051558cae5..be99d66618d 100644 --- a/myisam/mi_rkey.c +++ b/myisam/mi_rkey.c @@ -78,24 +78,18 @@ int mi_rkey(MI_INFO *info, byte *buf, int inx, const byte *key, uint key_len, if (!_mi_search(info,keyinfo, key_buff, use_key_length, myisam_read_vec[search_flag], info->s->state.key_root[inx])) { - if (info->lastpos >= info->state->data_file_length) + /* + If we searching for a partial key (or using >, >=, < or <=) and + the data is outside of the data file, we need to continue searching + for the first key inside the data file + */ + if (info->lastpos >= info->state->data_file_length && + (search_flag != HA_READ_KEY_EXACT || + last_used_keyseg != keyinfo->seg + keyinfo->keysegs)) { do { uint not_used; - /* - If we are searching for an exact key, abort if we find a bigger - key. - */ - if (search_flag == HA_READ_KEY_EXACT && - (use_key_length == USE_WHOLE_KEY || - _mi_key_cmp(keyinfo->seg, key_buff, info->lastkey, use_key_length, - SEARCH_FIND, ¬_used))) - { - my_errno= HA_ERR_END_OF_FILE; - info->lastpos= HA_OFFSET_ERROR; - break; - } /* Skip rows that are inserted by other threads since we got a lock Note that this can only happen if we are not searching after an @@ -107,8 +101,20 @@ int mi_rkey(MI_INFO *info, byte *buf, int inx, const byte *key, uint key_len, myisam_readnext_vec[search_flag], info->s->state.key_root[inx])) break; - } - while (info->lastpos >= info->state->data_file_length); + /* + Check that the found key does still match the search. + _mi_search_next() delivers the next key regardless of its + value. + */ + if (search_flag == HA_READ_KEY_EXACT && + _mi_key_cmp(keyinfo->seg, key_buff, info->lastkey, use_key_length, + SEARCH_FIND, ¬_used)) + { + my_errno= HA_ERR_KEY_NOT_FOUND; + info->lastpos= HA_OFFSET_ERROR; + break; + } + } while (info->lastpos >= info->state->data_file_length); } } if (share->concurrent_insert) diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index e7c1ad7e344..9d8e8289667 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -487,3 +487,16 @@ a a b 1 1 1 2 2 1 drop table t1,t2; +CREATE TABLE t1 (c1 varchar(250) NOT NULL); +CREATE TABLE t2 (c1 varchar(250) NOT NULL, PRIMARY KEY (c1)); +INSERT INTO t1 VALUES ('test000001'), ('test000002'), ('test000003'); +INSERT INTO t2 VALUES ('test000002'), ('test000003'), ('test000004'); +LOCK TABLES t1 READ LOCAL, t2 READ LOCAL; +SELECT t1.c1 AS t1c1, t2.c1 AS t2c1 FROM t1, t2 +WHERE t1.c1 = t2.c1 HAVING t1c1 != t2c1; +t1c1 t2c1 +INSERT INTO t2 VALUES ('test000001'), ('test000005'); +SELECT t1.c1 AS t1c1, t2.c1 AS t2c1 FROM t1, t2 +WHERE t1.c1 = t2.c1 HAVING t1c1 != t2c1; +t1c1 t2c1 +DROP TABLE t1,t2; diff --git a/mysql-test/t/myisam.test b/mysql-test/t/myisam.test index bb8dc30395b..83d93686429 100644 --- a/mysql-test/t/myisam.test +++ b/mysql-test/t/myisam.test @@ -461,6 +461,7 @@ drop table t1; # # Bug #14400 Join could miss concurrently inserted row # +# Partial key. create table t1 (a int not null, primary key(a)); create table t2 (a int not null, b int not null, primary key(a,b)); insert into t1 values (1),(2),(3),(4),(5),(6); @@ -473,5 +474,22 @@ disconnect root; connection default; select straight_join * from t1,t2 force index (primary) where t1.a=t2.a; drop table t1,t2; +# +# Full key. +CREATE TABLE t1 (c1 varchar(250) NOT NULL); +CREATE TABLE t2 (c1 varchar(250) NOT NULL, PRIMARY KEY (c1)); +INSERT INTO t1 VALUES ('test000001'), ('test000002'), ('test000003'); +INSERT INTO t2 VALUES ('test000002'), ('test000003'), ('test000004'); +LOCK TABLES t1 READ LOCAL, t2 READ LOCAL; +SELECT t1.c1 AS t1c1, t2.c1 AS t2c1 FROM t1, t2 + WHERE t1.c1 = t2.c1 HAVING t1c1 != t2c1; +connect (con1,localhost,root,,); +connection con1; +INSERT INTO t2 VALUES ('test000001'), ('test000005'); +disconnect con1; +connection default; +SELECT t1.c1 AS t1c1, t2.c1 AS t2c1 FROM t1, t2 + WHERE t1.c1 = t2.c1 HAVING t1c1 != t2c1; +DROP TABLE t1,t2; # end of 4.0 tests From 44f167ee1d0ca223562066c5d2ccd85be93832d0 Mon Sep 17 00:00:00 2001 From: "svoj@april.(none)" <> Date: Tue, 12 Sep 2006 18:25:35 +0500 Subject: [PATCH 5/6] BUG#20256 - LOCK WRITE - MyISAM Only MyISAM tables locked with LOCK TABLES ... WRITE were affected. A query that is optimized with index_merge doesn't reflect rows inserted within LOCK TABLES. MyISAM doesn't flush a state within LOCK TABLES. index_merge optimization creates a copy of the handler, which thus gets outdated MyISAM state. New handler->clone() method is introduced to fix this problem. For non-MyISAM storage engines it allocates a handler and opens it with ha_open(). For MyISAM it additionally copies MyISAM state pointer to cloned handler. --- mysql-test/r/index_merge.result | 31 +++++++++++++++++++++++++++++++ mysql-test/t/index_merge.test | 32 ++++++++++++++++++++++++++++++++ sql/ha_myisam.cc | 8 ++++++++ sql/ha_myisam.h | 1 + sql/handler.cc | 9 +++++++++ sql/handler.h | 1 + sql/opt_range.cc | 5 +---- 7 files changed, 83 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/index_merge.result b/mysql-test/r/index_merge.result index 3a69f56cbd3..3f3360e2da0 100644 --- a/mysql-test/r/index_merge.result +++ b/mysql-test/r/index_merge.result @@ -424,3 +424,34 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t3 index_merge a,b a,b 5,5 NULL # Using intersect(a,b); Using where drop table t3; drop table t0, t1, t2; +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES(1); +CREATE TABLE t2(a INT, b INT, dummy CHAR(16) DEFAULT '', KEY(a), KEY(b)); +INSERT INTO t2(a,b) VALUES +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(1,2); +LOCK TABLES t1 WRITE, t2 WRITE; +INSERT INTO t2(a,b) VALUES(1,2); +SELECT t2.a FROM t1,t2 WHERE t2.b=2 AND t2.a=1; +a +1 +1 +UNLOCK TABLES; +DROP TABLE t1, t2; diff --git a/mysql-test/t/index_merge.test b/mysql-test/t/index_merge.test index 3da5711bf7a..30eb0b40fca 100644 --- a/mysql-test/t/index_merge.test +++ b/mysql-test/t/index_merge.test @@ -383,3 +383,35 @@ explain select * from t3 where a=1 and b=1; drop table t3; drop table t0, t1, t2; + +# +# BUG#20256 - LOCK WRITE - MyISAM +# +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES(1); +CREATE TABLE t2(a INT, b INT, dummy CHAR(16) DEFAULT '', KEY(a), KEY(b)); +INSERT INTO t2(a,b) VALUES +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(1,2); +LOCK TABLES t1 WRITE, t2 WRITE; +INSERT INTO t2(a,b) VALUES(1,2); +SELECT t2.a FROM t1,t2 WHERE t2.b=2 AND t2.a=1; +UNLOCK TABLES; +DROP TABLE t1, t2; diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index 128cc191434..876cd33ec9c 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -169,6 +169,14 @@ ha_myisam::ha_myisam(TABLE *table_arg) can_enable_indexes(1) {} +handler *ha_myisam::clone(MEM_ROOT *mem_root) +{ + ha_myisam *new_handler= static_cast (handler::clone(mem_root)); + if (new_handler) + new_handler->file->state= file->state; + return new_handler; +} + static const char *ha_myisam_exts[] = { ".MYI", diff --git a/sql/ha_myisam.h b/sql/ha_myisam.h index ca684463311..950817d42bd 100644 --- a/sql/ha_myisam.h +++ b/sql/ha_myisam.h @@ -45,6 +45,7 @@ class ha_myisam: public handler public: ha_myisam(TABLE *table_arg); ~ha_myisam() {} + handler *clone(MEM_ROOT *mem_root); const char *table_type() const { return "MyISAM"; } const char *index_type(uint key_number); const char **bas_ext() const; diff --git a/sql/handler.cc b/sql/handler.cc index 01aa7491383..81981a5dcc6 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1372,6 +1372,15 @@ int ha_delete_table(THD *thd, enum db_type table_type, const char *path, /**************************************************************************** ** General handler functions ****************************************************************************/ +handler *handler::clone(MEM_ROOT *mem_root) +{ + handler *new_handler= get_new_handler(table, mem_root, table->s->db_type); + if (new_handler && !new_handler->ha_open(table->s->path, table->db_stat, + HA_OPEN_IGNORE_IF_LOCKED)) + return new_handler; + return NULL; +} + /* Open database-handler. Try O_RDONLY if can't open as O_RDWR */ /* Don't wait for locks if not HA_OPEN_WAIT_IF_LOCKED is set */ diff --git a/sql/handler.h b/sql/handler.h index 44de0cc715a..471bb08b748 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -563,6 +563,7 @@ public: pushed_cond(NULL) {} virtual ~handler(void) { /* TODO: DBUG_ASSERT(inited == NONE); */ } + virtual handler *clone(MEM_ROOT *mem_root); int ha_open(const char *name, int mode, int test_if_locked); void adjust_next_insert_id_after_explicit_value(ulonglong nr); bool update_auto_increment(); diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 3b77d1b419e..5866fe731cf 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1033,10 +1033,7 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler) } THD *thd= current_thd; - if (!(file= get_new_handler(head, thd->mem_root, head->s->db_type))) - goto failure; - DBUG_PRINT("info", ("Allocated new handler %p", file)); - if (file->ha_open(head->s->path, head->db_stat, HA_OPEN_IGNORE_IF_LOCKED)) + if (!(file= head->file->clone(thd->mem_root))) { /* Caller will free the memory */ goto failure; From d53ac1962008a89141782e061866518075300037 Mon Sep 17 00:00:00 2001 From: "istruewing@chilla.local" <> Date: Thu, 14 Sep 2006 16:55:57 +0200 Subject: [PATCH 6/6] Bug#14400 - Query joins wrong rows from table which is subject of "concurrent insert" After merge fix. --- myisam/mi_rkey.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/myisam/mi_rkey.c b/myisam/mi_rkey.c index 9ca1ee91273..60ef5ad32f0 100644 --- a/myisam/mi_rkey.c +++ b/myisam/mi_rkey.c @@ -103,7 +103,7 @@ int mi_rkey(MI_INFO *info, byte *buf, int inx, const byte *key, uint key_len, { do { - uint not_used; + uint not_used[2]; /* Skip rows that are inserted by other threads since we got a lock Note that this can only happen if we are not searching after an @@ -122,7 +122,7 @@ int mi_rkey(MI_INFO *info, byte *buf, int inx, const byte *key, uint key_len, */ if (search_flag == HA_READ_KEY_EXACT && ha_key_cmp(keyinfo->seg, key_buff, info->lastkey, use_key_length, - SEARCH_FIND, ¬_used)) + SEARCH_FIND, not_used)) { my_errno= HA_ERR_KEY_NOT_FOUND; info->lastpos= HA_OFFSET_ERROR;