Skip to content

Commit 85a541c

Browse files
kahatlenbjornmu
authored andcommitted
Bug#21824519: ASSERTION IN DROP TRIGGER WHEN TABLE HAS VIRTUAL GENERATED COLUMN
In some cases a DROP TRIGGER statement could hit a DBUG_ASSERT in debug builds if the trigger was defined on a table that contained a generated column. The same DBUG_ASSERT could under some circumstances be hit when calling a stored function that referenced a table that contained a generated column. The DBUG_ASSERT was hit during re-resolving (aka "refixing") of the generated column expression. The first time a statement opens a table that contains a generated column, all the generated columns in that table will be re-resolved to ensure that they are in a pristine state. The DBUG_ASSERT (in TABLE_LIST::map()) fails because the TABLE_LIST object associated with the Item_field that is being re-resolved, is in an inconsistent state where m_map is not in sync with m_tableno. The TABLE_LIST objects in question are allocated in sp_add_to_query_tables() in the DROP TRIGGER case and in sp_head::add_used_tables_to_table_list() in the stored function case. Common for these two functions is that they initialize the TABLE_LIST objects by manually setting the various members, instead of calling TABLE_LIST::init_one_table(), which is the common way of initializing such objects. This manual initialization forgets to set the m_map and m_tableno members to values that are consistent with each other. The fix makes the functions use init_one_table() to initialize the TABLE_LIST objects. init_one_table() also needed a little reorganizing to allow more flexible initialization of the associated lock request. (cherry picked from commit 1aabcfde317a5b96bf9d33226795aa096db2aab8)
1 parent 3572cae commit 85a541c

File tree

7 files changed

+91
-44
lines changed

7 files changed

+91
-44
lines changed

mysql-test/suite/gcol/r/gcol_bugfixes.result

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,3 +443,26 @@ SELECT MAX(a) FROM v WHERE b=1;
443443
MAX(a)
444444
1
445445
DROP TABLE v;
446+
#
447+
# Bug#21824519: ASSERTION IN DROP TRIGGER WHEN TABLE HAS
448+
# VIRTUAL GENERATED COLUMN
449+
#
450+
CREATE TABLE t (a INT, b INT GENERATED ALWAYS AS (a) VIRTUAL);
451+
CREATE TRIGGER tr BEFORE INSERT ON t FOR EACH ROW BEGIN END;
452+
INSERT INTO t (a) VALUES (1);
453+
SELECT * FROM t;
454+
a b
455+
1 1
456+
DROP TRIGGER tr;
457+
SELECT * FROM t;
458+
a b
459+
1 1
460+
CREATE FUNCTION f() RETURNS INT RETURN (SELECT COUNT(*) FROM t);
461+
SELECT f();
462+
f()
463+
1
464+
DROP FUNCTION f;
465+
SELECT * FROM t;
466+
a b
467+
1 1
468+
DROP TABLE t;

mysql-test/suite/gcol/t/gcol_bugfixes.test

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,3 +443,21 @@ b INT, KEY(b));
443443
INSERT INTO v (a,b) VALUES (1,1);
444444
SELECT MAX(a) FROM v WHERE b=1;
445445
DROP TABLE v;
446+
447+
--echo #
448+
--echo # Bug#21824519: ASSERTION IN DROP TRIGGER WHEN TABLE HAS
449+
--echo # VIRTUAL GENERATED COLUMN
450+
--echo #
451+
CREATE TABLE t (a INT, b INT GENERATED ALWAYS AS (a) VIRTUAL);
452+
CREATE TRIGGER tr BEFORE INSERT ON t FOR EACH ROW BEGIN END;
453+
INSERT INTO t (a) VALUES (1);
454+
SELECT * FROM t;
455+
# DROP TRIGGER used to hit a DBUG_ASSERT.
456+
DROP TRIGGER tr;
457+
SELECT * FROM t;
458+
CREATE FUNCTION f() RETURNS INT RETURN (SELECT COUNT(*) FROM t);
459+
# And this function call hit the same DBUG_ASSERT.
460+
SELECT f();
461+
DROP FUNCTION f;
462+
SELECT * FROM t;
463+
DROP TABLE t;

