MDEV-23639 Auto-create does not work under LOCK TABLES or inside triggers

Don't do tdc_remove_table() for OT_ADD_HISTORY_PARTITION because it is
not possible in locked tables mode.

LTM_LOCK_TABLES mode (and LTM_PRELOCKED_UNDER_LOCK_TABLES) works out
of the box as fast_alter_partition_table() can reopen tables via
locked_tables_list.

In LTM_PRELOCKED we reopen and relock table manually.
This commit is contained in:
Aleksey Midenkov 2021-05-19 17:51:34 +03:00
parent 404428733b
commit 41a170c60a
7 changed files with 469 additions and 126 deletions

View File

@ -1506,16 +1506,14 @@ affected rows: 1
info: Rows matched: 1 Changed: 1 Inserted: 1 Warnings: 0
update t1 set x= x + 1;
affected rows: 1
info: Rows matched: 1 Changed: 1 Inserted: 1 Warnings: 1
Warnings:
Warning 4114 Versioned table `test`.`t1`: last HISTORY partition (`p1`) is out of LIMIT, need more HISTORY partitions
info: Rows matched: 1 Changed: 1 Inserted: 1 Warnings: 0
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`x` int(11) DEFAULT NULL
) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING
PARTITION BY SYSTEM_TIME LIMIT 1 AUTO
PARTITIONS 3
PARTITIONS 4
affected rows: 1
unlock tables;
affected rows: 0
@ -1525,7 +1523,7 @@ t1 CREATE TABLE `t1` (
`x` int(11) DEFAULT NULL
) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING
PARTITION BY SYSTEM_TIME LIMIT 1 AUTO
PARTITIONS 3
PARTITIONS 4
affected rows: 1
# Overflow prevention under LOCK TABLES
create or replace table t1 (x int)
@ -2115,3 +2113,167 @@ t CREATE TABLE `t` (
PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 02:00:00' AUTO
PARTITIONS 6
drop table t;
#
# MDEV-23639 Auto-create does not work under LOCK TABLES or inside triggers
#
set timestamp= unix_timestamp('2000-01-01 00:00:00');
create or replace table t1 (x int) with system versioning
partition by system_time interval 1 hour auto
partitions 3;
create table t2 (x int);
create table t3 (x int);
insert into t3 values (3);
create trigger tr after insert on t2 for each row update t1 set x= x + 11;
create or replace procedure sp() update t1 set x= x + 5;
create or replace procedure sp2() insert into t2 values (5);
prepare ps from 'update t1 set x= x + 6';
prepare ps2 from 'insert into t2 values (6)';
insert into t1 values (1);
set timestamp= unix_timestamp('2000-01-01 02:00:00');
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`x` int(11) DEFAULT NULL
) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING
PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO
PARTITIONS 3
insert into t2 values (2);
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`x` int(11) DEFAULT NULL
) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING
PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO
PARTITIONS 4
set timestamp= unix_timestamp('2000-01-01 03:00:00');
call sp;
call sp;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`x` int(11) DEFAULT NULL
) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING
PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO
PARTITIONS 5
set timestamp= unix_timestamp('2000-01-01 04:00:00');
call sp2;
call sp2;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`x` int(11) DEFAULT NULL
) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING
PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO
PARTITIONS 6
set timestamp= unix_timestamp('2000-01-01 05:00:00');
execute ps;
execute ps;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`x` int(11) DEFAULT NULL
) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING
PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO
PARTITIONS 7
set timestamp= unix_timestamp('2000-01-01 06:00:00');
execute ps2;
execute ps2;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`x` int(11) DEFAULT NULL
) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING
PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO
PARTITIONS 8
set timestamp= unix_timestamp('2000-01-01 08:00:00');
lock tables t1 write, t2 write;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`x` int(11) DEFAULT NULL
) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING
PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO
PARTITIONS 10
set timestamp= unix_timestamp('2000-01-01 09:00:00');
update t1 set x= x + 1;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`x` int(11) DEFAULT NULL
) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING
PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO
PARTITIONS 11
set timestamp= unix_timestamp('2000-01-01 10:00:00');
update t1 set x= x + 2;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`x` int(11) DEFAULT NULL
) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING
PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO
PARTITIONS 12
update t2 set x= x + 1;
set timestamp= unix_timestamp('2000-01-01 11:00:00');
insert into t2 values (4);
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`x` int(11) DEFAULT NULL
) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING
PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO
PARTITIONS 13
update t3 set x= x + 1;
ERROR HY000: Table 't3' was not locked with LOCK TABLES
set timestamp= unix_timestamp('2000-01-01 12:00:00');
call sp;
call sp;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`x` int(11) DEFAULT NULL
) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING
PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO
PARTITIONS 14
set timestamp= unix_timestamp('2000-01-01 13:00:00');
call sp2;
call sp2;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`x` int(11) DEFAULT NULL
) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING
PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO
PARTITIONS 15
set timestamp= unix_timestamp('2000-01-01 14:00:00');
execute ps;
execute ps;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`x` int(11) DEFAULT NULL
) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING
PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO
PARTITIONS 16
set timestamp= unix_timestamp('2000-01-01 15:00:00');
execute ps2;
execute ps2;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`x` int(11) DEFAULT NULL
) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING
PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO
PARTITIONS 17
unlock tables;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`x` int(11) DEFAULT NULL
) ENGINE=DEFAULT_ENGINE DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING
PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO
PARTITIONS 17
drop tables t1, t2, t3;
drop procedure sp;
drop procedure sp2;
drop prepare ps;
drop prepare ps2;

