Skip to content

Commit 7d2e283

Browse files
committed
Fix for MDEV-14831
MDEV-14831 CREATE OR REPLACE SEQUENCE under LOCK TABLE corrupts the sequence, causes ER_KEY_NOT_FOUND The problem was that sequence_insert didn't properly handle the case where there where there was a LOCK TABLE while creating the sequence. Fixed by opening the sequence table, for inserting the first record, in a new environment without any other open tables. Found also a bug in Locked_tables_list::reopen_tables() where the lock structure for the new tables was allocated in THD::mem_root, which causes crashes. This could cause problems with other create tables done under LOCK TABLES.
1 parent 19bb7fd commit 7d2e283

File tree

6 files changed

+104
-29
lines changed

6 files changed

+104
-29
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
drop table if exists s1, t1, t2;
2+
CREATE SEQUENCE s1;
3+
create table t1 (a int);
4+
create table t2 (a int);
5+
LOCK TABLE s1 WRITE, t1 write;
6+
create or replace sequence s1;
7+
select * from s1;
8+
next_not_cached_value minimum_value maximum_value start_value increment cache_size cycle_option cycle_count
9+
1 1 9223372036854775806 1 1 1000 0 0
10+
select * from t1;
11+
a
12+
select * from t2;
13+
ERROR HY000: Table 't2' was not locked with LOCK TABLES
14+
unlock tables;
15+
select * from t1;
16+
a
17+
select * from t2;
18+
a
19+
drop tables s1, t1, t2;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--source include/have_sequence.inc
2+
--source include/have_innodb.inc
3+
4+
--disable_warnings
5+
drop table if exists s1, t1, t2;
6+
--enable_warnings
7+
8+
#
9+
# MDEV-14831 CREATE OR REPLACE SEQUENCE under LOCK TABLE corrupts the
10+
# sequence, causes ER_KEY_NOT_FOUND
11+
#
12+
CREATE SEQUENCE s1;
13+
create table t1 (a int);
14+
create table t2 (a int);
15+
LOCK TABLE s1 WRITE, t1 write;
16+
create or replace sequence s1;
17+
select * from s1;
18+
select * from t1;
19+
--error ER_TABLE_NOT_LOCKED
20+
select * from t2;
21+
unlock tables;
22+
select * from t1;
23+
select * from t2;
24+
drop tables s1, t1, t2;

sql/lock.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,7 @@ static int unlock_external(THD *thd, TABLE **table,uint count)
747747
- GET_LOCK_UNLOCK : If we should send TL_IGNORE to store lock
748748
- GET_LOCK_STORE_LOCKS : Store lock info in TABLE
749749
- GET_LOCK_SKIP_SEQUENCES : Ignore sequences (for temporary unlock)
750+
- GET_LOCK_ON_THD : Store lock in thd->mem_root
750751
*/
751752

752753
MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags)

sql/sql_base.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2443,7 +2443,7 @@ Locked_tables_list::reopen_tables(THD *thd)
24432443
break something else.
24442444
*/
24452445
lock= mysql_lock_tables(thd, m_reopen_array, reopen_count,
2446-
MYSQL_OPEN_REOPEN);
2446+
MYSQL_OPEN_REOPEN | MYSQL_LOCK_USE_MALLOC);
24472447
thd->in_lock_tables= 0;
24482448
if (lock == NULL || (merged_lock=
24492449
mysql_lock_merge(thd->lock, lock)) == NULL)

sql/sql_sequence.cc

Lines changed: 58 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -275,69 +275,99 @@ bool prepare_sequence_fields(THD *thd, List<Create_field> *fields)
275275
There is also a MDL lock on the table.
276276
*/
277277

