From 2be617d869e614e2c0e7313fd77649a03c891cfd Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Wed, 13 Apr 2022 17:17:17 +0400 Subject: [PATCH 01/28] MDEV-25243 ASAN heap-use-after-free in Item_func_sp::execute_impl upon concurrent view DDL and I_S query with view and function --- .../r/information_schema_columns.result | 47 +++++++++ mysql-test/r/information_schema_tables.result | 77 +++++++++++++++ mysql-test/t/information_schema_columns.test | 48 ++++++++++ mysql-test/t/information_schema_tables.test | 95 +++++++++++++++++++ sql/sp_cache.cc | 12 +++ sql/sql_class.cc | 9 +- sql/sql_class.h | 50 +++++++++- sql/sql_show.cc | 31 +++++- 8 files changed, 358 insertions(+), 11 deletions(-) create mode 100644 mysql-test/r/information_schema_columns.result create mode 100644 mysql-test/r/information_schema_tables.result create mode 100644 mysql-test/t/information_schema_columns.test create mode 100644 mysql-test/t/information_schema_tables.test diff --git a/mysql-test/r/information_schema_columns.result b/mysql-test/r/information_schema_columns.result new file mode 100644 index 00000000000..3624a6db368 --- /dev/null +++ b/mysql-test/r/information_schema_columns.result @@ -0,0 +1,47 @@ +# +# Start of 10.2 tests +# +# +# MDEV-25243 ASAN heap-use-after-free in Item_func_sp::execute_impl upon concurrent view DDL and I_S query with view and function +# +CREATE FUNCTION f1() RETURNS INT RETURN 1; +CREATE VIEW v01 AS SELECT f1(); +CREATE VIEW v02 AS SELECT f1(); +connect con1,localhost,root,,; +SELECT GET_LOCK('v01',30); +GET_LOCK('v01',30) +1 +SELECT GET_LOCK('v02',30); +GET_LOCK('v02',30) +1 +connection default; +SELECT * FROM INFORMATION_SCHEMA.COLUMNS +WHERE TABLE_SCHEMA='test' + AND TABLE_NAME LIKE 'v0%' + AND GET_LOCK(TABLE_NAME,30) +AND RELEASE_LOCK(TABLE_NAME) +AND f1()=1 +ORDER BY TABLE_NAME; +connection con1; +connection con1; +SELECT RELEASE_LOCK('v01') /* Let the first row evaluate f1 */; +RELEASE_LOCK('v01') +1 +CREATE FUNCTION f2() RETURNS INT RETURN 1 /* Invalidate SP cache*/; +SELECT RELEASE_LOCK('v02') /* Let the second row evaluate f1() */; +RELEASE_LOCK('v02') +1 +DROP FUNCTION f2; +disconnect con1; +connection default; +SELECT RELEASE_LOCK('v01'); +RELEASE_LOCK('v01') +NULL +SELECT RELEASE_LOCK('v02'); +RELEASE_LOCK('v02') +NULL +DROP VIEW v01, v02; +DROP FUNCTION f1; +# +# End of 10.2 tests +# diff --git a/mysql-test/r/information_schema_tables.result b/mysql-test/r/information_schema_tables.result new file mode 100644 index 00000000000..33dafaa39eb --- /dev/null +++ b/mysql-test/r/information_schema_tables.result @@ -0,0 +1,77 @@ +# +# Start of 10.2 tests +# +# +# MDEV-25243 ASAN heap-use-after-free in Item_func_sp::execute_impl upon concurrent view DDL and I_S query with view and function +# +# The originally reported non-deterministic test. +# It did not fail reliably on every run. +CREATE TABLE t (a INT); +INSERT INTO t VALUES (1),(2); +CREATE FUNCTION f(b INT) RETURNS INT RETURN 1; +CREATE VIEW v AS SELECT f(SUM(a)) FROM t; +connect con1,localhost,root,,test; +LOOP +CREATE OR REPLACE VIEW vv AS SELECT 1; +END LOOP $ +connection default; +SELECT v.* FROM v JOIN INFORMATION_SCHEMA.TABLES WHERE DATA_LENGTH = -1; +f(SUM(a)) +KILL CONID; +disconnect con1; +connection default; +DROP VIEW IF EXISTS vv; +DROP VIEW v; +DROP FUNCTION f; +DROP TABLE t; +# The second test version from the MDEV. +# It failed more reliably, but still was not deterministic. +CREATE FUNCTION f() RETURNS INT RETURN 1; +CREATE VIEW v AS SELECT f() FROM seq_1_to_10; +SELECT * FROM INFORMATION_SCHEMA.TABLES, v;; +connect con1,localhost,root,,; +CREATE VIEW v2 AS SELECT 1; +connection default; +disconnect con1; +DROP VIEW v; +DROP VIEW v2; +DROP FUNCTION f; +# The third test version from the MDEV. +# It failed reliably, and should be deterninistic. +CREATE FUNCTION f1() RETURNS INT RETURN 1; +CREATE VIEW v01 AS SELECT f1(); +CREATE VIEW v02 AS SELECT f1(); +connect con1,localhost,root,,; +SELECT GET_LOCK('v01',30); +GET_LOCK('v01',30) +1 +SELECT GET_LOCK('v02',30); +GET_LOCK('v02',30) +1 +connection default; +SELECT * FROM INFORMATION_SCHEMA.TABLES +WHERE TABLE_SCHEMA='test' + AND TABLE_NAME LIKE 'v0%' + AND GET_LOCK(TABLE_NAME,30) +AND RELEASE_LOCK(TABLE_NAME) +AND f1()=1 +ORDER BY TABLE_NAME; +connection con1; +SELECT RELEASE_LOCK('v01') /* Let the first row evaluate f1 */; +RELEASE_LOCK('v01') +1 +CREATE FUNCTION f2() RETURNS INT RETURN 1 /* Invalidate SP cache*/; +SELECT RELEASE_LOCK('v02') /* Let the second row evaluate f1() */; +RELEASE_LOCK('v02') +1 +DROP FUNCTION f2; +disconnect con1; +connection default; +TABLE_CATALOG TABLE_SCHEMA TABLE_NAME TABLE_TYPE ENGINE VERSION ROW_FORMAT TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE AUTO_INCREMENT CREATE_TIME UPDATE_TIME CHECK_TIME TABLE_COLLATION CHECKSUM CREATE_OPTIONS TABLE_COMMENT +def test v01 VIEW NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL VIEW +def test v02 VIEW NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL VIEW +DROP VIEW v01, v02; +DROP FUNCTION f1; +# +# End of 10.2 tests +# diff --git a/mysql-test/t/information_schema_columns.test b/mysql-test/t/information_schema_columns.test new file mode 100644 index 00000000000..0171b221110 --- /dev/null +++ b/mysql-test/t/information_schema_columns.test @@ -0,0 +1,48 @@ +--echo # +--echo # Start of 10.2 tests +--echo # + +--echo # +--echo # MDEV-25243 ASAN heap-use-after-free in Item_func_sp::execute_impl upon concurrent view DDL and I_S query with view and function +--echo # + + +CREATE FUNCTION f1() RETURNS INT RETURN 1; +CREATE VIEW v01 AS SELECT f1(); +CREATE VIEW v02 AS SELECT f1(); + +--connect(con1,localhost,root,,) +SELECT GET_LOCK('v01',30); +SELECT GET_LOCK('v02',30); +--connection default + +--send + SELECT * FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA='test' + AND TABLE_NAME LIKE 'v0%' + AND GET_LOCK(TABLE_NAME,30) + AND RELEASE_LOCK(TABLE_NAME) + AND f1()=1 + ORDER BY TABLE_NAME; + +--connection con1 +--connection con1 +SELECT RELEASE_LOCK('v01') /* Let the first row evaluate f1 */; +CREATE FUNCTION f2() RETURNS INT RETURN 1 /* Invalidate SP cache*/; +SELECT RELEASE_LOCK('v02') /* Let the second row evaluate f1() */; +DROP FUNCTION f2; +--disconnect con1 + +--connection default +--disable_result_log +--reap +--enable_result_log +SELECT RELEASE_LOCK('v01'); +SELECT RELEASE_LOCK('v02'); + +DROP VIEW v01, v02; +DROP FUNCTION f1; + +--echo # +--echo # End of 10.2 tests +--echo # diff --git a/mysql-test/t/information_schema_tables.test b/mysql-test/t/information_schema_tables.test new file mode 100644 index 00000000000..ebb1e21a65b --- /dev/null +++ b/mysql-test/t/information_schema_tables.test @@ -0,0 +1,95 @@ +--source include/have_sequence.inc + +--echo # +--echo # Start of 10.2 tests +--echo # + +--echo # +--echo # MDEV-25243 ASAN heap-use-after-free in Item_func_sp::execute_impl upon concurrent view DDL and I_S query with view and function +--echo # + +--echo # The originally reported non-deterministic test. +--echo # It did not fail reliably on every run. + +CREATE TABLE t (a INT); +INSERT INTO t VALUES (1),(2); +CREATE FUNCTION f(b INT) RETURNS INT RETURN 1; +CREATE VIEW v AS SELECT f(SUM(a)) FROM t; +--connect (con1,localhost,root,,test) +--let $conid= `SELECT CONNECTION_ID()` +--delimiter $ +--send +LOOP + CREATE OR REPLACE VIEW vv AS SELECT 1; +END LOOP $ +--delimiter ; +--connection default +--disable_warnings +SELECT v.* FROM v JOIN INFORMATION_SCHEMA.TABLES WHERE DATA_LENGTH = -1; +--enable_warnings +# Cleanup +--replace_result $conid CONID +--eval KILL $conid +--disconnect con1 +--connection default +DROP VIEW IF EXISTS vv; +DROP VIEW v; +DROP FUNCTION f; +DROP TABLE t; + + +--echo # The second test version from the MDEV. +--echo # It failed more reliably, but still was not deterministic. + + +CREATE FUNCTION f() RETURNS INT RETURN 1; +CREATE VIEW v AS SELECT f() FROM seq_1_to_10; +--send SELECT * FROM INFORMATION_SCHEMA.TABLES, v; +--connect(con1,localhost,root,,) +CREATE VIEW v2 AS SELECT 1; +--connection default +--disable_result_log +--reap +--enable_result_log +--disconnect con1 +DROP VIEW v; +DROP VIEW v2; +DROP FUNCTION f; + +--echo # The third test version from the MDEV. +--echo # It failed reliably, and should be deterninistic. + +CREATE FUNCTION f1() RETURNS INT RETURN 1; +CREATE VIEW v01 AS SELECT f1(); +CREATE VIEW v02 AS SELECT f1(); + +--connect(con1,localhost,root,,) +SELECT GET_LOCK('v01',30); +SELECT GET_LOCK('v02',30); +--connection default + +--send + SELECT * FROM INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA='test' + AND TABLE_NAME LIKE 'v0%' + AND GET_LOCK(TABLE_NAME,30) + AND RELEASE_LOCK(TABLE_NAME) + AND f1()=1 + ORDER BY TABLE_NAME; + +--connection con1 +SELECT RELEASE_LOCK('v01') /* Let the first row evaluate f1 */; +CREATE FUNCTION f2() RETURNS INT RETURN 1 /* Invalidate SP cache*/; +SELECT RELEASE_LOCK('v02') /* Let the second row evaluate f1() */; +DROP FUNCTION f2; +--disconnect con1 +--connection default +--reap + + +DROP VIEW v01, v02; +DROP FUNCTION f1; + +--echo # +--echo # End of 10.2 tests +--echo # diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc index bc91634eb32..9829167d29d 100644 --- a/sql/sp_cache.cc +++ b/sql/sp_cache.cc @@ -313,3 +313,15 @@ sp_cache::cleanup() { my_hash_free(&m_hashtable); } + + +void Sp_caches::sp_caches_clear() +{ + sp_cache_clear(&sp_proc_cache); + sp_cache_clear(&sp_func_cache); +#if MYSQL_VERSION_ID >= 100300 +#error Remove the preprocessor condition, !!!but keep the code!!! + sp_cache_clear(&sp_package_spec_cache); + sp_cache_clear(&sp_package_body_cache); +#endif +} diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 4edf573e596..aa64e42e144 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -806,9 +806,6 @@ THD::THD(my_thread_id id, bool is_wsrep_applier) (my_hash_get_key) get_var_key, (my_hash_free_key) free_user_var, HASH_THREAD_SPECIFIC); - sp_proc_cache= NULL; - sp_func_cache= NULL; - /* For user vars replication*/ if (opt_bin_log) my_init_dynamic_array(&user_var_events, @@ -1353,8 +1350,7 @@ void THD::change_user(void) my_hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0, (my_hash_get_key) get_var_key, (my_hash_free_key) free_user_var, 0); - sp_cache_clear(&sp_proc_cache); - sp_cache_clear(&sp_func_cache); + sp_caches_clear(); } @@ -1410,8 +1406,7 @@ void THD::cleanup(void) #endif /* defined(ENABLED_DEBUG_SYNC) */ my_hash_free(&user_vars); - sp_cache_clear(&sp_proc_cache); - sp_cache_clear(&sp_func_cache); + sp_caches_clear(); auto_inc_intervals_forced.empty(); auto_inc_intervals_in_cur_stmt_for_binlog.empty(); diff --git a/sql/sql_class.h b/sql/sql_class.h index 54c9d4ac870..cceefb1793c 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -2116,6 +2116,51 @@ struct wait_for_commit }; +class Sp_caches +{ +public: + sp_cache *sp_proc_cache; + sp_cache *sp_func_cache; +#if MYSQL_VERSION_ID >= 100300 +#error Remove the preprocessor condition, !!!but keep the code!!! + sp_cache *sp_package_spec_cache; + sp_cache *sp_package_body_cache; +#endif + Sp_caches() + :sp_proc_cache(NULL), + sp_func_cache(NULL) +#if MYSQL_VERSION_ID >= 100300 +#error Remove the preprocessor condition, !!!but keep the code!!! + , + sp_package_spec_cache(NULL), + sp_package_body_cache(NULL) +#endif + { } + ~Sp_caches() + { + // All caches must be freed by the caller explicitly + DBUG_ASSERT(sp_proc_cache == NULL); + DBUG_ASSERT(sp_func_cache == NULL); +#if MYSQL_VERSION_ID >= 100300 +#error Remove the preprocessor condition, !!!but keep the code!!! + DBUG_ASSERT(sp_package_spec_cache == NULL); + DBUG_ASSERT(sp_package_body_cache == NULL); +#endif + } + void sp_caches_swap(Sp_caches &rhs) + { + swap_variables(sp_cache*, sp_proc_cache, rhs.sp_proc_cache); + swap_variables(sp_cache*, sp_func_cache, rhs.sp_func_cache); +#if MYSQL_VERSION_ID >= 100300 +#error Remove the preprocessor condition, !!!but keep the code!!! + swap_variables(sp_cache*, sp_package_spec_cache, rhs.sp_package_spec_cache); + swap_variables(sp_cache*, sp_package_body_cache, rhs.sp_package_body_cache); +#endif + } + void sp_caches_clear(); +}; + + extern "C" void my_message_sql(uint error, const char *str, myf MyFlags); class THD; @@ -2139,7 +2184,8 @@ class THD :public Statement, */ public Item_change_list, public MDL_context_owner, - public Open_tables_state + public Open_tables_state, + public Sp_caches { private: inline bool is_stmt_prepare() const @@ -3089,8 +3135,6 @@ public: int slave_expected_error; sp_rcontext *spcont; // SP runtime context - sp_cache *sp_proc_cache; - sp_cache *sp_func_cache; /** number of name_const() substitutions, see sp_head.cc:subst_spvars() */ uint query_name_consts; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index c708849d049..ee295eb3834 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -4907,6 +4907,7 @@ public: int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) { + DBUG_ENTER("get_all_tables"); LEX *lex= thd->lex; TABLE *table= tables->table; TABLE_LIST table_acl_check; @@ -4923,7 +4924,28 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) #endif uint table_open_method= tables->table_open_method; bool can_deadlock; - DBUG_ENTER("get_all_tables"); + /* + We're going to open FRM files for tables. + In case of VIEWs that contain stored function calls, + these stored functions will be parsed and put to the SP cache. + + Suppose we have a view containing a stored function call: + CREATE VIEW v1 AS SELECT f1() AS c1; + and now we're running: + SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=f1(); + If a parallel thread invalidates the cache, + e.g. by creating or dropping some stored routine, + the SELECT query will re-parse f1() when processing "v1" + and replace the outdated cached version of f1() to a new one. + But the old version of f1() is referenced from the m_sp member + of the Item_func_sp instances used in the WHERE condition. + We cannot destroy it. To avoid such clashes, let's remember + all old routines into a temporary SP cache collection + and process tables with a new empty temporary SP cache collection. + Then restore to the old SP cache collection at the end. + */ + Sp_caches old_sp_caches; + old_sp_caches.sp_caches_swap(*thd); /* In cases when SELECT from I_S table being filled by this call is @@ -5092,6 +5114,13 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) err: thd->restore_backup_open_tables_state(&open_tables_state_backup); + /* + Now restore to the saved SP cache collection + and clear the temporary SP cache collection. + */ + old_sp_caches.sp_caches_swap(*thd); + old_sp_caches.sp_caches_clear(); + DBUG_RETURN(error); } From 3c209bfc040ddfc41ece8357d772547432353fd2 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Thu, 21 Apr 2022 15:03:23 +0300 Subject: [PATCH 02/28] MDEV-25994: Crash with union of my_decimal type in ORDER BY clause When single-row subquery fails with "Subquery reutrns more than 1 row" error, it will raise an error and return NULL. On the other hand, Item_singlerow_subselect sets item->maybe_null=0 for table-less subqueries like "(SELECT not_null_value)" (*) This discrepancy (item with maybe_null=0 returning NULL) causes the code in Type_handler_decimal_result::make_sort_key_part() to crash. Fixed this by allowing inference (*) only when the subquery is NOT a UNION. --- mysql-test/r/order_by.result | 20 +++++++++++++++ mysql-test/r/subselect.result | 2 +- mysql-test/r/subselect_no_exists_to_in.result | 2 +- mysql-test/r/subselect_no_mat.result | 2 +- mysql-test/r/subselect_no_opts.result | 2 +- mysql-test/r/subselect_no_scache.result | 2 +- mysql-test/r/subselect_no_semijoin.result | 2 +- mysql-test/t/order_by.test | 25 +++++++++++++++++++ sql/item_subselect.cc | 15 ++++++++--- 9 files changed, 62 insertions(+), 10 deletions(-) diff --git a/mysql-test/r/order_by.result b/mysql-test/r/order_by.result index d1f2d067643..b21e350ac4a 100644 --- a/mysql-test/r/order_by.result +++ b/mysql-test/r/order_by.result @@ -3508,4 +3508,24 @@ DELETE FROM t1 ORDER BY c; DROP TABLE t1; SET @@SESSION.max_sort_length=DEFAULT; SET sql_mode=DEFAULT; +# +# MDEV-25994 Crash with union of my_decimal type in ORDER BY clause +# +CREATE TABLE t1 (v1 INTEGER) ; +INSERT INTO t1 (v1) VALUES (8); +UPDATE t1 SET v1 = 1 ORDER BY (SELECT 1.1 UNION SELECT -1); +ERROR 21000: Subquery returns more than 1 row +# This one must be successful +UPDATE t1 SET v1 = 2 ORDER BY (SELECT 1 UNION SELECT 1); +UPDATE t1 SET v1 = 3 ORDER BY (SELECT 'a' UNION SELECT 'b'); +ERROR 21000: Subquery returns more than 1 row +# Insert some more data +INSERT INTO t1 (v1) VALUES (8),(9),(100),(-234),(46584),(0); +UPDATE t1 SET v1 = v1+1 ORDER BY (SELECT 100.122 UNION SELECT -189.2); +ERROR 21000: Subquery returns more than 1 row +# This one must be successful +UPDATE t1 SET v1 = v1-200 ORDER BY (SELECT 1 UNION SELECT 1); +UPDATE t1 SET v1 = v1 ORDER BY (SELECT 'abc' UNION SELECT 'bbb'); +ERROR 21000: Subquery returns more than 1 row +DROP TABLE t1; # End of 10.2 tests diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index a03a2cff207..ed3b49ff40d 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -1261,7 +1261,7 @@ a SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` bigint(20) NOT NULL + `a` bigint(20) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; create table t1 (a int); diff --git a/mysql-test/r/subselect_no_exists_to_in.result b/mysql-test/r/subselect_no_exists_to_in.result index f7da3fdb2e2..499cad5538a 100644 --- a/mysql-test/r/subselect_no_exists_to_in.result +++ b/mysql-test/r/subselect_no_exists_to_in.result @@ -1265,7 +1265,7 @@ a SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` bigint(20) NOT NULL + `a` bigint(20) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; create table t1 (a int); diff --git a/mysql-test/r/subselect_no_mat.result b/mysql-test/r/subselect_no_mat.result index 6ab304c190b..a6e97636eed 100644 --- a/mysql-test/r/subselect_no_mat.result +++ b/mysql-test/r/subselect_no_mat.result @@ -1268,7 +1268,7 @@ a SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` bigint(20) NOT NULL + `a` bigint(20) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; create table t1 (a int); diff --git a/mysql-test/r/subselect_no_opts.result b/mysql-test/r/subselect_no_opts.result index 338ddd5808b..571d0553212 100644 --- a/mysql-test/r/subselect_no_opts.result +++ b/mysql-test/r/subselect_no_opts.result @@ -1264,7 +1264,7 @@ a SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` bigint(20) NOT NULL + `a` bigint(20) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; create table t1 (a int); diff --git a/mysql-test/r/subselect_no_scache.result b/mysql-test/r/subselect_no_scache.result index 741b070a38b..b2b25c3efa8 100644 --- a/mysql-test/r/subselect_no_scache.result +++ b/mysql-test/r/subselect_no_scache.result @@ -1267,7 +1267,7 @@ a SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` bigint(20) NOT NULL + `a` bigint(20) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; create table t1 (a int); diff --git a/mysql-test/r/subselect_no_semijoin.result b/mysql-test/r/subselect_no_semijoin.result index ebabafb9c77..af8df6a6331 100644 --- a/mysql-test/r/subselect_no_semijoin.result +++ b/mysql-test/r/subselect_no_semijoin.result @@ -1264,7 +1264,7 @@ a SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` bigint(20) NOT NULL + `a` bigint(20) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; create table t1 (a int); diff --git a/mysql-test/t/order_by.test b/mysql-test/t/order_by.test index 7cb24b7d03b..2187786deb7 100644 --- a/mysql-test/t/order_by.test +++ b/mysql-test/t/order_by.test @@ -2331,5 +2331,30 @@ DROP TABLE t1; SET @@SESSION.max_sort_length=DEFAULT; SET sql_mode=DEFAULT; +--echo # +--echo # MDEV-25994 Crash with union of my_decimal type in ORDER BY clause +--echo # + +CREATE TABLE t1 (v1 INTEGER) ; +INSERT INTO t1 (v1) VALUES (8); +--error ER_SUBQUERY_NO_1_ROW +UPDATE t1 SET v1 = 1 ORDER BY (SELECT 1.1 UNION SELECT -1); +--echo # This one must be successful +UPDATE t1 SET v1 = 2 ORDER BY (SELECT 1 UNION SELECT 1); +--error ER_SUBQUERY_NO_1_ROW +UPDATE t1 SET v1 = 3 ORDER BY (SELECT 'a' UNION SELECT 'b'); + +-- echo # Insert some more data +INSERT INTO t1 (v1) VALUES (8),(9),(100),(-234),(46584),(0); +--error ER_SUBQUERY_NO_1_ROW +UPDATE t1 SET v1 = v1+1 ORDER BY (SELECT 100.122 UNION SELECT -189.2); +--echo # This one must be successful +UPDATE t1 SET v1 = v1-200 ORDER BY (SELECT 1 UNION SELECT 1); +--error ER_SUBQUERY_NO_1_ROW +UPDATE t1 SET v1 = v1 ORDER BY (SELECT 'abc' UNION SELECT 'bbb'); + + +DROP TABLE t1; + --echo # End of 10.2 tests diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index e01c92b7a4f..7033d38c07b 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1266,11 +1266,18 @@ bool Item_singlerow_subselect::fix_length_and_dec() } unsigned_flag= value->unsigned_flag; /* - If there are not tables in subquery then ability to have NULL value - depends on SELECT list (if single row subquery have tables then it - always can be NULL if there are not records fetched). + If the subquery has no tables (1) and is not a UNION (2), like: + + (SELECT subq_value) + + then its NULLability is the same as subq_value's NULLability. + + (1): A subquery that uses a table will return NULL when the table is empty. + (2): A UNION subquery will return NULL if it produces a "Subquery returns + more than one row" error. */ - if (engine->no_tables()) + if (engine->no_tables() && + engine->engine_type() != subselect_engine::UNION_ENGINE) maybe_null= engine->may_be_null(); else { From bc7ba7afee8ba6f7d8fe61078d4c46184dc6fa56 Mon Sep 17 00:00:00 2001 From: Dmitry Shulga Date: Fri, 22 Apr 2022 18:47:19 +0700 Subject: [PATCH 03/28] MDEV-27758: Errors when building Connect engine on os x 11.6.2 Added checking for support of vfork by a platform where building being done. Set HAVE_VFORK macros in case vfork() system call is supported. Use vfork() system call if the macros HAVE_VFORK is set, else use fork(). --- config.h.cmake | 2 ++ configure.cmake | 16 ++++++++++++++++ storage/connect/tabrest.cpp | 6 +++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/config.h.cmake b/config.h.cmake index c74592b4a65..3a43c6132f0 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -577,3 +577,5 @@ #endif // !defined(__STDC_FORMAT_MACROS) #endif + +#cmakedefine HAVE_VFORK 1 diff --git a/configure.cmake b/configure.cmake index 942d5780ed9..3ac42e8e3e4 100644 --- a/configure.cmake +++ b/configure.cmake @@ -1033,3 +1033,19 @@ IF(NOT MSVC) HAVE_FALLOC_PUNCH_HOLE_AND_KEEP_SIZE ) ENDIF() + +MY_CHECK_C_COMPILER_FLAG("-Werror") +IF(have_C__Werror) + SET(SAVE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Werror") + CHECK_C_SOURCE_COMPILES(" + #include + int main() + { + pid_t pid=vfork(); + return (int)pid; + }" + HAVE_VFORK + ) + SET(CMAKE_REQUIRED_FLAGS ${SAVE_CMAKE_REQUIRED_FLAGS}) +ENDIF() diff --git a/storage/connect/tabrest.cpp b/storage/connect/tabrest.cpp index c66d8d76f3d..7e8b51714fb 100644 --- a/storage/connect/tabrest.cpp +++ b/storage/connect/tabrest.cpp @@ -112,7 +112,11 @@ int Xcurl(PGLOBAL g, PCSZ Http, PCSZ Uri, PCSZ filename) } // endif f - pID = vfork(); +#ifdef HAVE_VFORK + pID = vfork(); +#else + pID = fork(); +#endif sprintf(fn, "-o%s", filename); if (pID == 0) { From a7923b37c4554fe6634a583586c1e444bee6118b Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Fri, 22 Apr 2022 13:56:08 +1000 Subject: [PATCH 04/28] MDEV-28263 mariadb-tzinfo-to-sql binlog fixes The --skip-write-binlog message was confusing that it only had an effect if the galera was enabled. There are uses beyond galera so we apply SET SESSION SQL_LOG_BIN=0 as implied by the option without being conditional on the wsrep status. Remove wsrep.mysql_tzinfo_to_sql_symlink{,_skip} tests as they offered no additional coverage beyond main.mysql_tzinfo_to_sql_symlink as no server testing was done. Introduced a variant of the galera.mariadb_tzinfo_to_sql as galera.mysql_tzinfo_to_sql, which does testing using the mysql client rather than directly importing into the server via mysqltest. Update man page and mysql_tzinfo_to_sql to having a --skip-write-binlog option. merge notes: 10.4: - conflicts in tztime.cc can revert to this version of --help text. - tztime.cc - merge execute immediate @prep1, and leave %s%s trunc_tables, lock_tables after that. 10.6: - Need to remove the not_embedded.inc in mysql_tzinfo_to_sql.test and replace it with no_protocol.inc - leave both mysql_tzinfo_to_sql.test and mariadb_tzinfo_to_sql.sql tests. - sql/tztime.cc - keep entirely 10.6 version. --- man/mysql_tzinfo_to_sql.1 | 4 + .../r/mysql_tzinfo_to_sql_symlink.result | 23 +++ .../suite/galera/r/mysql_tzinfo_to_sql.result | 152 +++++++++++++++++ .../suite/galera/t/mysql_tzinfo_to_sql.opt | 1 + .../suite/galera/t/mysql_tzinfo_to_sql.test | 156 ++++++++++++++++++ .../r/mysql_tzinfo_to_sql_symlink.result | 150 ----------------- .../r/mysql_tzinfo_to_sql_symlink_skip.result | 78 --------- .../wsrep/t/mysql_tzinfo_to_sql_symlink.opt | 3 - .../wsrep/t/mysql_tzinfo_to_sql_symlink.test | 41 ----- .../t/mysql_tzinfo_to_sql_symlink_skip.opt | 3 - .../t/mysql_tzinfo_to_sql_symlink_skip.test | 41 ----- mysql-test/t/mysql_tzinfo_to_sql_symlink.test | 17 ++ sql/tztime.cc | 8 +- 13 files changed, 357 insertions(+), 320 deletions(-) create mode 100644 mysql-test/suite/galera/r/mysql_tzinfo_to_sql.result create mode 100644 mysql-test/suite/galera/t/mysql_tzinfo_to_sql.opt create mode 100644 mysql-test/suite/galera/t/mysql_tzinfo_to_sql.test delete mode 100644 mysql-test/suite/wsrep/r/mysql_tzinfo_to_sql_symlink.result delete mode 100644 mysql-test/suite/wsrep/r/mysql_tzinfo_to_sql_symlink_skip.result delete mode 100644 mysql-test/suite/wsrep/t/mysql_tzinfo_to_sql_symlink.opt delete mode 100644 mysql-test/suite/wsrep/t/mysql_tzinfo_to_sql_symlink.test delete mode 100644 mysql-test/suite/wsrep/t/mysql_tzinfo_to_sql_symlink_skip.opt delete mode 100644 mysql-test/suite/wsrep/t/mysql_tzinfo_to_sql_symlink_skip.test diff --git a/man/mysql_tzinfo_to_sql.1 b/man/mysql_tzinfo_to_sql.1 index 9ab70fbc2e4..1c7de159e75 100644 --- a/man/mysql_tzinfo_to_sql.1 +++ b/man/mysql_tzinfo_to_sql.1 @@ -41,6 +41,7 @@ can be invoked several ways: shell> \fBmysql_tzinfo_to_sql \fR\fB\fItz_dir\fR\fR shell> \fBmysql_tzinfo_to_sql \fR\fB\fItz_file tz_name\fR\fR shell> \fBmysql_tzinfo_to_sql \-\-leap \fR\fB\fItz_file\fR\fR +shell> \fBmysql_tzinfo_to_sql \-\-skip\-write\-binlog \fR\fB\fItz_dir\fR\fR .fi .if n \{\ .RE @@ -100,6 +101,9 @@ shell> \fBmysql_tzinfo_to_sql \-\-leap \fR\fB\fItz_file\fR\fR\fB | mysql \-u roo .RE .\} .PP +Using the \-\-skip\-write\-binlog option prevents writing of changes to the binary log or to other Galera +cluster members. This can be used with any form of running \fBmysql_tzinfo_to_sql\fR. +.PP After running \fBmysql_tzinfo_to_sql\fR, it is best to restart the server so that it does not continue to use any previously cached time zone data\&. .SH "COPYRIGHT" diff --git a/mysql-test/r/mysql_tzinfo_to_sql_symlink.result b/mysql-test/r/mysql_tzinfo_to_sql_symlink.result index f56a2df2012..5753a719769 100644 --- a/mysql-test/r/mysql_tzinfo_to_sql_symlink.result +++ b/mysql-test/r/mysql_tzinfo_to_sql_symlink.result @@ -149,6 +149,29 @@ ALTER TABLE time_zone_transition_type ENGINE=MyISAM; END IF| \d ; # +# MDEV-28263: mariadb-tzinfo-to-sql improve wsrep and binlog cases +# +# +# Testing --skip-write-binlog +# +set @prep1=if((select count(*) from information_schema.global_variables where variable_name='wsrep_on' and variable_value='ON'), 'SET SESSION WSREP_ON=OFF', 'do 0'); +SET SESSION SQL_LOG_BIN=0; +execute immediate @prep1; +INSERT INTO time_zone (Use_leap_seconds) VALUES ('N'); +SET @time_zone_id= LAST_INSERT_ID(); +INSERT INTO time_zone_name (Name, Time_zone_id) VALUES ('XXX', @time_zone_id); +INSERT INTO time_zone_transition_type (Time_zone_id, Transition_type_id, Offset, Is_DST, Abbreviation) VALUES + (@time_zone_id, 0, 0, 0, 'GMT') +; +set @prep1=if((select count(*) from information_schema.global_variables where variable_name='wsrep_on' and variable_value='ON'), 'SET SESSION WSREP_ON=OFF', 'do 0'); +SET SESSION SQL_LOG_BIN=0; +execute immediate @prep1; +TRUNCATE TABLE time_zone_leap_second; +ALTER TABLE time_zone_leap_second ORDER BY Transition_time; +# +# End of 10.2 tests +# +# # MDEV-6236 - [PATCH] mysql_tzinfo_to_sql may produce invalid SQL # \d | diff --git a/mysql-test/suite/galera/r/mysql_tzinfo_to_sql.result b/mysql-test/suite/galera/r/mysql_tzinfo_to_sql.result new file mode 100644 index 00000000000..a140f517ac8 --- /dev/null +++ b/mysql-test/suite/galera/r/mysql_tzinfo_to_sql.result @@ -0,0 +1,152 @@ +# +# MDEV-28263: mariadb-tzinfo-to-sql improve wsrep and binlog cases +# + +# On node_1 +connection node_1; +CREATE TABLE time_zone LIKE mysql.time_zone; +CREATE TABLE time_zone_name LIKE mysql.time_zone_name; +CREATE TABLE time_zone_transition LIKE mysql.time_zone_transition; +CREATE TABLE time_zone_transition_type LIKE mysql.time_zone_transition_type; +CREATE TABLE time_zone_leap_second LIKE mysql.time_zone_leap_second; +# +# Run on zoneinfo directory --skip-write-binlog +# + +# Apply on node_1 + +load timezones +'binlog stationary as expected' +SELECT COUNT(*) FROM time_zone; +COUNT(*) +2 +SELECT COUNT(*) FROM time_zone_name; +COUNT(*) +2 +SELECT COUNT(*) FROM time_zone_transition; +COUNT(*) +0 +SELECT COUNT(*) FROM time_zone_transition_type; +COUNT(*) +2 +SELECT COUNT(*) FROM time_zone_leap_second; +COUNT(*) +0 + +# On node_2 (not replicated) + +connection node_2; +SELECT COUNT(*) FROM time_zone; +COUNT(*) +0 +SELECT COUNT(*) FROM time_zone_name; +COUNT(*) +0 +SELECT COUNT(*) FROM time_zone_transition; +COUNT(*) +0 +SELECT COUNT(*) FROM time_zone_transition_type; +COUNT(*) +0 +SELECT COUNT(*) FROM time_zone_leap_second; +COUNT(*) +0 +# +# Run on zoneinfo directory without --skip-write-binlog +# + +# Apply on node_1 + +connection node_1; +load timezones +'binlog advanced as expected' +SELECT COUNT(*) FROM time_zone; +COUNT(*) +2 +SELECT COUNT(*) FROM time_zone_name; +COUNT(*) +2 +SELECT COUNT(*) FROM time_zone_transition; +COUNT(*) +0 +SELECT COUNT(*) FROM time_zone_transition_type; +COUNT(*) +2 +SELECT COUNT(*) FROM time_zone_leap_second; +COUNT(*) +0 + +# On node_2 (replicated via InnoDB) + +connection node_2; +SELECT COUNT(*) FROM time_zone; +COUNT(*) +2 +SELECT COUNT(*) FROM time_zone_name; +COUNT(*) +2 +SELECT COUNT(*) FROM time_zone_transition; +COUNT(*) +0 +SELECT COUNT(*) FROM time_zone_transition_type; +COUNT(*) +2 +SELECT COUNT(*) FROM time_zone_leap_second; +COUNT(*) +0 +TRUNCATE TABLE time_zone; +TRUNCATE TABLE time_zone_name; +TRUNCATE TABLE time_zone_transition; +TRUNCATE TABLE time_zone_transition_type; +TRUNCATE TABLE time_zone_leap_second; + +# Apply on node_1 (with wsrep_on=OFF) + +connection node_1; +SET GLOBAL WSREP_ON=OFF; +load timezones +SET GLOBAL WSREP_ON=ON; +'binlog advanced as expected' +SELECT COUNT(*) FROM time_zone; +COUNT(*) +2 +SELECT COUNT(*) FROM time_zone_name; +COUNT(*) +2 +SELECT COUNT(*) FROM time_zone_transition; +COUNT(*) +0 +SELECT COUNT(*) FROM time_zone_transition_type; +COUNT(*) +2 +SELECT COUNT(*) FROM time_zone_leap_second; +COUNT(*) +0 + +# On node_2 (Should not have been replicated) + +connection node_2; +SELECT COUNT(*) FROM time_zone; +COUNT(*) +0 +SELECT COUNT(*) FROM time_zone_name; +COUNT(*) +0 +SELECT COUNT(*) FROM time_zone_transition; +COUNT(*) +0 +SELECT COUNT(*) FROM time_zone_transition_type; +COUNT(*) +0 +SELECT COUNT(*) FROM time_zone_leap_second; +COUNT(*) +0 +connection node_1; +DROP TABLE time_zone; +DROP TABLE time_zone_name; +DROP TABLE time_zone_transition; +DROP TABLE time_zone_transition_type; +DROP TABLE time_zone_leap_second; +# +# End of 10.2 tests +# diff --git a/mysql-test/suite/galera/t/mysql_tzinfo_to_sql.opt b/mysql-test/suite/galera/t/mysql_tzinfo_to_sql.opt new file mode 100644 index 00000000000..beae84b3862 --- /dev/null +++ b/mysql-test/suite/galera/t/mysql_tzinfo_to_sql.opt @@ -0,0 +1 @@ +--log-bin diff --git a/mysql-test/suite/galera/t/mysql_tzinfo_to_sql.test b/mysql-test/suite/galera/t/mysql_tzinfo_to_sql.test new file mode 100644 index 00000000000..6bfad2f18b5 --- /dev/null +++ b/mysql-test/suite/galera/t/mysql_tzinfo_to_sql.test @@ -0,0 +1,156 @@ +--source include/galera_cluster.inc +--source include/have_innodb.inc +--source include/not_embedded.inc +# merge note: 10.6 change not_embedded.inc to no_protocol.inc + +# Unlike the similar galera.mariadb_tzinfo_to_sql.test in 10.6+, this +# tests that the output can be parsed by the mysql client. +--echo # +--echo # MDEV-28263: mariadb-tzinfo-to-sql improve wsrep and binlog cases +--echo # + +--exec mkdir $MYSQLTEST_VARDIR/zoneinfo +--exec ln -s $MYSQLTEST_VARDIR/zoneinfo $MYSQLTEST_VARDIR/zoneinfo/posix +--copy_file std_data/zoneinfo/GMT $MYSQLTEST_VARDIR/zoneinfo/GMT + +--echo +--echo # On node_1 +--connection node_1 + +CREATE TABLE time_zone LIKE mysql.time_zone; +CREATE TABLE time_zone_name LIKE mysql.time_zone_name; +CREATE TABLE time_zone_transition LIKE mysql.time_zone_transition; +CREATE TABLE time_zone_transition_type LIKE mysql.time_zone_transition_type; +CREATE TABLE time_zone_leap_second LIKE mysql.time_zone_leap_second; + +--echo # +--echo # Run on zoneinfo directory --skip-write-binlog +--echo # + +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--exec $MYSQL_TZINFO_TO_SQL --skip-write-binlog $MYSQLTEST_VARDIR/zoneinfo 2>/dev/null > $MYSQL_TMP_DIR/tz.sql + +--echo +--echo # Apply on node_1 +--echo +--let $snap_pos= query_get_value(SHOW STATUS LIKE 'binlog_snapshot_position', Value, 1) +--echo load timezones +--exec $MYSQL test < "$MYSQL_TMP_DIR/tz.sql" +--let $new_snap_pos= query_get_value(SHOW STATUS LIKE 'binlog_snapshot_position', Value, 1) + +if ($snap_pos == $new_snap_pos) +{ +--echo 'binlog stationary as expected' +} + +SELECT COUNT(*) FROM time_zone; +SELECT COUNT(*) FROM time_zone_name; +SELECT COUNT(*) FROM time_zone_transition; +SELECT COUNT(*) FROM time_zone_transition_type; +SELECT COUNT(*) FROM time_zone_leap_second; + +--echo +--echo # On node_2 (not replicated) +--echo +--connection node_2 + +SELECT COUNT(*) FROM time_zone; +SELECT COUNT(*) FROM time_zone_name; +SELECT COUNT(*) FROM time_zone_transition; +SELECT COUNT(*) FROM time_zone_transition_type; +SELECT COUNT(*) FROM time_zone_leap_second; + +--echo # +--echo # Run on zoneinfo directory without --skip-write-binlog +--echo # + +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--exec $MYSQL_TZINFO_TO_SQL $MYSQLTEST_VARDIR/zoneinfo 2>/dev/null > $MYSQL_TMP_DIR/tz.sql + +--echo +--echo # Apply on node_1 +--echo +--connection node_1 + +--echo load timezones +--exec $MYSQL test < "$MYSQL_TMP_DIR/tz.sql" +--let $new_snap_pos= query_get_value(SHOW STATUS LIKE 'binlog_snapshot_position', Value, 1) + +if ($snap_pos < $new_snap_pos) +{ +--echo 'binlog advanced as expected' +} + +SELECT COUNT(*) FROM time_zone; +SELECT COUNT(*) FROM time_zone_name; +SELECT COUNT(*) FROM time_zone_transition; +SELECT COUNT(*) FROM time_zone_transition_type; +SELECT COUNT(*) FROM time_zone_leap_second; + +--echo +--echo # On node_2 (replicated via InnoDB) +--echo +--connection node_2 + +SELECT COUNT(*) FROM time_zone; +SELECT COUNT(*) FROM time_zone_name; +SELECT COUNT(*) FROM time_zone_transition; +SELECT COUNT(*) FROM time_zone_transition_type; +SELECT COUNT(*) FROM time_zone_leap_second; + +TRUNCATE TABLE time_zone; +TRUNCATE TABLE time_zone_name; +TRUNCATE TABLE time_zone_transition; +TRUNCATE TABLE time_zone_transition_type; +TRUNCATE TABLE time_zone_leap_second; + +--echo +--echo # Apply on node_1 (with wsrep_on=OFF) +--echo +--connection node_1 + +SET GLOBAL WSREP_ON=OFF; +--let $snap_pos= $new_snap_pos +--echo load timezones +--exec $MYSQL test < "$MYSQL_TMP_DIR/tz.sql" +--let $new_snap_pos= query_get_value(SHOW STATUS LIKE 'binlog_snapshot_position', Value, 1) +SET GLOBAL WSREP_ON=ON; + +if ($snap_pos < $new_snap_pos) +{ +--echo 'binlog advanced as expected' +} + +SELECT COUNT(*) FROM time_zone; +SELECT COUNT(*) FROM time_zone_name; +SELECT COUNT(*) FROM time_zone_transition; +SELECT COUNT(*) FROM time_zone_transition_type; +SELECT COUNT(*) FROM time_zone_leap_second; + +--echo +--echo # On node_2 (Should not have been replicated) +--echo +--connection node_2 + +SELECT COUNT(*) FROM time_zone; +SELECT COUNT(*) FROM time_zone_name; +SELECT COUNT(*) FROM time_zone_transition; +SELECT COUNT(*) FROM time_zone_transition_type; +SELECT COUNT(*) FROM time_zone_leap_second; + +# +# Cleanup +# + +--connection node_1 +--remove_file $MYSQL_TMP_DIR/tz.sql +--exec rm -rf $MYSQLTEST_VARDIR/zoneinfo +DROP TABLE time_zone; +DROP TABLE time_zone_name; +DROP TABLE time_zone_transition; +DROP TABLE time_zone_transition_type; +DROP TABLE time_zone_leap_second; + +--echo # +--echo # End of 10.2 tests +--echo # diff --git a/mysql-test/suite/wsrep/r/mysql_tzinfo_to_sql_symlink.result b/mysql-test/suite/wsrep/r/mysql_tzinfo_to_sql_symlink.result deleted file mode 100644 index bcd96b86516..00000000000 --- a/mysql-test/suite/wsrep/r/mysql_tzinfo_to_sql_symlink.result +++ /dev/null @@ -1,150 +0,0 @@ -# -# MDEV-5226 mysql_tzinfo_to_sql errors with tzdata 2013f and above -# -# Verbose run -\d | -IF (select count(*) from information_schema.global_variables where -variable_name='wsrep_on' and variable_value='ON') = 1 THEN -ALTER TABLE time_zone ENGINE=InnoDB; -ALTER TABLE time_zone_name ENGINE=InnoDB; -ALTER TABLE time_zone_transition ENGINE=InnoDB; -ALTER TABLE time_zone_transition_type ENGINE=InnoDB; -END IF| -\d ; -TRUNCATE TABLE time_zone; -TRUNCATE TABLE time_zone_name; -TRUNCATE TABLE time_zone_transition; -TRUNCATE TABLE time_zone_transition_type; -START TRANSACTION; -INSERT INTO time_zone (Use_leap_seconds) VALUES ('N'); -SET @time_zone_id= LAST_INSERT_ID(); -INSERT INTO time_zone_name (Name, Time_zone_id) VALUES ('GMT', @time_zone_id); -INSERT INTO time_zone_transition_type (Time_zone_id, Transition_type_id, Offset, Is_DST, Abbreviation) VALUES - (@time_zone_id, 0, 0, 0, 'GMT') -; -Warning: Unable to load 'MYSQLTEST_VARDIR/zoneinfo/garbage' as time zone. Skipping it. -Warning: Unable to load 'MYSQLTEST_VARDIR/zoneinfo/ignored.tab' as time zone. Skipping it. -INSERT INTO time_zone (Use_leap_seconds) VALUES ('N'); -SET @time_zone_id= LAST_INSERT_ID(); -INSERT INTO time_zone_name (Name, Time_zone_id) VALUES ('posix/GMT', @time_zone_id); -INSERT INTO time_zone_transition_type (Time_zone_id, Transition_type_id, Offset, Is_DST, Abbreviation) VALUES - (@time_zone_id, 0, 0, 0, 'GMT') -; -Warning: Unable to load 'MYSQLTEST_VARDIR/zoneinfo/posix/garbage' as time zone. Skipping it. -Warning: Unable to load 'MYSQLTEST_VARDIR/zoneinfo/posix/ignored.tab' as time zone. Skipping it. -Warning: Skipping directory 'MYSQLTEST_VARDIR/zoneinfo/posix/posix': to avoid infinite symlink recursion. -COMMIT; -ALTER TABLE time_zone_transition ORDER BY Time_zone_id, Transition_time; -ALTER TABLE time_zone_transition_type ORDER BY Time_zone_id, Transition_type_id; -\d | -IF (select count(*) from information_schema.global_variables where -variable_name='wsrep_on' and variable_value='ON') = 1 THEN -ALTER TABLE time_zone ENGINE=MyISAM; -ALTER TABLE time_zone_name ENGINE=MyISAM; -ALTER TABLE time_zone_transition ENGINE=MyISAM; -ALTER TABLE time_zone_transition_type ENGINE=MyISAM; -END IF| -\d ; -# Silent run -\d | -IF (select count(*) from information_schema.global_variables where -variable_name='wsrep_on' and variable_value='ON') = 1 THEN -ALTER TABLE time_zone ENGINE=InnoDB; -ALTER TABLE time_zone_name ENGINE=InnoDB; -ALTER TABLE time_zone_transition ENGINE=InnoDB; -ALTER TABLE time_zone_transition_type ENGINE=InnoDB; -END IF| -\d ; -TRUNCATE TABLE time_zone; -TRUNCATE TABLE time_zone_name; -TRUNCATE TABLE time_zone_transition; -TRUNCATE TABLE time_zone_transition_type; -START TRANSACTION; -INSERT INTO time_zone (Use_leap_seconds) VALUES ('N'); -SET @time_zone_id= LAST_INSERT_ID(); -INSERT INTO time_zone_name (Name, Time_zone_id) VALUES ('GMT', @time_zone_id); -INSERT INTO time_zone_transition_type (Time_zone_id, Transition_type_id, Offset, Is_DST, Abbreviation) VALUES - (@time_zone_id, 0, 0, 0, 'GMT') -; -Warning: Unable to load 'MYSQLTEST_VARDIR/zoneinfo/garbage' as time zone. Skipping it. -INSERT INTO time_zone (Use_leap_seconds) VALUES ('N'); -SET @time_zone_id= LAST_INSERT_ID(); -INSERT INTO time_zone_name (Name, Time_zone_id) VALUES ('posix/GMT', @time_zone_id); -INSERT INTO time_zone_transition_type (Time_zone_id, Transition_type_id, Offset, Is_DST, Abbreviation) VALUES - (@time_zone_id, 0, 0, 0, 'GMT') -; -Warning: Unable to load 'MYSQLTEST_VARDIR/zoneinfo/posix/garbage' as time zone. Skipping it. -COMMIT; -ALTER TABLE time_zone_transition ORDER BY Time_zone_id, Transition_time; -ALTER TABLE time_zone_transition_type ORDER BY Time_zone_id, Transition_type_id; -\d | -IF (select count(*) from information_schema.global_variables where -variable_name='wsrep_on' and variable_value='ON') = 1 THEN -ALTER TABLE time_zone ENGINE=MyISAM; -ALTER TABLE time_zone_name ENGINE=MyISAM; -ALTER TABLE time_zone_transition ENGINE=MyISAM; -ALTER TABLE time_zone_transition_type ENGINE=MyISAM; -END IF| -\d ; -# -# Testing with explicit timezonefile -# -\d | -IF (select count(*) from information_schema.global_variables where -variable_name='wsrep_on' and variable_value='ON') = 1 THEN -ALTER TABLE time_zone ENGINE=InnoDB; -ALTER TABLE time_zone_name ENGINE=InnoDB; -ALTER TABLE time_zone_transition ENGINE=InnoDB; -ALTER TABLE time_zone_transition_type ENGINE=InnoDB; -END IF| -\d ; -INSERT INTO time_zone (Use_leap_seconds) VALUES ('N'); -SET @time_zone_id= LAST_INSERT_ID(); -INSERT INTO time_zone_name (Name, Time_zone_id) VALUES ('XXX', @time_zone_id); -INSERT INTO time_zone_transition_type (Time_zone_id, Transition_type_id, Offset, Is_DST, Abbreviation) VALUES - (@time_zone_id, 0, 0, 0, 'GMT') -; -\d | -IF (select count(*) from information_schema.global_variables where -variable_name='wsrep_on' and variable_value='ON') = 1 THEN -ALTER TABLE time_zone ENGINE=MyISAM; -ALTER TABLE time_zone_name ENGINE=MyISAM; -ALTER TABLE time_zone_transition ENGINE=MyISAM; -ALTER TABLE time_zone_transition_type ENGINE=MyISAM; -END IF| -\d ; -# -# Testing --leap -# -\d | -IF (select count(*) from information_schema.global_variables where -variable_name='wsrep_on' and variable_value='ON') = 1 THEN -ALTER TABLE time_zone ENGINE=InnoDB; -ALTER TABLE time_zone_name ENGINE=InnoDB; -ALTER TABLE time_zone_transition ENGINE=InnoDB; -ALTER TABLE time_zone_transition_type ENGINE=InnoDB; -END IF| -\d ; -\d | -IF (select count(*) from information_schema.global_variables where -variable_name='wsrep_on' and variable_value='ON') = 1 THEN -ALTER TABLE time_zone_leap_second ENGINE=InnoDB; -END IF| -\d ; -TRUNCATE TABLE time_zone_leap_second; -\d | -IF (select count(*) from information_schema.global_variables where -variable_name='wsrep_on' and variable_value='ON') = 1 THEN -ALTER TABLE time_zone_leap_second ENGINE=MyISAM; -END IF| -\d ; -ALTER TABLE time_zone_leap_second ORDER BY Transition_time; -\d | -IF (select count(*) from information_schema.global_variables where -variable_name='wsrep_on' and variable_value='ON') = 1 THEN -ALTER TABLE time_zone ENGINE=MyISAM; -ALTER TABLE time_zone_name ENGINE=MyISAM; -ALTER TABLE time_zone_transition ENGINE=MyISAM; -ALTER TABLE time_zone_transition_type ENGINE=MyISAM; -END IF| -\d ; diff --git a/mysql-test/suite/wsrep/r/mysql_tzinfo_to_sql_symlink_skip.result b/mysql-test/suite/wsrep/r/mysql_tzinfo_to_sql_symlink_skip.result deleted file mode 100644 index aff02cb413e..00000000000 --- a/mysql-test/suite/wsrep/r/mysql_tzinfo_to_sql_symlink_skip.result +++ /dev/null @@ -1,78 +0,0 @@ -# -# MDEV-5226 mysql_tzinfo_to_sql errors with tzdata 2013f and above -# -# Verbose run -set @prep1=if((select count(*) from information_schema.global_variables where variable_name='wsrep_on' and variable_value='ON'), 'SET SESSION SQL_LOG_BIN=?, WSREP_ON=OFF;', 'do ?'); -prepare set_wsrep_write_binlog from @prep1; -set @toggle=0; execute set_wsrep_write_binlog using @toggle; -TRUNCATE TABLE time_zone; -TRUNCATE TABLE time_zone_name; -TRUNCATE TABLE time_zone_transition; -TRUNCATE TABLE time_zone_transition_type; -START TRANSACTION; -INSERT INTO time_zone (Use_leap_seconds) VALUES ('N'); -SET @time_zone_id= LAST_INSERT_ID(); -INSERT INTO time_zone_name (Name, Time_zone_id) VALUES ('GMT', @time_zone_id); -INSERT INTO time_zone_transition_type (Time_zone_id, Transition_type_id, Offset, Is_DST, Abbreviation) VALUES - (@time_zone_id, 0, 0, 0, 'GMT') -; -Warning: Unable to load 'MYSQLTEST_VARDIR/zoneinfo/garbage' as time zone. Skipping it. -Warning: Unable to load 'MYSQLTEST_VARDIR/zoneinfo/ignored.tab' as time zone. Skipping it. -INSERT INTO time_zone (Use_leap_seconds) VALUES ('N'); -SET @time_zone_id= LAST_INSERT_ID(); -INSERT INTO time_zone_name (Name, Time_zone_id) VALUES ('posix/GMT', @time_zone_id); -INSERT INTO time_zone_transition_type (Time_zone_id, Transition_type_id, Offset, Is_DST, Abbreviation) VALUES - (@time_zone_id, 0, 0, 0, 'GMT') -; -Warning: Unable to load 'MYSQLTEST_VARDIR/zoneinfo/posix/garbage' as time zone. Skipping it. -Warning: Unable to load 'MYSQLTEST_VARDIR/zoneinfo/posix/ignored.tab' as time zone. Skipping it. -Warning: Skipping directory 'MYSQLTEST_VARDIR/zoneinfo/posix/posix': to avoid infinite symlink recursion. -COMMIT; -ALTER TABLE time_zone_transition ORDER BY Time_zone_id, Transition_time; -ALTER TABLE time_zone_transition_type ORDER BY Time_zone_id, Transition_type_id; -# Silent run -set @prep1=if((select count(*) from information_schema.global_variables where variable_name='wsrep_on' and variable_value='ON'), 'SET SESSION SQL_LOG_BIN=?, WSREP_ON=OFF;', 'do ?'); -prepare set_wsrep_write_binlog from @prep1; -set @toggle=0; execute set_wsrep_write_binlog using @toggle; -TRUNCATE TABLE time_zone; -TRUNCATE TABLE time_zone_name; -TRUNCATE TABLE time_zone_transition; -TRUNCATE TABLE time_zone_transition_type; -START TRANSACTION; -INSERT INTO time_zone (Use_leap_seconds) VALUES ('N'); -SET @time_zone_id= LAST_INSERT_ID(); -INSERT INTO time_zone_name (Name, Time_zone_id) VALUES ('GMT', @time_zone_id); -INSERT INTO time_zone_transition_type (Time_zone_id, Transition_type_id, Offset, Is_DST, Abbreviation) VALUES - (@time_zone_id, 0, 0, 0, 'GMT') -; -Warning: Unable to load 'MYSQLTEST_VARDIR/zoneinfo/garbage' as time zone. Skipping it. -INSERT INTO time_zone (Use_leap_seconds) VALUES ('N'); -SET @time_zone_id= LAST_INSERT_ID(); -INSERT INTO time_zone_name (Name, Time_zone_id) VALUES ('posix/GMT', @time_zone_id); -INSERT INTO time_zone_transition_type (Time_zone_id, Transition_type_id, Offset, Is_DST, Abbreviation) VALUES - (@time_zone_id, 0, 0, 0, 'GMT') -; -Warning: Unable to load 'MYSQLTEST_VARDIR/zoneinfo/posix/garbage' as time zone. Skipping it. -COMMIT; -ALTER TABLE time_zone_transition ORDER BY Time_zone_id, Transition_time; -ALTER TABLE time_zone_transition_type ORDER BY Time_zone_id, Transition_type_id; -# -# Testing with explicit timezonefile -# -set @prep1=if((select count(*) from information_schema.global_variables where variable_name='wsrep_on' and variable_value='ON'), 'SET SESSION SQL_LOG_BIN=?, WSREP_ON=OFF;', 'do ?'); -prepare set_wsrep_write_binlog from @prep1; -set @toggle=0; execute set_wsrep_write_binlog using @toggle; -INSERT INTO time_zone (Use_leap_seconds) VALUES ('N'); -SET @time_zone_id= LAST_INSERT_ID(); -INSERT INTO time_zone_name (Name, Time_zone_id) VALUES ('XXX', @time_zone_id); -INSERT INTO time_zone_transition_type (Time_zone_id, Transition_type_id, Offset, Is_DST, Abbreviation) VALUES - (@time_zone_id, 0, 0, 0, 'GMT') -; -# -# Testing --leap -# -set @prep1=if((select count(*) from information_schema.global_variables where variable_name='wsrep_on' and variable_value='ON'), 'SET SESSION SQL_LOG_BIN=?, WSREP_ON=OFF;', 'do ?'); -prepare set_wsrep_write_binlog from @prep1; -set @toggle=0; execute set_wsrep_write_binlog using @toggle; -TRUNCATE TABLE time_zone_leap_second; -ALTER TABLE time_zone_leap_second ORDER BY Transition_time; diff --git a/mysql-test/suite/wsrep/t/mysql_tzinfo_to_sql_symlink.opt b/mysql-test/suite/wsrep/t/mysql_tzinfo_to_sql_symlink.opt deleted file mode 100644 index 864f7342cc7..00000000000 --- a/mysql-test/suite/wsrep/t/mysql_tzinfo_to_sql_symlink.opt +++ /dev/null @@ -1,3 +0,0 @@ ---wsrep-provider=$WSREP_PROVIDER --wsrep-cluster-address=gcomm:// --wsrep-on=1 --binlog_format=ROW - - diff --git a/mysql-test/suite/wsrep/t/mysql_tzinfo_to_sql_symlink.test b/mysql-test/suite/wsrep/t/mysql_tzinfo_to_sql_symlink.test deleted file mode 100644 index 87554635666..00000000000 --- a/mysql-test/suite/wsrep/t/mysql_tzinfo_to_sql_symlink.test +++ /dev/null @@ -1,41 +0,0 @@ ---source include/have_wsrep.inc ---source include/have_symlink.inc ---source include/not_windows.inc ---source include/have_innodb.inc - ---echo # ---echo # MDEV-5226 mysql_tzinfo_to_sql errors with tzdata 2013f and above ---echo # - ---exec mkdir $MYSQLTEST_VARDIR/zoneinfo ---exec ln -s $MYSQLTEST_VARDIR/zoneinfo $MYSQLTEST_VARDIR/zoneinfo/posix ---copy_file std_data/zoneinfo/GMT $MYSQLTEST_VARDIR/zoneinfo/GMT ---copy_file std_data/words.dat $MYSQLTEST_VARDIR/zoneinfo/garbage ---copy_file std_data/words.dat $MYSQLTEST_VARDIR/zoneinfo/ignored.tab - ---echo # Verbose run ---replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR ---exec $MYSQL_TZINFO_TO_SQL --verbose $MYSQLTEST_VARDIR/zoneinfo 2>&1 - ---echo # Silent run ---replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR ---exec $MYSQL_TZINFO_TO_SQL $MYSQLTEST_VARDIR/zoneinfo 2>&1 - ---echo # ---echo # Testing with explicit timezonefile ---echo # - ---replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR ---exec $MYSQL_TZINFO_TO_SQL $MYSQLTEST_VARDIR/zoneinfo/GMT XXX 2>&1 - ---echo # ---echo # Testing --leap ---echo # - ---exec $MYSQL_TZINFO_TO_SQL --leap $MYSQLTEST_VARDIR/zoneinfo/GMT 2>&1 - -# -# Cleanup -# - ---exec rm -rf $MYSQLTEST_VARDIR/zoneinfo diff --git a/mysql-test/suite/wsrep/t/mysql_tzinfo_to_sql_symlink_skip.opt b/mysql-test/suite/wsrep/t/mysql_tzinfo_to_sql_symlink_skip.opt deleted file mode 100644 index 864f7342cc7..00000000000 --- a/mysql-test/suite/wsrep/t/mysql_tzinfo_to_sql_symlink_skip.opt +++ /dev/null @@ -1,3 +0,0 @@ ---wsrep-provider=$WSREP_PROVIDER --wsrep-cluster-address=gcomm:// --wsrep-on=1 --binlog_format=ROW - - diff --git a/mysql-test/suite/wsrep/t/mysql_tzinfo_to_sql_symlink_skip.test b/mysql-test/suite/wsrep/t/mysql_tzinfo_to_sql_symlink_skip.test deleted file mode 100644 index ab1f94cc1cf..00000000000 --- a/mysql-test/suite/wsrep/t/mysql_tzinfo_to_sql_symlink_skip.test +++ /dev/null @@ -1,41 +0,0 @@ ---source include/have_wsrep.inc ---source include/have_symlink.inc ---source include/not_windows.inc ---source include/have_innodb.inc - ---echo # ---echo # MDEV-5226 mysql_tzinfo_to_sql errors with tzdata 2013f and above ---echo # - ---exec mkdir $MYSQLTEST_VARDIR/zoneinfo ---exec ln -s $MYSQLTEST_VARDIR/zoneinfo $MYSQLTEST_VARDIR/zoneinfo/posix ---copy_file std_data/zoneinfo/GMT $MYSQLTEST_VARDIR/zoneinfo/GMT ---copy_file std_data/words.dat $MYSQLTEST_VARDIR/zoneinfo/garbage ---copy_file std_data/words.dat $MYSQLTEST_VARDIR/zoneinfo/ignored.tab - ---echo # Verbose run ---replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR ---exec $MYSQL_TZINFO_TO_SQL --verbose --skip-write-binlog $MYSQLTEST_VARDIR/zoneinfo 2>&1 - ---echo # Silent run ---replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR ---exec $MYSQL_TZINFO_TO_SQL --skip-write-binlog $MYSQLTEST_VARDIR/zoneinfo 2>&1 - ---echo # ---echo # Testing with explicit timezonefile ---echo # - ---replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR ---exec $MYSQL_TZINFO_TO_SQL --skip-write-binlog $MYSQLTEST_VARDIR/zoneinfo/GMT XXX 2>&1 - ---echo # ---echo # Testing --leap ---echo # - ---exec $MYSQL_TZINFO_TO_SQL --leap --skip-write-binlog $MYSQLTEST_VARDIR/zoneinfo/GMT 2>&1 - -# -# Cleanup -# - ---exec rm -rf $MYSQLTEST_VARDIR/zoneinfo diff --git a/mysql-test/t/mysql_tzinfo_to_sql_symlink.test b/mysql-test/t/mysql_tzinfo_to_sql_symlink.test index 8ca82b87e30..fcb5916c4a2 100644 --- a/mysql-test/t/mysql_tzinfo_to_sql_symlink.test +++ b/mysql-test/t/mysql_tzinfo_to_sql_symlink.test @@ -32,6 +32,23 @@ --exec $MYSQL_TZINFO_TO_SQL --leap $MYSQLTEST_VARDIR/zoneinfo/GMT 2>&1 +--echo # +--echo # MDEV-28263: mariadb-tzinfo-to-sql improve wsrep and binlog cases +--echo # + +--echo # +--echo # Testing --skip-write-binlog +--echo # + +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--exec $MYSQL_TZINFO_TO_SQL --skip-write-binlog $MYSQLTEST_VARDIR/zoneinfo/GMT XXX 2>&1 + +--exec $MYSQL_TZINFO_TO_SQL --skip-write-binlog --leap $MYSQLTEST_VARDIR/zoneinfo/GMT 2>&1 + +--echo # +--echo # End of 10.2 tests +--echo # + # # Cleanup # diff --git a/sql/tztime.cc b/sql/tztime.cc index 47bc44f3386..8790673c5a0 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -2642,7 +2642,7 @@ static struct my_option my_long_options[] = &opt_verbose, &opt_verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"skip-write-binlog", 'S', "Do not replicate changes to time zone tables to other nodes in a Galera cluster.", + {"skip-write-binlog", 'S', "Do not replicate changes to time zone tables to the binary log, or to other nodes in a Galera cluster.", &opt_skip_write_binlog,&opt_skip_write_binlog, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; @@ -2738,9 +2738,9 @@ main(int argc, char **argv) sql_log_bin and wsrep_on to avoid Galera replicating below truncate table clauses. This will allow user to set different time zones to nodes in Galera cluster. */ - printf("set @prep1=if((select count(*) from information_schema.global_variables where variable_name='wsrep_on' and variable_value='ON'), 'SET SESSION SQL_LOG_BIN=?, WSREP_ON=OFF;', 'do ?');\n" - "prepare set_wsrep_write_binlog from @prep1;\n" - "set @toggle=0; execute set_wsrep_write_binlog using @toggle;\n"); + printf("set @prep1=if((select count(*) from information_schema.global_variables where variable_name='wsrep_on' and variable_value='ON'), 'SET SESSION WSREP_ON=OFF', 'do 0');\n" + "SET SESSION SQL_LOG_BIN=0;\n" + "execute immediate @prep1;\n"); } else { From 3b6c04f44c492bc3d0e494c9c7af7e95c682f3da Mon Sep 17 00:00:00 2001 From: Nayuta Yanagisawa Date: Sun, 24 Apr 2022 16:13:00 +0900 Subject: [PATCH 05/28] MDEV-27065 fixup: disable tests under valgrind The server behaves differently on the DATA/INDEX DIRECTORY clause under valgrind because symlink is disabled under valgrind. --- mysql-test/suite/parts/inc/part_alter_values.inc | 2 ++ mysql-test/suite/parts/t/partition_alter_myisam.test | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mysql-test/suite/parts/inc/part_alter_values.inc b/mysql-test/suite/parts/inc/part_alter_values.inc index 2f16476c78b..ca18faa5758 100644 --- a/mysql-test/suite/parts/inc/part_alter_values.inc +++ b/mysql-test/suite/parts/inc/part_alter_values.inc @@ -1,3 +1,5 @@ +--source include/have_symlink.inc + --echo # --echo # MDEV-14641 Incompatible key or row definition between the MariaDB .frm file and the information in the storage engine --echo # diff --git a/mysql-test/suite/parts/t/partition_alter_myisam.test b/mysql-test/suite/parts/t/partition_alter_myisam.test index 326cf52b018..d3abb8842e1 100644 --- a/mysql-test/suite/parts/t/partition_alter_myisam.test +++ b/mysql-test/suite/parts/t/partition_alter_myisam.test @@ -1,5 +1,4 @@ --source include/have_partition.inc ---source include/have_symlink.inc --let $engine=MyISAM --source inc/part_alter_values.inc From 3fec38d91d852d0762dbcbc60c1d911a68f1509f Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Mon, 25 Apr 2022 09:08:09 +0400 Subject: [PATCH 06/28] MDEV-28405 main.information_schema_tables fails sporadically with ER_NEED_REPREPARE or extra warning --- mysql-test/t/information_schema_tables.test | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mysql-test/t/information_schema_tables.test b/mysql-test/t/information_schema_tables.test index ebb1e21a65b..bc4f269a3fb 100644 --- a/mysql-test/t/information_schema_tables.test +++ b/mysql-test/t/information_schema_tables.test @@ -24,9 +24,14 @@ LOOP END LOOP $ --delimiter ; --connection default +# Avoid "Prepared statement needs to be re-prepared" +# Note, the code could probably eventually fixed to avoid forcing re-pepare if +# the *temporary* instance of Sp_caches (not the permanent one) was invalidated. +--disable_ps_protocol --disable_warnings SELECT v.* FROM v JOIN INFORMATION_SCHEMA.TABLES WHERE DATA_LENGTH = -1; --enable_warnings +--enable_ps_protocol # Cleanup --replace_result $conid CONID --eval KILL $conid From 907e4c62cec951646e9049dffc41a0a6eeeb821d Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Mon, 18 Apr 2022 19:43:25 +0400 Subject: [PATCH 07/28] MDEV-21037 mariabackup does not detect multi-source replication slave --- extra/mariabackup/backup_mysql.cc | 398 ++++++++++++++---- .../include/show_xtrabackup_slave_info.inc | 14 + .../show_xtrabackup_slave_info_out.inc | 20 + .../suite/mariabackup/rpl_slave_info.result | 7 + .../suite/mariabackup/rpl_slave_info.test | 5 + .../suite/mariabackup/slave_info_norpl.result | 59 +++ .../suite/mariabackup/slave_info_norpl.test | 86 ++++ 7 files changed, 510 insertions(+), 79 deletions(-) create mode 100644 mysql-test/suite/mariabackup/include/show_xtrabackup_slave_info.inc create mode 100644 mysql-test/suite/mariabackup/include/show_xtrabackup_slave_info_out.inc create mode 100644 mysql-test/suite/mariabackup/slave_info_norpl.result create mode 100644 mysql-test/suite/mariabackup/slave_info_norpl.test diff --git a/extra/mariabackup/backup_mysql.cc b/extra/mariabackup/backup_mysql.cc index 78af2b7f5ac..bddf069f4e9 100644 --- a/extra/mariabackup/backup_mysql.cc +++ b/extra/mariabackup/backup_mysql.cc @@ -265,7 +265,8 @@ free_mysql_variables(mysql_variable *vars) static char * -read_mysql_one_value(MYSQL *connection, const char *query) +read_mysql_one_value(MYSQL *connection, const char *query, + uint column, uint expect_columns) { MYSQL_RES *mysql_result; MYSQL_ROW row; @@ -273,10 +274,10 @@ read_mysql_one_value(MYSQL *connection, const char *query) mysql_result = xb_mysql_query(connection, query, true); - ut_ad(mysql_num_fields(mysql_result) == 1); + ut_ad(mysql_num_fields(mysql_result) == expect_columns); if ((row = mysql_fetch_row(mysql_result))) { - result = strdup(row[0]); + result = strdup(row[column]); } mysql_free_result(mysql_result); @@ -284,6 +285,15 @@ read_mysql_one_value(MYSQL *connection, const char *query) return(result); } + +static +char * +read_mysql_one_value(MYSQL *mysql, const char *query) +{ + return read_mysql_one_value(mysql, query, 0/*offset*/, 1/*total columns*/); +} + + static bool check_server_version(unsigned long version_number, @@ -1196,92 +1206,322 @@ cleanup: } +class Var +{ + const char *m_name; + char *m_value; + /* + Disable copying constructors for safety, as the default binary copying + which would be wrong. If we ever want them, the m_value + member should be copied using an strdup()-alike function. + */ + Var(const Var &); // Disabled + Var(Var &); // Disabled +public: + ~Var() + { + free(m_value); + } + Var(const char *name) + :m_name(name), + m_value(NULL) + { } + // Init using a SHOW VARIABLES LIKE 'name' query + Var(const char *name, MYSQL *mysql) + :m_name(name) + { + char buf[128]; + my_snprintf(buf, sizeof(buf), "SHOW VARIABLES LIKE '%s'", m_name); + m_value= read_mysql_one_value(mysql, buf, 1/*offset*/, 2/*total columns*/); + } + /* + Init by name from a result set. + If the variable name is not found in the result set metadata field names, + it's value stays untouched. + */ + bool init(MYSQL_RES *mysql_result, MYSQL_ROW row) + { + MYSQL_FIELD *field= mysql_fetch_fields(mysql_result); + for (uint i= 0; i < mysql_num_fields(mysql_result); i++) + { + if (!strcmp(field[i].name, m_name)) + { + free(m_value); // In case it was initialized earlier + m_value= row[i] ? strdup(row[i]) : NULL; + return false; + } + } + return true; + } + void replace(char from, char to) + { + ut_ad(m_value); + for (char *ptr= strchr(m_value, from); ptr; ptr= strchr(ptr, from)) + *ptr= to; + } + + const char *value() const { return m_value; } + bool eq_value(const char *str, size_t length) const + { + return m_value && !strncmp(m_value, str, length) && m_value[length] == '\0'; + } + bool is_null_or_empty() const { return !m_value || !m_value[0]; } + bool print(String *to) const + { + ut_ad(m_value); + return to->append(m_value); + } + bool print_quoted(String *to) const + { + ut_ad(m_value); + return to->append("'") || to->append(m_value) || to->append("'"); + } + bool print_set_global(String *to) const + { + ut_ad(m_value); + return + to->append("SET GLOBAL ") || + to->append(m_name) || + to->append(" = '") || + to->append(m_value) || + to->append("';\n"); + } +}; + + +class Show_slave_status +{ + Var m_mariadb_connection_name; // MariaDB: e.g. 'master1' + Var m_master; // e.g. 'localhost' + Var m_filename; // e.g. 'source-bin.000002' + Var m_position; // a number + Var m_mysql_gtid_executed; // MySQL56: e.g. single ':1-5" or multiline + // ':1-10,\n:1-20\n:1-30' + Var m_mariadb_using_gtid; // MariaDB: 'No','Slave_Pos','Current_Pos' + +public: + + Show_slave_status() + :m_mariadb_connection_name("Connection_name"), + m_master("Master_Host"), + m_filename("Relay_Master_Log_File"), + m_position("Exec_Master_Log_Pos"), + m_mysql_gtid_executed("Executed_Gtid_Set"), + m_mariadb_using_gtid("Using_Gtid") + { } + + void init(MYSQL_RES *res, MYSQL_ROW row) + { + m_mariadb_connection_name.init(res, row); + m_master.init(res, row); + m_filename.init(res, row); + m_position.init(res, row); + m_mysql_gtid_executed.init(res, row); + m_mariadb_using_gtid.init(res, row); + // Normalize + if (m_mysql_gtid_executed.value()) + m_mysql_gtid_executed.replace('\n', ' '); + } + + static void msg_is_not_slave() + { + msg("Failed to get master binlog coordinates " + "from SHOW SLAVE STATUS.This means that the server is not a " + "replication slave. Ignoring the --slave-info option"); + } + + bool is_mariadb_using_gtid() const + { + return !m_mariadb_using_gtid.eq_value("No", 2); + } + + static bool start_comment_chunk(String *to) + { + return to->length() ? to->append("; ") : false; + } + + bool print_connection_name_if_set(String *to) const + { + if (!m_mariadb_connection_name.is_null_or_empty()) + return m_mariadb_connection_name.print_quoted(to) || to->append(' '); + return false; + } + + bool print_comment_master_identity(String *comment) const + { + if (comment->append("master ")) + return true; + if (!m_mariadb_connection_name.is_null_or_empty()) + return m_mariadb_connection_name.print_quoted(comment); + return comment->append("''"); // Default not named master + } + + bool print_using_master_log_pos(String *sql, String *comment) const + { + return + sql->append("CHANGE MASTER ") || + print_connection_name_if_set(sql) || + sql->append("TO MASTER_LOG_FILE=") || m_filename.print_quoted(sql) || + sql->append(", MASTER_LOG_POS=") || m_position.print(sql) || + sql->append(";\n") || + print_comment_master_identity(comment) || + comment->append(" filename ") || m_filename.print_quoted(comment) || + comment->append(" position ") || m_position.print_quoted(comment); + } + + bool print_mysql56(String *sql, String *comment) const + { + /* + SET @@GLOBAL.gtid_purged = '2174B383-5441-11E8-B90A-C80AA9429562:1-1029, ' + '224DA167-0C0C-11E8-8442-00059A3C7B00:1-2695'; + CHANGE MASTER TO MASTER_AUTO_POSITION=1; + */ + return + sql->append("SET GLOBAL gtid_purged=") || + m_mysql_gtid_executed.print_quoted(sql) || + sql->append(";\n") || + sql->append("CHANGE MASTER TO MASTER_AUTO_POSITION=1;\n") || + print_comment_master_identity(comment) || + comment->append(" purge list ") || + m_mysql_gtid_executed.print_quoted(comment); + } + + bool print_mariadb10_using_gtid(String *sql, String *comment) const + { + return + sql->append("CHANGE MASTER ") || + print_connection_name_if_set(sql) || + sql->append("TO master_use_gtid = slave_pos;\n") || + print_comment_master_identity(comment) || + comment->append(" master_use_gtid = slave_pos"); + } + + bool print(String *sql, String *comment, const Var >id_slave_pos) const + { + if (!m_mysql_gtid_executed.is_null_or_empty()) + { + /* MySQL >= 5.6 with GTID enabled */ + return print_mysql56(sql, comment); + } + + if (!gtid_slave_pos.is_null_or_empty() && is_mariadb_using_gtid()) + { + /* MariaDB >= 10.0 with GTID enabled */ + return print_mariadb10_using_gtid(sql, comment); + } + + return print_using_master_log_pos(sql, comment); + } + + /* + Get master info into strings "sql" and "comment" from a MYSQL_RES. + @return false on success + @return true on error + */ + static bool get_slave_info(MYSQL_RES *show_slave_info_result, + const Var >id_slave_pos, + String *sql, String *comment) + { + if (!gtid_slave_pos.is_null_or_empty()) + { + // Print gtid_slave_pos if any of the masters really needs it. + while (MYSQL_ROW row= mysql_fetch_row(show_slave_info_result)) + { + Show_slave_status status; + status.init(show_slave_info_result, row); + if (status.is_mariadb_using_gtid()) + { + if (gtid_slave_pos.print_set_global(sql) || + comment->append("gtid_slave_pos ") || + gtid_slave_pos.print_quoted(comment)) + return true; // Error + break; + } + } + } + + // Print the list of masters + mysql_data_seek(show_slave_info_result, 0); + while (MYSQL_ROW row= mysql_fetch_row(show_slave_info_result)) + { + Show_slave_status status; + status.init(show_slave_info_result, row); + if (start_comment_chunk(comment) || + status.print(sql, comment, gtid_slave_pos)) + return true; // Error + } + return false; // Success + } + + /* + Get master info into strings "sql" and "comment". + @return false on success + @return true on error + */ + static bool get_slave_info(MYSQL *mysql, bool show_all_slave_status, + String *sql, String *comment) + { + bool rc= false; // Success + // gtid_slave_pos - MariaDB variable : e.g. "0-1-1" or "1-10-100,2-20-500" + Var gtid_slave_pos("gtid_slave_pos", mysql); + const char *query= show_all_slave_status ? "SHOW ALL SLAVES STATUS" : + "SHOW SLAVE STATUS"; + MYSQL_RES *mysql_result= xb_mysql_query(mysql, query, true); + if (!mysql_num_rows(mysql_result)) + { + msg_is_not_slave(); + // Don't change rc, we still want to continue the backup + } + else + { + rc= get_slave_info(mysql_result, gtid_slave_pos, sql, comment); + } + mysql_free_result(mysql_result); + return rc; + } +}; + + + /*********************************************************************//** Retrieves MySQL binlog position of the master server in a replication setup and saves it in a file. It also saves it in mysql_slave_position -variable. */ +variable. +@returns false on error +@returns true on success +*/ bool write_slave_info(MYSQL *connection) { - char *master = NULL; - char *filename = NULL; - char *gtid_executed = NULL; - char *using_gtid = NULL; - char *position = NULL; - char *gtid_slave_pos = NULL; - char *ptr; - bool result = false; + String sql, comment; + bool show_all_slaves_status= false; - mysql_variable status[] = { - {"Master_Host", &master}, - {"Relay_Master_Log_File", &filename}, - {"Exec_Master_Log_Pos", &position}, - {"Executed_Gtid_Set", >id_executed}, - {"Using_Gtid", &using_gtid}, - {NULL, NULL} - }; + switch (server_flavor) + { + case FLAVOR_MARIADB: + show_all_slaves_status= mysql_server_version >= 100000; + break; + case FLAVOR_UNKNOWN: + case FLAVOR_MYSQL: + case FLAVOR_PERCONA_SERVER: + break; + } - mysql_variable variables[] = { - {"gtid_slave_pos", >id_slave_pos}, - {NULL, NULL} - }; + if (Show_slave_status::get_slave_info(connection, show_all_slaves_status, + &sql, &comment)) + return false; // Error - read_mysql_variables(connection, "SHOW SLAVE STATUS", status, false); - read_mysql_variables(connection, "SHOW VARIABLES", variables, true); + if (!sql.length()) + { + /* + SHOW [ALL] SLAVE STATUS returned no rows. + Don't create the file, but return success to continue the backup. + */ + return true; // Success + } - if (master == NULL || filename == NULL || position == NULL) { - msg("Failed to get master binlog coordinates " - "from SHOW SLAVE STATUS.This means that the server is not a " - "replication slave. Ignoring the --slave-info option"); - /* we still want to continue the backup */ - result = true; - goto cleanup; - } - - /* Print slave status to a file. - If GTID mode is used, construct a CHANGE MASTER statement with - MASTER_AUTO_POSITION and correct a gtid_purged value. */ - if (gtid_executed != NULL && *gtid_executed) { - /* MySQL >= 5.6 with GTID enabled */ - - for (ptr = strchr(gtid_executed, '\n'); - ptr; - ptr = strchr(ptr, '\n')) { - *ptr = ' '; - } - - result = backup_file_printf(XTRABACKUP_SLAVE_INFO, - "SET GLOBAL gtid_purged='%s';\n" - "CHANGE MASTER TO MASTER_AUTO_POSITION=1\n", - gtid_executed); - - ut_a(asprintf(&mysql_slave_position, - "master host '%s', purge list '%s'", - master, gtid_executed) != -1); - } else if (gtid_slave_pos && *gtid_slave_pos && - !(using_gtid && !strncmp(using_gtid, "No", 2))) { - /* MariaDB >= 10.0 with GTID enabled */ - result = backup_file_printf(XTRABACKUP_SLAVE_INFO, - "SET GLOBAL gtid_slave_pos = '%s';\n" - "CHANGE MASTER TO master_use_gtid = slave_pos\n", - gtid_slave_pos); - ut_a(asprintf(&mysql_slave_position, - "master host '%s', gtid_slave_pos %s", - master, gtid_slave_pos) != -1); - } else { - result = backup_file_printf(XTRABACKUP_SLAVE_INFO, - "CHANGE MASTER TO MASTER_LOG_FILE='%s', " - "MASTER_LOG_POS=%s\n", filename, position); - ut_a(asprintf(&mysql_slave_position, - "master host '%s', filename '%s', position '%s'", - master, filename, position) != -1); - } - -cleanup: - free_mysql_variables(status); - free_mysql_variables(variables); - - return(result); + mysql_slave_position= strdup(comment.c_ptr()); + return backup_file_print_buf(XTRABACKUP_SLAVE_INFO, sql.ptr(), sql.length()); } diff --git a/mysql-test/suite/mariabackup/include/show_xtrabackup_slave_info.inc b/mysql-test/suite/mariabackup/include/show_xtrabackup_slave_info.inc new file mode 100644 index 00000000000..4a83c9c394e --- /dev/null +++ b/mysql-test/suite/mariabackup/include/show_xtrabackup_slave_info.inc @@ -0,0 +1,14 @@ +--disable_query_log +--file_exists $targetdir/xtrabackup_slave_info +CREATE TEMPORARY TABLE tmp_slave_info(lineno SERIAL, line TEXT); +--replace_result $targetdir TARGETDIR +--eval LOAD DATA LOCAL INFILE '$targetdir/xtrabackup_slave_info' INTO TABLE tmp_slave_info (line); +SELECT + lineno, + regexp_replace( + regexp_replace(line, '(?<=MASTER_LOG_POS=)[0-9]+', ''), + '[0-9]+-[0-9]+-[0-9]+', '') + AS line +FROM tmp_slave_info ORDER BY lineno; +DROP TEMPORARY TABLE tmp_slave_info; +--enable_query_log diff --git a/mysql-test/suite/mariabackup/include/show_xtrabackup_slave_info_out.inc b/mysql-test/suite/mariabackup/include/show_xtrabackup_slave_info_out.inc new file mode 100644 index 00000000000..90b2d00b61d --- /dev/null +++ b/mysql-test/suite/mariabackup/include/show_xtrabackup_slave_info_out.inc @@ -0,0 +1,20 @@ +--disable_query_log +--file_exists $XTRABACKUP_OUT +CREATE TEMPORARY TABLE tmp_slave_info_out(lineno SERIAL, line TEXT); +--replace_result $targetdir TARGETDIR +--eval LOAD DATA LOCAL INFILE '$XTRABACKUP_OUT' INTO TABLE tmp_slave_info_out (line); +SELECT + replace( + regexp_replace( + regexp_replace(line, + '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9]', + 'YYYY-MM-DD hh:mm:ss'), + '[0-9]+-[0-9]+-[0-9]+', ''), + '\r','' /* Remove CR on Windows */) + AS line +FROM tmp_slave_info_out +WHERE line LIKE '%MySQL slave binlog position%' + OR line LIKE '%Failed to get master binlog coordinates%' +ORDER BY lineno; +DROP TEMPORARY TABLE tmp_slave_info_out; +--enable_query_log diff --git a/mysql-test/suite/mariabackup/rpl_slave_info.result b/mysql-test/suite/mariabackup/rpl_slave_info.result index 13044fd6c39..ec27166ecfb 100644 --- a/mysql-test/suite/mariabackup/rpl_slave_info.result +++ b/mysql-test/suite/mariabackup/rpl_slave_info.result @@ -13,6 +13,9 @@ connection slave; "using_gtid: Slave_Pos" FOUND 1 /gtid_slave_pos/ in xtrabackup_slave_info NOT FOUND /MASTER_LOG_FILE/ in xtrabackup_slave_info +lineno line +1 SET GLOBAL gtid_slave_pos = ''; +2 CHANGE MASTER TO master_use_gtid = slave_pos; ############### # If Using_Gtid != 'No' and !gtid_slave_pos, backup master position ######################## @@ -20,6 +23,8 @@ include/stop_slave.inc SET GLOBAL gtid_slave_pos=""; NOT FOUND /gtid_slave_pos/ in xtrabackup_slave_info FOUND 1 /MASTER_LOG_FILE/ in xtrabackup_slave_info +lineno line +1 CHANGE MASTER TO MASTER_LOG_FILE='master-bin.000001', MASTER_LOG_POS=; ############### # If Using_Gtid == 'No', backup Exec_Master_Log_Pos ######################## @@ -31,6 +36,8 @@ connection slave; "using_gtid: No" NOT FOUND /gtid_slave_pos/ in xtrabackup_slave_info FOUND 1 /MASTER_LOG_FILE/ in xtrabackup_slave_info +lineno line +1 CHANGE MASTER TO MASTER_LOG_FILE='master-bin.000001', MASTER_LOG_POS=; connection master; DROP TABLE t; connection slave; diff --git a/mysql-test/suite/mariabackup/rpl_slave_info.test b/mysql-test/suite/mariabackup/rpl_slave_info.test index ca7682d8af9..1c5dd89acc9 100644 --- a/mysql-test/suite/mariabackup/rpl_slave_info.test +++ b/mysql-test/suite/mariabackup/rpl_slave_info.test @@ -27,6 +27,7 @@ exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --defaults-group-suffi --source include/search_pattern_in_file.inc --let SEARCH_PATTERN=MASTER_LOG_FILE --source include/search_pattern_in_file.inc +--source include/show_xtrabackup_slave_info.inc rmdir $targetdir; @@ -35,7 +36,9 @@ rmdir $targetdir; --echo ######################## --source include/stop_slave.inc +--disable_warnings SET GLOBAL gtid_slave_pos=""; +--enable_warnings --let $targetdir=$MYSQLTEST_VARDIR/tmp/backup --disable_result_log @@ -47,6 +50,7 @@ exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --defaults-group-suffi --source include/search_pattern_in_file.inc --let SEARCH_PATTERN=MASTER_LOG_FILE --source include/search_pattern_in_file.inc +--source include/show_xtrabackup_slave_info.inc rmdir $targetdir; @@ -74,6 +78,7 @@ exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --defaults-group-suffi --source include/search_pattern_in_file.inc --let SEARCH_PATTERN=MASTER_LOG_FILE --source include/search_pattern_in_file.inc +--source include/show_xtrabackup_slave_info.inc rmdir $targetdir; diff --git a/mysql-test/suite/mariabackup/slave_info_norpl.result b/mysql-test/suite/mariabackup/slave_info_norpl.result new file mode 100644 index 00000000000..9fcd67a8916 --- /dev/null +++ b/mysql-test/suite/mariabackup/slave_info_norpl.result @@ -0,0 +1,59 @@ +# +# Start of 10.2 tests +# +# +# MDEV-21037 mariabackup does not detect multi-source replication slave +# +SELECT @@global.gtid_slave_pos; +@@global.gtid_slave_pos + + +# Without any masters the file xtrabackup_slave_info is not created +line +[00] YYYY-MM-DD hh:mm:ss Failed to get master binlog coordinates from SHOW SLAVE STATUS.This means that the server is not a replication slave. Ignoring the --slave-info option + +CHANGE MASTER TO MASTER_HOST='localhost', MASTER_PORT=10000; +lineno line +1 CHANGE MASTER TO MASTER_LOG_FILE='', MASTER_LOG_POS=; +line +[00] YYYY-MM-DD hh:mm:ss MySQL slave binlog position: master '' filename '' position '0' + +CHANGE MASTER 'master2' TO MASTER_HOST='localhost', MASTER_PORT=10002; +lineno line +1 CHANGE MASTER TO MASTER_LOG_FILE='', MASTER_LOG_POS=; +2 CHANGE MASTER 'master2' TO MASTER_LOG_FILE='', MASTER_LOG_POS=; +line +[00] YYYY-MM-DD hh:mm:ss MySQL slave binlog position: master '' filename '' position '0'; master 'master2' filename '' position '0' + +SET GLOBAL gtid_slave_pos='1-1-1,2-2-2'; +CHANGE MASTER 'master3' TO MASTER_HOST='localhost', MASTER_PORT=10003, MASTER_USE_GTID=slave_pos; +CHANGE MASTER 'master4' TO MASTER_HOST='localhost', MASTER_PORT=10004, MASTER_USE_GTID=no; +CHANGE MASTER 'master5' TO MASTER_HOST='localhost', MASTER_PORT=10005, MASTER_USE_GTID=slave_pos; +lineno line +1 SET GLOBAL gtid_slave_pos = ','; +2 CHANGE MASTER TO MASTER_LOG_FILE='', MASTER_LOG_POS=; +3 CHANGE MASTER 'master2' TO MASTER_LOG_FILE='', MASTER_LOG_POS=; +4 CHANGE MASTER 'master3' TO master_use_gtid = slave_pos; +5 CHANGE MASTER 'master4' TO MASTER_LOG_FILE='', MASTER_LOG_POS=; +6 CHANGE MASTER 'master5' TO master_use_gtid = slave_pos; +line +[00] YYYY-MM-DD hh:mm:ss MySQL slave binlog position: gtid_slave_pos ','; master '' filename '' position '0'; master 'master2' filename '' position '0'; master 'master3' master_use_gtid = slave_pos; master 'master4' filename '' position '0'; master 'master5' master_use_gtid = slave_pos + +CHANGE MASTER TO MASTER_HOST='localhost', MASTER_PORT=10000, MASTER_USE_GTID=slave_pos; +lineno line +1 SET GLOBAL gtid_slave_pos = ','; +2 CHANGE MASTER TO master_use_gtid = slave_pos; +3 CHANGE MASTER 'master2' TO MASTER_LOG_FILE='', MASTER_LOG_POS=; +4 CHANGE MASTER 'master3' TO master_use_gtid = slave_pos; +5 CHANGE MASTER 'master4' TO MASTER_LOG_FILE='', MASTER_LOG_POS=; +6 CHANGE MASTER 'master5' TO master_use_gtid = slave_pos; +line +[00] YYYY-MM-DD hh:mm:ss MySQL slave binlog position: gtid_slave_pos ','; master '' master_use_gtid = slave_pos; master 'master2' filename '' position '0'; master 'master3' master_use_gtid = slave_pos; master 'master4' filename '' position '0'; master 'master5' master_use_gtid = slave_pos +RESET SLAVE ALL; +RESET SLAVE 'master2' ALL; +RESET SLAVE 'master3' ALL; +RESET SLAVE 'master4' ALL; +RESET SLAVE 'master5' ALL; +# +# End of 10.2 tests +# diff --git a/mysql-test/suite/mariabackup/slave_info_norpl.test b/mysql-test/suite/mariabackup/slave_info_norpl.test new file mode 100644 index 00000000000..0d2d2ed4861 --- /dev/null +++ b/mysql-test/suite/mariabackup/slave_info_norpl.test @@ -0,0 +1,86 @@ +# +# "mariabackup --slave-info" tests that can be run without +# actually starting the replication. +# + +--echo # +--echo # Start of 10.2 tests +--echo # + +--echo # +--echo # MDEV-21037 mariabackup does not detect multi-source replication slave +--echo # + +--let $targetdir=$MYSQLTEST_VARDIR/tmp/backup +--let $XTRABACKUP_ARGS=--defaults-file=$MYSQLTEST_VARDIR/my.cnf --defaults-group-suffix=.2 --slave-info --backup --databases=test --target-dir=$targetdir +--let $XTRABACKUP_OUT=$MYSQLTEST_VARDIR/tmp/xtrabackup_out + +# Should be empty by default +SELECT @@global.gtid_slave_pos; + +--echo +--echo # Without any masters the file xtrabackup_slave_info is not created + +--disable_result_log +exec $XTRABACKUP $XTRABACKUP_ARGS >$XTRABACKUP_OUT; +--enable_result_log +--error 1 +--file_exists $targetdir/xtrabackup_slave_info +--source include/show_xtrabackup_slave_info_out.inc +--remove_file $XTRABACKUP_OUT +rmdir $targetdir; + +--echo +CHANGE MASTER TO MASTER_HOST='localhost', MASTER_PORT=10000; +--disable_result_log +exec $XTRABACKUP $XTRABACKUP_ARGS >$XTRABACKUP_OUT; +--enable_result_log +--source include/show_xtrabackup_slave_info.inc +--source include/show_xtrabackup_slave_info_out.inc +--remove_file $XTRABACKUP_OUT +rmdir $targetdir; + +--echo +CHANGE MASTER 'master2' TO MASTER_HOST='localhost', MASTER_PORT=10002; +--disable_result_log +exec $XTRABACKUP $XTRABACKUP_ARGS >$XTRABACKUP_OUT; +--enable_result_log +--source include/show_xtrabackup_slave_info.inc +--source include/show_xtrabackup_slave_info_out.inc +--remove_file $XTRABACKUP_OUT +rmdir $targetdir; + +--echo +SET GLOBAL gtid_slave_pos='1-1-1,2-2-2'; +CHANGE MASTER 'master3' TO MASTER_HOST='localhost', MASTER_PORT=10003, MASTER_USE_GTID=slave_pos; +CHANGE MASTER 'master4' TO MASTER_HOST='localhost', MASTER_PORT=10004, MASTER_USE_GTID=no; +CHANGE MASTER 'master5' TO MASTER_HOST='localhost', MASTER_PORT=10005, MASTER_USE_GTID=slave_pos; + +--disable_result_log +exec $XTRABACKUP $XTRABACKUP_ARGS >$XTRABACKUP_OUT; +--enable_result_log +--source include/show_xtrabackup_slave_info.inc +--source include/show_xtrabackup_slave_info_out.inc +--remove_file $XTRABACKUP_OUT +rmdir $targetdir; + +--echo +CHANGE MASTER TO MASTER_HOST='localhost', MASTER_PORT=10000, MASTER_USE_GTID=slave_pos; +--disable_result_log +exec $XTRABACKUP $XTRABACKUP_ARGS >$XTRABACKUP_OUT; +--enable_result_log +--source include/show_xtrabackup_slave_info.inc +--source include/show_xtrabackup_slave_info_out.inc +--remove_file $XTRABACKUP_OUT +rmdir $targetdir; + +RESET SLAVE ALL; +RESET SLAVE 'master2' ALL; +RESET SLAVE 'master3' ALL; +RESET SLAVE 'master4' ALL; +RESET SLAVE 'master5' ALL; + + +--echo # +--echo # End of 10.2 tests +--echo # From 1bcdc3e9eb1a393fd07403c874ac7d7edc6d8a0a Mon Sep 17 00:00:00 2001 From: Andrei Date: Thu, 14 Apr 2022 18:59:24 +0300 Subject: [PATCH 08/28] MDEV-27697 slave must recognize incomplete replication event group In cases of a faulty master or an incorrect binlog event producer, that slave is working with, sends an incomplete group of events slave must react with an error to not to log into the relay-log any new events that do not belong to the incomplete group. Fixed with extending received event properties check when slave connects to master in gtid mode. Specifically for the event that can be a part of a group its relay-logging is permitted only when its position within the group is validated. Otherwise slave IO thread stops with ER_SLAVE_RELAY_LOG_WRITE_FAILURE. --- mysql-test/extra/rpl_tests/rpl_parallel.inc | 1 + .../suite/rpl/r/rpl_gtid_grouping.result | 54 ++++++ mysql-test/suite/rpl/r/rpl_parallel.result | 1 + mysql-test/suite/rpl/t/rpl_gtid_grouping.test | 97 +++++++++++ sql/rpl_gtid.h | 1 + sql/slave.cc | 164 +++++++++++++++--- 6 files changed, 292 insertions(+), 26 deletions(-) create mode 100644 mysql-test/suite/rpl/r/rpl_gtid_grouping.result create mode 100644 mysql-test/suite/rpl/t/rpl_gtid_grouping.test diff --git a/mysql-test/extra/rpl_tests/rpl_parallel.inc b/mysql-test/extra/rpl_tests/rpl_parallel.inc index b88d2126d4d..9ba7a30f2eb 100644 --- a/mysql-test/extra/rpl_tests/rpl_parallel.inc +++ b/mysql-test/extra/rpl_tests/rpl_parallel.inc @@ -1872,6 +1872,7 @@ SET GLOBAL slave_parallel_threads=10; SET GLOBAL slave_parallel_threads=1; SET @old_dbug= @@GLOBAL.debug_dbug; SET GLOBAL debug_dbug="+d,slave_discard_xid_for_gtid_0_x_1000"; +CALL mtr.add_suppression("Unexpected break of being relay-logged GTID"); --connection server_1 INSERT INTO t2 VALUES (101); diff --git a/mysql-test/suite/rpl/r/rpl_gtid_grouping.result b/mysql-test/suite/rpl/r/rpl_gtid_grouping.result new file mode 100644 index 00000000000..ad7d6116c49 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_gtid_grouping.result @@ -0,0 +1,54 @@ +include/master-slave.inc +[connection master] +connection slave; +call mtr.add_suppression("Unexpected break of being relay-logged GTID 0-27697-1000"); +call mtr.add_suppression("Relay log write failure: could not queue event from master"); +call mtr.add_suppression("The current group of events starts with a non-GTID"); +include/stop_slave.inc +CHANGE MASTER TO MASTER_USE_GTID=slave_pos; +include/start_slave.inc +connection master; +CREATE TABLE t (a INT) ENGINE=innodb; +INSERT INTO t VALUES(1); +### A. Simulate an unnoticeable loss of Xid event +connection slave; +SET @@global.debug_dbug="+d,slave_discard_xid_for_gtid_0_x_1000"; +connection master; +SET @@gtid_seq_no=1000; +set @@server_id=27697; +INSERT INTO t VALUES(1000); +set @@server_id=default; +INSERT INTO t VALUES(1001); +## Prove the error occurs. +connection slave; +include/wait_for_slave_io_error.inc [errno=1595] +## Prove the slave recovers after the simulation condtion is lifted. +SET @@global.debug_dbug=default; +include/start_slave.inc +### B. Do the same to GTID event. +connection slave; +SET @@global.debug_dbug="+d,slave_discard_gtid_0_x_1002"; +connection master; +SET @@gtid_seq_no=1002; +set @@server_id=27697; +INSERT INTO t VALUES(1002); +set @@server_id=default; +INSERT INTO t VALUES(1003); +## Prove the error occurs. +connection slave; +include/wait_for_slave_io_error.inc [errno=1595] +## Prove the slave recovers after the simulation condtion is lifted. +SET @@global.debug_dbug=default; +include/start_slave.inc +connection master; +connection slave; +include/diff_tables.inc [master:t,slave:t] +"===== Clean up =====" +connection slave; +include/stop_slave.inc +CHANGE MASTER TO MASTER_USE_GTID=no; +include/start_slave.inc +connection master; +DROP TABLE t; +SET GLOBAL LOG_WARNINGS=default; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/r/rpl_parallel.result b/mysql-test/suite/rpl/r/rpl_parallel.result index 657b3ba7448..2601b30279e 100644 --- a/mysql-test/suite/rpl/r/rpl_parallel.result +++ b/mysql-test/suite/rpl/r/rpl_parallel.result @@ -1378,6 +1378,7 @@ include/stop_slave.inc SET GLOBAL slave_parallel_threads=1; SET @old_dbug= @@GLOBAL.debug_dbug; SET GLOBAL debug_dbug="+d,slave_discard_xid_for_gtid_0_x_1000"; +CALL mtr.add_suppression("Unexpected break of being relay-logged GTID"); connection server_1; INSERT INTO t2 VALUES (101); INSERT INTO t2 VALUES (102); diff --git a/mysql-test/suite/rpl/t/rpl_gtid_grouping.test b/mysql-test/suite/rpl/t/rpl_gtid_grouping.test new file mode 100644 index 00000000000..66448c4f96c --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_gtid_grouping.test @@ -0,0 +1,97 @@ +# ==== Purpose ==== +# +# Test verifies that replicated transaction boundaries are set properly +# at receiving from master time. +# +# ==== Implementation ==== +# +# A. Simulate an unnoticeable loss of Xid event to observe a slave error, +# then restart slave to recover from the failure. +# B. Do the same to GTID event. +# +# ==== References ==== +# +# MDEV-27697 slave must recognize incomplete replication event group +# +--source include/have_binlog_format_mixed.inc +--source include/have_innodb.inc +--source include/have_debug.inc +--source include/master-slave.inc + +--connection slave +call mtr.add_suppression("Unexpected break of being relay-logged GTID 0-27697-1000"); +call mtr.add_suppression("Relay log write failure: could not queue event from master"); +call mtr.add_suppression("The current group of events starts with a non-GTID"); + +--source include/stop_slave.inc +CHANGE MASTER TO MASTER_USE_GTID=slave_pos; +--source include/start_slave.inc + +--connection master +CREATE TABLE t (a INT) ENGINE=innodb; +INSERT INTO t VALUES(1); +save_master_pos; + +--echo ### A. Simulate an unnoticeable loss of Xid event +--sync_slave_with_master +SET @@global.debug_dbug="+d,slave_discard_xid_for_gtid_0_x_1000"; + +--connection master +SET @@gtid_seq_no=1000; +set @@server_id=27697; +INSERT INTO t VALUES(1000); +set @@server_id=default; +INSERT INTO t VALUES(1001); + +--echo ## Prove the error occurs. +--connection slave +# ER_SLAVE_RELAY_LOG_WRITE_FAILURE +--let $slave_io_errno = 1595 +--source include/wait_for_slave_io_error.inc +## EOP + +--echo ## Prove the slave recovers after the simulation condtion is lifted. +SET @@global.debug_dbug=default; +--source include/start_slave.inc + +--echo ### B. Do the same to GTID event. +--connection slave +SET @@global.debug_dbug="+d,slave_discard_gtid_0_x_1002"; + +--connection master +SET @@gtid_seq_no=1002; +set @@server_id=27697; +INSERT INTO t VALUES(1002); +set @@server_id=default; +INSERT INTO t VALUES(1003); + +--echo ## Prove the error occurs. +--connection slave +# ER_SLAVE_RELAY_LOG_WRITE_FAILURE +--let $slave_io_errno = 1595 +--source include/wait_for_slave_io_error.inc +## EOP + +--echo ## Prove the slave recovers after the simulation condtion is lifted. +SET @@global.debug_dbug=default; +--source include/start_slave.inc + +--connection master +save_master_pos; + +--sync_slave_with_master +## EOP + +--let $diff_tables=master:t,slave:t +--source include/diff_tables.inc + +--echo "===== Clean up =====" +--connection slave +--source include/stop_slave.inc +CHANGE MASTER TO MASTER_USE_GTID=no; +--source include/start_slave.inc + +--connection master +DROP TABLE t; +SET GLOBAL LOG_WARNINGS=default; +--source include/rpl_end.inc diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h index e4949ff0d39..bb171f3d95d 100644 --- a/sql/rpl_gtid.h +++ b/sql/rpl_gtid.h @@ -26,6 +26,7 @@ extern const LEX_STRING rpl_gtid_slave_state_table_name; class String; +#define PARAM_GTID(G) G.domain_id, G.server_id, G.seq_no struct rpl_gtid { diff --git a/sql/slave.cc b/sql/slave.cc index 2ff1a0490e9..31e50753c9e 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -6196,23 +6196,75 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) } } - if (unlikely(mi->gtid_reconnect_event_skip_count)) - { - goto default_action; - } - /* - We have successfully queued to relay log everything before this GTID, so + Unless the previous group is malformed, + we have successfully queued to relay log everything before this GTID, so in case of reconnect we can start from after any previous GTID. - (Normally we would have updated gtid_current_pos earlier at the end of - the previous event group, but better leave an extra check here for - safety). + (We must have updated gtid_current_pos earlier at the end of + the previous event group. Unless ...) */ - if (mi->events_queued_since_last_gtid) + if (unlikely(mi->events_queued_since_last_gtid > + mi->gtid_reconnect_event_skip_count)) { - mi->gtid_current_pos.update(&mi->last_queued_gtid); - mi->events_queued_since_last_gtid= 0; + /* + ...unless the last group has not been completed. An assert below + can be satisfied only with the strict mode that ensures + against "genuine" gtid duplicates. + */ + rpl_gtid *gtid_in_slave_state= + mi->gtid_current_pos.find(mi->last_queued_gtid.domain_id); + + // Slave gtid state must not have updated yet to the last received gtid. + DBUG_ASSERT((mi->using_gtid == Master_info::USE_GTID_NO || + !opt_gtid_strict_mode) || + (!gtid_in_slave_state || + !(*gtid_in_slave_state == mi->last_queued_gtid))); + + DBUG_EXECUTE_IF("slave_discard_xid_for_gtid_0_x_1000", + { + /* Inject an event group that is missing its XID commit event. */ + if (mi->last_queued_gtid.domain_id == 0 && + mi->last_queued_gtid.seq_no == 1000) + { + sql_print_warning( + "Unexpected break of being relay-logged GTID %u-%u-%llu " + "event group by the current GTID event %u-%u-%llu", + PARAM_GTID(mi->last_queued_gtid),PARAM_GTID(event_gtid)); + DBUG_SET("-d,slave_discard_xid_for_gtid_0_x_1000"); + goto dbug_gtid_accept; + } + }); + error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE; + sql_print_error("Unexpected break of being relay-logged GTID %u-%u-%llu " + "event group by the current GTID event %u-%u-%llu", + PARAM_GTID(mi->last_queued_gtid),PARAM_GTID(event_gtid)); + goto err; } + else if (unlikely(mi->gtid_reconnect_event_skip_count > 0)) + { + if (mi->gtid_reconnect_event_skip_count == + mi->events_queued_since_last_gtid) + { + DBUG_ASSERT(event_gtid == mi->last_queued_gtid); + + goto default_action; + } + + DBUG_ASSERT(0); + } + // else_likely{... +#ifndef DBUG_OFF +dbug_gtid_accept: + DBUG_EXECUTE_IF("slave_discard_gtid_0_x_1002", + { + if (mi->last_queued_gtid.server_id == 27697 && + mi->last_queued_gtid.seq_no == 1002) + { + DBUG_SET("-d,slave_discard_gtid_0_x_1002"); + goto skip_relay_logging; + } + }); +#endif mi->last_queued_gtid= event_gtid; mi->last_queued_gtid_standalone= (gtid_flag & Gtid_log_event::FL_STANDALONE) != 0; @@ -6222,6 +6274,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) ++mi->events_queued_since_last_gtid; inc_pos= event_len; + // ...} eof else_likely } break; /* @@ -6274,6 +6327,12 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) case XID_EVENT: DBUG_EXECUTE_IF("slave_discard_xid_for_gtid_0_x_1000", { + if (mi->last_queued_gtid.server_id == 27697 && + mi->last_queued_gtid.seq_no == 1000) + { + DBUG_SET("-d,slave_discard_xid_for_gtid_0_x_1000"); + goto skip_relay_logging; + } /* Inject an event group that is missing its XID commit event. */ if (mi->last_queued_gtid.domain_id == 0 && mi->last_queued_gtid.seq_no == 1000) @@ -6319,15 +6378,48 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) } };); - if (mi->using_gtid != Master_info::USE_GTID_NO && mi->gtid_event_seen) + if (mi->using_gtid != Master_info::USE_GTID_NO) { - if (unlikely(mi->gtid_reconnect_event_skip_count)) + if (likely(mi->gtid_event_seen)) { - --mi->gtid_reconnect_event_skip_count; - gtid_skip_enqueue= true; + if (unlikely(mi->gtid_reconnect_event_skip_count)) + { + if (!got_gtid_event && + mi->gtid_reconnect_event_skip_count == + mi->events_queued_since_last_gtid) + goto gtid_not_start; // the 1st re-sent must be gtid + + --mi->gtid_reconnect_event_skip_count; + gtid_skip_enqueue= true; + } + else if (likely(mi->events_queued_since_last_gtid)) + { + DBUG_ASSERT(!got_gtid_event); + + ++mi->events_queued_since_last_gtid; + } + else if (Log_event::is_group_event((Log_event_type) (uchar) + buf[EVENT_TYPE_OFFSET])) + { + goto gtid_not_start; // no first gtid event in this group + } + } + else if (Log_event::is_group_event((Log_event_type) (uchar) + buf[EVENT_TYPE_OFFSET])) + { + gtid_not_start: + + DBUG_ASSERT(!got_gtid_event); + + error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE; + sql_print_error("The current group of events starts with " + "a non-GTID %s event; " + "the last seen GTID is %u-%u-%llu", + Log_event::get_type_str((Log_event_type) (uchar) + buf[EVENT_TYPE_OFFSET]), + mi->last_queued_gtid); + goto err; } - else if (mi->events_queued_since_last_gtid) - ++mi->events_queued_since_last_gtid; } if (!is_compress_event) @@ -6500,15 +6592,35 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) Query_log_event::peek_is_commit_rollback(buf, event_len, checksum_alg)))))) { - /* - The whole of the current event group is queued. So in case of - reconnect we can start from after the current GTID. - */ - mi->gtid_current_pos.update(&mi->last_queued_gtid); - mi->events_queued_since_last_gtid= 0; + DBUG_ASSERT(mi->events_queued_since_last_gtid > 1); - /* Reset the domain_id_filter flag. */ - mi->domain_id_filter.reset_filter(); + if (unlikely(gtid_skip_enqueue)) + { + error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE; + sql_print_error("Recieved a group closing %s event " + "at %llu position in the group while there are " + "still %llu events to skip upon reconnecting; " + "the last seen GTID is %u-%u-%llu", + Log_event::get_type_str((Log_event_type) (uchar) + buf[EVENT_TYPE_OFFSET]), + (mi->events_queued_since_last_gtid - + mi->gtid_reconnect_event_skip_count), + mi->events_queued_since_last_gtid, + mi->last_queued_gtid); + goto err; + } + else + { + /* + The whole of the current event group is queued. So in case of + reconnect we can start from after the current GTID. + */ + mi->gtid_current_pos.update(&mi->last_queued_gtid); + mi->events_queued_since_last_gtid= 0; + + /* Reset the domain_id_filter flag. */ + mi->domain_id_filter.reset_filter(); + } } skip_relay_logging: From c5e68b6dcddbb6325c7684bfb4fba28e1c13e1ae Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Fri, 22 Apr 2022 07:53:16 -0700 Subject: [PATCH 09/28] MDEV-27212 Crash in Item_equal::sort on second execution of stored procedure This bug could cause a crash of the server at the second call of a stored procedure when it executed a query containing a mergeable derived table / view whose specification used another mergeable derived_table or view and a subquery with outer reference in the select list of the specification. Such queries could cause the same problem when they were executed for the second time in a prepared mode. The problem appeared due to a typo mistake in the legacy code of the function create_view_field() that prevented building Item_direct_view_ref wrapper for the mentioned outer reference at the second execution of the query and setting the depended_from field for the outer reference. Approved by Oleksandr Byelkin --- mysql-test/r/derived_view.result | 60 ++++++++++++++++++++++++++++++++ mysql-test/t/derived_view.test | 52 +++++++++++++++++++++++++++ sql/table.cc | 2 +- 3 files changed, 113 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/derived_view.result b/mysql-test/r/derived_view.result index 0c045e39271..31a92207442 100644 --- a/mysql-test/r/derived_view.result +++ b/mysql-test/r/derived_view.result @@ -3586,4 +3586,64 @@ f2 f3 DROP PROCEDURE p1; DROP VIEW v1,v2,v3; DROP TABLE t1; +# +# MDEV-27212: 2-nd execution of PS for select with embedded derived tables +# and correlated subquery in select list of outer derived +# +create table t1 ( id int, id2 int ) engine=myisam; +create table t2 ( x3 int , x1 int , x2 int, a1 int) engine=myisam; +insert into t1 values (3, 2), (4, 2), (3, 4); +insert into t2 values (1, 2, 2, 1), (1, 3, 3, 2), (2, 3, 3, 1); +prepare stmt from "select id from t1 +join +( select dt2.x1, +( select sum(a1) from t2 where t2.x1 = dt2.x1 ) m +from ( select x1 from t2 u where x3 = 1 ) dt2 +) dt +on t1.id = dt.x1 +where t1.id2 < dt.m"; +execute stmt; +id +3 +execute stmt; +id +3 +deallocate prepare stmt; +create procedure sp1() select id from t1 +join +( select dt2.x1, +( select sum(a1) from t2 where t2.x1 = dt2.x1 ) m +from ( select x1 from t2 u where x3 = 1 ) dt2 +) dt +on t1.id = dt.x1 +where t1.id2 < dt.m; +call sp1(); +id +3 +call sp1(); +id +3 +create view v2 as select x1 from t2 u where x3 = 1; +create view v as +select v2.x1, +( select sum(a1) from t2 where t2.x1 = v2.x1 ) m from v2; +prepare stmt from "select id from t1 join v on t1.id = v.x1 where t1.id2 < v.m"; +execute stmt; +id +3 +execute stmt; +id +3 +deallocate prepare stmt; +create procedure sp2() select id from t1 join v on t1.id = v.x1 where t1.id2 < v.m; +call sp2(); +id +3 +call sp2(); +id +3 +drop procedure sp1; +drop procedure sp2; +drop view v, v2; +drop table t1,t2; # End of 10.2 tests diff --git a/mysql-test/t/derived_view.test b/mysql-test/t/derived_view.test index 0f3d9b224ba..f36401271cc 100644 --- a/mysql-test/t/derived_view.test +++ b/mysql-test/t/derived_view.test @@ -2376,4 +2376,56 @@ DROP PROCEDURE p1; DROP VIEW v1,v2,v3; DROP TABLE t1; +--echo # +--echo # MDEV-27212: 2-nd execution of PS for select with embedded derived tables +--echo # and correlated subquery in select list of outer derived +--echo # +create table t1 ( id int, id2 int ) engine=myisam; +create table t2 ( x3 int , x1 int , x2 int, a1 int) engine=myisam; +insert into t1 values (3, 2), (4, 2), (3, 4); +insert into t2 values (1, 2, 2, 1), (1, 3, 3, 2), (2, 3, 3, 1); + +let $q= +select id from t1 + join + ( select dt2.x1, + ( select sum(a1) from t2 where t2.x1 = dt2.x1 ) m + from ( select x1 from t2 u where x3 = 1 ) dt2 + ) dt + on t1.id = dt.x1 +where t1.id2 < dt.m; + +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +deallocate prepare stmt; + +eval create procedure sp1() $q; +call sp1(); +call sp1(); + +create view v2 as select x1 from t2 u where x3 = 1; +create view v as +select v2.x1, + ( select sum(a1) from t2 where t2.x1 = v2.x1 ) m from v2; + +let $q= +select id from t1 join v on t1.id = v.x1 where t1.id2 < v.m; + +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +deallocate prepare stmt; + +eval create procedure sp2() $q; +call sp2(); +call sp2(); + +drop procedure sp1; +drop procedure sp2; + +drop view v, v2; + +drop table t1,t2; + --echo # End of 10.2 tests diff --git a/sql/table.cc b/sql/table.cc index a5f1a5d96cf..605a2e0ddbf 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -5984,7 +5984,7 @@ Item *Field_iterator_view::create_item(THD *thd) Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref, const char *name) { - bool save_wrapper= thd->lex->select_lex.no_wrap_view_item; + bool save_wrapper= thd->lex->current_select->no_wrap_view_item; Item *field= *field_ref; DBUG_ENTER("create_view_field"); From 9b7886bbf1eeb80136f0c208ef74fcc300514f50 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sun, 24 Apr 2022 15:16:07 +0200 Subject: [PATCH 10/28] MDEV-28403 ASAN heap-use-after-free in String::copy / get_field_default_value This reverts commit 5ba77222e9fe7af8ff403816b5338b18b342053c but keeps the test. A different fix for MDEV-21028 Server crashes in Query_arena::set_query_arena upon SELECT from view internal temporary tables should use THD as expr_area --- mysql-test/r/func_default.result | 21 +++++++++++++++++---- mysql-test/t/func_default.test | 11 +++++++++++ sql/sql_select.cc | 4 ++-- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/func_default.result b/mysql-test/r/func_default.result index 2b5c869ee66..8e4a647ff8e 100644 --- a/mysql-test/r/func_default.result +++ b/mysql-test/r/func_default.result @@ -167,8 +167,8 @@ create algorithm=temptable view v1 as select * from t1; create algorithm=merge view v2 as select * from t1; select default(a) = now() from v1; default(a) = now() -NULL -NULL +1 +1 select default(a) = now() from v2; default(a) = now() 1 @@ -191,16 +191,29 @@ default(v1) 2001-01-01 10:20:30 select default(v1) from (select v1 from t1 group by v1) dt; default(v1) -0000-00-00 00:00:00 +2001-01-01 10:20:30 drop table t1; create table t1 (a text default ''); create algorithm=temptable view v1 as select * from t1; insert into t1 values ('a'); select default(a) from v1; default(a) -NULL + drop view v1; drop table t1; # +# MDEV-28403 ASAN heap-use-after-free in String::copy / get_field_default_value +# +create table t (a blob default 'x'); +create view v as select * from t; +insert into t () values (); +update t set a = default; +select table_name,column_name,column_default from information_schema.columns where table_name = 'v'; +table_name v +column_name a +column_default 'x' +drop view v; +drop table t; +# # End of 10.2 tests # diff --git a/mysql-test/t/func_default.test b/mysql-test/t/func_default.test index f542a279478..53cd94e58c4 100644 --- a/mysql-test/t/func_default.test +++ b/mysql-test/t/func_default.test @@ -165,6 +165,17 @@ select default(a) from v1; drop view v1; drop table t1; +--echo # +--echo # MDEV-28403 ASAN heap-use-after-free in String::copy / get_field_default_value +--echo # +create table t (a blob default 'x'); +create view v as select * from t; +insert into t () values (); +update t set a = default; +query_vertical select table_name,column_name,column_default from information_schema.columns where table_name = 'v'; +drop view v; +drop table t; + --echo # --echo # End of 10.2 tests --echo # diff --git a/sql/sql_select.cc b/sql/sql_select.cc index d2186f60709..e9d81417ee6 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -16333,8 +16333,7 @@ Field *create_tmp_field_from_field(THD *thd, Field *org_field, table->s->db_create_options|= HA_OPTION_PACK_RECORD; else if (org_field->type() == FIELD_TYPE_DOUBLE) ((Field_double *) new_field)->not_fixed= TRUE; - new_field->vcol_info= new_field->default_value= - new_field->check_constraint= 0; + new_field->vcol_info= 0; new_field->cond_selectivity= 1.0; new_field->next_equal_field= NULL; new_field->option_list= NULL; @@ -16901,6 +16900,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List &fields, table->intersect_keys.init(); table->keys_in_use_for_query.init(); table->no_rows_with_nulls= param->force_not_null_cols; + table->expr_arena= thd; table->s= share; init_tmp_table_share(thd, share, "", 0, "(temporary)", tmpname); From 1a94d2fdb13a7a49471a2bb00a6bc6e596752aeb Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sat, 23 Apr 2022 11:44:45 +0200 Subject: [PATCH 11/28] cleanup: main.create_select test --- ...select_tmp.result => create_select.result} | 8 ++++- ...ate_select_tmp.test => create_select.test} | 34 +++++++++---------- 2 files changed, 24 insertions(+), 18 deletions(-) rename mysql-test/r/{create_select_tmp.result => create_select.result} (82%) rename mysql-test/t/{create_select_tmp.test => create_select.test} (82%) diff --git a/mysql-test/r/create_select_tmp.result b/mysql-test/r/create_select.result similarity index 82% rename from mysql-test/r/create_select_tmp.result rename to mysql-test/r/create_select.result index f499e539baf..ef400c1cad9 100644 --- a/mysql-test/r/create_select_tmp.result +++ b/mysql-test/r/create_select.result @@ -1,4 +1,7 @@ -drop table if exists t1, t2; +CALL mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT"); +# +# Testcase for BUG#4551 +# CREATE TABLE t1 ( a int ); INSERT INTO t1 VALUES (1),(2),(1); CREATE TABLE t2 ( PRIMARY KEY (a) ) ENGINE=INNODB SELECT a FROM t1; @@ -18,3 +21,6 @@ ERROR 23000: Duplicate entry '1' for key 'PRIMARY' select * from t2; ERROR 42S02: Table 'test.t2' doesn't exist drop table t1; +# +# End of 4.1 tests +# diff --git a/mysql-test/t/create_select_tmp.test b/mysql-test/t/create_select.test similarity index 82% rename from mysql-test/t/create_select_tmp.test rename to mysql-test/t/create_select.test index ef3315aed97..c97624eae7e 100644 --- a/mysql-test/t/create_select_tmp.test +++ b/mysql-test/t/create_select.test @@ -1,39 +1,39 @@ -# Testcase for BUG#4551 +# This does not work for RBR yet. +--source include/have_innodb.inc +--source include/have_binlog_format_mixed_or_statement.inc + +CALL mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT"); + +--echo # +--echo # Testcase for BUG#4551 +--echo # + # The bug was that when the table was TEMPORARY, it was not deleted if # the CREATE SELECT failed (the code intended too, but it actually # didn't). And as the CREATE TEMPORARY TABLE was not written to the # binlog if it was a transactional table, it resulted in an # inconsistency between binlog and the internal list of temp tables. -# This does not work for RBR yet. ---source include/have_binlog_format_mixed_or_statement.inc - ---disable_query_log -CALL mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT"); ---enable_query_log - --- source include/have_innodb.inc ---disable_warnings -drop table if exists t1, t2; ---enable_warnings CREATE TABLE t1 ( a int ); INSERT INTO t1 VALUES (1),(2),(1); --error ER_DUP_ENTRY CREATE TABLE t2 ( PRIMARY KEY (a) ) ENGINE=INNODB SELECT a FROM t1; ---error 1146 +--error ER_NO_SUCH_TABLE select * from t2; --error ER_DUP_ENTRY CREATE TEMPORARY TABLE t2 ( PRIMARY KEY (a) ) ENGINE=INNODB SELECT a FROM t1; ---error 1146 +--error ER_NO_SUCH_TABLE select * from t2; --error ER_DUP_ENTRY CREATE TABLE t2 ( PRIMARY KEY (a) ) ENGINE=MYISAM SELECT a FROM t1; ---error 1146 +--error ER_NO_SUCH_TABLE select * from t2; --error ER_DUP_ENTRY CREATE TEMPORARY TABLE t2 ( PRIMARY KEY (a) ) ENGINE=MYISAM SELECT a FROM t1; ---error 1146 +--error ER_NO_SUCH_TABLE select * from t2; drop table t1; -# End of 4.1 tests +--echo # +--echo # End of 4.1 tests +--echo # From 7753eae1c06044d998318fa3a1dbb98f5a310c57 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sat, 23 Apr 2022 12:21:08 +0200 Subject: [PATCH 12/28] MDEV-28393 Server crashes in TABLE::mark_default_fields_for_write test only --- mysql-test/r/create_select.result | 11 +++++++++++ mysql-test/t/create_select.test | 13 +++++++++++++ 2 files changed, 24 insertions(+) diff --git a/mysql-test/r/create_select.result b/mysql-test/r/create_select.result index ef400c1cad9..6c6ab77522d 100644 --- a/mysql-test/r/create_select.result +++ b/mysql-test/r/create_select.result @@ -24,3 +24,14 @@ drop table t1; # # End of 4.1 tests # +# +# MDEV-28393 Server crashes in TABLE::mark_default_fields_for_write +# +create table t1 (a int, b text not null default ''); +alter table t1 character set = utf8; +create table t2 select * from t1; +insert into t1 values (1,''); +drop table t1, t2; +# +# End of 10.2 tests +# diff --git a/mysql-test/t/create_select.test b/mysql-test/t/create_select.test index c97624eae7e..170bbba31d4 100644 --- a/mysql-test/t/create_select.test +++ b/mysql-test/t/create_select.test @@ -37,3 +37,16 @@ drop table t1; --echo # --echo # End of 4.1 tests --echo # + +--echo # +--echo # MDEV-28393 Server crashes in TABLE::mark_default_fields_for_write +--echo # +create table t1 (a int, b text not null default ''); +alter table t1 character set = utf8; +create table t2 select * from t1; +insert into t1 values (1,''); +drop table t1, t2; + +--echo # +--echo # End of 10.2 tests +--echo # From 3988dfff6234581e80bdeb768058b9ac47078681 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Thu, 21 Apr 2022 17:34:30 +0200 Subject: [PATCH 13/28] MDEV-6899 extra semicolon in show create event syntax to detect the end of SP definition correctly we need to know where the parser stopped parsing the SP. lip->get_cpp_ptr() shows the current parsing position, lip->get_cpp_tok_start() shows the start of the last parsed token. The actual value depends on whether the parser has performed a look-ahead. For example, in CREATE PROCEDURE ... BEGIN ... END ; the parser reads 'END' and knows that this ends the procedure definition, it does not need to read the next token for this. But in CREATE PROCEDURE ... SELECT 1 ; the parser cannot know that the procedure ends at '1'. It has to read the semicolon first (it could be '1 + 2' for example). In the first case, the "current parsing position" is after END, before the semicolon, in the second case it's *after* the semicolon. Note that SP definition in both cases ends before the semicolon. To be able to detect the end of SP deterministically, we need the parser to do the look-ahead always or never. The bug fix introduces a new parser token FORCE_LOOKAHEAD. Lexer never returns it, so this token can never match. But the parser cannot know it so it will have to perform a look-ahead to determine that the next token is not FORCE_LOOKAHEAD. This way we deterministically end SP parsing with a look-ahead. --- mysql-test/r/parser.result | 42 +++++++++++++++++++++++++ mysql-test/r/parser_not_embedded.result | 20 ++++++++++++ mysql-test/t/parser.test | 24 ++++++++++++++ mysql-test/t/parser_not_embedded.test | 15 +++++++++ sql/sp_head.cc | 2 +- sql/sql_yacc.yy | 18 ++++++----- 6 files changed, 113 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/parser.result b/mysql-test/r/parser.result index 9e091688e1d..e8bf9d12a33 100644 --- a/mysql-test/r/parser.result +++ b/mysql-test/r/parser.result @@ -1367,5 +1367,47 @@ SELECT tmp 1.e.test FROM scientific_notation AS tmp; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '1.e.test FROM scientific_notation AS tmp' at line 1 DROP TABLE scientific_notation; # +# MDEV-6899 extra semicolon in show create event syntax +# +set timestamp=unix_timestamp('2020-10-10 5:5:5'); +create table t1 (a int); +create trigger a before insert on t1 for each row set @a:=1;select 2$ +2 +2 +show create trigger a; +Trigger a +sql_mode STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION +SQL Original Statement CREATE DEFINER=`root`@`localhost` trigger a before insert on t1 for each row set @a:=1 +character_set_client latin1 +collation_connection latin1_swedish_ci +Database Collation latin1_swedish_ci +Created 2020-10-10 05:05:05.00 +drop table t1; +create procedure a() select 1;select 2$ +2 +2 +show create procedure a; +Procedure a +sql_mode STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION +Create Procedure CREATE DEFINER=`root`@`localhost` PROCEDURE `a`() +select 1 +character_set_client latin1 +collation_connection latin1_swedish_ci +Database Collation latin1_swedish_ci +drop procedure a; +create function a() returns int return 1;select 2$ +2 +2 +show create function a; +Function a +sql_mode STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION +Create Function CREATE DEFINER=`root`@`localhost` FUNCTION `a`() RETURNS int(11) +return 1 +character_set_client latin1 +collation_connection latin1_swedish_ci +Database Collation latin1_swedish_ci +drop function a; +set timestamp=default; +# # End of 10.2 tests # diff --git a/mysql-test/r/parser_not_embedded.result b/mysql-test/r/parser_not_embedded.result index 25349e51577..2147e25b3b1 100644 --- a/mysql-test/r/parser_not_embedded.result +++ b/mysql-test/r/parser_not_embedded.result @@ -102,3 +102,23 @@ ROLLBACK AND NO CHAIN NO RELEASE; # # End of 5.5 tests # +# +# MDEV-6899 extra semicolon in show create event syntax +# +set timestamp=unix_timestamp('2020-10-10 5:5:5'); +create event a on schedule every 1 day do set @a:=1;select 2$ +2 +2 +show create event a; +Event a +sql_mode STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION +time_zone SYSTEM +Create Event CREATE DEFINER=`root`@`localhost` EVENT `a` ON SCHEDULE EVERY 1 DAY STARTS '2020-10-10 05:05:05' ON COMPLETION NOT PRESERVE ENABLE DO set @a:=1 +character_set_client latin1 +collation_connection latin1_swedish_ci +Database Collation latin1_swedish_ci +drop event a; +set timestamp=default; +# +# End of 10.2 tests +# diff --git a/mysql-test/t/parser.test b/mysql-test/t/parser.test index efb936d8ea4..095d274724b 100644 --- a/mysql-test/t/parser.test +++ b/mysql-test/t/parser.test @@ -1408,6 +1408,30 @@ SELECT tmp 1.e.test FROM scientific_notation AS tmp; DROP TABLE scientific_notation; +--echo # +--echo # MDEV-6899 extra semicolon in show create event syntax +--echo # +set timestamp=unix_timestamp('2020-10-10 5:5:5'); +create table t1 (a int); +delimiter $; +create trigger a before insert on t1 for each row set @a:=1;select 2$ +delimiter ;$ +query_vertical show create trigger a; +drop table t1; + +delimiter $; +create procedure a() select 1;select 2$ +delimiter ;$ +query_vertical show create procedure a; +drop procedure a; + +delimiter $; +create function a() returns int return 1;select 2$ +delimiter ;$ +query_vertical show create function a; +drop function a; +set timestamp=default; + --echo # --echo # End of 10.2 tests --echo # diff --git a/mysql-test/t/parser_not_embedded.test b/mysql-test/t/parser_not_embedded.test index 3af1260f4ad..270573ece93 100644 --- a/mysql-test/t/parser_not_embedded.test +++ b/mysql-test/t/parser_not_embedded.test @@ -99,3 +99,18 @@ ROLLBACK AND NO CHAIN NO RELEASE; --echo # --echo # End of 5.5 tests --echo # + +--echo # +--echo # MDEV-6899 extra semicolon in show create event syntax +--echo # +set timestamp=unix_timestamp('2020-10-10 5:5:5'); +delimiter $; +create event a on schedule every 1 day do set @a:=1;select 2$ +delimiter ;$ +query_vertical show create event a; +drop event a; +set timestamp=default; + +--echo # +--echo # End of 10.2 tests +--echo # diff --git a/sql/sp_head.cc b/sql/sp_head.cc index cb5348cd110..66df02d99fb 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -700,7 +700,7 @@ void sp_head::set_stmt_end(THD *thd) { Lex_input_stream *lip= & thd->m_parser_state->m_lip; /* shortcut */ - const char *end_ptr= lip->get_cpp_ptr(); /* shortcut */ + const char *end_ptr= lip->get_cpp_tok_start(); /* shortcut */ uint not_used; /* Make the string of parameters. */ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 528d4ee4bdc..bade0bd3752 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1035,10 +1035,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %parse-param { THD *thd } %lex-param { THD *thd } /* - Currently there are 98 shift/reduce conflicts. + Currently there are 119 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 115 +%expect 119 /* Comments for TOKENS. @@ -1258,6 +1258,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %token FOLLOWS_SYM /* MYSQL trigger*/ %token FOLLOWING_SYM /* SQL-2011-N */ %token FORCE_SYM +%token FORCE_LOOKAHEAD /* INTERNAL never returned by the lexer */ %token FOREIGN /* SQL-2003-R */ %token FOR_SYM /* SQL-2003-R */ %token FORMAT_SYM @@ -2710,6 +2711,9 @@ server_option: } ; +/* this rule is used to force look-ahead in the parser */ +force_lookahead: {} | FORCE_LOOKAHEAD {} ; + event_tail: remember_name EVENT_SYM opt_if_not_exists sp_name { @@ -2854,7 +2858,7 @@ ev_sql_stmt: lex->sp_chistics.suid= SP_IS_SUID; //always the definer! lex->sphead->set_body_start(thd, lip->get_cpp_ptr()); } - sp_proc_stmt + sp_proc_stmt force_lookahead { LEX *lex= thd->lex; @@ -16838,8 +16842,8 @@ trigger_tail: lex->sphead->set_body_start(thd, lip->get_cpp_tok_start()); } - sp_proc_stmt /* $20 */ - { /* $21 */ + sp_proc_stmt force_lookahead + { LEX *lex= Lex; sp_head *sp= lex->sphead; @@ -16939,7 +16943,7 @@ sf_tail: lex->sphead->set_body_start(thd, lip->get_cpp_tok_start()); } - sp_proc_stmt_in_returns_clause /* $15 */ + sp_proc_stmt_in_returns_clause /* $15 */ force_lookahead { LEX *lex= thd->lex; sp_head *sp= lex->sphead; @@ -17020,7 +17024,7 @@ sp_tail: { Lex->sphead->set_body_start(thd, YYLIP->get_cpp_tok_start()); } - sp_proc_stmt + sp_proc_stmt force_lookahead { LEX *lex= Lex; sp_head *sp= lex->sphead; From 25ccf8f6dcfa23ba07a46efe5e85787689571b17 Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Mon, 25 Apr 2022 15:47:33 +0200 Subject: [PATCH 14/28] New CC version --- libmariadb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libmariadb b/libmariadb index f6c3d9fd2af..f33017c19a5 160000 --- a/libmariadb +++ b/libmariadb @@ -1 +1 @@ -Subproject commit f6c3d9fd2af5d17db64cc996574aa312efd70fcf +Subproject commit f33017c19a5b02394de5d0816513d2e2c9d1767c From 9b2d36660bbb4f93c6b9e0761c91d57d47f59196 Mon Sep 17 00:00:00 2001 From: Rucha Deodhar Date: Thu, 27 Jan 2022 01:03:52 +0530 Subject: [PATCH 15/28] MDEV-20207: Assertion ! is_set() failed in Diagnostics_area::set_eof_status upon HANDLER READ Analysis: The error state is not stored while checking condition and key name. Fix: Return true while checking condition and key name if error is reported because geometry object can't be created from the data in the index value for HANDLER READ. --- mysql-test/suite/handler/aria.result | 16 ++++++++++++++++ mysql-test/suite/handler/aria.test | 20 ++++++++++++++++++++ mysql-test/suite/handler/innodb.result | 16 ++++++++++++++++ mysql-test/suite/handler/innodb.test | 20 ++++++++++++++++++++ mysql-test/suite/handler/interface.result | 16 ++++++++++++++++ mysql-test/suite/handler/interface.test | 20 ++++++++++++++++++++ mysql-test/suite/handler/myisam.result | 14 ++++++++++++++ mysql-test/suite/handler/myisam.test | 20 ++++++++++++++++++++ sql/sql_handler.cc | 4 +++- 9 files changed, 145 insertions(+), 1 deletion(-) diff --git a/mysql-test/suite/handler/aria.result b/mysql-test/suite/handler/aria.result index 6b02ac9b085..433a8a7008e 100644 --- a/mysql-test/suite/handler/aria.result +++ b/mysql-test/suite/handler/aria.result @@ -1845,3 +1845,19 @@ a b HANDLER t1 CLOSE; DROP TABLE t1; End of 5.1 tests +# +# 10.2 Test +# +# MDEV-20207: Assertion `! is_set()' failed in +# Diagnostics_area::set_eof_status upon HANDLER READ +# +DROP TABLE IF EXISTS t1; +Warnings: +Note 1051 Unknown table 'test.t1' +CREATE TABLE t1 (a POINT, KEY(a)); +HANDLER t1 OPEN h; +HANDLER h READ a = (0); +ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field +HANDLER h CLOSE; +DROP TABLE t1; +# End of 10.2 Test diff --git a/mysql-test/suite/handler/aria.test b/mysql-test/suite/handler/aria.test index 1913d2b791c..912a9e89721 100644 --- a/mysql-test/suite/handler/aria.test +++ b/mysql-test/suite/handler/aria.test @@ -80,3 +80,23 @@ HANDLER t1 CLOSE; DROP TABLE t1; --echo End of 5.1 tests + +--echo # +--echo # 10.2 Test +--echo # +--echo # MDEV-20207: Assertion `! is_set()' failed in +--echo # Diagnostics_area::set_eof_status upon HANDLER READ +--echo # + +DROP TABLE IF EXISTS t1; + +CREATE TABLE t1 (a POINT, KEY(a)); +HANDLER t1 OPEN h; + +--error ER_CANT_CREATE_GEOMETRY_OBJECT +HANDLER h READ a = (0); + +HANDLER h CLOSE; +DROP TABLE t1; + +--echo # End of 10.2 Test diff --git a/mysql-test/suite/handler/innodb.result b/mysql-test/suite/handler/innodb.result index 80e8ed679a9..7f9995c9e84 100644 --- a/mysql-test/suite/handler/innodb.result +++ b/mysql-test/suite/handler/innodb.result @@ -1748,3 +1748,19 @@ HANDLER t1 READ `PRIMARY` PREV; f1 f2 3 3 DROP TABLE t1; +# +# 10.2 Test +# +# MDEV-20207: Assertion `! is_set()' failed in +# Diagnostics_area::set_eof_status upon HANDLER READ +# +DROP TABLE IF EXISTS t1; +Warnings: +Note 1051 Unknown table 'test.t1' +CREATE TABLE t1 (a POINT, KEY(a)); +HANDLER t1 OPEN h; +HANDLER h READ a = (0); +ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field +HANDLER h CLOSE; +DROP TABLE t1; +# End of 10.2 Test diff --git a/mysql-test/suite/handler/innodb.test b/mysql-test/suite/handler/innodb.test index d752da7dc31..74b0a650c25 100644 --- a/mysql-test/suite/handler/innodb.test +++ b/mysql-test/suite/handler/innodb.test @@ -31,3 +31,23 @@ HANDLER t1 OPEN; HANDLER t1 READ FIRST WHERE f2 <= 1; HANDLER t1 READ `PRIMARY` PREV; DROP TABLE t1; + +--echo # +--echo # 10.2 Test +--echo # +--echo # MDEV-20207: Assertion `! is_set()' failed in +--echo # Diagnostics_area::set_eof_status upon HANDLER READ +--echo # + +DROP TABLE IF EXISTS t1; + +CREATE TABLE t1 (a POINT, KEY(a)); +HANDLER t1 OPEN h; + +--error ER_CANT_CREATE_GEOMETRY_OBJECT +HANDLER h READ a = (0); + +HANDLER h CLOSE; +DROP TABLE t1; + +--echo # End of 10.2 Test diff --git a/mysql-test/suite/handler/interface.result b/mysql-test/suite/handler/interface.result index a4ac32c16b4..1b0d08635f9 100644 --- a/mysql-test/suite/handler/interface.result +++ b/mysql-test/suite/handler/interface.result @@ -312,3 +312,19 @@ Note 1050 Table 'v' already exists handler v read next; ERROR 42S02: Unknown table 'v' in HANDLER drop view v; +# +# 10.2 Test +# +# MDEV-20207: Assertion `! is_set()' failed in +# Diagnostics_area::set_eof_status upon HANDLER READ +# +DROP TABLE IF EXISTS t1; +Warnings: +Note 1051 Unknown table 'test.t1' +CREATE TABLE t1 (a POINT, KEY(a)); +HANDLER t1 OPEN h; +HANDLER h READ a = (0); +ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field +HANDLER h CLOSE; +DROP TABLE t1; +# End of 10.2 Test diff --git a/mysql-test/suite/handler/interface.test b/mysql-test/suite/handler/interface.test index 2f576c9b291..8a90ffc0cf7 100644 --- a/mysql-test/suite/handler/interface.test +++ b/mysql-test/suite/handler/interface.test @@ -354,3 +354,23 @@ execute stmt; --error ER_UNKNOWN_TABLE handler v read next; drop view v; + +--echo # +--echo # 10.2 Test +--echo # +--echo # MDEV-20207: Assertion `! is_set()' failed in +--echo # Diagnostics_area::set_eof_status upon HANDLER READ +--echo # + +DROP TABLE IF EXISTS t1; + +CREATE TABLE t1 (a POINT, KEY(a)); +HANDLER t1 OPEN h; + +--error ER_CANT_CREATE_GEOMETRY_OBJECT +HANDLER h READ a = (0); + +HANDLER h CLOSE; +DROP TABLE t1; + +--echo # End of 10.2 Test diff --git a/mysql-test/suite/handler/myisam.result b/mysql-test/suite/handler/myisam.result index 90e1767a1f3..c444027d062 100644 --- a/mysql-test/suite/handler/myisam.result +++ b/mysql-test/suite/handler/myisam.result @@ -1931,3 +1931,17 @@ test.t1 preload_keys status OK HANDLER t1 READ FIRST; ERROR 42S02: Unknown table 't1' in HANDLER End of 5.1 tests +# +# 10.2 Test +# +# MDEV-20207: Assertion `! is_set()' failed in +# Diagnostics_area::set_eof_status upon HANDLER READ +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (a POINT, KEY(a)); +HANDLER t1 OPEN h; +HANDLER h READ a = (0); +ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field +HANDLER h CLOSE; +DROP TABLE t1; +# End of 10.2 Test diff --git a/mysql-test/suite/handler/myisam.test b/mysql-test/suite/handler/myisam.test index a2d87630aef..2fce8548322 100644 --- a/mysql-test/suite/handler/myisam.test +++ b/mysql-test/suite/handler/myisam.test @@ -169,3 +169,23 @@ HANDLER t1 READ FIRST; --echo End of 5.1 tests + +--echo # +--echo # 10.2 Test +--echo # +--echo # MDEV-20207: Assertion `! is_set()' failed in +--echo # Diagnostics_area::set_eof_status upon HANDLER READ +--echo # + +DROP TABLE IF EXISTS t1; + +CREATE TABLE t1 (a POINT, KEY(a)); +HANDLER t1 OPEN h; + +--error ER_CANT_CREATE_GEOMETRY_OBJECT +HANDLER h READ a = (0); + +HANDLER h CLOSE; +DROP TABLE t1; + +--echo # End of 10.2 Test diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index ec3756eceba..132556f57b8 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -613,8 +613,10 @@ mysql_ha_fix_cond_and_key(SQL_HANDLER *handler, if (!in_prepare) { MY_BITMAP *old_map= dbug_tmp_use_all_columns(table, &table->write_set); - (void) item->save_in_field(key_part->field, 1); + int res= item->save_in_field(key_part->field, 1); dbug_tmp_restore_column_map(&table->write_set, old_map); + if (res) + return 1; } key_len+= key_part->store_length; keypart_map= (keypart_map << 1) | 1; From 5100b20b15edd93200f34a79d25f1b14e46a677e Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Fri, 22 Apr 2022 20:26:14 +0300 Subject: [PATCH 16/28] MDEV-26047: MariaDB server crash at Item_subselect::init_expr_cache_tracker The cause of crash: remove_redundant_subquery_clauses() removes redundant item expressions. The primary goal of this is to remove the subquery items. The removal process unlinks the subquery from SELECT_LEX tree, but does not remove it from SELECT_LEX:::ref_pointer_array or from JOIN::all_fields. Then, setup_subquery_caches() tries to wrap the subquery item in an expression cache, which fails, the first reason for failure being that the item doesn't have a query plan. Solution: do not wrap eliminated items with expression cache. (also added an assert to check that we do not attempt to execute them). This may look like an incomplete fix: why don't we remove any mention of eliminated item everywhere? The difficulties here are: * items can be "un-removed" (see set_fake_select_as_master_processor) * it's difficult to remove an element from ref_pointer_array: Item_ref objects refer to elements of that array, so one can't shift elements in it. Replacing eliminated subselect with a dummy Item doesn't look like a good idea, either. --- mysql-test/r/subselect_innodb.result | 32 ++++++++++++++++++++++++++++ mysql-test/t/subselect_innodb.test | 28 ++++++++++++++++++++++++ sql/item_subselect.cc | 11 ++++++++++ 3 files changed, 71 insertions(+) diff --git a/mysql-test/r/subselect_innodb.result b/mysql-test/r/subselect_innodb.result index 4eaea099451..4796245f8e3 100644 --- a/mysql-test/r/subselect_innodb.result +++ b/mysql-test/r/subselect_innodb.result @@ -629,3 +629,35 @@ a b 2019-03-10 02:55:05 2019-03-10 02:55:05 DROP TABLE t1,t2; set character_set_connection=@save_character_set_connection; +# +# MDEV-26047: MariaDB server crash at Item_subselect::init_expr_cache_tracker +# +CREATE TABLE t1 (a int) engine=innodb; +SELECT 1 IN ( +SELECT NULL +FROM t1 +WHERE +a IS NOT NULL +GROUP BY +(SELECT NULL from dual WHERE a = 1) +); +1 IN ( +SELECT NULL +FROM t1 +WHERE +a IS NOT NULL +GROUP BY +(SELECT NULL from dual WHERE a = 1) +) +0 +drop table t1; +# Testcase from MDEV-26164 +create table t1(a int); +# Disable the warning as it includes current time and changes for every test run. +select 1 from t1 where not exists +( +select 1 from t1 where binary current_time() +group by (select a),(select 1) +); +1 +drop table t1; diff --git a/mysql-test/t/subselect_innodb.test b/mysql-test/t/subselect_innodb.test index 2c117fe00d6..f42ac514d53 100644 --- a/mysql-test/t/subselect_innodb.test +++ b/mysql-test/t/subselect_innodb.test @@ -627,3 +627,31 @@ SELECT * FROM t1 WHERE (SELECT 1,CONCAT(a) FROM t1) = (SELECT 1,CONCAT(a) FROM t DROP TABLE t1,t2; set character_set_connection=@save_character_set_connection; + +--echo # +--echo # MDEV-26047: MariaDB server crash at Item_subselect::init_expr_cache_tracker +--echo # +CREATE TABLE t1 (a int) engine=innodb; + +SELECT 1 IN ( + SELECT NULL + FROM t1 + WHERE + a IS NOT NULL + GROUP BY + (SELECT NULL from dual WHERE a = 1) +); +drop table t1; + +--echo # Testcase from MDEV-26164 +create table t1(a int); +--echo # Disable the warning as it includes current time and changes for every test run. +--disable_warnings +select 1 from t1 where not exists +( + select 1 from t1 where binary current_time() + group by (select a),(select 1) +); +--enable_warnings +drop table t1; + diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 7033d38c07b..de9b5dc8438 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -752,6 +752,7 @@ bool Item_subselect::exec() DBUG_ENTER("Item_subselect::exec"); DBUG_ASSERT(fixed); + DBUG_ASSERT(!eliminated); /* Do not execute subselect in case of a fatal error @@ -1313,6 +1314,16 @@ Item* Item_singlerow_subselect::expr_cache_insert_transformer(THD *tmp_thd, DBUG_ASSERT(thd == tmp_thd); + /* + Do not create subquery cache if the subquery was eliminated. + The optimizer may eliminate subquery items (see + eliminate_subselect_processor). However it does not update + all query's data structures, so the eliminated item may be + still reachable. + */ + if (eliminated) + DBUG_RETURN(this); + if (expr_cache) DBUG_RETURN(expr_cache); From 945245aea4baf5399470ec0cff5d5d51c36a95d6 Mon Sep 17 00:00:00 2001 From: Andrei Date: Tue, 26 Apr 2022 17:03:32 +0300 Subject: [PATCH 17/28] MDEV-27697. Two affected tests fixed. A result file is updated in one case and former error simulation got refined. --- mysql-test/suite/binlog_encryption/rpl_parallel.result | 1 + mysql-test/suite/rpl/r/rpl_parallel_temptable.result | 5 +++++ mysql-test/suite/rpl/t/rpl_parallel_temptable.test | 8 ++++++++ sql/slave.cc | 6 ++++-- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/mysql-test/suite/binlog_encryption/rpl_parallel.result b/mysql-test/suite/binlog_encryption/rpl_parallel.result index 12e5455c6c1..79efad1a27a 100644 --- a/mysql-test/suite/binlog_encryption/rpl_parallel.result +++ b/mysql-test/suite/binlog_encryption/rpl_parallel.result @@ -1379,6 +1379,7 @@ include/stop_slave.inc SET GLOBAL slave_parallel_threads=1; SET @old_dbug= @@GLOBAL.debug_dbug; SET GLOBAL debug_dbug="+d,slave_discard_xid_for_gtid_0_x_1000"; +CALL mtr.add_suppression("Unexpected break of being relay-logged GTID"); connection server_1; INSERT INTO t2 VALUES (101); INSERT INTO t2 VALUES (102); diff --git a/mysql-test/suite/rpl/r/rpl_parallel_temptable.result b/mysql-test/suite/rpl/r/rpl_parallel_temptable.result index 52efed22541..1a1c12f836d 100644 --- a/mysql-test/suite/rpl/r/rpl_parallel_temptable.result +++ b/mysql-test/suite/rpl/r/rpl_parallel_temptable.result @@ -132,8 +132,13 @@ connection server_1; INSERT INTO t1 VALUES (0, 1); include/save_master_gtid.inc connection server_2; +set @@sql_log_bin=0; +call mtr.add_suppression("Unexpected break of being relay-logged GTID 1-1-32 event group by the current GTID event 0-1-4"); +set @@sql_log_bin=1; +set @@global.debug_dbug="+d,slave_discard_xid_for_gtid_0_x_1000"; include/start_slave.inc include/sync_with_master_gtid.inc +set @@global.debug_dbug=""; SELECT * FROM t1 ORDER BY a; a b 0 1 diff --git a/mysql-test/suite/rpl/t/rpl_parallel_temptable.test b/mysql-test/suite/rpl/t/rpl_parallel_temptable.test index 04165ee4752..d444793a99d 100644 --- a/mysql-test/suite/rpl/t/rpl_parallel_temptable.test +++ b/mysql-test/suite/rpl/t/rpl_parallel_temptable.test @@ -201,9 +201,17 @@ INSERT INTO t1 VALUES (0, 1); # execution of format_description event will not wait infinitely # for a commit of the incomplete group that never happens. +# Apart from the suppression, MDEV-27697 refinement to the original test needs +# an allowance to one time accept malformed event group. +set @@sql_log_bin=0; +call mtr.add_suppression("Unexpected break of being relay-logged GTID 1-1-32 event group by the current GTID event 0-1-4"); +set @@sql_log_bin=1; +set @@global.debug_dbug="+d,slave_discard_xid_for_gtid_0_x_1000"; + --source include/start_slave.inc #--sync_with_master --source include/sync_with_master_gtid.inc +set @@global.debug_dbug=""; SELECT * FROM t1 ORDER BY a; SHOW STATUS LIKE 'Slave_open_temp_tables'; diff --git a/sql/slave.cc b/sql/slave.cc index 31e50753c9e..0d50e8b0c61 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -6223,8 +6223,10 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) DBUG_EXECUTE_IF("slave_discard_xid_for_gtid_0_x_1000", { /* Inject an event group that is missing its XID commit event. */ - if (mi->last_queued_gtid.domain_id == 0 && - mi->last_queued_gtid.seq_no == 1000) + if ((mi->last_queued_gtid.domain_id == 0 && + mi->last_queued_gtid.seq_no == 1000) || + (mi->last_queued_gtid.domain_id == 1 && + mi->last_queued_gtid.seq_no == 32)) { sql_print_warning( "Unexpected break of being relay-logged GTID %u-%u-%llu " From eca207c46293bc72dd8d0d5622153fab4d3fccf1 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Fri, 22 Apr 2022 01:32:55 +0400 Subject: [PATCH 18/28] MDEV-25317 Assertion `scale <= precision' failed in decimal_bin_size And Assertion `scale >= 0 && precision > 0 && scale <= precision' failed in decimal_bin_size_inline/decimal_bin_size. Precision should be kept below DECIMAL_MAX_SCALE for computations. It can be bigger in Item_decimal. I'd fix this too but it changes the existing behaviour so problemmatic to ix. --- mysql-test/r/func_time.result | 6 ++++++ mysql-test/r/type_newdecimal.result | 24 ++++++++++++++++++++++++ mysql-test/t/func_time.test | 4 ++++ mysql-test/t/type_newdecimal.test | 19 +++++++++++++++++++ sql/field.cc | 28 ++++++++++++++++------------ sql/field.h | 2 ++ sql/item_func.cc | 6 ++++-- sql/item_sum.cc | 6 ++++-- sql/sql_type.cc | 1 - 9 files changed, 79 insertions(+), 17 deletions(-) diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result index a82cf3c19c2..563212cb756 100644 --- a/mysql-test/r/func_time.result +++ b/mysql-test/r/func_time.result @@ -989,6 +989,12 @@ ADDTIME('916:40:00', '416:40:00') Warnings: Warning 1292 Truncated incorrect time value: '916:40:00' Warning 1292 Truncated incorrect time value: '1255:39:59.999999' +SELECT ADDTIME(20010101,1e0), ADDTIME(20010101,1.1e0); +ADDTIME(20010101,1e0) ADDTIME(20010101,1.1e0) +2001-01-01 00:00:01 2001-01-01 00:00:01.100000 +SELECT ADDTIME(ADDTIME(20010101,1e0), 0); +ADDTIME(ADDTIME(20010101,1e0), 0) +2001-01-01 00:00:01 SELECT SUBTIME('916:40:00', '416:40:00'); SUBTIME('916:40:00', '416:40:00') 422:19:59.999999 diff --git a/mysql-test/r/type_newdecimal.result b/mysql-test/r/type_newdecimal.result index 0fbf64a4b18..0df2b59d832 100644 --- a/mysql-test/r/type_newdecimal.result +++ b/mysql-test/r/type_newdecimal.result @@ -2456,5 +2456,29 @@ t1 CREATE TABLE `t1` ( ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; # +# MDEV-25317 Assertion `scale <= precision' failed in +# decimal_bin_size And Assertion `scale >= 0 && precision > 0 && scale <= precision' +# failed in decimal_bin_size_inline/decimal_bin_size. +# +SELECT AVG(DISTINCT 0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001); +AVG(DISTINCT 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0.00000000000000000000000000000000000000 +CREATE TABLE t1 AS SELECT NULL AS v1; +SELECT 1 FROM t1 GROUP BY v1 ORDER BY AVG ( from_unixtime ( '' ) ) ; +1 +1 +Warnings: +Warning 1292 Truncated incorrect DECIMAL value: '' +DROP TABLE t1; +SELECT SUM(DISTINCT 0.000000000000000000000000000000000000001); +SUM(DISTINCT 0.000000000000000000000000000000000000001) +0.00000000000000000000000000000000000000 +CREATE TABLE t1 AS SELECT 1.000000000000000000000000000000000 AS a; +ALTER TABLE t1 ADD COLUMN b INT; +SELECT ROUND (a,b) AS c FROM t1 ORDER BY c; +c +NULL +DROP TABLE t1; +# # End of 10.2 tests # diff --git a/mysql-test/t/func_time.test b/mysql-test/t/func_time.test index 22729b4300b..7f35e658d1b 100644 --- a/mysql-test/t/func_time.test +++ b/mysql-test/t/func_time.test @@ -513,6 +513,10 @@ SELECT TIME_TO_SEC('916:40:00'); SELECT ADDTIME('500:00:00', '416:40:00'); SELECT ADDTIME('916:40:00', '416:40:00'); +# check if ADDTIME() handles NOT_FIXED_DEC correctly +SELECT ADDTIME(20010101,1e0), ADDTIME(20010101,1.1e0); +SELECT ADDTIME(ADDTIME(20010101,1e0), 0); + # check if SUBTIME() handles out-of-range values correctly SELECT SUBTIME('916:40:00', '416:40:00'); SELECT SUBTIME('-916:40:00', '416:40:00'); diff --git a/mysql-test/t/type_newdecimal.test b/mysql-test/t/type_newdecimal.test index e5d903e95cd..a5cb15e1a2c 100644 --- a/mysql-test/t/type_newdecimal.test +++ b/mysql-test/t/type_newdecimal.test @@ -1894,6 +1894,25 @@ show create table t1; drop table t1; +--echo # +--echo # MDEV-25317 Assertion `scale <= precision' failed in +--echo # decimal_bin_size And Assertion `scale >= 0 && precision > 0 && scale <= precision' +--echo # failed in decimal_bin_size_inline/decimal_bin_size. +--echo # + +SELECT AVG(DISTINCT 0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001); + +CREATE TABLE t1 AS SELECT NULL AS v1; +SELECT 1 FROM t1 GROUP BY v1 ORDER BY AVG ( from_unixtime ( '' ) ) ; +DROP TABLE t1; + +SELECT SUM(DISTINCT 0.000000000000000000000000000000000000001); + +CREATE TABLE t1 AS SELECT 1.000000000000000000000000000000000 AS a; +ALTER TABLE t1 ADD COLUMN b INT; +SELECT ROUND (a,b) AS c FROM t1 ORDER BY c; +DROP TABLE t1; + --echo # --echo # End of 10.2 tests --echo # diff --git a/sql/field.cc b/sql/field.cc index 74317a4d70b..07212a53b45 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -3103,6 +3103,15 @@ Field *Field_decimal::make_new_field(MEM_ROOT *root, TABLE *new_table, } +void Field_new_decimal::set_and_validate_prec(uint32 len_arg, + uint8 dec_arg, bool unsigned_arg) +{ + precision= my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg); + set_if_smaller(precision, DECIMAL_MAX_PRECISION); + bin_size= my_decimal_get_binary_size(precision, dec); +} + + /**************************************************************************** ** Field_new_decimal ****************************************************************************/ @@ -3115,13 +3124,10 @@ Field_new_decimal::Field_new_decimal(uchar *ptr_arg, uint8 dec_arg,bool zero_arg, bool unsigned_arg) :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, dec_arg, zero_arg, unsigned_arg) + unireg_check_arg, field_name_arg, + MY_MIN(dec_arg, DECIMAL_MAX_SCALE), zero_arg, unsigned_arg) { - precision= my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg); - set_if_smaller(precision, DECIMAL_MAX_PRECISION); - DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION) && - (dec <= DECIMAL_MAX_SCALE)); - bin_size= my_decimal_get_binary_size(precision, dec); + set_and_validate_prec(len_arg, dec_arg, unsigned_arg); } @@ -3132,13 +3138,11 @@ Field_new_decimal::Field_new_decimal(uint32 len_arg, bool unsigned_arg) :Field_num((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, 0, - NONE, name, dec_arg, 0, unsigned_arg) + NONE, name, + MY_MIN(dec_arg, DECIMAL_MAX_SCALE), 0, unsigned_arg) { - precision= my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg); - set_if_smaller(precision, DECIMAL_MAX_PRECISION); - DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION) && - (dec <= DECIMAL_MAX_SCALE)); - bin_size= my_decimal_get_binary_size(precision, dec); + DBUG_ASSERT(dec <= DECIMAL_MAX_SCALE); + set_and_validate_prec(len_arg, dec_arg, unsigned_arg); } diff --git a/sql/field.h b/sql/field.h index 78b6dcb516b..ad01b2ff642 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1913,6 +1913,8 @@ public: class Field_new_decimal :public Field_num { private: int do_save_field_metadata(uchar *first_byte); + void set_and_validate_prec(uint32 len_arg, + uint8 dec_arg, bool unsigned_arg); public: /* The maximum number of decimal digits can be stored */ uint precision; diff --git a/sql/item_func.cc b/sql/item_func.cc index 7a7eaf9dc23..4e28e8e9528 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2453,14 +2453,16 @@ bool Item_func_round::fix_length_and_dec() if (!args[1]->const_item()) { decimals= args[0]->decimals; - max_length= float_length(decimals); if (args[0]->result_type() == DECIMAL_RESULT) { - max_length++; + max_length= args[0]->max_length; set_handler_by_result_type(DECIMAL_RESULT); } else + { + max_length= float_length(decimals); set_handler_by_result_type(REAL_RESULT); + } return FALSE; } diff --git a/sql/item_sum.cc b/sql/item_sum.cc index dd65f04a652..77224d5321d 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1346,6 +1346,8 @@ bool Item_sum_sum::fix_length_and_dec() { /* SUM result can't be longer than length(arg) + length(MAX_ROWS) */ int precision= args[0]->decimal_precision() + DECIMAL_LONGLONG_DIGITS; + decimals= MY_MIN(decimals, DECIMAL_MAX_SCALE); + precision= MY_MIN(precision, DECIMAL_MAX_PRECISION); max_length= my_decimal_precision_to_length_no_truncation(precision, decimals, unsigned_flag); @@ -1673,12 +1675,12 @@ bool Item_sum_avg::fix_length_and_dec() if (Item_sum_avg::result_type() == DECIMAL_RESULT) { int precision= args[0]->decimal_precision() + prec_increment; - decimals= MY_MIN(args[0]->decimals + prec_increment, DECIMAL_MAX_SCALE); + decimals= MY_MIN(args[0]->decimal_scale() + prec_increment, DECIMAL_MAX_SCALE); max_length= my_decimal_precision_to_length_no_truncation(precision, decimals, unsigned_flag); f_precision= MY_MIN(precision+DECIMAL_LONGLONG_DIGITS, DECIMAL_MAX_PRECISION); - f_scale= args[0]->decimals; + f_scale= args[0]->decimal_scale(); dec_bin_size= my_decimal_get_binary_size(f_precision, f_scale); } else diff --git a/sql/sql_type.cc b/sql/sql_type.cc index c1f192b0622..b1e141d0eeb 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -258,7 +258,6 @@ Type_handler_decimal_result::make_num_distinct_aggregator_field( const Item *item) const { - DBUG_ASSERT(item->decimals <= DECIMAL_MAX_SCALE); return new (mem_root) Field_new_decimal(NULL, item->max_length, (uchar *) (item->maybe_null ? "" : 0), From 388032e99057449219d4a943b4407e36c42ec4af Mon Sep 17 00:00:00 2001 From: Andrei Date: Tue, 26 Apr 2022 19:46:44 +0300 Subject: [PATCH 19/28] MDEV-27697. Removed a false assert. --- libmariadb | 2 +- sql/slave.cc | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/libmariadb b/libmariadb index f33017c19a5..f6c3d9fd2af 160000 --- a/libmariadb +++ b/libmariadb @@ -1 +1 @@ -Subproject commit f33017c19a5b02394de5d0816513d2e2c9d1767c +Subproject commit f6c3d9fd2af5d17db64cc996574aa312efd70fcf diff --git a/sql/slave.cc b/sql/slave.cc index 0d50e8b0c61..80b1137d90e 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -6594,8 +6594,6 @@ dbug_gtid_accept: Query_log_event::peek_is_commit_rollback(buf, event_len, checksum_alg)))))) { - DBUG_ASSERT(mi->events_queued_since_last_gtid > 1); - if (unlikely(gtid_skip_enqueue)) { error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE; From 44e7c312bad9d00498f6f04afe165f5d8570765d Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Mon, 25 Apr 2022 15:47:33 +0200 Subject: [PATCH 20/28] New C/C version *again* after 388032e9905 has reverted 25ccf8f6dcf by mistake --- libmariadb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libmariadb b/libmariadb index f6c3d9fd2af..f33017c19a5 160000 --- a/libmariadb +++ b/libmariadb @@ -1 +1 @@ -Subproject commit f6c3d9fd2af5d17db64cc996574aa312efd70fcf +Subproject commit f33017c19a5b02394de5d0816513d2e2c9d1767c From 39990135e57e7377fd2f9a156973a59eb07bc739 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 26 Apr 2022 14:18:32 +0200 Subject: [PATCH 21/28] MDEV-28020 CHECKSUM TABLE calculates different checksums Two bugs here: 1. CHECKSUM TABLE asserted that all fields in the table are arranged sequentially in the record, but virtual columns are always at the end, violating this assertion 2. virtual columns were not calculated for CHECKSUM, so CHECKSUM was using, essentially, garbage left from the previous statement. (that's why the test must use INSERT IGNORE to have this "previous statement" mark vcols not null) Fix: don't include virtual columns into the table CHECKSUM. Indeed, they cannot be included as the engine does not see virtual columns, so in-engine checksum cannot include them, meaning in-server checksum should not either --- mysql-test/r/row-checksum-old.result | 45 +++++++++++++++++++++++++++- mysql-test/r/row-checksum.result | 45 +++++++++++++++++++++++++++- mysql-test/t/row-checksum.test | 35 ++++++++++++++++++---- sql/sql_table.cc | 2 ++ 4 files changed, 119 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/row-checksum-old.result b/mysql-test/r/row-checksum-old.result index d374013f61c..7caed4fc7dc 100644 --- a/mysql-test/r/row-checksum-old.result +++ b/mysql-test/r/row-checksum-old.result @@ -1,4 +1,3 @@ -drop table if exists t1; create table t1 (a int null, v varchar(100)) engine=myisam checksum=0; insert into t1 values(null, null), (1, "hello"); checksum table t1; @@ -98,4 +97,48 @@ CHECKSUM TABLE t1 EXTENDED; Table Checksum test.t1 2326430205 drop table t1; +# # End of 5.5 tests +# +# +# MDEV-28020 CHECKSUM TABLE calculates different checksums +# +create table t1 ( a int, b int as (a) virtual, c text) engine=myisam checksum=1; +insert ignore t1 values (1,2,'foo'),(2,3,'bar'); +Warnings: +Warning 1906 The value specified for generated column 'b' in table 't1' has been ignored +Warning 1906 The value specified for generated column 'b' in table 't1' has been ignored +checksum table t1 extended; +Table Checksum +test.t1 4101438232 +checksum table t1; +Table Checksum +test.t1 4101438232 +drop table t1; +create table t1 ( a int, b int as (a) virtual, c text, key(b)) engine=myisam checksum=1; +insert ignore t1 values (1,2,'foo'),(2,3,'bar'); +Warnings: +Warning 1906 The value specified for generated column 'b' in table 't1' has been ignored +Warning 1906 The value specified for generated column 'b' in table 't1' has been ignored +checksum table t1 extended; +Table Checksum +test.t1 4101438232 +checksum table t1; +Table Checksum +test.t1 4101438232 +drop table t1; +create table t1 ( a int, b int as (a) stored, c text) engine=myisam checksum=1; +insert ignore t1 values (1,2,'foo'),(2,3,'bar'); +Warnings: +Warning 1906 The value specified for generated column 'b' in table 't1' has been ignored +Warning 1906 The value specified for generated column 'b' in table 't1' has been ignored +checksum table t1 extended; +Table Checksum +test.t1 2897795735 +checksum table t1; +Table Checksum +test.t1 2897795735 +drop table t1; +# +# End of 10.2 tests +# diff --git a/mysql-test/r/row-checksum.result b/mysql-test/r/row-checksum.result index 4625e09c060..c80ca4eed9a 100644 --- a/mysql-test/r/row-checksum.result +++ b/mysql-test/r/row-checksum.result @@ -1,4 +1,3 @@ -drop table if exists t1; create table t1 (a int null, v varchar(100)) engine=myisam checksum=0; insert into t1 values(null, null), (1, "hello"); checksum table t1; @@ -98,4 +97,48 @@ CHECKSUM TABLE t1 EXTENDED; Table Checksum test.t1 2326430205 drop table t1; +# # End of 5.5 tests +# +# +# MDEV-28020 CHECKSUM TABLE calculates different checksums +# +create table t1 ( a int, b int as (a) virtual, c text) engine=myisam checksum=1; +insert ignore t1 values (1,2,'foo'),(2,3,'bar'); +Warnings: +Warning 1906 The value specified for generated column 'b' in table 't1' has been ignored +Warning 1906 The value specified for generated column 'b' in table 't1' has been ignored +checksum table t1 extended; +Table Checksum +test.t1 4101438232 +checksum table t1; +Table Checksum +test.t1 4101438232 +drop table t1; +create table t1 ( a int, b int as (a) virtual, c text, key(b)) engine=myisam checksum=1; +insert ignore t1 values (1,2,'foo'),(2,3,'bar'); +Warnings: +Warning 1906 The value specified for generated column 'b' in table 't1' has been ignored +Warning 1906 The value specified for generated column 'b' in table 't1' has been ignored +checksum table t1 extended; +Table Checksum +test.t1 4101438232 +checksum table t1; +Table Checksum +test.t1 4101438232 +drop table t1; +create table t1 ( a int, b int as (a) stored, c text) engine=myisam checksum=1; +insert ignore t1 values (1,2,'foo'),(2,3,'bar'); +Warnings: +Warning 1906 The value specified for generated column 'b' in table 't1' has been ignored +Warning 1906 The value specified for generated column 'b' in table 't1' has been ignored +checksum table t1 extended; +Table Checksum +test.t1 2897795735 +checksum table t1; +Table Checksum +test.t1 2897795735 +drop table t1; +# +# End of 10.2 tests +# diff --git a/mysql-test/t/row-checksum.test b/mysql-test/t/row-checksum.test index 5acfda45f75..3b510abba84 100644 --- a/mysql-test/t/row-checksum.test +++ b/mysql-test/t/row-checksum.test @@ -2,12 +2,8 @@ # Test checksum # --- source include/have_innodb.inc --- source include/have_maria.inc - ---disable_warnings -drop table if exists t1; ---enable_warnings +--source include/have_innodb.inc +--source include/have_maria.inc create table t1 (a int null, v varchar(100)) engine=myisam checksum=0; insert into t1 values(null, null), (1, "hello"); @@ -76,4 +72,31 @@ CHECKSUM TABLE t1 EXTENDED; drop table t1; +--echo # --echo # End of 5.5 tests +--echo # + +--echo # +--echo # MDEV-28020 CHECKSUM TABLE calculates different checksums +--echo # +create table t1 ( a int, b int as (a) virtual, c text) engine=myisam checksum=1; +insert ignore t1 values (1,2,'foo'),(2,3,'bar'); +checksum table t1 extended; +checksum table t1; +drop table t1; + +create table t1 ( a int, b int as (a) virtual, c text, key(b)) engine=myisam checksum=1; +insert ignore t1 values (1,2,'foo'),(2,3,'bar'); +checksum table t1 extended; +checksum table t1; +drop table t1; + +create table t1 ( a int, b int as (a) stored, c text) engine=myisam checksum=1; +insert ignore t1 values (1,2,'foo'),(2,3,'bar'); +checksum table t1 extended; +checksum table t1; +drop table t1; + +--echo # +--echo # End of 10.2 tests +--echo # diff --git a/sql/sql_table.cc b/sql/sql_table.cc index a2dc5c97aeb..f9d7f4a2ea2 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -10631,6 +10631,8 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, for (uint i= 0; i < t->s->fields; i++ ) { Field *f= t->field[i]; + if (!f->stored_in_db()) + continue; if (! thd->variables.old_mode && f->is_real_null(0)) { From f21a87560091a8149752b13e097d382ba30786d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 27 Apr 2022 07:57:04 +0300 Subject: [PATCH 22/28] MDEV-28415 ALTER TABLE on a large table hangs InnoDB buf_flush_page(): Never wait for a page latch, even in checkpoint flushing (flush_type == BUF_FLUSH_LIST), to prevent a hang of the page cleaner threads when a large number of pages is latched. In mysql/mysql-server@9542f3015b00330ef537f6223565b28b82a5b325 it was claimed that such a hang only affects CREATE FULLTEXT INDEX. Their fix was to retain buffer-fix but release exclusive latch on non-leaf pages, and subsequently write to those pages while they are not associated with the mini-transaction, which would trip a debug assertion in the MariaDB version of mtr_t::memo_modify_page() and cause potential corruption when using the default MariaDB setting innodb_log_optimize_ddl=OFF. This change essentially backports a small part of commit 7cffb5f6e8a231a041152447be8980ce35d2c9b8 (MDEV-23399) from MariaDB Server 10.5.7. --- storage/innobase/buf/buf0flu.cc | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/storage/innobase/buf/buf0flu.cc b/storage/innobase/buf/buf0flu.cc index 2beddf4b243..e029948be55 100644 --- a/storage/innobase/buf/buf0flu.cc +++ b/storage/innobase/buf/buf0flu.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2017, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2013, 2020, MariaDB Corporation. +Copyright (c) 2013, 2022, MariaDB Corporation. Copyright (c) 2013, 2014, Fusion-io This program is free software; you can redistribute it and/or modify it under @@ -1160,15 +1160,10 @@ buf_flush_page( /* For table residing in temporary tablespace sync is done using IO_FIX and so before scheduling for flush ensure that page is not fixed. */ - flush = FALSE; + return FALSE; } else { rw_lock = &reinterpret_cast(bpage)->lock; - if (flush_type != BUF_FLUSH_LIST) { - flush = rw_lock_sx_lock_nowait(rw_lock, BUF_IO_WRITE); - } else { - /* Will SX lock later */ - flush = TRUE; - } + flush = rw_lock_sx_lock_nowait(rw_lock, BUF_IO_WRITE); } if (flush) { @@ -1190,22 +1185,6 @@ buf_flush_page( buf_pool_mutex_exit(buf_pool); - if (flush_type == BUF_FLUSH_LIST - && is_uncompressed - && !rw_lock_sx_lock_nowait(rw_lock, BUF_IO_WRITE)) { - - if (!fsp_is_system_temporary(bpage->id.space())) { - /* avoiding deadlock possibility involves - doublewrite buffer, should flush it, because - it might hold the another block->lock. */ - buf_dblwr_flush_buffered_writes(); - } else { - buf_dblwr_sync_datafiles(); - } - - rw_lock_sx_lock_gen(rw_lock, BUF_IO_WRITE); - } - /* If there is an observer that want to know if the asynchronous flushing was sent then notify it. Note: we set flush observer to a page with x-latch, so we can From 44a27a26e910c9fb27731bd2cb267262949ea2da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 27 Apr 2022 08:08:06 +0300 Subject: [PATCH 23/28] MDEV-28416 Incorrect AUTO_INCREMENT may be issued when close to UINT64_MAX ha_innobase::get_auto_increment(): In the overflow check, account for 64-bit unsigned integer wrap-around. Based on mysql/mysql-server@25ecfe7f49b5a649e96d462cb90602de9de3b919 --- .../suite/innodb/r/innodb-autoinc-part.result | 34 +++++++++++++++++++ .../suite/innodb/r/innodb-autoinc.result | 25 +++++++++++++- .../suite/innodb/t/innodb-autoinc-part.test | 24 +++++++++++++ mysql-test/suite/innodb/t/innodb-autoinc.test | 23 +++++++++---- storage/innobase/handler/ha_innodb.cc | 4 +-- 5 files changed, 101 insertions(+), 9 deletions(-) create mode 100644 mysql-test/suite/innodb/r/innodb-autoinc-part.result create mode 100644 mysql-test/suite/innodb/t/innodb-autoinc-part.test diff --git a/mysql-test/suite/innodb/r/innodb-autoinc-part.result b/mysql-test/suite/innodb/r/innodb-autoinc-part.result new file mode 100644 index 00000000000..6872b5e02f5 --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb-autoinc-part.result @@ -0,0 +1,34 @@ +# +# MDEV-28416 Incorrect AUTO_INCREMENT may be issued +# +SET @aii=@@auto_increment_increment; +SET auto_increment_increment=300; +CREATE TABLE t1 (a SERIAL) ENGINE=innodb +PARTITION BY RANGE (a) ( +PARTITION p0 VALUES LESS THAN (6), +PARTITION p1 VALUES LESS THAN MAXVALUE +); +INSERT INTO t1 VALUES (18446744073709551613); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + UNIQUE KEY `a` (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=18446744073709551614 DEFAULT CHARSET=latin1 + PARTITION BY RANGE (`a`) +(PARTITION `p0` VALUES LESS THAN (6) ENGINE = InnoDB, + PARTITION `p1` VALUES LESS THAN MAXVALUE ENGINE = InnoDB) +INSERT INTO t1 VALUES (NULL); +ERROR 22003: Out of range value for column 'a' at row 1 +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + UNIQUE KEY `a` (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=298 DEFAULT CHARSET=latin1 + PARTITION BY RANGE (`a`) +(PARTITION `p0` VALUES LESS THAN (6) ENGINE = InnoDB, + PARTITION `p1` VALUES LESS THAN MAXVALUE ENGINE = InnoDB) +DROP TABLE t1; +SET auto_increment_increment=@aii; +# End of 10.2 tests diff --git a/mysql-test/suite/innodb/r/innodb-autoinc.result b/mysql-test/suite/innodb/r/innodb-autoinc.result index 6c405627e08..00690ecaff7 100644 --- a/mysql-test/suite/innodb/r/innodb-autoinc.result +++ b/mysql-test/suite/innodb/r/innodb-autoinc.result @@ -1,4 +1,3 @@ -drop table if exists t1; CREATE TABLE t1 (c1 BIGINT PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB; INSERT INTO t1 VALUES (9223372036854775807, null); INSERT INTO t1 (c2) VALUES ('innodb'); @@ -1619,3 +1618,27 @@ id name -1 dog 2 cat DROP PROCEDURE autoinc_mdev15353_one; +# +# MDEV-28416 Incorrect AUTO_INCREMENT may be issued +# +SET @aii=@@auto_increment_increment; +SET auto_increment_increment=300; +CREATE TABLE t1 (a SERIAL) ENGINE=innodb; +INSERT INTO t1 VALUES (18446744073709551613); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + UNIQUE KEY `a` (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=18446744073709551614 DEFAULT CHARSET=latin1 +INSERT INTO t1 VALUES (NULL); +ERROR 22003: Out of range value for column 'a' at row 1 +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + UNIQUE KEY `a` (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=18446744073709551615 DEFAULT CHARSET=latin1 +DROP TABLE t1; +SET auto_increment_increment=@aii; +# End of 10.2 tests diff --git a/mysql-test/suite/innodb/t/innodb-autoinc-part.test b/mysql-test/suite/innodb/t/innodb-autoinc-part.test new file mode 100644 index 00000000000..100546704ce --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb-autoinc-part.test @@ -0,0 +1,24 @@ +--source include/have_partition.inc +--source include/have_innodb.inc + +--echo # +--echo # MDEV-28416 Incorrect AUTO_INCREMENT may be issued +--echo # + +SET @aii=@@auto_increment_increment; +SET auto_increment_increment=300; + +CREATE TABLE t1 (a SERIAL) ENGINE=innodb +PARTITION BY RANGE (a) ( + PARTITION p0 VALUES LESS THAN (6), + PARTITION p1 VALUES LESS THAN MAXVALUE +); +INSERT INTO t1 VALUES (18446744073709551613); +SHOW CREATE TABLE t1; +--error HA_ERR_AUTOINC_ERANGE +INSERT INTO t1 VALUES (NULL); +SHOW CREATE TABLE t1; +DROP TABLE t1; +SET auto_increment_increment=@aii; + +--echo # End of 10.2 tests diff --git a/mysql-test/suite/innodb/t/innodb-autoinc.test b/mysql-test/suite/innodb/t/innodb-autoinc.test index ca7727d4cef..158460558d5 100644 --- a/mysql-test/suite/innodb/t/innodb-autoinc.test +++ b/mysql-test/suite/innodb/t/innodb-autoinc.test @@ -1,10 +1,4 @@ --source include/have_innodb.inc -# embedded server ignores 'delayed', so skip this --- source include/not_embedded.inc - ---disable_warnings -drop table if exists t1; ---enable_warnings # # Bug #34335 @@ -770,3 +764,20 @@ DROP TABLE t1; SET @engine='INNODB'; --source include/autoinc_mdev15353.inc + +--echo # +--echo # MDEV-28416 Incorrect AUTO_INCREMENT may be issued +--echo # + +SET @aii=@@auto_increment_increment; +SET auto_increment_increment=300; +CREATE TABLE t1 (a SERIAL) ENGINE=innodb; +INSERT INTO t1 VALUES (18446744073709551613); +SHOW CREATE TABLE t1; +--error HA_ERR_AUTOINC_ERANGE +INSERT INTO t1 VALUES (NULL); +SHOW CREATE TABLE t1; +DROP TABLE t1; +SET auto_increment_increment=@aii; + +--echo # End of 10.2 tests diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 1884250ee48..53d5a07b79f 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -16970,8 +16970,8 @@ ha_innobase::get_auto_increment( (3) It is restricted only for insert operations. */ - if (increment > 1 && thd_sql_command(m_user_thd) != SQLCOM_ALTER_TABLE - && autoinc < col_max_value) { + if (increment > 1 && increment <= ~autoinc && autoinc < col_max_value + && thd_sql_command(m_user_thd) != SQLCOM_ALTER_TABLE) { ulonglong prev_auto_inc = autoinc; From eea15803ec875a27801ed9d32b7f5fbc4aec1fc7 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Tue, 26 Apr 2022 23:03:34 +0300 Subject: [PATCH 24/28] MDEV-28268: Server crashes in Expression_cache_tracker::fetch_current_stats Expression_cache_tmptable object uses an Expression_cache_tracker object to report the statistics. In the common scenario, Expression_cache_tmptable destructor sets tracker->cache=NULL. The tracker object survives after the expression cache is deleted and one may call cache_tracker->fetch_current_stats() for it with no harm. However a degenerate cache with no parameters does not set tracker->cache=NULL in Expression_cache_tmptable destructor which results in an attempt to use freed data in the cache_tracker->fetch_current_stats() call. Fixed by setting tracker->cache to NULL and wrapping the assignment into a function. --- mysql-test/r/subselect4.result | 75 ++++++++++++++++++++++++++++++++++ mysql-test/t/subselect4.test | 15 +++++++ sql/sql_expression_cache.cc | 4 +- sql/sql_expression_cache.h | 5 +++ 4 files changed, 98 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/subselect4.result b/mysql-test/r/subselect4.result index 43e698d2b56..e2c81d17737 100644 --- a/mysql-test/r/subselect4.result +++ b/mysql-test/r/subselect4.result @@ -2841,4 +2841,79 @@ id select_type table type possible_keys key key_len ref rows Extra 2 DEPENDENT SUBQUERY tn eq_ref PRIMARY PRIMARY 32 test.tms.key1 1 Using where set optimizer_switch=@tmp_os; drop table t1, t10, t11; +# +# MDEV-28268: Server crashes in Expression_cache_tracker::fetch_current_stats +# +CREATE TABLE t1 (a INT, b INT); +INSERT INTO t1 VALUES (1,2),(3,4); +ANALYZE FORMAT=JSON +SELECT DISTINCT +(SELECT MIN(a) FROM t1 WHERE b <= ANY (SELECT a FROM t1)) AS f +FROM t1; +ANALYZE +{ + "query_block": { + "select_id": 1, + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "duplicate_removal": { + "temporary_table": { + "table": { + "table_name": "t1", + "access_type": "ALL", + "r_loops": 1, + "rows": 2, + "r_rows": 2, + "r_total_time_ms": "REPLACED", + "filtered": 100, + "r_filtered": 100 + }, + "subqueries": [ + { + "expression_cache": { + "state": "disabled", + "r_loops": 0, + "query_block": { + "select_id": 2, + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "table": { + "table_name": "t1", + "access_type": "ALL", + "r_loops": 1, + "rows": 2, + "r_rows": 2, + "r_total_time_ms": "REPLACED", + "filtered": 100, + "r_filtered": 50, + "attached_condition": "((t1.b,(subquery#3) >= 4))" + }, + "subqueries": [ + { + "query_block": { + "select_id": 3, + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "table": { + "table_name": "t1", + "access_type": "ALL", + "r_loops": 1, + "rows": 2, + "r_rows": 2, + "r_total_time_ms": "REPLACED", + "filtered": 100, + "r_filtered": 100 + } + } + } + ] + } + } + } + ] + } + } + } +} +DROP TABLE t1; # End of 10.2 tests diff --git a/mysql-test/t/subselect4.test b/mysql-test/t/subselect4.test index 1313d2e49e7..6641cdc4557 100644 --- a/mysql-test/t/subselect4.test +++ b/mysql-test/t/subselect4.test @@ -2351,4 +2351,19 @@ set optimizer_switch=@tmp_os; drop table t1, t10, t11; +--echo # +--echo # MDEV-28268: Server crashes in Expression_cache_tracker::fetch_current_stats +--echo # +CREATE TABLE t1 (a INT, b INT); +INSERT INTO t1 VALUES (1,2),(3,4); + +--source include/analyze-format.inc +ANALYZE FORMAT=JSON +SELECT DISTINCT + (SELECT MIN(a) FROM t1 WHERE b <= ANY (SELECT a FROM t1)) AS f +FROM t1; + +# Cleanup +DROP TABLE t1; + --echo # End of 10.2 tests diff --git a/sql/sql_expression_cache.cc b/sql/sql_expression_cache.cc index 4b6d4b4472e..5ce50a3fe52 100644 --- a/sql/sql_expression_cache.cc +++ b/sql/sql_expression_cache.cc @@ -63,7 +63,7 @@ void Expression_cache_tmptable::disable_cache() cache_table= NULL; update_tracker(); if (tracker) - tracker->cache= NULL; + tracker->detach_from_cache(); } @@ -188,6 +188,8 @@ Expression_cache_tmptable::~Expression_cache_tmptable() else { update_tracker(); + if (tracker) + tracker->detach_from_cache(); tracker= NULL; } } diff --git a/sql/sql_expression_cache.h b/sql/sql_expression_cache.h index 61e0c4c69b3..d4bb252dccb 100644 --- a/sql/sql_expression_cache.h +++ b/sql/sql_expression_cache.h @@ -83,7 +83,11 @@ public: cache(c), hit(0), miss(0), state(UNINITED) {} +private: + // This can be NULL if the cache is already deleted Expression_cache *cache; + +public: ulong hit, miss; enum expr_cache_state state; @@ -91,6 +95,7 @@ public: void set(ulong h, ulong m, enum expr_cache_state s) {hit= h; miss= m; state= s;} + void detach_from_cache() { cache= NULL; } void fetch_current_stats() { if (cache) From 1430cf7873e0c2f1c2082291531835cec342e625 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 27 Apr 2022 16:39:50 +0200 Subject: [PATCH 25/28] MDEV-28428 Master_SSL_Crl shows Master_SSL_CA value in SHOW SLAVE STATUS output it was showing ca and capath instead of crl and crl_path --- mysql-test/extra/rpl_tests/rpl_ssl.inc | 2 +- mysql-test/suite/binlog_encryption/rpl_ssl.result | 4 ++++ mysql-test/suite/rpl/r/rpl_ssl.result | 4 ++++ sql/slave.cc | 6 +++--- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/mysql-test/extra/rpl_tests/rpl_ssl.inc b/mysql-test/extra/rpl_tests/rpl_ssl.inc index aff5499c8e5..bd77f213ae1 100644 --- a/mysql-test/extra/rpl_tests/rpl_ssl.inc +++ b/mysql-test/extra/rpl_tests/rpl_ssl.inc @@ -37,7 +37,7 @@ select * from t1; # The slave is synced and waiting/reading from master # SHOW SLAVE STATUS will show "Waiting for master to send event" -let $status_items= Master_SSL_Allowed, Master_SSL_CA_Path, Master_SSL_CA_File, Master_SSL_Cert, Master_SSL_Key; +let $status_items= Master_SSL_Allowed, Master_SSL_CA_Path, Master_SSL_CA_File, Master_SSL_Crl, Master_SSL_Crlpath, Master_SSL_Cert, Master_SSL_Key; source include/show_slave_status.inc; source include/check_slave_is_running.inc; diff --git a/mysql-test/suite/binlog_encryption/rpl_ssl.result b/mysql-test/suite/binlog_encryption/rpl_ssl.result index 0b3a6cd0eca..ce9e4d486cf 100644 --- a/mysql-test/suite/binlog_encryption/rpl_ssl.result +++ b/mysql-test/suite/binlog_encryption/rpl_ssl.result @@ -23,6 +23,8 @@ t Master_SSL_Allowed = 'Yes' Master_SSL_CA_Path = '' Master_SSL_CA_File = 'MYSQL_TEST_DIR/std_data/cacert.pem' +Master_SSL_Crl = '' +Master_SSL_Crlpath = '' Master_SSL_Cert = 'MYSQL_TEST_DIR/std_data/client-cert.pem' Master_SSL_Key = 'MYSQL_TEST_DIR/std_data/client-key.pem' include/check_slave_is_running.inc @@ -37,6 +39,8 @@ include/wait_for_slave_to_start.inc Master_SSL_Allowed = 'Yes' Master_SSL_CA_Path = '' Master_SSL_CA_File = 'MYSQL_TEST_DIR/std_data/cacert.pem' +Master_SSL_Crl = '' +Master_SSL_Crlpath = '' Master_SSL_Cert = 'MYSQL_TEST_DIR/std_data/client-cert.pem' Master_SSL_Key = 'MYSQL_TEST_DIR/std_data/client-key.pem' include/check_slave_is_running.inc diff --git a/mysql-test/suite/rpl/r/rpl_ssl.result b/mysql-test/suite/rpl/r/rpl_ssl.result index 0b3a6cd0eca..ce9e4d486cf 100644 --- a/mysql-test/suite/rpl/r/rpl_ssl.result +++ b/mysql-test/suite/rpl/r/rpl_ssl.result @@ -23,6 +23,8 @@ t Master_SSL_Allowed = 'Yes' Master_SSL_CA_Path = '' Master_SSL_CA_File = 'MYSQL_TEST_DIR/std_data/cacert.pem' +Master_SSL_Crl = '' +Master_SSL_Crlpath = '' Master_SSL_Cert = 'MYSQL_TEST_DIR/std_data/client-cert.pem' Master_SSL_Key = 'MYSQL_TEST_DIR/std_data/client-key.pem' include/check_slave_is_running.inc @@ -37,6 +39,8 @@ include/wait_for_slave_to_start.inc Master_SSL_Allowed = 'Yes' Master_SSL_CA_Path = '' Master_SSL_CA_File = 'MYSQL_TEST_DIR/std_data/cacert.pem' +Master_SSL_Crl = '' +Master_SSL_Crlpath = '' Master_SSL_Cert = 'MYSQL_TEST_DIR/std_data/client-cert.pem' Master_SSL_Key = 'MYSQL_TEST_DIR/std_data/client-key.pem' include/check_slave_is_running.inc diff --git a/sql/slave.cc b/sql/slave.cc index 80b1137d90e..ca3397e5e7b 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -2914,9 +2914,9 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full, protocol->store((uint32) mi->master_id); // SQL_Delay // Master_Ssl_Crl - protocol->store(mi->ssl_ca, &my_charset_bin); + protocol->store(mi->ssl_crl, &my_charset_bin); // Master_Ssl_Crlpath - protocol->store(mi->ssl_capath, &my_charset_bin); + protocol->store(mi->ssl_crlpath, &my_charset_bin); // Using_Gtid protocol->store(mi->using_gtid_astext(mi->using_gtid), &my_charset_bin); // Gtid_IO_Pos @@ -3004,7 +3004,7 @@ bool show_all_master_info(THD* thd) String gtid_pos; Master_info **tmp; List field_list; - DBUG_ENTER("show_master_info"); + DBUG_ENTER("show_all_master_info"); mysql_mutex_assert_owner(&LOCK_active_mi); gtid_pos.length(0); From f354e457e1c4171ce38cd3db3e492640db95036c Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 27 Apr 2022 18:48:06 +0200 Subject: [PATCH 26/28] Bug#33578113: DROP privilege on performance_schema.* can't be revoked test case only --- mysql-test/r/grant.result | 10 ++++++++-- mysql-test/t/grant.test | 18 +++++++++++------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result index bd24c5bf380..e4e4e2339e5 100644 --- a/mysql-test/r/grant.result +++ b/mysql-test/r/grant.result @@ -2,8 +2,6 @@ set GLOBAL sql_mode=""; set LOCAL sql_mode=""; SET @old_log_bin_trust_function_creators= @@global.log_bin_trust_function_creators; SET GLOBAL log_bin_trust_function_creators = 1; -drop table if exists t1; -drop database if exists mysqltest; connect master,localhost,root,,; connection master; SET NAMES binary; @@ -2785,5 +2783,13 @@ DROP USER foo; DROP TABLE db.t; DROP DATABASE db; # +# Bug#33578113: DROP privilege on performance_schema.* can't be revoked +# +connection default; +CREATE USER bug33578113; +GRANT DROP ON performance_schema.* TO bug33578113; +REVOKE DROP ON performance_schema.* FROM bug33578113; +DROP USER bug33578113; +# # End of 10.2 tests # diff --git a/mysql-test/t/grant.test b/mysql-test/t/grant.test index fa3a690a2ee..5f862db1f35 100644 --- a/mysql-test/t/grant.test +++ b/mysql-test/t/grant.test @@ -1,7 +1,7 @@ # Test of GRANT commands # Grant tests not performed with embedded server --- source include/not_embedded.inc +--source include/not_embedded.inc # Save the initial number of concurrent sessions --source include/count_sessions.inc @@ -11,12 +11,6 @@ set LOCAL sql_mode=""; SET @old_log_bin_trust_function_creators= @@global.log_bin_trust_function_creators; SET GLOBAL log_bin_trust_function_creators = 1; -# Cleanup ---disable_warnings -drop table if exists t1; -drop database if exists mysqltest; ---enable_warnings - connect (master,localhost,root,,); connection master; SET NAMES binary; @@ -2292,6 +2286,16 @@ DROP USER foo; DROP TABLE db.t; DROP DATABASE db; +--echo # +--echo # Bug#33578113: DROP privilege on performance_schema.* can't be revoked +--echo # +connection default; +CREATE USER bug33578113; +GRANT DROP ON performance_schema.* TO bug33578113; +REVOKE DROP ON performance_schema.* FROM bug33578113; +DROP USER bug33578113; +--source include/wait_until_count_sessions.inc + --echo # --echo # End of 10.2 tests --echo # From 680ca15269ba3fddf3c795da6bf889acacfdd258 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Sat, 30 Apr 2022 08:49:13 +0400 Subject: [PATCH 27/28] MDEV-28446 mariabackup prepare fails for incrementals if a new schema is created after full backup is taken When "mariabackup --target-dir=$basedir --incremental-dir=$incremental_dir" is running and is moving a new table file (e.g. `db1/t1.new`) from the incremental directory to the base directory, it needs to verify that the base backup database directory (e.g. `$basedir/db1`) really exists (or create it otherwise). The table `db1/t1` can come from a new database `db1` which was created during the base mariabackup execution time. In such case the directory `db1` exists only in the incremental directory, but does not exist in the base directory. --- extra/mariabackup/xtrabackup.cc | 16 ++++++- .../incremental_newdb_while_backup.result | 24 ++++++++++ .../incremental_newdb_while_backup.test | 47 +++++++++++++++++++ 3 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 mysql-test/suite/mariabackup/incremental_newdb_while_backup.result create mode 100644 mysql-test/suite/mariabackup/incremental_newdb_while_backup.test diff --git a/extra/mariabackup/xtrabackup.cc b/extra/mariabackup/xtrabackup.cc index c1b2a1a9c85..ada63e1e882 100644 --- a/extra/mariabackup/xtrabackup.cc +++ b/extra/mariabackup/xtrabackup.cc @@ -5441,11 +5441,23 @@ static ibool prepare_handle_new_files(const char *data_home_dir, const char *file_name, void *arg) { const char *dest_dir = static_cast(arg); - std::string src_path = std::string(data_home_dir) + '/' + std::string(db_name) + '/' + file_name; + std::string src_path = std::string(data_home_dir) + '/' + std::string(db_name) + '/'; /* Copy "*.new" files from incremental to base dir for incremental backup */ std::string dest_path= dest_dir ? std::string(dest_dir) + '/' + std::string(db_name) + - '/' + file_name : src_path; + '/' : src_path; + + /* + A CREATE DATABASE could have happened during the base mariabackup run. + In case if the current table file (e.g. `t1.new`) is from such + a new database, the database directory may not exist yet in + the base backup directory. Let's make sure to check if the directory + exists (and create if needed). + */ + if (!directory_exists(dest_path.c_str(), true/*create if not exists*/)) + return FALSE; + src_path+= file_name; + dest_path+= file_name; size_t index = dest_path.find(".new"); DBUG_ASSERT(index != std::string::npos); diff --git a/mysql-test/suite/mariabackup/incremental_newdb_while_backup.result b/mysql-test/suite/mariabackup/incremental_newdb_while_backup.result new file mode 100644 index 00000000000..e1d5c7a5df4 --- /dev/null +++ b/mysql-test/suite/mariabackup/incremental_newdb_while_backup.result @@ -0,0 +1,24 @@ +call mtr.add_suppression("InnoDB: New log files created"); +# +# Start of 10.2 tests +# +# +# MDEV-28446 mariabackup prepare fails for incrementals if a new schema is created after full backup is taken +# +CREATE TABLE t1(i INT PRIMARY KEY) ENGINE INNODB; +# Prepare full backup, apply incremental one +# shutdown server +# remove datadir +# xtrabackup move back +# restart server +SELECT COUNT(*) FROM test.t1; +COUNT(*) +0 +SELECT COUNT(*) FROM test1.t1; +COUNT(*) +10000 +DROP TABLE t1; +DROP DATABASE test1; +# +# End of 10.2 tests +# diff --git a/mysql-test/suite/mariabackup/incremental_newdb_while_backup.test b/mysql-test/suite/mariabackup/incremental_newdb_while_backup.test new file mode 100644 index 00000000000..2433fb4a35a --- /dev/null +++ b/mysql-test/suite/mariabackup/incremental_newdb_while_backup.test @@ -0,0 +1,47 @@ +--source include/have_innodb.inc +--source include/have_debug.inc + +call mtr.add_suppression("InnoDB: New log files created"); + +--echo # +--echo # Start of 10.2 tests +--echo # + +--echo # +--echo # MDEV-28446 mariabackup prepare fails for incrementals if a new schema is created after full backup is taken +--echo # + +--let $basedir=$MYSQLTEST_VARDIR/tmp/backup +--let $incremental_dir=$MYSQLTEST_VARDIR/tmp/backup_inc1 + +CREATE TABLE t1(i INT PRIMARY KEY) ENGINE INNODB; + +--disable_result_log +--exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$basedir +--enable_result_log + +--let after_load_tablespaces=BEGIN NOT ATOMIC CREATE DATABASE test1; CREATE TABLE test1.t1 ENGINE=INNODB SELECT UUID() from test.seq_1_to_10000; END + +--exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$incremental_dir --incremental-basedir=$basedir --dbug=+d,mariabackup_events,mariabackup_inject_code +--let after_load_tablespaces= +--disable_result_log +--echo # Prepare full backup, apply incremental one +--exec $XTRABACKUP --apply-log-only --prepare --target-dir=$basedir +--exec $XTRABACKUP --prepare --target-dir=$basedir --incremental-dir=$incremental_dir +--enable_result_log + +--let $targetdir=$basedir +--source include/restart_and_restore.inc +--enable_result_log + +SELECT COUNT(*) FROM test.t1; +SELECT COUNT(*) FROM test1.t1; + +DROP TABLE t1; +DROP DATABASE test1; +--rmdir $basedir +--rmdir $incremental_dir + +--echo # +--echo # End of 10.2 tests +--echo # From 70555454b4c224e85383d482411961c7f2eba2e2 Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Mon, 2 May 2022 11:55:31 +0200 Subject: [PATCH 28/28] New CC 3.1 --- libmariadb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libmariadb b/libmariadb index f33017c19a5..ab7a81e79e4 160000 --- a/libmariadb +++ b/libmariadb @@ -1 +1 @@ -Subproject commit f33017c19a5b02394de5d0816513d2e2c9d1767c +Subproject commit ab7a81e79e4be4324a2d09d19d4f5249801ef665