From badf6de1715bbdddcaf8c1535747f94602aaa13f Mon Sep 17 00:00:00 2001 From: Weijun Huang Date: Sun, 12 Feb 2023 18:42:23 +0100 Subject: [PATCH 1/3] MDEV-30412: JSON_OBJECTAGG doesn't escape double quote in key --- mysql-test/main/func_json.result | 12 ++++++++++++ mysql-test/main/func_json.test | 8 ++++++++ sql/item_jsonfunc.cc | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/mysql-test/main/func_json.result b/mysql-test/main/func_json.result index 2a293fc72c7..b9df193d3bd 100644 --- a/mysql-test/main/func_json.result +++ b/mysql-test/main/func_json.result @@ -1623,5 +1623,17 @@ id doc {"$oid":"611c0a463b150154132f6636"} { "_id" : { "$oid" : "611c0a463b150154132f6636" }, "a" : [ { "a" : [ { "a" : [ { "a" : [ { "a" : [ { "a" : [ { "a" : [ { "a" : [ { "a" : [ { "a" : [ { "a" : [ { "a" : [ { "a" : [ { "a" : [ { "a" : [ { "a" : 1.0 } ] } ] } ] } ] } ] } ] } ] } ] } ] } ] } ] } ] } ] } ] } ] } DROP TABLE arrNestTest; # +# MDEV-30412 JSON_OBJECTAGG doesn't escape double quote in key +# +SELECT JSON_OBJECTAGG('"', 1); +JSON_OBJECTAGG('"', 1) +{"\"":1} +SELECT JSON_OBJECTAGG('\"', 1); +JSON_OBJECTAGG('\"', 1) +{"\"":1} +SELECT JSON_OBJECTAGG('\\', 1); +JSON_OBJECTAGG('\\', 1) +{"\\":1} +# # End of 10.5 tests # diff --git a/mysql-test/main/func_json.test b/mysql-test/main/func_json.test index 35645bcb226..0a5d1638717 100644 --- a/mysql-test/main/func_json.test +++ b/mysql-test/main/func_json.test @@ -1067,6 +1067,14 @@ INSERT INTO test.arrNestTest (doc) VALUES ('{ "_id" : { "$oid" : "611c0a463b1501 SELECT * FROM arrNestTest; DROP TABLE arrNestTest; +--echo # +--echo # MDEV-30412 JSON_OBJECTAGG doesn't escape double quote in key +--echo # + +SELECT JSON_OBJECTAGG('"', 1); +SELECT JSON_OBJECTAGG('\"', 1); +SELECT JSON_OBJECTAGG('\\', 1); + --echo # --echo # End of 10.5 tests --echo # diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index 9843b6d14b1..b85adeff05d 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -4060,7 +4060,7 @@ bool Item_func_json_objectagg::add() result.append(", "); result.append("\""); - result.append(*key); + st_append_escaped(&result,key); result.append("\":"); buf.length(0); From 3eea2e8e10b6d8f5280e40478dcaac8961b74de4 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Tue, 14 Feb 2023 14:35:35 +0530 Subject: [PATCH 2/3] MDEV-30551 InnoDB recovery hangs when buffer pool ran out of memory - During non-last batch of multi-batch recovery, InnoDB holds log_sys.mutex and preallocates the block which may intiate page flush, which may initiate log flush, which requires log_sys.mutex to acquire again. This leads to assert failure. So InnoDB recovery should release log_sys.mutex before preallocating the block. --- mysql-test/suite/innodb/r/alter_copy.result | 2 +- mysql-test/suite/innodb/t/alter_copy.test | 2 +- storage/innobase/buf/buf0lru.cc | 9 ++++++++- storage/innobase/log/log0recv.cc | 18 ++++++++++++++++++ 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/innodb/r/alter_copy.result b/mysql-test/suite/innodb/r/alter_copy.result index 4d6d55f01cf..22916598083 100644 --- a/mysql-test/suite/innodb/r/alter_copy.result +++ b/mysql-test/suite/innodb/r/alter_copy.result @@ -51,7 +51,7 @@ ADD INDEX(a,b,d), ADD INDEX(a,d,b), ADD INDEX(b,c,d), ADD INDEX(b,d,c), ALGORITHM=COPY; connection default; SET DEBUG_SYNC='now WAIT_FOR hung'; -# restart: --innodb-force-recovery=3 +# restart: --innodb-force-recovery=3 --debug_dbug=+d,recv_ran_out_of_buffer disconnect hang; #sql-alter.frm #sql-alter.ibd diff --git a/mysql-test/suite/innodb/t/alter_copy.test b/mysql-test/suite/innodb/t/alter_copy.test index e67f6f9a66b..f321308ce26 100644 --- a/mysql-test/suite/innodb/t/alter_copy.test +++ b/mysql-test/suite/innodb/t/alter_copy.test @@ -57,7 +57,7 @@ ALTER TABLE t ADD INDEX(b,c,d,a),ADD INDEX(b,c,a,d),ADD INDEX(b,a,c,d),ADD INDEX connection default; SET DEBUG_SYNC='now WAIT_FOR hung'; let $shutdown_timeout=0; ---let $restart_parameters= --innodb-force-recovery=3 +--let $restart_parameters= --innodb-force-recovery=3 --debug_dbug="+d,recv_ran_out_of_buffer" --source include/restart_mysqld.inc disconnect hang; let $shutdown_timeout=; diff --git a/storage/innobase/buf/buf0lru.cc b/storage/innobase/buf/buf0lru.cc index f7a3543c493..6d83d448bd6 100644 --- a/storage/innobase/buf/buf0lru.cc +++ b/storage/innobase/buf/buf0lru.cc @@ -408,6 +408,11 @@ buf_block_t *buf_LRU_get_free_block(bool have_mutex) mysql_mutex_assert_owner(&buf_pool.mutex); goto got_mutex; } + DBUG_EXECUTE_IF("recv_ran_out_of_buffer", + if (recv_recovery_is_on() + && recv_sys.apply_log_recs) { + goto flush_lru; + }); mysql_mutex_lock(&buf_pool.mutex); got_mutex: buf_LRU_check_size_of_non_data_objects(); @@ -493,7 +498,9 @@ not_found: removing the block from buf_pool.page_hash and buf_pool.LRU is fairly involved (particularly in case of ROW_FORMAT=COMPRESSED pages). We can do that in a separate patch sometime in future. */ - +#ifndef DBUG_OFF +flush_lru: +#endif if (!buf_flush_LRU(innodb_lru_flush_size)) { MONITOR_INC(MONITOR_LRU_SINGLE_FLUSH_FAILURE_COUNT); ++flush_failures; diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc index 4a6527962f6..eb2931adf0e 100644 --- a/storage/innobase/log/log0recv.cc +++ b/storage/innobase/log/log0recv.cc @@ -2692,8 +2692,23 @@ void recv_sys_t::apply(bool last_batch) fil_system.extend_to_recv_size(); + /* Release the log_sys mutex in non-last batches of multi-batch + recovery mode and recv_sys.mutex before preallocating the + block because while preallocating the block which may initiate + log flush which requires log_sys mutex to acquire again, which + should be acquired before recv_sys.mutex in order to avoid + deadlocks. */ + mutex_exit(&mutex); + if (!last_batch) + mysql_mutex_unlock(&log_sys.mutex); + + mysql_mutex_assert_not_owner(&log_sys.mutex); buf_block_t *free_block= buf_LRU_get_free_block(false); + if (!last_batch) + mysql_mutex_lock(&log_sys.mutex); + mutex_enter(&mutex); + for (map::iterator p= pages.begin(); p != pages.end(); ) { const page_id_t page_id= p->first; @@ -2708,7 +2723,10 @@ void recv_sys_t::apply(bool last_batch) if (UNIV_LIKELY(!!recover_low(page_id, p, mtr, free_block))) { mutex_exit(&mutex); + if (!last_batch) mysql_mutex_unlock(&log_sys.mutex); + mysql_mutex_assert_not_owner(&log_sys.mutex); free_block= buf_LRU_get_free_block(false); + if (!last_batch) mysql_mutex_lock(&log_sys.mutex); mutex_enter(&mutex); break; } From 1a5c7552ea3a0233e7abff56a167c3a532c7da0a Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Tue, 14 Feb 2023 14:36:17 +0530 Subject: [PATCH 3/3] MDEV-30552 InnoDB recovery crashes when error handling scenario - InnoDB fails to reset the after_apply variable before applying the redo log in last batch during multi-batch recovery. --- .../suite/innodb/r/recovery_memory.result | 20 ++++++++++++++++++ .../suite/innodb/t/recovery_memory.test | 21 +++++++++++++++++++ storage/innobase/ibuf/ibuf0ibuf.cc | 4 ++++ storage/innobase/log/log0recv.cc | 5 +++++ 4 files changed, 50 insertions(+) diff --git a/mysql-test/suite/innodb/r/recovery_memory.result b/mysql-test/suite/innodb/r/recovery_memory.result index 4fa31009130..6faea097616 100644 --- a/mysql-test/suite/innodb/r/recovery_memory.result +++ b/mysql-test/suite/innodb/r/recovery_memory.result @@ -1,3 +1,7 @@ +call mtr.add_suppression("InnoDB: The change buffer is corrupted"); +call mtr.add_suppression("InnoDB: Plugin initialization aborted at srv0start.cc"); +call mtr.add_suppression("Plugin 'InnoDB' init function returned error"); +call mtr.add_suppression("Plugin 'InnoDB' registration as a STORAGE ENGINE failed."); CREATE TABLE t1(c TEXT, KEY(c(3072)))ENGINE=InnoDB; CREATE PROCEDURE dorepeat() LOOP @@ -10,3 +14,19 @@ CALL dorepeat(); connection default; # restart: --innodb_buffer_pool_size=5242880 DROP TABLE t1; +DROP PROCEDURE dorepeat; +# +# MDEV-30552 InnoDB recovery crashes when error +# handling scenario +# +SET DEBUG_DBUG="+d,ib_log_checkpoint_avoid_hard"; +CREATE TABLE t1(f1 INT NOT NULL)ENGINE=InnoDB; +INSERT INTO t1 SELECT * FROM seq_1_to_65536; +# restart: --innodb_buffer_pool_size=5242880 --debug_dbug=+d,ibuf_init_corrupt +# restart +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `f1` int(11) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/t/recovery_memory.test b/mysql-test/suite/innodb/t/recovery_memory.test index d9afd52c499..e723ba25d36 100644 --- a/mysql-test/suite/innodb/t/recovery_memory.test +++ b/mysql-test/suite/innodb/t/recovery_memory.test @@ -1,5 +1,10 @@ --source include/have_innodb.inc --source include/big_test.inc +--source include/have_sequence.inc +call mtr.add_suppression("InnoDB: The change buffer is corrupted"); +call mtr.add_suppression("InnoDB: Plugin initialization aborted at srv0start.cc"); +call mtr.add_suppression("Plugin 'InnoDB' init function returned error"); +call mtr.add_suppression("Plugin 'InnoDB' registration as a STORAGE ENGINE failed."); CREATE TABLE t1(c TEXT, KEY(c(3072)))ENGINE=InnoDB; DELIMITER |; @@ -19,3 +24,19 @@ let $shutdown_timeout=0; let $restart_parameters=--innodb_buffer_pool_size=5242880; --source include/restart_mysqld.inc DROP TABLE t1; +DROP PROCEDURE dorepeat; + +--echo # +--echo # MDEV-30552 InnoDB recovery crashes when error +--echo # handling scenario +--echo # +SET DEBUG_DBUG="+d,ib_log_checkpoint_avoid_hard"; +CREATE TABLE t1(f1 INT NOT NULL)ENGINE=InnoDB; +INSERT INTO t1 SELECT * FROM seq_1_to_65536; +let $shutdown_timeout=0; +let $restart_parameters=--innodb_buffer_pool_size=5242880 --debug_dbug="+d,ibuf_init_corrupt"; +--source include/restart_mysqld.inc +let $restart_parameters=; +--source include/restart_mysqld.inc +SHOW CREATE TABLE t1; +DROP TABLE t1; diff --git a/storage/innobase/ibuf/ibuf0ibuf.cc b/storage/innobase/ibuf/ibuf0ibuf.cc index d88c2b0027e..dff0ad57057 100644 --- a/storage/innobase/ibuf/ibuf0ibuf.cc +++ b/storage/innobase/ibuf/ibuf0ibuf.cc @@ -450,6 +450,10 @@ err_exit: root = buf_block_get_frame(block); } + DBUG_EXECUTE_IF("ibuf_init_corrupt", + err = DB_CORRUPTION; + goto err_exit;); + if (page_is_comp(root) || fil_page_get_type(root) != FIL_PAGE_INDEX || btr_page_get_index_id(root) != DICT_IBUF_ID_MIN) { err = DB_CORRUPTION; diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc index eb2931adf0e..9b4ceb4f03e 100644 --- a/storage/innobase/log/log0recv.cc +++ b/storage/innobase/log/log0recv.cc @@ -3642,6 +3642,11 @@ completed: mysql_mutex_unlock(&log_sys.mutex); return(DB_ERROR); } + + /* In case of multi-batch recovery, + redo log for the last batch is not + applied yet. */ + ut_d(recv_sys.after_apply = false); } } else { ut_ad(!rescan || recv_sys.pages.empty());