278-
bool sequence_insert(THD *thd, LEX *lex, TABLE_LIST *table_list)
278+
bool sequence_insert(THD *thd, LEX *lex, TABLE_LIST *org_table_list)
279279
{
280280
int error;
281281
TABLE *table;
282-
TABLE_LIST::enum_open_strategy save_open_strategy;
283282
Reprepare_observer *save_reprepare_observer;
284283
sequence_definition *seq= lex->create_info.seq_create_info;
285-
bool temporary_table= table_list->table != 0;
286-
TABLE_LIST *org_next_global= table_list->next_global;
284+
bool temporary_table= org_table_list->table != 0;
285+
Open_tables_backup open_tables_backup;
286+
Query_tables_list query_tables_list_backup;
287+
TABLE_LIST table_list; // For sequence table
287288
DBUG_ENTER("sequence_insert");
288289

290+
/*
291+
seq is 0 if sequence was created with CREATE TABLE instead of
292+
CREATE SEQUENCE
293+
*/
294+
if (!seq)
295+
{
296+
if (!(seq= new (thd->mem_root) sequence_definition))
297+
DBUG_RETURN(TRUE);
298+
}
299+
289300
/* If not temporary table */
290301
if (!temporary_table)
291302
{
292-
/* Table was locked as part of create table. Free it but keep MDL locks */
293-
close_thread_tables(thd);
294-
table_list->next_global= 0; // Close LIKE TABLE
295-
table_list->lock_type= TL_WRITE_DEFAULT;
296-
table_list->updating= 1;
303+
/*
304+
The following code works like open_system_tables_for_read() and
305+
close_system_tables()
306+
The idea is:
307+
- Copy the table_list object for the sequence that was created
308+
- Backup the current state of open tables and create a new
309+
environment for open tables without any tables opened
310+
- open the newly sequence table for write
311+
This is safe as the sequence table has a mdl lock thanks to the
312+
create sequence statement that is calling this function
313+
*/
314+
315+
table_list.init_one_table(&org_table_list->db,
316+
&org_table_list->table_name,
317+
NULL, TL_WRITE_DEFAULT);
318+
table_list.updating= 1;
319+
table_list.open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
320+
table_list.open_type= OT_BASE_ONLY;
321+
322+
DBUG_ASSERT(!thd->locked_tables_mode ||
323+
(thd->variables.option_bits & OPTION_TABLE_LOCK));
324+
lex->reset_n_backup_query_tables_list(&query_tables_list_backup);
325+
thd->reset_n_backup_open_tables_state(&open_tables_backup);
326+
297327
/*
298328
The FOR CREATE flag is needed to ensure that ha_open() doesn't try to
299329
read the not yet existing row in the sequence table
300330
*/
301331
thd->open_options|= HA_OPEN_FOR_CREATE;
302-
save_open_strategy= table_list->open_strategy;
303332
/*
304333
We have to reset the reprepare observer to be able to open the
305334
table under prepared statements.
306335
*/
307336
save_reprepare_observer= thd->m_reprepare_observer;
308337
thd->m_reprepare_observer= 0;
309-
table_list->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
310-
table_list->open_type= OT_BASE_ONLY;
311-
error= open_and_lock_tables(thd, table_list, FALSE,
338+
lex->sql_command= SQLCOM_CREATE_SEQUENCE;
339+
error= open_and_lock_tables(thd, &table_list, FALSE,
312340
MYSQL_LOCK_IGNORE_TIMEOUT |
313341
MYSQL_OPEN_HAS_MDL_LOCK);
314-
table_list->open_strategy= save_open_strategy;
315342
thd->open_options&= ~HA_OPEN_FOR_CREATE;
316343
thd->m_reprepare_observer= save_reprepare_observer;
317-
table_list->next_global= org_next_global;
318344
if (error)
319-
DBUG_RETURN(TRUE); /* purify inspected */
320-
}
321-
322-
table= table_list->table;
323-
324-
/*
325-
seq is 0 if sequence was created with CREATE TABLE instead of
326-
CREATE SEQUENCE
327-
*/
328-
if (!seq)
329-
{
330-
if (!(seq= new (thd->mem_root) sequence_definition))
331-
DBUG_RETURN(TRUE); // EOM
345+
{
346+
lex->restore_backup_query_tables_list(&query_tables_list_backup);
347+
thd->restore_backup_open_tables_state(&open_tables_backup);
348+
DBUG_RETURN(error);
349+
}
350+
table= table_list.table;
332351
}
352+
else
353+
table= org_table_list->table;
333354

334355
seq->reserved_until= seq->start;
335356
error= seq->write_initial_sequence(table);
336357

337358
trans_commit_stmt(thd);
338359
trans_commit_implicit(thd);
360+
339361
if (!temporary_table)
362+
{
340363
close_thread_tables(thd);
364+
lex->restore_backup_query_tables_list(&query_tables_list_backup);
365+
thd->restore_backup_open_tables_state(&open_tables_backup);
366+
367+
/* OPTION_TABLE_LOCK was reset in trans_commit_implicit */
368+
if (thd->locked_tables_mode)
369+
thd->variables.option_bits|= OPTION_TABLE_LOCK;
370+
}
341371
DBUG_RETURN(error);
342372
}
343373

sql/sql_table.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5221,6 +5221,7 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
52215221
if (thd->locked_tables_mode && pos_in_locked_tables &&
52225222
create_info->or_replace())
52235223
{
5224+
DBUG_ASSERT(thd->variables.option_bits & OPTION_TABLE_LOCK);
52245225
/*
52255226
Add back the deleted table and re-created table as a locked table
52265227
This should always work as we have a meta lock on the table.

0 commit comments

Comments
 (0)