diff options
author | Aleksey Midenkov <midenok@gmail.com> | 2020-07-20 18:28:07 +0300 |
---|---|---|
committer | Aleksey Midenkov <midenok@gmail.com> | 2020-07-20 18:28:07 +0300 |
commit | af57c658090cc52aa20e8cde7d24a563d73ff886 (patch) | |
tree | 8e0dadc7c3d5d42226b2e8b49b19a1613767d48b | |
parent | acc58fd83584c49049951a2c54c7f82a0c7ec412 (diff) | |
download | mariadb-git-af57c658090cc52aa20e8cde7d24a563d73ff886.tar.gz |
MDEV-22061 InnoDB: Assertion of missing row in sec index row_start upon REPLACE on a system-versioned table
make_versioned_helper() appended new update field unconditionally
while it should check if this field already exists in update vector.
Misc renames to conform versioning prefix. vers_update_fields() name
conforms with sql layer TABLE::vers_update_fields().
-rw-r--r-- | mysql-test/suite/versioning/r/update.result | 16 | ||||
-rw-r--r-- | mysql-test/suite/versioning/t/update.test | 20 | ||||
-rw-r--r-- | storage/innobase/include/row0upd.h | 18 | ||||
-rw-r--r-- | storage/innobase/row/row0mysql.cc | 16 | ||||
-rw-r--r-- | storage/innobase/row/row0upd.cc | 47 |
5 files changed, 83 insertions, 34 deletions
diff --git a/mysql-test/suite/versioning/r/update.result b/mysql-test/suite/versioning/r/update.result index f7901d11d2a..512e39355e2 100644 --- a/mysql-test/suite/versioning/r/update.result +++ b/mysql-test/suite/versioning/r/update.result @@ -319,3 +319,19 @@ create or replace table t1 (f point, key(f)) with system versioning engine=myisa update t1 set f = null where f = 'foo'; ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field drop table t1; +# +# MDEV-22061 InnoDB: Assertion of missing row in sec index row_start upon REPLACE on a system-versioned table +# +create or replace table t1 ( +a int, +b int, +row_start bigint(20) unsigned generated always as row start, +row_end bigint(20) unsigned generated always as row end, +unique key (b,row_end), +key (row_start), +period for system_time (row_start,row_end) +) engine=innodb with system versioning; +insert into t1 (a, b) values (1, 2); +replace into t1 (a, b) values (3, 2); +replace into t1 (a, b) values (4, 2); +drop table t1; diff --git a/mysql-test/suite/versioning/t/update.test b/mysql-test/suite/versioning/t/update.test index 5b0a9eb5c42..d2af2cac80c 100644 --- a/mysql-test/suite/versioning/t/update.test +++ b/mysql-test/suite/versioning/t/update.test @@ -245,4 +245,24 @@ update t1 set f = null where f = 'foo'; # cleanup drop table t1; +--echo # +--echo # MDEV-22061 InnoDB: Assertion of missing row in sec index row_start upon REPLACE on a system-versioned table +--echo # +create or replace table t1 ( + a int, + b int, + row_start bigint(20) unsigned generated always as row start, + row_end bigint(20) unsigned generated always as row end, + unique key (b,row_end), + key (row_start), + period for system_time (row_start,row_end) +) engine=innodb with system versioning; + +insert into t1 (a, b) values (1, 2); +replace into t1 (a, b) values (3, 2); +replace into t1 (a, b) values (4, 2); + +# cleanup +drop table t1; + source suite/versioning/common_finish.inc; diff --git a/storage/innobase/include/row0upd.h b/storage/innobase/include/row0upd.h index b34acfd8dc1..de97b379d9e 100644 --- a/storage/innobase/include/row0upd.h +++ b/storage/innobase/include/row0upd.h @@ -587,25 +587,25 @@ private: make_versioned_delete(). @param[in] trx transaction @param[in] vers_sys_idx table->row_start or table->row_end */ - void make_versioned_helper(const trx_t* trx, ulint idx); + void vers_update_fields(const trx_t *trx, ulint idx); public: /** Also set row_start = CURRENT_TIMESTAMP/trx->id @param[in] trx transaction */ - void make_versioned_update(const trx_t* trx) - { - make_versioned_helper(trx, table->vers_start); - } + void vers_make_update(const trx_t *trx) + { + vers_update_fields(trx, table->vers_start); + } /** Only set row_end = CURRENT_TIMESTAMP/trx->id. Do not touch other fields at all. @param[in] trx transaction */ - void make_versioned_delete(const trx_t* trx) - { + void vers_make_delete(const trx_t *trx) + { update->n_fields = 0; is_delete = VERSIONED_DELETE; - make_versioned_helper(trx, table->vers_end); - } + vers_update_fields(trx, table->vers_end); + } }; #define UPD_NODE_MAGIC_N 1579975 diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index b2256e9905d..d2fc63a5f38 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -1871,10 +1871,10 @@ row_update_for_mysql(row_prebuilt_t* prebuilt) if (prebuilt->versioned_write) { if (node->is_delete == VERSIONED_DELETE) { - node->make_versioned_delete(trx); - } else if (node->update->affects_versioned()) { - node->make_versioned_update(trx); - } + node->vers_make_delete(trx); + } else if (node->update->affects_versioned()) { + node->vers_make_update(trx); + } } for (;;) { @@ -2239,14 +2239,14 @@ row_update_cascade_for_mysql( if (table->versioned()) { if (node->is_delete == PLAIN_DELETE) { - node->make_versioned_delete(trx); - } else if (node->update->affects_versioned()) { + node->vers_make_delete(trx); + } else if (node->update->affects_versioned()) { dberr_t err = row_update_vers_insert(thr, node); if (err != DB_SUCCESS) { return err; } - node->make_versioned_update(trx); - } + node->vers_make_update(trx); + } } for (;;) { diff --git a/storage/innobase/row/row0upd.cc b/storage/innobase/row/row0upd.cc index 0f700c77c36..8ce47e74a1c 100644 --- a/storage/innobase/row/row0upd.cc +++ b/storage/innobase/row/row0upd.cc @@ -3486,32 +3486,45 @@ Supposed to be called only by make_versioned_update() and make_versioned_delete(). @param[in] trx transaction @param[in] vers_sys_idx table->row_start or table->row_end */ -void upd_node_t::make_versioned_helper(const trx_t* trx, ulint idx) +void upd_node_t::vers_update_fields(const trx_t *trx, ulint idx) { ut_ad(in_mysql_interface); // otherwise needs to recalculate // node->cmpl_info ut_ad(idx == table->vers_start || idx == table->vers_end); dict_index_t* clust_index = dict_table_get_first_index(table); - - /* row_create_update_node_for_mysql() pre-allocated this much. + const dict_col_t *col= dict_table_get_nth_col(table, idx); + ulint field_no= dict_col_get_clust_pos(col, clust_index); + upd_field_t *ufield; + + for (ulint i= 0; i < update->n_fields; ++i) + { + if (update->fields[i].field_no == field_no) + { + ufield= &update->fields[i]; + goto skip_append; + } + } + + /* row_create_update_node_for_mysql() pre-allocated this much. At least one PK column always remains unchanged. */ ut_ad(update->n_fields < ulint(table->n_cols + table->n_v_cols)); update->n_fields++; - upd_field_t* ufield = upd_get_nth_field(update, update->n_fields - 1); - const dict_col_t* col = dict_table_get_nth_col(table, idx); - - upd_field_set_field_no(ufield, dict_col_get_clust_pos(col, clust_index), - clust_index); - - char* where = reinterpret_cast<char*>(update->vers_sys_value); - if (col->vers_native()) { - mach_write_to_8(where, trx->id); - } else { - thd_get_query_start_data(trx->mysql_thd, where); - } - - dfield_set_data(&ufield->new_val, update->vers_sys_value, col->len); + ufield= upd_get_nth_field(update, update->n_fields - 1); + upd_field_set_field_no(ufield, field_no, clust_index); + +skip_append: + char *where= reinterpret_cast<char *>(update->vers_sys_value); + if (col->vers_native()) + { + mach_write_to_8(where, trx->id); + } + else + { + thd_get_query_start_data(trx->mysql_thd, where); + } + + dfield_set_data(&ufield->new_val, update->vers_sys_value, col->len); } |