Commit 9b38ed4c authored by Aleksey Midenkov's avatar Aleksey Midenkov

MDEV-23446 UPDATE does not insert history row if the row is not changed

Add history row outside of compare_record() check. For TRX_ID
versioning we have to fail can_compare_record to force InnoDB update
which adds history row; and there in ha_innobase::update_row() is
additional "row changed" check where we force history row anyway.
parent 932ec586
...@@ -370,3 +370,32 @@ insert into t1 (a) values (1), (2); ...@@ -370,3 +370,32 @@ insert into t1 (a) values (1), (2);
update ignore t1 set a= 3; update ignore t1 set a= 3;
delete history from t1; delete history from t1;
drop table t1; drop table t1;
#
# MDEV-23446 UPDATE does not insert history row if the row is not changed
#
create table t1 (
a int,
row_start SYS_DATATYPE as row start invisible,
row_end SYS_DATATYPE as row end invisible,
period for system_time (row_start, row_end)) with system versioning;
insert into t1 values (1);
update t1 set a= 1;
select *, check_row(row_start, row_end) from t1 for system_time all order by row_end;
a check_row(row_start, row_end)
1 HISTORICAL ROW
1 CURRENT ROW
# multi-update
create or replace table t2 like t1;
create or replace table t3 like t1;
insert into t2 values (1);
insert into t3 values (1);
update t2, t3 set t2.a= 1, t3.a= 1 where t2.a = t3.a;
select *, check_row(row_start, row_end) from t2 for system_time all order by row_end;
a check_row(row_start, row_end)
1 HISTORICAL ROW
1 CURRENT ROW
select *, check_row(row_start, row_end) from t2 for system_time all order by row_end;
a check_row(row_start, row_end)
1 HISTORICAL ROW
1 CURRENT ROW
drop tables t1, t2, t3;
...@@ -301,4 +301,29 @@ delete history from t1; ...@@ -301,4 +301,29 @@ delete history from t1;
# cleanup # cleanup
drop table t1; drop table t1;
--echo #
--echo # MDEV-23446 UPDATE does not insert history row if the row is not changed
--echo #
replace_result $sys_datatype_expl SYS_DATATYPE;
eval create table t1 (
a int,
row_start $sys_datatype_expl as row start invisible,
row_end $sys_datatype_expl as row end invisible,
period for system_time (row_start, row_end)) with system versioning;
insert into t1 values (1);
update t1 set a= 1;
select *, check_row(row_start, row_end) from t1 for system_time all order by row_end;
--echo # multi-update
create or replace table t2 like t1;
create or replace table t3 like t1;
insert into t2 values (1);
insert into t3 values (1);
update t2, t3 set t2.a= 1, t3.a= 1 where t2.a = t3.a;
select *, check_row(row_start, row_end) from t2 for system_time all order by row_end;
select *, check_row(row_start, row_end) from t2 for system_time all order by row_end;
# cleanup
drop tables t1, t2, t3;
source suite/versioning/common_finish.inc; source suite/versioning/common_finish.inc;
...@@ -52,8 +52,9 @@ ...@@ -52,8 +52,9 @@
compare_record(TABLE*). compare_record(TABLE*).
*/ */
bool records_are_comparable(const TABLE *table) { bool records_are_comparable(const TABLE *table) {
return ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) == 0) || return !table->versioned(VERS_TRX_ID) &&
bitmap_is_subset(table->write_set, table->read_set); (((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) == 0) ||
bitmap_is_subset(table->write_set, table->read_set));
} }
...@@ -960,25 +961,27 @@ int mysql_update(THD *thd, ...@@ -960,25 +961,27 @@ int mysql_update(THD *thd,
} }
else if (likely(!error)) else if (likely(!error))
{ {
if (has_vers_fields && table->versioned()) if (has_vers_fields && table->versioned(VERS_TRX_ID))
{
if (table->versioned(VERS_TIMESTAMP))
{
store_record(table, record[2]);
table->mark_columns_per_binlog_row_image();
error= vers_insert_history_row(table);
restore_record(table, record[2]);
}
if (likely(!error))
updated_sys_ver++; updated_sys_ver++;
}
if (likely(!error))
updated++; updated++;
} }
if (unlikely(error) && if (unlikely(error) &&
(!ignore || table->file->is_fatal_error(error, HA_CHECK_ALL))) (!ignore || table->file->is_fatal_error(error, HA_CHECK_ALL)))
{ {
goto error;
}
}
if (likely(!error) && has_vers_fields && table->versioned(VERS_TIMESTAMP))
{
store_record(table, record[2]);
table->mark_columns_per_binlog_row_image();
error= vers_insert_history_row(table);
restore_record(table, record[2]);
if (unlikely(error))
{
error:
/* /*
If (ignore && error is ignorable) we don't have to If (ignore && error is ignorable) we don't have to
do anything; otherwise... do anything; otherwise...
...@@ -993,6 +996,7 @@ int mysql_update(THD *thd, ...@@ -993,6 +996,7 @@ int mysql_update(THD *thd,
error= 1; error= 1;
break; break;
} }
updated_sys_ver++;
} }
if (table->triggers && if (table->triggers &&
...@@ -2410,6 +2414,7 @@ int multi_update::send_data(List<Item> &not_used_values) ...@@ -2410,6 +2414,7 @@ int multi_update::send_data(List<Item> &not_used_values)
if (!ignore || if (!ignore ||
table->file->is_fatal_error(error, HA_CHECK_ALL)) table->file->is_fatal_error(error, HA_CHECK_ALL))
{ {
error:
/* /*
If (ignore && error == is ignorable) we don't have to If (ignore && error == is ignorable) we don't have to
do anything; otherwise... do anything; otherwise...
...@@ -2431,19 +2436,8 @@ int multi_update::send_data(List<Item> &not_used_values) ...@@ -2431,19 +2436,8 @@ int multi_update::send_data(List<Item> &not_used_values)
error= 0; error= 0;
updated--; updated--;
} }
else if (has_vers_fields && table->versioned()) else if (has_vers_fields && table->versioned(VERS_TRX_ID))
{ {
if (table->versioned(VERS_TIMESTAMP))
{
store_record(table, record[2]);
if (vers_insert_history_row(table))
{
restore_record(table, record[2]);
error= 1;
break;
}
restore_record(table, record[2]);
}
updated_sys_ver++; updated_sys_ver++;
} }
/* non-transactional or transactional table got modified */ /* non-transactional or transactional table got modified */
...@@ -2457,6 +2451,17 @@ int multi_update::send_data(List<Item> &not_used_values) ...@@ -2457,6 +2451,17 @@ int multi_update::send_data(List<Item> &not_used_values)
} }
} }
} }
if (has_vers_fields && table->versioned(VERS_TIMESTAMP))
{
store_record(table, record[2]);
if (vers_insert_history_row(table))
{
restore_record(table, record[2]);
goto error;
}
restore_record(table, record[2]);
updated_sys_ver++;
}
if (table->triggers && if (table->triggers &&
unlikely(table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, unlikely(table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
TRG_ACTION_AFTER, TRUE))) TRG_ACTION_AFTER, TRUE)))
......
...@@ -8799,6 +8799,20 @@ ha_innobase::update_row( ...@@ -8799,6 +8799,20 @@ ha_innobase::update_row(
MySQL that the row is not really updated and it MySQL that the row is not really updated and it
should not increase the count of updated rows. should not increase the count of updated rows.
This is fix for http://bugs.mysql.com/29157 */ This is fix for http://bugs.mysql.com/29157 */
if (m_prebuilt->versioned_write
&& thd_sql_command(m_user_thd) != SQLCOM_ALTER_TABLE
/* Multiple UPDATE of same rows in single transaction create
historical rows only once. */
&& trx->id != table->vers_start_id()) {
error = row_insert_for_mysql((byte*) old_row,
m_prebuilt,
ROW_INS_HISTORICAL);
if (error != DB_SUCCESS) {
goto func_exit;
}
innobase_srv_conc_exit_innodb(m_prebuilt);
innobase_active_small();
}
DBUG_RETURN(HA_ERR_RECORD_IS_THE_SAME); DBUG_RETURN(HA_ERR_RECORD_IS_THE_SAME);
} else { } else {
const bool vers_set_fields = m_prebuilt->versioned_write const bool vers_set_fields = m_prebuilt->versioned_write
......
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