mirror of
https://github.com/MariaDB/server.git
synced 2025-01-29 10:14:19 +01:00
WL#5000 FLUSH TABLES|TABLE table_list WITH READ LOCK.
Extend and implement the grammar that allows to FLUSH WITH READ LOCK a list of tables, rather than all of them. Incompatible grammar change: Previously one could perform FLUSH TABLES, HOSTS, PRIVILEGES in a single statement. After this change, FLUSH TABLES must always be alone on the list. Judging by the test suite, however, the old extended syntax was never or very rarely used. The new statement requires RELOAD ACL global privilege and LOCK_TABLES_ACL | SELECT_ACL on individual tables. In other words, it's an atomic combination of LOCK TALBES <list> READ and FLUSH TABLES <list>, and requires respective privileges. For additional information about the semantics, please see WL#5000 and the comment for flush_tables_with_read_lock() function in sql_parse.cc
This commit is contained in:
parent
2e9d7d78f9
commit
e5f5956014
4 changed files with 333 additions and 10 deletions
|
@ -111,3 +111,99 @@ commit;
|
|||
# which was already released by commit.
|
||||
unlock tables;
|
||||
drop tables t1, t2;
|
||||
#
|
||||
# Tests for WL#5000 FLUSH TABLES|TABLE table_list WITH READ LOCK
|
||||
#
|
||||
# I. Check the incompatible changes in the grammar.
|
||||
#
|
||||
flush tables with read lock, hosts;
|
||||
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ' hosts' at line 1
|
||||
flush privileges, tables;
|
||||
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'tables' at line 1
|
||||
flush privileges, tables with read lock;
|
||||
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'tables with read lock' at line 1
|
||||
flush privileges, tables;
|
||||
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'tables' at line 1
|
||||
flush tables with read lock, tables;
|
||||
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ' tables' at line 1
|
||||
show tables;
|
||||
Tables_in_test
|
||||
#
|
||||
# II. Check the allowed syntax.
|
||||
#
|
||||
drop table if exists t1, t2, t3;
|
||||
create table t1 (a int);
|
||||
create table t2 (a int);
|
||||
create table t3 (a int);
|
||||
lock table t1 read, t2 read;
|
||||
flush tables with read lock;
|
||||
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
|
||||
unlock tables;
|
||||
flush tables with read lock;
|
||||
flush tables t1, t2 with read lock;
|
||||
flush tables t1, t2 with read lock;
|
||||
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
|
||||
flush tables with read lock;
|
||||
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
|
||||
select * from t1;
|
||||
a
|
||||
select * from t2;
|
||||
a
|
||||
select * from t3;
|
||||
ERROR HY000: Table 't3' was not locked with LOCK TABLES
|
||||
insert into t1 (a) values (1);
|
||||
ERROR HY000: Table 't1' was locked with a READ lock and can't be updated
|
||||
insert into t2 (a) values (1);
|
||||
ERROR HY000: Table 't2' was locked with a READ lock and can't be updated
|
||||
insert into t3 (a) values (1);
|
||||
ERROR HY000: Table 't3' was not locked with LOCK TABLES
|
||||
lock table no_such_table read;
|
||||
ERROR 42S02: Table 'test.no_such_table' doesn't exist
|
||||
#
|
||||
# We implicitly left the locked tables
|
||||
# mode but still have the read lock.
|
||||
#
|
||||
insert into t2 (a) values (1);
|
||||
ERROR HY000: Can't execute the query because you have a conflicting read lock
|
||||
unlock tables;
|
||||
insert into t1 (a) values (1);
|
||||
insert into t2 (a) values (1);
|
||||
flush table t1, t2 with read lock;
|
||||
select * from t1;
|
||||
a
|
||||
1
|
||||
select * from t2;
|
||||
a
|
||||
1
|
||||
select * from t3;
|
||||
ERROR HY000: Table 't3' was not locked with LOCK TABLES
|
||||
insert into t1 (a) values (2);
|
||||
ERROR HY000: Table 't1' was locked with a READ lock and can't be updated
|
||||
insert into t2 (a) values (2);
|
||||
ERROR HY000: Table 't2' was locked with a READ lock and can't be updated
|
||||
insert into t3 (a) values (2);
|
||||
ERROR HY000: Table 't3' was not locked with LOCK TABLES
|
||||
lock table no_such_table read;
|
||||
ERROR 42S02: Table 'test.no_such_table' doesn't exist
|
||||
insert into t3 (a) values (2);
|
||||
#
|
||||
# III. Concurrent tests.
|
||||
#
|
||||
# --> connection default
|
||||
#
|
||||
# Check that flush tables <list> with read lock
|
||||
# does not affect non-locked tables.
|
||||
#
|
||||
flush tables t1 with read lock;
|
||||
# --> connection con1;
|
||||
select * from t1;
|
||||
a
|
||||
1
|
||||
select * from t2;
|
||||
a
|
||||
1
|
||||
insert into t2 (a) values (3);
|
||||
# --> connection default;
|
||||
unlock tables;
|
||||
# --> connection con1
|
||||
drop table t1, t2, t3;
|
||||
|
|
|
@ -224,3 +224,103 @@ commit;
|
|||
--echo # which was already released by commit.
|
||||
unlock tables;
|
||||
drop tables t1, t2;
|
||||
|
||||
|
||||
|
||||
--echo #
|
||||
--echo # Tests for WL#5000 FLUSH TABLES|TABLE table_list WITH READ LOCK
|
||||
--echo #
|
||||
--echo # I. Check the incompatible changes in the grammar.
|
||||
--echo #
|
||||
--error ER_PARSE_ERROR
|
||||
flush tables with read lock, hosts;
|
||||
--error ER_PARSE_ERROR
|
||||
flush privileges, tables;
|
||||
--error ER_PARSE_ERROR
|
||||
flush privileges, tables with read lock;
|
||||
--error ER_PARSE_ERROR
|
||||
flush privileges, tables;
|
||||
--error ER_PARSE_ERROR
|
||||
flush tables with read lock, tables;
|
||||
show tables;
|
||||
--echo #
|
||||
--echo # II. Check the allowed syntax.
|
||||
--echo #
|
||||
--disable_warnings
|
||||
drop table if exists t1, t2, t3;
|
||||
--enable_warnings
|
||||
create table t1 (a int);
|
||||
create table t2 (a int);
|
||||
create table t3 (a int);
|
||||
lock table t1 read, t2 read;
|
||||
--error ER_LOCK_OR_ACTIVE_TRANSACTION
|
||||
flush tables with read lock;
|
||||
unlock tables;
|
||||
flush tables with read lock;
|
||||
flush tables t1, t2 with read lock;
|
||||
--error ER_LOCK_OR_ACTIVE_TRANSACTION
|
||||
flush tables t1, t2 with read lock;
|
||||
--error ER_LOCK_OR_ACTIVE_TRANSACTION
|
||||
flush tables with read lock;
|
||||
select * from t1;
|
||||
select * from t2;
|
||||
--error ER_TABLE_NOT_LOCKED
|
||||
select * from t3;
|
||||
--error ER_TABLE_NOT_LOCKED_FOR_WRITE
|
||||
insert into t1 (a) values (1);
|
||||
--error ER_TABLE_NOT_LOCKED_FOR_WRITE
|
||||
insert into t2 (a) values (1);
|
||||
--error ER_TABLE_NOT_LOCKED
|
||||
insert into t3 (a) values (1);
|
||||
--error ER_NO_SUCH_TABLE
|
||||
lock table no_such_table read;
|
||||
--echo #
|
||||
--echo # We implicitly left the locked tables
|
||||
--echo # mode but still have the read lock.
|
||||
--echo #
|
||||
--error ER_CANT_UPDATE_WITH_READLOCK
|
||||
insert into t2 (a) values (1);
|
||||
unlock tables;
|
||||
insert into t1 (a) values (1);
|
||||
insert into t2 (a) values (1);
|
||||
flush table t1, t2 with read lock;
|
||||
select * from t1;
|
||||
select * from t2;
|
||||
--error ER_TABLE_NOT_LOCKED
|
||||
select * from t3;
|
||||
--error ER_TABLE_NOT_LOCKED_FOR_WRITE
|
||||
insert into t1 (a) values (2);
|
||||
--error ER_TABLE_NOT_LOCKED_FOR_WRITE
|
||||
insert into t2 (a) values (2);
|
||||
--error ER_TABLE_NOT_LOCKED
|
||||
insert into t3 (a) values (2);
|
||||
--error ER_NO_SUCH_TABLE
|
||||
lock table no_such_table read;
|
||||
insert into t3 (a) values (2);
|
||||
|
||||
|
||||
--echo #
|
||||
--echo # III. Concurrent tests.
|
||||
--echo #
|
||||
connect (con1,localhost,root,,);
|
||||
--echo # --> connection default
|
||||
--echo #
|
||||
--echo # Check that flush tables <list> with read lock
|
||||
--echo # does not affect non-locked tables.
|
||||
connection default;
|
||||
--echo #
|
||||
flush tables t1 with read lock;
|
||||
--echo # --> connection con1;
|
||||
connection con1;
|
||||
select * from t1;
|
||||
select * from t2;
|
||||
insert into t2 (a) values (3);
|
||||
--echo # --> connection default;
|
||||
connection default;
|
||||
unlock tables;
|
||||
--echo # --> connection con1
|
||||
connection con1;
|
||||
disconnect con1;
|
||||
--source include/wait_until_disconnected.inc
|
||||
connection default;
|
||||
drop table t1, t2, t3;
|
||||
|
|
116
sql/sql_parse.cc
116
sql/sql_parse.cc
|
@ -1587,6 +1587,113 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
Implementation of FLUSH TABLES <table_list> WITH READ LOCK.
|
||||
|
||||
In brief: take exclusive locks, expel tables from the table
|
||||
cache, reopen the tables, enter the 'LOCKED TABLES' mode,
|
||||
downgrade the locks.
|
||||
|
||||
Required privileges
|
||||
-------------------
|
||||
Since the statement implicitly enters LOCK TABLES mode,
|
||||
it requires LOCK TABLES privilege on every table.
|
||||
But since the rest of FLUSH commands require
|
||||
the global RELOAD_ACL, it also requires RELOAD_ACL.
|
||||
|
||||
Compatibility with the global read lock
|
||||
---------------------------------------
|
||||
We don't wait for the GRL, since neither the
|
||||
5.1 combination that this new statement is intended to
|
||||
replace (LOCK TABLE <list> WRITE; FLUSH TABLES;),
|
||||
nor FLUSH TABLES WITH READ LOCK do.
|
||||
@todo: this is not implemented, Dmitry disagrees.
|
||||
Currently we wait for GRL in another connection,
|
||||
but are compatible with a GRL in our own connection.
|
||||
|
||||
Behaviour under LOCK TABLES
|
||||
---------------------------
|
||||
Bail out: i.e. don't perform an implicit UNLOCK TABLES.
|
||||
This is not consistent with LOCK TABLES statement, but is
|
||||
in line with behaviour of FLUSH TABLES WITH READ LOCK, and we
|
||||
try to not introduce any new statements with implicit
|
||||
semantics.
|
||||
|
||||
Compatibility with parallel updates
|
||||
-----------------------------------
|
||||
As a result, we will wait for all open transactions
|
||||
against the tables to complete. After the lock downgrade,
|
||||
new transactions will be able to read the tables, but not
|
||||
write to them.
|
||||
|
||||
Differences from FLUSH TABLES <list>
|
||||
-------------------------------------
|
||||
- you can't flush WITH READ LOCK a non-existent table
|
||||
- you can't flush WITH READ LOCK under LOCK TABLES
|
||||
- currently incompatible with the GRL (@todo: fix)
|
||||
*/
|
||||
|
||||
static bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables)
|
||||
{
|
||||
Lock_tables_prelocking_strategy lock_tables_prelocking_strategy;
|
||||
TABLE_LIST *table_list;
|
||||
|
||||
/*
|
||||
This is called from SQLCOM_FLUSH, the transaction has
|
||||
been committed implicitly.
|
||||
*/
|
||||
|
||||
/* RELOAD_ACL is checked by the caller. Check table-level privileges. */
|
||||
if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
|
||||
FALSE, UINT_MAX, FALSE))
|
||||
goto error;
|
||||
|
||||
if (thd->locked_tables_mode)
|
||||
{
|
||||
my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
@todo: Since lock_table_names() acquires a global IX
|
||||
lock, this actually waits for a GRL in another connection.
|
||||
We are thus introducing an incompatibility.
|
||||
Do nothing for now, since not taking a global IX violates
|
||||
current internal MDL asserts, fix after discussing with
|
||||
Dmitry.
|
||||
*/
|
||||
if (lock_table_names(thd, all_tables))
|
||||
goto error;
|
||||
|
||||
if (open_and_lock_tables(thd, all_tables, FALSE,
|
||||
MYSQL_OPEN_HAS_MDL_LOCK,
|
||||
&lock_tables_prelocking_strategy) ||
|
||||
thd->locked_tables_list.init_locked_tables(thd))
|
||||
{
|
||||
close_thread_tables(thd);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
Downgrade the exclusive locks.
|
||||
Use MDL_SHARED_NO_WRITE as the intended
|
||||
post effect of this call is identical
|
||||
to LOCK TABLES <...> READ, and we didn't use
|
||||
thd->in_lock_talbes and thd->sql_command= SQLCOM_LOCK_TABLES
|
||||
hacks to enter the LTM.
|
||||
@todo: release the global IX lock here!!!
|
||||
*/
|
||||
for (table_list= all_tables; table_list;
|
||||
table_list= table_list->next_global)
|
||||
table_list->mdl_request.ticket->downgrade_exclusive_lock(MDL_SHARED_NO_WRITE);
|
||||
|
||||
return FALSE;
|
||||
|
||||
error:
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Read query from packet and store in thd->query.
|
||||
Used in COM_QUERY and COM_STMT_PREPARE.
|
||||
|
@ -3728,9 +3835,18 @@ end_with_restore_list:
|
|||
case SQLCOM_FLUSH:
|
||||
{
|
||||
bool write_to_binlog;
|
||||
|
||||
if (check_global_access(thd,RELOAD_ACL))
|
||||
goto error;
|
||||
|
||||
if (first_table && lex->type & REFRESH_READ_LOCK)
|
||||
{
|
||||
if (flush_tables_with_read_lock(thd, all_tables))
|
||||
goto error;
|
||||
my_ok(thd);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
reload_acl_and_cache() will tell us if we are allowed to write to the
|
||||
binlog or not.
|
||||
|
|
|
@ -767,10 +767,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||
|
||||
%pure_parser /* We have threads */
|
||||
/*
|
||||
Currently there are 169 shift/reduce conflicts.
|
||||
Currently there are 168 shift/reduce conflicts.
|
||||
We should not introduce new conflicts any more.
|
||||
*/
|
||||
%expect 169
|
||||
%expect 168
|
||||
|
||||
/*
|
||||
Comments for TOKENS.
|
||||
|
@ -1554,6 +1554,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||
opt_column_list grant_privileges grant_ident grant_list grant_option
|
||||
object_privilege object_privilege_list user_list rename_list
|
||||
clear_privileges flush_options flush_option
|
||||
opt_with_read_lock flush_options_list
|
||||
equal optional_braces
|
||||
opt_mi_check_type opt_to mi_check_types normal_join
|
||||
table_to_table_list table_to_table opt_table_list opt_as
|
||||
|
@ -11095,17 +11096,27 @@ flush:
|
|||
;
|
||||
|
||||
flush_options:
|
||||
flush_options ',' flush_option
|
||||
| flush_option
|
||||
;
|
||||
|
||||
flush_option:
|
||||
table_or_tables
|
||||
{ Lex->type|= REFRESH_TABLES; }
|
||||
opt_table_list {}
|
||||
| TABLES WITH READ_SYM LOCK_SYM
|
||||
{ Lex->type|= REFRESH_TABLES | REFRESH_READ_LOCK; }
|
||||
| ERROR_SYM LOGS_SYM
|
||||
opt_with_read_lock {}
|
||||
| flush_options_list
|
||||
;
|
||||
|
||||
opt_with_read_lock:
|
||||
/* empty */ {}
|
||||
| WITH READ_SYM LOCK_SYM
|
||||
{ Lex->type|= REFRESH_READ_LOCK; }
|
||||
;
|
||||
|
||||
flush_options_list:
|
||||
flush_options_list ',' flush_option
|
||||
| flush_option
|
||||
{}
|
||||
;
|
||||
|
||||
flush_option:
|
||||
ERROR_SYM LOGS_SYM
|
||||
{ Lex->type|= REFRESH_ERROR_LOG; }
|
||||
| ENGINE_SYM LOGS_SYM
|
||||
{ Lex->type|= REFRESH_ENGINE_LOG; }
|
||||
|
|
Loading…
Add table
Reference in a new issue