From 5c14b35a7f6d5af579b2da6033da031e6cee18e3 Mon Sep 17 00:00:00 2001 From: "ingo@mysql.com" <> Date: Mon, 29 Aug 2005 16:50:09 +0200 Subject: [PATCH 1/4] Bug#12565 - ERROR 1034 when running simple UPDATE or DELETE on large MyISAM table Changed end-space comparison so that the key is not used past its end. This is due to the new end-space behaviour in 4.1. See also bug 6151 and 9188. --- myisam/mi_search.c | 32 +++++++++++++++++++++++--------- mysql-test/r/key.result | 16 ++++++++++++++++ mysql-test/t/key.test | 16 ++++++++++++++++ 3 files changed, 55 insertions(+), 9 deletions(-) diff --git a/myisam/mi_search.c b/myisam/mi_search.c index 390e32b679d..6ed245d9715 100644 --- a/myisam/mi_search.c +++ b/myisam/mi_search.c @@ -316,19 +316,21 @@ int _mi_prefix_search(MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *page, get_key_pack_length(kseg_len,length_pack,kseg); key_len_skip=length_pack+kseg_len; key_len_left=(int) key_len- (int) key_len_skip; + /* If key_len is 0, then lenght_pack is 1, then key_len_left is -1. */ cmplen=(key_len_left>=0) ? kseg_len : key_len-length_pack; DBUG_PRINT("info",("key: '%.*s'",kseg_len,kseg)); /* Keys are compressed the following way: - If the max length of first key segment <= 127 characters the prefix is + If the max length of first key segment <= 127 bytes the prefix is 1 byte else it's 2 byte - prefix The high bit is set if this is a prefix for the prev key - length Packed length if the previous was a prefix byte - [length] Length character of data - next-key-seg Next key segments + (prefix) length The high bit is set if this is a prefix for the prev key. + [suffix length] Packed length of suffix if the previous was a prefix. + (suffix) data Key data bytes (past the common prefix or whole segment). + [next-key-seg] Next key segments (([packed length], data), ...) + pointer Reference to the data file (last_keyseg->length). */ matched=0; /* how many char's from prefix were alredy matched */ @@ -349,16 +351,23 @@ int _mi_prefix_search(MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *page, if (packed) { - if (suffix_len == 0) /* Same key */ + if (suffix_len == 0) + { + /* == 0x80 or 0x8000, same key, prefix length == old key length. */ prefix_len=len; + } else { + /* > 0x80 or 0x8000, this is prefix lgt, packed suffix lgt follows. */ prefix_len=suffix_len; get_key_length(suffix_len,vseg); } } else + { + /* Not packed. No prefix used from last key. */ prefix_len=0; + } len=prefix_len+suffix_len; seg_len_pack=get_pack_length(len); @@ -414,7 +423,12 @@ int _mi_prefix_search(MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *page, uint left; uchar *k=kseg+prefix_len; - left=(len>cmplen) ? cmplen-prefix_len : suffix_len; + /* + If prefix_len > cmplen then we are in the end-space comparison + phase. Do not try to acces the key any more ==> left= 0. + */ + left= ((len <= cmplen) ? suffix_len : + ((prefix_len < cmplen) ? cmplen - prefix_len : 0)); matched=prefix_len+left; @@ -451,7 +465,7 @@ int _mi_prefix_search(MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *page, my_flag= -1; else { - /* We have to compare k and vseg as if they where space extended */ + /* We have to compare k and vseg as if they were space extended */ uchar *end= k+ (cmplen - len); for ( ; k < end && *k == ' '; k++) ; if (k == end) @@ -470,7 +484,7 @@ int _mi_prefix_search(MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *page, if ((nextflag & SEARCH_PREFIX) && key_len_left == 0) goto fix_flag; - /* We have to compare k and vseg as if they where space extended */ + /* We have to compare k and vseg as if they were space extended */ for (end=vseg + (len-cmplen) ; vseg < end && *vseg == (uchar) ' '; vseg++, matched++) ; diff --git a/mysql-test/r/key.result b/mysql-test/r/key.result index cceaf393a60..f0a7afa239f 100644 --- a/mysql-test/r/key.result +++ b/mysql-test/r/key.result @@ -325,3 +325,19 @@ ERROR 42S21: Duplicate column name 'c1' alter table t1 add key (c1,c1,c2); ERROR 42S21: Duplicate column name 'c1' drop table t1; +create table t1 ( +c1 int, +c2 varchar(20) not null, +primary key (c1), +key (c2(10)) +) engine=myisam; +insert into t1 values (1,''); +insert into t1 values (2,' \t\tTest String'); +insert into t1 values (3,' \n\tTest String'); +update t1 set c2 = 'New Test String' where c1 = 1; +select * from t1; +c1 c2 +1 New Test String +2 Test String +3 + Test String diff --git a/mysql-test/t/key.test b/mysql-test/t/key.test index 23a4de8456c..85728582c75 100644 --- a/mysql-test/t/key.test +++ b/mysql-test/t/key.test @@ -321,4 +321,20 @@ alter table t1 add key (c1,c2,c1); alter table t1 add key (c1,c1,c2); drop table t1; +# +# Bug#12565 - ERROR 1034 when running simple UPDATE or DELETE +# on large MyISAM table +# +create table t1 ( + c1 int, + c2 varchar(20) not null, + primary key (c1), + key (c2(10)) +) engine=myisam; +insert into t1 values (1,''); +insert into t1 values (2,' \t\tTest String'); +insert into t1 values (3,' \n\tTest String'); +update t1 set c2 = 'New Test String' where c1 = 1; +select * from t1; + # End of 4.1 tests From 674f8dfa7cd8f1a733317834bb83ceeefc2173aa Mon Sep 17 00:00:00 2001 From: "ingo@mysql.com" <> Date: Mon, 29 Aug 2005 16:54:33 +0200 Subject: [PATCH 2/4] Bug#11493 - Alter table rename to default database does not work without db name qualifying Supplied the default database name for ALTER TABLE ... RENAME for the new table instead of the old tables db like we do for other ALTERs. --- mysql-test/r/alter_table.result | 15 +++++++++++++++ mysql-test/t/alter_table.test | 31 +++++++++++++++++++++++++++++++ sql/sql_parse.cc | 19 ++++++++++++++++++- 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/alter_table.result b/mysql-test/r/alter_table.result index b7d47a09bee..e9c9c873750 100644 --- a/mysql-test/r/alter_table.result +++ b/mysql-test/r/alter_table.result @@ -528,3 +528,18 @@ create table t1 ( a timestamp ); alter table t1 add unique ( a(1) ); ERROR HY000: Incorrect sub part key; the used key part isn't a string, the used length is longer than the key part, or the storage engine doesn't support unique sub keys drop table t1; +create database mysqltest1; +create table t1 (c1 int); +alter table t1 rename mysqltest1.t1; +drop table t1; +ERROR 42S02: Unknown table 't1' +alter table mysqltest1.t1 rename t1; +drop table t1; +create table t1 (c1 int); +use mysqltest1; +drop database mysqltest1; +alter table test.t1 rename t1; +ERROR 3D000: No database selected +alter table test.t1 rename test.t1; +use test; +drop table t1; diff --git a/mysql-test/t/alter_table.test b/mysql-test/t/alter_table.test index a237b21f403..9bd34c2a610 100644 --- a/mysql-test/t/alter_table.test +++ b/mysql-test/t/alter_table.test @@ -361,4 +361,35 @@ create table t1 ( a timestamp ); alter table t1 add unique ( a(1) ); drop table t1; +# +# Bug#11493 - Alter table rename to default database does not work without +# db name qualifying +# +create database mysqltest1; +create table t1 (c1 int); +# Move table to other database. +alter table t1 rename mysqltest1.t1; +# Assure that it has moved. +--error 1051 +drop table t1; +# Move table back. +alter table mysqltest1.t1 rename t1; +# Assure that it is back. +drop table t1; +# Now test for correct message if no database is selected. +# Create t1 in 'test'. +create table t1 (c1 int); +# Change to other db. +use mysqltest1; +# Drop the current db. This de-selects any db. +drop database mysqltest1; +# Now test for correct message. +--error 1046 +alter table test.t1 rename t1; +# Check that explicit qualifying works even with no selected db. +alter table test.t1 rename test.t1; +# Go back to standard 'test' db. +use test; +drop table t1; + # End of 4.1 tests diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index cd87b097038..caf76b19eb2 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2599,7 +2599,24 @@ unsent_create_error: break; } if (!select_lex->db) - select_lex->db=tables->db; + { + /* + In the case of ALTER TABLE ... RENAME we should supply the + default database if the new name is not explicitly qualified + by a database. (Bug #11493) + */ + if (lex->alter_info.flags & ALTER_RENAME) + { + if (! thd->db) + { + send_error(thd,ER_NO_DB_ERROR); + goto error; + } + select_lex->db= thd->db; + } + else + select_lex->db=tables->db; + } if (check_access(thd,ALTER_ACL,tables->db,&tables->grant.privilege,0,0) || check_access(thd,INSERT_ACL | CREATE_ACL,select_lex->db,&priv,0,0)|| check_merge_table_access(thd, tables->db, From 32e3238da7ce19ef0a8f5eed1e0ed9245d862ca2 Mon Sep 17 00:00:00 2001 From: "ingo@mysql.com" <> Date: Mon, 29 Aug 2005 17:01:46 +0200 Subject: [PATCH 3/4] Bug#11816 - Truncate table doesn't work with temporary innodb tables Handle temporary tables like permanent tables: If the storage engine cannot truncate, delete instead. --- mysql-test/r/innodb.result | 16 ++++++++++++++++ mysql-test/t/innodb.test | 18 ++++++++++++++++++ sql/sql_delete.cc | 29 ++++++++++++++++------------- 3 files changed, 50 insertions(+), 13 deletions(-) diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result index 9caa5817a4b..858daacffe9 100644 --- a/mysql-test/r/innodb.result +++ b/mysql-test/r/innodb.result @@ -1658,3 +1658,19 @@ a_id b_list 3 NULL DROP TABLE t2; DROP TABLE t1; +create temporary table t1 (a int) engine=innodb; +insert into t1 values (4711); +truncate t1; +insert into t1 values (42); +select * from t1; +a +42 +drop table t1; +create table t1 (a int) engine=innodb; +insert into t1 values (4711); +truncate t1; +insert into t1 values (42); +select * from t1; +a +42 +drop table t1; diff --git a/mysql-test/t/innodb.test b/mysql-test/t/innodb.test index be502c2db44..a4b2c00f95a 100644 --- a/mysql-test/t/innodb.test +++ b/mysql-test/t/innodb.test @@ -1202,4 +1202,22 @@ SELECT * FROM (SELECT t1.*,GROUP_CONCAT(t2.b_id SEPARATOR ',') as b_list FROM (t DROP TABLE t2; DROP TABLE t1; +# +# Bug#11816 - Truncate table doesn't work with temporary innodb tables +# This is not an innodb bug, but we test it using innodb. +# +create temporary table t1 (a int) engine=innodb; +insert into t1 values (4711); +truncate t1; +insert into t1 values (42); +select * from t1; +drop table t1; +# Show that it works with permanent tables too. +create table t1 (a int) engine=innodb; +insert into t1 values (4711); +truncate t1; +insert into t1 values (42); +select * from t1; +drop table t1; + # End of 4.1 tests diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 0752105bcae..7248adf6993 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -617,6 +617,8 @@ int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) TABLE *table= *table_ptr; table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK); db_type table_type=table->db_type; + if (!ha_supports_generate(table_type)) + goto trunc_by_del; strmov(path,table->path); *table_ptr= table->next; // Unlink table from list close_temporary(table,0); @@ -635,7 +637,7 @@ int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) (void) sprintf(path,"%s/%s/%s%s",mysql_data_home,table_list->db, table_list->real_name,reg_ext); - fn_format(path,path,"","",4); + fn_format(path, path, "", "", MY_UNPACK_FILENAME); if (!dont_send_ok) { @@ -647,18 +649,7 @@ int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) DBUG_RETURN(-1); } if (!ha_supports_generate(table_type)) - { - /* Probably InnoDB table */ - ulong save_options= thd->options; - table_list->lock_type= TL_WRITE; - thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT); - ha_enable_transaction(thd, FALSE); - error= mysql_delete(thd, table_list, (COND*) 0, (SQL_LIST*) 0, - HA_POS_ERROR, 0); - ha_enable_transaction(thd, TRUE); - thd->options= save_options; - DBUG_RETURN(error); - } + goto trunc_by_del; if (lock_and_wait_for_table_name(thd, table_list)) DBUG_RETURN(-1); } @@ -693,4 +684,16 @@ end: VOID(pthread_mutex_unlock(&LOCK_open)); } DBUG_RETURN(error ? -1 : 0); + + trunc_by_del: + /* Probably InnoDB table */ + ulong save_options= thd->options; + table_list->lock_type= TL_WRITE; + thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT); + ha_enable_transaction(thd, FALSE); + error= mysql_delete(thd, table_list, (COND*) 0, (SQL_LIST*) 0, + HA_POS_ERROR, 0); + ha_enable_transaction(thd, TRUE); + thd->options= save_options; + DBUG_RETURN(error); } From 330eb045364a1079815c58a12ef9685b2c437b75 Mon Sep 17 00:00:00 2001 From: "ingo@mysql.com" <> Date: Mon, 29 Aug 2005 17:08:41 +0200 Subject: [PATCH 4/4] Bug#12296 - CHECKSUM TABLE reports 0 for the table Skipping deleted records instead of breaking the loop during checksum calculation. --- mysql-test/r/myisam.result | 14 ++++++++++++++ mysql-test/t/myisam.test | 15 +++++++++++++++ sql/sql_table.cc | 9 ++++++++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index 38273c01a98..1837a4078a7 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -595,3 +595,17 @@ show keys from t1; Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment t1 1 a 1 a A 8 NULL NULL YES BTREE drop table t1; +create table t1 (c1 int); +insert into t1 values (1),(2),(3),(4); +checksum table t1; +Table Checksum +test.t1 149057747 +delete from t1 where c1 = 1; +create table t2 as select * from t1; +checksum table t1; +Table Checksum +test.t1 984116287 +checksum table t2; +Table Checksum +test.t2 984116287 +drop table t1, t2; diff --git a/mysql-test/t/myisam.test b/mysql-test/t/myisam.test index cffb402bbd4..83e9e1ba7d2 100644 --- a/mysql-test/t/myisam.test +++ b/mysql-test/t/myisam.test @@ -575,4 +575,19 @@ show keys from t1; drop table t1; +# +# Bug#12296 - CHECKSUM TABLE reports 0 for the table +# This happened if the first record was marked as deleted. +# +create table t1 (c1 int); +insert into t1 values (1),(2),(3),(4); +checksum table t1; +delete from t1 where c1 = 1; +create table t2 as select * from t1; +# The following returns 0 with the bug in place. +checksum table t1; +# The above should give the same number as the following. +checksum table t2; +drop table t1, t2; + # End of 4.1 tests diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 87b864c73fa..b593aed1453 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3745,9 +3745,16 @@ int mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt) protocol->store_null(); else { - while (!t->file->rnd_next(t->record[0])) + for (;;) { ha_checksum row_crc= 0; + int error= t->file->rnd_next(t->record[0]); + if (unlikely(error)) + { + if (error == HA_ERR_RECORD_DELETED) + continue; + break; + } if (t->record[0] != (byte*) t->field[0]->ptr) row_crc= my_checksum(row_crc, t->record[0], ((byte*) t->field[0]->ptr) - t->record[0]);