From 175c9fe1d57daae2cf5fbc514cfffc27a55afcb0 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 3 Aug 2021 09:50:22 +0200 Subject: [PATCH 01/41] 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/41] 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/41] 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/41] 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 527be044d5c1d916a98e742a43d6d8731f2ceccf Mon Sep 17 00:00:00 2001 From: Daniel Bartholomew Date: Thu, 5 Aug 2021 09:59:21 -0400 Subject: [PATCH 05/41] bump the VERSION --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index b8a2d3b2f92..6abe981f6c2 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ MYSQL_VERSION_MAJOR=10 MYSQL_VERSION_MINOR=4 -MYSQL_VERSION_PATCH=21 +MYSQL_VERSION_PATCH=22 SERVER_MATURITY=stable From 20a9f0b5110ddb04862e8b9cc8a1150617ad653a Mon Sep 17 00:00:00 2001 From: Daniel Bartholomew Date: Thu, 5 Aug 2021 10:18:19 -0400 Subject: [PATCH 06/41] bump the VERSION --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 34e37854e60..263ab5626e3 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ MYSQL_VERSION_MAJOR=10 MYSQL_VERSION_MINOR=5 -MYSQL_VERSION_PATCH=12 +MYSQL_VERSION_PATCH=13 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 07/41] 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 74cb160992c743d4121ba69260d93f68ac1f4c68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Fri, 6 Aug 2021 10:08:20 +0300 Subject: [PATCH 08/41] Fix test failure on galera_as_slave_replay by adding wait_conditions --- .../galera/r/galera_as_slave_replay.result | 18 ++++++++++++++---- .../suite/galera/t/galera_as_slave_replay.test | 12 +++++++++--- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/mysql-test/suite/galera/r/galera_as_slave_replay.result b/mysql-test/suite/galera/r/galera_as_slave_replay.result index 3c2cea19179..d81795eeed9 100644 --- a/mysql-test/suite/galera/r/galera_as_slave_replay.result +++ b/mysql-test/suite/galera/r/galera_as_slave_replay.result @@ -84,11 +84,21 @@ SET GLOBAL wsrep_provider_options = 'dbug='; SET GLOBAL wsrep_provider_options = 'signal=commit_monitor_enter_sync'; SET DEBUG_SYNC = "RESET"; connection node_2a; -set session wsrep_sync_wait=15; -SELECT COUNT(*) = 1 FROM test.t1 WHERE f2 = 'e'; -COUNT(*) = 1 -1 set session wsrep_sync_wait=0; +SELECT * from test.t1; +f1 f2 +1 a +2 b +3 e +4 d +connection node_1; +SELECT * from test.t1; +f1 f2 +1 a +2 b +3 e +4 d +connection node_2a; STOP SLAVE; RESET SLAVE; DROP TABLE t1; diff --git a/mysql-test/suite/galera/t/galera_as_slave_replay.test b/mysql-test/suite/galera/t/galera_as_slave_replay.test index 47f70bda721..2e8f45a047b 100644 --- a/mysql-test/suite/galera/t/galera_as_slave_replay.test +++ b/mysql-test/suite/galera/t/galera_as_slave_replay.test @@ -185,11 +185,17 @@ SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_cb"; SET DEBUG_SYNC = "RESET"; --connection node_2a - -set session wsrep_sync_wait=15; -SELECT COUNT(*) = 1 FROM test.t1 WHERE f2 = 'e'; set session wsrep_sync_wait=0; +--let $wait_condition = SELECT COUNT(*) = 1 FROM test.t1 where f2 = 'e' +--source include/wait_condition.inc +SELECT * from test.t1; +--connection node_1 +--let $wait_condition = SELECT COUNT(*) = 1 FROM test.t1 where f2 = 'e' +--source include/wait_condition.inc +SELECT * from test.t1; + +--connection node_2a STOP SLAVE; RESET SLAVE; From f725020ff7224f4d641cf0e11fe2a83f383e2e7b Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 9 Aug 2021 11:52:03 +0200 Subject: [PATCH 09/41] 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 10db7fcfa6ddea3e8ef047f095c4f136a1fb42ce Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Tue, 10 Aug 2021 23:22:04 +0400 Subject: [PATCH 10/41] MENT-977 log priv host / priv user. Add server functions to provide necessary data. --- sql/sql_class.cc | 26 ++++++++++++++++++++++++++ sql/sql_class.h | 2 ++ 2 files changed, 28 insertions(+) diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 31d9979b22c..37580568de5 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -4767,6 +4767,32 @@ extern "C" void thd_create_random_password(MYSQL_THD thd, } +extern "C" const char *thd_priv_host(MYSQL_THD thd, size_t *length) +{ + const Security_context *sctx= thd->security_ctx; + if (!sctx) + { + *length= 0; + return NULL; + } + *length= strlen(sctx->priv_host); + return sctx->priv_host; +} + + +extern "C" const char *thd_priv_user(MYSQL_THD thd, size_t *length) +{ + const Security_context *sctx= thd->security_ctx; + if (!sctx) + { + *length= 0; + return NULL; + } + *length= strlen(sctx->priv_user); + return sctx->priv_user; +} + + #ifdef INNODB_COMPATIBILITY_HOOKS /** open a table and add it to thd->open_tables diff --git a/sql/sql_class.h b/sql/sql_class.h index ec031bf06e6..f0b8ee651f3 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -197,6 +197,8 @@ extern MYSQL_PLUGIN_IMPORT const char **errmesg; extern "C" LEX_STRING * thd_query_string (MYSQL_THD thd); extern "C" unsigned long long thd_query_id(const MYSQL_THD thd); extern "C" size_t thd_query_safe(MYSQL_THD thd, char *buf, size_t buflen); +extern "C" const char *thd_priv_user(MYSQL_THD thd, size_t *length); +extern "C" const char *thd_priv_host(MYSQL_THD thd, size_t *length); extern "C" const char *thd_user_name(MYSQL_THD thd); extern "C" const char *thd_client_host(MYSQL_THD thd); extern "C" const char *thd_client_ip(MYSQL_THD thd); From 582cf12f949d462073b01aea4dc61628880d6d60 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 11 Aug 2021 11:40:42 +0200 Subject: [PATCH 11/41] mariabackup - fix string format in error message --- extra/mariabackup/xtrabackup.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extra/mariabackup/xtrabackup.cc b/extra/mariabackup/xtrabackup.cc index 198d39cdeb6..2dd3f3eb929 100644 --- a/extra/mariabackup/xtrabackup.cc +++ b/extra/mariabackup/xtrabackup.cc @@ -3531,7 +3531,7 @@ next_file: if (err == ERROR_NO_MORE_FILES) { status = 1; } else { - msg("readdir_next_file in %s returned %lu", dir, err); + msg("FindNextFile in %s returned %lu", dirname, err); status = -1; } } From 38b79d7295e7a9ed2e4fdd72e6a63505a32c806f Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Wed, 11 Aug 2021 23:00:37 +0400 Subject: [PATCH 12/41] MENT-1019. Audit-plugin related fixes. --- sql/sql_audit.h | 2 +- sql/sql_prepare.cc | 5 +++++ tests/mysql_client_test.c | 6 +++--- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/sql/sql_audit.h b/sql/sql_audit.h index 97317203e34..d76750e9b2a 100644 --- a/sql/sql_audit.h +++ b/sql/sql_audit.h @@ -155,7 +155,7 @@ void mysql_audit_general(THD *thd, uint event_subtype, DBUG_ENTER("mysql_audit_general"); if (mysql_audit_general_enabled()) { - char user_buff[MAX_USER_HOST_SIZE]; + char user_buff[MAX_USER_HOST_SIZE+1]; mysql_event_general event; event.event_subclass= event_subtype; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index a3027ea52fd..06e7921c90d 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -3442,6 +3442,11 @@ static void mysql_stmt_execute_common(THD *thd, stmt_id == LAST_STMT_ID, read_types)) { my_error(ER_MALFORMED_PACKET, MYF(0)); + /* + Let's set the thd->query_string so the audit plugin + can report the executed query that failed. + */ + thd->set_query_inner(stmt->query_string); DBUG_VOID_RETURN; } diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 32681ac20a1..78656850e48 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -5256,10 +5256,10 @@ static void test_manual_sample() { unsigned int param_count; MYSQL_STMT *stmt; - short small_data; - int int_data; + short small_data= 1; + int int_data= 2; int rc; - char str_data[50]; + char str_data[50]= "std_data"; ulonglong affected_rows; MYSQL_BIND my_bind[3]; my_bool is_null; From 070183cfc0d06254b1b4ce75be48bac9a8fa805b Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 12 Aug 2021 18:32:01 +0200 Subject: [PATCH 13/41] MDEV-26325 Shutdown hangs whenever named pipes were used for connections. This was a regression introduced in MDEV-18353, where to CONNECT objects were incorrectly counted for named pipes. --- sql/sql_connect.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sql/sql_connect.h b/sql/sql_connect.h index 1a84cb56e5c..8be6c1aecc0 100644 --- a/sql/sql_connect.h +++ b/sql/sql_connect.h @@ -35,7 +35,10 @@ public: #ifdef _WIN32 HANDLE pipe; CONNECT(HANDLE pipe_arg): pipe(pipe_arg), vio_type(VIO_TYPE_NAMEDPIPE), - scheduler(thread_scheduler), thread_id(0), prior_thr_create_utime(0) {} + scheduler(thread_scheduler), thread_id(0), prior_thr_create_utime(0) + { + count++; + } #endif enum enum_vio_type vio_type; scheduler_functions *scheduler; From 30d33d85cbeb7111898607fa9273bd308199eff7 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Wed, 11 Aug 2021 11:14:45 +1000 Subject: [PATCH 14/41] deb: s390x no WolfSSL workaround as upstream fixed in 4.6.0 https://github.com/wolfSSL/wolfssl/issues/2828 lists 4.6.0 a as the fixed version as we currently have 4.8.0. Since the time of the above issue, Debian has allowed OpenSSL linking. --- debian/rules | 8 -------- 1 file changed, 8 deletions(-) diff --git a/debian/rules b/debian/rules index 5e7da691b00..865784526be 100755 --- a/debian/rules +++ b/debian/rules @@ -52,14 +52,6 @@ ifneq ($(DEB_BUILD_ARCH),$(DEB_HOST_ARCH)) endif endif -# Add extra flag to avoid WolfSSL code crashing the entire mariadbd on s390x. This -# can be removed once upstream has made the code s390x compatible, see -# https://jira.mariadb.org/browse/MDEV-21705 and -# https://github.com/wolfSSL/wolfssl/issues/2828 -ifeq ($(DEB_HOST_ARCH),s390x) - CFLAGS += -DWC_NO_CACHE_RESISTANT -endif - # Add support for verbose builds MAKEFLAGS += VERBOSE=1 From 0268b8712288d46fbd8a43fdef6bada399b68dff Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Wed, 11 Aug 2021 11:28:08 +1000 Subject: [PATCH 15/41] deb: columnstore not 32bit Columnstore badly failed on 32bit. The way Debian triggers somehow doesn't detect the amd64 in the architecture of columnstore so we explicitly disable it to prevent failures on x86_32. The architecture from the control file is sufficient to not build of arm64 and other unsupported achitectures so we don't need to disable columnstore by default. The logic around not building columnstore on Travis/Gitlab ci can be preserved with a autobake-deb.sh restructure. --- debian/autobake-deb.sh | 20 ++++---------------- debian/rules | 21 +++++++++++++-------- 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/debian/autobake-deb.sh b/debian/autobake-deb.sh index 516b842ba57..1363f96dc5a 100755 --- a/debian/autobake-deb.sh +++ b/debian/autobake-deb.sh @@ -17,29 +17,17 @@ set -e # building the deb packages here. export DEB_BUILD_OPTIONS="nocheck $DEB_BUILD_OPTIONS" -# Take the files and part of control from MCS directory -if [[ -d storage/columnstore/columnstore/debian ]] -then - cp -v storage/columnstore/columnstore/debian/mariadb-plugin-columnstore.* debian/ - echo >> debian/control - cat storage/columnstore/columnstore/debian/control >> debian/control - - # ColumnStore is explicitly disabled in the native build, so allow it now - # when build it when triggered by autobake-deb.sh - sed '/-DPLUGIN_COLUMNSTORE=NO/d' -i debian/rules -fi - # General CI optimizations to keep build output smaller if [[ $TRAVIS ]] || [[ $GITLAB_CI ]] then # On both Travis and Gitlab the output log must stay under 4MB so make the # build less verbose - sed '/Add support for verbose builds/,/^$/d' -i debian/rules - # MCOL-4149: ColumnStore builds are so slow and big that they must be skipped on # both Travis-CI and Gitlab-CI - sed 's|$(CMAKEFLAGS)|$(CMAKEFLAGS) -DPLUGIN_COLUMNSTORE=NO|' -i debian/rules - sed "/Package: mariadb-plugin-columnstore/,/^$/d" -i debian/control + sed -e '/Add support for verbose builds/,/^$/d' \ + -e '/ColumnStore is part of the build/,/^$/d' \ + -e 's|$(CMAKEFLAGS)|$(CMAKEFLAGS) -DPLUGIN_COLUMNSTORE=NO|' \ + -i debian/rules fi # Don't build or try to put files in a package for selected plugins and components on Travis-CI diff --git a/debian/rules b/debian/rules index 865784526be..184a7733159 100755 --- a/debian/rules +++ b/debian/rules @@ -38,11 +38,6 @@ else NUMJOBS = 1 endif -# RocksDB cannot build on 32-bit platforms -ifeq (32,$(DEB_HOST_ARCH_BITS)) - CMAKEFLAGS += -DPLUGIN_ROCKSDB=NO -endif - ifneq ($(DEB_BUILD_ARCH),$(DEB_HOST_ARCH)) ifneq (,$(filter $(DEB_HOST_ARCH_CPU),alpha amd64 arm arm64 i386 ia64 m68k mips64el mipsel powerpc ppc64 ppc64el riscv64 s390x sh4 sparc64)) CMAKEFLAGS += -DSTACK_DIRECTION=-1 @@ -74,15 +69,26 @@ ifneq ($(DEB_BUILD_ARCH),$(DEB_HOST_ARCH)) dpkg-architecture -a$(DEB_BUILD_ARCH) -f -c dh_auto_configure --builddirectory=builddir-native dh_auto_build --builddirectory=builddir-native -- import_executables endif + + # ColumnStore is part of the build +ifneq (32,$(DEB_HOST_ARCH_BITS)) + # Take the files and part of control from MCS directory + cp -v storage/columnstore/columnstore/debian/mariadb-plugin-columnstore.* debian/ + # Don't include twice + grep -q '^Package: mariadb-plugin-columnstore$$' debian/control || \ + echo >> debian/control && \ + cat storage/columnstore/columnstore/debian/control >> debian/control +endif + echo "server:Version=$(DEB_VERSION)" >> debian/substvars - # Don't build ColumnStore as part of the native build, only build it when - # triggered by autobake-deb.sh. Saves build time and disk space. + # RocksDB and Column Store cannot build on 32-bit platforms PATH=$${MYSQL_BUILD_PATH:-"/usr/lib/ccache:/usr/local/bin:/usr/bin:/bin"} \ NO_UPDATE_BUILD_VERSION=1 \ dh_auto_configure --builddirectory=$(BUILDDIR) -- \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ $(CMAKEFLAGS) \ + $(if $(findstring $(DEB_HOST_ARCH_BITS),32),-DPLUGIN_ROCKSDB=NO -DPLUGIN_COLUMNSTORE=NO) \ $(if $(filter $(DEB_BUILD_ARCH),$(DEB_HOST_ARCH)),,-DIMPORT_EXECUTABLES=$(CURDIR)/builddir-native/import_executables.cmake) \ -DCOMPILATION_COMMENT="mariadb.org binary distribution" \ -DMYSQL_SERVER_SUFFIX="-$(DEB_VERSION_REVISION)" \ @@ -92,7 +98,6 @@ endif -DPLUGIN_TOKUDB=NO \ -DPLUGIN_CASSANDRA=NO \ -DPLUGIN_AWS_KEY_MANAGEMENT=NO \ - -DPLUGIN_COLUMNSTORE=NO \ -DDEB=$(DEB_VENDOR) # This is needed, otherwise 'make test' will run before binaries have been built From 46c3e7e3537c31a94289033bfeccf3faf8d4069e Mon Sep 17 00:00:00 2001 From: Brandon Nesterenko Date: Fri, 21 May 2021 14:53:43 -0600 Subject: [PATCH 16/41] MDEV-20215: binlog.show_concurrent_rotate failed in buildbot with wrong result Problem: ======= There are two issues that are addressed in this patch: 1) SHOW BINARY LOGS uses caching to store the binary logs that exist in the log directory; however, if new events are written to the logs, the caching strategy is unaware. This is okay for users, as it is okay for SHOW to return slightly old data. The test, however, can result in inconsistent data. It runs two connections concurrently, where one shows the logs, and the other adds a new file. The output of SHOW BINARY LOGS then depends on when the cache is built, with respect to the time that the second connection rotates the logs. 2) There is a race condition between RESET MASTER and SHOW BINARY LOGS. More specifically, where they both need the binary log lock to begin, SHOW BINARY LOGS only needs the lock to build its cache. If RESET MASTER is issued after SHOW BINARY LOGS has built its cache and before it has returned the results, the presented data may be incorrect. Solution: ======== 1) As it is okay for users to see stale data, to make the test consistent, use DEBUG_SYNC to force the race condition (problem 2) to make SHOW BINARY LOGS build a cache before RESET MASTER is called. Then, use additional logic from the next part of the solution to rebuild the cache. 2) Use an Atomic_counter to keep track of the number of times RESET MASTER has been called. If the value of the counter changes after building the cache, the cache should be rebuilt and the analysis should be restarted. Reviewed By: ============ Andrei Elkin: --- .../suite/binlog/r/show_concurrent_rotate.result | 3 ++- .../suite/binlog/t/show_concurrent_rotate.test | 7 ++++++- sql/log.cc | 3 ++- sql/log.h | 6 ++++++ sql/sql_repl.cc | 12 ++++++++++++ 5 files changed, 28 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/binlog/r/show_concurrent_rotate.result b/mysql-test/suite/binlog/r/show_concurrent_rotate.result index cee5de33973..b830b75eeef 100644 --- a/mysql-test/suite/binlog/r/show_concurrent_rotate.result +++ b/mysql-test/suite/binlog/r/show_concurrent_rotate.result @@ -2,9 +2,10 @@ connect con1,localhost,root,,; FLUSH LOGS; FLUSH LOGS; FLUSH LOGS; -SET DEBUG_SYNC= "at_after_lock_index WAIT_FOR con1_go"; +SET DEBUG_SYNC= "at_after_lock_index SIGNAL con1_ready WAIT_FOR con1_go"; SHOW BINARY LOGS; connect con2,localhost,root,,; +SET DEBUG_SYNC= "now WAIT_FOR con1_ready"; RESET MASTER; FLUSH LOGS; SET DEBUG_SYNC= "now SIGNAL con1_go"; diff --git a/mysql-test/suite/binlog/t/show_concurrent_rotate.test b/mysql-test/suite/binlog/t/show_concurrent_rotate.test index 79d36c30a86..b5758e3a883 100644 --- a/mysql-test/suite/binlog/t/show_concurrent_rotate.test +++ b/mysql-test/suite/binlog/t/show_concurrent_rotate.test @@ -8,10 +8,15 @@ FLUSH LOGS; FLUSH LOGS; FLUSH LOGS; -SET DEBUG_SYNC= "at_after_lock_index WAIT_FOR con1_go"; +# This forced synchronization pattern ensures con1 will execute its retry +# path. More specifically, con1 should see that the cache of log files it +# creates during SHOW BINARY LOGS becomes invalidated after con2 completes +# RESET MASTER. +SET DEBUG_SYNC= "at_after_lock_index SIGNAL con1_ready WAIT_FOR con1_go"; --send SHOW BINARY LOGS connect(con2,localhost,root,,); +SET DEBUG_SYNC= "now WAIT_FOR con1_ready"; RESET MASTER; FLUSH LOGS; SET DEBUG_SYNC= "now SIGNAL con1_go"; diff --git a/sql/log.cc b/sql/log.cc index eb82100d16c..93ed664bff9 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -3256,7 +3256,7 @@ MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period) checksum_alg_reset(BINLOG_CHECKSUM_ALG_UNDEF), relay_log_checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF), description_event_for_exec(0), description_event_for_queue(0), - current_binlog_id(0) + current_binlog_id(0), reset_master_count(0) { /* We don't want to initialize locks here as such initialization depends on @@ -4350,6 +4350,7 @@ err: } mysql_cond_broadcast(&COND_xid_list); reset_master_pending--; + reset_master_count++; mysql_mutex_unlock(&LOCK_xid_list); } diff --git a/sql/log.h b/sql/log.h index 8ba39614825..8994d536298 100644 --- a/sql/log.h +++ b/sql/log.h @@ -672,6 +672,11 @@ public: my_off_t last_commit_pos_offset; ulong current_binlog_id; + /* + Tracks the number of times that the master has been reset + */ + Atomic_counter reset_master_count; + MYSQL_BIN_LOG(uint *sync_period); /* note that there's no destructor ~MYSQL_BIN_LOG() ! @@ -881,6 +886,7 @@ public: inline mysql_mutex_t* get_log_lock() { return &LOCK_log; } inline mysql_cond_t* get_bin_log_cond() { return &COND_bin_log_updated; } inline IO_CACHE* get_log_file() { return &log_file; } + inline uint64 get_reset_master_count() { return reset_master_count; } inline void lock_index() { mysql_mutex_lock(&LOCK_index);} inline void unlock_index() { mysql_mutex_unlock(&LOCK_index);} diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 841ff561502..e96129465fe 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -4374,6 +4374,7 @@ bool show_binlogs(THD* thd) Protocol *protocol= thd->protocol; uint retry_count= 0; size_t cur_dir_len; + uint64 expected_reset_masters; DBUG_ENTER("show_binlogs"); if (!mysql_bin_log.is_open()) @@ -4399,6 +4400,7 @@ retry: mysql_mutex_lock(mysql_bin_log.get_log_lock()); mysql_bin_log.lock_index(); mysql_bin_log.raw_get_current_log(&cur); + expected_reset_masters= mysql_bin_log.get_reset_master_count(); mysql_mutex_unlock(mysql_bin_log.get_log_lock()); /* The following call unlocks lock_index */ @@ -4419,6 +4421,16 @@ retry: cur_link->name.str+= dir_len; cur_link->name.length-= dir_len; + if (mysql_bin_log.get_reset_master_count() > expected_reset_masters) + { + /* + Reset master was called after we cached filenames. + Reinitialize the cache. + */ + free_root(&mem_root, MYF(MY_MARK_BLOCKS_FREE)); + goto retry; + } + if (!(strncmp(fname+dir_len, cur.log_file_name+cur_dir_len, length))) cur_link->size= cur.pos; /* The active log, use the active position */ else From 3b29315fdeb496cc896bc5da0982a6ebbea91e23 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Sun, 15 Aug 2021 09:00:06 +1000 Subject: [PATCH 17/41] 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 18/41] 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 19/41] 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 20/41] 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 3d16e0e16c649505f06b39b7f7e800494ba0fef9 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Mon, 16 Aug 2021 15:40:55 +1000 Subject: [PATCH 21/41] deb: columnstore not 32bit (fix) Fix for previous commit, shell logic for repeat configure stages corrected to use subshell. --- debian/rules | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/debian/rules b/debian/rules index 184a7733159..13e5c5e57a7 100755 --- a/debian/rules +++ b/debian/rules @@ -76,8 +76,7 @@ ifneq (32,$(DEB_HOST_ARCH_BITS)) cp -v storage/columnstore/columnstore/debian/mariadb-plugin-columnstore.* debian/ # Don't include twice grep -q '^Package: mariadb-plugin-columnstore$$' debian/control || \ - echo >> debian/control && \ - cat storage/columnstore/columnstore/debian/control >> debian/control + ( echo && cat storage/columnstore/columnstore/debian/control ) >> debian/control endif echo "server:Version=$(DEB_VERSION)" >> debian/substvars 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 22/41] 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 23/41] 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 255313048ca00c48fe78250014570034475a9178 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Mon, 16 Aug 2021 17:02:15 +0530 Subject: [PATCH 24/41] MDEV-26273 InnoDB FTS DDL fails when innodb_force_recovery is set to 2 InnoDB DDL fails when it tries to sync the table when innodb_force_recovery is set to 2. Problem is that fts_optimize_wq is not initialized when there are no background threads. fts_sync_during_ddl() should check whether fts_optimize_wq is initialized. --- mysql-test/suite/innodb_fts/r/sync.result | 10 ++++++++++ mysql-test/suite/innodb_fts/t/sync.test | 14 ++++++++++++++ storage/innobase/fts/fts0opt.cc | 2 ++ 3 files changed, 26 insertions(+) diff --git a/mysql-test/suite/innodb_fts/r/sync.result b/mysql-test/suite/innodb_fts/r/sync.result index 5b8e4810028..861408cbe2d 100644 --- a/mysql-test/suite/innodb_fts/r/sync.result +++ b/mysql-test/suite/innodb_fts/r/sync.result @@ -145,3 +145,13 @@ id title 2 database 3 good DROP TABLE t1; +# +# MDEV-26273 InnoDB fts DDL fails when +# innodb_force_recovery is set to 2 +# +# restart: --innodb_force_recovery=2 +CREATE TABLE t1 (FTS_DOC_ID BIGINT UNSIGNED KEY, +f1 CHAR(200)) ENGINE=InnoDB; +ALTER TABLE t1 ADD FULLTEXT INDEX(f1); +DROP TABLE t1; +# restart diff --git a/mysql-test/suite/innodb_fts/t/sync.test b/mysql-test/suite/innodb_fts/t/sync.test index 6929dce31b8..f16953ba09f 100644 --- a/mysql-test/suite/innodb_fts/t/sync.test +++ b/mysql-test/suite/innodb_fts/t/sync.test @@ -170,3 +170,17 @@ SET GLOBAL innodb_ft_aux_table=default; SELECT * FROM t1 WHERE MATCH(title) AGAINST ('mysql database good'); DROP TABLE t1; + +--echo # +--echo # MDEV-26273 InnoDB fts DDL fails when +--echo # innodb_force_recovery is set to 2 +--echo # + +let $restart_parameters=--innodb_force_recovery=2; +--source include/restart_mysqld.inc +CREATE TABLE t1 (FTS_DOC_ID BIGINT UNSIGNED KEY, + f1 CHAR(200)) ENGINE=InnoDB; +ALTER TABLE t1 ADD FULLTEXT INDEX(f1); +DROP TABLE t1; +let $restart_parameters=; +--source include/restart_mysqld.inc diff --git a/storage/innobase/fts/fts0opt.cc b/storage/innobase/fts/fts0opt.cc index e3c0f8f56e4..df10cf63f36 100644 --- a/storage/innobase/fts/fts0opt.cc +++ b/storage/innobase/fts/fts0opt.cc @@ -3037,6 +3037,8 @@ fts_optimize_shutdown() @param[in] table table to be synced */ void fts_sync_during_ddl(dict_table_t* table) { + if (!fts_optimize_wq) + return; mutex_enter(&fts_optimize_wq->mutex); if (!table->fts->sync_message) { From dc58303cf8a7d0884d44cac54772b0aa506b6e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 16 Aug 2021 17:55:40 +0300 Subject: [PATCH 25/41] MDEV-26372 enforce-storage-engine=InnoDB has no usability as an option to mysqld-install-db Creation of tables by the three names mysql.user, mysql.host, mysql.db was being blocked in the function row_mysql_is_system_table(). Since commit 4abb8216a054e14afbeb81e8529e02bab6fa14ac (MDEV-17658), mysql.user is a view, not a table. Since commit ead9a34a3e934f607c2ea7a6c68f7f6d9d29b5bd (MDEV-15851), mysql.host is not being created at all. Let us remove the special handling of table names in InnoDB, and allow mysql.db to be created in InnoDB. The special handling was originally added in commit e84ef2b747e31235036429760bfda488b82db0bb without any explanation. Reviewed by: Sergei Golubchik --- storage/innobase/row/row0mysql.cc | 46 ++----------------------------- 1 file changed, 3 insertions(+), 43 deletions(-) diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index a6c75f7f450..0453b61b32c 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -100,25 +100,6 @@ static ib_mutex_t row_drop_list_mutex; /** Flag: has row_mysql_drop_list been initialized? */ static bool row_mysql_drop_list_inited; -/*******************************************************************//** -Determine if the given name is a name reserved for MySQL system tables. -@return TRUE if name is a MySQL system table name */ -static -ibool -row_mysql_is_system_table( -/*======================*/ - const char* name) -{ - if (strncmp(name, "mysql/", 6) != 0) { - - return(FALSE); - } - - return(0 == strcmp(name + 6, "host") - || 0 == strcmp(name + 6, "user") - || 0 == strcmp(name + 6, "db")); -} - #ifdef UNIV_DEBUG /** Wait for the background drop list to become empty. */ void @@ -2374,26 +2355,13 @@ row_create_table_for_mysql( DBUG_EXECUTE_IF( "ib_create_table_fail_at_start_of_row_create_table_for_mysql", - goto err_exit; + dict_mem_table_free(table); + trx->op_info = ""; + return DB_ERROR; ); trx->op_info = "creating table"; - if (row_mysql_is_system_table(table->name.m_name)) { - - ib::error() << "Trying to create a MySQL system table " - << table->name << " of type InnoDB. MySQL system" - " tables must be of the MyISAM type!"; -#ifndef DBUG_OFF -err_exit: -#endif /* !DBUG_OFF */ - dict_mem_table_free(table); - - trx->op_info = ""; - - return(DB_ERROR); - } - trx_start_if_not_started_xa(trx, true); heap = mem_heap_create(512); @@ -4196,14 +4164,6 @@ row_rename_table_for_mysql( if (high_level_read_only) { return(DB_READ_ONLY); - - } else if (row_mysql_is_system_table(new_name)) { - - ib::error() << "Trying to create a MySQL system table " - << new_name << " of type InnoDB. MySQL system tables" - " must be of the MyISAM type!"; - - goto funct_exit; } trx->op_info = "renaming table"; From cce33787c37a6a0724e518f0f74a445a3682dfc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 17 Aug 2021 12:01:10 +0300 Subject: [PATCH 26/41] MDEV-16264 fixup: Add missing 'static' --- storage/innobase/srv/srv0srv.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc index ad221dc205c..9525219a656 100644 --- a/storage/innobase/srv/srv0srv.cc +++ b/storage/innobase/srv/srv0srv.cc @@ -1960,7 +1960,7 @@ static std::mutex purge_thd_mutex; extern void* thd_attach_thd(THD*); extern void thd_detach_thd(void *); -THD* acquire_thd(void **ctx) +static THD *acquire_thd(void **ctx) { std::unique_lock lk(purge_thd_mutex); if (purge_thds.empty()) { @@ -1978,7 +1978,7 @@ THD* acquire_thd(void **ctx) return thd; } -void release_thd(THD *thd, void *ctx) +static void release_thd(THD *thd, void *ctx) { thd_detach_thd(ctx); std::unique_lock lk(purge_thd_mutex); From 2d259187a2f585ef6bc5bd66997975ebc4264c66 Mon Sep 17 00:00:00 2001 From: Vlad Lesin Date: Wed, 21 Jul 2021 17:55:51 +0300 Subject: [PATCH 27/41] 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 28/41] 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 112b23969a30ba6441efa5e22a3017435febfa17 Mon Sep 17 00:00:00 2001 From: Leandro Pacheco Date: Fri, 13 Aug 2021 14:19:29 -0300 Subject: [PATCH 29/41] MDEV-26308 : Galera test failure on galera.galera_split_brain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contains following fixes: * allow TOI commands to timeout while trying to acquire TOI with override lock_wait_timeout with a LONG_TIMEOUT only after succesfully entering TOI * only ignore lock_wait_timeout on TOI * fix galera_split_brain test as TOI operation now returns ER_LOCK_WAIT_TIMEOUT after lock_wait_timeout * explicitly test for TOI Reviewed-by: Jan Lindström --- .../suite/galera/r/galera_split_brain.result | 3 +- .../suite/galera/t/galera_split_brain.test | 3 +- sql/handler.cc | 4 +- sql/sql_insert.cc | 4 +- sql/sql_parse.cc | 4 +- sql/wsrep_client_service.cc | 6 +- sql/wsrep_high_priority_service.cc | 4 +- sql/wsrep_mysqld.cc | 66 ++++++++++++------- sql/wsrep_mysqld.h | 4 +- sql/wsrep_thd.h | 4 +- sql/wsrep_var.cc | 4 +- 11 files changed, 63 insertions(+), 43 deletions(-) diff --git a/mysql-test/suite/galera/r/galera_split_brain.result b/mysql-test/suite/galera/r/galera_split_brain.result index bd8c3a5bc44..08f9060d2a9 100644 --- a/mysql-test/suite/galera/r/galera_split_brain.result +++ b/mysql-test/suite/galera/r/galera_split_brain.result @@ -7,8 +7,9 @@ connection node_1; connection node_2; Killing server ... connection node_1; +SET SESSION lock_wait_timeout= 3; CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB; -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +ERROR HY000: Lock wait timeout exceeded; try restarting transaction connection node_2; connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2; disconnect node_2; diff --git a/mysql-test/suite/galera/t/galera_split_brain.test b/mysql-test/suite/galera/t/galera_split_brain.test index 6a822b8f127..ccdfd3fd506 100644 --- a/mysql-test/suite/galera/t/galera_split_brain.test +++ b/mysql-test/suite/galera/t/galera_split_brain.test @@ -22,7 +22,8 @@ call mtr.add_suppression("WSREP: TO isolation failed for: "); --source include/kill_galera.inc --connection node_1 ---error ER_LOCK_DEADLOCK +SET SESSION lock_wait_timeout= 3; +--error ER_LOCK_WAIT_TIMEOUT CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB; # Reset the master and restart the slave so that post-test checks can run diff --git a/sql/handler.cc b/sql/handler.cc index 6b8c39cb0c4..757fa95a9a3 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2016, Oracle and/or its affiliates. - Copyright (c) 2009, 2020, MariaDB Corporation. + Copyright (c) 2009, 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 @@ -1946,7 +1946,7 @@ int ha_rollback_trans(THD *thd, bool all) if (thd->is_error()) { WSREP_DEBUG("ha_rollback_trans(%lld, %s) rolled back: %s: %s; is_real %d", - thd->thread_id, all?"TRUE":"FALSE", WSREP_QUERY(thd), + thd->thread_id, all?"TRUE":"FALSE", wsrep_thd_query(thd), thd->get_stmt_da()->message(), is_real_trans); } (void) wsrep_after_rollback(thd, all); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index e04062d66d5..cbd815eb8fd 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1,6 +1,6 @@ /* Copyright (c) 2000, 2016, Oracle and/or its affiliates. - Copyright (c) 2010, 2019, MariaDB Corporation + Copyright (c) 2010, 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 @@ -4782,7 +4782,7 @@ bool select_create::send_eof() { WSREP_DEBUG("select_create commit failed, thd: %llu err: %s %s", thd->thread_id, - wsrep_thd_transaction_state_str(thd), WSREP_QUERY(thd)); + wsrep_thd_transaction_state_str(thd), wsrep_thd_query(thd)); mysql_mutex_unlock(&thd->LOCK_thd_data); abort_result_set(); DBUG_RETURN(true); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 9e78a1a95e2..8999397fee7 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -7845,7 +7845,7 @@ static bool wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, DBUG_ASSERT(!debug_sync_set_action(thd, STRING_WITH_LEN(act))); }); WSREP_DEBUG("wsrep retrying AC query: %lu %s", - thd->wsrep_retry_counter, WSREP_QUERY(thd)); + thd->wsrep_retry_counter, wsrep_thd_query(thd)); wsrep_prepare_for_autocommit_retry(thd, rawbuf, length, parser_state); if (thd->lex->explain) delete_explain_query(thd->lex); @@ -7859,7 +7859,7 @@ static bool wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, is_autocommit, thd->wsrep_retry_counter, thd->variables.wsrep_retry_autocommit, - WSREP_QUERY(thd)); + wsrep_thd_query(thd)); my_error(ER_LOCK_DEADLOCK, MYF(0)); thd->reset_kill_query(); thd->wsrep_retry_counter= 0; // reset diff --git a/sql/wsrep_client_service.cc b/sql/wsrep_client_service.cc index 89621619a23..f045e5d271a 100644 --- a/sql/wsrep_client_service.cc +++ b/sql/wsrep_client_service.cc @@ -1,4 +1,4 @@ -/* Copyright 2018 Codership Oy +/* Copyright 2018-2021 Codership Oy 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 @@ -109,14 +109,14 @@ int Wsrep_client_service::prepare_data_for_replication() "affected rows: %llu, " "changed tables: %d, " "sql_log_bin: %d", - WSREP_QUERY(m_thd), + wsrep_thd_query(m_thd), m_thd->get_stmt_da()->affected_rows(), stmt_has_updated_trans_table(m_thd), m_thd->variables.sql_log_bin); } else { - WSREP_DEBUG("empty rbr buffer, query: %s", WSREP_QUERY(m_thd)); + WSREP_DEBUG("empty rbr buffer, query: %s", wsrep_thd_query(m_thd)); } } DBUG_RETURN(0); diff --git a/sql/wsrep_high_priority_service.cc b/sql/wsrep_high_priority_service.cc index 0da71c3eda5..d62dda2dfbc 100644 --- a/sql/wsrep_high_priority_service.cc +++ b/sql/wsrep_high_priority_service.cc @@ -1,4 +1,4 @@ -/* Copyright 2018 Codership Oy +/* Copyright 2018-2021 Codership Oy 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 @@ -658,7 +658,7 @@ Wsrep_replayer_service::~Wsrep_replayer_service() DBUG_ASSERT(0); WSREP_ERROR("trx_replay failed for: %d, schema: %s, query: %s", m_replay_status, - orig_thd->db.str, WSREP_QUERY(orig_thd)); + orig_thd->db.str, wsrep_thd_query(orig_thd)); unireg_abort(1); } } diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index cabf066abf4..bf1e4e32b49 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -1,4 +1,4 @@ -/* Copyright 2008-2015 Codership Oy +/* Copyright 2008-2021 Codership Oy 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 @@ -2008,10 +2008,10 @@ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table, { DBUG_ASSERT(thd->variables.wsrep_OSU_method == WSREP_OSU_TOI); - WSREP_DEBUG("TOI Begin for %s", WSREP_QUERY(thd)); + WSREP_DEBUG("TOI Begin for %s", wsrep_thd_query(thd)); if (wsrep_can_run_in_toi(thd, db, table, table_list) == false) { - WSREP_DEBUG("No TOI for %s", WSREP_QUERY(thd)); + WSREP_DEBUG("No TOI for %s", wsrep_thd_query(thd)); return 1; } @@ -2039,7 +2039,7 @@ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table, /* non replicated DDL, affecting temporary tables only */ WSREP_DEBUG("TO isolation skipped, sql: %s." "Only temporary tables affected.", - WSREP_QUERY(thd)); + wsrep_thd_query(thd)); if (buf) my_free(buf); return -1; } @@ -2054,7 +2054,7 @@ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table, { DBUG_ASSERT(cs.current_error()); WSREP_DEBUG("to_execute_start() failed for %llu: %s, seqno: %lld", - thd->thread_id, WSREP_QUERY(thd), + thd->thread_id, wsrep_thd_query(thd), (long long)wsrep_thd_trx_seqno(thd)); /* jump to error handler in mysql_execute_command() */ @@ -2065,15 +2065,32 @@ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table, "Maximum size exceeded.", ret, (thd->db.str ? thd->db.str : "(null)"), - WSREP_QUERY(thd)); + wsrep_thd_query(thd)); my_error(ER_ERROR_DURING_COMMIT, MYF(0), WSREP_SIZE_EXCEEDED); break; + case wsrep::e_deadlock_error: + WSREP_WARN("TO isolation failed for: %d, schema: %s, sql: %s. " + "Deadlock error.", + ret, + (thd->db.str ? thd->db.str : "(null)"), + wsrep_thd_query(thd)); + my_error(ER_LOCK_DEADLOCK, MYF(0)); + break; + case wsrep::e_timeout_error: + WSREP_WARN("TO isolation failed for: %d, schema: %s, sql: %s. " + "Operation timed out.", + ret, + (thd->db.str ? thd->db.str : "(null)"), + wsrep_thd_query(thd)); + my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0)); + break; default: WSREP_WARN("TO isolation failed for: %d, schema: %s, sql: %s. " - "Check wsrep connection state and retry the query.", + "Check your wsrep connection state and retry the query.", ret, (thd->db.str ? thd->db.str : "(null)"), - WSREP_QUERY(thd)); + wsrep_thd_query(thd)); + if (!thd->is_error()) { my_error(ER_LOCK_DEADLOCK, MYF(0), "WSREP replication failed. Check " @@ -2106,19 +2123,19 @@ static void wsrep_TOI_end(THD *thd) { if (!ret) { WSREP_DEBUG("TO END: %lld: %s", - client_state.toi_meta().seqno().get(), WSREP_QUERY(thd)); + client_state.toi_meta().seqno().get(), wsrep_thd_query(thd)); } else { WSREP_WARN("TO isolation end failed for: %d, sql: %s", - ret, WSREP_QUERY(thd)); + ret, wsrep_thd_query(thd)); } } static int wsrep_RSU_begin(THD *thd, const char *db_, const char *table_) { WSREP_DEBUG("RSU BEGIN: %lld, : %s", wsrep_thd_trx_seqno(thd), - WSREP_QUERY(thd)); + wsrep_thd_query(thd)); if (thd->wsrep_cs().begin_rsu(5000)) { WSREP_WARN("RSU begin failed"); @@ -2133,7 +2150,7 @@ static int wsrep_RSU_begin(THD *thd, const char *db_, const char *table_) static void wsrep_RSU_end(THD *thd) { WSREP_DEBUG("RSU END: %lld : %s", wsrep_thd_trx_seqno(thd), - WSREP_QUERY(thd)); + wsrep_thd_query(thd)); if (thd->wsrep_cs().end_rsu()) { WSREP_WARN("Failed to end RSU, server may need to be restarted"); @@ -2175,7 +2192,7 @@ int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_, if (wsrep_debug && thd->mdl_context.has_locks()) { WSREP_DEBUG("thread holds MDL locks at TI begin: %s %llu", - WSREP_QUERY(thd), thd->thread_id); + wsrep_thd_query(thd), thd->thread_id); } /* @@ -2191,13 +2208,6 @@ int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_, thd->variables.auto_increment_increment= 1; } - /* - TOI operations will ignore provided lock_wait_timeout and restore it - after operation is done. - */ - thd->variables.saved_lock_wait_timeout= thd->variables.lock_wait_timeout; - thd->variables.lock_wait_timeout= LONG_TIMEOUT; - if (thd->variables.wsrep_on && wsrep_thd_is_local(thd)) { switch (thd->variables.wsrep_OSU_method) { @@ -2213,8 +2223,19 @@ int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_, ret= -1; break; } + switch (ret) { - case 0: /* wsrep_TOI_begin sould set toi mode */ break; + case 0: /* wsrep_TOI_begin sould set toi mode */ + if (thd->variables.wsrep_OSU_method == WSREP_OSU_TOI) + { + /* + TOI operations ignore the provided lock_wait_timeout once replicated, + and restore it after operation is done. + */ + thd->variables.saved_lock_wait_timeout= thd->variables.lock_wait_timeout; + thd->variables.lock_wait_timeout= LONG_TIMEOUT; + } + break; case 1: /* TOI replication skipped, treat as success */ ret= 0; @@ -2233,10 +2254,9 @@ void wsrep_to_isolation_end(THD *thd) DBUG_ASSERT(wsrep_thd_is_local_toi(thd) || wsrep_thd_is_in_rsu(thd)); - thd->variables.lock_wait_timeout= thd->variables.saved_lock_wait_timeout; - if (wsrep_thd_is_local_toi(thd)) { + thd->variables.lock_wait_timeout= thd->variables.saved_lock_wait_timeout; DBUG_ASSERT(thd->variables.wsrep_OSU_method == WSREP_OSU_TOI); wsrep_TOI_end(thd); } diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h index 748d93c72aa..db3e9b09b51 100644 --- a/sql/wsrep_mysqld.h +++ b/sql/wsrep_mysqld.h @@ -1,4 +1,4 @@ -/* Copyright 2008-2017 Codership Oy +/* Copyright 2008-2021 Codership Oy 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 @@ -289,8 +289,6 @@ static inline bool wsrep_cluster_address_exists() return wsrep_cluster_address && wsrep_cluster_address[0]; } -#define WSREP_QUERY(thd) (thd->query()) - extern my_bool wsrep_ready_get(); extern void wsrep_ready_wait(); diff --git a/sql/wsrep_thd.h b/sql/wsrep_thd.h index d24d8e6358f..3829176d72f 100644 --- a/sql/wsrep_thd.h +++ b/sql/wsrep_thd.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2013 Codership Oy +/* Copyright (C) 2013-2021 Codership Oy 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 @@ -292,7 +292,7 @@ static inline void wsrep_log_thd(const THD *thd, (thd->get_stmt_da()->is_error() ? thd->get_stmt_da()->message() : "") #ifdef WSREP_THD_LOG_QUERIES , thd->lex->sql_command, - WSREP_QUERY(thd) + wsrep_thd_query(thd) #endif /* WSREP_OBSERVER_LOG_QUERIES */ ); } diff --git a/sql/wsrep_var.cc b/sql/wsrep_var.cc index e4cfd0d89c9..cd7b9bdf870 100644 --- a/sql/wsrep_var.cc +++ b/sql/wsrep_var.cc @@ -1,4 +1,4 @@ -/* Copyright 2008-2015 Codership Oy +/* Copyright 2008-2021 Codership Oy 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 @@ -794,7 +794,7 @@ bool wsrep_desync_check (sys_var *self, THD* thd, set_var* var) ret= Wsrep_server_state::instance().provider().desync(); if (ret) { WSREP_WARN ("SET desync failed %d for schema: %s, query: %s", ret, - thd->db.str, WSREP_QUERY(thd)); + thd->db.str, wsrep_thd_query(thd)); my_error (ER_CANNOT_USER, MYF(0), "'desync'", thd->query()); return true; } 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 30/41] 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 da171182b7d79d21177d113d2bbaecbca21d8bbc Mon Sep 17 00:00:00 2001 From: mkaruza Date: Wed, 18 Aug 2021 10:13:02 +0200 Subject: [PATCH 31/41] MDEV-26223 Galera cluster node consider old server_id value even after modification of server_id [wsrep_gtid_mode=ON] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If cluster is bootstrapped in existing database, we should use provided configuration variables for wsrep_gtid_domain_id and server_id instead of recovered ones. If 'new' combination of wsrep_gtid_domain_id & server_id already existed somewere before in binlog we should continue from last seqno, if combination is new we start from seqno 0. Reviewed-by: Jan Lindström --- sql/wsrep_mysqld.cc | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index 972876c24c2..524410152ca 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -804,17 +804,20 @@ void wsrep_init_globals() { wsrep_gtid_server.domain_id= wsrep_gtid_domain_id; wsrep_init_sidno(Wsrep_server_state::instance().connected_gtid().id()); - wsrep_init_gtid(); /* Recover last written wsrep gtid */ + wsrep_init_gtid(); if (wsrep_new_cluster) { - wsrep_server_gtid_t gtid= {wsrep_gtid_server.domain_id, - wsrep_gtid_server.server_id, 0}; - wsrep_get_binlog_gtid_seqno(gtid); - wsrep_gtid_server.seqno(gtid.seqno); + /* Start with provided domain_id & server_id found in configuration */ + wsrep_server_gtid_t new_gtid; + new_gtid.domain_id= wsrep_gtid_domain_id; + new_gtid.server_id= global_system_variables.server_id; + new_gtid.seqno= 0; + /* Try to search for domain_id and server_id combination in binlog if found continue from last seqno */ + wsrep_get_binlog_gtid_seqno(new_gtid); + wsrep_gtid_server.gtid(new_gtid); } wsrep_init_schema(); - if (WSREP_ON) { Wsrep_server_state::instance().initialized(); From ac2857a5fbf851d90171ac55f23385869ee6ba83 Mon Sep 17 00:00:00 2001 From: Daniele Sciascia Date: Thu, 20 May 2021 14:12:22 +0200 Subject: [PATCH 32/41] MDEV-25717 Assertion `owning_thread_id_ == wsrep::this_thread::get_id()' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A test case to reproduce the issue. The actual fix is in galera library. Reviewed-by: Jan Lindström --- .../suite/galera_sr/r/MDEV-25717.result | 47 ++++++++ mysql-test/suite/galera_sr/t/MDEV-25717.test | 113 ++++++++++++++++++ sql/wsrep_high_priority_service.cc | 19 +++ sql/wsrep_thd.cc | 15 ++- sql/wsrep_thd.h | 2 +- 5 files changed, 193 insertions(+), 3 deletions(-) create mode 100644 mysql-test/suite/galera_sr/r/MDEV-25717.result create mode 100644 mysql-test/suite/galera_sr/t/MDEV-25717.test diff --git a/mysql-test/suite/galera_sr/r/MDEV-25717.result b/mysql-test/suite/galera_sr/r/MDEV-25717.result new file mode 100644 index 00000000000..22f8d5eb5db --- /dev/null +++ b/mysql-test/suite/galera_sr/r/MDEV-25717.result @@ -0,0 +1,47 @@ +connection node_2; +connection node_1; +connection node_1; +CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) Engine=InnoDB; +INSERT INTO t1 VALUES (1), (2), (3); +connection node_2; +SET SESSION wsrep_trx_fragment_size = 1; +START TRANSACTION; +INSERT INTO t1 VALUES (4); +connection node_1; +SELECT COUNT(*) FROM t1; +COUNT(*) +3 +connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2; +connection node_2a; +SET GLOBAL DEBUG_DBUG = "d,sync.wsrep_apply_toi"; +connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1; +connection node_1a; +SET GLOBAL DEBUG_DBUG = "d,sync.wsrep_bf_abort"; +connection node_1; +TRUNCATE TABLE t1; +connection node_1a; +SET DEBUG_SYNC = "now WAIT_FOR sync.wsrep_bf_abort_reached"; +connection node_2a; +SET DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_toi_reached"; +connection node_2; +INSERT INTO t1 VALUES (5); +connection node_2a; +SET SESSION wsrep_sync_wait = 0; +SET SESSION wsrep_sync_wait = DEFAULT; +SET GLOBAL DEBUG_DBUG = ""; +SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_toi"; +connection node_2; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +connection node_1a; +SET SESSION wsrep_sync_wait=0; +SET GLOBAL DEBUG_DBUG = "+d,sync.wsrep_log_dummy_write_set"; +SET DEBUG_SYNC = "now SIGNAL signal.wsrep_bf_abort"; +SET DEBUG_SYNC = "now WAIT_FOR sync.wsrep_log_dummy_write_set_reached"; +connection node_1; +connection node_2; +SET GLOBAL DEBUG_DBUG = ""; +SET DEBUG_SYNC = "RESET"; +connection node_1; +SET GLOBAL DEBUG_DBUG = ""; +SET DEBUG_SYNC = "RESET"; +DROP TABLE t1; diff --git a/mysql-test/suite/galera_sr/t/MDEV-25717.test b/mysql-test/suite/galera_sr/t/MDEV-25717.test new file mode 100644 index 00000000000..7188f8bb172 --- /dev/null +++ b/mysql-test/suite/galera_sr/t/MDEV-25717.test @@ -0,0 +1,113 @@ +# +# MDEV-25717 Assertion `owning_thread_id_ == wsrep::this_thread::get_id()' +# +# This test exposes a race condition between rollbacker thread and rollback +# fragment processing. +# + +--source include/galera_cluster.inc +--source include/have_debug_sync.inc + +--connection node_1 +CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) Engine=InnoDB; +INSERT INTO t1 VALUES (1), (2), (3); + +# +# On node_2 we start a SR transaction, it going to +# be BF aborted later on +# +--connection node_2 +SET SESSION wsrep_trx_fragment_size = 1; +START TRANSACTION; +INSERT INTO t1 VALUES (4); + +--connection node_1 +SELECT COUNT(*) FROM t1; # Sync wait + +# +# Issue a conflicting TRUNCATE statement on node_1: +# - on node_2, block it before it is going to apply +# - on node_1, block before the before it BF aborts the INSERT +# +--connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2 +--connection node_2a +SET GLOBAL DEBUG_DBUG = "d,sync.wsrep_apply_toi"; + +--connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1 +--connection node_1a +SET GLOBAL DEBUG_DBUG = "d,sync.wsrep_bf_abort"; + +--connection node_1 +--send TRUNCATE TABLE t1 + +--connection node_1a +SET DEBUG_SYNC = "now WAIT_FOR sync.wsrep_bf_abort_reached"; + +--connection node_2a +SET DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_toi_reached"; + +# +# Generate one more fragment on the SR transaction. +# This is going to fail certification and results +# in a rollback fragment. +# +--connection node_2 +--let $expected_cert_failures = `SELECT VARIABLE_VALUE + 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_cert_failures'` + +--send INSERT INTO t1 VALUES (5) + +# +# Wait until after certify and observe the certification +# failure. Let both continue and we are done on node_2. +# +--connection node_2a +SET SESSION wsrep_sync_wait = 0; +--let $wait_condition = SELECT VARIABLE_VALUE = $expected_cert_failures FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_cert_failures' +--source include/wait_condition.inc +SET SESSION wsrep_sync_wait = DEFAULT; + +SET GLOBAL DEBUG_DBUG = ""; +SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_toi"; + +--connection node_2 +--error ER_LOCK_DEADLOCK +--reap + +# +# On node_1 we expect the following things: +# - the TRUNCATE should successfully bf abort the transaction +# - A rollback fragment should be delivered as a result of +# certification failure. We expect the rollback fragment to +# be delivered after TRUNCATE has bf aborted, therefore rollback +# fragment logs a dummy writeset. +# +--connection node_1a +SET SESSION wsrep_sync_wait=0; +SET GLOBAL DEBUG_DBUG = "+d,sync.wsrep_log_dummy_write_set"; + +# Signal the TRUNCATE to continue and observe the BF abort +--let $expected_bf_aborts = `SELECT VARIABLE_VALUE + 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_bf_aborts'` +SET DEBUG_SYNC = "now SIGNAL signal.wsrep_bf_abort"; + +# Expect a timeout if bug is present +--let $wait_condition = SELECT VARIABLE_VALUE = $expected_bf_aborts FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_bf_aborts' +--source include/wait_condition.inc + +# Observe logging of dummy writeset +SET DEBUG_SYNC = "now WAIT_FOR sync.wsrep_log_dummy_write_set_reached"; + +# TRUNCATE succeeds +--connection node_1 +--reap + +# +# Cleanup +# +--connection node_2 +SET GLOBAL DEBUG_DBUG = ""; +SET DEBUG_SYNC = "RESET"; + +--connection node_1 +SET GLOBAL DEBUG_DBUG = ""; +SET DEBUG_SYNC = "RESET"; +DROP TABLE t1; diff --git a/sql/wsrep_high_priority_service.cc b/sql/wsrep_high_priority_service.cc index d62dda2dfbc..452242dfd23 100644 --- a/sql/wsrep_high_priority_service.cc +++ b/sql/wsrep_high_priority_service.cc @@ -379,6 +379,16 @@ int Wsrep_high_priority_service::apply_toi(const wsrep::ws_meta& ws_meta, WSREP_DEBUG("Wsrep_high_priority_service::apply_toi: %lld", client_state.toi_meta().seqno().get()); + DBUG_EXECUTE_IF("sync.wsrep_apply_toi", + { + const char act[]= + "now " + "SIGNAL sync.wsrep_apply_toi_reached " + "WAIT_FOR signal.wsrep_apply_toi"; + DBUG_ASSERT(!debug_sync_set_action(thd, + STRING_WITH_LEN(act))); + };); + int ret= wsrep_apply_events(thd, m_rli, data.data(), data.size()); if (ret != 0 || thd->wsrep_has_ignored_error) { @@ -427,6 +437,15 @@ int Wsrep_high_priority_service::log_dummy_write_set(const wsrep::ws_handle& ws_ DBUG_PRINT("info", ("Wsrep_high_priority_service::log_dummy_write_set: seqno=%lld", ws_meta.seqno().get())); + DBUG_EXECUTE_IF("sync.wsrep_log_dummy_write_set", + { + const char act[]= + "now " + "SIGNAL sync.wsrep_log_dummy_write_set_reached "; + DBUG_ASSERT(!debug_sync_set_action(m_thd, + STRING_WITH_LEN(act))); + };); + if (ws_meta.ordered()) { wsrep::client_state& cs(m_thd->wsrep_cs()); diff --git a/sql/wsrep_thd.cc b/sql/wsrep_thd.cc index d8f3d8959e0..2e02110d697 100644 --- a/sql/wsrep_thd.cc +++ b/sql/wsrep_thd.cc @@ -340,11 +340,20 @@ int wsrep_abort_thd(THD *bf_thd_ptr, THD *victim_thd_ptr, my_bool signal) DBUG_RETURN(1); } -bool wsrep_bf_abort(const THD* bf_thd, THD* victim_thd) +bool wsrep_bf_abort(THD* bf_thd, THD* victim_thd) { WSREP_LOG_THD(bf_thd, "BF aborter before"); WSREP_LOG_THD(victim_thd, "victim before"); - wsrep::seqno bf_seqno(bf_thd->wsrep_trx().ws_meta().seqno()); + + DBUG_EXECUTE_IF("sync.wsrep_bf_abort", + { + const char act[]= + "now " + "SIGNAL sync.wsrep_bf_abort_reached " + "WAIT_FOR signal.wsrep_bf_abort"; + DBUG_ASSERT(!debug_sync_set_action(bf_thd, + STRING_WITH_LEN(act))); + };); if (WSREP(victim_thd) && !victim_thd->wsrep_trx().active()) { @@ -362,6 +371,8 @@ bool wsrep_bf_abort(const THD* bf_thd, THD* victim_thd) } bool ret; + wsrep::seqno bf_seqno(bf_thd->wsrep_trx().ws_meta().seqno()); + if (wsrep_thd_is_toi(bf_thd)) { ret= victim_thd->wsrep_cs().total_order_bf_abort(bf_seqno); diff --git a/sql/wsrep_thd.h b/sql/wsrep_thd.h index 3829176d72f..560dbbdab44 100644 --- a/sql/wsrep_thd.h +++ b/sql/wsrep_thd.h @@ -87,7 +87,7 @@ int wsrep_show_bf_aborts (THD *thd, SHOW_VAR *var, char *buff, bool wsrep_create_appliers(long threads, bool mutex_protected=false); void wsrep_create_rollbacker(); -bool wsrep_bf_abort(const THD*, THD*); +bool wsrep_bf_abort(THD* bf_thd, THD* victim_thd); int wsrep_abort_thd(THD *bf_thd_ptr, THD *victim_thd_ptr, my_bool signal); extern void wsrep_thd_set_PA_safe(void *thd_ptr, my_bool safe); From 0dec71ca53729bd1a565bdc800e64008b44ffa48 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Wed, 18 Aug 2021 16:07:15 +1000 Subject: [PATCH 33/41] 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 34/41] 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 35/41] 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 36/41] 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 37/41] 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" From 89723ce17959d61a993136e51104fc9b830ac67c Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Thu, 19 Aug 2021 12:34:31 +0530 Subject: [PATCH 38/41] After-merge fixup f84e28c119b495da77e197f7cd18af4048fc3126 --- .../suite/encryption/t/innodb_import.test | 3 ++- .../suite/innodb/t/full_crc32_import.test | 1 + storage/innobase/row/row0import.cc | 26 ++++++++++++++----- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/mysql-test/suite/encryption/t/innodb_import.test b/mysql-test/suite/encryption/t/innodb_import.test index 791a1757878..2e5470c5568 100644 --- a/mysql-test/suite/encryption/t/innodb_import.test +++ b/mysql-test/suite/encryption/t/innodb_import.test @@ -1,5 +1,6 @@ --source include/have_innodb.inc --- source include/have_example_key_management_plugin.inc +--source include/have_example_key_management_plugin.inc +--source include/innodb_checksum_algorithm.inc --echo # --echo # MDEV-26131 SEGV in ha_innobase::discard_or_import_tablespace --echo # diff --git a/mysql-test/suite/innodb/t/full_crc32_import.test b/mysql-test/suite/innodb/t/full_crc32_import.test index 02ce565c7f8..c9195111c05 100644 --- a/mysql-test/suite/innodb/t/full_crc32_import.test +++ b/mysql-test/suite/innodb/t/full_crc32_import.test @@ -134,6 +134,7 @@ EOF ALTER TABLE t1 IMPORT TABLESPACE; --enable_warnings ALTER TABLE t1 DROP INDEX idx1; +--replace_regex /opening '.*\/test\//opening '.\/test\// ALTER TABLE t1 IMPORT TABLESPACE; --disable_warnings SHOW CREATE TABLE t1; diff --git a/storage/innobase/row/row0import.cc b/storage/innobase/row/row0import.cc index 0585ac08542..a774143200a 100644 --- a/storage/innobase/row/row0import.cc +++ b/storage/innobase/row/row0import.cc @@ -665,7 +665,7 @@ struct FetchIndexRootPages : public AbstractCallback { @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; + buf_block_t* block) UNIV_NOTHROW override; /** Called for each block as it is read from the file. @param block block to convert, it is not from the buffer pool. @@ -839,7 +839,8 @@ public: } } - dberr_t run(const fil_iterator_t& iter, buf_block_t* block) UNIV_NOTHROW + dberr_t run(const fil_iterator_t& iter, + buf_block_t* block) UNIV_NOTHROW override { return fil_iterate(iter, block, *this); } @@ -3448,6 +3449,8 @@ dberr_t FetchIndexRootPages::run(const fil_iterator_t& iter, #endif srv_page_size; byte* page_compress_buf = static_cast(malloc(buf_size)); + const bool full_crc32 = fil_space_t::full_crc32(m_space_flags); + bool skip_checksum_check = false; ut_ad(!srv_read_only_mode); if (!page_compress_buf) @@ -3486,15 +3489,18 @@ page_corrupted: goto func_exit; } - page_compressed= fil_page_is_compressed_encrypted(readptr) || - fil_page_is_compressed(readptr); + page_compressed= + (full_crc32 && fil_space_t::is_compressed(m_space_flags) && + buf_page_is_compressed(readptr, m_space_flags)) || + (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, zip_size)) + if (!buf_page_verify_crypt_checksum(readptr, m_space_flags)) goto page_corrupted; if (!fil_space_decrypt(get_space_id(), iter.crypt_data, readptr, @@ -3503,15 +3509,21 @@ page_corrupted: goto func_exit; } + /* For full_crc32 format, skip checksum check + after decryption. */ + skip_checksum_check= full_crc32 && encrypted; + if (page_compressed) { - ulint compress_length= fil_page_decompress(page_compress_buf, readptr, + ulint compress_length= fil_page_decompress(page_compress_buf, + readptr, m_space_flags); ut_ad(compress_length != srv_page_size); if (compress_length == 0) goto page_corrupted; } - else if (buf_page_is_corrupted(false, readptr, m_space_flags)) + else if (!skip_checksum_check + && buf_page_is_corrupted(false, readptr, m_space_flags)) goto page_corrupted; err= this->operator()(block); From 7d2d9f04b9d308745d1a99a47a67f17ced128562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 19 Aug 2021 11:29:32 +0300 Subject: [PATCH 39/41] MDEV-20931 fixup The merge commit 4a2595727465648f2d4e794d1b2f182345f0bee8 caused a test failure on Windows. The suppression regexp needs to accept the backslash. fil_invalid_page_access_msg(): Simplify the implementation and invoke sql_print_error() directly. fil_space_t::io(): Invoke fil_invalid_page_access_msg() only from one location. --- .../suite/innodb/r/import_corrupted.result | 2 +- .../suite/innodb/t/import_corrupted.test | 2 +- storage/innobase/fil/fil0fil.cc | 45 ++++++++++--------- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/mysql-test/suite/innodb/r/import_corrupted.result b/mysql-test/suite/innodb/r/import_corrupted.result index fe431e62eef..149a48dccfe 100644 --- a/mysql-test/suite/innodb/r/import_corrupted.result +++ b/mysql-test/suite/innodb/r/import_corrupted.result @@ -1,6 +1,6 @@ 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 .* bytes at .* outside the bounds of the file: ./test/t2.ibd"); +call mtr.add_suppression("Trying to read .* bytes at .* outside the bounds of the file: \\..test.t2\\.ibd"); CREATE TABLE t1 ( id INT AUTO_INCREMENT PRIMARY KEY, not_id INT, diff --git a/mysql-test/suite/innodb/t/import_corrupted.test b/mysql-test/suite/innodb/t/import_corrupted.test index fcdb03b4601..976cbe03dbb 100644 --- a/mysql-test/suite/innodb/t/import_corrupted.test +++ b/mysql-test/suite/innodb/t/import_corrupted.test @@ -2,7 +2,7 @@ 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 .* bytes at .* outside the bounds of the file: ./test/t2.ibd"); +call mtr.add_suppression("Trying to read .* bytes at .* outside the bounds of the file: \\..test.t2\\.ibd"); let MYSQLD_DATADIR = `SELECT @@datadir`; diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc index 1f3f73f3f8e..6af8b729d78 100644 --- a/storage/innobase/fil/fil0fil.cc +++ b/storage/innobase/fil/fil0fil.cc @@ -49,6 +49,7 @@ Created 10/25/1995 Heikki Tuuri #include "os0event.h" #include "sync0sync.h" #include "buf0flu.h" +#include "log.h" #ifdef UNIV_LINUX # include # include @@ -3217,14 +3218,17 @@ func_exit: /** Report information about an invalid page access. */ ATTRIBUTE_COLD -static std::string fil_invalid_page_access_msg(const char *name, - os_offset_t offset, ulint len, - bool is_read) +static void fil_invalid_page_access_msg(bool fatal, const char *name, + os_offset_t offset, ulint len, + bool is_read) { - std::stringstream ss; - ss << "Trying to " << (is_read ? "read " : "write ") << len << " bytes at " - << offset << " outside the bounds of the file: " << name; - return ss.str(); + sql_print_error("%s%s %zu bytes at " UINT64PF + " outside the bounds of the file: %s", + fatal ? "[FATAL] InnoDB: " : "InnoDB: ", + is_read ? "Trying to read" : "Trying to write", + len, offset, name); + if (fatal) + abort(); } /** Update the data structures on write completion */ @@ -3280,6 +3284,7 @@ fil_io_t fil_space_t::io(const IORequest &type, os_offset_t offset, size_t len, } ulint p = static_cast(offset >> srv_page_size_shift); + bool fatal; if (UNIV_LIKELY_NULL(UT_LIST_GET_NEXT(chain, node))) { ut_ad(this == fil_system.sys_space @@ -3294,9 +3299,13 @@ fil_io_t fil_space_t::io(const IORequest &type, os_offset_t offset, size_t len, release(); return {DB_ERROR, nullptr}; } - ib::fatal() - << fil_invalid_page_access_msg(name, - offset, len, type.is_read()); + + fatal = true; +fail: + fil_invalid_page_access_msg(fatal, node->name, + offset, len, + type.is_read()); + return {DB_IO_ERROR, nullptr}; } } @@ -3304,25 +3313,17 @@ fil_io_t fil_space_t::io(const IORequest &type, os_offset_t offset, size_t len, } if (UNIV_UNLIKELY(node->size <= p)) { + release(); + if (type.type == IORequest::READ_ASYNC) { - release(); /* If we can tolerate the non-existent pages, we should return with DB_ERROR and let caller decide what to do. */ return {DB_ERROR, nullptr}; } - if (node->space->purpose == FIL_TYPE_IMPORT) { - release(); - ib::error() << fil_invalid_page_access_msg( - node->name, offset, len, type.is_read()); - - - return {DB_IO_ERROR, nullptr}; - } - - ib::fatal() << fil_invalid_page_access_msg( - node->name, offset, len, type.is_read()); + fatal = node->space->purpose != FIL_TYPE_IMPORT; + goto fail; } dberr_t err; From a2a0ac7c4ca6b7ab994b30325045d2406db8ee16 Mon Sep 17 00:00:00 2001 From: Vlad Lesin Date: Wed, 18 Aug 2021 19:26:01 +0300 Subject: [PATCH 40/41] MDEV-26206 gap lock is not set if implicit lock exists Post-push fix for 10.5+. The fix influence MDEV-14479. Before the fix lock_rec_convert_impl_to_expl() did not create explicit lock if caller's transaction owns found implicit lock(see MDEV-14479 for details). After the fix lock_rec_convert_impl_to_expl() can create explicit lock under the above conditions if the requested lock mode is not LOCK_REC_NOT_GAP. And that is why we need to check if the table is X-locked before lock_rec_convert_impl_to_expl() call. --- storage/innobase/lock/lock0lock.cc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc index e46a0a91ac5..d7ec5736826 100644 --- a/storage/innobase/lock/lock0lock.cc +++ b/storage/innobase/lock/lock0lock.cc @@ -5640,7 +5640,9 @@ lock_sec_rec_read_check_and_lock( if the max trx id for the page >= min trx id for the trx list or a database recovery is running. */ - if (!page_rec_is_supremum(rec) + trx_t *trx = thr_get_trx(thr); + if (!lock_table_has(trx, index->table, LOCK_X) + && !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) @@ -5650,7 +5652,6 @@ lock_sec_rec_read_check_and_lock( } #ifdef WITH_WSREP - trx_t *trx= thr_get_trx(thr); /* If transaction scanning an unique secondary key is wsrep high priority thread (brute force) this scanning may involve GAP-locking in the index. As this locking happens also when @@ -5724,8 +5725,10 @@ lock_clust_rec_read_check_and_lock( heap_no = page_rec_get_heap_no(rec); - if (heap_no != PAGE_HEAP_NO_SUPREMUM - && lock_rec_convert_impl_to_expl(thr_get_trx(thr), block, rec, + trx_t *trx = thr_get_trx(thr); + if (!lock_table_has(trx, index->table, LOCK_X) + && heap_no != PAGE_HEAP_NO_SUPREMUM + && lock_rec_convert_impl_to_expl(trx, block, rec, index, offsets) && gap_mode == LOCK_REC_NOT_GAP) { /* We already hold an implicit exclusive lock. */ From 17980e35fafdf0b05c89c11ecafbea96e1cfc5e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 19 Aug 2021 12:05:02 +0300 Subject: [PATCH 41/41] MDEV-26439 Typo in Foreign Key error message foreign_push_index_error(): Fix a typo of 'match' that was introduced in commit 5130f5206c150ba1e8a723aae63884ff64408012 (MDEV-20480). Thanks to Oli Sennhauser for reporting this. --- mysql-test/suite/innodb/r/innodb-fk-warnings.result | 2 +- storage/innobase/handler/ha_innodb.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/suite/innodb/r/innodb-fk-warnings.result b/mysql-test/suite/innodb/r/innodb-fk-warnings.result index 99b16dc1e9c..c85dcf22c06 100644 --- a/mysql-test/suite/innodb/r/innodb-fk-warnings.result +++ b/mysql-test/suite/innodb/r/innodb-fk-warnings.result @@ -129,7 +129,7 @@ create table t2(a char(20), key(a), foreign key(a) references t1(f1)) engine=inn ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed") show warnings; Level Code Message -Warning 150 Create table `test`.`t2` with foreign key (a) constraint failed. Field type or character set for column 'a' does not mach referenced column 'f1'. +Warning 150 Create table `test`.`t2` with foreign key (a) constraint failed. Field type or character set for column 'a' does not match referenced column 'f1'. Error 1005 Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed") Warning 1215 Cannot add foreign key constraint for `t2` drop table t1; diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index d61624a5e37..a91f233e62f 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -11939,7 +11939,7 @@ foreign_push_index_error(trx_t* trx, const char* operation, trx, DB_CANNOT_ADD_CONSTRAINT, create_name, "%s table %s with foreign key %s constraint" " failed. Field type or character set for column '%s' " - "does not mach referenced column '%s'.", + "does not match referenced column '%s'.", operation, create_name, fk_text, columns[err_col], col_name); return;