View File

@ -1552,4 +1552,97 @@ show create table t;
# Cleanup
drop table t;
--echo #
--echo # MDEV-23639 Auto-create does not work under LOCK TABLES or inside triggers
--echo #
set timestamp= unix_timestamp('2000-01-01 00:00:00');
create or replace table t1 (x int) with system versioning
partition by system_time interval 1 hour auto
partitions 3;
create table t2 (x int);
create table t3 (x int);
insert into t3 values (3);
create trigger tr after insert on t2 for each row update t1 set x= x + 11;
create or replace procedure sp() update t1 set x= x + 5;
create or replace procedure sp2() insert into t2 values (5);
prepare ps from 'update t1 set x= x + 6';
prepare ps2 from 'insert into t2 values (6)';
insert into t1 values (1);
set timestamp= unix_timestamp('2000-01-01 02:00:00');
--replace_result $default_engine DEFAULT_ENGINE
show create table t1;
insert into t2 values (2);
--replace_result $default_engine DEFAULT_ENGINE
show create table t1;
set timestamp= unix_timestamp('2000-01-01 03:00:00');
call sp; call sp;
--replace_result $default_engine DEFAULT_ENGINE
show create table t1;
set timestamp= unix_timestamp('2000-01-01 04:00:00');
call sp2; call sp2;
--replace_result $default_engine DEFAULT_ENGINE
show create table t1;
set timestamp= unix_timestamp('2000-01-01 05:00:00');
execute ps; execute ps;
--replace_result $default_engine DEFAULT_ENGINE
show create table t1;
set timestamp= unix_timestamp('2000-01-01 06:00:00');
execute ps2; execute ps2;
--replace_result $default_engine DEFAULT_ENGINE
show create table t1;
set timestamp= unix_timestamp('2000-01-01 08:00:00');
lock tables t1 write, t2 write;
--replace_result $default_engine DEFAULT_ENGINE
show create table t1;
set timestamp= unix_timestamp('2000-01-01 09:00:00');
update t1 set x= x + 1;
--replace_result $default_engine DEFAULT_ENGINE
show create table t1;
set timestamp= unix_timestamp('2000-01-01 10:00:00');
update t1 set x= x + 2;
--replace_result $default_engine DEFAULT_ENGINE
show create table t1;
update t2 set x= x + 1;
set timestamp= unix_timestamp('2000-01-01 11:00:00');
insert into t2 values (4);
--replace_result $default_engine DEFAULT_ENGINE
show create table t1;
--error ER_TABLE_NOT_LOCKED
update t3 set x= x + 1;
set timestamp= unix_timestamp('2000-01-01 12:00:00');
call sp; call sp;
--replace_result $default_engine DEFAULT_ENGINE
show create table t1;
set timestamp= unix_timestamp('2000-01-01 13:00:00');
call sp2; call sp2;
--replace_result $default_engine DEFAULT_ENGINE
show create table t1;
set timestamp= unix_timestamp('2000-01-01 14:00:00');
execute ps; execute ps;
--replace_result $default_engine DEFAULT_ENGINE
show create table t1;
set timestamp= unix_timestamp('2000-01-01 15:00:00');
execute ps2; execute ps2;
--replace_result $default_engine DEFAULT_ENGINE
show create table t1;
unlock tables;
--replace_result $default_engine DEFAULT_ENGINE
show create table t1;
# Cleanup
drop tables t1, t2, t3;
drop procedure sp;
drop procedure sp2;
drop prepare ps;
drop prepare ps2;
--source suite/versioning/common_finish.inc

