1
0
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:
Alexander Barkov
2022-01-13 14:20:05 +04:00
parent 6d8794e567
commit 2832b949fc
4 changed files with 546 additions and 3 deletions

View File

@ -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