From 0932c5804d720e1e1ee1d632ad424883dddfeea0 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 2 Apr 2020 20:48:38 +0300 Subject: [PATCH] MDEV-20515 multi-update tries to position updated table by null reference Cause Join tmp table inserts null row because of OUTER JOIN, that's expected. Since `multi_update::prepare2()` converted `Item_temptable_rowid` into `Item_field` (28dbdf3) `multi_update::send_data()` accesses join tmp record directly and treats it as a normal row ignoring null status of ref field. NULL ref field is then treated as normal in `multi_update::do_updates()` which tries to position updated table by reference 0. Note that reference 0 may be valid reference and the first row of table can be wrongly updated (see multi_update.test). Fix Do not add row into multi-update tmp table in case of null ref field. Join tmp table does not have null_row status at this time (as well as `STATUS_NULL_ROW`) and cannot be skipped by these properties (see first comment in multi_update::send_data()). But it has all null fields (including the ref field). --- mysql-test/main/multi_update.result | 17 +++++++++++++++++ mysql-test/main/multi_update.test | 15 +++++++++++++++ mysql-test/suite/versioning/r/partition.result | 11 +++++++++++ mysql-test/suite/versioning/t/partition.test | 16 ++++++++++++++++ sql/sql_update.cc | 4 ++++ 5 files changed, 63 insertions(+) diff --git a/mysql-test/main/multi_update.result b/mysql-test/main/multi_update.result index 9d59244dd8b..b3edeb77c44 100644 --- a/mysql-test/main/multi_update.result +++ b/mysql-test/main/multi_update.result @@ -1081,3 +1081,20 @@ b c 2 0 drop view v1; drop table t0, t1,t2; +# +# MDEV-20515 multi-update tries to position updated table by null reference +# +create or replace table t1 (a int); +insert into t1 values (0), (2); +create or replace table t2 (b int); +insert into t2 values (1), (2); +select * from t1 left join t2 on a = b order by b; +a b +0 NULL +2 2 +update t1 left join t2 on a = b set b= 3 order by b; +select * from t2; +b +1 +3 +drop tables t1, t2; diff --git a/mysql-test/main/multi_update.test b/mysql-test/main/multi_update.test index b09d02cb5aa..8a32f626818 100644 --- a/mysql-test/main/multi_update.test +++ b/mysql-test/main/multi_update.test @@ -1019,3 +1019,18 @@ update v1,t0 set c=1 where b<3 and x=c order by x,b limit 1; select * from v1; drop view v1; drop table t0, t1,t2; + +--echo # +--echo # MDEV-20515 multi-update tries to position updated table by null reference +--echo # +create or replace table t1 (a int); +insert into t1 values (0), (2); + +create or replace table t2 (b int); +insert into t2 values (1), (2); + +select * from t1 left join t2 on a = b order by b; +update t1 left join t2 on a = b set b= 3 order by b; +select * from t2; + +drop tables t1, t2; diff --git a/mysql-test/suite/versioning/r/partition.result b/mysql-test/suite/versioning/r/partition.result index a75fa49c610..c4fedb08ad6 100644 --- a/mysql-test/suite/versioning/r/partition.result +++ b/mysql-test/suite/versioning/r/partition.result @@ -615,6 +615,17 @@ insert into t2 values (1),(2); update t1, t2 set a = 1; drop table t1, t2; # +# MDEV-20515 multi-update tries to position updated table by null reference +# +create or replace table t1 (a int); +insert into t1 values (0), (1); +create or replace table t2 (b int) with system versioning +partition by system_time +(partition p1 history, partition pn current); +insert into t2 values (0), (2); +update t1 left join t2 on a > b set b= 2 order by b; +drop table t1, t2; +# # MDEV-17091 Assertion `old_part_id == m_last_part' failed in # ha_partition::update_row or `part_id == m_last_part' in # ha_partition::delete_row upon UPDATE/DELETE after dropping versioning diff --git a/mysql-test/suite/versioning/t/partition.test b/mysql-test/suite/versioning/t/partition.test index 282af4b5bc7..6fee6f43847 100644 --- a/mysql-test/suite/versioning/t/partition.test +++ b/mysql-test/suite/versioning/t/partition.test @@ -567,6 +567,22 @@ update t1, t2 set a = 1; # cleanup drop table t1, t2; +--echo # +--echo # MDEV-20515 multi-update tries to position updated table by null reference +--echo # +create or replace table t1 (a int); +insert into t1 values (0), (1); + +create or replace table t2 (b int) with system versioning +partition by system_time +(partition p1 history, partition pn current); + +insert into t2 values (0), (2); +update t1 left join t2 on a > b set b= 2 order by b; + +# cleanup +drop table t1, t2; + --source suite/versioning/common_finish.inc --echo # --echo # MDEV-17091 Assertion `old_part_id == m_last_part' failed in diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 10b62af3d6f..56ff0ee4b34 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -2467,6 +2467,9 @@ int multi_update::send_data(List ¬_used_values) TABLE *tmp_table= tmp_tables[offset]; if (copy_funcs(tmp_table_param[offset].items_to_copy, thd)) DBUG_RETURN(1); + /* rowid field is NULL if join tmp table has null row from outer join */ + if (tmp_table->field[0]->is_null()) + continue; /* Store regular updated fields in the row. */ DBUG_ASSERT(1 + unupdated_check_opt_tables.elements == tmp_table_param[offset].func_count); @@ -2671,6 +2674,7 @@ int multi_update::do_updates() uint field_num= 0; do { + DBUG_ASSERT(!tmp_table->field[field_num]->is_null()); if (unlikely((local_error= tbl->file->ha_rnd_pos(tbl->record[0], (uchar *) tmp_table->