From d2b4d3ada02253e39cbec7e6ecf656b54a8476d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Wed, 13 Oct 2021 17:57:31 +0300 Subject: [PATCH 1/7] MDEV-26707: SR transaction rolls back locally, but not in cluster Record correct result file. --- .../galera_3nodes_sr/r/MDEV-26707.result | 25 +++---------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/mysql-test/suite/galera_3nodes_sr/r/MDEV-26707.result b/mysql-test/suite/galera_3nodes_sr/r/MDEV-26707.result index a041274162f..8de724c1576 100644 --- a/mysql-test/suite/galera_3nodes_sr/r/MDEV-26707.result +++ b/mysql-test/suite/galera_3nodes_sr/r/MDEV-26707.result @@ -71,35 +71,16 @@ connection node_3a; SET SESSION wsrep_sync_wait = 0; SET SESSION wsrep_sync_wait = DEFAULT; connection node_1a; -Timeout in wait_condition.inc for SELECT COUNT(*) = 0 FROM mysql.wsrep_streaming_log -Id User Host db Command Time State Info Progress -1 system user NULL Sleep 66 wsrep aborter idle NULL 0.000 -2 system user NULL Sleep 66 closing tables NULL 0.000 -10 root localhost test Sleep 58 NULL 0.000 -11 root localhost:52722 test Sleep 56 NULL 0.000 -12 root localhost:52724 test Query 0 starting show full processlist 0.000 SELECT COUNT(*) AS EXPECT_0 FROM mysql.wsrep_streaming_log; EXPECT_0 -1 +0 connection node_2a; -Timeout in wait_condition.inc for SELECT COUNT(*) = 0 FROM mysql.wsrep_streaming_log -Id User Host db Command Time State Info Progress -1 system user NULL Sleep 96 wsrep aborter idle NULL 0.000 -2 system user NULL Sleep 87 closing tables NULL 0.000 -10 root localhost:37222 test Sleep 64 NULL 0.000 -11 root localhost:37228 test Query 0 starting show full processlist 0.000 SELECT COUNT(*) AS EXPECT_0 FROM mysql.wsrep_streaming_log; EXPECT_0 -1 +0 connection node_3a; -Timeout in wait_condition.inc for SELECT COUNT(*) = 0 FROM mysql.wsrep_streaming_log -Id User Host db Command Time State Info Progress -1 system user NULL Sleep 122 wsrep aborter idle NULL 0.000 -2 system user NULL Sleep 117 closing tables NULL 0.000 -10 root localhost:60992 test Sleep 117 NULL 0.000 -11 root localhost:60994 test Query 0 starting show full processlist 0.000 SELECT COUNT(*) AS EXPECT_0 FROM mysql.wsrep_streaming_log; EXPECT_0 -1 +0 connection node_1; DROP TABLE t1; From bd1573b0f332d4c7c08aab2974aadd544dbc24e3 Mon Sep 17 00:00:00 2001 From: Sergei Krivonos Date: Wed, 6 Oct 2021 11:31:08 +0300 Subject: [PATCH 2/7] Xcode compatibility update: pcre, mysql-test-run.pl --- cmake/pcre.cmake | 19 ++++++++++--------- mysql-test/mysql-test-run.pl | 1 + 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/cmake/pcre.cmake b/cmake/pcre.cmake index 3d4f163fab9..5ea81f53828 100644 --- a/cmake/pcre.cmake +++ b/cmake/pcre.cmake @@ -11,21 +11,22 @@ MACRO(BUNDLE_PCRE2) FOREACH(lib pcre2-posix pcre2-8) ADD_LIBRARY(${lib} STATIC IMPORTED GLOBAL) ADD_DEPENDENCIES(${lib} pcre2) + + GET_PROPERTY(MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + IF(MULTICONFIG) + SET(intdir "${CMAKE_CFG_INTDIR}/") + ELSE() + SET(intdir) + ENDIF() + + SET(file ${dir}/src/pcre2-build/${intdir}${CMAKE_STATIC_LIBRARY_PREFIX}${lib}${CMAKE_STATIC_LIBRARY_SUFFIX}) + IF(WIN32) # Debug libary name. # Same condition as in pcre2 CMakeLists.txt that adds "d" - GET_PROPERTY(MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) - IF(MULTICONFIG) - SET(intdir "${CMAKE_CFG_INTDIR}/") - ELSE() - SET(intdir) - ENDIF() - - SET(file ${dir}/src/pcre2-build/${intdir}${CMAKE_STATIC_LIBRARY_PREFIX}${lib}${CMAKE_STATIC_LIBRARY_SUFFIX}) SET(file_d ${dir}/src/pcre2-build/${intdir}${CMAKE_STATIC_LIBRARY_PREFIX}${lib}d${CMAKE_STATIC_LIBRARY_SUFFIX}) SET_TARGET_PROPERTIES(${lib} PROPERTIES IMPORTED_LOCATION_DEBUG ${file_d}) ELSE() - SET(file ${dir}/src/pcre2-build/${CMAKE_STATIC_LIBRARY_PREFIX}${lib}${CMAKE_STATIC_LIBRARY_SUFFIX}) SET(file_d) ENDIF() SET(byproducts ${byproducts} BUILD_BYPRODUCTS ${file} ${file_d}) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index dbe5fc4d650..9a66a839de9 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1260,6 +1260,7 @@ sub command_line_setup { { $path_client_bindir= mtr_path_exists("$bindir/client_release", "$bindir/client_debug", + "$bindir/client/debug", "$bindir/client$opt_vs_config", "$bindir/client", "$bindir/bin"); From df383043427fb22b0735fe31968db860f4cdb7a0 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Thu, 14 Oct 2021 08:37:23 +0400 Subject: [PATCH 3/7] MDEV-26742 Assertion `field->type_handler() == this' failed in FixedBinTypeBundle::Type_handler_fbt::stored_field_cmp_to_item --- .../type_inet/type_inet6_innodb.result | 27 +++++++++++++++++++ .../type_inet/type_inet6_innodb.test | 18 +++++++++++++ .../type_inet/type_inet6_myisam.result | 19 +++++++++++++ .../type_inet/type_inet6_myisam.test | 12 +++++++++ sql/field.cc | 19 +++++++++++-- sql/field.h | 2 ++ 6 files changed, 95 insertions(+), 2 deletions(-) diff --git a/plugin/type_inet/mysql-test/type_inet/type_inet6_innodb.result b/plugin/type_inet/mysql-test/type_inet/type_inet6_innodb.result index 5f7063b8f4b..a6911751747 100644 --- a/plugin/type_inet/mysql-test/type_inet/type_inet6_innodb.result +++ b/plugin/type_inet/mysql-test/type_inet/type_inet6_innodb.result @@ -88,5 +88,32 @@ Warnings: Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = INET6'::ff' DROP TABLE t1; # +# MDEV-26742 Assertion `field->type_handler() == this' failed in FixedBinTypeBundle::Type_handler_fbt::stored_field_cmp_to_item +# +CREATE TABLE t1 (pk inet6, c text) engine=myisam; +INSERT INTO t1 VALUES ('::',1); +CREATE TABLE t2 (d text, KEY (d)) engine=innodb ; +Warnings: +Note 1071 Specified key was too long; max key length is 3072 bytes +INSERT INTO t2 VALUES (2); +SELECT * FROM t2 JOIN t1 ON ( t1.pk > t2.d); +d pk c +Warnings: +Warning 1292 Incorrect inet6 value: '2' +UPDATE t2 JOIN t1 ON ( t1.pk > t2.d) SET t1.c = 1; +ERROR 22007: Incorrect inet6 value: '2' +SET sql_mode=''; +UPDATE t2 JOIN t1 ON ( t1.pk > t2.d) SET t1.c = 1; +Warnings: +Warning 1292 Incorrect inet6 value: '2' +SET sql_mode=DEFAULT; +SELECT * FROM t1; +pk c +:: 1 +SELECT * FROM t2; +d +2 +DROP TABLE t1, t2; +# # End of 10.5 tests # diff --git a/plugin/type_inet/mysql-test/type_inet/type_inet6_innodb.test b/plugin/type_inet/mysql-test/type_inet/type_inet6_innodb.test index dd6049abbf3..55826cc3e3f 100644 --- a/plugin/type_inet/mysql-test/type_inet/type_inet6_innodb.test +++ b/plugin/type_inet/mysql-test/type_inet/type_inet6_innodb.test @@ -12,6 +12,24 @@ SET default_storage_engine=InnoDB; --source type_inet6_engines.inc +--echo # +--echo # MDEV-26742 Assertion `field->type_handler() == this' failed in FixedBinTypeBundle::Type_handler_fbt::stored_field_cmp_to_item +--echo # + +CREATE TABLE t1 (pk inet6, c text) engine=myisam; +INSERT INTO t1 VALUES ('::',1); +CREATE TABLE t2 (d text, KEY (d)) engine=innodb ; +INSERT INTO t2 VALUES (2); +SELECT * FROM t2 JOIN t1 ON ( t1.pk > t2.d); +--error ER_TRUNCATED_WRONG_VALUE +UPDATE t2 JOIN t1 ON ( t1.pk > t2.d) SET t1.c = 1; +SET sql_mode=''; +UPDATE t2 JOIN t1 ON ( t1.pk > t2.d) SET t1.c = 1; +SET sql_mode=DEFAULT; +SELECT * FROM t1; +SELECT * FROM t2; +DROP TABLE t1, t2; + --echo # --echo # End of 10.5 tests diff --git a/plugin/type_inet/mysql-test/type_inet/type_inet6_myisam.result b/plugin/type_inet/mysql-test/type_inet/type_inet6_myisam.result index c8dba6ff959..ba65d61cb08 100644 --- a/plugin/type_inet/mysql-test/type_inet/type_inet6_myisam.result +++ b/plugin/type_inet/mysql-test/type_inet/type_inet6_myisam.result @@ -88,5 +88,24 @@ Warnings: Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = INET6'::ff' DROP TABLE t1; # +# MDEV-26742 Assertion `field->type_handler() == this' failed in FixedBinTypeBundle::Type_handler_fbt::stored_field_cmp_to_item +# +CREATE TABLE t1 (c varchar(64), key(c)) engine=myisam; +INSERT INTO t1 VALUES ('0::1'),('::1'),('::2'); +SELECT * FROM t1 WHERE c>CAST('::1' AS INET6); +c +::2 +EXPLAIN SELECT * FROM t1 WHERE c>CAST('::1' AS INET6); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index c c 67 NULL 3 Using where; Using index +SELECT * FROM t1 WHERE c=CAST('::1' AS INET6); +c +0::1 +::1 +EXPLAIN SELECT * FROM t1 WHERE c=CAST('::1' AS INET6); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index c c 67 NULL 3 Using where; Using index +DROP TABLE t1; +# # End of 10.5 tests # diff --git a/plugin/type_inet/mysql-test/type_inet/type_inet6_myisam.test b/plugin/type_inet/mysql-test/type_inet/type_inet6_myisam.test index c5183f01cf0..0ba8369ac95 100644 --- a/plugin/type_inet/mysql-test/type_inet/type_inet6_myisam.test +++ b/plugin/type_inet/mysql-test/type_inet/type_inet6_myisam.test @@ -10,6 +10,18 @@ SET default_storage_engine=MyISAM; --source type_inet6_engines.inc +--echo # +--echo # MDEV-26742 Assertion `field->type_handler() == this' failed in FixedBinTypeBundle::Type_handler_fbt::stored_field_cmp_to_item +--echo # + +CREATE TABLE t1 (c varchar(64), key(c)) engine=myisam; +INSERT INTO t1 VALUES ('0::1'),('::1'),('::2'); +SELECT * FROM t1 WHERE c>CAST('::1' AS INET6); +EXPLAIN SELECT * FROM t1 WHERE c>CAST('::1' AS INET6); +SELECT * FROM t1 WHERE c=CAST('::1' AS INET6); +EXPLAIN SELECT * FROM t1 WHERE c=CAST('::1' AS INET6); +DROP TABLE t1; + --echo # --echo # End of 10.5 tests diff --git a/sql/field.cc b/sql/field.cc index 7ff07540538..2c768527ced 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1276,6 +1276,21 @@ bool Field::can_be_substituted_to_equal_item(const Context &ctx, } +bool Field::cmp_is_done_using_type_handler_of_this(const Item_bool_func *cond, + const Item *item) const +{ + /* + We could eventually take comparison_type_handler() from cond, + instead of calculating it again. But only some descendants of + Item_bool_func has this method. So this needs some hierarchy changes. + Another option is to pass "class Context" to this method. + */ + Type_handler_hybrid_field_type cmp(type_handler_for_comparison()); + return !cmp.aggregate_for_comparison(item->type_handler_for_comparison()) && + cmp.type_handler() == type_handler_for_comparison(); +} + + /* This handles all numeric and BIT data types. */ @@ -7356,7 +7371,7 @@ bool Field_longstr::cmp_to_string_with_same_collation(const Item_bool_func *cond, const Item *item) const { - return item->cmp_type() == STRING_RESULT && + return cmp_is_done_using_type_handler_of_this(cond, item) && charset() == cond->compare_collation(); } @@ -7365,7 +7380,7 @@ bool Field_longstr::cmp_to_string_with_stricter_collation(const Item_bool_func *cond, const Item *item) const { - return item->cmp_type() == STRING_RESULT && + return cmp_is_done_using_type_handler_of_this(cond, item) && (charset() == cond->compare_collation() || cond->compare_collation()->state & MY_CS_BINSORT); } diff --git a/sql/field.h b/sql/field.h index 6747f8070dc..47a85efc43e 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1648,6 +1648,8 @@ protected: } int warn_if_overflow(int op_result); Copy_func *get_identical_copy_func() const; + bool cmp_is_done_using_type_handler_of_this(const Item_bool_func *cond, + const Item *item) const; bool can_optimize_scalar_range(const RANGE_OPT_PARAM *param, const KEY_PART *key_part, const Item_bool_func *cond, From cf8e78a40170118e2595e35c9c5f43aedeca91a0 Mon Sep 17 00:00:00 2001 From: Sergei Krivonos Date: Sat, 16 Oct 2021 02:35:16 +0300 Subject: [PATCH 4/7] Implemented Json_writer_array & Json_writer_object subitems name presence control --- .gitignore | 3 +++ sql/my_json_writer.cc | 2 ++ sql/my_json_writer.h | 36 ++++++++++++++++++++++++++++++------ 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 5005dbae363..9abb2b075bc 100644 --- a/.gitignore +++ b/.gitignore @@ -10,8 +10,11 @@ .ninja_* *.mri *.mri.tpl +/.cproject +/.project .gdb_history .vs/ +/.settings/ errmsg.sys typescript _CPack_Packages diff --git a/sql/my_json_writer.cc b/sql/my_json_writer.cc index 3234b8f9995..e4033ed0c02 100644 --- a/sql/my_json_writer.cc +++ b/sql/my_json_writer.cc @@ -260,6 +260,8 @@ void Json_writer::add_str(const String &str) add_str(str.ptr(), str.length()); } +thread_local std::vector Json_writer_struct::named_items_expectation; + Json_writer_temp_disable::Json_writer_temp_disable(THD *thd_arg) { thd= thd_arg; diff --git a/sql/my_json_writer.h b/sql/my_json_writer.h index bc8002de529..f91a7c9ba8c 100644 --- a/sql/my_json_writer.h +++ b/sql/my_json_writer.h @@ -15,8 +15,12 @@ #ifndef JSON_WRITER_INCLUDED #define JSON_WRITER_INCLUDED + #include "my_base.h" #include "sql_select.h" + +#include + class Opt_trace_stmt; class Opt_trace_context; class Json_writer; @@ -308,6 +312,7 @@ public: /* A common base for Json_writer_object and Json_writer_array */ class Json_writer_struct { + static thread_local std::vector named_items_expectation; protected: Json_writer* my_writer; Json_value_helper context; @@ -317,16 +322,29 @@ protected: bool closed; public: - explicit Json_writer_struct(THD *thd) + explicit Json_writer_struct(THD *thd, bool expect_named_children) { my_writer= thd->opt_trace.get_current_json(); context.init(my_writer); closed= false; + named_items_expectation.push_back(expect_named_children); } - bool trace_started() + + virtual ~Json_writer_struct() + { + named_items_expectation.pop_back(); + } + + bool trace_started() const { return my_writer != 0; } + + bool named_item_expected() const + { + return named_items_expectation.size() > 1 + && *(named_items_expectation.rbegin() + 1); + } }; @@ -347,15 +365,17 @@ private: } public: explicit Json_writer_object(THD *thd) - : Json_writer_struct(thd) + : Json_writer_struct(thd, true) { + DBUG_ASSERT(!named_item_expected()); if (unlikely(my_writer)) my_writer->start_object(); } explicit Json_writer_object(THD* thd, const char *str) - : Json_writer_struct(thd) + : Json_writer_struct(thd, true) { + DBUG_ASSERT(named_item_expected()); if (unlikely(my_writer)) my_writer->add_member(str).start_object(); } @@ -519,14 +539,18 @@ public: class Json_writer_array : public Json_writer_struct { public: - Json_writer_array(THD *thd): Json_writer_struct(thd) + Json_writer_array(THD *thd) + : Json_writer_struct(thd, false) { + DBUG_ASSERT(!named_item_expected()); if (unlikely(my_writer)) my_writer->start_array(); } - Json_writer_array(THD *thd, const char *str) : Json_writer_struct(thd) + Json_writer_array(THD *thd, const char *str) + : Json_writer_struct(thd, false) { + DBUG_ASSERT(named_item_expected()); if (unlikely(my_writer)) my_writer->add_member(str).start_array(); } From 052dda61bb8d5fef271ecd091a5b5db25d57040b Mon Sep 17 00:00:00 2001 From: Sergei Krivonos Date: Sun, 17 Oct 2021 12:36:12 +0300 Subject: [PATCH 5/7] Made optional Json_writer_object / Json_writer_array consistency check --- CMakeLists.txt | 4 ++++ sql/my_json_writer.cc | 2 ++ sql/my_json_writer.h | 16 ++++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 591920450ea..2bafbda9964 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -185,6 +185,10 @@ IF(DISABLE_SHARED) SET(WITHOUT_DYNAMIC_PLUGINS 1) ENDIF() OPTION(ENABLED_PROFILING "Enable profiling" ON) +OPTION(ENABLED_JSON_WRITER_CONSISTENCY_CHECKS "Enable Json_writer_object / Json_writer_array checking to produce consistent JSON output" OFF) +IF(ENABLED_JSON_WRITER_CONSISTENCY_CHECKS) + ADD_DEFINITIONS(-DENABLED_JSON_WRITER_CONSISTENCY_CHECKS) +ENDIF() OPTION(WITHOUT_SERVER "Build only the client library and clients" OFF) IF(UNIX) OPTION(WITH_VALGRIND "Valgrind instrumentation" OFF) diff --git a/sql/my_json_writer.cc b/sql/my_json_writer.cc index e4033ed0c02..8e6f0942857 100644 --- a/sql/my_json_writer.cc +++ b/sql/my_json_writer.cc @@ -260,7 +260,9 @@ void Json_writer::add_str(const String &str) add_str(str.ptr(), str.length()); } +#ifdef ENABLED_JSON_WRITER_CONSISTENCY_CHECKS thread_local std::vector Json_writer_struct::named_items_expectation; +#endif Json_writer_temp_disable::Json_writer_temp_disable(THD *thd_arg) { diff --git a/sql/my_json_writer.h b/sql/my_json_writer.h index f91a7c9ba8c..9686984ba9b 100644 --- a/sql/my_json_writer.h +++ b/sql/my_json_writer.h @@ -312,7 +312,9 @@ public: /* A common base for Json_writer_object and Json_writer_array */ class Json_writer_struct { +#ifdef ENABLED_JSON_WRITER_CONSISTENCY_CHECKS static thread_local std::vector named_items_expectation; +#endif protected: Json_writer* my_writer; Json_value_helper context; @@ -327,12 +329,16 @@ public: my_writer= thd->opt_trace.get_current_json(); context.init(my_writer); closed= false; +#ifdef ENABLED_JSON_WRITER_CONSISTENCY_CHECKS named_items_expectation.push_back(expect_named_children); +#endif } virtual ~Json_writer_struct() { +#ifdef ENABLED_JSON_WRITER_CONSISTENCY_CHECKS named_items_expectation.pop_back(); +#endif } bool trace_started() const @@ -340,11 +346,13 @@ public: return my_writer != 0; } +#ifdef ENABLED_JSON_WRITER_CONSISTENCY_CHECKS bool named_item_expected() const { return named_items_expectation.size() > 1 && *(named_items_expectation.rbegin() + 1); } +#endif }; @@ -367,7 +375,9 @@ public: explicit Json_writer_object(THD *thd) : Json_writer_struct(thd, true) { +#ifdef ENABLED_JSON_WRITER_CONSISTENCY_CHECKS DBUG_ASSERT(!named_item_expected()); +#endif if (unlikely(my_writer)) my_writer->start_object(); } @@ -375,7 +385,9 @@ public: explicit Json_writer_object(THD* thd, const char *str) : Json_writer_struct(thd, true) { +#ifdef ENABLED_JSON_WRITER_CONSISTENCY_CHECKS DBUG_ASSERT(named_item_expected()); +#endif if (unlikely(my_writer)) my_writer->add_member(str).start_object(); } @@ -542,7 +554,9 @@ public: Json_writer_array(THD *thd) : Json_writer_struct(thd, false) { +#ifdef ENABLED_JSON_WRITER_CONSISTENCY_CHECKS DBUG_ASSERT(!named_item_expected()); +#endif if (unlikely(my_writer)) my_writer->start_array(); } @@ -550,7 +564,9 @@ public: Json_writer_array(THD *thd, const char *str) : Json_writer_struct(thd, false) { +#ifdef ENABLED_JSON_WRITER_CONSISTENCY_CHECKS DBUG_ASSERT(named_item_expected()); +#endif if (unlikely(my_writer)) my_writer->add_member(str).start_array(); } From 9068020efe8f8b0161ea0e48a89fb90fcd8e59aa Mon Sep 17 00:00:00 2001 From: Nayuta Yanagisawa Date: Tue, 21 Sep 2021 18:32:37 +0900 Subject: [PATCH 6/7] MDEV-26539 SIGSEGV in spider_check_and_set_trx_isolation and I_P_List_iterator from THD::drop_temporary_table (10.5.3 opt only) on ALTER The server crashes if ALTER TABLE, which accesses physical data placed at data nodes, is performed on a Spider table. The cause of the bug is that spider_check_trx_and_get_conn() does not allocate connections if sql_command == SQLCOM_ALTER_TABLE. Some ALTER TABLE statements, like ALTER TABLE ... CHECK PARTITION, access data nodes. So, we need to allocate a new connection before performing ALTER TABLEs. --- .../spider/bugfix/r/mdev_26539.result | 36 +++++++++++++++++ .../mysql-test/spider/bugfix/t/mdev_26539.cnf | 3 ++ .../spider/bugfix/t/mdev_26539.test | 40 +++++++++++++++++++ storage/spider/spd_trx.cc | 6 +-- 4 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 storage/spider/mysql-test/spider/bugfix/r/mdev_26539.result create mode 100644 storage/spider/mysql-test/spider/bugfix/t/mdev_26539.cnf create mode 100644 storage/spider/mysql-test/spider/bugfix/t/mdev_26539.test diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_26539.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_26539.result new file mode 100644 index 00000000000..4e195fddfad --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_26539.result @@ -0,0 +1,36 @@ +for master_1 +for child2 +child2_1 +child2_2 +child2_3 +for child3 +# +# MDEV-26539 SIGSEGV in spider_check_and_set_trx_isolation and I_P_List_iterator from THD::drop_temporary_table (10.5.3 opt only) on ALTER +# +connection child2_1; +CREATE DATABASE auto_test_remote; +USE auto_test_remote; +CREATE TABLE tbl_a ( +c INT +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +connection master_1; +CREATE DATABASE auto_test_local; +USE auto_test_local; +CREATE TABLE tbl_a ( +c INT +) ENGINE=Spider DEFAULT CHARSET=utf8 COMMENT='table "tbl_a"' PARTITION BY LIST COLUMNS (c) ( +PARTITION pt1 DEFAULT COMMENT = 'srv "s_2_1"' +); +INSERT INTO tbl_a VALUES (1); +ALTER TABLE tbl_a CHECK PARTITION ALL; +Table Op Msg_type Msg_text +auto_test_local.tbl_a check status OK +DROP DATABASE auto_test_local; +connection child2_1; +DROP DATABASE auto_test_remote; +for master_1 +for child2 +child2_1 +child2_2 +child2_3 +for child3 diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_26539.cnf b/storage/spider/mysql-test/spider/bugfix/t/mdev_26539.cnf new file mode 100644 index 00000000000..05dfd8a0bce --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_26539.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_26539.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_26539.test new file mode 100644 index 00000000000..f2561f8c9a5 --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_26539.test @@ -0,0 +1,40 @@ +--disable_query_log +--disable_result_log +--source ../../t/test_init.inc +--enable_result_log +--enable_query_log + +--echo # +--echo # MDEV-26539 SIGSEGV in spider_check_and_set_trx_isolation and I_P_List_iterator from THD::drop_temporary_table (10.5.3 opt only) on ALTER +--echo # + +--connection child2_1 +CREATE DATABASE auto_test_remote; +USE auto_test_remote; +eval CREATE TABLE tbl_a ( + c INT +) $CHILD2_1_ENGINE $CHILD2_1_CHARSET; + +--connection master_1 +CREATE DATABASE auto_test_local; +USE auto_test_local; + +eval CREATE TABLE tbl_a ( + c INT +) $MASTER_1_ENGINE $MASTER_1_CHARSET COMMENT='table "tbl_a"' PARTITION BY LIST COLUMNS (c) ( + PARTITION pt1 DEFAULT COMMENT = 'srv "s_2_1"' +); + +INSERT INTO tbl_a VALUES (1); +ALTER TABLE tbl_a CHECK PARTITION ALL; + +DROP DATABASE auto_test_local; + +--connection child2_1 +DROP DATABASE auto_test_remote; + +--disable_query_log +--disable_result_log +--source ../../t/test_deinit.inc +--enable_result_log +--enable_query_log diff --git a/storage/spider/spd_trx.cc b/storage/spider/spd_trx.cc index 0eda9d31df6..80658012506 100644 --- a/storage/spider/spd_trx.cc +++ b/storage/spider/spd_trx.cc @@ -3744,10 +3744,8 @@ int spider_check_trx_and_get_conn( } spider->wide_handler->trx = trx; spider->set_error_mode(); - if ( - spider->wide_handler->sql_command != SQLCOM_DROP_TABLE && - spider->wide_handler->sql_command != SQLCOM_ALTER_TABLE - ) { + if (spider->wide_handler->sql_command != SQLCOM_DROP_TABLE) + { SPIDER_TRX_HA *trx_ha = spider_check_trx_ha(trx, spider); if (!trx_ha || trx_ha->wait_for_reusing) spider_trx_set_link_idx_for_all(spider); From 18eab4a83280049974265358b0d78389d05cd67b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 18 Oct 2021 12:49:10 +0300 Subject: [PATCH 7/7] MDEV-26682 Replication timeouts with XA PREPARE The purpose of non-exclusive locks in a transaction is to guarantee that the records covered by those locks must remain in that way until the transaction is committed. (The purpose of gap locks is to ensure that a record that was nonexistent will remain that way.) Once a transaction has reached the XA PREPARE state, the only allowed further actions are XA ROLLBACK or XA COMMIT. Therefore, it can be argued that only the exclusive locks that the XA PREPARE transaction is holding are essential. Furthermore, InnoDB never preserved explicit locks across server restart. For XA PREPARE transations, we will only recover implicit exclusive locks for records that had been modified. Because of the fact that XA PREPARE followed by a server restart will cause some locks to be lost, we might as well always release all non-exclusive locks during the execution of an XA PREPARE statement. lock_release_on_prepare(): Release non-exclusive locks on XA PREPARE. trx_prepare(): Invoke lock_release_on_prepare() unless the isolation level is SERIALIZABLE or this is an internal distributed transaction with the binlog (not actual XA PREPARE statement). This has been discussed with Sergei Golubchik and Andrei Elkin. Reviewed by: Sergei Golubchik --- mysql-test/suite/rpl/r/rpl_xa.result | 61 ++++++++++++++ .../rpl/r/rpl_xa_gtid_pos_auto_engine.result | 61 ++++++++++++++ mysql-test/suite/rpl/t/rpl_xa.inc | 80 ++++++++++++++++++- storage/innobase/include/lock0lock.h | 6 +- storage/innobase/lock/lock0lock.cc | 59 ++++++++++++++ storage/innobase/trx/trx0trx.cc | 14 ++++ 6 files changed, 279 insertions(+), 2 deletions(-) diff --git a/mysql-test/suite/rpl/r/rpl_xa.result b/mysql-test/suite/rpl/r/rpl_xa.result index a90e6e0b996..061c7b360d0 100644 --- a/mysql-test/suite/rpl/r/rpl_xa.result +++ b/mysql-test/suite/rpl/r/rpl_xa.result @@ -219,4 +219,65 @@ include/sync_with_master_gtid.inc connection master; drop database test_ign; drop table t1, t2, t3, tm; +# +# MDEV-26682 slave lock timeout with XA and gap locks +# +create table t1 (a int primary key, b int unique) engine=innodb; +insert t1 values (1,1),(3,3),(5,5); +connection slave; +set session tx_isolation='repeatable-read'; +start transaction; +select * from t1; +a b +1 1 +3 3 +5 5 +connect m2, localhost, root; +delete from t1 where a=3; +xa start 'x1'; +update t1 set b=3 where a=5; +xa end 'x1'; +xa prepare 'x1'; +connect m3, localhost, root; +insert t1 values (2, 2); +-->slave +connection slave; +commit; +select * from t1; +a b +1 1 +2 2 +5 5 +connection m2; +xa rollback 'x1'; +disconnect m2; +disconnect m3; +connection master; +drop table t1; +create table t1 (id int auto_increment primary key, c1 int not null unique) +engine=innodb; +create table t2 (id int auto_increment primary key, c1 int not null, +foreign key(c1) references t1(c1), unique key(c1)) engine=innodb; +insert t1 values (869,1), (871,3), (873,4), (872,5), (870,6), (877,7); +insert t2 values (795,6), (800,7); +xa start '1'; +update t2 set id = 9, c1 = 5 where c1 in (null, null, null, null, null, 7, 3); +connect con1, localhost,root; +xa start '2'; +delete from t1 where c1 like '3%'; +xa end '2'; +xa prepare '2'; +connection master; +xa end '1'; +xa prepare '1'; +->slave +connection slave; +connection slave; +include/sync_with_master_gtid.inc +connection con1; +xa commit '2'; +disconnect con1; +connection master; +xa commit '1'; +drop table t2, t1; include/rpl_end.inc diff --git a/mysql-test/suite/rpl/r/rpl_xa_gtid_pos_auto_engine.result b/mysql-test/suite/rpl/r/rpl_xa_gtid_pos_auto_engine.result index ffd0426ab0d..35625cc7026 100644 --- a/mysql-test/suite/rpl/r/rpl_xa_gtid_pos_auto_engine.result +++ b/mysql-test/suite/rpl/r/rpl_xa_gtid_pos_auto_engine.result @@ -228,6 +228,67 @@ include/sync_with_master_gtid.inc connection master; drop database test_ign; drop table t1, t2, t3, tm; +# +# MDEV-26682 slave lock timeout with XA and gap locks +# +create table t1 (a int primary key, b int unique) engine=innodb; +insert t1 values (1,1),(3,3),(5,5); +connection slave; +set session tx_isolation='repeatable-read'; +start transaction; +select * from t1; +a b +1 1 +3 3 +5 5 +connect m2, localhost, root; +delete from t1 where a=3; +xa start 'x1'; +update t1 set b=3 where a=5; +xa end 'x1'; +xa prepare 'x1'; +connect m3, localhost, root; +insert t1 values (2, 2); +-->slave +connection slave; +commit; +select * from t1; +a b +1 1 +2 2 +5 5 +connection m2; +xa rollback 'x1'; +disconnect m2; +disconnect m3; +connection master; +drop table t1; +create table t1 (id int auto_increment primary key, c1 int not null unique) +engine=innodb; +create table t2 (id int auto_increment primary key, c1 int not null, +foreign key(c1) references t1(c1), unique key(c1)) engine=innodb; +insert t1 values (869,1), (871,3), (873,4), (872,5), (870,6), (877,7); +insert t2 values (795,6), (800,7); +xa start '1'; +update t2 set id = 9, c1 = 5 where c1 in (null, null, null, null, null, 7, 3); +connect con1, localhost,root; +xa start '2'; +delete from t1 where c1 like '3%'; +xa end '2'; +xa prepare '2'; +connection master; +xa end '1'; +xa prepare '1'; +->slave +connection slave; +connection slave; +include/sync_with_master_gtid.inc +connection con1; +xa commit '2'; +disconnect con1; +connection master; +xa commit '1'; +drop table t2, t1; connection slave; include/stop_slave.inc SET @@global.gtid_pos_auto_engines=""; diff --git a/mysql-test/suite/rpl/t/rpl_xa.inc b/mysql-test/suite/rpl/t/rpl_xa.inc index 38344da5e66..d22d2d2ef3d 100644 --- a/mysql-test/suite/rpl/t/rpl_xa.inc +++ b/mysql-test/suite/rpl/t/rpl_xa.inc @@ -1,6 +1,6 @@ # # This "body" file checks general properties of XA transaction replication -# as of MDEV-7974. +# as of MDEV-742. # Parameters: # --let rpl_xa_check= SELECT ... # @@ -353,3 +353,81 @@ source include/sync_with_master_gtid.inc; connection master; --eval drop database test_ign drop table t1, t2, t3, tm; + +--echo # +--echo # MDEV-26682 slave lock timeout with XA and gap locks +--echo # +create table t1 (a int primary key, b int unique) engine=innodb; +insert t1 values (1,1),(3,3),(5,5); +sync_slave_with_master; + +# set a strong isolation level to keep the read view below. +# alternatively a long-running select can do that too even in read-committed +set session tx_isolation='repeatable-read'; +start transaction; +# opens a read view to disable purge on the slave +select * from t1; + +connect m2, localhost, root; +# now, delete a value, purge it on the master, but not on the slave +delete from t1 where a=3; +xa start 'x1'; +# this sets a gap lock on <3>, when it exists (so, on the slave) +update t1 set b=3 where a=5; +xa end 'x1'; +xa prepare 'x1'; + +connect m3, localhost, root; +# and this tries to insert straight into the locked gap +insert t1 values (2, 2); + +echo -->slave; +sync_slave_with_master; +commit; +select * from t1; + +connection m2; +xa rollback 'x1'; +disconnect m2; +disconnect m3; + +connection master; + +drop table t1; + +create table t1 (id int auto_increment primary key, c1 int not null unique) +engine=innodb; + +create table t2 (id int auto_increment primary key, c1 int not null, +foreign key(c1) references t1(c1), unique key(c1)) engine=innodb; + +insert t1 values (869,1), (871,3), (873,4), (872,5), (870,6), (877,7); +insert t2 values (795,6), (800,7); + +xa start '1'; +update t2 set id = 9, c1 = 5 where c1 in (null, null, null, null, null, 7, 3); + +connect con1, localhost,root; +xa start '2'; +delete from t1 where c1 like '3%'; +xa end '2'; +xa prepare '2'; + +connection master; +xa end '1'; +xa prepare '1'; + +echo ->slave; + +sync_slave_with_master; + +connection slave; +source include/sync_with_master_gtid.inc; + +connection con1; +xa commit '2'; +disconnect con1; + +connection master; +xa commit '1'; +drop table t2, t1; diff --git a/storage/innobase/include/lock0lock.h b/storage/innobase/include/lock0lock.h index 3b63b06a9bb..225c246f4e7 100644 --- a/storage/innobase/include/lock0lock.h +++ b/storage/innobase/include/lock0lock.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 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 @@ -478,6 +478,10 @@ lock_rec_unlock( and release possible other transactions waiting because of these locks. */ void lock_release(trx_t* trx); +/** Release non-exclusive locks on XA PREPARE, +and release possible other transactions waiting because of these locks. */ +void lock_release_on_prepare(trx_t *trx); + /*************************************************************//** Get the lock hash table */ UNIV_INLINE diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc index d7ec5736826..12764470bef 100644 --- a/storage/innobase/lock/lock0lock.cc +++ b/storage/innobase/lock/lock0lock.cc @@ -4219,6 +4219,65 @@ void lock_release(trx_t* trx) #endif } +/** Release non-exclusive locks on XA PREPARE, +and release possible other transactions waiting because of these locks. */ +void lock_release_on_prepare(trx_t *trx) +{ + ulint count= 0; + lock_mutex_enter(); + ut_ad(!trx_mutex_own(trx)); + + for (lock_t *lock= UT_LIST_GET_LAST(trx->lock.trx_locks); lock; ) + { + ut_ad(lock->trx == trx); + + if (lock_get_type_low(lock) == LOCK_REC) + { + ut_ad(!lock->index->table->is_temporary()); + if (lock_rec_get_gap(lock) || lock_get_mode(lock) != LOCK_X) + lock_rec_dequeue_from_page(lock); + else + { + ut_ad(trx->dict_operation || + lock->index->table->id >= DICT_HDR_FIRST_ID); +retain_lock: + lock= UT_LIST_GET_PREV(trx_locks, lock); + continue; + } + } + else + { + ut_ad(lock_get_type_low(lock) & LOCK_TABLE); + dict_table_t *table= lock->un_member.tab_lock.table; + ut_ad(!table->is_temporary()); + + switch (lock_get_mode(lock)) { + case LOCK_IS: + case LOCK_S: + lock_table_dequeue(lock); + break; + case LOCK_IX: + case LOCK_X: + ut_ad(table->id >= DICT_HDR_FIRST_ID || trx->dict_operation); + /* fall through */ + default: + goto retain_lock; + } + } + + if (++count == LOCK_RELEASE_INTERVAL) + { + lock_mutex_exit(); + count= 0; + lock_mutex_enter(); + } + + lock= UT_LIST_GET_LAST(trx->lock.trx_locks); + } + + lock_mutex_exit(); +} + /* True if a lock mode is S or X */ #define IS_LOCK_S_OR_X(lock) \ (lock_get_mode(lock) == LOCK_S \ diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index cf8fa17cf1a..16ea7c41d71 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -1971,6 +1971,20 @@ trx_prepare( We must not be holding any mutexes or latches here. */ trx_flush_log_if_needed(lsn, trx); + + if (!UT_LIST_GET_LEN(trx->lock.trx_locks) + || trx->isolation_level == TRX_ISO_SERIALIZABLE) { + /* Do not release any locks at the + SERIALIZABLE isolation level. */ + } else if (!trx->mysql_thd + || thd_sql_command(trx->mysql_thd) + != SQLCOM_XA_PREPARE) { + /* Do not release locks for XA COMMIT ONE PHASE + or for internal distributed transactions + (XID::get_my_xid() would be nonzero). */ + } else { + lock_release_on_prepare(trx); + } } }