summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/r/flush.result96
-rw-r--r--mysql-test/t/flush.test100
-rw-r--r--sql/sql_parse.cc116
-rw-r--r--sql/sql_yacc.yy29
4 files changed, 332 insertions, 9 deletions
diff --git a/mysql-test/r/flush.result b/mysql-test/r/flush.result
index 2136bcd92f1..fd23bfa0562 100644
--- a/mysql-test/r/flush.result
+++ b/mysql-test/r/flush.result
@@ -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;
diff --git a/mysql-test/t/flush.test b/mysql-test/t/flush.test
index d41ac3100b0..582d2562fc6 100644
--- a/mysql-test/t/flush.test
+++ b/mysql-test/t/flush.test
@@ -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;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index e66f2756c54..7a13c46e4f6 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1588,6 +1588,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.
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index f03694cb359..5648e3cbdb5 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -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
+ table_or_tables
+ { Lex->type|= REFRESH_TABLES; }
+ opt_table_list {}
+ 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:
- 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
+ ERROR_SYM LOGS_SYM
{ Lex->type|= REFRESH_ERROR_LOG; }
| ENGINE_SYM LOGS_SYM
{ Lex->type|= REFRESH_ENGINE_LOG; }