From fe784950532e96812d06f1bcd6b977435154b15c Mon Sep 17 00:00:00 2001 From: Dmitry Shulga Date: Fri, 11 Jun 2021 11:25:56 +0700 Subject: [PATCH] MDEV-16708: fixed assert firing in the method THD::reset_for_the_next_command --- sql/sql_binlog.cc | 22 +++++++++++++++++++++- sql/sql_class.h | 27 +++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc index 5cd70199645..bab2afb957a 100644 --- a/sql/sql_binlog.cc +++ b/sql/sql_binlog.cc @@ -354,8 +354,28 @@ void mysql_client_binlog_statement(THD* thd) (ev->flags & LOG_EVENT_SKIP_REPLICATION_F ? OPTION_SKIP_REPLICATION : 0); - err= ev->apply_event(rgi); + { + /* + For conventional statements thd->lex points to thd->main_lex, that is + thd->lex == &thd->main_lex. On the other hand, for prepared statement + thd->lex points to the LEX object explicitly allocated for execution + of the prepared statement and in this case thd->lex != &thd->main_lex. + On handling the BINLOG statement, invocation of ev->apply_event(rgi) + initiates the following sequence of calls + Rows_log_event::do_apply_event -> THD::reset_for_next_command + Since the method THD::reset_for_next_command() contains assert + DBUG_ASSERT(lex == &main_lex) + this sequence of calls results in crash when a binlog event is + applied in PS mode. So, reset the current lex temporary to point to + thd->main_lex before running ev->apply_event() and restore its + original value on return. + */ + LEX *backup_lex; + thd->backup_and_reset_current_lex(&backup_lex); + err= ev->apply_event(rgi); + thd->restore_current_lex(backup_lex); + } thd->variables.option_bits= (thd->variables.option_bits & ~OPTION_SKIP_REPLICATION) | save_skip_replication; diff --git a/sql/sql_class.h b/sql/sql_class.h index 004b92712c8..713523b7d75 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -5453,6 +5453,33 @@ public: return (variables.old_behavior & OLD_MODE_UTF8_IS_UTF8MB3 ? MY_UTF8_IS_UTF8MB3 : 0); } + + /** + Save current lex to the output parameter and reset it to point to + main_lex. This method is called from mysql_client_binlog_statement() + to temporary + + @param[out] backup_lex original value of current lex + */ + + void backup_and_reset_current_lex(LEX **backup_lex) + { + *backup_lex= lex; + lex= &main_lex; + } + + + /** + Restore current lex to its original value it had before calling the method + backup_and_reset_current_lex(). + + @param backup_lex original value of current lex + */ + + void restore_current_lex(LEX *backup_lex) + { + lex= backup_lex; + } };