From 175c9fe1d57daae2cf5fbc514cfffc27a55afcb0 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 3 Aug 2021 09:50:22 +0200 Subject: [PATCH 01/20] cleanup: specifying plugin dependencies in CMakeLists.txt 1. rename option DEPENDENCIES in MYSQL_ADD_PLUGIN() to DEPENDS to be consistent with other cmake commands and macros 2. use this DEPENDS option in plugins 3. add dependencies to the plugin embedded target too 4. plugins don't need to add GenError dependency explicitly, all plugins depend on it automatically --- cmake/plugin.cmake | 14 +++++++------- storage/innobase/CMakeLists.txt | 4 ---- storage/perfschema/CMakeLists.txt | 5 +++-- storage/xtradb/CMakeLists.txt | 1 - 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/cmake/plugin.cmake b/cmake/plugin.cmake index 7a8eb97891e..4184dc46648 100644 --- a/cmake/plugin.cmake +++ b/cmake/plugin.cmake @@ -26,13 +26,13 @@ INCLUDE(CMakeParseArguments) # [STATIC_OUTPUT_NAME static_name] # [RECOMPILE_FOR_EMBEDDED] # [LINK_LIBRARIES lib1...libN] -# [DEPENDENCIES target1...targetN] +# [DEPENDS target1...targetN] MACRO(MYSQL_ADD_PLUGIN) CMAKE_PARSE_ARGUMENTS(ARG "STORAGE_ENGINE;STATIC_ONLY;MODULE_ONLY;MANDATORY;DEFAULT;DISABLED;RECOMPILE_FOR_EMBEDDED;CLIENT" "MODULE_OUTPUT_NAME;STATIC_OUTPUT_NAME;COMPONENT;CONFIG" - "LINK_LIBRARIES;DEPENDENCIES" + "LINK_LIBRARIES;DEPENDS" ${ARGN} ) IF(NOT WITHOUT_SERVER OR ARG_CLIENT) @@ -111,8 +111,8 @@ MACRO(MYSQL_ADD_PLUGIN) ENDIF() UNSET(${with_var} CACHE) - IF(NOT ARG_DEPENDENCIES) - SET(ARG_DEPENDENCIES) + IF(NOT ARG_DEPENDS) + SET(ARG_DEPENDS) ENDIF() IF(NOT ARG_MODULE_OUTPUT_NAME) @@ -138,7 +138,7 @@ MACRO(MYSQL_ADD_PLUGIN) ADD_LIBRARY(${target} STATIC ${SOURCES}) DTRACE_INSTRUMENT(${target}) - ADD_DEPENDENCIES(${target} GenError ${ARG_DEPENDENCIES}) + ADD_DEPENDENCIES(${target} GenError ${ARG_DEPENDS}) RESTRICT_SYMBOL_EXPORTS(${target}) IF(WITH_EMBEDDED_SERVER) # Embedded library should contain PIC code and be linkable @@ -152,7 +152,7 @@ MACRO(MYSQL_ADD_PLUGIN) SET_TARGET_PROPERTIES(${target}_embedded PROPERTIES COMPILE_DEFINITIONS "EMBEDDED_LIBRARY") ENDIF() - ADD_DEPENDENCIES(${target}_embedded GenError) + ADD_DEPENDENCIES(${target}_embedded GenError ${ARG_DEPENDS}) ENDIF() ENDIF() @@ -213,7 +213,7 @@ MACRO(MYSQL_ADD_PLUGIN) TARGET_LINK_LIBRARIES (${target} "-Wl,--no-undefined") ENDIF() - ADD_DEPENDENCIES(${target} GenError ${ARG_DEPENDENCIES}) + ADD_DEPENDENCIES(${target} GenError ${ARG_DEPENDS}) SET_TARGET_PROPERTIES(${target} PROPERTIES OUTPUT_NAME "${ARG_MODULE_OUTPUT_NAME}") diff --git a/storage/innobase/CMakeLists.txt b/storage/innobase/CMakeLists.txt index 08e8e3ff5ab..b965c4fbb45 100644 --- a/storage/innobase/CMakeLists.txt +++ b/storage/innobase/CMakeLists.txt @@ -188,7 +188,3 @@ IF(MSVC) ENDIF() ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/extra/mariabackup ${CMAKE_BINARY_DIR}/extra/mariabackup) - -IF(TARGET innobase) - ADD_DEPENDENCIES(innobase GenError) -ENDIF() diff --git a/storage/perfschema/CMakeLists.txt b/storage/perfschema/CMakeLists.txt index 56083f24223..7ed365ea175 100644 --- a/storage/perfschema/CMakeLists.txt +++ b/storage/perfschema/CMakeLists.txt @@ -187,9 +187,10 @@ table_session_connect_attrs.cc table_session_account_connect_attrs.cc ) -MYSQL_ADD_PLUGIN(perfschema ${PERFSCHEMA_SOURCES} STORAGE_ENGINE DEFAULT STATIC_ONLY) +MYSQL_ADD_PLUGIN(perfschema ${PERFSCHEMA_SOURCES} STORAGE_ENGINE DEFAULT + STATIC_ONLY DEPENDS GenServerSource) + IF (TARGET perfschema) - ADD_DEPENDENCIES(perfschema GenServerSource) IF(WITH_UNIT_TESTS) ADD_SUBDIRECTORY(unittest) ENDIF(WITH_UNIT_TESTS) diff --git a/storage/xtradb/CMakeLists.txt b/storage/xtradb/CMakeLists.txt index bed5946541c..3408ff69a13 100644 --- a/storage/xtradb/CMakeLists.txt +++ b/storage/xtradb/CMakeLists.txt @@ -524,7 +524,6 @@ IF(TARGET xtradb) IF(NOT XTRADB_OK) MESSAGE(FATAL_ERROR "Percona XtraDB is not supported on this platform") ENDIF() - ADD_DEPENDENCIES(xtradb GenError) ENDIF() ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/extra/mariabackup ${CMAKE_BINARY_DIR}/extra/mariabackup) From fa6eaead21eaec6ec9a463f3b890fe7537b2681c Mon Sep 17 00:00:00 2001 From: Yongxin Xu <55976466+yongxin-xu@users.noreply.github.com> Date: Thu, 5 Aug 2021 11:21:59 +0800 Subject: [PATCH 02/20] MDEV-24523 Execution of JSON_REPLACE failed on Spider JSON_REPLACE() function executed with an error on Spider SE. This patch fixes the problem, and it also fixes the MDEV-24541. The problem is that Item_func_json_insert::func_name() returns the wrong function name "json_update". The Spider SE reconstructs a query based on the return value in some cases. Thus, if the return value is wrong, the Spider SE may generate a wrong query. --- sql/item_jsonfunc.h | 2 +- .../bugfix/include/mdev_24523_deinit.inc | 9 +++ .../spider/bugfix/include/mdev_24523_init.inc | 31 +++++++++ .../spider/bugfix/r/mdev_24523.result | 58 ++++++++++++++++ .../mysql-test/spider/bugfix/t/mdev_24523.cnf | 3 + .../spider/bugfix/t/mdev_24523.test | 66 +++++++++++++++++++ 6 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 storage/spider/mysql-test/spider/bugfix/include/mdev_24523_deinit.inc create mode 100644 storage/spider/mysql-test/spider/bugfix/include/mdev_24523_init.inc create mode 100644 storage/spider/mysql-test/spider/bugfix/r/mdev_24523.result create mode 100644 storage/spider/mysql-test/spider/bugfix/t/mdev_24523.cnf create mode 100644 storage/spider/mysql-test/spider/bugfix/t/mdev_24523.test diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h index c703533f799..798c5d502db 100644 --- a/sql/item_jsonfunc.h +++ b/sql/item_jsonfunc.h @@ -375,7 +375,7 @@ public: const char *func_name() const { return mode_insert ? - (mode_replace ? "json_set" : "json_insert") : "json_update"; + (mode_replace ? "json_set" : "json_insert") : "json_replace"; } Item *get_copy(THD *thd) { return get_item_copy(thd, this); } diff --git a/storage/spider/mysql-test/spider/bugfix/include/mdev_24523_deinit.inc b/storage/spider/mysql-test/spider/bugfix/include/mdev_24523_deinit.inc new file mode 100644 index 00000000000..e8d30523978 --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/include/mdev_24523_deinit.inc @@ -0,0 +1,9 @@ +--let $MASTER_1_COMMENT_P_2_1= $MASTER_1_COMMENT_P_2_1_BACKUP +--let $CHILD2_1_CREATE_TABLES= $CHILD2_1_CREATE_TABLES_BACKUP +--disable_warnings +--disable_query_log +--disable_result_log +--source ../t/test_deinit.inc +--enable_result_log +--enable_query_log +--enable_warnings diff --git a/storage/spider/mysql-test/spider/bugfix/include/mdev_24523_init.inc b/storage/spider/mysql-test/spider/bugfix/include/mdev_24523_init.inc new file mode 100644 index 00000000000..989faa54c16 --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/include/mdev_24523_init.inc @@ -0,0 +1,31 @@ +--disable_warnings +--disable_query_log +--disable_result_log +--source ../t/test_init.inc +--enable_result_log +--enable_query_log +--enable_warnings +--let $MASTER_1_COMMENT_P_2_1_BACKUP= $MASTER_1_COMMENT_P_2_1 +let $MASTER_1_COMMENT_P_2_1= + PARTITION BY RANGE(i) ( + PARTITION pt1 VALUES LESS THAN (5) COMMENT='srv "s_2_1", table "ta_r2"', + PARTITION pt2 VALUES LESS THAN (10) COMMENT='srv "s_2_1", table "ta_r3"', + PARTITION pt3 VALUES LESS THAN MAXVALUE COMMENT='srv "s_2_1", table "ta_r4"' + ); +--let $CHILD2_1_CREATE_TABLES_BACKUP= $CHILD2_1_CREATE_TABLES +let $CHILD2_1_CREATE_TABLES= + CREATE TABLE ta_r2 ( + i INT, + j JSON, + PRIMARY KEY(i) + ) $CHILD2_1_ENGINE $CHILD2_1_CHARSET $STR_SEMICOLON + CREATE TABLE ta_r3 ( + i INT, + j JSON, + PRIMARY KEY(i) + ) $CHILD2_1_ENGINE $CHILD2_1_CHARSET $STR_SEMICOLON + CREATE TABLE ta_r4 ( + i INT, + j JSON, + PRIMARY KEY(i) + ) $CHILD2_1_ENGINE $CHILD2_1_CHARSET; diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_24523.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_24523.result new file mode 100644 index 00000000000..0b3d6c3142b --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_24523.result @@ -0,0 +1,58 @@ +for master_1 +for child2 +child2_1 +child2_2 +child2_3 +for child3 + +this test is for MDEV-24523 + +drop and create databases +connection master_1; +CREATE DATABASE auto_test_local; +USE auto_test_local; +connection child2_1; +CREATE DATABASE auto_test_remote; +USE auto_test_remote; + +create table and insert +connection child2_1; +CHILD2_1_CREATE_TABLES +connection master_1; +CREATE TABLE tbl_a ( +i INT, +j JSON, +PRIMARY KEY(i) +) ENGINE=Spider PARTITION BY RANGE(i) ( +PARTITION pt1 VALUES LESS THAN (5) COMMENT='srv "s_2_1", table "ta_r2"', +PARTITION pt2 VALUES LESS THAN (10) COMMENT='srv "s_2_1", table "ta_r3"', +PARTITION pt3 VALUES LESS THAN MAXVALUE COMMENT='srv "s_2_1", table "ta_r4"' + ) +INSERT INTO tbl_a VALUES (1, '{ "a": 1, "b": [2, 3]}'); + +test 1 +connection master_1; +UPDATE tbl_a SET j = JSON_REPLACE(j, '$.a', 10, '$.c', '[1, 2]'); +SELECT * FROM tbl_a; +i j +1 {"a": 10, "b": [2, 3]} +TRUNCATE TABLE tbl_a; +INSERT INTO tbl_a VALUES (1, '{ "a": 1, "b": [2, 3]}'); +UPDATE tbl_a SET j = JSON_REPLACE(j, '$.a', 10, '$.b', '[1, 2]'); +SELECT * FROM tbl_a; +i j +1 {"a": 10, "b": "[1, 2]"} + +deinit +connection master_1; +DROP DATABASE IF EXISTS auto_test_local; +connection child2_1; +DROP DATABASE IF EXISTS auto_test_remote; +for master_1 +for child2 +child2_1 +child2_2 +child2_3 +for child3 + +end of test diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_24523.cnf b/storage/spider/mysql-test/spider/bugfix/t/mdev_24523.cnf new file mode 100644 index 00000000000..05dfd8a0bce --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_24523.cnf @@ -0,0 +1,3 @@ +!include include/default_mysqld.cnf +!include ../my_1_1.cnf +!include ../my_2_1.cnf diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_24523.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_24523.test new file mode 100644 index 00000000000..00c0c873f20 --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_24523.test @@ -0,0 +1,66 @@ +--source ../include/mdev_24523_init.inc +--echo +--echo this test is for MDEV-24523 +--echo +--echo drop and create databases + +--connection master_1 +--disable_warnings +CREATE DATABASE auto_test_local; +USE auto_test_local; + +--connection child2_1 +CREATE DATABASE auto_test_remote; +USE auto_test_remote; +--enable_warnings + +--echo +--echo create table and insert + +--connection child2_1 +--disable_query_log +--disable_ps_protocol +echo CHILD2_1_CREATE_TABLES; +eval $CHILD2_1_CREATE_TABLES; +--enable_ps_protocol +--enable_query_log + +--connection master_1 +--disable_query_log +echo CREATE TABLE tbl_a ( + i INT, + j JSON, + PRIMARY KEY(i) +) $MASTER_1_ENGINE $MASTER_1_COMMENT_P_2_1; +eval CREATE TABLE tbl_a ( + i INT, + j JSON, + PRIMARY KEY(i) +) $MASTER_1_ENGINE $MASTER_1_COMMENT_P_2_1; +--enable_query_log +INSERT INTO tbl_a VALUES (1, '{ "a": 1, "b": [2, 3]}'); + +--echo +--echo test 1 + +--connection master_1 +UPDATE tbl_a SET j = JSON_REPLACE(j, '$.a', 10, '$.c', '[1, 2]'); +SELECT * FROM tbl_a; +TRUNCATE TABLE tbl_a; +INSERT INTO tbl_a VALUES (1, '{ "a": 1, "b": [2, 3]}'); +UPDATE tbl_a SET j = JSON_REPLACE(j, '$.a', 10, '$.b', '[1, 2]'); +SELECT * FROM tbl_a; +--echo +--echo deinit +--disable_warnings + +--connection master_1 +DROP DATABASE IF EXISTS auto_test_local; + +--connection child2_1 +DROP DATABASE IF EXISTS auto_test_remote; + +--enable_warnings +--source ../include/mdev_24523_deinit.inc +--echo +--echo end of test From b8deb02859bbb869159134fa20ec14cfd875e11b Mon Sep 17 00:00:00 2001 From: Daniel Bartholomew Date: Thu, 5 Aug 2021 09:11:46 -0400 Subject: [PATCH 03/20] bump the VERSION --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 456c22d6a3f..494d4b1db72 100644 --- a/VERSION +++ b/VERSION @@ -1,3 +1,3 @@ MYSQL_VERSION_MAJOR=10 MYSQL_VERSION_MINOR=2 -MYSQL_VERSION_PATCH=40 +MYSQL_VERSION_PATCH=41 From d9526ae60820d8b8d511f94edfff3ea2888766ca Mon Sep 17 00:00:00 2001 From: Daniel Bartholomew Date: Thu, 5 Aug 2021 09:35:44 -0400 Subject: [PATCH 04/20] bump the VERSION --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 53a575fa7f8..429bbd2a872 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ MYSQL_VERSION_MAJOR=10 MYSQL_VERSION_MINOR=3 -MYSQL_VERSION_PATCH=31 +MYSQL_VERSION_PATCH=32 SERVER_MATURITY=stable From 160d97a4aaacbefb7f91a7e30a79b4d7937468a8 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 5 Aug 2021 23:48:02 +0300 Subject: [PATCH 05/20] MDEV-18734 ASAN heap-use-after-free upon sorting by blob column from partitioned table ha_partition stores records in array of m_ordered_rec_buffer and uses it for prio queue in ordered index scan. When the records are restored from the array the blob buffers may be already freed or rewritten. The solution is to take temporary ownership of cached blob buffers via String::swap(). When the record is restored from m_ordered_rec_buffer the ownership is returned to table fields. Cleanups: init_record_priority_queue(): removed needless !m_ordered_rec_buffer check as there is same assertion few lines before. dbug_print_row() for arbitrary row pointer --- .../federated/federated_partition.result | 36 +++ .../suite/federated/federated_partition.test | 25 +++ mysql-test/suite/vcol/r/partition.result | 73 +++++++ mysql-test/suite/vcol/t/partition.test | 48 ++++ sql/field.cc | 1 + sql/field.h | 33 +++ sql/filesort.cc | 9 + sql/ha_partition.cc | 206 +++++++++++++----- sql/ha_partition.h | 11 + 9 files changed, 382 insertions(+), 60 deletions(-) diff --git a/mysql-test/suite/federated/federated_partition.result b/mysql-test/suite/federated/federated_partition.result index a2d5fcffd9b..cce5e3cf3e6 100644 --- a/mysql-test/suite/federated/federated_partition.result +++ b/mysql-test/suite/federated/federated_partition.result @@ -46,6 +46,42 @@ connection slave; drop table federated.t1_1; drop table federated.t1_2; End of 5.1 tests +# +# MDEV-18734 ASAN heap-use-after-free upon sorting by blob column from partitioned table +# +connection slave; +use federated; +create table t1_1 (x int, b text, key(x)); +create table t1_2 (x int, b text, key(x)); +connection master; +create table t1 (x int, b text, key(x)) engine=federated +partition by range columns (x) ( +partition p1 values less than (40) connection='mysql://root@127.0.0.1:SLAVE_PORT/federated/t1_1', +partition pn values less than (maxvalue) connection='mysql://root@127.0.0.1:SLAVE_PORT/federated/t1_2' +); +insert t1 values (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8); +insert t1 select x + 8, x + 8 from t1; +insert t1 select x + 16, x + 16 from t1; +insert t1 select x + 49, repeat(x + 49, 100) from t1; +flush tables; +# This produces wrong result before MDEV-17573 +select x, left(b, 10) from t1 where x > 30 and x < 60 order by b; +x left(b, 10) +31 31 +32 32 +50 5050505050 +51 5151515151 +52 5252525252 +53 5353535353 +54 5454545454 +55 5555555555 +56 5656565656 +57 5757575757 +58 5858585858 +59 5959595959 +drop table t1; +connection slave; +drop table t1_1, t1_2; connection master; DROP TABLE IF EXISTS federated.t1; DROP DATABASE IF EXISTS federated; diff --git a/mysql-test/suite/federated/federated_partition.test b/mysql-test/suite/federated/federated_partition.test index ef1e27ec505..c5c8433b4cf 100644 --- a/mysql-test/suite/federated/federated_partition.test +++ b/mysql-test/suite/federated/federated_partition.test @@ -50,4 +50,29 @@ drop table federated.t1_2; --echo End of 5.1 tests +--echo # +--echo # MDEV-18734 ASAN heap-use-after-free upon sorting by blob column from partitioned table +--echo # +connection slave; +use federated; +create table t1_1 (x int, b text, key(x)); +create table t1_2 (x int, b text, key(x)); +connection master; +--replace_result $SLAVE_MYPORT SLAVE_PORT +eval create table t1 (x int, b text, key(x)) engine=federated + partition by range columns (x) ( + partition p1 values less than (40) connection='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t1_1', + partition pn values less than (maxvalue) connection='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t1_2' +); +insert t1 values (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8); +insert t1 select x + 8, x + 8 from t1; +insert t1 select x + 16, x + 16 from t1; +insert t1 select x + 49, repeat(x + 49, 100) from t1; +flush tables; +--echo # This produces wrong result before MDEV-17573 +select x, left(b, 10) from t1 where x > 30 and x < 60 order by b; +drop table t1; +connection slave; +drop table t1_1, t1_2; + source include/federated_cleanup.inc; diff --git a/mysql-test/suite/vcol/r/partition.result b/mysql-test/suite/vcol/r/partition.result index bd1353fa145..d7c5052b72a 100644 --- a/mysql-test/suite/vcol/r/partition.result +++ b/mysql-test/suite/vcol/r/partition.result @@ -28,3 +28,76 @@ set statement sql_mode= '' for update t1 set i= 1, v= 2; Warnings: Warning 1906 The value specified for generated column 'v' in table 't1' has been ignored drop table t1; +# +# MDEV-18734 ASAN heap-use-after-free in my_strnxfrm_simple_internal upon update on versioned partitioned table +# +# Cover queue_fix() in ha_partition::handle_ordered_index_scan() +create or replace table t1 ( +x int auto_increment primary key, +b text, v mediumtext as (b) virtual, +index (v(10)) +) partition by range columns (x) ( +partition p1 values less than (3), +partition p2 values less than (6), +partition p3 values less than (9), +partition p4 values less than (12), +partition p5 values less than (15), +partition p6 values less than (17), +partition p7 values less than (19), +partition p8 values less than (21), +partition p9 values less than (23), +partition p10 values less than (25), +partition p11 values less than (27), +partition p12 values less than (29), +partition p13 values less than (31), +partition p14 values less than (33), +partition p15 values less than (35), +partition pn values less than (maxvalue)); +insert into t1 (b) values +(repeat('q', 8192)), (repeat('z', 8192)), (repeat('a', 8192)), (repeat('b', 8192)), +(repeat('x', 8192)), (repeat('y', 8192)); +insert t1 (b) select b from t1; +insert t1 (b) select b from t1; +insert t1 (b) select b from t1; +insert t1 (b) select b from t1; +select x, left(b, 10), left(v, 10) from t1 where x > 30 and x < 60 order by v; +x left(b, 10) left(v, 10) +33 aaaaaaaaaa aaaaaaaaaa +39 aaaaaaaaaa aaaaaaaaaa +45 aaaaaaaaaa aaaaaaaaaa +51 aaaaaaaaaa aaaaaaaaaa +57 aaaaaaaaaa aaaaaaaaaa +34 bbbbbbbbbb bbbbbbbbbb +40 bbbbbbbbbb bbbbbbbbbb +46 bbbbbbbbbb bbbbbbbbbb +52 bbbbbbbbbb bbbbbbbbbb +58 bbbbbbbbbb bbbbbbbbbb +31 qqqqqqqqqq qqqqqqqqqq +37 qqqqqqqqqq qqqqqqqqqq +43 qqqqqqqqqq qqqqqqqqqq +49 qqqqqqqqqq qqqqqqqqqq +55 qqqqqqqqqq qqqqqqqqqq +35 xxxxxxxxxx xxxxxxxxxx +41 xxxxxxxxxx xxxxxxxxxx +47 xxxxxxxxxx xxxxxxxxxx +53 xxxxxxxxxx xxxxxxxxxx +59 xxxxxxxxxx xxxxxxxxxx +36 yyyyyyyyyy yyyyyyyyyy +42 yyyyyyyyyy yyyyyyyyyy +48 yyyyyyyyyy yyyyyyyyyy +54 yyyyyyyyyy yyyyyyyyyy +32 zzzzzzzzzz zzzzzzzzzz +38 zzzzzzzzzz zzzzzzzzzz +44 zzzzzzzzzz zzzzzzzzzz +50 zzzzzzzzzz zzzzzzzzzz +56 zzzzzzzzzz zzzzzzzzzz +update t1 set b= 'bar' where v > 'a' limit 20; +drop table t1; +# Cover return_top_record() in ha_partition::handle_ordered_index_scan() +create table t1 (x int primary key, b tinytext, v text as (b) virtual) +partition by range columns (x) ( +partition p1 values less than (4), +partition pn values less than (maxvalue)); +insert into t1 (x, b) values (1, ''), (2, ''), (3, 'a'), (4, 'b'); +update t1 set b= 'bar' where x > 0 order by v limit 2; +drop table t1; diff --git a/mysql-test/suite/vcol/t/partition.test b/mysql-test/suite/vcol/t/partition.test index 889724fb1c5..408990b20a6 100644 --- a/mysql-test/suite/vcol/t/partition.test +++ b/mysql-test/suite/vcol/t/partition.test @@ -30,3 +30,51 @@ subpartition by hash(v) subpartitions 3 ( insert t1 set i= 0; set statement sql_mode= '' for update t1 set i= 1, v= 2; drop table t1; + +--echo # +--echo # MDEV-18734 ASAN heap-use-after-free in my_strnxfrm_simple_internal upon update on versioned partitioned table +--echo # +--echo # Cover queue_fix() in ha_partition::handle_ordered_index_scan() +create or replace table t1 ( + x int auto_increment primary key, + b text, v mediumtext as (b) virtual, + index (v(10)) +) partition by range columns (x) ( + partition p1 values less than (3), + partition p2 values less than (6), + partition p3 values less than (9), + partition p4 values less than (12), + partition p5 values less than (15), + partition p6 values less than (17), + partition p7 values less than (19), + partition p8 values less than (21), + partition p9 values less than (23), + partition p10 values less than (25), + partition p11 values less than (27), + partition p12 values less than (29), + partition p13 values less than (31), + partition p14 values less than (33), + partition p15 values less than (35), + partition pn values less than (maxvalue)); +insert into t1 (b) values +(repeat('q', 8192)), (repeat('z', 8192)), (repeat('a', 8192)), (repeat('b', 8192)), +(repeat('x', 8192)), (repeat('y', 8192)); + +insert t1 (b) select b from t1; +insert t1 (b) select b from t1; +insert t1 (b) select b from t1; +insert t1 (b) select b from t1; + +select x, left(b, 10), left(v, 10) from t1 where x > 30 and x < 60 order by v; +update t1 set b= 'bar' where v > 'a' limit 20; + +drop table t1; + +--echo # Cover return_top_record() in ha_partition::handle_ordered_index_scan() +create table t1 (x int primary key, b tinytext, v text as (b) virtual) +partition by range columns (x) ( + partition p1 values less than (4), + partition pn values less than (maxvalue)); +insert into t1 (x, b) values (1, ''), (2, ''), (3, 'a'), (4, 'b'); +update t1 set b= 'bar' where x > 0 order by v limit 2; +drop table t1; diff --git a/sql/field.cc b/sql/field.cc index 074de35e0cf..4e6bc6b8341 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -8318,6 +8318,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) copy_length= copier.well_formed_copy(field_charset, (char*) value.ptr(), new_length, cs, from, length); + value.length(copy_length); Field_blob::store_length(copy_length); bmove(ptr+packlength,(uchar*) &tmp,sizeof(char*)); diff --git a/sql/field.h b/sql/field.h index f63fb670211..6d8e2aecd6e 100644 --- a/sql/field.h +++ b/sql/field.h @@ -3465,6 +3465,12 @@ public: uchar *new_ptr, uint32 length, uchar *new_null_ptr, uint new_null_bit); void sql_type(String &str) const; + /** + Copy blob buffer into internal storage "value" and update record pointer. + + @retval true Memory allocation error + @retval false Success + */ inline bool copy() { uchar *tmp= get_ptr(); @@ -3477,6 +3483,33 @@ public: memcpy(ptr+packlength, &tmp, sizeof(char*)); return 0; } + void swap(String &inout, bool set_read_value) + { + if (set_read_value) + read_value.swap(inout); + else + value.swap(inout); + } + /** + Return pointer to blob cache or NULL if not cached. + */ + String * cached(bool *set_read_value) + { + char *tmp= (char *) get_ptr(); + if (!value.is_empty() && tmp == value.ptr()) + { + *set_read_value= false; + return &value; + } + + if (!read_value.is_empty() && tmp == read_value.ptr()) + { + *set_read_value= true; + return &read_value; + } + + return NULL; + } /* store value for the duration of the current read record */ inline void swap_value_and_read_value() { diff --git a/sql/filesort.cc b/sql/filesort.cc index d76c39c3bd4..8b019caf8f5 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -608,6 +608,15 @@ const char* dbug_print_table_row(TABLE *table) } +const char* dbug_print_row(TABLE *table, uchar *rec) +{ + table->move_fields(table->field, rec, table->record[0]); + const char* ret= dbug_print_table_row(table); + table->move_fields(table->field, table->record[0], rec); + return ret; +} + + /* Print a text, SQL-like record representation into dbug trace. diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 6f50d078dff..4345f9eb9ca 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -5103,59 +5103,69 @@ bool ha_partition::init_record_priority_queue() /* Initialize the ordered record buffer. */ - if (!m_ordered_rec_buffer) + uint alloc_len; + uint used_parts= bitmap_bits_set(&m_part_info->read_partitions); + /* Allocate record buffer for each used partition. */ + m_priority_queue_rec_len= m_rec_length + ORDERED_REC_OFFSET; + if (!m_using_extended_keys) + m_priority_queue_rec_len += m_file[0]->ref_length; + alloc_len= used_parts * m_priority_queue_rec_len; + /* Allocate a key for temporary use when setting up the scan. */ + alloc_len+= table_share->max_key_length; + Ordered_blob_storage **blob_storage; + Ordered_blob_storage *objs; + const size_t n_all= used_parts * table->s->blob_fields; + + if (!my_multi_malloc(MYF(MY_WME), &m_ordered_rec_buffer, alloc_len, + &blob_storage, n_all * sizeof(Ordered_blob_storage *), + &objs, n_all * sizeof(Ordered_blob_storage), NULL)) + DBUG_RETURN(true); + + /* + We set-up one record per partition and each record has 2 bytes in + front where the partition id is written. This is used by ordered + index_read. + We also set-up a reference to the first record for temporary use in + setting up the scan. + */ + char *ptr= (char*) m_ordered_rec_buffer; + uint i; + for (i= bitmap_get_first_set(&m_part_info->read_partitions); + i < m_tot_parts; + i= bitmap_get_next_set(&m_part_info->read_partitions, i)) { - uint alloc_len; - uint used_parts= bitmap_bits_set(&m_part_info->read_partitions); - /* Allocate record buffer for each used partition. */ - m_priority_queue_rec_len= m_rec_length + PARTITION_BYTES_IN_POS; - if (!m_using_extended_keys) - m_priority_queue_rec_len += m_file[0]->ref_length; - alloc_len= used_parts * m_priority_queue_rec_len; - /* Allocate a key for temporary use when setting up the scan. */ - alloc_len+= table_share->max_key_length; + DBUG_PRINT("info", ("init rec-buf for part %u", i)); + if (table->s->blob_fields) + { + for (uint j= 0; j < table->s->blob_fields; ++j, ++objs) + blob_storage[j]= new (objs) Ordered_blob_storage; + *((Ordered_blob_storage ***) ptr)= blob_storage; + blob_storage+= table->s->blob_fields; + } + int2store(ptr + sizeof(String **), i); + ptr+= m_priority_queue_rec_len; + } + m_start_key.key= (const uchar*)ptr; - if (!(m_ordered_rec_buffer= (uchar*)my_malloc(alloc_len, MYF(MY_WME)))) - DBUG_RETURN(true); - - /* - We set-up one record per partition and each record has 2 bytes in - front where the partition id is written. This is used by ordered - index_read. - We also set-up a reference to the first record for temporary use in - setting up the scan. - */ - char *ptr= (char*) m_ordered_rec_buffer; - uint i; - for (i= bitmap_get_first_set(&m_part_info->read_partitions); - i < m_tot_parts; - i= bitmap_get_next_set(&m_part_info->read_partitions, i)) - { - DBUG_PRINT("info", ("init rec-buf for part %u", i)); - int2store(ptr, i); - ptr+= m_priority_queue_rec_len; - } - m_start_key.key= (const uchar*)ptr; - - /* Initialize priority queue, initialized to reading forward. */ - int (*cmp_func)(void *, uchar *, uchar *); - void *cmp_arg; - if (!m_using_extended_keys) - { - cmp_func= cmp_key_rowid_part_id; - cmp_arg= (void*)this; - } - else - { - cmp_func= cmp_key_part_id; - cmp_arg= (void*)m_curr_key_info; - } - if (init_queue(&m_queue, used_parts, 0, 0, cmp_func, cmp_arg, 0, 0)) - { - my_free(m_ordered_rec_buffer); - m_ordered_rec_buffer= NULL; - DBUG_RETURN(true); - } + /* Initialize priority queue, initialized to reading forward. */ + int (*cmp_func)(void *, uchar *, uchar *); + void *cmp_arg; + if (!m_using_extended_keys) + { + cmp_func= cmp_key_rowid_part_id; + cmp_arg= (void*)this; + } + else + { + cmp_func= cmp_key_part_id; + cmp_arg= (void*)m_curr_key_info; + } + if (init_queue(&m_queue, used_parts, ORDERED_PART_NUM_OFFSET, + 0, cmp_func, cmp_arg, 0, 0)) + { + my_free(m_ordered_rec_buffer); + m_ordered_rec_buffer= NULL; + DBUG_RETURN(true); } DBUG_RETURN(false); } @@ -5170,6 +5180,20 @@ void ha_partition::destroy_record_priority_queue() DBUG_ENTER("ha_partition::destroy_record_priority_queue"); if (m_ordered_rec_buffer) { + if (table->s->blob_fields) + { + char *ptr= (char *) m_ordered_rec_buffer; + for (uint i= bitmap_get_first_set(&m_part_info->read_partitions); + i < m_tot_parts; + i= bitmap_get_next_set(&m_part_info->read_partitions, i)) + { + Ordered_blob_storage **blob_storage= *((Ordered_blob_storage ***) ptr); + for (uint b= 0; b < table->s->blob_fields; ++b) + blob_storage[b]->blob.free(); + ptr+= m_priority_queue_rec_len; + } + } + delete_queue(&m_queue); my_free(m_ordered_rec_buffer); m_ordered_rec_buffer= NULL; @@ -5394,7 +5418,7 @@ static int cmp_part_ids(uchar *ref1, uchar *ref2) extern "C" int cmp_key_part_id(void *key_p, uchar *ref1, uchar *ref2) { int res; - if ((res= key_rec_cmp(key_p, ref1 + PARTITION_BYTES_IN_POS, + if ((res= key_rec_cmp(key_p, ref1 + PARTITION_BYTES_IN_POS, ref2 + PARTITION_BYTES_IN_POS))) { return res; @@ -6133,8 +6157,8 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order) { DBUG_PRINT("info", ("reading from part %u (scan_type: %u)", i, m_index_scan_type)); - DBUG_ASSERT(i == uint2korr(part_rec_buf_ptr)); - uchar *rec_buf_ptr= part_rec_buf_ptr + PARTITION_BYTES_IN_POS; + DBUG_ASSERT(i == uint2korr(part_rec_buf_ptr + ORDERED_PART_NUM_OFFSET)); + uchar *rec_buf_ptr= part_rec_buf_ptr + ORDERED_REC_OFFSET; int error; handler *file= m_file[i]; @@ -6162,6 +6186,7 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order) error= file->read_range_first(m_start_key.key? &m_start_key: NULL, end_range, eq_range, TRUE); memcpy(rec_buf_ptr, table->record[0], m_rec_length); + reverse_order= FALSE; break; } @@ -6181,6 +6206,11 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order) Initialize queue without order first, simply insert */ queue_element(&m_queue, j++)= part_rec_buf_ptr; + if (table->s->blob_fields) + { + Ordered_blob_storage **storage= *((Ordered_blob_storage ***) part_rec_buf_ptr); + swap_blobs(rec_buf_ptr, storage, false); + } } else if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) { @@ -6229,10 +6259,15 @@ void ha_partition::return_top_record(uchar *buf) { uint part_id; uchar *key_buffer= queue_top(&m_queue); - uchar *rec_buffer= key_buffer + PARTITION_BYTES_IN_POS; + uchar *rec_buffer= key_buffer + ORDERED_REC_OFFSET; - part_id= uint2korr(key_buffer); + part_id= uint2korr(key_buffer + ORDERED_PART_NUM_OFFSET); memcpy(buf, rec_buffer, m_rec_length); + if (table->s->blob_fields) + { + Ordered_blob_storage **storage= *((Ordered_blob_storage ***) key_buffer); + swap_blobs(buf, storage, true); + } m_last_part= part_id; m_top_entry= part_id; } @@ -6268,7 +6303,7 @@ int ha_partition::handle_ordered_index_scan_key_not_found() This partition is used and did return HA_ERR_KEY_NOT_FOUND in index_read_map. */ - curr_rec_buf= part_buf + PARTITION_BYTES_IN_POS; + curr_rec_buf= part_buf + ORDERED_REC_OFFSET; error= m_file[i]->ha_index_next(curr_rec_buf); /* HA_ERR_KEY_NOT_FOUND is not allowed from index_next! */ DBUG_ASSERT(error != HA_ERR_KEY_NOT_FOUND); @@ -6293,6 +6328,48 @@ int ha_partition::handle_ordered_index_scan_key_not_found() } +void ha_partition::swap_blobs(uchar * rec_buf, Ordered_blob_storage ** storage, bool restore) +{ + uint *ptr, *end; + uint blob_n= 0; + table->move_fields(table->field, rec_buf, table->record[0]); + for (ptr= table->s->blob_field, end= ptr + table->s->blob_fields; + ptr != end; ++ptr, ++blob_n) + { + DBUG_ASSERT(*ptr < table->s->fields); + Field_blob *blob= (Field_blob*) table->field[*ptr]; + DBUG_ASSERT(blob->flags & BLOB_FLAG); + DBUG_ASSERT(blob->field_index == *ptr); + if (!bitmap_is_set(table->read_set, *ptr) || blob->is_null()) + continue; + + Ordered_blob_storage &s= *storage[blob_n]; + + if (restore) + { + /* + We protect only blob cache (value or read_value). If the cache was + empty that doesn't mean the blob was empty. Blobs allocated by a + storage engine should work just fine. + */ + if (!s.blob.is_empty()) + blob->swap(s.blob, s.set_read_value); + } + else + { + bool set_read_value; + String *cached= blob->cached(&set_read_value); + if (cached) + { + cached->swap(s.blob); + s.set_read_value= set_read_value; + } + } + } + table->move_fields(table->field, table->record[0], rec_buf); +} + + /* Common routine to handle index_next with ordered results @@ -6311,7 +6388,8 @@ int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same) { int error; uint part_id= m_top_entry; - uchar *rec_buf= queue_top(&m_queue) + PARTITION_BYTES_IN_POS; + uchar *part_rec_buf_ptr= queue_top(&m_queue); + uchar *rec_buf= part_rec_buf_ptr + ORDERED_REC_OFFSET; handler *file; DBUG_ENTER("ha_partition::handle_ordered_next"); @@ -6354,7 +6432,15 @@ int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same) if (m_index_scan_type == partition_read_range) { error= file->read_range_next(); - memcpy(rec_buf, table->record[0], m_rec_length); + if (!error) + { + memcpy(rec_buf, table->record[0], m_rec_length); + if (table->s->blob_fields) + { + Ordered_blob_storage **storage= *((Ordered_blob_storage ***) part_rec_buf_ptr); + swap_blobs(rec_buf, storage, false); + } + } } else if (!is_next_same) error= file->ha_index_next(rec_buf); @@ -6410,7 +6496,7 @@ int ha_partition::handle_ordered_prev(uchar *buf) { int error; uint part_id= m_top_entry; - uchar *rec_buf= queue_top(&m_queue) + PARTITION_BYTES_IN_POS; + uchar *rec_buf= queue_top(&m_queue) + ORDERED_REC_OFFSET; handler *file= m_file[part_id]; DBUG_ENTER("ha_partition::handle_ordered_prev"); diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 34174089bf6..2fccc37ec1f 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -21,7 +21,17 @@ #include "sql_partition.h" /* part_id_range, partition_element */ #include "queues.h" /* QUEUE */ +struct Ordered_blob_storage +{ + String blob; + bool set_read_value; + Ordered_blob_storage() : set_read_value(false) + {} +}; + #define PARTITION_BYTES_IN_POS 2 +#define ORDERED_PART_NUM_OFFSET sizeof(Ordered_blob_storage **) +#define ORDERED_REC_OFFSET (ORDERED_PART_NUM_OFFSET + PARTITION_BYTES_IN_POS) /** Struct used for partition_name_hash */ @@ -630,6 +640,7 @@ private: int handle_ordered_next(uchar * buf, bool next_same); int handle_ordered_prev(uchar * buf); void return_top_record(uchar * buf); + void swap_blobs(uchar* rec_buf, Ordered_blob_storage ** storage, bool restore); public: /* ------------------------------------------------------------------------- From f725020ff7224f4d641cf0e11fe2a83f383e2e7b Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 9 Aug 2021 11:52:03 +0200 Subject: [PATCH 06/20] Fix cmake warning caused by 751ebe44fda4deb715fc2235548517c287f2a559 CMake Warning (dev) at cmake/plugin.cmake:288 (GET_TARGET_PROPERTY): Policy CMP0045 is not set: Error on non-existent target... Also, fix condition argument in ADD_FEATURE_INFO. It is not a string but boolean --- cmake/plugin.cmake | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/cmake/plugin.cmake b/cmake/plugin.cmake index 4184dc46648..8b050a808ab 100644 --- a/cmake/plugin.cmake +++ b/cmake/plugin.cmake @@ -257,15 +257,20 @@ MACRO(MYSQL_ADD_PLUGIN) INSTALL_MYSQL_TEST("${CMAKE_CURRENT_SOURCE_DIR}/mysql-test/" "plugin/${subpath}") ENDIF() - GET_TARGET_PROPERTY(plugin_type ${target} TYPE) - STRING(REGEX REPLACE "_LIBRARY$" "" plugin_type ${plugin_type}) - STRING(REGEX REPLACE "^NO$" "" plugin_type ${plugin_type}) - IF(ARG_STORAGE_ENGINE) - ADD_FEATURE_INFO(${plugin} PLUGIN_${plugin} "Storage Engine ${plugin_type}") - ELSEIF(ARG_CLIENT) - ADD_FEATURE_INFO(${plugin} PLUGIN_${plugin} "Client plugin ${plugin_type}") + IF(TARGET ${target}) + GET_TARGET_PROPERTY(plugin_type ${target} TYPE) + STRING(REPLACE "_LIBRARY" "" plugin_type ${plugin_type}) + SET(have_target 1) ELSE() - ADD_FEATURE_INFO(${plugin} PLUGIN_${plugin} "Server plugin ${plugin_type}") + SET(plugin_type) + SET(have_target 0) + ENDIF() + IF(ARG_STORAGE_ENGINE) + ADD_FEATURE_INFO(${plugin} ${have_target} "Storage Engine ${plugin_type}") + ELSEIF(ARG_CLIENT) + ADD_FEATURE_INFO(${plugin} ${have_target} "Client plugin ${plugin_type}") + ELSE() + ADD_FEATURE_INFO(${plugin} ${have_target} "Server plugin ${plugin_type}") ENDIF() ENDIF(NOT WITHOUT_SERVER OR ARG_CLIENT) ENDMACRO() From 3b29315fdeb496cc896bc5da0982a6ebbea91e23 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Sun, 15 Aug 2021 09:00:06 +1000 Subject: [PATCH 07/20] mysql_client_test: test_bug40365 gcc-11.2.1 indentation complaint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Observed in 10.4 however same code in 10.2 mariadb-server-10.4/tests/mysql_client_test.c:18209:5: error: this ‘if’ clause does not guard... [-Werror=misleading-indentation] 18209 | if (!opt_silent) | ^~ In file included from mariadb-server-10.4/tests/mysql_client_test.c:38: mariadb-server-10.4/tests/mysql_client_fw.c:133:9: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘if’ 133 | ((void) ((expr) ? 0 : (die(__FILE__, __LINE__, #expr), 0))) | ^ mariadb-server-10.4/tests/mysql_client_test.c:18212:7: note: in expansion of macro ‘DIE_UNLESS’ 18212 | DIE_UNLESS(tm[i].year == 0); | ^~~~~~~~~~ $ /usr/bin/cc --version cc (GCC) 11.2.1 20210728 (Red Hat 11.2.1-1) --- tests/mysql_client_test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 1f0334ec2cf..ac9c06ac94b 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -18051,9 +18051,9 @@ static void test_bug40365(void) if (!opt_silent) fprintf(stdout, "\ntime[%d]: %02d-%02d-%02d ", i, tm[i].year, tm[i].month, tm[i].day); - DIE_UNLESS(tm[i].year == 0); - DIE_UNLESS(tm[i].month == 0); - DIE_UNLESS(tm[i].day == 0); + DIE_UNLESS(tm[i].year == 0); + DIE_UNLESS(tm[i].month == 0); + DIE_UNLESS(tm[i].day == 0); } mysql_stmt_close(stmt); rc= mysql_commit(mysql); From d1a948cfaaab67e699674af4c11efad3868a629d Mon Sep 17 00:00:00 2001 From: Julius Goryavsky Date: Sun, 15 Aug 2021 21:03:07 +0200 Subject: [PATCH 08/20] MDEV-26211: Cluster joiner node is failed to start when using TLS This commit adds support for reading new SSL configuration options (ssl-ca, ssl-cert and ssl-key) if the [sst] section with old options (tca, tcert and tkey) is missing in the config file, even if not specified authentication mode via the ssl-mode option. Before this change, new parameters were read only if the ssl-mode option was present in the configuration file and it was not equal to the 'DISABLED' value. Also added diagnostics (information level) which warns the user that due to the presence of the tca, tcert and/or tkey parameters in the [sst] section, new SSL configuration options will be ignored (if their values do not match the old ones). --- scripts/wsrep_sst_mariabackup.sh | 48 ++++++++++++++++++++---------- scripts/wsrep_sst_xtrabackup-v2.sh | 48 ++++++++++++++++++++---------- 2 files changed, 66 insertions(+), 30 deletions(-) diff --git a/scripts/wsrep_sst_mariabackup.sh b/scripts/wsrep_sst_mariabackup.sh index 46804c9dce4..562b9b929f2 100644 --- a/scripts/wsrep_sst_mariabackup.sh +++ b/scripts/wsrep_sst_mariabackup.sh @@ -166,7 +166,8 @@ get_keys() fi if [ -z "$ekey" -a ! -r "$ekeyfile" ]; then - wsrep_log_error "FATAL: Either key or keyfile must be readable" + wsrep_log_error "FATAL: Either key must be specified " \ + "or keyfile must be readable" exit 3 fi @@ -448,9 +449,30 @@ encgroups='--mysqld|sst|xtrabackup' check_server_ssl_config() { - tcert=$(parse_cnf "$encgroups" 'ssl-ca') - tpem=$(parse_cnf "$encgroups" 'ssl-cert') - tkey=$(parse_cnf "$encgroups" 'ssl-key') + # backward-compatible behavior: + tcert=$(parse_cnf 'sst' 'tca') + tpem=$(parse_cnf 'sst' 'tcert') + tkey=$(parse_cnf 'sst' 'tkey') + # reading new ssl configuration options: + local tcert2=$(parse_cnf "$encgroups" 'ssl-ca') + local tpem2=$(parse_cnf "$encgroups" 'ssl-cert') + local tkey2=$(parse_cnf "$encgroups" 'ssl-key') + # if there are no old options, then we take new ones: + if [ -z "$tcert" -a -z "$tpem" -a -z "$tkey" ]; then + tcert="$tcert2" + tpem="$tpem2" + tkey="$tkey2" + # checking for presence of the new-style SSL configuration: + elif [ -n "$tcert2" -o -n "$tpem2" -o -n "$tkey2" ]; then + if [ "$tcert" != "$tcert2" -o \ + "$tpem" != "$tpem2" -o \ + "$tkey" != "$tkey2" ] + then + wsrep_log_info "new ssl configuration options (ssl-ca, ssl-cert " \ + "and ssl-key) are ignored by SST due to presence " \ + "of the tca, tcert and/or tkey in the [sst] section" + fi + fi } read_cnf() @@ -463,18 +485,10 @@ read_cnf() if [ $encrypt -eq 0 -o $encrypt -ge 2 ] then - if [ "$tmode" != 'DISABLED' -o $encrypt -ge 2 ] - then - tcert=$(parse_cnf 'sst' 'tca') - tpem=$(parse_cnf 'sst' 'tcert') - tkey=$(parse_cnf 'sst' 'tkey') + if [ "$tmode" != 'DISABLED' -o $encrypt -ge 2 ]; then + check_server_ssl_config fi if [ "$tmode" != 'DISABLED' ]; then - # backward-incompatible behavior - if [ -z "$tpem" -a -z "$tkey" -a -z "$tcert" ]; then - # no old-style SSL config in [sst] - check_server_ssl_config - fi if [ 0 -eq $encrypt -a -n "$tpem" -a -n "$tkey" ] then encrypt=3 # enable cert/key SSL encyption @@ -489,7 +503,11 @@ read_cnf() ealgo=$(parse_cnf "$encgroups" 'encrypt-algo') eformat=$(parse_cnf "$encgroups" 'encrypt-format' 'openssl') ekey=$(parse_cnf "$encgroups" 'encrypt-key') - ekeyfile=$(parse_cnf "$encgroups" 'encrypt-key-file') + # The keyfile should be read only when the key + # is not specified or empty: + if [ -z "$ekey" ]; then + ekeyfile=$(parse_cnf "$encgroups" 'encrypt-key-file') + fi fi wsrep_log_info "SSL configuration: CA='$tcert', CERT='$tpem'," \ diff --git a/scripts/wsrep_sst_xtrabackup-v2.sh b/scripts/wsrep_sst_xtrabackup-v2.sh index 9600848dc77..13a4a1d25c1 100644 --- a/scripts/wsrep_sst_xtrabackup-v2.sh +++ b/scripts/wsrep_sst_xtrabackup-v2.sh @@ -165,7 +165,8 @@ get_keys() fi if [ -z "$ekey" -a ! -r "$ekeyfile" ]; then - wsrep_log_error "FATAL: Either key or keyfile must be readable" + wsrep_log_error "FATAL: Either key must be specified " \ + "or keyfile must be readable" exit 3 fi @@ -450,9 +451,30 @@ encgroups='--mysqld|sst|xtrabackup' check_server_ssl_config() { - tcert=$(parse_cnf "$encgroups" 'ssl-ca') - tpem=$(parse_cnf "$encgroups" 'ssl-cert') - tkey=$(parse_cnf "$encgroups" 'ssl-key') + # backward-compatible behavior: + tcert=$(parse_cnf 'sst' 'tca') + tpem=$(parse_cnf 'sst' 'tcert') + tkey=$(parse_cnf 'sst' 'tkey') + # reading new ssl configuration options: + local tcert2=$(parse_cnf "$encgroups" 'ssl-ca') + local tpem2=$(parse_cnf "$encgroups" 'ssl-cert') + local tkey2=$(parse_cnf "$encgroups" 'ssl-key') + # if there are no old options, then we take new ones: + if [ -z "$tcert" -a -z "$tpem" -a -z "$tkey" ]; then + tcert="$tcert2" + tpem="$tpem2" + tkey="$tkey2" + # checking for presence of the new-style SSL configuration: + elif [ -n "$tcert2" -o -n "$tpem2" -o -n "$tkey2" ]; then + if [ "$tcert" != "$tcert2" -o \ + "$tpem" != "$tpem2" -o \ + "$tkey" != "$tkey2" ] + then + wsrep_log_info "new ssl configuration options (ssl-ca, ssl-cert " \ + "and ssl-key) are ignored by SST due to presence " \ + "of the tca, tcert and/or tkey in the [sst] section" + fi + fi } read_cnf() @@ -465,18 +487,10 @@ read_cnf() if [ $encrypt -eq 0 -o $encrypt -ge 2 ] then - if [ "$tmode" != 'DISABLED' -o $encrypt -ge 2 ] - then - tcert=$(parse_cnf 'sst' 'tca') - tpem=$(parse_cnf 'sst' 'tcert') - tkey=$(parse_cnf 'sst' 'tkey') + if [ "$tmode" != 'DISABLED' -o $encrypt -ge 2 ]; then + check_server_ssl_config fi if [ "$tmode" != 'DISABLED' ]; then - # backward-incompatible behavior - if [ -z "$tpem" -a -z "$tkey" -a -z "$tcert" ]; then - # no old-style SSL config in [sst] - check_server_ssl_config - fi if [ 0 -eq $encrypt -a -n "$tpem" -a -n "$tkey" ] then encrypt=3 # enable cert/key SSL encyption @@ -491,7 +505,11 @@ read_cnf() ealgo=$(parse_cnf "$encgroups" 'encrypt-algo') eformat=$(parse_cnf "$encgroups" 'encrypt-format' 'xbcrypt') ekey=$(parse_cnf "$encgroups" 'encrypt-key') - ekeyfile=$(parse_cnf "$encgroups" 'encrypt-key-file') + # The keyfile should be read only when the key + # is not specified or empty: + if [ -z "$ekey" ]; then + ekeyfile=$(parse_cnf "$encgroups" 'encrypt-key-file') + fi fi wsrep_log_info "SSL configuration: CA='$tcert', CERT='$tpem'," \ From 094e03916670891b87ef1d95cf52ce6248c537ed Mon Sep 17 00:00:00 2001 From: Julius Goryavsky Date: Sun, 15 Aug 2021 21:12:58 +0200 Subject: [PATCH 09/20] MDEV-26340: rsync uses `--whole-file` only in wan mode This commit fixes a mistake where the --whole-file option is used by rsync SST in WAN mode instead of LAN. --- scripts/wsrep_sst_rsync.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/wsrep_sst_rsync.sh b/scripts/wsrep_sst_rsync.sh index fc9f5017937..cc1912abcd0 100644 --- a/scripts/wsrep_sst_rsync.sh +++ b/scripts/wsrep_sst_rsync.sh @@ -388,7 +388,7 @@ EOF # Use deltaxfer only for WAN inv=$(basename "$0") WHOLE_FILE_OPT="" - if [ "${inv%wsrep_sst_rsync_wan*}" != "$inv" ]; then + if [ "${inv%wsrep_sst_rsync_wan*}" = "$inv" ]; then WHOLE_FILE_OPT="--whole-file" fi From 50428b3995d24233d8ba3e76ebb51a18761f335d Mon Sep 17 00:00:00 2001 From: Julius Goryavsky Date: Mon, 16 Aug 2021 02:00:10 +0200 Subject: [PATCH 10/20] MDEV-26101: Galera WSREP SST broken on 10.6 under FreeBSD This commit fixes a call to the sockstat utility for FreeBSD, where this utility requires an extra "-s" parameter to display the connection status and prints one extra column. --- scripts/wsrep_sst_common.sh | 8 +++++++- scripts/wsrep_sst_rsync.sh | 10 +++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/scripts/wsrep_sst_common.sh b/scripts/wsrep_sst_common.sh index 562f9dc3aac..67244a7c622 100644 --- a/scripts/wsrep_sst_common.sh +++ b/scripts/wsrep_sst_common.sh @@ -1010,7 +1010,13 @@ check_port() lsof -Pnl -i ":$port" 2>/dev/null | \ grep -q -E "^($utils)[^[:space:]]*[[:space:]]+$pid[[:space:]].*\\(LISTEN\\)" && rc=0 elif [ $sockstat_available -ne 0 ]; then - sockstat -p "$port" 2>/dev/null | \ + local opts='-p' + if [ "$OS" = 'FreeBSD' ]; then + # sockstat on FreeBSD requires the "-s" option + # to display the connection state: + opts='-sp' + fi + sockstat "$opts" "$port" 2>/dev/null | \ grep -q -E "[[:space:]]+($utils)[^[:space:]]*[[:space:]]+$pid[[:space:]].*[[:space:]]LISTEN" && rc=0 elif [ $ss_available -ne 0 ]; then ss -nlpH "( sport = :$port )" 2>/dev/null | \ diff --git a/scripts/wsrep_sst_rsync.sh b/scripts/wsrep_sst_rsync.sh index cc1912abcd0..d90e87b68f2 100644 --- a/scripts/wsrep_sst_rsync.sh +++ b/scripts/wsrep_sst_rsync.sh @@ -93,7 +93,15 @@ check_pid_and_port() else local filter='([^[:space:]]+[[:space:]]+){4}[^[:space:]]+' if [ $sockstat_available -eq 1 ]; then - port_info=$(sockstat -p "$port" 2>/dev/null | \ + local opts='-p' + if [ "$OS" = 'FreeBSD' ]; then + # sockstat on FreeBSD requires the "-s" option + # to display the connection state: + opts='-sp' + # in addition, sockstat produces an additional column: + filter='([^[:space:]]+[[:space:]]+){5}[^[:space:]]+' + fi + port_info=$(sockstat "$opts" "$port" 2>/dev/null | \ grep -E '[[:space:]]LISTEN' | grep -o -E "$filter") else port_info=$(ss -nlpH "( sport = :$port )" 2>/dev/null | \ From 4cd063b9e40cfb77413bcd44bc7d922c6228f810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 16 Aug 2021 12:10:20 +0300 Subject: [PATCH 11/20] MDEV-26376 pars_info_bind_id() unnecessarily copies strings pars_info_bind_id(): Remove the parameter copy_name. It was always being passed as constant TRUE or true. It turns out that copying the string is completely unnecessary. In all calls except the one in fts_get_select_columns_str() and fts_doc_fetch_by_doc_id(), the parameter is being passed as a compile-time constant, and therefore the pointer cannot become stale. In that special call, the string that is being passed is allocated from the same memory heap that pars_info_bind_id() would have been using. pars_info_add_id(): Remove (unused declaration). --- storage/innobase/fts/fts0config.cc | 8 ++++---- storage/innobase/fts/fts0fts.cc | 18 +++++++++--------- storage/innobase/fts/fts0opt.cc | 28 +++++++++++++--------------- storage/innobase/fts/fts0que.cc | 8 ++++---- storage/innobase/fts/fts0sql.cc | 4 ++-- storage/innobase/handler/i_s.cc | 4 ++-- storage/innobase/include/pars0pars.h | 12 +----------- storage/innobase/pars/pars0pars.cc | 6 ++---- 8 files changed, 37 insertions(+), 51 deletions(-) diff --git a/storage/innobase/fts/fts0config.cc b/storage/innobase/fts/fts0config.cc index ed4340d818b..43471acdc19 100644 --- a/storage/innobase/fts/fts0config.cc +++ b/storage/innobase/fts/fts0config.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2007, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2020, MariaDB Corporation. +Copyright (c) 2017, 2021, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -97,7 +97,7 @@ fts_config_get_value( fts_table->suffix = "CONFIG"; fts_get_table_name(fts_table, table_name); - pars_info_bind_id(info, true, "table_name", table_name); + pars_info_bind_id(info, "table_name", table_name); graph = fts_parse_sql( fts_table, @@ -217,7 +217,7 @@ fts_config_set_value( fts_table->suffix = "CONFIG"; fts_get_table_name(fts_table, table_name, dict_locked); - pars_info_bind_id(info, true, "table_name", table_name); + pars_info_bind_id(info, "table_name", table_name); graph = fts_parse_sql( fts_table, info, @@ -245,7 +245,7 @@ fts_config_set_value( info, "value", value->f_str, value->f_len); fts_get_table_name(fts_table, table_name, dict_locked); - pars_info_bind_id(info, true, "table_name", table_name); + pars_info_bind_id(info, "table_name", table_name); graph = fts_parse_sql( fts_table, info, diff --git a/storage/innobase/fts/fts0fts.cc b/storage/innobase/fts/fts0fts.cc index b423db71c7d..07077006096 100644 --- a/storage/innobase/fts/fts0fts.cc +++ b/storage/innobase/fts/fts0fts.cc @@ -483,7 +483,7 @@ cleanup: pars_info_t* info = pars_info_create(); - pars_info_bind_id(info, TRUE, "table_stopword", stopword_table_name); + pars_info_bind_id(info, "table_stopword", stopword_table_name); pars_info_bind_function(info, "my_func", fts_read_stopword, stopword_info); @@ -1912,7 +1912,7 @@ fts_create_common_tables( fts_table.suffix = "CONFIG"; fts_get_table_name(&fts_table, fts_name, true); - pars_info_bind_id(info, true, "config_table", fts_name); + pars_info_bind_id(info, "config_table", fts_name); graph = fts_parse_sql_no_dict_lock( &fts_table, info, fts_config_table_insert_values_sql); @@ -2668,7 +2668,7 @@ retry: info, "my_func", fts_fetch_store_doc_id, doc_id); fts_get_table_name(&fts_table, table_name); - pars_info_bind_id(info, true, "config_table", table_name); + pars_info_bind_id(info, "config_table", table_name); graph = fts_parse_sql( &fts_table, info, @@ -2796,7 +2796,7 @@ fts_update_sync_doc_id( fts_get_table_name(&fts_table, fts_name, table->fts->dict_locked); - pars_info_bind_id(info, true, "table_name", fts_name); + pars_info_bind_id(info, "table_name", fts_name); graph = fts_parse_sql( &fts_table, info, @@ -2939,7 +2939,7 @@ fts_delete( fts_table.suffix = "DELETED"; fts_get_table_name(&fts_table, table_name); - pars_info_bind_id(info, true, "deleted", table_name); + pars_info_bind_id(info, "deleted", table_name); graph = fts_parse_sql( &fts_table, @@ -3764,7 +3764,7 @@ fts_doc_fetch_by_doc_id( pars_info_bind_function(info, "my_func", callback, arg); select_str = fts_get_select_columns_str(index, info, info->heap); - pars_info_bind_id(info, TRUE, "table_name", index->table_name); + pars_info_bind_id(info, "table_name", index->table_name); if (!get_doc || !get_doc->get_document_graph) { if (option == FTS_FETCH_DOC_BY_ID_EQUAL) { @@ -3871,7 +3871,7 @@ fts_write_node( info = pars_info_create(); fts_get_table_name(fts_table, table_name); - pars_info_bind_id(info, true, "index_table_name", table_name); + pars_info_bind_id(info, "index_table_name", table_name); } pars_info_bind_varchar_literal(info, "token", word->f_str, word->f_len); @@ -3946,7 +3946,7 @@ fts_sync_add_deleted_cache( &fts_table, "DELETED_CACHE", FTS_COMMON_TABLE, sync->table); fts_get_table_name(&fts_table, table_name); - pars_info_bind_id(info, true, "table_name", table_name); + pars_info_bind_id(info, "table_name", table_name); graph = fts_parse_sql( &fts_table, @@ -4951,7 +4951,7 @@ fts_get_rows_count( pars_info_bind_function(info, "my_func", fts_read_ulint, &count); fts_get_table_name(fts_table, table_name); - pars_info_bind_id(info, true, "table_name", table_name); + pars_info_bind_id(info, "table_name", table_name); graph = fts_parse_sql( fts_table, diff --git a/storage/innobase/fts/fts0opt.cc b/storage/innobase/fts/fts0opt.cc index 3cabb64b851..c4cbbfafff4 100644 --- a/storage/innobase/fts/fts0opt.cc +++ b/storage/innobase/fts/fts0opt.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2007, 2018, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2016, 2020, MariaDB Corporation. +Copyright (c) 2016, 2021, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -492,7 +492,7 @@ fts_index_fetch_nodes( fts_get_table_name(fts_table, table_name); - pars_info_bind_id(info, true, "table_name", table_name); + pars_info_bind_id(info, "table_name", table_name); } pars_info_bind_function(info, "my_func", fetch->read_record, fetch); @@ -821,7 +821,7 @@ fts_index_fetch_words( info, "word", word->f_str, word->f_len); fts_get_table_name(&optim->fts_index_table, table_name); - pars_info_bind_id(info, true, "table_name", table_name); + pars_info_bind_id(info, "table_name", table_name); graph = fts_parse_sql( &optim->fts_index_table, @@ -977,7 +977,7 @@ fts_table_fetch_doc_ids( pars_info_bind_function(info, "my_func", fts_fetch_doc_ids, doc_ids); fts_get_table_name(fts_table, table_name); - pars_info_bind_id(info, true, "table_name", table_name); + pars_info_bind_id(info, "table_name", table_name); graph = fts_parse_sql( fts_table, @@ -1441,7 +1441,7 @@ fts_optimize_write_word( fts_table->suffix = fts_get_suffix(selected); fts_get_table_name(fts_table, table_name); - pars_info_bind_id(info, true, "table_name", table_name); + pars_info_bind_id(info, "table_name", table_name); graph = fts_parse_sql( fts_table, @@ -2032,11 +2032,11 @@ fts_optimize_purge_deleted_doc_ids( used in the fts_delete_doc_ids_sql */ optim->fts_common_table.suffix = fts_common_tables[3]; fts_get_table_name(&optim->fts_common_table, deleted); - pars_info_bind_id(info, true, fts_common_tables[3], deleted); + pars_info_bind_id(info, fts_common_tables[3], deleted); optim->fts_common_table.suffix = fts_common_tables[4]; fts_get_table_name(&optim->fts_common_table, deleted_cache); - pars_info_bind_id(info, true, fts_common_tables[4], deleted_cache); + pars_info_bind_id(info, fts_common_tables[4], deleted_cache); graph = fts_parse_sql(NULL, info, fts_delete_doc_ids_sql); @@ -2089,12 +2089,11 @@ fts_optimize_purge_deleted_doc_id_snapshot( used in the fts_end_delete_sql */ optim->fts_common_table.suffix = fts_common_tables[0]; fts_get_table_name(&optim->fts_common_table, being_deleted); - pars_info_bind_id(info, true, fts_common_tables[0], being_deleted); + pars_info_bind_id(info, fts_common_tables[0], being_deleted); optim->fts_common_table.suffix = fts_common_tables[1]; fts_get_table_name(&optim->fts_common_table, being_deleted_cache); - pars_info_bind_id(info, true, fts_common_tables[1], - being_deleted_cache); + pars_info_bind_id(info, fts_common_tables[1], being_deleted_cache); /* Delete the doc ids that were copied to delete pending state at the start of optimize. */ @@ -2150,20 +2149,19 @@ fts_optimize_create_deleted_doc_id_snapshot( used in the fts_init_delete_sql */ optim->fts_common_table.suffix = fts_common_tables[0]; fts_get_table_name(&optim->fts_common_table, being_deleted); - pars_info_bind_id(info, true, fts_common_tables[0], being_deleted); + pars_info_bind_id(info, fts_common_tables[0], being_deleted); optim->fts_common_table.suffix = fts_common_tables[3]; fts_get_table_name(&optim->fts_common_table, deleted); - pars_info_bind_id(info, true, fts_common_tables[3], deleted); + pars_info_bind_id(info, fts_common_tables[3], deleted); optim->fts_common_table.suffix = fts_common_tables[1]; fts_get_table_name(&optim->fts_common_table, being_deleted_cache); - pars_info_bind_id(info, true, fts_common_tables[1], - being_deleted_cache); + pars_info_bind_id(info, fts_common_tables[1], being_deleted_cache); optim->fts_common_table.suffix = fts_common_tables[4]; fts_get_table_name(&optim->fts_common_table, deleted_cache); - pars_info_bind_id(info, true, fts_common_tables[4], deleted_cache); + pars_info_bind_id(info, fts_common_tables[4], deleted_cache); /* Move doc_ids that are to be deleted to state being deleted. */ graph = fts_parse_sql(NULL, info, fts_init_delete_sql); diff --git a/storage/innobase/fts/fts0que.cc b/storage/innobase/fts/fts0que.cc index df2b330fe4b..b4c72e53afe 100644 --- a/storage/innobase/fts/fts0que.cc +++ b/storage/innobase/fts/fts0que.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2007, 2020, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2020, MariaDB Corporation. +Copyright (c) 2017, 2021, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -2146,7 +2146,7 @@ fts_query_find_term( query->fts_index_table.suffix = fts_get_suffix(selected); fts_get_table_name(&query->fts_index_table, table_name); - pars_info_bind_id(info, true, "index_table_name", table_name); + pars_info_bind_id(info, "index_table_name", table_name); } select.found = FALSE; @@ -2286,7 +2286,7 @@ fts_query_total_docs_containing_term( fts_get_table_name(&query->fts_index_table, table_name); - pars_info_bind_id(info, true, "index_table_name", table_name); + pars_info_bind_id(info, "index_table_name", table_name); graph = fts_parse_sql( &query->fts_index_table, @@ -2369,7 +2369,7 @@ fts_query_terms_in_document( fts_get_table_name(&query->fts_index_table, table_name); - pars_info_bind_id(info, true, "index_table_name", table_name); + pars_info_bind_id(info, "index_table_name", table_name); graph = fts_parse_sql( &query->fts_index_table, diff --git a/storage/innobase/fts/fts0sql.cc b/storage/innobase/fts/fts0sql.cc index e61f2118e70..edbfe484395 100644 --- a/storage/innobase/fts/fts0sql.cc +++ b/storage/innobase/fts/fts0sql.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2007, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2019, MariaDB Corporation. +Copyright (c) 2019, 2021, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -249,7 +249,7 @@ fts_get_select_columns_str( sel_str = mem_heap_printf(heap, "sel%lu", (ulong) i); /* Set copy_name to TRUE since it's dynamic. */ - pars_info_bind_id(info, TRUE, sel_str, field->name); + pars_info_bind_id(info, sel_str, field->name); str = mem_heap_printf( heap, "%s%s$%s", str, (*str) ? ", " : "", sel_str); diff --git a/storage/innobase/handler/i_s.cc b/storage/innobase/handler/i_s.cc index bf6717de5ee..531a2936f0b 100644 --- a/storage/innobase/handler/i_s.cc +++ b/storage/innobase/handler/i_s.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2007, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2014, 2020, MariaDB Corporation. +Copyright (c) 2014, 2021, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -3456,7 +3456,7 @@ i_s_fts_index_table_fill_selected( FTS_INIT_INDEX_TABLE(&fts_table, fts_get_suffix(selected), FTS_INDEX_TABLE, index); fts_get_table_name(&fts_table, table_name); - pars_info_bind_id(info, true, "table_name", table_name); + pars_info_bind_id(info, "table_name", table_name); graph = fts_parse_sql( &fts_table, info, diff --git a/storage/innobase/include/pars0pars.h b/storage/innobase/include/pars0pars.h index f54c50e5b85..626ff3aff0c 100644 --- a/storage/innobase/include/pars0pars.h +++ b/storage/innobase/include/pars0pars.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2019, MariaDB Corporation. +Copyright (c) 2017, 2021, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -491,7 +491,6 @@ void pars_info_bind_id( /*=============*/ pars_info_t* info, /*!< in: info struct */ - ibool copy_name,/* in: make a copy of name if TRUE */ const char* name, /*!< in: name */ const char* id); /*!< in: id */ /****************************************************************//** @@ -537,15 +536,6 @@ pars_info_bind_ull_literal( const ib_uint64_t* val) /*!< in: value */ MY_ATTRIBUTE((nonnull)); -/****************************************************************//** -Add bound id. */ -void -pars_info_add_id( -/*=============*/ - pars_info_t* info, /*!< in: info struct */ - const char* name, /*!< in: name */ - const char* id); /*!< in: id */ - /****************************************************************//** Get bound literal with the given name. @return bound literal, or NULL if not found */ diff --git a/storage/innobase/pars/pars0pars.cc b/storage/innobase/pars/pars0pars.cc index 991762673aa..851c0a0316b 100644 --- a/storage/innobase/pars/pars0pars.cc +++ b/storage/innobase/pars/pars0pars.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2019, MariaDB Corporation. +Copyright (c) 2019, 2021, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -2352,7 +2352,6 @@ void pars_info_bind_id( /*==============*/ pars_info_t* info, /*!< in: info struct */ - ibool copy_name, /* in: copy name if TRUE */ const char* name, /*!< in: name */ const char* id) /*!< in: id */ { @@ -2375,8 +2374,7 @@ pars_info_bind_id( bid = static_cast( ib_vector_push(info->bound_ids, NULL)); - bid->name = (copy_name) - ? mem_heap_strdup(info->heap, name) : name; + bid->name = name; } bid->id = id; From 89445b64fef2c3c9f2cfb9f572dd19b88f3a48df Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Wed, 28 Jul 2021 19:25:25 +0530 Subject: [PATCH 12/20] MDEV-26131 SEGV in ha_innobase::discard_or_import_tablespace Import operation without .cfg file fails when there is mismatch of index between metadata table and .ibd file. Moreover, MDEV-19022 shows that InnoDB can end up with index tree where non-leaf page has only one child page. So it is unsafe to find the secondary index root page. This patch does the following when importing the table without .cfg file: 1) If the metadata contains more than one index then InnoDB stops the import operation and report the user to drop all secondary indexes before doing import operation. 2) When the metadata contain only clustered index then InnoDB finds the index id by reading page 0 & page 3 instead of traversing the whole tablespace. --- .../suite/encryption/r/innodb_import.result | 22 ++ .../encryption/t/innodb_import.combinations | 7 + .../suite/encryption/t/innodb_import.test | 21 ++ .../innodb_gis/r/alter_spatial_index.result | 39 +-- .../innodb_gis/t/alter_spatial_index.test | 10 +- storage/innobase/row/row0import.cc | 265 ++++++++++++------ 6 files changed, 249 insertions(+), 115 deletions(-) create mode 100644 mysql-test/suite/encryption/r/innodb_import.result create mode 100644 mysql-test/suite/encryption/t/innodb_import.combinations create mode 100644 mysql-test/suite/encryption/t/innodb_import.test diff --git a/mysql-test/suite/encryption/r/innodb_import.result b/mysql-test/suite/encryption/r/innodb_import.result new file mode 100644 index 00000000000..54b95ab26d4 --- /dev/null +++ b/mysql-test/suite/encryption/r/innodb_import.result @@ -0,0 +1,22 @@ +# +# MDEV-26131 SEGV in ha_innobase::discard_or_import_tablespace +# +CREATE TABLE t1(f1 int,f2 text)ENGINE=InnoDB; +INSERT INTO t1 VALUES(1, "InnoDB"); +CREATE TABLE t2 LIKE t1; +ALTER TABLE t2 ADD KEY idx (f2(13)); +ALTER TABLE t2 DISCARD TABLESPACE; +FLUSH TABLES t1 FOR EXPORT; +UNLOCK TABLES; +ALTER TABLE t2 IMPORT TABLESPACE; +ERROR HY000: Internal error: Drop all secondary indexes before importing table test/t2 when .cfg file is missing. +ALTER TABLE t2 DROP KEY idx; +Warnings: +Warning 1814 Tablespace has been discarded for table `t2` +ALTER TABLE t2 IMPORT TABLESPACE; +Warnings: +Warning 1810 IO Read error: (2, No such file or directory) Error opening './test/t2.cfg', will attempt to import without schema verification +SELECT * FROM t2; +f1 f2 +1 InnoDB +DROP TABLE t1, t2; diff --git a/mysql-test/suite/encryption/t/innodb_import.combinations b/mysql-test/suite/encryption/t/innodb_import.combinations new file mode 100644 index 00000000000..75458949582 --- /dev/null +++ b/mysql-test/suite/encryption/t/innodb_import.combinations @@ -0,0 +1,7 @@ +[page_compressed] +innodb-compression-default=1 +[encryption] +innodb-encrypt-tables=1 +[page_compressed_encryption] +innodb-compression-default=1 +innodb-encrypt-tables=1 diff --git a/mysql-test/suite/encryption/t/innodb_import.test b/mysql-test/suite/encryption/t/innodb_import.test new file mode 100644 index 00000000000..12c8d8bb017 --- /dev/null +++ b/mysql-test/suite/encryption/t/innodb_import.test @@ -0,0 +1,21 @@ +--source include/have_innodb.inc +-- source include/have_example_key_management_plugin.inc +--echo # +--echo # MDEV-26131 SEGV in ha_innobase::discard_or_import_tablespace +--echo # +let $MYSQLD_DATADIR = `SELECT @@datadir`; +CREATE TABLE t1(f1 int,f2 text)ENGINE=InnoDB; +INSERT INTO t1 VALUES(1, "InnoDB"); +CREATE TABLE t2 LIKE t1; +ALTER TABLE t2 ADD KEY idx (f2(13)); +ALTER TABLE t2 DISCARD TABLESPACE; +FLUSH TABLES t1 FOR EXPORT; +--copy_file $MYSQLD_DATADIR/test/t1.ibd $MYSQLD_DATADIR/test/t2.ibd +UNLOCK TABLES; +--error ER_INTERNAL_ERROR +ALTER TABLE t2 IMPORT TABLESPACE; + +ALTER TABLE t2 DROP KEY idx; +ALTER TABLE t2 IMPORT TABLESPACE; +SELECT * FROM t2; +DROP TABLE t1, t2; diff --git a/mysql-test/suite/innodb_gis/r/alter_spatial_index.result b/mysql-test/suite/innodb_gis/r/alter_spatial_index.result index bef144e6072..f550d6168e2 100644 --- a/mysql-test/suite/innodb_gis/r/alter_spatial_index.result +++ b/mysql-test/suite/innodb_gis/r/alter_spatial_index.result @@ -252,6 +252,16 @@ UNLOCK TABLES; ALTER TABLE tab DISCARD TABLESPACE; SELECT c1,ST_Astext(c2),ST_Astext(c4) FROM tab; ERROR HY000: Tablespace has been discarded for table `tab` +ERROR HY000: Internal error: Drop all secondary indexes before importing table test/tab when .cfg file is missing. +Table Create Table +tab CREATE TABLE `tab` ( + `c1` int(11) NOT NULL, + `c2` point NOT NULL, + `c3` linestring NOT NULL, + `c4` polygon NOT NULL, + `c5` geometry NOT NULL, + PRIMARY KEY (`c2`(25)) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 CHECK TABLE tab; Table Op Msg_type Msg_text test.tab check status OK @@ -282,9 +292,6 @@ INSERT INTO tab SELECT * FROM tab1; ALTER TABLE tab DROP PRIMARY KEY; affected rows: 1 info: Records: 1 Duplicates: 0 Warnings: 0 -ALTER TABLE tab DROP INDEX idx2; -affected rows: 0 -info: Records: 0 Duplicates: 0 Warnings: 0 SET STATEMENT sql_mode = 'NO_ENGINE_SUBSTITUTION' FOR CREATE TEMPORARY TABLE temp_tab AS SELECT * FROM tab where c1 = c2; INSERT INTO temp_tab SELECT * FROM tab; @@ -322,18 +329,10 @@ tab CREATE TABLE `tab` ( `c2` point NOT NULL, `c3` linestring NOT NULL, `c4` polygon NOT NULL, - `c5` geometry NOT NULL, - SPATIAL KEY `idx3` (`c3`), - SPATIAL KEY `idx4` (`c4`) COMMENT 'testing spatial index on Polygon', - SPATIAL KEY `idx5` (`c5`) COMMENT 'testing spatial index on Geometry', - KEY `idx6` (`c4`(10)) USING BTREE + `c5` geometry NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1 SHOW INDEX FROM tab; Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment -tab 1 idx3 1 c3 A # 32 NULL SPATIAL -tab 1 idx4 1 c4 A # 32 NULL SPATIAL testing spatial index on Polygon -tab 1 idx5 1 c5 A # 32 NULL SPATIAL testing spatial index on Geometry -tab 1 idx6 1 c4 A # 10 NULL BTREE DELETE FROM tab; ALTER TABLE tab ADD PRIMARY KEY(c2); affected rows: 0 @@ -354,20 +353,12 @@ tab CREATE TABLE `tab` ( `c5` geometry NOT NULL, PRIMARY KEY (`c2`(25)), UNIQUE KEY `const_1` (`c2`(25)), - SPATIAL KEY `idx3` (`c3`), - SPATIAL KEY `idx4` (`c4`) COMMENT 'testing spatial index on Polygon', - SPATIAL KEY `idx5` (`c5`) COMMENT 'testing spatial index on Geometry', - KEY `idx6` (`c4`(10)) USING BTREE, SPATIAL KEY `idx2` (`c2`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 SHOW INDEX FROM tab; Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment tab 0 PRIMARY 1 c2 A # 25 NULL BTREE tab 0 const_1 1 c2 A # 25 NULL BTREE -tab 1 idx3 1 c3 A # 32 NULL SPATIAL -tab 1 idx4 1 c4 A # 32 NULL SPATIAL testing spatial index on Polygon -tab 1 idx5 1 c5 A # 32 NULL SPATIAL testing spatial index on Geometry -tab 1 idx6 1 c4 A # 10 NULL BTREE tab 1 idx2 1 c2 A # 32 NULL SPATIAL INSERT INTO tab(c1,c2,c3,c4,c5) VALUES(1,ST_GeomFromText('POINT(10 10)'),ST_GeomFromText('LINESTRING(5 5,20 20,30 30)'), @@ -396,20 +387,12 @@ tab CREATE TABLE `tab` ( `c5` geometry NOT NULL, PRIMARY KEY (`c5`(10)), UNIQUE KEY `const_1` (`c5`(10)), - SPATIAL KEY `idx3` (`c3`), - SPATIAL KEY `idx4` (`c4`) COMMENT 'testing spatial index on Polygon', - SPATIAL KEY `idx5` (`c5`) COMMENT 'testing spatial index on Geometry', - KEY `idx6` (`c4`(10)) USING BTREE, SPATIAL KEY `idx2` (`c2`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 SHOW INDEX FROM tab; Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment tab 0 PRIMARY 1 c5 A # 10 NULL BTREE tab 0 const_1 1 c5 A # 10 NULL BTREE -tab 1 idx3 1 c3 A # 32 NULL SPATIAL -tab 1 idx4 1 c4 A # 32 NULL SPATIAL testing spatial index on Polygon -tab 1 idx5 1 c5 A # 32 NULL SPATIAL testing spatial index on Geometry -tab 1 idx6 1 c4 A # 10 NULL BTREE tab 1 idx2 1 c2 A # 32 NULL SPATIAL INSERT INTO tab(c1,c2,c3,c4,c5) VALUES(1,ST_GeomFromText('POINT(10 10)'),ST_GeomFromText('LINESTRING(5 5,20 20,30 30)'), diff --git a/mysql-test/suite/innodb_gis/t/alter_spatial_index.test b/mysql-test/suite/innodb_gis/t/alter_spatial_index.test index c4cd100c7e3..cd148820e85 100644 --- a/mysql-test/suite/innodb_gis/t/alter_spatial_index.test +++ b/mysql-test/suite/innodb_gis/t/alter_spatial_index.test @@ -277,8 +277,17 @@ SELECT c1,ST_Astext(c2),ST_Astext(c4) FROM tab; --disable_query_log +--error ER_INTERNAL_ERROR ALTER TABLE tab IMPORT TABLESPACE; +ALTER TABLE tab DROP INDEX idx2; +ALTER TABLE tab DROP INDEX idx3; +ALTER TABLE tab DROP INDEX idx4; +ALTER TABLE tab DROP INDEX idx5; +ALTER TABLE tab DROP INDEX idx6; + +SHOW CREATE TABLE tab; +ALTER TABLE tab IMPORT TABLESPACE; --enable_query_log CHECK TABLE tab; @@ -308,7 +317,6 @@ INSERT INTO tab SELECT * FROM tab1; --enable_info ALTER TABLE tab DROP PRIMARY KEY; -ALTER TABLE tab DROP INDEX idx2; --disable_info # Check spatial index on temp tables diff --git a/storage/innobase/row/row0import.cc b/storage/innobase/row/row0import.cc index fa7db1e27b8..699392d2a5b 100644 --- a/storage/innobase/row/row0import.cc +++ b/storage/innobase/row/row0import.cc @@ -222,6 +222,19 @@ struct row_import { found and was readable */ }; +struct fil_iterator_t { + pfs_os_file_t file; /*!< File handle */ + const char* filepath; /*!< File path name */ + os_offset_t start; /*!< From where to start */ + os_offset_t end; /*!< Where to stop */ + os_offset_t file_size; /*!< File size in bytes */ + ulint n_io_buffers; /*!< Number of pages to use + for IO */ + byte* io_buffer; /*!< Buffer to use for IO */ + fil_space_crypt_t *crypt_data; /*!< Crypt data (if encrypted) */ + byte* crypt_io_buffer; /*!< IO buffer when encrypted */ +}; + /** Use the page cursor to iterate over records in a block. */ class RecIterator { public: @@ -432,6 +445,10 @@ public: ? block->page.zip.data : block->frame; } + /** Invoke the functionality for the callback */ + virtual dberr_t run(const fil_iterator_t& iter, + buf_block_t* block) UNIV_NOTHROW = 0; + protected: /** Get the physical offset of the extent descriptor within the page. @param page_no page number of the extent descriptor @@ -591,6 +608,24 @@ AbstractCallback::init( return set_current_xdes(0, page); } +/** +TODO: This can be made parallel trivially by chunking up the file +and creating a callback per thread.. Main benefit will be to use +multiple CPUs for checksums and compressed tables. We have to do +compressed tables block by block right now. Secondly we need to +decompress/compress and copy too much of data. These are +CPU intensive. + +Iterate over all the pages in the tablespace. +@param iter - Tablespace iterator +@param block - block to use for IO +@param callback - Callback to inspect and update page contents +@retval DB_SUCCESS or error code */ +static dberr_t fil_iterate( + const fil_iterator_t& iter, + buf_block_t* block, + AbstractCallback& callback); + /** Try and determine the index root pages by checking if the next/prev pointers are both FIL_NULL. We need to ensure that skip deleted pages. */ @@ -608,15 +643,13 @@ struct FetchIndexRootPages : public AbstractCallback { ulint m_page_no; /*!< Root page number */ }; - typedef std::vector > Indexes; - /** Constructor @param trx covering (user) transaction @param table table definition in server .*/ FetchIndexRootPages(const dict_table_t* table, trx_t* trx) : AbstractCallback(trx), - m_table(table) UNIV_NOTHROW { } + m_table(table), m_index(0, 0) UNIV_NOTHROW { } /** Destructor */ virtual ~FetchIndexRootPages() UNIV_NOTHROW { } @@ -628,6 +661,13 @@ struct FetchIndexRootPages : public AbstractCallback { return(m_space); } + /** Fetch the clustered index root page in the tablespace + @param iter Tablespace iterator + @param block Block to use for IO + @retval DB_SUCCESS or error code */ + dberr_t run(const fil_iterator_t& iter, + buf_block_t* block) UNIV_NOTHROW; + /** Called for each block as it is read from the file. @param block block to convert, it is not from the buffer pool. @retval DB_SUCCESS or error code. */ @@ -641,7 +681,7 @@ struct FetchIndexRootPages : public AbstractCallback { const dict_table_t* m_table; /** Index information */ - Indexes m_indexes; + Index m_index; }; /** Called for each block as it is read from the file. Check index pages to @@ -656,31 +696,21 @@ dberr_t FetchIndexRootPages::operator()(buf_block_t* block) UNIV_NOTHROW const page_t* page = get_frame(block); - ulint page_type = fil_page_get_type(page); + index_id_t id = btr_page_get_index_id(page); - if (page_type == FIL_PAGE_TYPE_XDES) { - return set_current_xdes(block->page.id.page_no(), page); - } else if (fil_page_index_page_check(page) - && !is_free(block->page.id.page_no()) - && !page_has_siblings(page)) { + m_index.m_id = id; + m_index.m_page_no = block->page.id.page_no(); - index_id_t id = btr_page_get_index_id(page); - - m_indexes.push_back(Index(id, block->page.id.page_no())); - - if (m_indexes.size() == 1) { - /* Check that the tablespace flags match the table flags. */ - ulint expected = dict_tf_to_fsp_flags(m_table->flags); - if (!fsp_flags_match(expected, m_space_flags)) { - ib_errf(m_trx->mysql_thd, IB_LOG_LEVEL_ERROR, - ER_TABLE_SCHEMA_MISMATCH, - "Expected FSP_SPACE_FLAGS=0x%x, .ibd " - "file contains 0x%x.", - unsigned(expected), - unsigned(m_space_flags)); - return(DB_CORRUPTION); - } - } + /* Check that the tablespace flags match the table flags. */ + ulint expected = dict_tf_to_fsp_flags(m_table->flags); + if (!fsp_flags_match(expected, m_space_flags)) { + ib_errf(m_trx->mysql_thd, IB_LOG_LEVEL_ERROR, + ER_TABLE_SCHEMA_MISMATCH, + "Expected FSP_SPACE_FLAGS=0x%x, .ibd " + "file contains 0x%x.", + unsigned(expected), + unsigned(m_space_flags)); + return(DB_CORRUPTION); } return DB_SUCCESS; @@ -692,11 +722,9 @@ Update the import configuration that will be used to import the tablespace. dberr_t FetchIndexRootPages::build_row_import(row_import* cfg) const UNIV_NOTHROW { - Indexes::const_iterator end = m_indexes.end(); - ut_a(cfg->m_table == m_table); cfg->m_page_size.copy_from(m_page_size); - cfg->m_n_indexes = m_indexes.size(); + cfg->m_n_indexes = 1; if (cfg->m_n_indexes == 0) { @@ -722,38 +750,33 @@ FetchIndexRootPages::build_row_import(row_import* cfg) const UNIV_NOTHROW row_index_t* cfg_index = cfg->m_indexes; - for (Indexes::const_iterator it = m_indexes.begin(); - it != end; - ++it, ++cfg_index) { + char name[BUFSIZ]; - char name[BUFSIZ]; + snprintf(name, sizeof(name), "index" IB_ID_FMT, m_index.m_id); - snprintf(name, sizeof(name), "index" IB_ID_FMT, it->m_id); + ulint len = strlen(name) + 1; - ulint len = strlen(name) + 1; + cfg_index->m_name = UT_NEW_ARRAY_NOKEY(byte, len); - cfg_index->m_name = UT_NEW_ARRAY_NOKEY(byte, len); + /* Trigger OOM */ + DBUG_EXECUTE_IF( + "ib_import_OOM_12", + UT_DELETE_ARRAY(cfg_index->m_name); + cfg_index->m_name = NULL; + ); - /* Trigger OOM */ - DBUG_EXECUTE_IF( - "ib_import_OOM_12", - UT_DELETE_ARRAY(cfg_index->m_name); - cfg_index->m_name = NULL; - ); - - if (cfg_index->m_name == NULL) { - return(DB_OUT_OF_MEMORY); - } - - memcpy(cfg_index->m_name, name, len); - - cfg_index->m_id = it->m_id; - - cfg_index->m_space = m_space; - - cfg_index->m_page_no = it->m_page_no; + if (cfg_index->m_name == NULL) { + return(DB_OUT_OF_MEMORY); } + memcpy(cfg_index->m_name, name, len); + + cfg_index->m_id = m_index.m_id; + + cfg_index->m_space = m_space; + + cfg_index->m_page_no = m_index.m_page_no; + return(DB_SUCCESS); } @@ -803,6 +826,11 @@ public: return(m_cfg->m_table->space); } + dberr_t run(const fil_iterator_t& iter, buf_block_t* block) UNIV_NOTHROW + { + return fil_iterate(iter, block, *this); + } + /** Called for each block as it is read from the file. @param block block to convert, it is not from the buffer pool. @retval DB_SUCCESS or error code. */ @@ -1872,7 +1900,7 @@ PageConverter::update_index_page( if (is_free(block->page.id.page_no())) { return(DB_SUCCESS); - } else if ((id = btr_page_get_index_id(page)) != m_index->m_id) { + } else if ((id = btr_page_get_index_id(page)) != m_index->m_id && !m_cfg->m_missing) { row_index_t* index = find_index(id); @@ -3362,20 +3390,6 @@ row_import_update_discarded_flag( return(err); } -struct fil_iterator_t { - pfs_os_file_t file; /*!< File handle */ - const char* filepath; /*!< File path name */ - os_offset_t start; /*!< From where to start */ - os_offset_t end; /*!< Where to stop */ - os_offset_t file_size; /*!< File size in bytes */ - ulint n_io_buffers; /*!< Number of pages to use - for IO */ - byte* io_buffer; /*!< Buffer to use for IO */ - fil_space_crypt_t *crypt_data; /*!< Crypt data (if encrypted) */ - byte* crypt_io_buffer; /*!< IO buffer when encrypted */ -}; - - /** InnoDB writes page by page when there is page compressed tablespace involved. It does help to save the disk space when punch hole is enabled @@ -3426,22 +3440,91 @@ dberr_t fil_import_compress_fwrite(const fil_iterator_t &iter, return err; } -/********************************************************************//** -TODO: This can be made parallel trivially by chunking up the file and creating -a callback per thread. . Main benefit will be to use multiple CPUs for -checksums and compressed tables. We have to do compressed tables block by -block right now. Secondly we need to decompress/compress and copy too much -of data. These are CPU intensive. +dberr_t FetchIndexRootPages::run(const fil_iterator_t& iter, + buf_block_t* block) UNIV_NOTHROW +{ + const ulint size= get_page_size().physical(); + const ulint buf_size = srv_page_size +#ifdef HAVE_LZO + + LZO1X_1_15_MEM_COMPRESS +#elif defined HAVE_SNAPPY + + snappy_max_compressed_length(srv_page_size) +#endif + ; + byte* page_compress_buf = static_cast(malloc(buf_size)); + ut_ad(!srv_read_only_mode); -Iterate over all the pages in the tablespace. -@param iter - Tablespace iterator -@param block - block to use for IO -@param callback - Callback to inspect and update page contents -@retval DB_SUCCESS or error code */ -static -dberr_t -fil_iterate( -/*========*/ + if (!page_compress_buf) + return DB_OUT_OF_MEMORY; + + const bool encrypted= iter.crypt_data != NULL && + iter.crypt_data->should_encrypt(); + byte* const readptr= iter.io_buffer; + block->frame= readptr; + + if (block->page.zip.data) + block->page.zip.data= readptr; + + IORequest read_request(IORequest::READ); + read_request.disable_partial_io_warnings(); + ulint page_no= 0; + bool page_compressed= false; + + dberr_t err= os_file_read_no_error_handling( + read_request, iter.file, readptr, 3 * size, size, 0); + if (err != DB_SUCCESS) + { + ib::error() << iter.filepath << ": os_file_read() failed"; + goto func_exit; + } + + block->page.id.set_page_no(3); + page_no= page_get_page_no(readptr); + + if (page_no != 3) + { +page_corrupted: + ib::warn() << filename() << ": Page 3 at offset " + << 3 * size << " looks corrupted."; + err= DB_CORRUPTION; + goto func_exit; + } + + page_compressed= fil_page_is_compressed_encrypted(readptr) || + fil_page_is_compressed(readptr); + + if (page_compressed && block->page.zip.data) + goto page_corrupted; + + if (encrypted) + { + if (!fil_space_verify_crypt_checksum(readptr, get_page_size())) + goto page_corrupted; + + if (!fil_space_decrypt(iter.crypt_data, readptr, + get_page_size(), readptr, &err) || + err != DB_SUCCESS) + goto func_exit; + } + + if (page_compressed) + { + ulint compress_length = fil_page_decompress(page_compress_buf, readptr); + ut_ad(compress_length != srv_page_size); + if (compress_length == 0) + goto page_corrupted; + } + else if (buf_page_is_corrupted( + false, readptr, get_page_size(), NULL)) + goto page_corrupted; + + err = this->operator()(block); +func_exit: + free(page_compress_buf); + return err; +} + +static dberr_t fil_iterate( const fil_iterator_t& iter, buf_block_t* block, AbstractCallback& callback) @@ -3877,7 +3960,7 @@ fil_tablespace_iterate( block->page.zip.data = block->frame + UNIV_PAGE_SIZE; } - err = fil_iterate(iter, block, callback); + err = callback.run(iter, block); if (iter.crypt_data) { fil_space_destroy_crypt_data(&iter.crypt_data); @@ -4022,6 +4105,16 @@ row_import_for_mysql( cfg.m_page_size.copy_from(univ_page_size); + if (UT_LIST_GET_LEN(table->indexes) > 1) { + ib_errf(trx->mysql_thd, IB_LOG_LEVEL_ERROR, + ER_INTERNAL_ERROR, + "Drop all secondary indexes before importing " + "table %s when .cfg file is missing.", + table->name.m_name); + err = DB_ERROR; + return row_import_error(prebuilt, trx, err); + } + FetchIndexRootPages fetchIndexRootPages(table, trx); err = fil_tablespace_iterate( From 2d259187a2f585ef6bc5bd66997975ebc4264c66 Mon Sep 17 00:00:00 2001 From: Vlad Lesin Date: Wed, 21 Jul 2021 17:55:51 +0300 Subject: [PATCH 13/20] MDEV-26206 gap lock is not set if implicit lock exists If lock type is LOCK_GAP or LOCK_ORDINARY, and the transaction holds implicit lock for the record, then explicit gap-lock will not be set for the record, as lock_rec_convert_impl_to_expl() returns true and lock_rec_convert_impl_to_expl() bypasses lock_rec_lock() call. The fix converts explicit lock to implicit one if requested lock type is not LOCK_REC_NOT_GAP. innodb_information_schema test result is also changed as after the fix the following statements execution: SET autocommit=0; INSERT INTO t1 VALUES (5,10); SELECT * FROM t1 FOR UPDATE; leads to additional gap lock requests. --- .../r/implicit_gap_lock_convertion.result | 17 +++++++++++++++ .../innodb/r/innodb_information_schema.result | 2 +- .../t/implicit_gap_lock_convertion.test | 21 +++++++++++++++++++ storage/innobase/lock/lock0lock.cc | 6 ++++-- 4 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 mysql-test/suite/innodb/r/implicit_gap_lock_convertion.result create mode 100644 mysql-test/suite/innodb/t/implicit_gap_lock_convertion.test diff --git a/mysql-test/suite/innodb/r/implicit_gap_lock_convertion.result b/mysql-test/suite/innodb/r/implicit_gap_lock_convertion.result new file mode 100644 index 00000000000..fd197324c3e --- /dev/null +++ b/mysql-test/suite/innodb/r/implicit_gap_lock_convertion.result @@ -0,0 +1,17 @@ +CREATE TABLE t(a INT UNSIGNED PRIMARY KEY) ENGINE=InnoDB; +INSERT INTO t VALUES (10), (30); +connect con1,localhost,root,,; +SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; +BEGIN; +INSERT INTO t VALUES (20); +SELECT * FROM t WHERE a BETWEEN 10 AND 30; +a +10 +20 +30 +connection default; +SET session innodb_lock_wait_timeout=1; +INSERT INTO t VALUES (15); +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +disconnect con1; +DROP TABLE t; diff --git a/mysql-test/suite/innodb/r/innodb_information_schema.result b/mysql-test/suite/innodb/r/innodb_information_schema.result index 766d5f47c2d..f8b53f1b2c8 100644 --- a/mysql-test/suite/innodb/r/innodb_information_schema.result +++ b/mysql-test/suite/innodb/r/innodb_information_schema.result @@ -45,7 +45,7 @@ trx_last_foreign_key_error varchar(256) YES NULL trx_is_read_only int(1) NO 0 trx_autocommit_non_locking int(1) NO 0 trx_state trx_weight trx_tables_in_use trx_tables_locked trx_rows_locked trx_rows_modified trx_concurrency_tickets trx_isolation_level trx_unique_checks trx_foreign_key_checks -RUNNING 3 0 1 5 1 0 REPEATABLE READ 1 1 +RUNNING 3 0 1 6 1 0 REPEATABLE READ 1 1 trx_isolation_level trx_unique_checks trx_foreign_key_checks SERIALIZABLE 0 0 trx_state trx_isolation_level trx_last_foreign_key_error diff --git a/mysql-test/suite/innodb/t/implicit_gap_lock_convertion.test b/mysql-test/suite/innodb/t/implicit_gap_lock_convertion.test new file mode 100644 index 00000000000..bf2d09ffb2e --- /dev/null +++ b/mysql-test/suite/innodb/t/implicit_gap_lock_convertion.test @@ -0,0 +1,21 @@ +--source include/have_innodb.inc +--source include/count_sessions.inc + +CREATE TABLE t(a INT UNSIGNED PRIMARY KEY) ENGINE=InnoDB; + +INSERT INTO t VALUES (10), (30); + +--connect (con1,localhost,root,,) +SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; +BEGIN; +INSERT INTO t VALUES (20); +SELECT * FROM t WHERE a BETWEEN 10 AND 30; + +--connection default +SET session innodb_lock_wait_timeout=1; +--error ER_LOCK_WAIT_TIMEOUT +INSERT INTO t VALUES (15); + +--disconnect con1 +DROP TABLE t; +--source include/wait_until_count_sessions.inc diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc index 5c45d38525f..37e23b56dfc 100644 --- a/storage/innobase/lock/lock0lock.cc +++ b/storage/innobase/lock/lock0lock.cc @@ -5802,7 +5802,8 @@ lock_sec_rec_read_check_and_lock( if (!page_rec_is_supremum(rec) && page_get_max_trx_id(block->frame) >= trx_sys.get_min_trx_id() && lock_rec_convert_impl_to_expl(thr_get_trx(thr), block, rec, - index, offsets)) { + index, offsets) + && gap_mode == LOCK_REC_NOT_GAP) { /* We already hold an implicit exclusive lock. */ return DB_SUCCESS; } @@ -5884,7 +5885,8 @@ lock_clust_rec_read_check_and_lock( if (heap_no != PAGE_HEAP_NO_SUPREMUM && lock_rec_convert_impl_to_expl(thr_get_trx(thr), block, rec, - index, offsets)) { + index, offsets) + && gap_mode == LOCK_REC_NOT_GAP) { /* We already hold an implicit exclusive lock. */ return DB_SUCCESS; } From 890f2ad76975d66bfc78f54ec38dfa95c7248bdd Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Fri, 16 Jul 2021 00:39:39 +0300 Subject: [PATCH 14/20] MDEV-20931 ALTER...IMPORT can crash the server Main idea: don't log-and-crash but propogate error to the upper layers of stack to handle it and show to a user. --- .../suite/innodb/r/import_corrupted.result | 30 ++++++++ .../suite/innodb/t/import_corrupted.test | 68 +++++++++++++++++++ storage/innobase/btr/btr0btr.cc | 22 ++++-- storage/innobase/btr/btr0cur.cc | 10 +-- storage/innobase/btr/btr0defragment.cc | 3 +- storage/innobase/buf/buf0buf.cc | 4 ++ storage/innobase/buf/buf0rea.cc | 3 +- storage/innobase/fil/fil0fil.cc | 57 ++++++++++------ storage/innobase/include/btr0btr.h | 2 +- 9 files changed, 166 insertions(+), 33 deletions(-) create mode 100644 mysql-test/suite/innodb/r/import_corrupted.result create mode 100644 mysql-test/suite/innodb/t/import_corrupted.test diff --git a/mysql-test/suite/innodb/r/import_corrupted.result b/mysql-test/suite/innodb/r/import_corrupted.result new file mode 100644 index 00000000000..c0474ebbb1d --- /dev/null +++ b/mysql-test/suite/innodb/r/import_corrupted.result @@ -0,0 +1,30 @@ +call mtr.add_suppression("Table `test`.`t2` should have 2 indexes but the tablespace has 1 indexes"); +call mtr.add_suppression("Index for table 't2' is corrupt; try to repair it"); +call mtr.add_suppression("Trying to read page number 23 in space .*, space name test/t2, which is outside the tablespace bounds. Byte offset 0, len 16384"); +CREATE TABLE t1 ( +id INT AUTO_INCREMENT PRIMARY KEY, +not_id INT, +data CHAR(255), +data2 BLOB +) ENGINE=INNODB; +ALTER TABLE t1 MODIFY not_id INT UNIQUE KEY; +connect purge_control,localhost,root,,; +START TRANSACTION WITH CONSISTENT SNAPSHOT; +connection default; +DELETE FROM t1 WHERE id % 2 = 1; +FLUSH TABLES t1 FOR EXPORT; +UNLOCK TABLES; +connection purge_control; +COMMIT; +connection default; +DROP TABLE t1; +CREATE TABLE t2 ( +id INT AUTO_INCREMENT PRIMARY KEY, +not_id INT UNIQUE KEY, +data CHAR(255), +data2 BLOB +) ENGINE=INNODB; +ALTER TABLE t2 DISCARD TABLESPACE; +ALTER TABLE t2 IMPORT TABLESPACE; +ERROR HY000: Index for table 't2' is corrupt; try to repair it +DROP TABLE t2; diff --git a/mysql-test/suite/innodb/t/import_corrupted.test b/mysql-test/suite/innodb/t/import_corrupted.test new file mode 100644 index 00000000000..ad8db5eb339 --- /dev/null +++ b/mysql-test/suite/innodb/t/import_corrupted.test @@ -0,0 +1,68 @@ +--source include/have_innodb.inc + +call mtr.add_suppression("Table `test`.`t2` should have 2 indexes but the tablespace has 1 indexes"); +call mtr.add_suppression("Index for table 't2' is corrupt; try to repair it"); +call mtr.add_suppression("Trying to read page number 23 in space .*, space name test/t2, which is outside the tablespace bounds. Byte offset 0, len 16384"); + +let MYSQLD_DATADIR = `SELECT @@datadir`; + +CREATE TABLE t1 ( + id INT AUTO_INCREMENT PRIMARY KEY, + not_id INT, + data CHAR(255), + data2 BLOB +) ENGINE=INNODB; + +--disable_query_log +--let i = 0 +while ($i != 1000) { + eval INSERT INTO t1 VALUES (DEFAULT, $i, REPEAT('b', 255), REPEAT('a', 5000)); + --inc $i +} +--enable_query_log + +ALTER TABLE t1 MODIFY not_id INT UNIQUE KEY; + +connect (purge_control,localhost,root,,); +START TRANSACTION WITH CONSISTENT SNAPSHOT; +connection default; + +DELETE FROM t1 WHERE id % 2 = 1; + +FLUSH TABLES t1 FOR EXPORT; + +--copy_file $MYSQLD_DATADIR/test/t1.ibd $MYSQLD_DATADIR/test/tmp.ibd +--copy_file $MYSQLD_DATADIR/test/t1.cfg $MYSQLD_DATADIR/test/tmp.cfg + +perl; +use strict; +die unless open(FILE, "+<$ENV{MYSQLD_DATADIR}/test/tmp.ibd"); +die unless truncate(FILE, 16384*23); +close(FILE); +EOF + +UNLOCK TABLES; +connection purge_control; +COMMIT; +connection default; +DROP TABLE t1; + +CREATE TABLE t2 ( + id INT AUTO_INCREMENT PRIMARY KEY, + not_id INT UNIQUE KEY, + data CHAR(255), + data2 BLOB +) ENGINE=INNODB; + +ALTER TABLE t2 DISCARD TABLESPACE; + +--copy_file $MYSQLD_DATADIR/test/tmp.ibd $MYSQLD_DATADIR/test/t2.ibd +--copy_file $MYSQLD_DATADIR/test/tmp.cfg $MYSQLD_DATADIR/test/t2.cfg + +--error ER_NOT_KEYFILE +ALTER TABLE t2 IMPORT TABLESPACE; + +DROP TABLE t2; + +--remove_file $MYSQLD_DATADIR/test/tmp.ibd +--remove_file $MYSQLD_DATADIR/test/t2.ibd diff --git a/storage/innobase/btr/btr0btr.cc b/storage/innobase/btr/btr0btr.cc index f7fe4413086..a8ab25f12e9 100644 --- a/storage/innobase/btr/btr0btr.cc +++ b/storage/innobase/btr/btr0btr.cc @@ -3141,7 +3141,7 @@ func_exit: @param[in,out] page page to remove @param[in] index index tree @param[in,out] mtr mini-transaction */ -void +dberr_t btr_level_list_remove_func( ulint space, const page_size_t& page_size, @@ -3184,6 +3184,10 @@ btr_level_list_remove_func( page_id_t(space, next_page_no), page_size, RW_X_LATCH, index, mtr); + if (!next_block) { + return DB_ERROR; + } + page_t* next_page = buf_block_get_frame(next_block); #ifdef UNIV_BTR_DEBUG @@ -3196,6 +3200,8 @@ btr_level_list_remove_func( buf_block_get_page_zip(next_block), prev_page_no, mtr); } + + return DB_SUCCESS; } /****************************************************************//** @@ -3675,7 +3681,10 @@ retry: btr_search_drop_page_hash_index(block); /* Remove the page from the level list */ - btr_level_list_remove(space, page_size, page, index, mtr); + if (DB_SUCCESS != btr_level_list_remove(space, page_size, + page, index, mtr)) { + goto err_exit; + } if (dict_index_is_spatial(index)) { rec_t* my_rec = father_cursor.page_cur.rec; @@ -3807,7 +3816,11 @@ retry: #endif /* UNIV_BTR_DEBUG */ /* Remove the page from the level list */ - btr_level_list_remove(space, page_size, (page_t*)page, index, mtr); + if (DB_SUCCESS != btr_level_list_remove(space, page_size, + (page_t*)page, + index, mtr)) { + goto err_exit; + } ut_ad(btr_node_ptr_get_child_page_no( btr_cur_get_rec(&father_cursor), offsets) @@ -4186,7 +4199,8 @@ btr_discard_page( } /* Remove the page from the level list */ - btr_level_list_remove(space, page_size, page, index, mtr); + ut_a(DB_SUCCESS == btr_level_list_remove(space, page_size, page, + index, mtr)); #ifdef UNIV_ZIP_DEBUG { diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc index 3bbddb79a0c..8d0a34d07a1 100644 --- a/storage/innobase/btr/btr0cur.cc +++ b/storage/innobase/btr/btr0cur.cc @@ -344,10 +344,12 @@ btr_cur_latch_leaves( page_size, RW_X_LATCH, cursor->index, mtr); latch_leaves.blocks[2] = get_block; #ifdef UNIV_BTR_DEBUG - ut_a(page_is_comp(get_block->frame) - == page_is_comp(page)); - ut_a(btr_page_get_prev(get_block->frame) - == page_get_page_no(page)); + if (get_block) { + ut_a(page_is_comp(get_block->frame) + == page_is_comp(page)); + ut_a(btr_page_get_prev(get_block->frame) + == page_get_page_no(page)); + } #endif /* UNIV_BTR_DEBUG */ if (spatial) { cursor->rtr_info->tree_blocks[ diff --git a/storage/innobase/btr/btr0defragment.cc b/storage/innobase/btr/btr0defragment.cc index 645334cbf4d..38ef1db9cec 100644 --- a/storage/innobase/btr/btr0defragment.cc +++ b/storage/innobase/btr/btr0defragment.cc @@ -487,7 +487,8 @@ btr_defragment_merge_pages( lock_update_merge_left(to_block, orig_pred, from_block); btr_search_drop_page_hash_index(from_block); - btr_level_list_remove(space, page_size, (page_t*)from_page, index, mtr); + ut_a(DB_SUCCESS == btr_level_list_remove(space, page_size, + (page_t*)from_page, index, mtr)); btr_page_get_father(index, from_block, mtr, &parent); btr_cur_node_ptr_delete(&parent, mtr); /* btr_blob_dbg_remove(from_page, index, diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc index 3f399024b13..4c47c01bf63 100644 --- a/storage/innobase/buf/buf0buf.cc +++ b/storage/innobase/buf/buf0buf.cc @@ -4360,6 +4360,10 @@ loop: return (NULL); } + if (local_err == DB_IO_ERROR) { + return NULL; + } + ib::fatal() << "Unable to read page " << page_id << " into the buffer pool after " << BUF_PAGE_READ_MAX_RETRIES diff --git a/storage/innobase/buf/buf0rea.cc b/storage/innobase/buf/buf0rea.cc index 6b68e9f8fa5..c3f23df7509 100644 --- a/storage/innobase/buf/buf0rea.cc +++ b/storage/innobase/buf/buf0rea.cc @@ -201,7 +201,8 @@ buf_read_page_low( } return(0); } else if (IORequest::ignore_missing(type) - || *err == DB_TABLESPACE_DELETED) { + || *err == DB_TABLESPACE_DELETED + || *err == DB_IO_ERROR) { buf_read_page_handle_error(bpage); return(0); } diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc index a727215b433..a0542825891 100644 --- a/storage/innobase/fil/fil0fil.cc +++ b/storage/innobase/fil/fil0fil.cc @@ -4794,27 +4794,30 @@ fil_node_complete_io(fil_node_t* node, const IORequest& type) } } -/** Report information about an invalid page access. */ -static -void -fil_report_invalid_page_access( - ulint block_offset, /*!< in: block offset */ - ulint space_id, /*!< in: space id */ - const char* space_name, /*!< in: space name */ - ulint byte_offset, /*!< in: byte offset */ - ulint len, /*!< in: I/O length */ - bool is_read) /*!< in: I/O type */ +/** Compose error message about an invalid page access. +@param[in] block_offset block offset +@param[in] space_id space id +@param[in] space_name space name +@param[in] byte_offset byte offset +@param[in] len I/O length +@param[in] is_read I/O type +@return std::string with error message */ +static std::string fil_invalid_page_access_msg(size_t block_offset, + size_t space_id, + const char *space_name, + size_t byte_offset, size_t len, + bool is_read) { - ib::fatal() - << "Trying to " << (is_read ? "read" : "write") - << " page number " << block_offset << " in" - " space " << space_id << ", space name " << space_name << "," - " which is outside the tablespace bounds. Byte offset " - << byte_offset << ", len " << len << - (space_id == 0 && !srv_was_started - ? "Please check that the configuration matches" - " the InnoDB system tablespace location (ibdata files)" - : ""); + std::stringstream ss; + ss << "Trying to " << (is_read ? "read" : "write") << " page number " + << block_offset << " in space " << space_id << ", space name " + << space_name << ", which is outside the tablespace bounds. Byte offset " + << byte_offset << ", len " << len + << (space_id == 0 && !srv_was_started + ? "Please check that the configuration matches" + " the InnoDB system tablespace location (ibdata files)" + : ""); + return ss.str(); } /** Reads or writes data. This operation could be asynchronous (aio). @@ -4951,7 +4954,17 @@ fil_io( return(DB_ERROR); } - fil_report_invalid_page_access( + if (space->purpose == FIL_TYPE_IMPORT) { + mutex_exit(&fil_system->mutex); + ib::error() << fil_invalid_page_access_msg( + page_id.page_no(), page_id.space(), + space->name, byte_offset, len, + req_type.is_read()); + + return DB_IO_ERROR; + } + + ib::fatal() << fil_invalid_page_access_msg( page_id.page_no(), page_id.space(), space->name, byte_offset, len, req_type.is_read()); @@ -5032,7 +5045,7 @@ fil_io( return(DB_ERROR); } - fil_report_invalid_page_access( + ib::fatal() << fil_invalid_page_access_msg( page_id.page_no(), page_id.space(), space->name, byte_offset, len, req_type.is_read()); } diff --git a/storage/innobase/include/btr0btr.h b/storage/innobase/include/btr0btr.h index 29ece955702..04f2cd0f160 100644 --- a/storage/innobase/include/btr0btr.h +++ b/storage/innobase/include/btr0btr.h @@ -731,7 +731,7 @@ btr_validate_index( /*************************************************************//** Removes a page from the level list of pages. */ UNIV_INTERN -void +MY_ATTRIBUTE((warn_unused_result)) dberr_t btr_level_list_remove_func( /*=======================*/ ulint space, /*!< in: space where removed */ From f73eea4984b632c1955211b9ea5b54be9dd56975 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 18 Aug 2021 12:10:31 +0300 Subject: [PATCH 15/20] MDEV-26131 fixup: ./mtr --embedded encryption.innodb_import --- mysql-test/suite/encryption/t/innodb_import.test | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/suite/encryption/t/innodb_import.test b/mysql-test/suite/encryption/t/innodb_import.test index 12c8d8bb017..791a1757878 100644 --- a/mysql-test/suite/encryption/t/innodb_import.test +++ b/mysql-test/suite/encryption/t/innodb_import.test @@ -16,6 +16,7 @@ UNLOCK TABLES; ALTER TABLE t2 IMPORT TABLESPACE; ALTER TABLE t2 DROP KEY idx; +--replace_regex /opening '.*\/test\//opening '.\/test\// ALTER TABLE t2 IMPORT TABLESPACE; SELECT * FROM t2; DROP TABLE t1, t2; From 0dec71ca53729bd1a565bdc800e64008b44ffa48 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Wed, 18 Aug 2021 16:07:15 +1000 Subject: [PATCH 16/20] MDEV-26350: select_lex->ref_pointer_array.size() % 5 == 0 Due to an integer overflow an invalid size of ref_pointer_array could be allocated. Using size_t allows this continue. Allocation failures are handled gracefully if the value is too big. Thanks to Zuming Jiang for the bug report and fuzzing MariaDB. Reviewer: Sanja --- sql/sql_lex.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index eaaa3139c59..b7ed632ed12 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -2698,7 +2698,7 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num) prepared statement */ Query_arena *arena= thd->stmt_arena; - const uint n_elems= (n_sum_items + + const size_t n_elems= (n_sum_items + n_child_sum_items + item_list.elements + select_n_reserved + @@ -2706,7 +2706,8 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num) select_n_where_fields + order_group_num + hidden_bit_fields + - fields_in_window_functions) * 5; + fields_in_window_functions) * (size_t) 5; + DBUG_ASSERT(n_elems % 5 == 0); if (!ref_pointer_array.is_null()) { /* From dc3a350df665b8e9bddc96365cec23a525f6b0b4 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Wed, 18 Aug 2021 13:31:32 +0300 Subject: [PATCH 17/20] MDEV-18734 ASAN additional fix for 10.3 Do swap_blobs() for new partition_read_multi_range mode. --- sql/ha_partition.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index bae15892ba3..fc1f0ebcb4e 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -7956,6 +7956,11 @@ int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same) DBUG_PRINT("info", ("m_mrr_range_current->id: %u", m_mrr_range_current->id)); memcpy(rec_buf, table->record[0], m_rec_length); + if (table->s->blob_fields) + { + Ordered_blob_storage **storage= *((Ordered_blob_storage ***) part_rec_buf_ptr); + swap_blobs(rec_buf, storage, false); + } if (((PARTITION_KEY_MULTI_RANGE *) m_range_info[part_id])->id != m_mrr_range_current->id) { From 1b45e05ccefc6a00513516b8bc0fb41a42d1b695 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Mon, 19 Jul 2021 22:17:51 +0300 Subject: [PATCH 18/20] MDEV-21555 Assertion secondary index is out of sync on delete from versioned table Delete-marked record is on the secondary index and the clustered index already purged the corresponding record. We cannot detect if such record is historical and we should not: the algorithm of row_ins_check_foreign_constraint() skips such record anyway. --- mysql-test/suite/versioning/r/foreign.result | 13 ++++++++ mysql-test/suite/versioning/t/foreign.test | 18 +++++++++++ storage/innobase/row/row0ins.cc | 34 ++++++++++---------- 3 files changed, 48 insertions(+), 17 deletions(-) diff --git a/mysql-test/suite/versioning/r/foreign.result b/mysql-test/suite/versioning/r/foreign.result index e54afdbc74e..d157916c60c 100644 --- a/mysql-test/suite/versioning/r/foreign.result +++ b/mysql-test/suite/versioning/r/foreign.result @@ -443,3 +443,16 @@ pk f1 f2 left(f3, 4) check_row_ts(row_start, row_end) 1 8 8 SHOR HISTORICAL ROW 2 8 8 LONG HISTORICAL ROW drop table t1; +# +# MDEV-21555 Assertion secondary index is out of sync on delete from versioned table +# +create table t1 (a int, b int as (a + 1) virtual, key(a)) engine=innodb with system versioning; +set foreign_key_checks= off; +insert into t1 (a) values (1), (2); +alter table t1 add foreign key (b) references t1 (a), algorithm=copy; +update t1 set a= null where a = 1; +delete from t1 where a is null; +set foreign_key_checks= on; +delete history from t1; +delete from t1; +drop table t1; diff --git a/mysql-test/suite/versioning/t/foreign.test b/mysql-test/suite/versioning/t/foreign.test index 725f51f0660..1c834719e0f 100644 --- a/mysql-test/suite/versioning/t/foreign.test +++ b/mysql-test/suite/versioning/t/foreign.test @@ -476,4 +476,22 @@ select pk, f1, f2, left(f3, 4), check_row_ts(row_start, row_end) from t1 for sys # cleanup drop table t1; +--echo # +--echo # MDEV-21555 Assertion secondary index is out of sync on delete from versioned table +--echo # +create table t1 (a int, b int as (a + 1) virtual, key(a)) engine=innodb with system versioning; + +set foreign_key_checks= off; +insert into t1 (a) values (1), (2); +alter table t1 add foreign key (b) references t1 (a), algorithm=copy; +update t1 set a= null where a = 1; +delete from t1 where a is null; +set foreign_key_checks= on; + +delete history from t1; +delete from t1; + +# cleanup +drop table t1; + --source suite/versioning/common_finish.inc diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc index 380ef7def31..ae105743d45 100644 --- a/storage/innobase/row/row0ins.cc +++ b/storage/innobase/row/row0ins.cc @@ -1666,23 +1666,6 @@ row_ins_check_foreign_constraint( cmp = cmp_dtuple_rec(entry, rec, offsets); if (cmp == 0) { - if (check_table->versioned()) { - bool history_row = false; - - if (check_index->is_primary()) { - history_row = check_index-> - vers_history_row(rec, offsets); - } else if (check_index-> - vers_history_row(rec, history_row)) - { - break; - } - - if (history_row) { - continue; - } - } - if (rec_get_deleted_flag(rec, rec_offs_comp(offsets))) { /* In delete-marked records, DB_TRX_ID must @@ -1704,6 +1687,23 @@ row_ins_check_foreign_constraint( goto end_scan; } } else { + if (check_table->versioned()) { + bool history_row = false; + + if (check_index->is_primary()) { + history_row = check_index-> + vers_history_row(rec, + offsets); + } else if (check_index-> + vers_history_row(rec, + history_row)) { + break; + } + + if (history_row) { + continue; + } + } /* Found a matching record. Lock only a record because we can allow inserts into gaps */ From 0edf44c53a6b63e0f026b30034fd5f1f36a09556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 18 Aug 2021 16:28:34 +0300 Subject: [PATCH 19/20] MDEV-20931 fixup: innodb.import_corrupted test case cleanup --- mysql-test/suite/innodb/t/import_corrupted.test | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/suite/innodb/t/import_corrupted.test b/mysql-test/suite/innodb/t/import_corrupted.test index ad8db5eb339..050bde5d97d 100644 --- a/mysql-test/suite/innodb/t/import_corrupted.test +++ b/mysql-test/suite/innodb/t/import_corrupted.test @@ -65,4 +65,5 @@ ALTER TABLE t2 IMPORT TABLESPACE; DROP TABLE t2; --remove_file $MYSQLD_DATADIR/test/tmp.ibd +--remove_file $MYSQLD_DATADIR/test/tmp.cfg --remove_file $MYSQLD_DATADIR/test/t2.ibd From da6f4d5164aae2642b1763577e1997d0098cc947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 18 Aug 2021 16:45:09 +0300 Subject: [PATCH 20/20] MDEV-26131 fixup PageConverter::update_index_page(): Always validate the PAGE_INDEX_ID. Failure to do so could cause a crash when iterating secondary index pages. This was caught by the 10.4 test innodb.full_crc32_import. --- storage/innobase/row/row0import.cc | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/storage/innobase/row/row0import.cc b/storage/innobase/row/row0import.cc index 699392d2a5b..f4df44b3e2b 100644 --- a/storage/innobase/row/row0import.cc +++ b/storage/innobase/row/row0import.cc @@ -696,9 +696,7 @@ dberr_t FetchIndexRootPages::operator()(buf_block_t* block) UNIV_NOTHROW const page_t* page = get_frame(block); - index_id_t id = btr_page_get_index_id(page); - - m_index.m_id = id; + m_index.m_id = btr_page_get_index_id(page); m_index.m_page_no = block->page.id.page_no(); /* Check that the tablespace flags match the table flags. */ @@ -1900,11 +1898,14 @@ PageConverter::update_index_page( if (is_free(block->page.id.page_no())) { return(DB_SUCCESS); - } else if ((id = btr_page_get_index_id(page)) != m_index->m_id && !m_cfg->m_missing) { - + } else if ((id = btr_page_get_index_id(page)) != m_index->m_id) { row_index_t* index = find_index(id); if (UNIV_UNLIKELY(!index)) { + if (m_cfg->m_missing) { + return DB_SUCCESS; + } + ib::error() << "Page for tablespace " << m_space << " is index page with id " << id << " but that index is not found from"