mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
MDEV-25659 trigger name is empty after upgrade to 10.4
Problem: At some point, we made stored rountines fail at CREATE time instead of execution time in case of this syntax: IF unknown_variable ... END IF As a result, a trigger created before this change and contained an unknown variable worked in a bad way after upgrade: - It was displayed with an empty trigger name by SHOW CREATE TRIGGER - It was displayed with an empty trigger name by INFORMATION_SCHEMA.TRIGGERS - An attempt to DROP this trigger returned errors - nothing happened. - DROP TABLE did not remove the .TRN file corresponding to this broken trigger. Underlying code observations: The old code assumed that the trigger name resides in the current lex: if(thd->lex->spname) m_trigger_name= &thd->lex->spname->m_name; This is not always the case. Some SP statements (e.g. IF) do the following in their beginning: - create a separate local LEX - set thd->lex to this new local LEX - push the new local LEX to the stack in sp_head::m_lex and the following at the end of the statement: - pop the previous LEX from the stack sp_head::m_lex - set thd->lex back to the popped value So when the parse error happens inside e.g. IF statement, thd->lex->spname is a NULL pointer, because thd->lex points to the local LEX (without SP name) rather than the top level LEX (with SP name). Fix: - Adding a new method sp_head::find_spname_recursive() which walks inside the LEX stack sp_head::m_lex from the top (the newest, most local) to the bottom (the oldest), and finds the one which contains a non-zero spname pointer. - Using the new method inside Deprecated_trigger_syntax_handler::handle_condition(): First it still tests thd->lex->spname (like before this change), and uses it in case it is not empty. Otherwise (if thd->lex->spname is empty), it calls sp_head::find_spname_recursive() to find the LEX with a non-empty spname inside the LEX stack of the current sphead.
This commit is contained in:
@ -292,7 +292,7 @@ class Deprecated_trigger_syntax_handler : public Internal_error_handler
|
||||
private:
|
||||
|
||||
char m_message[MYSQL_ERRMSG_SIZE];
|
||||
LEX_CSTRING *m_trigger_name;
|
||||
const LEX_CSTRING *m_trigger_name;
|
||||
|
||||
public:
|
||||
|
||||
@ -308,8 +308,23 @@ public:
|
||||
if (sql_errno != EE_OUTOFMEMORY &&
|
||||
sql_errno != ER_OUT_OF_RESOURCES)
|
||||
{
|
||||
// Check if the current LEX contains a non-empty spname
|
||||
if(thd->lex->spname)
|
||||
m_trigger_name= &thd->lex->spname->m_name;
|
||||
else if (thd->lex->sphead)
|
||||
{
|
||||
/*
|
||||
Some SP statements, for example IF, create their own local LEX.
|
||||
All LEX instances are available in the LEX stack in sphead::m_lex.
|
||||
Let's find the one that contains a non-zero spname.
|
||||
Note, although a parse error has happened, the LEX instances
|
||||
in sphead::m_lex are not freed yet at this point. The first
|
||||
found non-zero spname contains the valid trigger name.
|
||||
*/
|
||||
const sp_name *spname= thd->lex->sphead->find_spname_recursive();
|
||||
if (spname)
|
||||
m_trigger_name= &spname->m_name;
|
||||
}
|
||||
if (m_trigger_name)
|
||||
my_snprintf(m_message, sizeof(m_message),
|
||||
ER_THD(thd, ER_ERROR_IN_TRIGGER_BODY),
|
||||
@ -322,7 +337,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
LEX_CSTRING *get_trigger_name() { return m_trigger_name; }
|
||||
const LEX_CSTRING *get_trigger_name() { return m_trigger_name; }
|
||||
char *get_error_message() { return m_message; }
|
||||
};
|
||||
|
||||
@ -1490,7 +1505,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const LEX_CSTRING *db,
|
||||
|
||||
if (unlikely(parse_error))
|
||||
{
|
||||
LEX_CSTRING *name;
|
||||
const LEX_CSTRING *name;
|
||||
|
||||
/*
|
||||
In case of errors, disable all triggers for the table, but keep
|
||||
|
Reference in New Issue
Block a user