View File

@ -654,7 +654,7 @@ bool mysql_lock_abort_for_thread(THD *thd, TABLE *table)
a and b are freed with my_free()
*/
MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b)
MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a, MYSQL_LOCK *b, THD *thd)
{
MYSQL_LOCK *sql_lock;
TABLE **table, **end_table;
@ -662,16 +662,28 @@ MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b)
DBUG_PRINT("enter", ("a->lock_count: %u b->lock_count: %u",
a->lock_count, b->lock_count));
if (!(sql_lock= (MYSQL_LOCK*)
my_malloc(key_memory_MYSQL_LOCK, sizeof(*sql_lock) +
sizeof(THR_LOCK_DATA*)*((a->lock_count+b->lock_count)*2) +
sizeof(TABLE*)*(a->table_count+b->table_count),MYF(MY_WME))))
DBUG_RETURN(0); // Fatal error
const size_t lock_size= sizeof(*sql_lock) +
sizeof(THR_LOCK_DATA *) * ((a->lock_count + b->lock_count) * 2) +
sizeof(TABLE *) * (a->table_count + b->table_count);
if (thd)
{
sql_lock= (MYSQL_LOCK *) thd->alloc(lock_size);
if (!sql_lock)
DBUG_RETURN(0);
sql_lock->flags= GET_LOCK_ON_THD;
}
else
{
sql_lock= (MYSQL_LOCK *)
my_malloc(key_memory_MYSQL_LOCK, lock_size, MYF(MY_WME));
if (!sql_lock)
DBUG_RETURN(0);
sql_lock->flags= 0;
}
sql_lock->lock_count=a->lock_count+b->lock_count;
sql_lock->table_count=a->table_count+b->table_count;
sql_lock->locks=(THR_LOCK_DATA**) (sql_lock+1);
sql_lock->table=(TABLE**) (sql_lock->locks+sql_lock->lock_count*2);
sql_lock->flags= 0;
memcpy(sql_lock->locks,a->locks,a->lock_count*sizeof(*a->locks));
memcpy(sql_lock->locks+a->lock_count,b->locks,
b->lock_count*sizeof(*b->locks));
@ -705,8 +717,10 @@ MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b)
a->lock_count, b->lock_count);
/* Delete old, not needed locks */
my_free(a);
my_free(b);
if (!(a->flags & GET_LOCK_ON_THD))
my_free(a);
if (!(b->flags & GET_LOCK_ON_THD))
my_free(b);
DBUG_RETURN(sql_lock);
}

View File

@ -34,7 +34,7 @@ int mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock);
int mysql_unlock_some_tables(THD *thd, TABLE **table,uint count, uint flag);
int mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table);
bool mysql_lock_abort_for_thread(THD *thd, TABLE *table);
MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b);
MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a, MYSQL_LOCK *b, THD *thd= NULL);
/* Lock based on name */
bool lock_schema_name(THD *thd, const char *db);
/* Lock based on stored routine name */

View File

@ -910,15 +910,6 @@ bool partition_info::vers_set_hist_part(THD *thd, uint *create_count)
}
}
/*
When hist_part is almost full LOCK TABLES may overflow the partition as we
can't add new partitions under LOCK TABLES. Reserve one more for this case.
*/
if (auto_hist && *create_count == 0 &&
thd->lex->sql_command == SQLCOM_LOCK_TABLES &&
vers_info->hist_part->id + 1 == vers_info->now_part->id)
++*create_count;
return false;
}

View File

