From 7229107e3e2c59febd10b40b243011e8bba94f99 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Tue, 15 Jun 2021 13:13:01 +0530 Subject: [PATCH] MDEV-25872 InnoDB: Assertion failure in row_merge_read_clustered_index upon ALTER on table with indexed virtual columns - InnoDB fails to check DB_COMPUTE_VALUE_FAILED error in row_merge_read_clustered_index() and wrongly asserts that the buffer shouldn't be ran out of memory. Alter table should give warning when the column value is being truncated. --- .../suite/gcol/r/gcol_keys_innodb.result | 2 ++ .../suite/gcol/r/innodb_virtual_index.result | 20 ++++++++++++++ .../suite/gcol/t/innodb_virtual_index.opt | 1 + .../suite/gcol/t/innodb_virtual_index.test | 21 +++++++++++++++ mysql-test/suite/innodb/r/innodb-alter.result | 8 ++++-- sql/sql_table.cc | 8 ++++++ storage/innobase/row/row0merge.cc | 27 +++++++++---------- 7 files changed, 71 insertions(+), 16 deletions(-) create mode 100644 mysql-test/suite/gcol/t/innodb_virtual_index.opt diff --git a/mysql-test/suite/gcol/r/gcol_keys_innodb.result b/mysql-test/suite/gcol/r/gcol_keys_innodb.result index 5a29c64bc20..37b350c0c32 100644 --- a/mysql-test/suite/gcol/r/gcol_keys_innodb.result +++ b/mysql-test/suite/gcol/r/gcol_keys_innodb.result @@ -691,6 +691,8 @@ a b c 1 127 0 SET STATEMENT sql_mode = 'NO_ENGINE_SUBSTITUTION' FOR ALTER TABLE t ADD UNIQUE INDEX (c(1)); +Warnings: +Warning 1264 Out of range value for column 'b' at row 1 SELECT * FROM t WHERE c = '0'; a b c 1 127 0 diff --git a/mysql-test/suite/gcol/r/innodb_virtual_index.result b/mysql-test/suite/gcol/r/innodb_virtual_index.result index 20311a21136..ff339280e8a 100644 --- a/mysql-test/suite/gcol/r/innodb_virtual_index.result +++ b/mysql-test/suite/gcol/r/innodb_virtual_index.result @@ -266,3 +266,23 @@ CHECK TABLE t1; Table Op Msg_type Msg_text test.t1 check status OK DROP TABLE t1; +# +# MDEV-25872 InnoDB: Assertion failure in row_merge_read_clustered_index +# upon ALTER on table with indexed virtual columns +# +CREATE TABLE t1 ( +id BIGINT AUTO_INCREMENT PRIMARY KEY, +a INT, +va INT ZEROFILL AS (a) VIRTUAL, +b TIMESTAMP, +c CHAR(204), +vc CHAR(8), +KEY(vc,c(64),b,va) +) ENGINE=InnoDB CHARACTER SET utf32; +INSERT INTO t1 (id) SELECT NULL FROM seq_1_to_75; +INSERT IGNORE INTO t1 (id, a) VALUES (NULL, -1); +Warnings: +Warning 1264 Out of range value for column 'va' at row 1 +ALTER TABLE t1 FORCE; +ERROR 22003: Out of range value for column 'va' at row 1 +DROP TABLE t1; diff --git a/mysql-test/suite/gcol/t/innodb_virtual_index.opt b/mysql-test/suite/gcol/t/innodb_virtual_index.opt new file mode 100644 index 00000000000..c3f4a891cca --- /dev/null +++ b/mysql-test/suite/gcol/t/innodb_virtual_index.opt @@ -0,0 +1 @@ +--innodb_sort_buffer_size=64k diff --git a/mysql-test/suite/gcol/t/innodb_virtual_index.test b/mysql-test/suite/gcol/t/innodb_virtual_index.test index 353841840dc..9e765ca104c 100644 --- a/mysql-test/suite/gcol/t/innodb_virtual_index.test +++ b/mysql-test/suite/gcol/t/innodb_virtual_index.test @@ -1,4 +1,5 @@ --source include/have_innodb.inc +--source include/have_sequence.inc # Ensure that the history list length will actually be decremented by purge. SET @saved_frequency = @@GLOBAL.innodb_purge_rseg_truncate_frequency; @@ -281,3 +282,23 @@ ROLLBACK; SELECT * FROM t1; CHECK TABLE t1; DROP TABLE t1; + +--echo # +--echo # MDEV-25872 InnoDB: Assertion failure in row_merge_read_clustered_index +--echo # upon ALTER on table with indexed virtual columns +--echo # + +CREATE TABLE t1 ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + a INT, + va INT ZEROFILL AS (a) VIRTUAL, + b TIMESTAMP, + c CHAR(204), + vc CHAR(8), + KEY(vc,c(64),b,va) +) ENGINE=InnoDB CHARACTER SET utf32; +INSERT INTO t1 (id) SELECT NULL FROM seq_1_to_75; +INSERT IGNORE INTO t1 (id, a) VALUES (NULL, -1); +--error ER_WARN_DATA_OUT_OF_RANGE +ALTER TABLE t1 FORCE; +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/r/innodb-alter.result b/mysql-test/suite/innodb/r/innodb-alter.result index 80cf14c1725..53e6c95af0f 100644 --- a/mysql-test/suite/innodb/r/innodb-alter.result +++ b/mysql-test/suite/innodb/r/innodb-alter.result @@ -1041,7 +1041,9 @@ CREATE TABLE t1 (a INT NOT NULL DEFAULT 0) ENGINE=InnoDB; iNSERT INTO t1 VALUES (10); ALTER TABLE t1 ADD b DATE NOT NULL DEFAULT if(unix_timestamp()>1,TIMESTAMP'2001-01-01 10:20:30',0); affected rows: 0 -info: Records: 0 Duplicates: 0 Warnings: 0 +info: Records: 0 Duplicates: 0 Warnings: 1 +Warnings: +Note 1265 Data truncated for column 'b' at row 1 SELECT * FROM t1; a b 10 2001-01-01 @@ -1050,7 +1052,9 @@ CREATE TABLE t1 (a INT NOT NULL DEFAULT 0) ENGINE=InnoDB; iNSERT INTO t1 VALUES (10); ALTER TABLE t1 ADD b TIME NOT NULL DEFAULT if(unix_timestamp()>1,TIMESTAMP'2001-01-01 10:20:30',0); affected rows: 0 -info: Records: 0 Duplicates: 0 Warnings: 0 +info: Records: 0 Duplicates: 0 Warnings: 1 +Warnings: +Note 1265 Data truncated for column 'b' at row 1 SELECT * FROM t1; a b 10 10:20:30 diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 3a3a903bc35..9556cb9f136 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -9680,9 +9680,17 @@ do_continue:; if (use_inplace) { table->s->frm_image= &frm; + enum_check_fields save_count_cuted_fields= thd->count_cuted_fields; + /* + Set the truncated column values of thd as warning for alter table. + */ + thd->count_cuted_fields= CHECK_FIELD_WARN; int res= mysql_inplace_alter_table(thd, table_list, table, altered_table, &ha_alter_info, inplace_supported, &target_mdl_request, &alter_ctx); + + thd->count_cuted_fields= save_count_cuted_fields; + my_free(const_cast(frm.str)); if (res) diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc index e3b3f2c2762..93ea4d2d743 100644 --- a/storage/innobase/row/row0merge.cc +++ b/storage/innobase/row/row0merge.cc @@ -524,7 +524,9 @@ row_merge_buf_add( DBUG_ENTER("row_merge_buf_add"); if (buf->n_tuples >= buf->max_tuples) { - DBUG_RETURN(0); +error: + n_row_added = 0; + goto end; } DBUG_EXECUTE_IF( @@ -845,11 +847,6 @@ end: if (vcol_storage.innobase_record) innobase_free_row_for_vcol(&vcol_storage); DBUG_RETURN(n_row_added); - -error: - if (vcol_storage.innobase_record) - innobase_free_row_for_vcol(&vcol_storage); - DBUG_RETURN(0); } /*************************************************************//** @@ -2567,16 +2564,18 @@ write_buffers: new_table, psort_info, row, ext, &doc_id, conv_heap, &err, &v_heap, eval_table, trx)))) { - /* An empty buffer should have enough - room for at least one record. */ - ut_error; + /* An empty buffer should have enough + room for at least one record. */ + ut_ad(err == DB_COMPUTE_VALUE_FAILED + || err == DB_OUT_OF_MEMORY + || err == DB_TOO_BIG_RECORD); + } else if (err == DB_SUCCESS) { + file->n_rec += rows_added; + continue; } - if (err != DB_SUCCESS) { - break; - } - - file->n_rec += rows_added; + trx->error_key_num = i; + break; } }