diff --git a/mysql-test/main/sp_validation.result b/mysql-test/main/sp_validation.result index 324ac71cb1a..192a5e1d240 100644 --- a/mysql-test/main/sp_validation.result +++ b/mysql-test/main/sp_validation.result @@ -2122,6 +2122,23 @@ i 2 1 1 +DROP TEMPORARY TABLE t1; DROP PACKAGE BODY pkg1; DROP PACKAGE pkg1; SET sql_mode=@save_sql_mode; +# +# MDEV-40004: Server crashes in sp_head::register_instr_mem_root_for_deallocation upon shutdown +# +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1),(2); +CREATE TRIGGER tr BEFORE UPDATE ON t1 FOR EACH ROW DELETE FROM v1; +CREATE VIEW v1 AS SELECT 0 AS a; +UPDATE t1 SET a = 3; +ERROR HY000: The target table v1 of the DELETE is not updatable +CREATE OR REPLACE VIEW v1 AS SELECT 0 AS a; +UPDATE t1 SET a = 4; +ERROR HY000: The target table v1 of the DELETE is not updatable +# restart +# Clean up +DROP VIEW v1; +DROP TABLE t1; diff --git a/mysql-test/main/sp_validation.test b/mysql-test/main/sp_validation.test index 839024148dc..fc84c6de975 100644 --- a/mysql-test/main/sp_validation.test +++ b/mysql-test/main/sp_validation.test @@ -2923,9 +2923,33 @@ $ --delimiter ; CALL pkg1.p2(); +DROP TEMPORARY TABLE t1; DROP PACKAGE BODY pkg1; DROP PACKAGE pkg1; SET sql_mode=@save_sql_mode; +--echo # +--echo # MDEV-40004: Server crashes in sp_head::register_instr_mem_root_for_deallocation upon shutdown +--echo # +CREATE TABLE t1 (a INT); + +INSERT INTO t1 VALUES (1),(2); +CREATE TRIGGER tr BEFORE UPDATE ON t1 FOR EACH ROW DELETE FROM v1; +CREATE VIEW v1 AS SELECT 0 AS a; + +--error ER_NON_UPDATABLE_TABLE +UPDATE t1 SET a = 3; + +CREATE OR REPLACE VIEW v1 AS SELECT 0 AS a; + +--error ER_NON_UPDATABLE_TABLE +UPDATE t1 SET a = 4; + +--source include/restart_mysqld.inc + +--echo # Clean up +DROP VIEW v1; +DROP TABLE t1; + --enable_ps2_protocol diff --git a/sql/mysqld.cc b/sql/mysqld.cc index f8befb83cf2..a8c382f93cb 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1565,7 +1565,7 @@ void handle_connections_sockets(); static bool read_init_file(char *file_name); pthread_handler_t handle_slave(void *arg); -static void clean_up(bool print_message); +static void clean_up(bool print_message, bool use_dummy_thd= false); static int test_if_case_insensitive(const char *dir_name); #ifndef EMBEDDED_LIBRARY @@ -1974,7 +1974,7 @@ static void mysqld_exit(int exit_code) #endif /* !EMBEDDED_LIBRARY */ -static void clean_up(bool print_message) +static void clean_up(bool print_message, bool use_dummy_thd) { DBUG_PRINT("exit",("clean_up")); if (cleanup_done++) @@ -2009,7 +2009,7 @@ static void clean_up(bool print_message) lex_free(); /* Free some memory */ item_create_cleanup(); cleanup_json_schema_keyword_hash(); - tdc_start_shutdown(); + tdc_start_shutdown(use_dummy_thd); #ifdef HAVE_REPLICATION semi_sync_master_deinit(); #endif @@ -6164,7 +6164,7 @@ int mysqld_main(int argc, char **argv) close_connections(); ha_pre_shutdown(); - clean_up(1); + clean_up(1, true); sd_notify(0, "STATUS=MariaDB server is down"); /* (void) pthread_attr_destroy(&connection_attrib); */ diff --git a/sql/table_cache.cc b/sql/table_cache.cc index 48cd63401e6..7dd8c0c9ba6 100644 --- a/sql/table_cache.cc +++ b/sql/table_cache.cc @@ -632,7 +632,7 @@ bool tdc_init(void) minimal in order to reduce number of references to pluggable engines. */ -void tdc_start_shutdown(void) +void tdc_start_shutdown(bool use_dummy_thd) { DBUG_ENTER("tdc_start_shutdown"); if (tdc_inited) @@ -645,8 +645,50 @@ void tdc_start_shutdown(void) */ tdc_size= 0; tc_size= 0; + + THD *thd= nullptr; + bool reset_back_current_thd= false; + + if (use_dummy_thd && _current_thd() == nullptr) + { + /* + There is no active THD, use the surrogate one to handle memory + allocations in Sql_alloc::operator new() that uses current_thd() + for getting THD to get access to memory allocation API + */ + thd= new THD(0); + DBUG_ASSERT(thd != nullptr); + + thd->store_globals(); + thd->init(); + thd->set_query_inner((char*) STRING_WITH_LEN("intern:tdc_start_shutdown"), + default_charset_info); + + reset_back_current_thd= true; + } /* Free all cached but unused TABLEs and TABLE_SHAREs. */ + /* + purge_tables() iterates along opened tables close every one of them. + In case tables use triggers it results in destroying sp_head objects + for every associated trigger. Destroying an object of the sp_head class + leads to deleting sp_instr objects. In case some of SP instruction + were previously re-parsed by the reason of changes in tables metadata, + the method + sp_head::register_instr_mem_root_for_deallocation() + is called to add a memory root used for re-parsing to the list of + mem_roots to be freed on sp_head destruction. Mem_roots for later + destruction are placed on the List that use current_thd() for memory + allocation. That is the reason why the dummy THD is created before + in case there is no active THD at the moment the function + tdc_start_shutdown is invoked. + */ purge_tables(); + + if (reset_back_current_thd) + { + thd->reset_globals(); + delete thd; + } } DBUG_VOID_RETURN; } diff --git a/sql/table_cache.h b/sql/table_cache.h index 71704ff2ed0..4847e53b63c 100644 --- a/sql/table_cache.h +++ b/sql/table_cache.h @@ -66,7 +66,7 @@ extern ulong tc_size; extern uint32 tc_instances; extern bool tdc_init(void); -extern void tdc_start_shutdown(void); +extern void tdc_start_shutdown(bool use_dummy_thd); extern void tdc_deinit(void); extern ulong tdc_records(void); extern void tdc_purge(bool all);