@ -63,6 +63,9 @@
#include "wsrep_trans_observer.h"
#endif /* WITH_WSREP */
static bool
open_tables_check_upgradable_mdl(THD *thd, TABLE_LIST *tables_start,
TABLE_LIST *tables_end, uint flags);
bool
No_such_table_error_handler::handle_condition(THD *,
@ -1622,26 +1625,48 @@ bool is_locked_view(THD *thd, TABLE_LIST *t)
}
bool TABLE::vers_need_hist_part(const THD *thd, const TABLE_LIST *table_list) const
{
#ifdef WITH_PARTITION_STORAGE_ENGINE
if (part_info && part_info->part_type == VERSIONING_PARTITION &&
!table_list->vers_conditions.delete_history &&
!thd->stmt_arena->is_stmt_prepare() &&
table_list->lock_type >= TL_WRITE_ALLOW_WRITE &&
table_list->mdl_request.type >= MDL_SHARED_WRITE &&
table_list->mdl_request.type < MDL_EXCLUSIVE)
/**
Switch part_info->hist_part and request partition creation if needed.
@retval true Error or partition creation was requested.
@retval false No error
*/
bool TABLE::vers_switch_partition(THD *thd, TABLE_LIST *table_list,
Open_table_context *ot_ctx)
{
if (!part_info || part_info->part_type != VERSIONING_PARTITION ||
table_list->vers_conditions.delete_history ||
thd->stmt_arena->is_stmt_prepare() ||
table_list->lock_type < TL_WRITE_ALLOW_WRITE ||
table_list->mdl_request.type < MDL_SHARED_WRITE ||
table_list->mdl_request.type == MDL_EXCLUSIVE)
{
return false;
}
/*
NOTE: we need this condition of prelocking_placeholder because we cannot do
auto-create after the transaction is started. Auto-create does
close_tables_for_reopen() and that is not possible under started transaction.
Also the transaction may not be cancelled at that moment: f.ex. trigger
after insert is run when some data is already written.
We must do auto-creation for PRELOCK_ROUTINE tables at the initial
open_tables() no matter what initiating sql_command is.
*/
if (table_list->prelocking_placeholder != TABLE_LIST::PRELOCK_ROUTINE)
{
switch (thd->lex->sql_command)
{
case SQLCOM_INSERT:
if (thd->lex->duplicates != DUP_UPDATE)
break;
return true;
return false;
break;
case SQLCOM_LOAD:
if (thd->lex->duplicates != DUP_REPLACE)
break;
return true;
return false;
break;
case SQLCOM_LOCK_TABLES:
case SQLCOM_DELETE:
case SQLCOM_UPDATE:
@ -1649,35 +1674,87 @@ bool TABLE::vers_need_hist_part(const THD *thd, const TABLE_LIST *table_list) co
case SQLCOM_REPLACE_SELECT:
case SQLCOM_DELETE_MULTI:
case SQLCOM_UPDATE_MULTI:
return true;
default:;
break;
}
/*
TODO: make row events set thd->lex->sql_command appropriately.
default:
/*
TODO: make row events set thd->lex->sql_command appropriately.
Sergei Golubchik: f.ex. currently row events increment
thd->status_var.com_stat[] each event for its own SQLCOM_xxx, it won't be
needed if they'll just set thd->lex->sql_command.
*/
if (thd->rgi_slave && thd->rgi_slave->current_event &&
thd->lex->sql_command == SQLCOM_END)
{
switch (thd->rgi_slave->current_event->get_type_code())
{
case UPDATE_ROWS_EVENT:
case UPDATE_ROWS_EVENT_V1:
case DELETE_ROWS_EVENT:
case DELETE_ROWS_EVENT_V1:
return true;
default:;
Sergei Golubchik: f.ex. currently row events increment
thd->status_var.com_stat[] each event for its own SQLCOM_xxx, it won't be
needed if they'll just set thd->lex->sql_command.
*/
if (thd->rgi_slave && thd->rgi_slave->current_event &&
thd->lex->sql_command == SQLCOM_END)
{
switch (thd->rgi_slave->current_event->get_type_code())
{
case UPDATE_ROWS_EVENT:
case UPDATE_ROWS_EVENT_V1:
case DELETE_ROWS_EVENT:
case DELETE_ROWS_EVENT_V1:
break;
default:;
return false;
}
}
break;
}
}
}
#endif
TABLE *table= this;
/*
NOTE: The semantics of vers_set_hist_part() is double: even when we
don't need auto-create, we need to update part_info->hist_part.
*/
uint *create_count= table_list->vers_skip_create ?
NULL : &ot_ctx->vers_create_count;
table_list->vers_skip_create= true;
if (table->part_info->vers_set_hist_part(thd, create_count))
{
MYSQL_UNBIND_TABLE(table->file);
tc_release_table(table);
return true;
}
if (ot_ctx->vers_create_count)
{
Open_table_context::enum_open_table_action action;
TABLE_LIST *table_arg;
mysql_mutex_lock(&table->s->LOCK_share);
if (!table->s->vers_skip_auto_create)
{
table->s->vers_skip_auto_create= true;
action= Open_table_context::OT_ADD_HISTORY_PARTITION;
table_arg= table_list;
}
else
{
/*
NOTE: this may repeat multiple times until creating thread acquires
MDL_EXCLUSIVE. Since auto-creation is rare operation this is acceptable.
We could suspend this thread on cond-var but we must first exit
MDL_SHARED_WRITE first and we cannot store cond-var into TABLE_SHARE
because it is already released and there is no guarantee that it will
be same instance if we acquire it again.
*/
table_list->vers_skip_create= false;
ot_ctx->vers_create_count= 0;
action= Open_table_context::OT_REOPEN_TABLES;
table_arg= NULL;
}
mysql_mutex_unlock(&table->s->LOCK_share);
if (!thd->locked_tables_mode)
{
MYSQL_UNBIND_TABLE(table->file);
tc_release_table(table);
}
ot_ctx->request_backoff_action(action, table_arg);
return true;
}
return false;
}
#endif /* WITH_PARTITION_STORAGE_ENGINE */
/**
@ -1832,14 +1909,8 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx)
DBUG_PRINT("info",("Using locked table"));
#ifdef WITH_PARTITION_STORAGE_ENGINE
part_names_error= set_partitions_as_used(table_list, table);
if (table->vers_need_hist_part(thd, table_list))
{
/*
New partitions are not auto-created under LOCK TABLES (TODO: fix it)
but rotation can still happen.
*/
(void) table->part_info->vers_set_hist_part(thd, NULL);
}
if (table->vers_switch_partition(thd, table_list, ot_ctx))
DBUG_RETURN(true);
#endif
goto reset;
}
@ -2095,54 +2166,10 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx)
tc_add_table(thd, table);
}
if (table->vers_need_hist_part(thd, table_list))
{
/*
NOTE: The semantics of vers_set_hist_part() is double: even when we
don't need auto-create, we need to update part_info->hist_part.
*/
uint *create_count= table_list->vers_skip_create ?
NULL : &ot_ctx->vers_create_count;
table_list->vers_skip_create= true;
if (table->part_info->vers_set_hist_part(thd, create_count))
{
MYSQL_UNBIND_TABLE(table->file);
tc_release_table(table);
DBUG_RETURN(TRUE);
}
if (ot_ctx->vers_create_count)
{
Open_table_context::enum_open_table_action action;
TABLE_LIST *table_arg;
mysql_mutex_lock(&table->s->LOCK_share);
if (!table->s->vers_skip_auto_create)
{
table->s->vers_skip_auto_create= true;
action= Open_table_context::OT_ADD_HISTORY_PARTITION;
table_arg= table_list;
}
else
{
/*
NOTE: this may repeat multiple times until creating thread acquires
MDL_EXCLUSIVE. Since auto-creation is rare operation this is acceptable.
We could suspend this thread on cond-var but we must first exit
MDL_SHARED_WRITE first and we cannot store cond-var into TABLE_SHARE
because it is already released and there is no guarantee that it will
be same instance if we acquire it again.
*/
table_list->vers_skip_create= false;
ot_ctx->vers_create_count= 0;
action= Open_table_context::OT_REOPEN_TABLES;
table_arg= NULL;
}
mysql_mutex_unlock(&table->s->LOCK_share);
MYSQL_UNBIND_TABLE(table->file);
tc_release_table(table);
ot_ctx->request_backoff_action(action, table_arg);
DBUG_RETURN(TRUE);
}
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
if (table->vers_switch_partition(thd, table_list, ot_ctx))
DBUG_RETURN(true);
#endif /* WITH_PARTITION_STORAGE_ENGINE */
if (!(flags & MYSQL_OPEN_HAS_MDL_LOCK) &&
table->s->table_category < TABLE_CATEGORY_INFORMATION)
@ -3291,8 +3318,14 @@ Open_table_context::recover_from_failed_open()
case OT_DISCOVER:
case OT_REPAIR:
case OT_ADD_HISTORY_PARTITION:
result= lock_table_names(m_thd, m_thd->lex->create_info, m_failed_table,
NULL, get_timeout(), 0);
if (!m_thd->locked_tables_mode)
result= lock_table_names(m_thd, m_thd->lex->create_info, m_failed_table,
NULL, get_timeout(), 0);
else
{
DBUG_ASSERT(!result);
DBUG_ASSERT(m_action == OT_ADD_HISTORY_PARTITION);
}
/*
We are now under MDL_EXCLUSIVE mode. Other threads have no table share
acquired: they are blocked either at open_table_get_mdl_lock() in
@ -3320,8 +3353,13 @@ Open_table_context::recover_from_failed_open()
break;
}
tdc_remove_table(m_thd, m_failed_table->db.str,
m_failed_table->table_name.str);
/*
We don't need to remove share under OT_ADD_HISTORY_PARTITION.
Moreover fast_alter_partition_table() works with TABLE instance.
*/
if (m_action != OT_ADD_HISTORY_PARTITION)
tdc_remove_table(m_thd, m_failed_table->db.str,
m_failed_table->table_name.str);
switch (m_action)
{
@ -3361,12 +3399,56 @@ Open_table_context::recover_from_failed_open()
}
DBUG_ASSERT(vers_create_count);
TABLE_LIST *tl= m_failed_table;
result= vers_create_partitions(m_thd, tl, vers_create_count);
result= vers_create_partitions(m_thd, m_failed_table, vers_create_count);
vers_create_count= 0;
if (!m_thd->transaction->stmt.is_empty())
trans_commit_stmt(m_thd);
close_tables_for_reopen(m_thd, &tl, start_of_statement_svp());
vers_create_count= 0;
DBUG_ASSERT(!result ||
!m_thd->locked_tables_mode ||
m_thd->lock->lock_count);
if (result)
break;
if (!m_thd->locked_tables_mode)
{
/*
alter_partition_lock_handling() does mysql_lock_remove() but
does not clear thd->lock completely.
*/
DBUG_ASSERT(m_thd->lock->lock_count == 0);
if (!(m_thd->lock->flags & GET_LOCK_ON_THD))
my_free(m_thd->lock);
m_thd->lock= NULL;
}
else if (m_thd->locked_tables_mode == LTM_PRELOCKED)
{
MYSQL_LOCK *lock;
MYSQL_LOCK *merged_lock;
/*
In LTM_LOCK_TABLES table was reopened via locked_tables_list,
but not in prelocked environment where we have to reopen
the table manually.
*/
Open_table_context ot_ctx(m_thd, MYSQL_OPEN_REOPEN);
if (open_table(m_thd, m_failed_table, &ot_ctx))
{
result= true;
break;
}
TABLE *table= m_failed_table->table;
table->reginfo.lock_type= m_thd->update_lock_default;
m_thd->in_lock_tables= 1;
lock= mysql_lock_tables(m_thd, &table, 1,
MYSQL_OPEN_REOPEN | MYSQL_LOCK_USE_MALLOC);
m_thd->in_lock_tables= 0;
if (lock == NULL ||
!(merged_lock= mysql_lock_merge(m_thd->lock, lock, m_thd)))
{
result= true;
break;
}
m_thd->lock= merged_lock;
}
break;
}
case OT_BACKOFF_AND_RETRY:
@ -5309,9 +5391,6 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
if (table_list->table)
DBUG_RETURN(table_list->table);
/* should not be used in a prelocked_mode context, see NOTE above */
DBUG_ASSERT(thd->locked_tables_mode < LTM_PRELOCKED);
THD_STAGE_INFO(thd, stage_opening_tables);
thd->current_tablenr= 0;
/* open_ltable can be used only for BASIC TABLEs */

View File

@ -64,6 +64,7 @@ class derived_handler;
class Pushdown_derived;
struct Name_resolution_context;
class Table_function_json_table;
class Open_table_context;
/*
Used to identify NESTED_JOIN structures within a join (applicable only to
@ -1768,7 +1769,10 @@ struct TABLE
ulonglong vers_start_id() const;
ulonglong vers_end_id() const;
bool vers_need_hist_part(const THD *thd, const TABLE_LIST *table_list) const;
#ifdef WITH_PARTITION_STORAGE_ENGINE
bool vers_switch_partition(THD *thd, TABLE_LIST *table_list,
Open_table_context *ot_ctx);
#endif
int update_generated_fields();
int period_make_insert(Item *src, Field *dst);