sql/sp.cc

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2626,26 +2626,24 @@ bool sp_check_name(LEX_STRING *ident)
26262626
the global table list, e.g. "mysql", "proc".
26272627
*/
26282628
TABLE_LIST *sp_add_to_query_tables(THD *thd, LEX *lex,
2629-
const char *db, const char *name,
2630-
thr_lock_type locktype,
2631-
enum_mdl_type mdl_type)
2629+
const char *db, const char *name)
26322630
{
2633-
TABLE_LIST *table= (TABLE_LIST *)thd->mem_calloc(sizeof(TABLE_LIST));
2631+
TABLE_LIST *table= static_cast<TABLE_LIST*>(thd->alloc(sizeof(TABLE_LIST)));
26342632

26352633
if (!table)
26362634
return NULL;
26372635

2638-
table->db_length= strlen(db);
2639-
table->db= thd->strmake(db, table->db_length);
2640-
table->table_name_length= strlen(name);
2641-
table->table_name= thd->strmake(name, table->table_name_length);
2642-
table->alias= thd->mem_strdup(name);
2643-
table->lock_type= locktype;
2636+
size_t db_length= strlen(db);
2637+
size_t table_name_length= strlen(name);
2638+
2639+
table->init_one_table(thd->strmake(db, db_length), db_length,
2640+
thd->strmake(name, table_name_length),
2641+
table_name_length,
2642+
thd->mem_strdup(name),
2643+
TL_IGNORE, MDL_SHARED_NO_WRITE);
2644+
26442645
table->select_lex= lex->current_select();
26452646
table->cacheable_table= 1;
2646-
MDL_REQUEST_INIT(&table->mdl_request,
2647-
MDL_key::TABLE, table->db, table->table_name,
2648-
mdl_type, MDL_TRANSACTION);
26492647

26502648
lex->add_to_query_tables(table);
26512649

sql/sp.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,7 @@ uint sp_get_flags_for_command(LEX *lex);
207207
bool sp_check_name(LEX_STRING *ident);
208208

209209
TABLE_LIST *sp_add_to_query_tables(THD *thd, LEX *lex,
210-
const char *db, const char *name,
211-
thr_lock_type locktype,
212-
enum_mdl_type mdl_type);
210+
const char *db, const char *name);
213211

214212
bool sp_check_show_access(THD *thd, sp_head *sp, bool *full_access);
215213

sql/sp_head.cc

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "sp_cache.h"
3333
#include "sql_parse.h" // cleanup_items
3434
#include "sql_base.h" // close_thread_tables
35+
#include "template_utils.h" // pointer_cast
3536
#include "transaction.h" // trans_commit_stmt
3637
#include "opt_trace.h" // opt_trace_disable_etc
3738

