summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVlad Lesin <vlad_lesin@mail.ru>2022-02-09 16:50:28 +0300
committerVlad Lesin <vlad_lesin@mail.ru>2022-02-11 12:26:27 +0300
commit3b10e8f80c434277207db9a613d9110c8283ea6a (patch)
tree8cc7974b9600f17c6a1f04cf26181430a28e695b
parent3a52569499e2f0c4d1f25db1e81617a9d9755400 (diff)
downloadmariadb-git-3b10e8f80c434277207db9a613d9110c8283ea6a.tar.gz
MDEV-27746 Wrong comparision of BLOB's empty preffix with non-preffixed BLOB causes rows count mismatch for clustered and secondary indexes during non-locking read
row_sel_sec_rec_is_for_clust_rec() treats empty BLOB prefix field in secondary index as a field equal to any external BLOB field in clustered index. Row_sel_get_clust_rec_for_mysql::operator() doesn't zerro out clustered record pointer in row_search_mvcc(), and row_search_mvcc() thinks that delete-marked secondary index record has visible for "CHECK TABLE"'s read view old-versioned clustered index record, and row_scan_index_for_mysql() counts it as a row. The fix is to execute row_sel_sec_rec_is_for_blob() in row_sel_sec_rec_is_for_clust_rec() if clustered field contains BLOB's reference.
-rw-r--r--mysql-test/suite/innodb/r/blob_cmp_empty.result19
-rw-r--r--mysql-test/suite/innodb/t/blob_cmp_empty.test26
-rw-r--r--storage/innobase/row/row0sel.cc8
3 files changed, 52 insertions, 1 deletions
diff --git a/mysql-test/suite/innodb/r/blob_cmp_empty.result b/mysql-test/suite/innodb/r/blob_cmp_empty.result
new file mode 100644
index 00000000000..24ae8e1c01e
--- /dev/null
+++ b/mysql-test/suite/innodb/r/blob_cmp_empty.result
@@ -0,0 +1,19 @@
+connect prevent_purge,localhost,root,,;
+start transaction with consistent snapshot;
+connection default;
+SET @fill_amount = (@@innodb_page_size / 2 ) + 1;
+CREATE TABLE t1 (col_text TEXT NOT NULL, KEY (col_text(9))) ENGINE=InnoDB;
+INSERT INTO t1 (col_text) VALUES (REPEAT('x', @fill_amount));
+UPDATE t1 SET col_text='';
+UPDATE t1 SET col_text=REPEAT('y', @fill_amount);
+connect con1,localhost,root,,;
+SET @fill_amount = (@@innodb_page_size / 2 ) + 1;
+BEGIN;
+INSERT INTO t1 (col_text) VALUES (REPEAT('z', @fill_amount));
+connection default;
+CHECK TABLE t1;
+Table Op Msg_type Msg_text
+test.t1 check status OK
+disconnect con1;
+disconnect prevent_purge;
+DROP TABLE t1;
diff --git a/mysql-test/suite/innodb/t/blob_cmp_empty.test b/mysql-test/suite/innodb/t/blob_cmp_empty.test
new file mode 100644
index 00000000000..f41ac79b16f
--- /dev/null
+++ b/mysql-test/suite/innodb/t/blob_cmp_empty.test
@@ -0,0 +1,26 @@
+--source include/innodb_row_format.inc
+--source include/count_sessions.inc
+--connect(prevent_purge,localhost,root,,)
+start transaction with consistent snapshot;
+
+--connection default
+SET @fill_amount = (@@innodb_page_size / 2 ) + 1;
+CREATE TABLE t1 (col_text TEXT NOT NULL, KEY (col_text(9))) ENGINE=InnoDB;
+
+INSERT INTO t1 (col_text) VALUES (REPEAT('x', @fill_amount));
+UPDATE t1 SET col_text='';
+UPDATE t1 SET col_text=REPEAT('y', @fill_amount);
+
+--connect(con1,localhost,root,,)
+SET @fill_amount = (@@innodb_page_size / 2 ) + 1;
+BEGIN;
+INSERT INTO t1 (col_text) VALUES (REPEAT('z', @fill_amount));
+
+--connection default
+# If the bug is not fixed, CHECK TABLE will complain about wrong secondary index
+# rows count
+CHECK TABLE t1;
+--disconnect con1
+--disconnect prevent_purge
+DROP TABLE t1;
+--source include/wait_until_count_sessions.inc
diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc
index 87c67edff4b..d513ab09dcf 100644
--- a/storage/innobase/row/row0sel.cc
+++ b/storage/innobase/row/row0sel.cc
@@ -352,11 +352,16 @@ row_sel_sec_rec_is_for_clust_rec(
}
len = clust_len;
+ ulint prefix_len = ifield->prefix_len;
if (rec_offs_nth_extern(clust_offs, clust_pos)) {
+ /* BLOB can contain prefix. */
len -= BTR_EXTERN_FIELD_REF_SIZE;
+ if (!len) {
+ goto compare_blobs;
+ }
}
- if (ulint prefix_len = ifield->prefix_len) {
+ if (prefix_len) {
len = dtype_get_at_most_n_mbchars(
col->prtype, col->mbminlen,
col->mbmaxlen, prefix_len, len,
@@ -369,6 +374,7 @@ row_sel_sec_rec_is_for_clust_rec(
check_for_blob:
if (rec_offs_nth_extern(clust_offs,
clust_pos)) {
+compare_blobs:
if (!row_sel_sec_rec_is_for_blob(
col->mtype, col->prtype,
col->mbminlen,