From a68a457be8f7ec13fa601e1e66be2b47f6e2aafa Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 8 Jul 2007 18:13:04 +0400 Subject: [PATCH] Bug#29310: An InnoDB table was updated when the data wasn't actually changed. When a table is being updated it has two set of fields - fields required for checks of conditions and fields to be updated. A storage engine is allowed not to retrieve columns marked for update. Due to this fact records can't be compared to see whether the data has been changed or not. This makes the server always update records independently of data change. Now when an auto-updatable timestamp field is present and server sees that a table handle isn't going to retrieve write-only fields then all of such fields are marked as to be read to force the handler to retrieve them. mysql-test/r/innodb_mysql.result: Added a test case for the bug#29310: An InnoDB table was updated when the data wasn't actually changed. mysql-test/include/mix1.inc: Added a test case for the bug#29310: An InnoDB table was updated when the data wasn't actually changed. sql/sql_update.cc: Bug#29310: An InnoDB table was updated when the data wasn't actually changed. Now the mysql_update function when an auto-updatable timestamp field is present marks write-only fields as to be read to force the table handler to retrieve them. sql/sql_insert.cc: Bug#29310: An InnoDB table was updated when the data wasn't actually changed. Now the write_record function can compare records when fileds to be written is a subset of the fields to be read while updating a record. --- mysql-test/include/mix1.inc | 24 ++++++++++++++++++++++++ mysql-test/r/innodb_mysql.result | 28 ++++++++++++++++++++++++++++ sql/sql_insert.cc | 3 ++- sql/sql_update.cc | 11 +++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/mysql-test/include/mix1.inc b/mysql-test/include/mix1.inc index d8034e19898..03df7c33a00 100644 --- a/mysql-test/include/mix1.inc +++ b/mysql-test/include/mix1.inc @@ -948,5 +948,29 @@ unlock tables; select * from t1; drop tables t1; +# +# Bug#29310: An InnoDB table was updated when the data wasn't actually changed. +# +create table t1(f1 varchar(5) unique, f2 timestamp NOT NULL DEFAULT + CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP); +insert into t1(f1) values(1); +--replace_column 1 # +select @a:=f2 from t1; +--sleep 5 +update t1 set f1=1; +--replace_column 1 # +select @b:=f2 from t1; +select if(@a=@b,"ok","wrong"); +--sleep 5 +insert into t1(f1) values (1) on duplicate key update f1="1"; +--replace_column 1 # +select @b:=f2 from t1; +select if(@a=@b,"ok","wrong"); +--sleep 5 +insert into t1(f1) select f1 from t1 on duplicate key update f1="1"; +--replace_column 1 # +select @b:=f2 from t1; +select if(@a=@b,"ok","wrong"); +drop table t1; --echo End of 5.1 tests diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index db912c650c3..93b78143de7 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -951,4 +951,32 @@ NULL 1 Two drop tables t1; +create table t1(f1 varchar(5) unique, f2 timestamp NOT NULL DEFAULT +CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP); +insert into t1(f1) values(1); +select @a:=f2 from t1; +@a:=f2 +# +update t1 set f1=1; +select @b:=f2 from t1; +@b:=f2 +# +select if(@a=@b,"ok","wrong"); +if(@a=@b,"ok","wrong") +ok +insert into t1(f1) values (1) on duplicate key update f1="1"; +select @b:=f2 from t1; +@b:=f2 +# +select if(@a=@b,"ok","wrong"); +if(@a=@b,"ok","wrong") +ok +insert into t1(f1) select f1 from t1 on duplicate key update f1="1"; +select @b:=f2 from t1; +@b:=f2 +# +select if(@a=@b,"ok","wrong"); +if(@a=@b,"ok","wrong") +ok +drop table t1; End of 5.1 tests diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index eeac5e7c4fe..7717d2c92c1 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1419,7 +1419,8 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) goto before_trg_err; table->file->restore_auto_increment(prev_insert_id); - if ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) || + if ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ && + !bitmap_is_subset(table->write_set, table->read_set)) || compare_record(table)) { if ((error=table->file->ha_update_row(table->record[1], diff --git a/sql/sql_update.cc b/sql/sql_update.cc index ef1f46bfdd2..6927835762b 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -231,6 +231,17 @@ int mysql_update(THD *thd, if (cond_value == Item::COND_FALSE) limit= 0; // Impossible WHERE } + + /* + If a timestamp field settable on UPDATE is present then to avoid wrong + update force the table handler to retrieve write-only fields to be able + to compare records and detect data change. + */ + if (table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ && + table->timestamp_field && + (table->timestamp_field_type == TIMESTAMP_AUTO_SET_ON_UPDATE || + table->timestamp_field_type == TIMESTAMP_AUTO_SET_ON_BOTH)) + bitmap_union(table->read_set, table->write_set); // Don't count on usage of 'only index' when calculating which key to use table->covering_keys.clear_all();