@@ -2101,31 +2102,19 @@ void sp_head::add_used_tables_to_table_list(THD *thd,
21012102

21022103
for (uint i= 0; i < m_sptabs.records; i++)
21032104
{
2104-
char *tab_buff, *key_buff;
2105-
SP_TABLE *stab= (SP_TABLE*) my_hash_element(&m_sptabs, i);
2105+
SP_TABLE *stab= pointer_cast<SP_TABLE*>(my_hash_element(&m_sptabs, i));
21062106
if (stab->temp)
21072107
continue;
21082108

2109-
if (!(tab_buff= (char *)thd->mem_calloc(ALIGN_SIZE(sizeof(TABLE_LIST)) *
2110-
stab->lock_count)) ||
2111-
!(key_buff= (char*)thd->memdup(stab->qname.str,
2112-
stab->qname.length)))
2109+
char *tab_buff= static_cast<char*>
2110+
(thd->alloc(ALIGN_SIZE(sizeof(TABLE_LIST)) * stab->lock_count));
2111+
char *key_buff= static_cast<char*>(thd->memdup(stab->qname.str,
2112+
stab->qname.length));
2113+
if (!tab_buff || !key_buff)
21132114
return;
21142115

21152116
for (uint j= 0; j < stab->lock_count; j++)
21162117
{
2117-
TABLE_LIST *table= (TABLE_LIST *)tab_buff;
2118-
2119-
table->db= key_buff;
2120-
table->db_length= stab->db_length;
2121-
table->table_name= table->db + table->db_length + 1;
2122-
table->table_name_length= stab->table_name_length;
2123-
table->alias= table->table_name + table->table_name_length + 1;
2124-
table->lock_type= stab->lock_type;
2125-
table->cacheable_table= 1;
2126-
table->prelocking_placeholder= 1;
2127-
table->belong_to_view= belong_to_view;
2128-
table->trg_event_map= stab->trg_event_map;
21292118
/*
21302119
Since we don't allow DDL on base tables in prelocked mode it
21312120
is safe to infer the type of metadata lock from the type of
@@ -2140,7 +2129,7 @@ void sp_head::add_used_tables_to_table_list(THD *thd,
21402129
acquire "strong" locks to ensure that LOCK TABLES properly
21412130
works for storage engines which don't use THR_LOCK locks.
21422131
*/
2143-
mdl_lock_type= (table->lock_type >= TL_WRITE_ALLOW_WRITE) ?
2132+
mdl_lock_type= (stab->lock_type >= TL_WRITE_ALLOW_WRITE) ?
21442133
MDL_SHARED_NO_READ_WRITE : MDL_SHARED_READ_ONLY;
21452134
}
21462135
else
@@ -2150,12 +2139,21 @@ void sp_head::add_used_tables_to_table_list(THD *thd,
21502139
Let us respect explicit LOW_PRIORITY clause if was used
21512140
in the routine.
21522141
*/
2153-
mdl_lock_type= mdl_type_for_dml(table->lock_type);
2142+
mdl_lock_type= mdl_type_for_dml(stab->lock_type);
21542143
}
21552144

2156-
MDL_REQUEST_INIT(&table->mdl_request,
2157-
MDL_key::TABLE, table->db, table->table_name,
2158-
mdl_lock_type, MDL_TRANSACTION);
2145+
TABLE_LIST *table= pointer_cast<TABLE_LIST*>(tab_buff);
2146+
table->init_one_table(key_buff, stab->db_length,
2147+
key_buff + stab->db_length + 1,
2148+
stab->table_name_length,
2149+
key_buff + stab->db_length + 1 +
2150+
stab->table_name_length + 1,
2151+
stab->lock_type, mdl_lock_type);
2152+
2153+
table->cacheable_table= 1;
2154+
table->prelocking_placeholder= 1;
2155+
table->belong_to_view= belong_to_view;
2156+
table->trg_event_map= stab->trg_event_map;
21592157

21602158
/* Everyting else should be zeroed */
21612159

sql/sql_trigger.cc

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -361,9 +361,7 @@ bool add_table_for_trigger(THD *thd,
361361
if (Trigger_loader::load_trn_file(thd, trigger_name, trn_path, &tbl_name))
362362
DBUG_RETURN(TRUE);
363363

364-
*table= sp_add_to_query_tables(thd, lex, db_name.str,
365-
tbl_name.str, TL_IGNORE,
366-
MDL_SHARED_NO_WRITE);
364+
*table= sp_add_to_query_tables(thd, lex, db_name.str, tbl_name.str);
367365

368366
DBUG_RETURN(*table ? FALSE : TRUE);
369367
}

sql/table.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1707,7 +1707,8 @@ struct TABLE_LIST
17071707
const char *table_name_arg,
17081708
size_t table_name_length_arg,
17091709
const char *alias_arg,
1710-
enum thr_lock_type lock_type_arg)
1710+
enum thr_lock_type lock_type_arg,
1711+
enum enum_mdl_type mdl_type_arg)
17111712
{
17121713
memset(this, 0, sizeof(*this));
17131714
m_map= 1;
@@ -1719,13 +1720,26 @@ struct TABLE_LIST
17191720
lock_type= lock_type_arg;
17201721
MDL_REQUEST_INIT(&mdl_request,
17211722
MDL_key::TABLE, db, table_name,
1722-
mdl_type_for_dml(lock_type),
1723+
mdl_type_arg,
17231724
MDL_TRANSACTION);
17241725
callback_func= 0;
17251726
opt_hints_table= NULL;
17261727
opt_hints_qb= NULL;
17271728
}
17281729

1730+
inline void init_one_table(const char *db_name_arg,
1731+
size_t db_length_arg,
1732+
const char *table_name_arg,
1733+
size_t table_name_length_arg,
1734+
const char *alias_arg,
1735+
enum thr_lock_type lock_type_arg)
1736+
{
1737+
init_one_table(db_name_arg, db_length_arg,
1738+
table_name_arg, table_name_length_arg,
1739+
alias_arg, lock_type_arg,
1740+
mdl_type_for_dml(lock_type_arg));
1741+
}
1742+
17291743
/// Create a TABLE_LIST object representing a nested join
17301744
static TABLE_LIST *new_nested_join(MEM_ROOT *allocator,
17311745
const char *alias,

0 commit comments

Comments
 (0)