diff options
-rw-r--r-- | mysql-test/suite/innodb/r/mvcc_secondary.result | 24 | ||||
-rw-r--r-- | mysql-test/suite/innodb/t/mvcc_secondary.test | 26 | ||||
-rw-r--r-- | storage/innobase/row/row0sel.cc | 90 |
3 files changed, 105 insertions, 35 deletions
diff --git a/mysql-test/suite/innodb/r/mvcc_secondary.result b/mysql-test/suite/innodb/r/mvcc_secondary.result new file mode 100644 index 00000000000..2289742e830 --- /dev/null +++ b/mysql-test/suite/innodb/r/mvcc_secondary.result @@ -0,0 +1,24 @@ +# +# MDEV-25459 MVCC read from index on CHAR or VARCHAR wrongly omits rows +# +CREATE TABLE t1 ( +pk int PRIMARY KEY, c varchar(255) UNIQUE, +d char(255), e varchar(255), f char(255), g char(255) +) ENGINE=InnoDB ROW_FORMAT=DYNAMIC DEFAULT CHARACTER SET ucs2; +INSERT INTO t1 VALUES +(1,REPEAT('c',248),REPEAT('a',106),REPEAT('b',220),REPEAT('x',14),''); +BEGIN; +UPDATE t1 SET c=REPEAT('d',170); +connect con1,localhost,root,,; +SELECT pk FROM t1 FORCE INDEX (c); +pk +1 +connection default; +COMMIT; +connection con1; +SELECT pk FROM t1 FORCE INDEX (c); +pk +1 +disconnect con1; +connection default; +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/t/mvcc_secondary.test b/mysql-test/suite/innodb/t/mvcc_secondary.test new file mode 100644 index 00000000000..93c91c40076 --- /dev/null +++ b/mysql-test/suite/innodb/t/mvcc_secondary.test @@ -0,0 +1,26 @@ +--source include/innodb_page_size_small.inc + +--echo # +--echo # MDEV-25459 MVCC read from index on CHAR or VARCHAR wrongly omits rows +--echo # + +CREATE TABLE t1 ( + pk int PRIMARY KEY, c varchar(255) UNIQUE, + d char(255), e varchar(255), f char(255), g char(255) +) ENGINE=InnoDB ROW_FORMAT=DYNAMIC DEFAULT CHARACTER SET ucs2; + +INSERT INTO t1 VALUES +(1,REPEAT('c',248),REPEAT('a',106),REPEAT('b',220),REPEAT('x',14),''); + +BEGIN; +UPDATE t1 SET c=REPEAT('d',170); + +connect (con1,localhost,root,,); +SELECT pk FROM t1 FORCE INDEX (c); +connection default; +COMMIT; +connection con1; +SELECT pk FROM t1 FORCE INDEX (c); +disconnect con1; +connection default; +DROP TABLE t1; diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 864f7bd54ab..86cb6b0ea1c 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -79,9 +79,9 @@ is alphabetically the same as the corresponding BLOB column in the clustered index record. NOTE: the comparison is NOT done as a binary comparison, but character fields are compared with collation! -@return TRUE if the columns are equal */ +@return whether the columns are equal */ static -ibool +bool row_sel_sec_rec_is_for_blob( /*========================*/ ulint mtype, /*!< in: main type */ @@ -100,19 +100,18 @@ row_sel_sec_rec_is_for_blob( const byte* sec_field, /*!< in: column in secondary index */ ulint sec_len, /*!< in: length of sec_field */ ulint prefix_len, /*!< in: index column prefix length - in bytes */ + in bytes, or 0 for full column */ dict_table_t* table) /*!< in: table */ { ulint len; - byte buf[REC_VERSION_56_MAX_INDEX_COL_LEN]; + byte buf[REC_VERSION_56_MAX_INDEX_COL_LEN + 1]; /* This function should never be invoked on an Antelope format table, because they should always contain enough prefix in the clustered index record. */ ut_ad(dict_table_get_format(table) >= UNIV_FORMAT_B); ut_a(clust_len >= BTR_EXTERN_FIELD_REF_SIZE); - ut_ad(prefix_len >= sec_len); - ut_ad(prefix_len > 0); + ut_ad(!prefix_len || prefix_len >= sec_len); ut_a(prefix_len <= sizeof buf); if (!memcmp(clust_field + clust_len - BTR_EXTERN_FIELD_REF_SIZE, @@ -121,11 +120,12 @@ row_sel_sec_rec_is_for_blob( This record should only be seen by recv_recovery_rollback_active() or any TRX_ISO_READ_UNCOMMITTED transactions. */ - return(FALSE); + return false; } len = btr_copy_externally_stored_field_prefix( - buf, prefix_len, dict_tf_get_page_size(table->flags), + buf, prefix_len ? prefix_len : sizeof buf, + dict_tf_get_page_size(table->flags), clust_field, clust_len); if (len == 0) { @@ -134,11 +134,18 @@ row_sel_sec_rec_is_for_blob( referring to this clustered index record, because btr_free_externally_stored_field() is called after all secondary index entries of the row have been purged. */ - return(FALSE); + return false; } - len = dtype_get_at_most_n_mbchars(prtype, mbminlen, mbmaxlen, - prefix_len, len, (const char*) buf); + if (prefix_len) { + len = dtype_get_at_most_n_mbchars(prtype, mbminlen, mbmaxlen, + prefix_len, len, + reinterpret_cast<const char*> + (buf)); + } else if (len >= sizeof buf) { + ut_ad("too long column" == 0); + return false; + } return(!cmp_data_data(mtype, prtype, buf, len, sec_field, sec_len)); } @@ -218,6 +225,8 @@ row_sel_sec_rec_is_for_clust_rec( ifield = dict_index_get_nth_field(sec_index, i); col = dict_field_get_col(ifield); + sec_field = rec_get_nth_field(sec_rec, sec_offs, i, &sec_len); + is_virtual = dict_col_is_virtual(col); /* For virtual column, its value will need to be @@ -250,43 +259,54 @@ row_sel_sec_rec_is_for_clust_rec( innobase_report_computed_value_failed(row); return DB_COMPUTE_VALUE_FAILED; } - clust_len = vfield->len; + len = clust_len = vfield->len; clust_field = static_cast<byte*>(vfield->data); } else { clust_pos = dict_col_get_clust_pos(col, clust_index); clust_field = rec_get_nth_field( clust_rec, clust_offs, clust_pos, &clust_len); - } - - sec_field = rec_get_nth_field(sec_rec, sec_offs, i, &sec_len); - - len = clust_len; - - if (ifield->prefix_len > 0 && len != UNIV_SQL_NULL - && sec_len != UNIV_SQL_NULL && !is_virtual) { + if (clust_len == UNIV_SQL_NULL) { + if (sec_len == UNIV_SQL_NULL) { + continue; + } + return DB_SUCCESS; + } + if (sec_len == UNIV_SQL_NULL) { + return DB_SUCCESS; + } + len = clust_len; if (rec_offs_nth_extern(clust_offs, clust_pos)) { len -= BTR_EXTERN_FIELD_REF_SIZE; } - len = dtype_get_at_most_n_mbchars( - col->prtype, col->mbminlen, col->mbmaxlen, - ifield->prefix_len, len, (char*) clust_field); - - if (rec_offs_nth_extern(clust_offs, clust_pos) - && len < sec_len) { - if (!row_sel_sec_rec_is_for_blob( - col->mtype, col->prtype, - col->mbminlen, col->mbmaxlen, - clust_field, clust_len, - sec_field, sec_len, - ifield->prefix_len, - clust_index->table)) { - return DB_SUCCESS; + if (ulint prefix_len = ifield->prefix_len) { + len = dtype_get_at_most_n_mbchars( + col->prtype, col->mbminlen, + col->mbmaxlen, prefix_len, len, + reinterpret_cast<const char*>( + clust_field)); + if (len < sec_len) { + goto check_for_blob; } + } else { +check_for_blob: + if (rec_offs_nth_extern(clust_offs, + clust_pos)) { + if (!row_sel_sec_rec_is_for_blob( + col->mtype, col->prtype, + col->mbminlen, + col->mbmaxlen, + clust_field, clust_len, + sec_field, sec_len, + prefix_len, + clust_index->table)) { + return DB_SUCCESS; + } - continue; + continue; + } } } |