From 280b158501fdb889beee3a0ea0b9acbf723acf66 Mon Sep 17 00:00:00 2001 From: Disconnect3d Date: Mon, 13 Apr 2020 16:12:18 +0200 Subject: [PATCH 01/13] Fix wrong argument size passed to --parent-pid strncmp check This PR fixes wrong size argument passed in `strncmp(arg, "--parent-pid", 10)` as the `"--parent-pid"` string has length of 12. Closes #1502 --- mysql-test/lib/My/SafeProcess/safe_process_win.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/lib/My/SafeProcess/safe_process_win.cc b/mysql-test/lib/My/SafeProcess/safe_process_win.cc index 7d81bf1a1b4..4dd4e24f30d 100644 --- a/mysql-test/lib/My/SafeProcess/safe_process_win.cc +++ b/mysql-test/lib/My/SafeProcess/safe_process_win.cc @@ -206,7 +206,7 @@ int main(int argc, const char** argv ) } else { if (strcmp(arg, "--verbose") == 0) verbose++; - else if (strncmp(arg, "--parent-pid", 10) == 0) + else if (strncmp(arg, "--parent-pid", 12) == 0) { /* Override parent_pid with a value provided by user */ const char* start; From 29cdd5082279ab5bcae04464a08b2499444c86f5 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Fri, 17 Apr 2020 20:58:55 -0700 Subject: [PATCH 02/13] MDEV-21932 Another attempt to fix the bug . The first patch for the bug was erroneous: it did not take into account the fact that the modified function get_key_scans_params() was called in different contexts. As a result the patch caused a regression bug MDEV-22191. The patch for this bug introduced an extra parameter. Actually we can do without this parameter and use the fourth parameter for the same puropose - to differentiate between the calls of the function for range access and for index merge access. Also removed the call of get_key_scans_params() in the code of the function merge_same_index_scans() as not needed. --- sql/opt_range.cc | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 9e96e94ac91..5658bb065b7 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -948,9 +948,8 @@ QUICK_RANGE_SELECT *get_quick_select(PARAM *param,uint index, uint mrr_buf_size, MEM_ROOT *alloc); static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree, bool index_read_must_be_used, - bool update_tbl_stats, - double read_time, - bool ror_scans_required); + bool for_range_access, + double read_time); static TRP_INDEX_INTERSECT *get_best_index_intersect(PARAM *param, SEL_TREE *tree, double read_time); @@ -3147,7 +3146,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, /* Get best 'range' plan and prepare data for making other plans */ if ((range_trp= get_key_scans_params(¶m, tree, FALSE, TRUE, - best_read_time, FALSE))) + best_read_time))) { best_trp= range_trp; best_read_time= best_trp->read_cost; @@ -4673,7 +4672,6 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge, double roru_index_costs; ha_rows roru_total_records; double roru_intersect_part= 1.0; - bool only_ror_scans_required= FALSE; DBUG_ENTER("get_best_disjunct_quick"); DBUG_PRINT("info", ("Full table scan cost: %g", read_time)); @@ -4700,8 +4698,6 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge, n_child_scans))) DBUG_RETURN(NULL); - only_ror_scans_required= !optimizer_flag(param->thd, - OPTIMIZER_SWITCH_INDEX_MERGE_SORT_UNION); /* Collect best 'range' scan for each of disjuncts, and, while doing so, analyze possibility of ROR scans. Also calculate some values needed by @@ -4714,8 +4710,7 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge, DBUG_EXECUTE("info", print_sel_tree(param, *ptree, &(*ptree)->keys_map, "tree in SEL_IMERGE");); if (!(*cur_child= get_key_scans_params(param, *ptree, TRUE, FALSE, - read_time, - only_ror_scans_required))) + read_time))) { /* One of index scans in this index_merge is more expensive than entire @@ -5035,9 +5030,12 @@ TABLE_READ_PLAN *merge_same_index_scans(PARAM *param, SEL_IMERGE *imerge, a random order 2. the functions that estimate the cost of a range scan and an index merge retrievals are not well calibrated + + As the best range access has been already chosen it does not + make sense to evaluate the one obtained from a degenerated + index merge. */ - trp= get_key_scans_params(param, *imerge->trees, FALSE, TRUE, - read_time, FALSE); + trp= 0; } DBUG_RETURN(trp); @@ -6753,9 +6751,9 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param, tree make range select for this SEL_TREE index_read_must_be_used if TRUE, assume 'index only' option will be set (except for clustered PK indexes) + for_range_access if TRUE the function is called to get the best range + plan for range access, not for index merge access read_time don't create read plans with cost > read_time. - only_ror_scans_required set to TRUE when we are only interested - in ROR scan RETURN Best range read plan NULL if no plan found or error occurred @@ -6763,9 +6761,8 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param, static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree, bool index_read_must_be_used, - bool update_tbl_stats, - double read_time, - bool only_ror_scans_required) + bool for_range_access, + double read_time) { uint idx; SEL_ARG **key,**end, **key_to_read= NULL; @@ -6809,10 +6806,11 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree, (bool) param->table->covering_keys.is_set(keynr); found_records= check_quick_select(param, idx, read_index_only, *key, - update_tbl_stats, &mrr_flags, + for_range_access, &mrr_flags, &buf_size, &cost); - if (only_ror_scans_required && !param->is_ror_scan) + if (!for_range_access && !param->is_ror_scan && + !optimizer_flag(param->thd,OPTIMIZER_SWITCH_INDEX_MERGE_SORT_UNION)) { /* The scan is not a ROR-scan, just skip it */ continue; From bc1be399723090eef40fbfbbe486d44b80214676 Mon Sep 17 00:00:00 2001 From: Anel Husakovic Date: Mon, 13 Apr 2020 15:47:02 +0200 Subject: [PATCH 03/13] Fix failure for ipv6 not enabled In case of ipv6 not enabled tests like `main.ipv6, rpl.rpl_ipv6` failed on aarch buildbot. Fix it by following commits 70dcb46e98e6 and 0bae1957dd124f8382a for `10.2`. --- mysql-test/mysql-test-run.pl | 2 +- mysql-test/suite.pm | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 6d32e97d6b4..1f332dfd82f 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -269,7 +269,7 @@ my $current_config_name; # The currently running config file template our @opt_experimentals; our $experimental_test_cases= []; -my $baseport; +our $baseport; # $opt_build_thread may later be set from $opt_port_base my $opt_build_thread= $ENV{'MTR_BUILD_THREAD'} || "auto"; my $opt_port_base= $ENV{'MTR_PORT_BASE'} || "auto"; diff --git a/mysql-test/suite.pm b/mysql-test/suite.pm index 15615c46c81..40ad76a7f57 100644 --- a/mysql-test/suite.pm +++ b/mysql-test/suite.pm @@ -33,9 +33,10 @@ sub skip_combinations { sub ipv6_ok() { use Socket; return 0 unless socket my $sock, PF_INET6, SOCK_STREAM, getprotobyname('tcp'); + $!=""; # eval{}, if there's no Socket::sockaddr_in6 at all, old Perl installation - eval { connect $sock, sockaddr_in6(7, Socket::IN6ADDR_LOOPBACK) }; - return $@ eq ""; + eval { bind $sock, sockaddr_in6($::baseport, Socket::IN6ADDR_LOOPBACK) }; + return $@ eq "" && $! eq "" } $skip{'include/check_ipv6.inc'} = 'No IPv6' unless ipv6_ok(); From ac2604f923f5bd81920c5edd2c572a88778026dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Sat, 25 Apr 2020 13:47:43 +0300 Subject: [PATCH 04/13] Correct the name of a contributor The name was correctly encoded in UTF-8 before commit 0ce12f70ed2eee1b92e2af27e7dda30db544f492. --- client/mysqldump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/mysqldump.c b/client/mysqldump.c index f976cc31fc8..07afc3119b5 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -32,7 +32,7 @@ ** master/autocommit code by Brian Aker ** SSL by ** Andrei Errapart -** Tõnu Samuel +** Tõnu Samuel ** XML by Gary Huntress 10/10/01, cleaned up ** and adapted to mysqldump 05/11/01 by Jani Tolonen ** Added --single-transaction option 06/06/2002 by Peter Zaitsev From a13157a561d960604c0c8cfd23b79783cfe76861 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Mon, 27 Apr 2020 15:50:51 +0200 Subject: [PATCH 05/13] don't enable -Werror in ft-index --- .../tokudb/ft-index/cmake_modules/TokuSetupCompiler.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/storage/tokudb/ft-index/cmake_modules/TokuSetupCompiler.cmake b/storage/tokudb/ft-index/cmake_modules/TokuSetupCompiler.cmake index fe99c9167d3..f2a43a6077d 100644 --- a/storage/tokudb/ft-index/cmake_modules/TokuSetupCompiler.cmake +++ b/storage/tokudb/ft-index/cmake_modules/TokuSetupCompiler.cmake @@ -182,9 +182,9 @@ if (NOT CMAKE_CXX_COMPILER_ID STREQUAL Clang) set_cflags_if_supported(-Wcast-align) endif () -## always want these -set(CMAKE_C_FLAGS "-Wall -Werror ${CMAKE_C_FLAGS}") -set(CMAKE_CXX_FLAGS "-Wall -Werror ${CMAKE_CXX_FLAGS}") +## never want these +set(CMAKE_C_FLAGS "-Wno-error ${CMAKE_C_FLAGS}") +set(CMAKE_CXX_FLAGS "-Wno-error ${CMAKE_CXX_FLAGS}") ## need to set -stdlib=libc++ to get real c++11 support on darwin if (APPLE) From 4d1de554bbba09b4be64e7f1a9bd005e7168864b Mon Sep 17 00:00:00 2001 From: Maheedhar PV Date: Tue, 26 Nov 2019 09:39:35 +0530 Subject: [PATCH 06/13] Bug#28388217 - SERVER CAN FAIL WHILE REPLICATING CONDITIONAL COMMENTS Cause: In case of version based condtional comments, if the condition evaluates to false, it is converted to a regular comment for replication by replacing "!" by " ". Nested comment in a conditional comment is replicated as is. Nested comments are supported only in case of conditional comments and when a the comment on slave is no more a conditional comment, the statement execution fails on the slave. Fix: Convert the nested comment, start from "/*" to "(*" and comment end from "*/" to "*)" for replication. Change-Id: I1a8e385a267b2370529eade094f0258fa96886c0 --- sql/sql_lex.cc | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index fe4dcfd1524..cb850e06ba6 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2000, 2014, Oracle and/or its affiliates. - Copyright (c) 2009, 2018, MariaDB Corporation +/* Copyright (c) 2000, 2019, Oracle and/or its affiliates. + Copyright (c) 2009, 2020, 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 @@ -940,17 +940,27 @@ static inline uint int_token(const char *str,uint length) */ bool consume_comment(Lex_input_stream *lip, int remaining_recursions_permitted) { + // only one level of nested comments are allowed + DBUG_ASSERT(remaining_recursions_permitted == 0 || + remaining_recursions_permitted == 1); reg1 uchar c; while (! lip->eof()) { c= lip->yyGet(); - if (remaining_recursions_permitted > 0) + if (remaining_recursions_permitted == 1) { if ((c == '/') && (lip->yyPeek() == '*')) { + lip->yyUnput('('); // Replace nested "/*..." with "(*..." + lip->yySkip(); // and skip "(" + lip->yySkip(); /* Eat asterisk */ - consume_comment(lip, remaining_recursions_permitted-1); + if (consume_comment(lip, 0)) + return true; + + lip->yyUnput(')'); // Replace "...*/" with "...*)" + lip->yySkip(); // and skip ")" continue; } } From 59880df8cd69e9154ca27c5da8fd8b2cc107c4d4 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Mon, 27 Apr 2020 15:49:27 +0200 Subject: [PATCH 07/13] Bug#28388217 - SERVER CAN FAIL WHILE REPLICATING CONDITIONAL COMMENTS test case --- .../suite/rpl/r/rpl_conditional_comments.result | 15 +++++++++++++++ .../suite/rpl/t/rpl_conditional_comments.test | 12 +++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/mysql-test/suite/rpl/r/rpl_conditional_comments.result b/mysql-test/suite/rpl/r/rpl_conditional_comments.result index 5471f788561..04eec43f3de 100644 --- a/mysql-test/suite/rpl/r/rpl_conditional_comments.result +++ b/mysql-test/suite/rpl/r/rpl_conditional_comments.result @@ -60,5 +60,20 @@ include/diff_tables.inc [master:t1,slave:t1] # comments SELECT c1 FROM /*!99999 t1 WHEREN; 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 '/*!99999 t1 WHEREN' at line 1 +insert t1 values (/*!50505 1 /* foo */ */ + 2); +insert t1 values (/*!999999 10 /* foo */ */ + 20); +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; insert t1 values (/*!50505 1 /* foo */ */ + 2) +master-bin.000001 # Query # # COMMIT +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; insert t1 values (/* 999999 10 (* foo *) */ + 20) +master-bin.000001 # Query # # COMMIT +select * from t1; +c1 +62 +3 +20 DROP TABLE t1; include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_conditional_comments.test b/mysql-test/suite/rpl/t/rpl_conditional_comments.test index 88adf3a20f1..dc364783734 100644 --- a/mysql-test/suite/rpl/t/rpl_conditional_comments.test +++ b/mysql-test/suite/rpl/t/rpl_conditional_comments.test @@ -68,7 +68,17 @@ sync_slave_with_master; --echo # comments --connection master --error 1064 -SELECT c1 FROM /*!99999 t1 WHEREN; +SELECT c1 FROM /*!99999 t1 WHEREN; #*/ + +# +# Bug#28388217 - SERVER CAN FAIL WHILE REPLICATING CONDITIONAL COMMENTS +# +insert t1 values (/*!50505 1 /* foo */ */ + 2); +insert t1 values (/*!999999 10 /* foo */ */ + 20); +source include/show_binlog_events.inc; +sync_slave_with_master; +select * from t1; +connection master; DROP TABLE t1; --source include/rpl_end.inc From 8c534bdeb8c50bc1df088feca10ea0585f73a46e Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 28 Apr 2020 14:45:36 +0200 Subject: [PATCH 08/13] cleanup: remove dbug keywords that are never used --- sql/slave.cc | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/sql/slave.cc b/sql/slave.cc index ae1c5ca2cf8..d359e1bdae4 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -3048,9 +3048,6 @@ pthread_handler_t handle_slave_io(void *arg) uint retry_count; bool suppress_warnings; int ret; -#ifndef DBUG_OFF - uint retry_count_reg= 0, retry_count_dump= 0, retry_count_event= 0; -#endif // needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff my_thread_init(); DBUG_ENTER("handle_slave_io"); @@ -3182,16 +3179,6 @@ connected: goto err; goto connected; } - DBUG_EXECUTE_IF("FORCE_SLAVE_TO_RECONNECT_REG", - if (!retry_count_reg) - { - retry_count_reg++; - sql_print_information("Forcing to reconnect slave I/O thread"); - if (try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings, - reconnect_messages[SLAVE_RECON_ACT_REG])) - goto err; - goto connected; - }); } DBUG_PRINT("info",("Starting reading binary log from master")); @@ -3208,16 +3195,6 @@ requesting master dump") || goto err; goto connected; } - DBUG_EXECUTE_IF("FORCE_SLAVE_TO_RECONNECT_DUMP", - if (!retry_count_dump) - { - retry_count_dump++; - sql_print_information("Forcing to reconnect slave I/O thread"); - if (try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings, - reconnect_messages[SLAVE_RECON_ACT_DUMP])) - goto err; - goto connected; - }); const char *event_buf; DBUG_ASSERT(mi->last_error().number == 0); @@ -3235,16 +3212,6 @@ requesting master dump") || if (check_io_slave_killed(thd, mi, "Slave I/O thread killed while \ reading event")) goto err; - DBUG_EXECUTE_IF("FORCE_SLAVE_TO_RECONNECT_EVENT", - if (!retry_count_event) - { - retry_count_event++; - sql_print_information("Forcing to reconnect slave I/O thread"); - if (try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings, - reconnect_messages[SLAVE_RECON_ACT_EVENT])) - goto err; - goto connected; - }); if (event_len == packet_error) { From 6bb28e0bc52d415619ad33c60c05dfa2794a5412 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 28 Apr 2020 14:59:47 +0200 Subject: [PATCH 09/13] Bug#29915479 RUNNING COM_REGISTER_SLAVE WITHOUT COM_BINLOG_DUMP CAN RESULTS IN SERVER EXIT in fact, in MariaDB it cannot, but it can show spurious slaves in SHOW SLAVE HOSTS. slave was registered in COM_REGISTER_SLAVE and un-registered after COM_BINLOG_DUMP. If there was no COM_BINLOG_DUMP, it would never unregister. --- .../suite/rpl/r/rpl_fail_register.result | 16 +++++++++ mysql-test/suite/rpl/t/rpl_fail_register.test | 33 +++++++++++++++++++ sql/slave.cc | 1 + sql/sql_class.cc | 2 ++ sql/sql_parse.cc | 1 - 5 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 mysql-test/suite/rpl/r/rpl_fail_register.result create mode 100644 mysql-test/suite/rpl/t/rpl_fail_register.test diff --git a/mysql-test/suite/rpl/r/rpl_fail_register.result b/mysql-test/suite/rpl/r/rpl_fail_register.result new file mode 100644 index 00000000000..4e433636ad9 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_fail_register.result @@ -0,0 +1,16 @@ +include/master-slave.inc +[connection master] +set @old_dbug=@@global.debug_dbug; +set global debug_dbug='d,fail_com_register_slave'; +stop slave; +reset slave; +include/wait_for_slave_to_stop.inc +start slave; +stop slave; +include/wait_for_slave_to_stop.inc +set global debug_dbug=@old_dbug; +kill DUMP_THREAD; +show slave hosts; +Server_id Host Port Master_id +start slave; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_fail_register.test b/mysql-test/suite/rpl/t/rpl_fail_register.test new file mode 100644 index 00000000000..bfecc9d7f14 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_fail_register.test @@ -0,0 +1,33 @@ +source include/have_debug.inc; +source include/have_binlog_format_mixed.inc; +source include/master-slave.inc; + +connection slave; + +set @old_dbug=@@global.debug_dbug; +set global debug_dbug='d,fail_com_register_slave'; + +stop slave; +reset slave; +source include/wait_for_slave_to_stop.inc; +start slave; +stop slave; +source include/wait_for_slave_to_stop.inc; +set global debug_dbug=@old_dbug; + +connection master; + +### why is that needed? +let $id=`SELECT id from information_schema.processlist where command='Binlog Dump'`; +if ($id) { + replace_result $id DUMP_THREAD; + eval kill $id; + let $wait_condition= SELECT count(*)=0 from information_schema.processlist where command='Binlog Dump'; + source include/wait_condition.inc; +} + +show slave hosts; + +connection slave; +start slave; +source include/rpl_end.inc; diff --git a/sql/slave.cc b/sql/slave.cc index d359e1bdae4..add75e281a8 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -3179,6 +3179,7 @@ connected: goto err; goto connected; } + DBUG_EXECUTE_IF("fail_com_register_slave", goto err;); } DBUG_PRINT("info",("Starting reading binary log from master")); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 96d56bfda17..a665cd6522e 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -63,6 +63,7 @@ #include "sql_parse.h" // is_update_query #include "sql_callback.h" #include "sql_connect.h" +#include "repl_failsafe.h" /* The following is used to initialise Table_ident with a internal @@ -1496,6 +1497,7 @@ THD::~THD() if (rli_slave) rli_slave->cleanup_after_session(); my_free(semisync_info); + unregister_slave(this, true, true); #endif free_root(&main_mem_root, MYF(0)); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index ae5a6b4cd35..82dbe809c7d 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1269,7 +1269,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, general_log_print(thd, command, "Log: '%s' Pos: %lu", name, pos); if (nlen < FN_REFLEN) mysql_binlog_send(thd, thd->strmake(name, nlen), (my_off_t)pos, flags); - unregister_slave(thd,1,1); /* fake COM_QUIT -- if we get here, the thread needs to terminate */ error = TRUE; break; From 39c60116e8065e9161f14f65ceb26e54782388bc Mon Sep 17 00:00:00 2001 From: Sivert Sorumgard Date: Fri, 13 Dec 2019 13:03:08 +0100 Subject: [PATCH 10/13] Bug#30628268: OUT OF MEMORY CRASH The event scheduler has a THD which is used for e.g. keeping track of the timing of the events. Thus, each scheduling of an event will make use of this THD, which in turn allocates memory in the THD's mem root. However, the mem root was never cleared, and hence, the memory occupied would monotonically increase throughout the life time of the server. The root cause was found by Jon Olav Hauglid, and this fix clears the THD's mem root for each event being scheduled. Change-Id: I462d2b9fd9658c9f33ab5080f7cd0e0ea28382df --- sql/event_scheduler.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index dfb72007ec6..2f5487a3541 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -1,4 +1,5 @@ -/* Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2006, 2019, Oracle and/or its affiliates. + Copyright (c) 2009, 2020, 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 @@ -490,6 +491,7 @@ Event_scheduler::run(THD *thd) DBUG_PRINT("info", ("job_data is NULL, the thread was killed")); } DBUG_PRINT("info", ("state=%s", scheduler_states_names[state].str)); + free_root(thd->mem_root, MYF(0)); } LOCK_DATA(); From e8e67bd4a4cabaae98dcbfac79c1d4b84066bf1c Mon Sep 17 00:00:00 2001 From: Anushree Prakash B Date: Thu, 26 Dec 2019 16:29:04 +0530 Subject: [PATCH 11/13] Bug#30689251 - BACKPORT TO MYSQL-5.6, BUG#29597896 - NULL POINTER DEREFERENCE IN LIBMYSQL DESCRIPTION: ============ There can be issues if the packets sent by the server are not proper. Certain checks should be performed at the client side while unpacking fields data. FIX: ==== Check for the appropriate fields data and error out if it is not present. RB: 23601 --- sql-common/client.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/sql-common/client.c b/sql-common/client.c index c7fb70fbeef..22a4f789485 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -1538,9 +1538,23 @@ unpack_fields(MYSQL *mysql, MYSQL_DATA *data,MEM_ROOT *alloc,uint fields, { if (field - result >= fields) goto err; + + /* + If any of the row->data[] below is NULL, it can result in a + crash. Error out early as it indicates a malformed packet. + For data[0], data[1] and data[5], strmake_root will handle + NULL values. + */ + if (!row->data[2] || !row->data[3] || !row->data[4]) + { + free_rows(data); + set_mysql_error(mysql, CR_MALFORMED_PACKET, unknown_sqlstate); + DBUG_RETURN(0); + } + cli_fetch_lengths(&lengths[0], row->data, default_value ? 6 : 5); - field->org_table= field->table= strdup_root(alloc,(char*) row->data[0]); - field->name= strdup_root(alloc,(char*) row->data[1]); + field->org_table= field->table= strmake_root(alloc,(char*) row->data[0], lengths[0]); + field->name= strmake_root(alloc,(char*) row->data[1], lengths[1]); field->length= (uint) uint3korr(row->data[2]); field->type= (enum enum_field_types) (uchar) row->data[3][0]; @@ -1565,7 +1579,7 @@ unpack_fields(MYSQL *mysql, MYSQL_DATA *data,MEM_ROOT *alloc,uint fields, field->flags|= NUM_FLAG; if (default_value && row->data[5]) { - field->def=strdup_root(alloc,(char*) row->data[5]); + field->def= strmake_root(alloc,(char*) row->data[5], lengths[5]); field->def_length= lengths[5]; } else From 69bd73173d041a06504161a0b93bb529737f2c84 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 28 Apr 2020 21:41:49 +0200 Subject: [PATCH 12/13] correct off-by-one error in CONCAT CONCAT_WS didn't have it --- sql/item_strfunc.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index f8f3d913cd8..5fdb1745068 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -539,7 +539,7 @@ String *Item_func_concat::val_str(String *str) use_as_buff=str; // Put next arg here } else if (tmp_value.is_alloced() && res2->ptr() >= tmp_value.ptr() && - res2->ptr() <= tmp_value.ptr() + tmp_value.alloced_length()) + res2->ptr() < tmp_value.ptr() + tmp_value.alloced_length()) { /* This happens really seldom: From 6a31aea5a1a50614af3a6591a085dc47061cdd0e Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 28 Apr 2020 11:20:52 +0200 Subject: [PATCH 13/13] BUG#30301356 - SOME EVENTS ARE DELAYED AFTER DROPPING EVENT queues.c cleanup and refactoring. Restore old version of _downhead() (from before cd483c55209) that works well in an average case. Use it for queue_fix(). Move existing specialized version of _downhead() to queue_replace() where it'll be handling the case it was specifically optimized for (moving the element to the end of the queue). And correct it to fix the heap not only down, but also up (this fixes BUG#30301356). Add unit tests. Collateral cosmetic fixes. --- include/queues.h | 10 +- mysys/queues.c | 169 ++++++++++++++++++---------------- unittest/mysys/CMakeLists.txt | 2 +- unittest/mysys/queues-t.c | 138 +++++++++++++++++++++++++++ 4 files changed, 234 insertions(+), 85 deletions(-) create mode 100644 unittest/mysys/queues-t.c diff --git a/include/queues.h b/include/queues.h index 4fef72b149c..c4630cf886c 100644 --- a/include/queues.h +++ b/include/queues.h @@ -54,7 +54,7 @@ typedef struct st_queue { #define queue_top(queue) ((queue)->root[1]) #define queue_element(queue,index) ((queue)->root[index]) #define queue_end(queue) ((queue)->root[(queue)->elements]) -#define queue_replace_top(queue) _downheap(queue, 1, (queue)->root[1]) +#define queue_replace_top(queue) _downheap(queue, 1) #define queue_set_cmp_arg(queue, set_arg) (queue)->first_cmp_arg= set_arg #define queue_set_max_at_top(queue, set_arg) \ (queue)->max_at_top= set_arg ? -1 : 1 @@ -62,23 +62,23 @@ typedef struct st_queue { typedef int (*queue_compare)(void *,uchar *, uchar *); int init_queue(QUEUE *queue,uint max_elements,uint offset_to_key, - pbool max_at_top, queue_compare compare, + my_bool max_at_top, queue_compare compare, void *first_cmp_arg, uint offset_to_queue_pos, uint auto_extent); int reinit_queue(QUEUE *queue,uint max_elements,uint offset_to_key, - pbool max_at_top, queue_compare compare, + my_bool max_at_top, queue_compare compare, void *first_cmp_arg, uint offset_to_queue_pos, uint auto_extent); int resize_queue(QUEUE *queue, uint max_elements); void delete_queue(QUEUE *queue); -void queue_insert(QUEUE *queue,uchar *element); +void queue_insert(QUEUE *queue, uchar *element); int queue_insert_safe(QUEUE *queue, uchar *element); uchar *queue_remove(QUEUE *queue,uint idx); void queue_replace(QUEUE *queue,uint idx); #define queue_remove_all(queue) { (queue)->elements= 0; } #define queue_is_full(queue) (queue->elements == queue->max_elements) -void _downheap(QUEUE *queue, uint idx, uchar *element); +void _downheap(QUEUE *queue, uint idx); void queue_fix(QUEUE *queue); #define is_queue_inited(queue) ((queue)->root != 0) diff --git a/mysys/queues.c b/mysys/queues.c index 418163d7c58..e11af4a1ea9 100644 --- a/mysys/queues.c +++ b/mysys/queues.c @@ -70,10 +70,9 @@ */ int init_queue(QUEUE *queue, uint max_elements, uint offset_to_key, - pbool max_at_top, int (*compare) (void *, uchar *, uchar *), + my_bool max_at_top, int (*compare) (void *, uchar *, uchar *), void *first_cmp_arg, uint offset_to_queue_pos, uint auto_extent) - { DBUG_ENTER("init_queue"); if ((queue->root= (uchar **) my_malloc((max_elements + 1) * sizeof(void*), @@ -109,7 +108,7 @@ int init_queue(QUEUE *queue, uint max_elements, uint offset_to_key, */ int reinit_queue(QUEUE *queue, uint max_elements, uint offset_to_key, - pbool max_at_top, int (*compare) (void *, uchar *, uchar *), + my_bool max_at_top, int (*compare) (void *, uchar *, uchar *), void *first_cmp_arg, uint offset_to_queue_pos, uint auto_extent) { @@ -182,6 +181,28 @@ void delete_queue(QUEUE *queue) } +static void insert_at(QUEUE *queue, uchar *element, uint idx) +{ + uint next_index, offset_to_key= queue->offset_to_key; + uint offset_to_queue_pos= queue->offset_to_queue_pos; + /* max_at_top swaps the comparison if we want to order by desc */ + while ((next_index= idx >> 1) > 0 && + queue->compare(queue->first_cmp_arg, + element + offset_to_key, + queue->root[next_index] + offset_to_key) * + queue->max_at_top < 0) + { + queue->root[idx]= queue->root[next_index]; + if (offset_to_queue_pos) + (*(uint*) (queue->root[idx] + offset_to_queue_pos-1))= idx; + idx= next_index; + } + queue->root[idx]= element; + if (offset_to_queue_pos) + (*(uint*) (element + offset_to_queue_pos-1))= idx; +} + + /* Insert element in queue @@ -191,28 +212,10 @@ void delete_queue(QUEUE *queue) element Element to insert */ -void queue_insert(register QUEUE *queue, uchar *element) +void queue_insert(QUEUE *queue, uchar *element) { - reg2 uint idx, next; - uint offset_to_queue_pos= queue->offset_to_queue_pos; DBUG_ASSERT(queue->elements < queue->max_elements); - - idx= ++queue->elements; - /* max_at_top swaps the comparison if we want to order by desc */ - while (idx > 1 && - (queue->compare(queue->first_cmp_arg, - element + queue->offset_to_key, - queue->root[(next= idx >> 1)] + - queue->offset_to_key) * queue->max_at_top) < 0) - { - queue->root[idx]= queue->root[next]; - if (offset_to_queue_pos) - (*(uint*) (queue->root[idx] + offset_to_queue_pos-1))= idx; - idx= next; - } - queue->root[idx]= element; - if (offset_to_queue_pos) - (*(uint*) (element+ offset_to_queue_pos-1))= idx; + insert_at(queue, element, ++queue->elements); } @@ -230,7 +233,7 @@ void queue_insert(register QUEUE *queue, uchar *element) 2 auto_extend is 0; No insertion done */ -int queue_insert_safe(register QUEUE *queue, uchar *element) +int queue_insert_safe(QUEUE *queue, uchar *element) { if (queue->elements == queue->max_elements) @@ -240,7 +243,7 @@ int queue_insert_safe(register QUEUE *queue, uchar *element) if (resize_queue(queue, queue->max_elements + queue->auto_extent)) return 1; } - + queue_insert(queue, element); return 0; } @@ -259,81 +262,55 @@ int queue_insert_safe(register QUEUE *queue, uchar *element) pointer to removed element */ -uchar *queue_remove(register QUEUE *queue, uint idx) +uchar *queue_remove(QUEUE *queue, uint idx) { uchar *element; - DBUG_ASSERT(idx >= 1 && idx <= queue->elements); + DBUG_ASSERT(idx >= 1); + DBUG_ASSERT(idx <= queue->elements); element= queue->root[idx]; - _downheap(queue, idx, queue->root[queue->elements--]); + queue->root[idx]= queue->root[queue->elements--]; + queue_replace(queue, idx); return element; } /* - Add element to fixed position and update heap + Restores the heap property from idx down the heap SYNOPSIS _downheap() queue Queue to use idx Index of element to change - element Element to store at 'idx' - - NOTE - This only works if element is >= all elements <= start_idx */ -void _downheap(register QUEUE *queue, uint start_idx, uchar *element) +void _downheap(QUEUE *queue, uint idx) { - uint elements,half_queue,offset_to_key, next_index, offset_to_queue_pos; - register uint idx= start_idx; - my_bool first= TRUE; - - offset_to_key=queue->offset_to_key; - offset_to_queue_pos= queue->offset_to_queue_pos; - half_queue= (elements= queue->elements) >> 1; + uchar *element= queue->root[idx]; + uint next_index, + elements= queue->elements, + half_queue= elements >> 1, + offset_to_key= queue->offset_to_key, + offset_to_queue_pos= queue->offset_to_queue_pos; while (idx <= half_queue) { - next_index=idx+idx; + next_index= idx+idx; if (next_index < elements && - (queue->compare(queue->first_cmp_arg, - queue->root[next_index]+offset_to_key, - queue->root[next_index+1]+offset_to_key) * - queue->max_at_top) > 0) + (queue->compare(queue->first_cmp_arg, + queue->root[next_index]+offset_to_key, + queue->root[next_index+1]+offset_to_key) * + queue->max_at_top) > 0) next_index++; - if (first && - (((queue->compare(queue->first_cmp_arg, - queue->root[next_index]+offset_to_key, - element+offset_to_key) * queue->max_at_top) >= 0))) - { - queue->root[idx]= element; - if (offset_to_queue_pos) - (*(uint*) (element + offset_to_queue_pos-1))= idx; - return; - } - first= FALSE; - queue->root[idx]= queue->root[next_index]; - if (offset_to_queue_pos) - (*(uint*) (queue->root[idx] + offset_to_queue_pos-1))= idx; - idx=next_index; - } - - /* - Insert the element into the right position. This is the same code - as we have in queue_insert() - */ - while ((next_index= (idx >> 1)) > start_idx && - queue->compare(queue->first_cmp_arg, - element+offset_to_key, - queue->root[next_index]+offset_to_key)* - queue->max_at_top < 0) - { + if ((queue->compare(queue->first_cmp_arg, + queue->root[next_index]+offset_to_key, + element+offset_to_key) * queue->max_at_top) >= 0) + break; queue->root[idx]= queue->root[next_index]; if (offset_to_queue_pos) (*(uint*) (queue->root[idx] + offset_to_queue_pos-1))= idx; idx= next_index; } - queue->root[idx]= element; + queue->root[idx]=element; if (offset_to_queue_pos) (*(uint*) (element + offset_to_queue_pos-1))= idx; } @@ -351,7 +328,7 @@ void queue_fix(QUEUE *queue) { uint i; for (i= queue->elements >> 1; i > 0; i--) - _downheap(queue, i, queue_element(queue, i)); + _downheap(queue, i); } @@ -362,13 +339,47 @@ void queue_fix(QUEUE *queue) queue_replace() queue Queue to use idx Index of element to change - element Element to store at 'idx' + + NOTE + optimized for the case when the new position is close to the end of the + heap (typical for queue_remove() replacements). */ void queue_replace(QUEUE *queue, uint idx) { uchar *element= queue->root[idx]; - DBUG_ASSERT(idx >= 1 && idx <= queue->elements); - queue_remove(queue, idx); - queue_insert(queue, element); + uint next_index, + elements= queue->elements, + half_queue= elements>>1, + offset_to_key= queue->offset_to_key, + offset_to_queue_pos= queue->offset_to_queue_pos; + my_bool first= TRUE; + + while (idx <= half_queue) + { + next_index= idx + idx; + if (next_index < elements && + queue->compare(queue->first_cmp_arg, + queue->root[next_index]+offset_to_key, + queue->root[next_index+1]+offset_to_key) * + queue->max_at_top > 0) + next_index++; + if (first && + queue->compare(queue->first_cmp_arg, + queue->root[next_index]+offset_to_key, + element+offset_to_key) * queue->max_at_top >= 0) + { + queue->root[idx]= element; + if (offset_to_queue_pos) + (*(uint*) (element + offset_to_queue_pos-1))= idx; + break; + } + first= FALSE; + queue->root[idx]= queue->root[next_index]; + if (offset_to_queue_pos) + (*(uint*) (queue->root[idx] + offset_to_queue_pos-1))= idx; + idx=next_index; + } + + insert_at(queue, element, idx); } diff --git a/unittest/mysys/CMakeLists.txt b/unittest/mysys/CMakeLists.txt index de7efeaaaec..21be7e82cda 100644 --- a/unittest/mysys/CMakeLists.txt +++ b/unittest/mysys/CMakeLists.txt @@ -14,7 +14,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA MY_ADD_TESTS(bitmap base64 my_atomic my_rdtsc lf my_malloc my_getopt dynstring - LINK_LIBRARIES mysys) + queues LINK_LIBRARIES mysys) MY_ADD_TESTS(my_vsnprintf LINK_LIBRARIES strings mysys) IF(WIN32) diff --git a/unittest/mysys/queues-t.c b/unittest/mysys/queues-t.c new file mode 100644 index 00000000000..d12dd86a7f6 --- /dev/null +++ b/unittest/mysys/queues-t.c @@ -0,0 +1,138 @@ +/* Copyright (c) 2020, 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 Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include +#include +#include +#include "tap.h" + +int cmp(void *arg __attribute__((unused)), uchar *a, uchar *b) +{ + return *a < *b ? -1 : *a > *b; +} + +#define rnd(R) ((uint)(my_rnd(R) * INT_MAX32)) + +#define el(Q,I) ((uint)*queue_element(Q, I)) + +my_bool verbose; + +my_bool check_queue(QUEUE *queue) +{ + char b[1024]={0}, *s, *e=b+sizeof(b)-2; + my_bool ok=1; + uint i; + + s= b + my_snprintf(b, e-b, "%x", el(queue, 1)); + for (i=2; i <= queue->elements; i++) + { + s+= my_snprintf(s, e-s, ", %x", el(queue, i)); + ok &= el(queue, i) <= el(queue, i>>1); + } + if (!ok || verbose) + diag("%s", b); + return ok; +} + +int main(int argc __attribute__((unused)), char *argv[]) +{ + QUEUE q, *queue=&q; + MY_INIT(argv[0]); + plan(19); + + verbose=1; + + init_queue(queue, 256, 0, 1, cmp, NULL, 0, 0); + queue_insert(queue, (uchar*)"\x99"); + queue_insert(queue, (uchar*)"\x19"); + queue_insert(queue, (uchar*)"\x36"); + queue_insert(queue, (uchar*)"\x17"); + queue_insert(queue, (uchar*)"\x12"); + queue_insert(queue, (uchar*)"\x05"); + queue_insert(queue, (uchar*)"\x25"); + queue_insert(queue, (uchar*)"\x09"); + queue_insert(queue, (uchar*)"\x15"); + queue_insert(queue, (uchar*)"\x06"); + queue_insert(queue, (uchar*)"\x11"); + queue_insert(queue, (uchar*)"\x01"); + queue_insert(queue, (uchar*)"\x04"); + queue_insert(queue, (uchar*)"\x13"); + queue_insert(queue, (uchar*)"\x24"); + ok(check_queue(queue), "after insert"); + queue_remove(queue, 5); + ok(check_queue(queue), "after remove 5th"); + + queue_element(queue, 1) = (uchar*)"\x01"; + queue_element(queue, 2) = (uchar*)"\x10"; + queue_element(queue, 3) = (uchar*)"\x04"; + queue_element(queue, 4) = (uchar*)"\x09"; + queue_element(queue, 5) = (uchar*)"\x13"; + queue_element(queue, 6) = (uchar*)"\x03"; + queue_element(queue, 7) = (uchar*)"\x08"; + queue_element(queue, 8) = (uchar*)"\x07"; + queue_element(queue, 9) = (uchar*)"\x06"; + queue_element(queue,10) = (uchar*)"\x12"; + queue_element(queue,11) = (uchar*)"\x05"; + queue_element(queue,12) = (uchar*)"\x02"; + queue_element(queue,13) = (uchar*)"\x11"; + queue->elements= 13; + ok(!check_queue(queue), "manually filled (queue property violated)"); + + queue_fix(queue); + ok(check_queue(queue), "fixed"); + + ok(*queue_remove_top(queue) == 0x13, "remove top 13"); + ok(*queue_remove_top(queue) == 0x12, "remove top 12"); + ok(*queue_remove_top(queue) == 0x11, "remove top 11"); + ok(*queue_remove_top(queue) == 0x10, "remove top 10"); + ok(*queue_remove_top(queue) == 0x09, "remove top 9"); + ok(*queue_remove_top(queue) == 0x08, "remove top 8"); + ok(*queue_remove_top(queue) == 0x07, "remove top 7"); + ok(*queue_remove_top(queue) == 0x06, "remove top 6"); + ok(*queue_remove_top(queue) == 0x05, "remove top 5"); + ok(*queue_remove_top(queue) == 0x04, "remove top 4"); + ok(*queue_remove_top(queue) == 0x03, "remove top 3"); + ok(*queue_remove_top(queue) == 0x02, "remove top 2"); + ok(*queue_remove_top(queue) == 0x01, "remove top 1"); + + /* random test */ + { + int i, res; + struct my_rnd_struct rand; + my_rnd_init(&rand, (ulong)(intptr)&i, (ulong)(intptr)argv); + verbose=0; + + for (res= i=1; i <= 250; i++) + { + uchar *s=alloca(2); + *s= rnd(&rand) % 251; + queue_insert(queue, s); + res &= check_queue(queue); + } + ok(res, "inserted 250"); + + while (queue->elements) + { + queue_remove(queue, (rnd(&rand) % queue->elements) + 1); + res &= check_queue(queue); + } + ok(res, "removed 250"); + } + + delete_queue(queue); + my_end(0); + return exit_status(); +} +