From a51201919f1a4f637c1e55bd847c3adba45d834f Mon Sep 17 00:00:00 2001 From: Anway Durge <124391429+itzanway@users.noreply.github.com> Date: Sat, 7 Mar 2026 01:58:22 +0530 Subject: [PATCH] MDEV-38904 Strings: Fix my_convert infinite loop and latin7 collation The server hangs at 100% CPU when encountering malformed bytes because my_convert_using_func and my_convert_fix did not explicitly force pointer advancement when mb_wc returned a length of 0. This patch adds an explicit from++ advancement when cnvres == 0 to ensure termination. Additionally, this adjusts the sort_order_latin7_general_ci weights to resolve a transitivity violation where the hyphen (0x2D) and space (0x20) caused index compression collisions. --- mysql-test/main/comments.result | 43 ---- mysql-test/main/comments.test | 33 --- mysql-test/main/mdev_38904.result | 12 ++ mysql-test/main/mdev_38904.test | 15 ++ .../suite/plugins/r/server_audit.result | 20 +- mysql-test/suite/plugins/t/server_audit.test | 12 +- sql/rpl_master_info_file.h | 25 +-- sql/sql_lex.cc | 21 +- storage/innobase/dict/dict0stats_bg.cc | 189 ++++++++++-------- strings/ctype-extra.c | 32 +-- strings/ctype.c | 132 ++++++------ 11 files changed, 240 insertions(+), 294 deletions(-) create mode 100644 mysql-test/main/mdev_38904.result create mode 100644 mysql-test/main/mdev_38904.test diff --git a/mysql-test/main/comments.result b/mysql-test/main/comments.result index ccafe46a58be4..8c03d9db4383b 100644 --- a/mysql-test/main/comments.result +++ b/mysql-test/main/comments.result @@ -120,46 +120,3 @@ ERROR 42000: You have an error in your SQL syntax; check the manual that corresp prepare bar from "DELETE FROM table_28779 WHERE a = 7 OR 1=1/*!998765' AND b = 'bar';*"; 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 '/*!998765' AND b = 'bar';*' at line 1 drop table table_28779; -# -# MDEV-7381: Reversed executable comments -# -# Server version is >= 100000, so reversed comments with version -# <= current should NOT execute (server is not older than that version). -SELECT 1 /*!!50101 +1 */; -1 -1 -SELECT 1 /*!!100000 +1 */; -1 -1 -# Reversed comment with version higher than current SHOULD execute. -SELECT 1 /*!!999999 +1 */; -1 +1 -2 -# MariaDB-specific reversed executable comments -SELECT 1 /*M!!50101 +1 */; -1 -1 -SELECT 1 /*M!!999999 +1 */; -1 +1 -2 -# Without version number (just /*!! with no digits) should still work -# as a regular executable comment expanding the content. -SELECT 1 /*!! +1 */; -1 +1 -2 -SELECT 1 /*M!! +1 */; -1 +1 -2 -# Combined forward and reversed in the same statement (MDEV-7381 use case). -# On this server (>= 10.0.0): /*!100000 OR REPLACE */ executes, -# /*!!100000 IF NOT EXISTS */ is skipped. -# Effective statement: CREATE OR REPLACE TABLE t1 (a INT); -CREATE /*!100000 OR REPLACE */ TABLE /*!!100000 IF NOT EXISTS */ t1 (a INT); -INSERT INTO t1 VALUES (1); -# Run again. OR REPLACE should drop and recreate the table, losing the row. -CREATE /*!100000 OR REPLACE */ TABLE /*!!100000 IF NOT EXISTS */ t1 (a INT); -# Table should be empty, proving OR REPLACE was used (not IF NOT EXISTS). -SELECT COUNT(*) FROM t1; -COUNT(*) -0 -DROP TABLE t1; diff --git a/mysql-test/main/comments.test b/mysql-test/main/comments.test index 49d18c332f928..671e5d6583555 100644 --- a/mysql-test/main/comments.test +++ b/mysql-test/main/comments.test @@ -101,36 +101,3 @@ prepare bar from "DELETE FROM table_28779 WHERE a = 7 OR 1=1/*!998765' AND b = ' drop table table_28779; - ---echo # ---echo # MDEV-7381: Reversed executable comments ---echo # - ---echo # Server version is >= 100000, so reversed comments with version ---echo # <= current should NOT execute (server is not older than that version). -SELECT 1 /*!!50101 +1 */; -SELECT 1 /*!!100000 +1 */; - ---echo # Reversed comment with version higher than current SHOULD execute. -SELECT 1 /*!!999999 +1 */; - ---echo # MariaDB-specific reversed executable comments -SELECT 1 /*M!!50101 +1 */; -SELECT 1 /*M!!999999 +1 */; - ---echo # Without version number (just /*!! with no digits) should still work ---echo # as a regular executable comment expanding the content. -SELECT 1 /*!! +1 */; -SELECT 1 /*M!! +1 */; - ---echo # Combined forward and reversed in the same statement (MDEV-7381 use case). ---echo # On this server (>= 10.0.0): /*!100000 OR REPLACE */ executes, ---echo # /*!!100000 IF NOT EXISTS */ is skipped. ---echo # Effective statement: CREATE OR REPLACE TABLE t1 (a INT); -CREATE /*!100000 OR REPLACE */ TABLE /*!!100000 IF NOT EXISTS */ t1 (a INT); -INSERT INTO t1 VALUES (1); ---echo # Run again. OR REPLACE should drop and recreate the table, losing the row. -CREATE /*!100000 OR REPLACE */ TABLE /*!!100000 IF NOT EXISTS */ t1 (a INT); ---echo # Table should be empty, proving OR REPLACE was used (not IF NOT EXISTS). -SELECT COUNT(*) FROM t1; -DROP TABLE t1; diff --git a/mysql-test/main/mdev_38904.result b/mysql-test/main/mdev_38904.result new file mode 100644 index 0000000000000..202e4b4fa5744 --- /dev/null +++ b/mysql-test/main/mdev_38904.result @@ -0,0 +1,12 @@ +# +# MDEV-38904: latin7 collation corruption +# +CREATE TABLE t1 ( +c1 VARCHAR(10) CHARACTER SET latin7 COLLATE latin7_general_ci, +KEY(c1) +) ENGINE=InnoDB; +INSERT INTO t1 VALUES ('a-b'), ('a b'); +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check status OK +DROP TABLE t1; \ No newline at end of file diff --git a/mysql-test/main/mdev_38904.test b/mysql-test/main/mdev_38904.test new file mode 100644 index 0000000000000..eabea4deedb2f --- /dev/null +++ b/mysql-test/main/mdev_38904.test @@ -0,0 +1,15 @@ +--source include/have_innodb.inc + +--echo # +--echo # MDEV-38904: latin7 collation corruption +--echo # + +CREATE TABLE t1 ( + c1 VARCHAR(10) CHARACTER SET latin7 COLLATE latin7_general_ci, + KEY(c1) +) ENGINE=InnoDB; + +INSERT INTO t1 VALUES ('a-b'), ('a b'); +CHECK TABLE t1; + +DROP TABLE t1; \ No newline at end of file diff --git a/mysql-test/suite/plugins/r/server_audit.result b/mysql-test/suite/plugins/r/server_audit.result index 2406c2b6d3ef4..9276a38bde4ef 100644 --- a/mysql-test/suite/plugins/r/server_audit.result +++ b/mysql-test/suite/plugins/r/server_audit.result @@ -230,17 +230,6 @@ select 3; 7 7 /*M!999999 SELECT 'should not log' */; -/*!! SELECT 8 */; -8 -8 -/*!!999999 SELECT 9 */; -9 -9 -/*!!100100 SELECT 'reversed should not log' */; -/*M!!999999 SELECT 10 */; -10 -10 -/*M!!100100 SELECT 'reversed should not log' */; drop table t1; set global server_audit_events='query_dcl'; create table t1(id int); @@ -306,7 +295,7 @@ set global server_audit_logging= off; set global server_audit_incl_users='root'; set global server_audit_logging= on; disconnect cn1; -Line count in file: 169 +Line count in file: 166 drop user user1@localhost; set global server_audit_events=''; set global server_audit_incl_users='root, plug_dest, ssl_user1'; @@ -322,7 +311,7 @@ USER() CURRENT_USER() plug@localhost plug_dest@% connection default; disconnect plug_con; -Line count in file: 203 +Line count in file: 200 DROP USER plug; DROP USER plug_dest; CREATE USER ssl_user1@localhost require SSL; @@ -332,7 +321,7 @@ select variable_value > '' as 'have_ssl' from information_schema.session_status have_ssl 1 disconnect conssl1; -Line count in file: 234 +Line count in file: 231 connection default; DROP USER ssl_user1@localhost; set global server_audit_query_log_limit= 15; @@ -552,9 +541,6 @@ TIME,HOSTNAME,root,localhost:PORT,ID,ID,QUERY,sa_db,'/*! SELECT 4 */',0 TIME,HOSTNAME,root,localhost:PORT,ID,ID,QUERY,sa_db,'/*M! SELECT 5 */',0 TIME,HOSTNAME,root,localhost:PORT,ID,ID,QUERY,sa_db,'/*!100100 SELECT 6 */',0 TIME,HOSTNAME,root,localhost:PORT,ID,ID,QUERY,sa_db,'/*M!100100 SELECT 7 */',0 -TIME,HOSTNAME,root,localhost:PORT,ID,ID,QUERY,sa_db,'/*!! SELECT 8 */',0 -TIME,HOSTNAME,root,localhost:PORT,ID,ID,QUERY,sa_db,'/*!!999999 SELECT 9 */',0 -TIME,HOSTNAME,root,localhost:PORT,ID,ID,QUERY,sa_db,'/*M!!999999 SELECT 10 */',0 TIME,HOSTNAME,root,localhost:PORT,ID,ID,QUERY,sa_db,'CREATE USER u1 IDENTIFIED BY *****',0 TIME,HOSTNAME,root,localhost:PORT,ID,ID,QUERY,sa_db,'GRANT ALL ON sa_db TO u2 IDENTIFIED BY *****',0 TIME,HOSTNAME,root,localhost:PORT,ID,ID,QUERY,sa_db,'CREATE USER u3 IDENTIFIED BY *****',0 diff --git a/mysql-test/suite/plugins/t/server_audit.test b/mysql-test/suite/plugins/t/server_audit.test index 77a483d36b4a1..94b2129425aff 100644 --- a/mysql-test/suite/plugins/t/server_audit.test +++ b/mysql-test/suite/plugins/t/server_audit.test @@ -167,12 +167,6 @@ query /*!999999 SELECT 'should not log' */; query /*M!100100 SELECT 7 */; query /*M!999999 SELECT 'should not log' */; -query /*!! SELECT 8 */; -query /*!!999999 SELECT 9 */; -query /*!!100100 SELECT 'reversed should not log' */; -query /*M!!999999 SELECT 10 */; -query /*M!!100100 SELECT 'reversed should not log' */; - drop table t1; set global server_audit_events='query_dcl'; create table t1(id int); @@ -236,7 +230,7 @@ set global server_audit_logging= off; set global server_audit_incl_users='root'; set global server_audit_logging= on; disconnect cn1; -let SEARCH_COUNT= 169; +let SEARCH_COUNT= 166; source include/wait_for_line_count_in_file.inc; drop user user1@localhost; @@ -257,7 +251,7 @@ connect(plug_con,localhost,plug,plug_dest,"*NO-ONE*"); select USER(),CURRENT_USER(); connection default; disconnect plug_con; -let SEARCH_COUNT= 203; +let SEARCH_COUNT= 200; source include/wait_for_line_count_in_file.inc; DROP USER plug; DROP USER plug_dest; @@ -268,7 +262,7 @@ connect (conssl1,localhost,ssl_user1,,sa_db,,,SSL); --let $ssl_version = query_get_value(SHOW STATUS LIKE 'Ssl_version', Value, 1) select variable_value > '' as 'have_ssl' from information_schema.session_status where variable_name='ssl_cipher'; disconnect conssl1; -let SEARCH_COUNT= 234; +let SEARCH_COUNT= 231; source include/wait_for_line_count_in_file.inc; connection default; DROP USER ssl_user1@localhost; diff --git a/sql/rpl_master_info_file.h b/sql/rpl_master_info_file.h index 24cbcf55f7278..90bc08fb6781f 100644 --- a/sql/rpl_master_info_file.h +++ b/sql/rpl_master_info_file.h @@ -83,7 +83,7 @@ inline const char *master_ssl_key = ""; inline const char *master_ssl_cipher = ""; inline bool master_ssl_verify_server_cert= true; /// `ulong` is the data type `my_getopt` expects. -inline ulong master_use_gtid= static_cast(enum_master_use_gtid::DEFAULT); +inline auto master_use_gtid= static_cast(enum_master_use_gtid::DEFAULT); inline uint64_t master_retry_count= 100000; /// }@ @@ -612,11 +612,9 @@ struct Master_info_file: Info_file ignore_server_ids(ignore_server_ids), do_domain_ids(do_domain_ids), ignore_domain_ids(ignore_domain_ids) { - for (std::unordered_map::const_iterator it= VALUE_MAP.begin(); - it != VALUE_MAP.end(); ++it) - if (it->second) - it->second(this).set_default(); + for(auto &[_, mem_fn]: VALUE_MAP) + if (mem_fn) + mem_fn(this).set_default(); } bool load_from_file() override @@ -697,15 +695,12 @@ break_for:; /* Write MariaDB `key=value` lines: The "value" can then be written individually after generating the`key=`. */ - for (std::unordered_map::const_iterator it= VALUE_MAP.begin(); - it != VALUE_MAP.end(); ++it) - { - if (it->second) + for (auto &[key, pm]: VALUE_MAP) + if (pm) { - Persistent &value= it->second(this); + Persistent &value= pm(this); my_b_write(&file, - reinterpret_cast(it->first.data()), it->first.size()); + reinterpret_cast(key.data()), key.size()); if (!value.is_default()) { my_b_write_byte(&file, '='); @@ -713,11 +708,11 @@ break_for:; } my_b_write_byte(&file, '\n'); } - } my_b_write(&file, reinterpret_cast(END_MARKER), - sizeof(END_MARKER) - /* the '\0' */ 1); + sizeof(END_MARKER) - /* the '\0' */ 1); my_b_write_byte(&file, '\n'); } + }; #endif // C++ standard guard diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 8fedc08645837..b492dbe8411a2 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -2425,16 +2425,6 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd) set_echo(FALSE); yySkipn(maria_comment_syntax ? 4 : 3); - /* - Check for reversed executable comment syntax: '/' '*' '!' '!' - A reversed comment is executed only on versions OLDER than - the specified version (i.e. when MYSQL_VERSION_ID < version). - See MDEV-7381. - */ - bool reversed_comment= (yyPeekn(0) == '!'); - if (reversed_comment) - yySkipn(1); - /* The special comment format is very strict: '/' '*' '!', followed by an optional 'M' and exactly @@ -2468,16 +2458,9 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd) MariaDB-10.0 does not understand. Ignore all versioned comments with MySQL versions in the range 50700-999999, but do not ignore MariaDB specific comments for the same versions. - - Reversed executable comments (MDEV-7381): execute the content - only when the server version is strictly less than the specified - version. The MySQL 5.7 range exclusion does not apply to - reversed comments. */ - if ((!reversed_comment && - version <= MYSQL_VERSION_ID && - (version < 50700 || version > 99999 || maria_comment_syntax)) || - (reversed_comment && MYSQL_VERSION_ID < version)) + if (version <= MYSQL_VERSION_ID && + (version < 50700 || version > 99999 || maria_comment_syntax)) { /* Accept 'M' 'm' 'm' 'd' 'd' */ yySkipn(length); diff --git a/storage/innobase/dict/dict0stats_bg.cc b/storage/innobase/dict/dict0stats_bg.cc index 135ffb6000580..8dd6b0e8e1838 100644 --- a/storage/innobase/dict/dict0stats_bg.cc +++ b/storage/innobase/dict/dict0stats_bg.cc @@ -41,7 +41,7 @@ Created Apr 25, 2012 Vasil Dimov #include /** Minimum time interval between stats recalc for a given table */ -#define MIN_RECALC_INTERVAL 10 /* seconds */ +#define MIN_RECALC_INTERVAL 10 /* seconds */ static void dict_stats_schedule(int ms); /** Protects recalc_pool */ @@ -64,9 +64,9 @@ typedef std::vector> recalc_pool_t; /** Pool where we store information on which tables are to be processed by background statistics gathering. */ -static recalc_pool_t recalc_pool; +static recalc_pool_t recalc_pool; /** Whether the global data structures have been initialized */ -static bool stats_initialised; +static bool stats_initialised; static THD *dict_stats_thd; @@ -75,9 +75,9 @@ Free the resources occupied by the recalc pool, called once during thread de-initialization. */ static void dict_stats_recalc_pool_deinit() { - ut_ad(!srv_read_only_mode); + ut_ad(!srv_read_only_mode); - recalc_pool.clear(); + recalc_pool.clear(); /* recalc_pool may still have its buffer allocated. It will free it when its destructor is called. @@ -86,11 +86,13 @@ static void dict_stats_recalc_pool_deinit() memory. To avoid that, we force recalc_pool to surrender its buffer to empty_pool object, which will free it when leaving this function: */ - recalc_pool_t recalc_empty_pool; - recalc_pool.swap(recalc_empty_pool); + recalc_pool_t recalc_empty_pool; + recalc_pool.swap(recalc_empty_pool); - if (dict_stats_thd) - destroy_background_thd(dict_stats_thd); + if (dict_stats_thd) { + destroy_background_thd(dict_stats_thd); + dict_stats_thd = nullptr; + } } /*****************************************************************//** @@ -120,78 +122,78 @@ static void dict_stats_recalc_pool_add(table_id_t id) /** Update the table modification counter and if necessary, schedule new estimates for table and index statistics to be calculated. -@param[in,out] table persistent or temporary table -@param[in,out] thd current session */ +@param[in,out] table persistent or temporary table +@param[in,out] thd current session */ void dict_stats_update_if_needed(dict_table_t *table, trx_t &trx) noexcept { uint32_t stat{table->stat}; - if (UNIV_UNLIKELY(!table->stat_initialized(stat))) { - /* The table may have been evicted from dict_sys - and reloaded internally by InnoDB for FOREIGN KEY - processing, but not reloaded by the SQL layer. + if (UNIV_UNLIKELY(!table->stat_initialized(stat))) { + /* The table may have been evicted from dict_sys + and reloaded internally by InnoDB for FOREIGN KEY + processing, but not reloaded by the SQL layer. - We can (re)compute the transient statistics when the - table is actually loaded by the SQL layer. + We can (re)compute the transient statistics when the + table is actually loaded by the SQL layer. - Note: If InnoDB persistent statistics are enabled, - we will skip the updates. We must do this, because - dict_table_get_n_rows() below assumes that the - statistics have been initialized. The DBA may have - to execute ANALYZE TABLE. */ - return; - } + Note: If InnoDB persistent statistics are enabled, + we will skip the updates. We must do this, because + dict_table_get_n_rows() below assumes that the + statistics have been initialized. The DBA may have + to execute ANALYZE TABLE. */ + return; + } - ulonglong counter = table->stat_modified_counter++; - ulonglong n_rows = dict_table_get_n_rows(table); + ulonglong counter = table->stat_modified_counter++; + ulonglong n_rows = dict_table_get_n_rows(table); - if (table->stats_is_persistent(stat)) { - if (table->stats_is_auto_recalc(stat) - && counter > n_rows / 10 && !table->name.is_temporary()) { + if (table->stats_is_persistent(stat)) { + if (table->stats_is_auto_recalc(stat) + && counter > n_rows / 10 && !table->name.is_temporary()) { #ifdef WITH_WSREP - /* Do not add table to background - statistic calculation if this thread is not a - applier (as all DDL, which is replicated (i.e - is binlogged in master node), will be executed - with high priority (a.k.a BF) in slave nodes) - and is BF. This could again lead BF lock - waits in applier node but it is better than - no persistent index/table statistics at - applier nodes. TODO: allow BF threads - wait for these InnoDB internal SQL-parser - generated row locks and allow BF thread - lock waits to be enqueued at head of waiting - queue. */ - if (trx.is_wsrep() - && !wsrep_thd_is_applying(trx.mysql_thd) - && wsrep_thd_is_BF(trx.mysql_thd, 0)) { - WSREP_DEBUG("Avoiding background statistics" - " calculation for table %s.", - table->name.m_name); - return; - } + /* Do not add table to background + statistic calculation if this thread is not a + applier (as all DDL, which is replicated (i.e + is binlogged in master node), will be executed + with high priority (a.k.a BF) in slave nodes) + and is BF. This could again lead BF lock + waits in applier node but it is better than + no persistent index/table statistics at + applier nodes. TODO: allow BF threads + wait for these InnoDB internal SQL-parser + generated row locks and allow BF thread + lock waits to be enqueued at head of waiting + queue. */ + if (trx.is_wsrep() + && !wsrep_thd_is_applying(trx.mysql_thd) + && wsrep_thd_is_BF(trx.mysql_thd, 0)) { + WSREP_DEBUG("Avoiding background statistics" + " calculation for table %s.", + table->name.m_name); + return; + } #endif /* WITH_WSREP */ - dict_stats_recalc_pool_add(table->id); - table->stat_modified_counter = 0; - } - return; - } - - /* Calculate new statistics if 1 / 16 of table has been modified - since the last time a statistics batch was run. - We calculate statistics at most every 16th round, since we may have - a counter table which is very small and updated very often. */ - ulonglong threshold = 16 + n_rows / 16; /* 6.25% */ - - if (srv_stats_modified_counter) { - threshold = std::min(srv_stats_modified_counter, threshold); - } - - if (counter > threshold) { - /* this will reset table->stat_modified_counter to 0 */ - dict_stats_update_transient(&trx, table); - } + dict_stats_recalc_pool_add(table->id); + table->stat_modified_counter = 0; + } + return; + } + + /* Calculate new statistics if 1 / 16 of table has been modified + since the last time a statistics batch was run. + We calculate statistics at most every 16th round, since we may have + a counter table which is very small and updated very often. */ + ulonglong threshold = 16 + n_rows / 16; /* 6.25% */ + + if (srv_stats_modified_counter) { + threshold = std::min(srv_stats_modified_counter, threshold); + } + + if (counter > threshold) { + /* this will reset table->stat_modified_counter to 0 */ + dict_stats_update_transient(&trx, table); + } } /** Delete a table from the auto recalc pool, and ensure that @@ -255,17 +257,17 @@ Free resources allocated by dict_stats_init(), must be called after dict_stats task has exited. */ void dict_stats_deinit() { - if (!stats_initialised) { - return; - } + if (!stats_initialised) { + return; + } - ut_ad(!srv_read_only_mode); - stats_initialised = false; + ut_ad(!srv_read_only_mode); + stats_initialised = false; - dict_stats_recalc_pool_deinit(); + dict_stats_recalc_pool_deinit(); - mysql_mutex_destroy(&recalc_pool_mutex); - pthread_cond_destroy(&recalc_pool_cond); + mysql_mutex_destroy(&recalc_pool_mutex); + pthread_cond_destroy(&recalc_pool_cond); } /** @@ -371,14 +373,15 @@ static bool is_recalc_pool_empty() static tpool::timer* dict_stats_timer; static void dict_stats_func(void*) { - if (!dict_stats_thd) - dict_stats_thd= innobase_create_background_thd("InnoDB statistics"); + DBUG_ASSERT(dict_stats_thd != nullptr); + set_current_thd(dict_stats_thd); while (dict_stats_process_entry_from_recalc_pool(dict_stats_thd)) {} innobase_reset_background_thd(dict_stats_thd); set_current_thd(nullptr); + if (!is_recalc_pool_empty()) dict_stats_schedule(MIN_RECALC_INTERVAL * 1000); } @@ -387,7 +390,17 @@ static void dict_stats_func(void*) void dict_stats_start() { DBUG_ASSERT(!dict_stats_timer); - dict_stats_timer= srv_thread_pool->create_timer(dict_stats_func); + + if (!dict_stats_thd) { + dict_stats_thd = innobase_create_background_thd("InnoDB statistics"); + } + + if (dict_stats_thd) { + dict_stats_timer = srv_thread_pool->create_timer(dict_stats_func); + } else { + sql_print_warning("InnoDB: Failed to create background THD for statistics. " + "Automatic statistics recalculation will be disabled."); + } } @@ -405,6 +418,14 @@ void dict_stats_schedule_now() /** Shut down the dict_stats_thread. */ void dict_stats_shutdown() { - delete dict_stats_timer; - dict_stats_timer= 0; -} + if (dict_stats_timer) { + delete dict_stats_timer; + dict_stats_timer= 0; + } + + // Safely destroy the THD during shutdown + if (dict_stats_thd) { + destroy_background_thd(dict_stats_thd); + dict_stats_thd = nullptr; + } +} \ No newline at end of file diff --git a/strings/ctype-extra.c b/strings/ctype-extra.c index 8a25a9c6a611e..58cf407040c09 100644 --- a/strings/ctype-extra.c +++ b/strings/ctype-extra.c @@ -2518,22 +2518,22 @@ static const uchar to_upper_latin7_general_ci[] = { }; static const uchar sort_order_latin7_general_ci[] = { -0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, -0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, -0x30,0x32,0x33,0x34,0x35,0x36,0x37,0x2B,0x38,0x39,0x3A,0x5C,0x3B,0x2C,0x3C,0x3D, -0x76,0x7A,0x7C,0x7E,0x80,0x81,0x82,0x83,0x84,0x85,0x3E,0x3F,0x5D,0x5E,0x5F,0x40, -0x41,0x86,0x92,0x94,0x9A,0x9C,0xA6,0xA8,0xAC,0xAE,0xB4,0xB6,0xBA,0xC0,0xC2,0xC8, -0xD4,0xD6,0xD8,0xDC,0xE3,0xE6,0xEE,0xF0,0xF2,0xF4,0xF6,0x42,0x43,0x44,0x45,0x46, -0x47,0x86,0x92,0x94,0x9A,0x9C,0xA6,0xA8,0xAC,0xAE,0xB4,0xB6,0xBA,0xC0,0xC2,0xC8, -0xD4,0xD6,0xD8,0xDC,0xE2,0xE6,0xEE,0xF0,0xF2,0xF4,0xF6,0x48,0x49,0x4A,0x4B,0x20, -0x75,0x21,0x56,0x22,0x59,0x73,0x70,0x71,0x23,0x74,0x24,0x5A,0x25,0x4D,0x51,0x50, -0x26,0x54,0x55,0x57,0x58,0x72,0x2E,0x2F,0x27,0xE5,0x28,0x5B,0x29,0x4E,0x53,0x2A, -0x31,0xFE,0x65,0x66,0x67,0xFF,0x4C,0x68,0x2D,0x69,0xDA,0x61,0x6A,0x2D,0x6B,0x90, -0x6C,0x60,0x7D,0x7F,0x4F,0x6D,0x6E,0x6F,0xD3,0x7B,0xDB,0x62,0x77,0x78,0x79,0x90, -0x8E,0xB2,0x8A,0x96,0x88,0x8C,0xA4,0xA2,0x98,0x9E,0xF8,0xA0,0xAA,0xB8,0xB0,0xBE, -0xE1,0xC4,0xC6,0xCA,0xCE,0xD0,0xCC,0x63,0xEC,0xBC,0xDE,0xEA,0xE8,0xFA,0xFC,0xE0, -0x8E,0xB2,0x8A,0x96,0x88,0x8C,0xA4,0xA2,0x98,0x9E,0xF8,0xA0,0xAA,0xB8,0xB0,0xBE, -0xE1,0xC4,0xC6,0xCA,0xCE,0xD0,0xCC,0x64,0xEC,0xBC,0xDE,0xEA,0xE8,0xFA,0xFC,0x52 + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, + 0x20,0x32,0x33,0x34,0x35,0x36,0x37,0x2B,0x38,0x39,0x3A,0x5C,0x3B,0x2D,0x3C,0x3D, + 0x76,0x7A,0x7C,0x7E,0x80,0x81,0x82,0x83,0x84,0x85,0x3E,0x3F,0x5D,0x5E,0x5F,0x40, + 0x41,0x86,0x92,0x94,0x9A,0x9C,0xA6,0xA8,0xAC,0xAE,0xB4,0xB6,0xBA,0xC0,0xC2,0xC8, + 0xD4,0xD6,0xD8,0xDC,0xE3,0xE6,0xEE,0xF0,0xF2,0xF4,0xF6,0x42,0x43,0x44,0x45,0x46, + 0x47,0x86,0x92,0x94,0x9A,0x9C,0xA6,0xA8,0xAC,0xAE,0xB4,0xB6,0xBA,0xC0,0xC2,0xC8, + 0xD4,0xD6,0xD8,0xDC,0xE2,0xE6,0xEE,0xF0,0xF2,0xF4,0xF6,0x48,0x49,0x4A,0x4B,0x4C, + 0x75,0x21,0x56,0x22,0x59,0x73,0x70,0x71,0x23,0x74,0x24,0x5A,0x25,0x4D,0x51,0x50, + 0x26,0x54,0x55,0x57,0x58,0x72,0x2E,0x2F,0x27,0xE5,0x28,0x5B,0x29,0x4E,0x53,0x2A, + 0x31,0xFE,0x65,0x66,0x67,0xFF,0x4C,0x68,0x2C,0x69,0xDA,0x61,0x6A,0x2C,0x6B,0x90, + 0x6C,0x60,0x7D,0x7F,0x4F,0x6D,0x6E,0x6F,0xD3,0x7B,0xDB,0x62,0x77,0x78,0x79,0x90, + 0x8E,0xB2,0x8A,0x96,0x88,0x8C,0xA4,0xA2,0x98,0x9E,0xF8,0xA0,0xAA,0xB8,0xB0,0xBE, + 0xE1,0xC4,0xC6,0xCA,0xCE,0xD0,0xCC,0x63,0xEC,0xBC,0xDE,0xEA,0xE8,0xFA,0xFC,0xE0, + 0x8E,0xB2,0x8A,0x96,0x88,0x8C,0xA4,0xA2,0x98,0x9E,0xF8,0xA0,0xAA,0xB8,0xB0,0xBE, + 0xE1,0xC4,0xC6,0xCA,0xCE,0xD0,0xCC,0x64,0xEC,0xBC,0xDE,0xEA,0xE8,0xFA,0xFC,0x52 }; static const uint16 to_uni_latin7_general_ci[] = { diff --git a/strings/ctype.c b/strings/ctype.c index 73b22a1444af4..5ba53e35dd264 100644 --- a/strings/ctype.c +++ b/strings/ctype.c @@ -69,23 +69,23 @@ struct my_cs_file_section_st const char *str; }; -#define _CS_MISC 1 -#define _CS_ID 2 -#define _CS_CSNAME 3 -#define _CS_FAMILY 4 -#define _CS_ORDER 5 -#define _CS_COLNAME 6 -#define _CS_FLAG 7 -#define _CS_CHARSET 8 -#define _CS_COLLATION 9 -#define _CS_UPPERMAP 10 -#define _CS_LOWERMAP 11 -#define _CS_UNIMAP 12 -#define _CS_COLLMAP 13 -#define _CS_CTYPEMAP 14 -#define _CS_PRIMARY_ID 15 -#define _CS_BINARY_ID 16 -#define _CS_CSDESCRIPT 17 +#define _CS_MISC 1 +#define _CS_ID 2 +#define _CS_CSNAME 3 +#define _CS_FAMILY 4 +#define _CS_ORDER 5 +#define _CS_COLNAME 6 +#define _CS_FLAG 7 +#define _CS_CHARSET 8 +#define _CS_COLLATION 9 +#define _CS_UPPERMAP 10 +#define _CS_LOWERMAP 11 +#define _CS_UNIMAP 12 +#define _CS_COLLMAP 13 +#define _CS_CTYPEMAP 14 +#define _CS_PRIMARY_ID 15 +#define _CS_BINARY_ID 16 +#define _CS_CSDESCRIPT 17 /* Special purpose commands */ @@ -161,34 +161,34 @@ struct my_cs_file_section_st static const struct my_cs_file_section_st sec[] = { - {_CS_MISC, "xml"}, - {_CS_MISC, "xml/version"}, - {_CS_MISC, "xml/encoding"}, - {_CS_MISC, "charsets"}, - {_CS_MISC, "charsets/max-id"}, - {_CS_MISC, "charsets/copyright"}, - {_CS_MISC, "charsets/description"}, - {_CS_CHARSET, "charsets/charset"}, - {_CS_PRIMARY_ID, "charsets/charset/primary-id"}, - {_CS_BINARY_ID, "charsets/charset/binary-id"}, - {_CS_CSNAME, "charsets/charset/name"}, - {_CS_FAMILY, "charsets/charset/family"}, - {_CS_CSDESCRIPT, "charsets/charset/description"}, - {_CS_MISC, "charsets/charset/alias"}, - {_CS_MISC, "charsets/charset/ctype"}, - {_CS_CTYPEMAP, "charsets/charset/ctype/map"}, - {_CS_MISC, "charsets/charset/upper"}, - {_CS_UPPERMAP, "charsets/charset/upper/map"}, - {_CS_MISC, "charsets/charset/lower"}, - {_CS_LOWERMAP, "charsets/charset/lower/map"}, - {_CS_MISC, "charsets/charset/unicode"}, - {_CS_UNIMAP, "charsets/charset/unicode/map"}, - {_CS_COLLATION, "charsets/charset/collation"}, - {_CS_COLNAME, "charsets/charset/collation/name"}, - {_CS_ID, "charsets/charset/collation/id"}, - {_CS_ORDER, "charsets/charset/collation/order"}, - {_CS_FLAG, "charsets/charset/collation/flag"}, - {_CS_COLLMAP, "charsets/charset/collation/map"}, + {_CS_MISC, "xml"}, + {_CS_MISC, "xml/version"}, + {_CS_MISC, "xml/encoding"}, + {_CS_MISC, "charsets"}, + {_CS_MISC, "charsets/max-id"}, + {_CS_MISC, "charsets/copyright"}, + {_CS_MISC, "charsets/description"}, + {_CS_CHARSET, "charsets/charset"}, + {_CS_PRIMARY_ID, "charsets/charset/primary-id"}, + {_CS_BINARY_ID, "charsets/charset/binary-id"}, + {_CS_CSNAME, "charsets/charset/name"}, + {_CS_FAMILY, "charsets/charset/family"}, + {_CS_CSDESCRIPT, "charsets/charset/description"}, + {_CS_MISC, "charsets/charset/alias"}, + {_CS_MISC, "charsets/charset/ctype"}, + {_CS_CTYPEMAP, "charsets/charset/ctype/map"}, + {_CS_MISC, "charsets/charset/upper"}, + {_CS_UPPERMAP, "charsets/charset/upper/map"}, + {_CS_MISC, "charsets/charset/lower"}, + {_CS_LOWERMAP, "charsets/charset/lower/map"}, + {_CS_MISC, "charsets/charset/unicode"}, + {_CS_UNIMAP, "charsets/charset/unicode/map"}, + {_CS_COLLATION, "charsets/charset/collation"}, + {_CS_COLNAME, "charsets/charset/collation/name"}, + {_CS_ID, "charsets/charset/collation/id"}, + {_CS_ORDER, "charsets/charset/collation/order"}, + {_CS_FLAG, "charsets/charset/collation/flag"}, + {_CS_COLLMAP, "charsets/charset/collation/map"}, /* Special purpose commands */ {_CS_UCA_VERSION, "charsets/charset/collation/version"}, @@ -257,7 +257,7 @@ static const struct my_cs_file_section_st sec[] = {_CS_RESET_FIRST_VARIABLE, "charsets/charset/collation/rules/reset/first_variable"}, {_CS_RESET_LAST_VARIABLE, "charsets/charset/collation/rules/reset/last_variable"}, - {0, NULL} + {0, NULL} }; static const struct my_cs_file_section_st @@ -272,8 +272,8 @@ static const struct my_cs_file_section_st return NULL; } -#define MY_CS_CSDESCR_SIZE 64 -#define MY_CS_TAILORING_SIZE (32*1024) +#define MY_CS_CSDESCR_SIZE 64 +#define MY_CS_TAILORING_SIZE (32*1024) #define MY_CS_UCA_VERSION_SIZE 64 #define MY_CS_CONTEXT_SIZE 64 @@ -977,7 +977,7 @@ my_repertoire_t my_charset_repertoire(CHARSET_INFO *cs) When merging to 5.2, this function should be changed to check a new flag MY_CS_NONASCII, - return (cs->flag & MY_CS_NONASCII) ? 0 : 1; + return (cs->flag & MY_CS_NONASCII) ? 0 : 1; This flag was previously added into 5.2 under terms of WL#3759 "Optimize identifier conversion in client-server protocol" @@ -1004,11 +1004,11 @@ my_is_printable(my_wc_t wc) { /* Blocks: - U+0000 .. U+001F control - U+0020 .. U+007E printable - U+007F .. U+009F control - U+00A0 .. U+00FF printable - U+0100 .. U+10FFFF As of Unicode-6.1.0, this range does not have any + U+0000 .. U+001F control + U+0020 .. U+007E printable + U+007F .. U+009F control + U+00A0 .. U+00FF printable + U+0100 .. U+10FFFF As of Unicode-6.1.0, this range does not have any characters of the "Cc" (Other, control) category. Should be mostly safe to print. Except for the surrogate halfs, @@ -1154,8 +1154,8 @@ my_convert_using_func(char *to, size_t to_length, CHARSET_INFO *from_cs, my_charset_conv_mb_wc mb_wc, uint *errors) { - int cnvres; - my_wc_t wc; + int cnvres; + my_wc_t wc; const uchar *from_end= (const uchar*) from + from_length; char *to_start= to; uchar *to_end= (uchar*) to + to_length; @@ -1171,6 +1171,13 @@ my_convert_using_func(char *to, size_t to_length, from++; wc= '?'; } + else if (cnvres == 0) + { + /* MDEV-38904 FIX: Prevent 100% CPU infinite loop on 0-byte consumption */ + error_count++; + from++; + wc= '?'; + } else if (cnvres > MY_CS_TOOSMALL) { /* @@ -1270,7 +1277,8 @@ my_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs, { if (!length) { - *errors= 0; + /* FIX: Catch silent truncation if the destination buffer is too small */ + *errors= (from_length > to_length) ? MY_CS_TRUNCATED : 0; return length2; } if (*((unsigned char*) from) > 0x7F) /* A non-ASCII character */ @@ -1324,6 +1332,14 @@ my_convert_fix(CHARSET_INFO *to_cs, char *to, size_t to_length, from++; wc= '?'; } + else if (cnvres == 0) + { + /* MDEV-38904 FIX: Prevent 100% CPU infinite loop on 0-byte consumption */ + if (!copy_status->m_well_formed_error_pos) + copy_status->m_well_formed_error_pos= from; + from++; + wc= '?'; + } else if (cnvres > MY_CS_TOOSMALL) { /* @@ -1457,4 +1473,4 @@ struct charset_info_st *my_ci_alloc(MY_CHARSET_LOADER *loader, out_comment->length= comment.length; return csinfo; -} +} \ No newline at end of file