From 9b4dfdaa5a1e1ca84a2e9e65dd3066b382f65ae7 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Thu, 18 Jan 2018 15:25:40 +0300 Subject: [PATCH 1/7] MDEV-13352: Server crashes in st_join_table::remove_duplicates join_tab->distinct=true means "Before doing record read with this join_tab, call join_tab->remove_duplicates() to eliminate duplicates". remove_duplicates() assumes that - there is a temporary table $T with rows that are to be de-duplicated - there is a previous join_tab (e.g. with join_tab->fields) which was used to populate the temp.table $T. When the query has "Impossible WHERE" and window function, then the above conditions are not met (but we still might need a window function computation step when the query has implicit grouping). The fix is to not add remove_duplicates step if the select execution is degenerate (and we'll have at most one row in the output anyway). --- mysql-test/r/win.result | 10 ++++++++++ mysql-test/t/win.test | 10 ++++++++++ sql/sql_select.cc | 10 +++++++++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/win.result b/mysql-test/r/win.result index 7e0c86b1668..e3cb40e8343 100644 --- a/mysql-test/r/win.result +++ b/mysql-test/r/win.result @@ -3289,3 +3289,13 @@ SELECT id, window FROM door as window; 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 'window' at line 2 DROP TABLE door; +# +# MDEV-13352: Server crashes in st_join_table::remove_duplicates +# +CREATE TABLE t1 (i INT); +INSERT INTO t1 VALUES (1),(2); +SELECT DISTINCT ROW_NUMBER() OVER(), i FROM t1 WHERE 0; +ROW_NUMBER() OVER() i +SELECT ROW_NUMBER() OVER(), i FROM t1 WHERE 0; +ROW_NUMBER() OVER() i +DROP TABLE t1; diff --git a/mysql-test/t/win.test b/mysql-test/t/win.test index 1e747e59a1a..95ffb6d9909 100644 --- a/mysql-test/t/win.test +++ b/mysql-test/t/win.test @@ -2057,3 +2057,13 @@ SELECT id, window FROM door as window; DROP TABLE door; + +--echo # +--echo # MDEV-13352: Server crashes in st_join_table::remove_duplicates +--echo # +CREATE TABLE t1 (i INT); +INSERT INTO t1 VALUES (1),(2); +SELECT DISTINCT ROW_NUMBER() OVER(), i FROM t1 WHERE 0; +SELECT ROW_NUMBER() OVER(), i FROM t1 WHERE 0; +DROP TABLE t1; + diff --git a/sql/sql_select.cc b/sql/sql_select.cc index e80c02b52b4..6b1d406bf8a 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2670,7 +2670,15 @@ bool JOIN::make_aggr_tables_info() curr_tab->having= having; having->update_used_tables(); } - curr_tab->distinct= true; + /* + We only need DISTINCT operation if the join is not degenerate. + If it is, we must not request DISTINCT processing, because + remove_duplicates() assumes there is a preceding computation step (and + in the degenerate join, there's none) + */ + if (top_join_tab_count) + curr_tab->distinct= true; + having= NULL; select_distinct= false; } From d04e1d4bdc956e6c8768df895b9d7607b543fff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 22 Jan 2018 15:27:24 +0200 Subject: [PATCH 2/7] MDEV-15029 XA COMMIT and XA ROLLBACK operate on freed transaction object innobase_commit_by_xid(), innobase_rollback_by_xid(): Decrement the reference count before freeing the transaction object to the pool. Failure to do so might corrupt the transaction bookkeeping if trx_create_low() returns the same object to another thread before we are done with it. trx_sys_close(): Detach the recovered XA PREPARE transactions from trx_sys->rw_trx_list before freeing them. --- storage/innobase/handler/ha_innodb.cc | 26 +++++++++++++++----------- storage/innobase/trx/trx0sys.cc | 10 +++------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index bfcf8dfe741..3ed43155e70 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -17881,12 +17881,14 @@ innobase_commit_by_xid( } if (trx_t* trx = trx_get_trx_by_xid(xid)) { - TrxInInnoDB trx_in_innodb(trx); - - innobase_commit_low(trx); - ut_ad(trx->mysql_thd == NULL); + ut_ad(trx->in_innodb & TRX_FORCE_ROLLBACK_DISABLE); /* use cases are: disconnected xa, slave xa, recovery */ - trx_deregister_from_2pc(trx); + { + TrxInInnoDB trx_in_innodb(trx); + innobase_commit_low(trx); + ut_ad(trx->mysql_thd == NULL); + trx_deregister_from_2pc(trx); + } ut_ad(!trx->will_lock); /* trx cache requirement */ trx_free_for_background(trx); @@ -17915,12 +17917,14 @@ innobase_rollback_by_xid( } if (trx_t* trx = trx_get_trx_by_xid(xid)) { - TrxInInnoDB trx_in_innodb(trx); - - int ret = innobase_rollback_trx(trx); - - trx_deregister_from_2pc(trx); - ut_ad(!trx->will_lock); + int ret; + ut_ad(trx->in_innodb & TRX_FORCE_ROLLBACK_DISABLE); + { + TrxInInnoDB trx_in_innodb(trx); + ret = innobase_rollback_trx(trx); + trx_deregister_from_2pc(trx); + ut_ad(!trx->will_lock); + } trx_free_for_background(trx); return(ret); diff --git a/storage/innobase/trx/trx0sys.cc b/storage/innobase/trx/trx0sys.cc index 3a798a504fc..393dff8c026 100644 --- a/storage/innobase/trx/trx0sys.cc +++ b/storage/innobase/trx/trx0sys.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, MariaDB Corporation. +Copyright (c) 2017, 2018, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -939,13 +939,9 @@ trx_sys_close(void) || srv_read_only_mode || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO); - for (trx_t* trx = UT_LIST_GET_FIRST(trx_sys->rw_trx_list); - trx != NULL; - trx = UT_LIST_GET_FIRST(trx_sys->rw_trx_list)) { - - trx_free_prepared(trx); - + while (trx_t* trx = UT_LIST_GET_FIRST(trx_sys->rw_trx_list)) { UT_LIST_REMOVE(trx_sys->rw_trx_list, trx); + trx_free_prepared(trx); } /* There can't be any active transactions. */ From 30f1d2f642a0f9702e799b0153b28f0d402a8073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 22 Jan 2018 16:29:43 +0200 Subject: [PATCH 3/7] Remove useless method LatchCounter::sum_deregister() --- storage/innobase/include/sync0policy.h | 9 +-------- storage/innobase/include/sync0types.h | 10 +--------- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/storage/innobase/include/sync0policy.h b/storage/innobase/include/sync0policy.h index 1b86d2633bf..de27c87816c 100644 --- a/storage/innobase/include/sync0policy.h +++ b/storage/innobase/include/sync0policy.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2013, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, MariaDB Corporation. +Copyright (c) 2017, 2018, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -454,14 +454,7 @@ public: void destroy() UNIV_NOTHROW { - latch_meta_t& meta = sync_latch_get_meta(m_id); - - ut_ad(meta.get_id() == m_id); - - meta.get_counter()->sum_deregister(m_count); - m_count = NULL; - ut_d(MutexDebug::destroy()); } diff --git a/storage/innobase/include/sync0types.h b/storage/innobase/include/sync0types.h index d7583f87f3b..711225041bc 100644 --- a/storage/innobase/include/sync0types.h +++ b/storage/innobase/include/sync0types.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, MariaDB Corporation. +Copyright (c) 2017, 2018, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -640,14 +640,6 @@ public: return(count); } - /** Deregister the count. We don't do anything - @param[in] count The count instance to deregister */ - void sum_deregister(Count* count) - UNIV_NOTHROW - { - /* Do nothing */ - } - /** Register a single instance counter */ void single_register(Count* count) UNIV_NOTHROW From 89ae5d7f2fca3efdea69bf13abb1e2785af7fe10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 22 Jan 2018 16:30:38 +0200 Subject: [PATCH 4/7] Allocate mutex_monitor, create_tracker statically --- storage/innobase/handler/ha_innodb.cc | 10 +++++----- storage/innobase/include/ut0mutex.h | 2 +- storage/innobase/sync/sync0debug.cc | 24 +++++------------------- storage/innobase/sync/sync0sync.cc | 2 +- 4 files changed, 12 insertions(+), 26 deletions(-) diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 3ed43155e70..a17abf199fe 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -16740,7 +16740,7 @@ innodb_show_mutex_status( DBUG_ASSERT(hton == innodb_hton_ptr); - mutex_monitor->iterate(collector); + mutex_monitor.iterate(collector); if (!collector.to_string(hton, thd, stat_print)) { DBUG_RETURN(1); @@ -18907,7 +18907,7 @@ innodb_monitor_set_option( if (MONITOR_IS_ON(MONITOR_LATCHES)) { - mutex_monitor->enable(); + mutex_monitor.enable(); } break; @@ -18922,7 +18922,7 @@ innodb_monitor_set_option( if (!MONITOR_IS_ON(MONITOR_LATCHES)) { - mutex_monitor->disable(); + mutex_monitor.disable(); } break; @@ -18931,13 +18931,13 @@ innodb_monitor_set_option( if (monitor_id == (MONITOR_LATCHES)) { - mutex_monitor->reset(); + mutex_monitor.reset(); } break; case MONITOR_RESET_ALL_VALUE: srv_mon_reset_all(monitor_id); - mutex_monitor->reset(); + mutex_monitor.reset(); break; default: diff --git a/storage/innobase/include/ut0mutex.h b/storage/innobase/include/ut0mutex.h index bd3603ad4d0..dc387dadbdc 100644 --- a/storage/innobase/include/ut0mutex.h +++ b/storage/innobase/include/ut0mutex.h @@ -164,7 +164,7 @@ public: }; /** Defined in sync0sync.cc */ -extern MutexMonitor* mutex_monitor; +extern MutexMonitor mutex_monitor; /** Creates, or rather, initializes a mutex object in a specified memory diff --git a/storage/innobase/sync/sync0debug.cc b/storage/innobase/sync/sync0debug.cc index fa7a99ea4c0..793c6a59e33 100644 --- a/storage/innobase/sync/sync0debug.cc +++ b/storage/innobase/sync/sync0debug.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2014, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, MariaDB Corporation. +Copyright (c) 2017, 2018, MariaDB Corporation. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -1705,7 +1705,7 @@ private: }; /** Track latch creation location. For reducing the size of the latches */ -static CreateTracker* create_tracker; +static CreateTracker create_tracker; /** Register a latch, called when it is created @param[in] ptr Latch instance that was created @@ -1717,7 +1717,7 @@ sync_file_created_register( const char* filename, uint16_t line) { - create_tracker->register_latch(ptr, filename, line); + create_tracker.register_latch(ptr, filename, line); } /** Deregister a latch, called when it is destroyed @@ -1725,7 +1725,7 @@ sync_file_created_register( void sync_file_created_deregister(const void* ptr) { - create_tracker->deregister_latch(ptr); + create_tracker.deregister_latch(ptr); } /** Get the string where the file was created. Its format is "name:line" @@ -1734,7 +1734,7 @@ sync_file_created_deregister(const void* ptr) std::string sync_file_created_get(const void* ptr) { - return(create_tracker->get(ptr)); + return(create_tracker.get(ptr)); } /** Initializes the synchronization data structures. */ @@ -1744,12 +1744,6 @@ sync_check_init() ut_ad(!LatchDebug::s_initialized); ut_d(LatchDebug::s_initialized = true); - /** For collecting latch statistic - SHOW ... MUTEX */ - mutex_monitor = UT_NEW_NOKEY(MutexMonitor()); - - /** For trcking mutex creation location */ - create_tracker = UT_NEW_NOKEY(CreateTracker()); - sync_latch_meta_init(); /* Init the rw-lock & mutex list and create the mutex to protect it. */ @@ -1773,14 +1767,6 @@ sync_check_close() sync_array_close(); - UT_DELETE(mutex_monitor); - - mutex_monitor = NULL; - - UT_DELETE(create_tracker); - - create_tracker = NULL; - sync_latch_meta_destroy(); } diff --git a/storage/innobase/sync/sync0sync.cc b/storage/innobase/sync/sync0sync.cc index 4be7162f631..16dd90cd879 100644 --- a/storage/innobase/sync/sync0sync.cc +++ b/storage/innobase/sync/sync0sync.cc @@ -118,7 +118,7 @@ mysql_pfs_key_t trx_purge_latch_key; #endif /* UNIV_PFS_RWLOCK */ /** For monitoring active mutexes */ -MutexMonitor* mutex_monitor; +MutexMonitor mutex_monitor; /** Prints wait info of the sync system. From 29eeb527fd4496eeb852f4aec80a43e60a1bc10c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 22 Jan 2018 16:53:33 +0200 Subject: [PATCH 5/7] MDEV-12173 "[Warning] Trying to access missing tablespace" ibuf_merge_or_delete_for_page(): Invoke fil_space_acquire_silent() instead of fil_space_acquire() in order to avoid displaying a useless message. We know perfectly well that a tablespace can be dropped while a change buffer merge is pending, because change buffer merges skip any transactional locks. --- storage/innobase/ibuf/ibuf0ibuf.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/innobase/ibuf/ibuf0ibuf.cc b/storage/innobase/ibuf/ibuf0ibuf.cc index d26391a80f4..7041ad53f0d 100644 --- a/storage/innobase/ibuf/ibuf0ibuf.cc +++ b/storage/innobase/ibuf/ibuf0ibuf.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2016, 2017, MariaDB Corporation. +Copyright (c) 2016, 2018, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -4492,7 +4492,7 @@ ibuf_merge_or_delete_for_page( return; } - space = fil_space_acquire(page_id.space()); + space = fil_space_acquire_silent(page_id.space()); if (UNIV_UNLIKELY(!space)) { /* Do not try to read the bitmap page from the From 87db5eb8130a58bd7556bda8a5637dfef982d51a Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 23 Jan 2018 09:12:25 +0000 Subject: [PATCH 6/7] MDEV-13825 mariabackup --lock-ddl-per-table does not properly lock FULLTEXT auxiliary tables Change the logic to take mdl lock on all tables before tablespaces are copied, rather than lock every single tablespace just before it is copied. --- extra/mariabackup/xtrabackup.cc | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/extra/mariabackup/xtrabackup.cc b/extra/mariabackup/xtrabackup.cc index 375d8845a2a..7eee64bd6ef 100644 --- a/extra/mariabackup/xtrabackup.cc +++ b/extra/mariabackup/xtrabackup.cc @@ -433,6 +433,22 @@ datafiles_iter_free(datafiles_iter_t *it) free(it); } +void mdl_lock_all() +{ + mdl_lock_init(); + datafiles_iter_t *it = datafiles_iter_new(fil_system); + if (!it) + return; + + while (fil_node_t *node = datafiles_iter_next(it)){ + if (fil_is_user_tablespace_id(node->space->id) + && check_if_skip_table(node->space->name)) + continue; + + mdl_lock_table(node->space->id); + } + datafiles_iter_free(it); +} /* ======== Date copying thread context ======== */ typedef struct { @@ -2199,10 +2215,6 @@ xtrabackup_copy_datafile(fil_node_t* node, uint thread_n) return(FALSE); } - if (opt_lock_ddl_per_table) { - mdl_lock_table(node->space->id); - } - if (!changed_page_bitmap) { read_filter = &rf_pass_through; } @@ -3562,9 +3574,7 @@ xtrabackup_backup_func() "or RENAME TABLE during the backup, inconsistent backup will be " "produced.\n"); - if (opt_lock_ddl_per_table) { - mdl_lock_init(); - } + /* initialize components */ if(innodb_init_param()) { @@ -3879,6 +3889,10 @@ reread_log_header: "files transfer\n", xtrabackup_parallel); } + if (opt_lock_ddl_per_table) { + mdl_lock_all(); + } + it = datafiles_iter_new(fil_system); if (it == NULL) { msg("mariabackup: Error: datafiles_iter_new() failed.\n"); From 7cc507f22e6eaec5ec83e24cd45275656bc7962f Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Tue, 23 Jan 2018 17:12:29 +0400 Subject: [PATCH 7/7] MDEV-14603 signal 11 with short stacktrace --- mysql-test/r/ps.result | 42 +++++++++++++++++++++++++++++ mysql-test/t/ps.test | 57 +++++++++++++++++++++++++++++++++++++++ sql/sql_class.h | 19 +++++++++++++ sql/sql_prepare.cc | 61 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 179 insertions(+) diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 1958cafca1e..96d4b3d2f70 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -5121,3 +5121,45 @@ END; $$ ERROR 22007: Incorrect datetime value: '10' for column 'a' at row 1 DROP PROCEDURE p1; +# +# MDEV-14603 signal 11 with short stacktrace +# +SET NAMES utf8; +CREATE TABLE t1(i INT); +CREATE PROCEDURE p1(tn VARCHAR(32)) +EXECUTE IMMEDIATE CONCAT('ANALYZE TABLE ',tn); +CALL p1('t1'); +Table Op Msg_type Msg_text +test.t1 analyze status Table is already up to date +DROP PROCEDURE p1; +DROP TABLE t1; +SET NAMES utf8; +CREATE PROCEDURE p1() +EXECUTE IMMEDIATE CONCAT('SELECT ',CONVERT(RAND() USING latin1)); +CALL p1(); +DROP PROCEDURE p1; +SET NAMES utf8; +CREATE PROCEDURE p1() +BEGIN +PREPARE stmt FROM CONCAT('SELECT ',CONVERT(RAND() USING latin1)); +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +END; +$$ +CALL p1(); +DROP PROCEDURE p1; +SET NAMES utf8; +CREATE PROCEDURE p1(a VARCHAR(10) CHARACTER SET utf8) +EXECUTE IMMEDIATE 'SELECT ?' USING CONCAT(a, CONVERT(RAND() USING latin1)); +CALL p1('x'); +DROP PROCEDURE p1; +SET NAMES utf8; +CREATE PROCEDURE p1(a VARCHAR(10) CHARACTER SET utf8) +BEGIN +PREPARE stmt FROM 'SELECT ?'; +EXECUTE stmt USING CONCAT(a, CONVERT(RAND() USING latin1)); +DEALLOCATE PREPARE stmt; +END; +$$ +CALL p1('x'); +DROP PROCEDURE p1; diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index 327a94cdace..565f831efdd 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -4578,3 +4578,60 @@ END; $$ DELIMITER ;$$ DROP PROCEDURE p1; + +--echo # +--echo # MDEV-14603 signal 11 with short stacktrace +--echo # + +SET NAMES utf8; +CREATE TABLE t1(i INT); +CREATE PROCEDURE p1(tn VARCHAR(32)) + EXECUTE IMMEDIATE CONCAT('ANALYZE TABLE ',tn); +CALL p1('t1'); +DROP PROCEDURE p1; +DROP TABLE t1; + +SET NAMES utf8; +CREATE PROCEDURE p1() + EXECUTE IMMEDIATE CONCAT('SELECT ',CONVERT(RAND() USING latin1)); +--disable_result_log +CALL p1(); +--enable_result_log +DROP PROCEDURE p1; + +SET NAMES utf8; +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + PREPARE stmt FROM CONCAT('SELECT ',CONVERT(RAND() USING latin1)); + EXECUTE stmt; + DEALLOCATE PREPARE stmt; +END; +$$ +DELIMITER ;$$ +--disable_result_log +CALL p1(); +--enable_result_log +DROP PROCEDURE p1; + +SET NAMES utf8; +CREATE PROCEDURE p1(a VARCHAR(10) CHARACTER SET utf8) + EXECUTE IMMEDIATE 'SELECT ?' USING CONCAT(a, CONVERT(RAND() USING latin1)); +--disable_result_log +CALL p1('x'); +--enable_result_log +DROP PROCEDURE p1; + +SET NAMES utf8; +DELIMITER $$; +CREATE PROCEDURE p1(a VARCHAR(10) CHARACTER SET utf8) +BEGIN + PREPARE stmt FROM 'SELECT ?'; + EXECUTE stmt USING CONCAT(a, CONVERT(RAND() USING latin1)); + DEALLOCATE PREPARE stmt; +END; +$$ +DELIMITER ;$$ +--disable_result_log +CALL p1('x'); +DROP PROCEDURE p1; diff --git a/sql/sql_class.h b/sql/sql_class.h index bcd43cd62cd..5a409ec8268 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1296,6 +1296,25 @@ public: }; +class Item_change_list_savepoint: public Item_change_list +{ +public: + Item_change_list_savepoint(Item_change_list *list) + { + list->move_elements_to(this); + } + void rollback(Item_change_list *list) + { + list->rollback_item_tree_changes(); + move_elements_to(list); + } + ~Item_change_list_savepoint() + { + DBUG_ASSERT(is_empty()); + } +}; + + /** Type of locked tables mode. See comment for THD::locked_tables_mode for complete description. diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 4e847fb9ff3..16c386e5e8f 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2823,6 +2823,25 @@ void mysql_sql_stmt_prepare(THD *thd) DBUG_VOID_RETURN; } + /* + Make sure we call Prepared_statement::prepare() with an empty + THD::change_list. It can be non-empty as LEX::get_dynamic_sql_string() + calls fix_fields() for the Item containing the PS source, + e.g. on character set conversion: + + SET NAMES utf8; + DELIMITER $$ + CREATE PROCEDURE p1() + BEGIN + PREPARE stmt FROM CONCAT('SELECT ',CONVERT(RAND() USING latin1)); + EXECUTE stmt; + END; + $$ + DELIMITER ; + CALL p1(); + */ + Item_change_list_savepoint change_list_savepoint(thd); + if (stmt->prepare(query.str, (uint) query.length)) { /* Statement map deletes the statement on erase */ @@ -2833,6 +2852,7 @@ void mysql_sql_stmt_prepare(THD *thd) SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL); my_ok(thd, 0L, 0L, "Statement prepared"); } + change_list_savepoint.rollback(thd); DBUG_VOID_RETURN; } @@ -2864,7 +2884,28 @@ void mysql_sql_stmt_execute_immediate(THD *thd) // See comments on thd->free_list in mysql_sql_stmt_execute() Item *free_list_backup= thd->free_list; thd->free_list= NULL; + /* + Make sure we call Prepared_statement::execute_immediate() + with an empty THD::change_list. It can be non empty as the above + LEX::prepared_stmt_params_fix_fields() and LEX::get_dynamic_str_string() + call fix_fields() for the PS source and PS parameter Items and + can do Item tree changes, e.g. on character set conversion: + + - Example #1: Item tree changes in get_dynamic_str_string() + SET NAMES utf8; + CREATE PROCEDURE p1() + EXECUTE IMMEDIATE CONCAT('SELECT ',CONVERT(RAND() USING latin1)); + CALL p1(); + + - Example #2: Item tree changes in prepared_stmt_param_fix_fields(): + SET NAMES utf8; + CREATE PROCEDURE p1(a VARCHAR(10) CHARACTER SET utf8) + EXECUTE IMMEDIATE 'SELECT ?' USING CONCAT(a, CONVERT(RAND() USING latin1)); + CALL p1('x'); + */ + Item_change_list_savepoint change_list_savepoint(thd); (void) stmt->execute_immediate(query.str, (uint) query.length); + change_list_savepoint.rollback(thd); thd->free_items(); thd->free_list= free_list_backup; @@ -3262,7 +3303,27 @@ void mysql_sql_stmt_execute(THD *thd) */ Item *free_list_backup= thd->free_list; thd->free_list= NULL; // Hide the external (e.g. "SET STATEMENT") Items + /* + Make sure we call Prepared_statement::execute_loop() with an empty + THD::change_list. It can be non-empty because the above + LEX::prepared_stmt_params_fix_fields() calls fix_fields() for + the PS parameter Items and can do some Item tree changes, + e.g. on character set conversion: + + SET NAMES utf8; + DELIMITER $$ + CREATE PROCEDURE p1(a VARCHAR(10) CHARACTER SET utf8) + BEGIN + PREPARE stmt FROM 'SELECT ?'; + EXECUTE stmt USING CONCAT(a, CONVERT(RAND() USING latin1)); + END; + $$ + DELIMITER ; + CALL p1('x'); + */ + Item_change_list_savepoint change_list_savepoint(thd); (void) stmt->execute_loop(&expanded_query, FALSE, NULL, NULL); + change_list_savepoint.rollback(thd); thd->free_items(); // Free items created by execute_loop() /* Now restore the "external" (e.g. "SET STATEMENT") Item list.