diff options
author | Monty <monty@mariadb.org> | 2021-02-20 14:46:19 +0200 |
---|---|---|
committer | Sergei Golubchik <serg@mariadb.org> | 2021-02-22 22:42:38 +0100 |
commit | 640f42311a72fa82bf7117c2791fc47ceb420361 (patch) | |
tree | 6135b491b2a6d6325f1f3d55c3f13779c80b90fb | |
parent | 0ab1e3914c78e4a82a8e4502b58b20e5598727ab (diff) | |
download | mariadb-git-640f42311a72fa82bf7117c2791fc47ceb420361.tar.gz |
MDEV-24929 Server crash in thr_multi_unlock or in get_schema_tables_result
This was caused by two different bugs:
1) Information_schema tables where not locked by lock_tables, but
get_lock_data() was not filtering these out. This caused a crash when
mysql_unlock_some_tables() tried to unlock tables early, including
not locked information schema tables.
Fixed by not locking SYSTEM_TMP_TABLES
2) In some cases the optimizer will notice that we do not need to read
the information_schema tables at all. In this case
join_tab->read_record is not set, which caused a crash in
get_schema_tables_result()
Fixed by ignoring const tables in get_schema_tables_result()
-rw-r--r-- | mysql-test/main/information_schema.result | 13 | ||||
-rw-r--r-- | mysql-test/main/information_schema.test | 10 | ||||
-rw-r--r-- | sql/lock.cc | 17 | ||||
-rw-r--r-- | sql/sql_show.cc | 10 |
4 files changed, 43 insertions, 7 deletions
diff --git a/mysql-test/main/information_schema.result b/mysql-test/main/information_schema.result index 93ff6e170f6..c65a10cbd4b 100644 --- a/mysql-test/main/information_schema.result +++ b/mysql-test/main/information_schema.result @@ -2303,5 +2303,18 @@ group by f; f drop table t1; # +# MDEV-24929 Server crash in thr_multi_unlock or in +# get_schema_tables_result upon select from I_S with joins +# +CREATE TABLE t1 (a TIMESTAMP, KEY (a)); +INSERT INTO t1 VALUES ('2012-12-12'),('2021-11-11'); +SELECT count(*) FROM t1 AS t1a LEFT JOIN (t1 AS t1b JOIN INFORMATION_SCHEMA.ROUTINES) ON (t1b.a IS NULL); +count(*) +2 +SELECT count(*) FROM t1 AS t1a LEFT JOIN (t1 AS t1b JOIN INFORMATION_SCHEMA.PROFILING) ON (t1b.a IS NULL); +count(*) +2 +DROP TABLE t1; +# # End of 10.3 tests # diff --git a/mysql-test/main/information_schema.test b/mysql-test/main/information_schema.test index 6319cc79df7..4468eb18e45 100644 --- a/mysql-test/main/information_schema.test +++ b/mysql-test/main/information_schema.test @@ -2035,5 +2035,15 @@ group by f; drop table t1; --echo # +--echo # MDEV-24929 Server crash in thr_multi_unlock or in +--echo # get_schema_tables_result upon select from I_S with joins +--echo # + +CREATE TABLE t1 (a TIMESTAMP, KEY (a)); +INSERT INTO t1 VALUES ('2012-12-12'),('2021-11-11'); +SELECT count(*) FROM t1 AS t1a LEFT JOIN (t1 AS t1b JOIN INFORMATION_SCHEMA.ROUTINES) ON (t1b.a IS NULL); +SELECT count(*) FROM t1 AS t1a LEFT JOIN (t1 AS t1b JOIN INFORMATION_SCHEMA.PROFILING) ON (t1b.a IS NULL); +DROP TABLE t1; +--echo # --echo # End of 10.3 tests --echo # diff --git a/sql/lock.cc b/sql/lock.cc index 7dcba179a36..4d055f58242 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -727,6 +727,9 @@ static int unlock_external(THD *thd, TABLE **table,uint count) - GET_LOCK_STORE_LOCKS : Store lock info in TABLE - GET_LOCK_SKIP_SEQUENCES : Ignore sequences (for temporary unlock) - GET_LOCK_ON_THD : Store lock in thd->mem_root + + Temporary tables are not locked (as these are single user), except for + TRANSACTIONAL_TMP_TABLES as locking is needed to handle transactions. */ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags) @@ -743,8 +746,8 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags) { TABLE *t= table_ptr[i]; - if (t->s->tmp_table != NON_TRANSACTIONAL_TMP_TABLE && - t->s->tmp_table != INTERNAL_TMP_TABLE && + if ((likely(!t->s->tmp_table) || + (t->s->tmp_table == TRANSACTIONAL_TMP_TABLE)) && (!(flags & GET_LOCK_SKIP_SEQUENCES) || t->s->sequence == 0)) { lock_count+= t->file->lock_count(); @@ -772,13 +775,13 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags) for (i=0 ; i < count ; i++) { - TABLE *table; + TABLE *table= table_ptr[i]; enum thr_lock_type lock_type; THR_LOCK_DATA **locks_start; - table= table_ptr[i]; - if (table->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE || - table->s->tmp_table == INTERNAL_TMP_TABLE || - ((flags & GET_LOCK_SKIP_SEQUENCES) && table->s->sequence)) + + if (!((likely(!table->s->tmp_table) || + (table->s->tmp_table == TRANSACTIONAL_TMP_TABLE)) && + (!(flags & GET_LOCK_SKIP_SEQUENCES) || table->s->sequence == 0))) continue; lock_type= table->reginfo.lock_type; DBUG_ASSERT(lock_type != TL_WRITE_DEFAULT && lock_type != TL_READ_DEFAULT); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 2e0a44e4b8a..e6b5461e5af 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -8837,6 +8837,16 @@ bool get_schema_tables_result(JOIN *join, if (table_list->schema_table->fill_table == 0) continue; + /* + Do not fill in tables thare are marked as JT_CONST as these will never + be read and they also don't have a tab->read_record.table set! + This can happen with queries like + SELECT * FROM t1 LEFT JOIN (t1 AS t1b JOIN INFORMATION_SCHEMA.ROUTINES) + ON (t1b.a IS NULL); + */ + if (tab->type == JT_CONST) + continue; + /* skip I_S optimizations specific to get_all_tables */ if (lex->describe && (table_list->schema_table->fill_table != get_all_tables)) |