From ce8a74f2352f894e35c958195e532cfd90aba2ae Mon Sep 17 00:00:00 2001 From: Monty Date: Mon, 31 Mar 2025 20:07:13 +0300 Subject: [PATCH] MDEV-36425 Extend read_only to also block share locks and super user The main purpose of this allow one to use the --read-only option to ensure that no one can issue a query that can block replication. The --read-only option can now take 4 different values: 0 No read only (as before). 1 Blocks changes for users without the 'READ ONLY ADMIN' privilege (as before). 2 Blocks in addition LOCK TABLES and SELECT IN SHARE MODE for not 'READ ONLY ADMIN' users. 3 Blocks in addition 'READ_ONLY_ADMIN' users for all the previous statements. read_only is changed to an enum and one can use the following names for the lock levels: OFF, ON, NO_LOCK, NO_LOCK_NO_ADMIN Too keep things compatible with older versions config files, one can still use values FALSE and TRUE, which are mapped to OFF and ON. The main visible changes are: - 'show variables like "read_only"' now returns a string instead of a number. - Error messages related to read_only violations now contains the current value off readonly. Other things: - is_read_only_ctx() renamed to check_read_only_with_error() - Moved TL_READ_SKIP_LOCKED to it's logical place Reviewed by: Sergei Golubchik --- include/thr_lock.h | 10 +- mysql-test/main/alter_user.result | 4 +- mysql-test/main/bug58669.result | 2 +- mysql-test/main/grant.result | 2 +- mysql-test/main/grant_read_only.result | 14 +- mysql-test/main/mysqld--help.result | 12 +- mysql-test/main/read_only.result | 158 +++++++++++++++--- mysql-test/main/read_only.test | 105 +++++++++++- mysql-test/main/read_only_innodb.result | 8 +- .../binlog_dmls_on_tmp_tables_readonly.result | 4 +- mysql-test/suite/binlog/r/read_only.result | 8 +- .../suite/binlog/r/read_only_statement.result | 8 +- mysql-test/suite/events/events_bugs.result | 6 +- .../suite/perfschema/r/read_only.result | 6 +- mysql-test/suite/rpl/r/rpl_read_only.result | 22 +-- .../rpl/r/rpl_row_drop_temp_table.result | 2 +- mysql-test/suite/sql_sequence/gtid-slave.opt | 2 +- mysql-test/suite/sql_sequence/gtid.result | 2 +- .../suite/sql_sequence/replication-slave.opt | 2 +- .../suite/sql_sequence/replication.result | 2 +- .../suite/sys_vars/r/read_only_basic.result | 46 ++--- .../suite/sys_vars/r/read_only_func.result | 2 +- .../sys_vars/r/sysvars_server_embedded.result | 6 +- .../r/sysvars_server_notembedded.result | 6 +- .../suite/sys_vars/t/read_only_basic.test | 4 +- sql/handler.cc | 5 +- sql/lock.cc | 76 +++++++-- sql/mysqld.cc | 2 +- sql/mysqld.h | 2 +- sql/sql_class.h | 15 +- sql/sql_parse.cc | 8 +- sql/sql_show.cc | 2 +- sql/sql_trigger.cc | 7 +- sql/sys_vars.cc | 32 +++- sql/transaction.cc | 12 +- sql/xa.cc | 20 +-- storage/innobase/handler/ha_innodb.cc | 2 - 37 files changed, 455 insertions(+), 171 deletions(-) diff --git a/include/thr_lock.h b/include/thr_lock.h index 443d707bd2b..b1b54836795 100644 --- a/include/thr_lock.h +++ b/include/thr_lock.h @@ -41,15 +41,16 @@ enum thr_lock_type { TL_IGNORE=-1, modify tables. */ TL_READ_DEFAULT, + /* READ, but skip locks if found */ + TL_READ_SKIP_LOCKED, TL_READ, /* Read lock */ + /* Get shared locks on all read rows */ TL_READ_WITH_SHARED_LOCKS, /* High prior. than TL_WRITE. Allow concurrent insert */ TL_READ_HIGH_PRIORITY, /* READ, Don't allow concurrent insert */ TL_READ_NO_INSERT, - /* READ, but skip locks if found */ - TL_READ_SKIP_LOCKED, - /* + /* Write lock, but allow other threads to read / write. Used by BDB tables in MySQL to mark that someone is reading/writing to the table. @@ -85,6 +86,9 @@ enum thr_lock_type { TL_IGNORE=-1, */ #define TL_FIRST_WRITE TL_WRITE_ALLOW_WRITE +/* First lock that can block readonly slaves using InnoDB tables */ +#define TL_BLOCKS_READONLY TL_READ_WITH_SHARED_LOCKS + enum enum_thr_lock_result { THR_LOCK_SUCCESS= 0, THR_LOCK_ABORTED= 1, THR_LOCK_WAIT_TIMEOUT= 2, THR_LOCK_DEADLOCK= 3 }; diff --git a/mysql-test/main/alter_user.result b/mysql-test/main/alter_user.result index 7cca2db0e5e..19e427e8f10 100644 --- a/mysql-test/main/alter_user.result +++ b/mysql-test/main/alter_user.result @@ -31,9 +31,9 @@ grant create user on *.* to foo; connect a, localhost, foo; select @@global.read_only; @@global.read_only -1 +ON alter user foo; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement # Grant READ_ONLY ADMIN privilege to the user. connection default; grant READ_ONLY ADMIN on *.* to foo; diff --git a/mysql-test/main/bug58669.result b/mysql-test/main/bug58669.result index a7d1d7b5581..4d9680389d5 100644 --- a/mysql-test/main/bug58669.result +++ b/mysql-test/main/bug58669.result @@ -14,7 +14,7 @@ SHOW VARIABLES LIKE "read_only%"; Variable_name Value read_only ON INSERT INTO db1.t1 VALUES (1); -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement connection default; disconnect con1; DROP DATABASE db1; diff --git a/mysql-test/main/grant.result b/mysql-test/main/grant.result index cb986283d6c..3f70f1b2b93 100644 --- a/mysql-test/main/grant.result +++ b/mysql-test/main/grant.result @@ -637,7 +637,7 @@ Update Tables To update existing rows Set user Server To create views and stored routines with a different definer Federated admin Server To execute the CREATE SERVER, ALTER SERVER, DROP SERVER statements Connection admin Server To bypass connection limits and kill other users' connections -Read_only admin Server To perform write operations even if @@read_only=ON +Read_only admin Server To perform write operations even if @@read_only=READ_ONLY or READ_ONLY_NO_LOCK Usage Server Admin No privileges - allow connect only Show Create Routine Databases,Functions,Procedures To allow SHOW CREATE PROCEDURE/FUNCTION/PACKAGE connect root,localhost,root,,test,$MASTER_MYPORT,$MASTER_MYSOCK; diff --git a/mysql-test/main/grant_read_only.result b/mysql-test/main/grant_read_only.result index 18d97cc412f..57e2ee8f374 100644 --- a/mysql-test/main/grant_read_only.result +++ b/mysql-test/main/grant_read_only.result @@ -12,11 +12,11 @@ SET @@GLOBAL.read_only=1; connect con1,localhost,user1,,; connection con1; UPDATE t1 SET a=11 WHERE a=10; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement DELETE FROM t1 WHERE a=11; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement INSERT INTO t1 VALUES (20); -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement disconnect con1; connection default; SET @@GLOBAL.read_only=0; @@ -36,7 +36,7 @@ connect con1,localhost,user1,,; connection con1; SELECT @@read_only; @@read_only -1 +ON UPDATE t1 SET a=11 WHERE a=10; DELETE FROM t1 WHERE a=11; INSERT INTO t1 VALUES (20); @@ -59,11 +59,11 @@ connect con1,localhost,user1,,; connection con1; SELECT @@read_only; @@read_only -1 +ON UPDATE t1 SET a=11 WHERE a=10; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement DELETE FROM t1 WHERE a=11; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement connection default; grant read only admin on *.* to user1@localhost; disconnect con1; diff --git a/mysql-test/main/mysqld--help.result b/mysql-test/main/mysqld--help.result index cdb0ca15b32..269a4b2502b 100644 --- a/mysql-test/main/mysqld--help.result +++ b/mysql-test/main/mysqld--help.result @@ -1154,9 +1154,13 @@ The following specify which files/extra groups are read (specified before remain buffer of this size for each table it scans. If you do many sequential scans, you may want to increase this value - --read-only Make all non-temporary tables read-only, with the - exception for replication (slave) threads and users with - the 'READ ONLY ADMIN' privilege + --read-only[=name] Do not allow changes to non-temporary tables. Options + are:OFF changes allowed. ON Disallow changes for users + without the READ ONLY ADMIN privilege. NO_LOCK + Additionally disallows LOCK TABLES and SELECT IN SHARE + MODE. NO_LOCK_NO_ADMIN Disallows also for users with + READ_ONLY ADMIN privilege. Replication (slave) threads + are not affected by this option --read-rnd-buffer-size=# When reading rows in sorted order after a sort, the rows are read through this buffer to avoid a disk seeks @@ -1972,7 +1976,7 @@ query-prealloc-size 32768 range-alloc-block-size 4096 read-binlog-speed-limit 0 read-buffer-size 131072 -read-only FALSE +read-only OFF read-rnd-buffer-size 262144 redirect-url relay-log (No default value) diff --git a/mysql-test/main/read_only.result b/mysql-test/main/read_only.result index 65cc12ffce9..d48edd0340e 100644 --- a/mysql-test/main/read_only.result +++ b/mysql-test/main/read_only.result @@ -18,31 +18,31 @@ drop table t3; connection con1; select @@global.read_only; @@global.read_only -1 +ON create table t3 (a int); -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement insert into t1 values(1); -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement drop trigger trg1; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement update t1 set a=1 where 1=0; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement update t1,t2 set t1.a=t2.a+1 where t1.a=t2.a; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement delete t1,t2 from t1,t2 where t1.a=t2.a; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement create temporary table t3 (a int); create temporary table t4 (a int) select * from t3; insert into t3 values(1); insert into t4 select * from t3; create table t3 (a int); -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement update t1,t3 set t1.a=t3.a+1 where t1.a=t3.a; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement update t1,t3 set t3.a=t1.a+1 where t1.a=t3.a; update t4,t3 set t4.a=t3.a+1 where t4.a=t3.a; delete t1 from t1,t3 where t1.a=t3.a; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement delete t3 from t1,t3 where t1.a=t3.a; delete t4 from t3,t4 where t4.a=t3.a; create temporary table t1 (a int); @@ -51,7 +51,7 @@ update t1,t3 set t1.a=t3.a+1 where t1.a=t3.a; delete t1 from t1,t3 where t1.a=t3.a; drop table t1; insert into t1 values(1); -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement drop temporary table if exists t1; Warnings: Note 1051 Unknown table 'test.t1' @@ -69,11 +69,11 @@ set global read_only=1; connection con1; select @@global.read_only; @@global.read_only -0 +OFF unlock tables ; select @@global.read_only; @@global.read_only -1 +ON connection default; reap; connection default; @@ -88,11 +88,11 @@ unlock tables ; set global read_only=1; select @@global.read_only; @@global.read_only -1 +ON connection con1; select @@global.read_only; @@global.read_only -1 +ON unlock tables ; connection default; connection default; @@ -108,7 +108,7 @@ set global read_only=1; connection con1; select @@global.read_only; @@global.read_only -1 +ON ROLLBACK; connection default; set global read_only=0; @@ -124,7 +124,7 @@ set global read_only=1; connection default; select @@global.read_only; @@global.read_only -1 +ON unlock tables; disconnect root2; drop temporary table ttt; @@ -157,11 +157,11 @@ flush privileges; connect con_bug27440,127.0.0.1,mysqltest_u1,,test,$MASTER_MYPORT,; connection con_bug27440; create database mysqltest_db2; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement show databases like '%mysqltest_db2%'; Database (%mysqltest_db2%) drop database mysqltest_db1; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement disconnect con_bug27440; connection default; delete from mysql.user where User like 'mysqltest_%'; @@ -179,7 +179,7 @@ GRANT ALTER ON test1.* TO user1@localhost; CREATE DATABASE test1; SET GLOBAL read_only=1; ALTER DATABASE test1 CHARACTER SET utf8; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement SET GLOBAL read_only=0; DROP DATABASE test1; DROP USER user1@localhost; @@ -210,17 +210,17 @@ connection con1; START TRANSACTION; # Check that table updates are still disallowed. INSERT INTO t1 VALUES (3); -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement UPDATE t1 SET a= 1; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement DELETE FROM t1; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement COMMIT; START TRANSACTION READ ONLY; COMMIT; # Explicit RW trans is not allowed without super privilege START TRANSACTION READ WRITE; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement COMMIT; disconnect con1; connection default; @@ -228,3 +228,113 @@ DROP USER user1; SET GLOBAL read_only= 0; DROP TABLE t1; # End of 10.0 tests +# +# MDEV-36425 Extend read_only to also block share locks and super user +# +CREATE USER user1; +GRANT ALL on test.* to user1; +connect con1, localhost, user1; +connection default; +CREATE TABLE t1 (a int primary key auto_increment); +insert into t1 values (1),(2); +show variables like "read_only"; +Variable_name Value +read_only OFF +SET GLOBAL read_only=1; +show variables like "read_only"; +Variable_name Value +read_only ON +# admin +insert into t1 values (); +lock tables t1 read; +unlock tables; +lock tables t1 write; +unlock tables; +begin; +select count(*) from t1 LOCK IN SHARE MODE; +count(*) +3 +select count(*) from t1,(select a from t1 LOCK IN SHARE MODE) as t2; +count(*) +9 +commit; +# user +connection con1; +insert into t1 values (); +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement +lock tables t1 read; +unlock tables; +lock tables t1 write; +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement +unlock tables; +begin; +select count(*) from t1 LOCK IN SHARE MODE; +count(*) +3 +select count(*) from t1,(select a from t1 LOCK IN SHARE MODE) as t2; +count(*) +9 +commit; +connection default; +SET GLOBAL read_only=2; +show variables like "read_only"; +Variable_name Value +read_only NO_LOCK +# admin +insert into t1 values (); +lock tables t1 read; +unlock tables; +lock tables t1 write; +unlock tables; +begin; +select count(*) from t1 LOCK IN SHARE MODE; +count(*) +4 +select count(*) from t1,(select a from t1 LOCK IN SHARE MODE) as t2; +count(*) +16 +commit; +#user +connection con1; +insert into t1 values (); +ERROR HY000: The MariaDB server is running with the --read-only=NO_LOCK option so it cannot execute this statement +lock tables t1 read; +ERROR HY000: The MariaDB server is running with the --read-only=NO_LOCK option so it cannot execute this statement +unlock tables; +lock tables t1 write; +ERROR HY000: The MariaDB server is running with the --read-only=NO_LOCK option so it cannot execute this statement +unlock tables; +begin; +select count(*) from t1 LOCK IN SHARE MODE; +ERROR HY000: The MariaDB server is running with the --read-only=NO_LOCK option so it cannot execute this statement +select count(*) from t1,(select a from t1 LOCK IN SHARE MODE) as t2; +ERROR HY000: The MariaDB server is running with the --read-only=NO_LOCK option so it cannot execute this statement +commit; +connection default; +SET GLOBAL read_only=3; +show variables like "read_only"; +Variable_name Value +read_only NO_LOCK_NO_ADMIN +# admin +insert into t1 values (); +ERROR HY000: The MariaDB server is running with the --read-only=NO_LOCK_NO_ADMIN option so it cannot execute this statement +lock tables t1 read; +ERROR HY000: The MariaDB server is running with the --read-only=NO_LOCK_NO_ADMIN option so it cannot execute this statement +unlock tables; +lock tables t1 write; +ERROR HY000: The MariaDB server is running with the --read-only=NO_LOCK_NO_ADMIN option so it cannot execute this statement +unlock tables; +begin; +select count(*) from t1 LOCK IN SHARE MODE; +ERROR HY000: The MariaDB server is running with the --read-only=NO_LOCK_NO_ADMIN option so it cannot execute this statement +select count(*) from t1,(select a from t1 LOCK IN SHARE MODE) as t2; +ERROR HY000: The MariaDB server is running with the --read-only=NO_LOCK_NO_ADMIN option so it cannot execute this statement +commit; +SET GLOBAL read_only=0; +select count(*) from t1; +count(*) +4 +drop table t1; +drop user user1; +disconnect con1; +# End of 11.8 tests diff --git a/mysql-test/main/read_only.test b/mysql-test/main/read_only.test index 107a67c31fa..da2bb6eb625 100644 --- a/mysql-test/main/read_only.test +++ b/mysql-test/main/read_only.test @@ -146,7 +146,8 @@ send set global read_only=1; connection con1; select @@global.read_only; unlock tables ; -let $wait_condition= SELECT @@global.read_only= 1; +let $wait_timeout=10; +let $wait_condition= SELECT @@global.read_only="on"; --source include/wait_condition.inc select @@global.read_only; @@ -288,6 +289,7 @@ set global read_only= @start_read_only; --echo # --echo # MDEV-16987 - ALTER DATABASE possible in read-only mode --echo # + CREATE USER user1@localhost; GRANT ALTER ON test1.* TO user1@localhost; CREATE DATABASE test1; @@ -362,3 +364,104 @@ DROP TABLE t1; --source include/wait_until_count_sessions.inc --echo # End of 10.0 tests + +--echo # +--echo # MDEV-36425 Extend read_only to also block share locks and super user +--echo # + + +CREATE USER user1; +GRANT ALL on test.* to user1; +connect (con1, localhost, user1); +connection default; + +CREATE TABLE t1 (a int primary key auto_increment); +insert into t1 values (1),(2); +show variables like "read_only"; + +SET GLOBAL read_only=1; +show variables like "read_only"; +--echo # admin +insert into t1 values (); +lock tables t1 read; +unlock tables; +lock tables t1 write; +unlock tables; +begin; +select count(*) from t1 LOCK IN SHARE MODE; +select count(*) from t1,(select a from t1 LOCK IN SHARE MODE) as t2; +commit; + +--echo # user +connection con1; +--error ER_OPTION_PREVENTS_STATEMENT +insert into t1 values (); +lock tables t1 read; +unlock tables; +--error ER_OPTION_PREVENTS_STATEMENT +lock tables t1 write; +unlock tables; +begin; +select count(*) from t1 LOCK IN SHARE MODE; +select count(*) from t1,(select a from t1 LOCK IN SHARE MODE) as t2; +commit; + +connection default; +SET GLOBAL read_only=2; +show variables like "read_only"; + +--echo # admin +insert into t1 values (); +lock tables t1 read; +unlock tables; +lock tables t1 write; +unlock tables; +begin; +select count(*) from t1 LOCK IN SHARE MODE; +select count(*) from t1,(select a from t1 LOCK IN SHARE MODE) as t2; +commit; + +--echo #user +connection con1; +--error ER_OPTION_PREVENTS_STATEMENT +insert into t1 values (); +--error ER_OPTION_PREVENTS_STATEMENT +lock tables t1 read; +unlock tables; +--error ER_OPTION_PREVENTS_STATEMENT +lock tables t1 write; +unlock tables; +begin; +--error ER_OPTION_PREVENTS_STATEMENT +select count(*) from t1 LOCK IN SHARE MODE; +--error ER_OPTION_PREVENTS_STATEMENT +select count(*) from t1,(select a from t1 LOCK IN SHARE MODE) as t2; +commit; + +connection default; +SET GLOBAL read_only=3; +show variables like "read_only"; + +--echo # admin +--error ER_OPTION_PREVENTS_STATEMENT +insert into t1 values (); +--error ER_OPTION_PREVENTS_STATEMENT +lock tables t1 read; +unlock tables; +--error ER_OPTION_PREVENTS_STATEMENT +lock tables t1 write; +unlock tables; +begin; +--error ER_OPTION_PREVENTS_STATEMENT +select count(*) from t1 LOCK IN SHARE MODE; +--error ER_OPTION_PREVENTS_STATEMENT +select count(*) from t1,(select a from t1 LOCK IN SHARE MODE) as t2; +commit; + +SET GLOBAL read_only=0; +select count(*) from t1; +drop table t1; +drop user user1; +disconnect con1; + +--echo # End of 11.8 tests diff --git a/mysql-test/main/read_only_innodb.result b/mysql-test/main/read_only_innodb.result index 85aee64087f..6616356385d 100644 --- a/mysql-test/main/read_only_innodb.result +++ b/mysql-test/main/read_only_innodb.result @@ -13,12 +13,12 @@ set global read_only=1; connection con1; select @@global.read_only; @@global.read_only -1 +ON select * from table_11733 ; a 11733 COMMIT; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement connection default; set global read_only=0; drop table table_11733 ; @@ -115,7 +115,7 @@ a 1 CREATE TEMPORARY TABLE temp (a INT) ENGINE=INNODB; INSERT INTO t1 VALUES (1); -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement INSERT INTO temp VALUES (1); SELECT * FROM t2; a @@ -135,7 +135,7 @@ a SELECT * FROM temp; a INSERT INTO t1 VALUES (1); -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement INSERT INTO temp VALUES (1); DROP TABLE temp; UNLOCK TABLES; diff --git a/mysql-test/suite/binlog/r/binlog_dmls_on_tmp_tables_readonly.result b/mysql-test/suite/binlog/r/binlog_dmls_on_tmp_tables_readonly.result index 1b60e50c09d..722b07e8ae1 100644 --- a/mysql-test/suite/binlog/r/binlog_dmls_on_tmp_tables_readonly.result +++ b/mysql-test/suite/binlog/r/binlog_dmls_on_tmp_tables_readonly.result @@ -31,7 +31,7 @@ SET GLOBAL READ_ONLY=1; connection con1; SELECT @@GLOBAL.READ_ONLY; @@GLOBAL.READ_ONLY -1 +ON COMMIT; connection default; SET GLOBAL READ_ONLY=0; @@ -45,7 +45,7 @@ SET GLOBAL READ_ONLY=1; connection con1; SELECT @@GLOBAL.READ_ONLY; @@GLOBAL.READ_ONLY -1 +ON COMMIT; SELECT * FROM t1; a diff --git a/mysql-test/suite/binlog/r/read_only.result b/mysql-test/suite/binlog/r/read_only.result index 1084970d11a..5dc5bc48b5f 100644 --- a/mysql-test/suite/binlog/r/read_only.result +++ b/mysql-test/suite/binlog/r/read_only.result @@ -13,7 +13,7 @@ set global read_only=1; # Ensure that optimize and analyze doesn't log to binary log connect con1,localhost,test,,test; insert into t1 values(3); -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement analyze table t1; Table Op Msg_type Msg_text test.t1 analyze status Engine-independent statistics collected @@ -23,10 +23,10 @@ Table Op Msg_type Msg_text test.t1 check status OK repair table t1; Table Op Msg_type Msg_text -test.t1 repair Error The MariaDB server is running with the --read-only option so it cannot execute this statement +test.t1 repair Error The MariaDB server is running with the --read-only=ON option so it cannot execute this statement test.t1 repair error Corrupt optimize table t1; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement # Ensure that using temporary tables is not logged create temporary table tmp1 (a int) engine=myisam; insert into tmp1 values (1),(2); @@ -48,7 +48,7 @@ a b 1 NULL 2 NULL insert into t1 select a+100 from tmp2; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement drop table tmp1,tmp2,tmp3; # Clean up test connection disconnect con1; diff --git a/mysql-test/suite/binlog/r/read_only_statement.result b/mysql-test/suite/binlog/r/read_only_statement.result index 1084970d11a..5dc5bc48b5f 100644 --- a/mysql-test/suite/binlog/r/read_only_statement.result +++ b/mysql-test/suite/binlog/r/read_only_statement.result @@ -13,7 +13,7 @@ set global read_only=1; # Ensure that optimize and analyze doesn't log to binary log connect con1,localhost,test,,test; insert into t1 values(3); -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement analyze table t1; Table Op Msg_type Msg_text test.t1 analyze status Engine-independent statistics collected @@ -23,10 +23,10 @@ Table Op Msg_type Msg_text test.t1 check status OK repair table t1; Table Op Msg_type Msg_text -test.t1 repair Error The MariaDB server is running with the --read-only option so it cannot execute this statement +test.t1 repair Error The MariaDB server is running with the --read-only=ON option so it cannot execute this statement test.t1 repair error Corrupt optimize table t1; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement # Ensure that using temporary tables is not logged create temporary table tmp1 (a int) engine=myisam; insert into tmp1 values (1),(2); @@ -48,7 +48,7 @@ a b 1 NULL 2 NULL insert into t1 select a+100 from tmp2; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement drop table tmp1,tmp2,tmp3; # Clean up test connection disconnect con1; diff --git a/mysql-test/suite/events/events_bugs.result b/mysql-test/suite/events/events_bugs.result index a34a6111eb6..49c8b9dd2d1 100644 --- a/mysql-test/suite/events/events_bugs.result +++ b/mysql-test/suite/events/events_bugs.result @@ -694,13 +694,13 @@ SET GLOBAL READ_ONLY = 1; connect u1_con,localhost,mysqltest_u1,,events_test; CREATE EVENT e1 ON SCHEDULE AT '2038-01-01 00:00:00' DO SET @a = 1; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement ALTER EVENT e1 COMMENT 'comment'; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement DROP EVENT e1; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement connect root_con,localhost,root,,events_test; diff --git a/mysql-test/suite/perfschema/r/read_only.result b/mysql-test/suite/perfschema/r/read_only.result index aa18efc928f..666c100d958 100644 --- a/mysql-test/suite/perfschema/r/read_only.result +++ b/mysql-test/suite/perfschema/r/read_only.result @@ -9,7 +9,7 @@ set global read_only=0; connection con1; select @@global.read_only; @@global.read_only -0 +OFF show grants; Grants for pfsuser@localhost GRANT USAGE ON *.* TO `pfsuser`@`localhost` @@ -23,7 +23,7 @@ set global read_only=1; connection con1; select @@global.read_only; @@global.read_only -1 +ON show grants; Grants for pfsuser@localhost GRANT USAGE ON *.* TO `pfsuser`@`localhost` @@ -38,7 +38,7 @@ disconnect con1; connect con1, localhost, pfsuser, ,"*NO-ONE*"; select @@global.read_only; @@global.read_only -1 +ON show grants; Grants for pfsuser@localhost GRANT READ_ONLY ADMIN ON *.* TO `pfsuser`@`localhost` diff --git a/mysql-test/suite/rpl/r/rpl_read_only.result b/mysql-test/suite/rpl/r/rpl_read_only.result index 0c685f3ddfb..6cf12b7def1 100644 --- a/mysql-test/suite/rpl/r/rpl_read_only.result +++ b/mysql-test/suite/rpl/r/rpl_read_only.result @@ -17,7 +17,7 @@ set global read_only=1; connection master1; select @@read_only; @@read_only -1 +ON select * from t1; a 1001 @@ -27,7 +27,7 @@ a connection slave; select @@read_only; @@read_only -0 +OFF select * from t1; a 1001 @@ -43,7 +43,7 @@ BEGIN; connection master; select @@read_only; @@read_only -0 +OFF set global read_only=1; connection master1; *** On SUPER USER connection *** @@ -52,9 +52,9 @@ insert into t2 values(2002); connection master2; *** On regular USER connection *** insert into t1 values(1003); -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement insert into t2 values(2003); -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement connection master1; *** SUPER USER COMMIT (must succeed) *** COMMIT; @@ -64,7 +64,7 @@ COMMIT; connection master; select @@read_only; @@read_only -1 +ON set global read_only=0; connection master1; insert into t1 values(1004); @@ -95,7 +95,7 @@ set global read_only=1; connection slave; select @@read_only; @@read_only -1 +ON show create table t1; Table Create Table t1 CREATE TABLE `t1` ( @@ -137,9 +137,9 @@ a 2005 connection slave2; insert into t1 values(1006); -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement insert into t2 values(2006); -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement # # MDEV-30978: On slave XA COMMIT/XA ROLLBACK fail to return an error in read-only mode # @@ -155,9 +155,9 @@ xa prepare '1'; connection slave; connection slave2; xa commit '1'; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement xa rollback '1'; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement connection master; xa rollback '1'; connection master; diff --git a/mysql-test/suite/rpl/r/rpl_row_drop_temp_table.result b/mysql-test/suite/rpl/r/rpl_row_drop_temp_table.result index eea0dbd40e7..cd66b98c587 100644 --- a/mysql-test/suite/rpl/r/rpl_row_drop_temp_table.result +++ b/mysql-test/suite/rpl/r/rpl_row_drop_temp_table.result @@ -26,7 +26,7 @@ END connection slave; SELECT @@read_only; @@read_only -1 +ON ======== CLEAN UP ========= connection master; DROP TEMPORARY TABLE t1_tmp; diff --git a/mysql-test/suite/sql_sequence/gtid-slave.opt b/mysql-test/suite/sql_sequence/gtid-slave.opt index dc0ff1864e0..4cf926bfe2f 100644 --- a/mysql-test/suite/sql_sequence/gtid-slave.opt +++ b/mysql-test/suite/sql_sequence/gtid-slave.opt @@ -1,4 +1,4 @@ --binlog_format=row --query_cache_type=1 ---read_only=true +--read_only=ON --log-slave-updates diff --git a/mysql-test/suite/sql_sequence/gtid.result b/mysql-test/suite/sql_sequence/gtid.result index f23ebc56688..04a545e0811 100644 --- a/mysql-test/suite/sql_sequence/gtid.result +++ b/mysql-test/suite/sql_sequence/gtid.result @@ -239,7 +239,7 @@ show global variables like 'read_only'; Variable_name Value read_only ON select next value for s_db.s1; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement connection m_normal_1; drop sequence s_db.s1; ########################################### diff --git a/mysql-test/suite/sql_sequence/replication-slave.opt b/mysql-test/suite/sql_sequence/replication-slave.opt index a4e068e4b43..ba189968c34 100644 --- a/mysql-test/suite/sql_sequence/replication-slave.opt +++ b/mysql-test/suite/sql_sequence/replication-slave.opt @@ -1 +1 @@ ---binlog_format=row --query_cache_type=1 --read_only=true +--binlog_format=row --query_cache_type=1 --read_only=on diff --git a/mysql-test/suite/sql_sequence/replication.result b/mysql-test/suite/sql_sequence/replication.result index f7e46b74f98..a406b547133 100644 --- a/mysql-test/suite/sql_sequence/replication.result +++ b/mysql-test/suite/sql_sequence/replication.result @@ -347,7 +347,7 @@ show global variables like 'read_only'; Variable_name Value read_only ON select next value for s_db.s1; -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement connection m_normal_1; drop sequence s_db.s1; ########################################### diff --git a/mysql-test/suite/sys_vars/r/read_only_basic.result b/mysql-test/suite/sys_vars/r/read_only_basic.result index d765b7fcae0..fea00e7f16f 100644 --- a/mysql-test/suite/sys_vars/r/read_only_basic.result +++ b/mysql-test/suite/sys_vars/r/read_only_basic.result @@ -1,95 +1,97 @@ SET @start_value = @@global.read_only; SELECT @start_value; @start_value -0 +OFF '#--------------------FN_DYNVARS_139_01------------------------#' SET @@global.read_only = 1; SET @@global.read_only = DEFAULT; SELECT @@global.read_only; @@global.read_only -0 +OFF '#---------------------FN_DYNVARS_139_02-------------------------#' SET @@global.read_only = @start_value; SELECT @@global.read_only = 0; @@global.read_only = 0 1 +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: 'OFF' '#--------------------FN_DYNVARS_139_03------------------------#' SET @@global.read_only = 0; SELECT @@global.read_only; @@global.read_only -0 +OFF SET @@global.read_only = 1; SELECT @@global.read_only; @@global.read_only -1 +ON SET @@global.read_only = TRUE; SELECT @@global.read_only; @@global.read_only -1 +ON SET @@global.read_only = FALSE; SELECT @@global.read_only; @@global.read_only -0 +OFF SET @@global.read_only = ON; SELECT @@global.read_only; @@global.read_only -1 +ON SET @@global.read_only = OFF; SELECT @@global.read_only; @@global.read_only -0 +OFF '#--------------------FN_DYNVARS_139_04-------------------------#' SET @@global.read_only = -1; ERROR 42000: Variable 'read_only' can't be set to the value of '-1' SELECT @@global.read_only; @@global.read_only -0 +OFF SET @@global.read_only = 4294967296; ERROR 42000: Variable 'read_only' can't be set to the value of '4294967296' SELECT @@global.read_only; @@global.read_only -0 +OFF SET @@global.read_only = 10240022115; ERROR 42000: Variable 'read_only' can't be set to the value of '10240022115' SELECT @@global.read_only; @@global.read_only -0 +OFF SET @@global.read_only = 10000.01; ERROR 42000: Incorrect argument type to variable 'read_only' SELECT @@global.read_only; @@global.read_only -0 +OFF SET @@global.read_only = -1024; ERROR 42000: Variable 'read_only' can't be set to the value of '-1024' SELECT @@global.read_only; @@global.read_only -0 +OFF SET @@global.read_only = 42949672950; ERROR 42000: Variable 'read_only' can't be set to the value of '42949672950' SELECT @@global.read_only; @@global.read_only -0 +OFF SET @@global.read_only = 'test'; ERROR 42000: Variable 'read_only' can't be set to the value of 'test' SELECT @@global.read_only; @@global.read_only -0 +OFF '#-------------------FN_DYNVARS_139_05----------------------------#' SET @@session.read_only = 0; ERROR HY000: Variable 'read_only' is a GLOBAL variable and should be set with SET GLOBAL SELECT @@read_only; @@read_only -0 +OFF '#----------------------FN_DYNVARS_139_06------------------------#' -SELECT IF(@@global.read_only, "ON", "OFF") = VARIABLE_VALUE +SELECT @@global.read_only = VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME='read_only'; -IF(@@global.read_only, "ON", "OFF") = VARIABLE_VALUE +@@global.read_only = VARIABLE_VALUE 1 -SELECT IF(@@read_only, "ON", "OFF") = VARIABLE_VALUE +SELECT @@global.read_only = VARIABLE_VALUE FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE VARIABLE_NAME='read_only'; -IF(@@read_only, "ON", "OFF") = VARIABLE_VALUE +@@global.read_only = VARIABLE_VALUE 1 '#---------------------FN_DYNVARS_139_07----------------------#' SET @@global.read_only = 1; @@ -101,7 +103,7 @@ SET read_only = 1; ERROR HY000: Variable 'read_only' is a GLOBAL variable and should be set with SET GLOBAL SELECT @@read_only; @@read_only -1 +ON SELECT local.read_only; ERROR 42S02: Unknown table 'local' in SELECT SELECT global.read_only; @@ -111,4 +113,4 @@ ERROR 42S22: Unknown column 'read_only' in 'SELECT' SET @@global.read_only = @start_value; SELECT @@global.read_only; @@global.read_only -0 +OFF diff --git a/mysql-test/suite/sys_vars/r/read_only_func.result b/mysql-test/suite/sys_vars/r/read_only_func.result index f9ac41293ab..24a7eed4e2c 100644 --- a/mysql-test/suite/sys_vars/r/read_only_func.result +++ b/mysql-test/suite/sys_vars/r/read_only_func.result @@ -28,7 +28,7 @@ id INT NOT NULL auto_increment, PRIMARY KEY (id), name BLOB ); -ERROR HY000: The MariaDB server is running with the --read-only option so it cannot execute this statement +ERROR HY000: The MariaDB server is running with the --read-only=ON option so it cannot execute this statement not updating values INSERT into t2(name) values("aaassssssssddddddddffffff"); Got one of the listed errors diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result index 75ccd8955e6..de5b732e9c7 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result @@ -3384,12 +3384,12 @@ READ_ONLY NO COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME READ_ONLY VARIABLE_SCOPE GLOBAL -VARIABLE_TYPE BOOLEAN -VARIABLE_COMMENT Make all non-temporary tables read-only, with the exception for replication (slave) threads and users with the 'READ ONLY ADMIN' privilege +VARIABLE_TYPE ENUM +VARIABLE_COMMENT Do not allow changes to non-temporary tables. Options are:OFF changes allowed. ON Disallow changes for users without the READ ONLY ADMIN privilege. NO_LOCK Additionally disallows LOCK TABLES and SELECT IN SHARE MODE. NO_LOCK_NO_ADMIN Disallows also for users with READ_ONLY ADMIN privilege. Replication (slave) threads are not affected by this option NUMERIC_MIN_VALUE NULL NUMERIC_MAX_VALUE NULL NUMERIC_BLOCK_SIZE NULL -ENUM_VALUE_LIST OFF,ON +ENUM_VALUE_LIST OFF,ON,NO_LOCK,NO_LOCK_NO_ADMIN,FALSE,TRUE READ_ONLY NO COMMAND_LINE_ARGUMENT OPTIONAL VARIABLE_NAME READ_RND_BUFFER_SIZE diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result index 51011cc1976..dbb253311c8 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result @@ -3604,12 +3604,12 @@ READ_ONLY NO COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME READ_ONLY VARIABLE_SCOPE GLOBAL -VARIABLE_TYPE BOOLEAN -VARIABLE_COMMENT Make all non-temporary tables read-only, with the exception for replication (slave) threads and users with the 'READ ONLY ADMIN' privilege +VARIABLE_TYPE ENUM +VARIABLE_COMMENT Do not allow changes to non-temporary tables. Options are:OFF changes allowed. ON Disallow changes for users without the READ ONLY ADMIN privilege. NO_LOCK Additionally disallows LOCK TABLES and SELECT IN SHARE MODE. NO_LOCK_NO_ADMIN Disallows also for users with READ_ONLY ADMIN privilege. Replication (slave) threads are not affected by this option NUMERIC_MIN_VALUE NULL NUMERIC_MAX_VALUE NULL NUMERIC_BLOCK_SIZE NULL -ENUM_VALUE_LIST OFF,ON +ENUM_VALUE_LIST OFF,ON,NO_LOCK,NO_LOCK_NO_ADMIN,FALSE,TRUE READ_ONLY NO COMMAND_LINE_ARGUMENT OPTIONAL VARIABLE_NAME READ_RND_BUFFER_SIZE diff --git a/mysql-test/suite/sys_vars/t/read_only_basic.test b/mysql-test/suite/sys_vars/t/read_only_basic.test index 3ef35020f01..7a6c9553d42 100644 --- a/mysql-test/suite/sys_vars/t/read_only_basic.test +++ b/mysql-test/suite/sys_vars/t/read_only_basic.test @@ -120,11 +120,11 @@ SELECT @@read_only; # Check if the value in GLOBAL & SESSION Tables matches values in variable # ############################################################################## -SELECT IF(@@global.read_only, "ON", "OFF") = VARIABLE_VALUE +SELECT @@global.read_only = VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME='read_only'; -SELECT IF(@@read_only, "ON", "OFF") = VARIABLE_VALUE +SELECT @@global.read_only = VARIABLE_VALUE FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE VARIABLE_NAME='read_only'; diff --git a/sql/handler.cc b/sql/handler.cc index 703af18700b..23c123a31b5 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1855,11 +1855,8 @@ int ha_commit_trans(THD *thd, bool all) DEBUG_SYNC(thd, "ha_commit_trans_after_acquire_commit_lock"); } - if (rw_trans && thd->is_read_only_ctx()) - { - my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only"); + if (rw_trans && thd->check_read_only_with_error()) goto err; - } #if 1 // FIXME: This should be done in ha_prepare(). if (rw_trans || (thd->lex->sql_command == SQLCOM_ALTER_TABLE && diff --git a/sql/lock.cc b/sql/lock.cc index 1296e5455d5..ac38014ca6b 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -99,6 +99,28 @@ static int unlock_external(THD *thd, TABLE **table,uint count); static int thr_lock_errno_to_mysql[]= { 0, ER_LOCK_ABORTED, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK }; + +extern const char *read_only_mode_names[]; + +/* + Give an error in case if users violates read only state +*/ + +void mariadb_error_read_only() +{ + char msg[60]; + int read_only= opt_readonly; + DBUG_ASSERT(read_only); + if (unlikely(read_only == 0)) + read_only= 1; // If global readonly changed during call + + strxnmov(msg, sizeof(msg), "--read-only=", + read_only_mode_names[read_only], + NullS); + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), msg); +} + + /** Perform semantic checks for mysql_lock_tables. @param thd The current thread @@ -112,13 +134,10 @@ static int lock_tables_check(THD *thd, TABLE **tables, uint count, uint flags) { uint system_count, i; - bool ignore_read_only, log_table_write_query; - + bool log_table_write_query; DBUG_ENTER("lock_tables_check"); system_count= 0; - ignore_read_only= - (thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY) != NO_ACL; log_table_write_query= (is_log_table_write_query(thd->lex->sql_command) || ((flags & MYSQL_LOCK_LOG_TABLE) != 0)); @@ -143,8 +162,8 @@ lock_tables_check(THD *thd, TABLE **tables, uint count, uint flags) or hold any type of lock in a session, since this would be a DOS attack. */ - if ((t->reginfo.lock_type >= TL_FIRST_WRITE) - || (thd->lex->sql_command == SQLCOM_LOCK_TABLES)) + if ((t->reginfo.lock_type >= TL_FIRST_WRITE) || + (thd->lex->sql_command == SQLCOM_LOCK_TABLES)) { my_error(ER_CANT_LOCK_LOG_TABLE, MYF(0)); DBUG_RETURN(1); @@ -179,19 +198,46 @@ lock_tables_check(THD *thd, TABLE **tables, uint count, uint flags) /* Prevent modifications to base tables if READ_ONLY is activated. - In any case, read only does not apply to temporary tables. + In any case, read only does not apply to temporary tables or slave + threads. */ - if (!(flags & MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY) && !t->s->tmp_table) + if (unlikely(opt_readonly) && + !(flags & MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY) && !t->s->tmp_table && + !thd->slave_thread) { - if (t->reginfo.lock_type >= TL_FIRST_WRITE && - !ignore_read_only && opt_readonly && !thd->slave_thread) + switch (opt_readonly) { - my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only"); - DBUG_RETURN(1); + case READONLY_OFF: // Impossible + DBUG_ASSERT(0); + break; + case READONLY_ON: // Compatibility + if (!(thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY) && + t->reginfo.lock_type >= TL_FIRST_WRITE) + { + mariadb_error_read_only(); + DBUG_RETURN(1); + } + break; + case READONLY_NO_LOCK: + if (!(thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY) && + (thd->lex->sql_command == SQLCOM_LOCK_TABLES || + t->reginfo.lock_type >= TL_BLOCKS_READONLY)) + { + mariadb_error_read_only(); + DBUG_RETURN(1); + } + break; + case READONLY_NO_LOCK_NO_ADMIN: + if (thd->lex->sql_command == SQLCOM_LOCK_TABLES || + t->reginfo.lock_type >= TL_BLOCKS_READONLY) + { + mariadb_error_read_only(); + DBUG_RETURN(1); + } + break; } } } - /* Locking of system tables is restricted: locking a mix of system and non-system tables in the same lock @@ -387,10 +433,10 @@ static int lock_external(THD *thd, TABLE **tables, uint count) DBUG_PRINT("info", ("count %d", count)); for (i=1 ; i <= count ; i++, tables++) { - DBUG_ASSERT((*tables)->reginfo.lock_type >= TL_READ); + DBUG_ASSERT((*tables)->reginfo.lock_type >= TL_READ_SKIP_LOCKED); lock_type=F_WRLCK; /* Lock exclusive */ if ((*tables)->db_stat & HA_READ_ONLY || - ((*tables)->reginfo.lock_type >= TL_READ && + ((*tables)->reginfo.lock_type >= TL_READ_SKIP_LOCKED && (*tables)->reginfo.lock_type < TL_FIRST_WRITE)) lock_type=F_RDLCK; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 3be492d56cd..c46b3eba37b 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -419,7 +419,7 @@ handlerton *heap_hton; handlerton *myisam_hton; handlerton *partition_hton; -my_bool read_only= 0, opt_readonly= 0; +ulong read_only= 0, opt_readonly= 0; my_bool use_temp_pool, relay_log_purge; my_bool relay_log_recovery; my_bool opt_sync_frm, opt_allow_suspicious_udfs; diff --git a/sql/mysqld.h b/sql/mysqld.h index 03494875872..4050c3c0852 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -129,7 +129,7 @@ extern ulong rpl_transactions_multi_engine; extern ulong transactions_gtid_foreign_engine; extern ulong slave_run_triggers_for_rbr; extern ulonglong slave_type_conversions_options; -extern my_bool read_only, opt_readonly; +extern ulong read_only, opt_readonly; extern MYSQL_PLUGIN_IMPORT my_bool lower_case_file_system; extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs; extern my_bool opt_secure_auth; diff --git a/sql/sql_class.h b/sql/sql_class.h index 037604c948f..5e0fc395567 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -126,6 +126,8 @@ enum enum_slave_run_triggers_for_rbr { SLAVE_RUN_TRIGGERS_FOR_RBR_NO, SLAVE_RUN_TRIGGERS_FOR_RBR_ENFORCE}; enum enum_slave_type_conversions { SLAVE_TYPE_CONVERSIONS_ALL_LOSSY, SLAVE_TYPE_CONVERSIONS_ALL_NON_LOSSY}; +enum read_only_options { READONLY_OFF, READONLY_ON, READONLY_NO_LOCK, + READONLY_NO_LOCK_NO_ADMIN}; /* COLUMNS_READ: A column is goind to be read. @@ -235,6 +237,8 @@ extern "C" int thd_current_status(MYSQL_THD thd); extern "C" enum enum_server_command thd_current_command(MYSQL_THD thd); extern "C" int thd_double_innodb_cardinality(MYSQL_THD thd); +extern void mariadb_error_read_only(); + /** @class CSET_STRING @brief Character set armed LEX_STRING @@ -3534,11 +3538,14 @@ public: /** Checks if a user connection is read-only */ - inline bool is_read_only_ctx() + inline bool check_read_only_with_error() { - return opt_readonly && - !(security_ctx->master_access & PRIV_IGNORE_READ_ONLY) && - !slave_thread; + if (likely(!opt_readonly) || slave_thread || + ((security_ctx->master_access & PRIV_IGNORE_READ_ONLY) && + opt_readonly != READONLY_NO_LOCK_NO_ADMIN)) + return false; + mariadb_error_read_only(); + return true; } private: diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 7208f77f4fe..5ee1943f71b 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1470,14 +1470,16 @@ out: static bool deny_updates_if_read_only_option(THD *thd, TABLE_LIST *all_tables) { DBUG_ENTER("deny_updates_if_read_only_option"); + DBUG_ASSERT(!thd->slave_thread); // Checked by caller if (!opt_readonly) DBUG_RETURN(FALSE); LEX *lex= thd->lex; - /* Super user is allowed to do changes */ - if ((thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY) != NO_ACL) + /* Super user is allowed to do changes in some cases */ + if ((thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY) != NO_ACL && + opt_readonly < READONLY_NO_LOCK_NO_ADMIN) DBUG_RETURN(FALSE); /* Check if command doesn't update anything */ @@ -3669,7 +3671,7 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) */ if (deny_updates_if_read_only_option(thd, all_tables)) { - my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only"); + mariadb_error_read_only(); DBUG_RETURN(-1); } #ifdef HAVE_REPLICATION diff --git a/sql/sql_show.cc b/sql/sql_show.cc index f9df46f1214..110c8b6296b 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -575,7 +575,7 @@ static struct show_privileges_st sys_privileges[]= {"Set user","Server", "To create views and stored routines with a different definer"}, {"Federated admin", "Server", "To execute the CREATE SERVER, ALTER SERVER, DROP SERVER statements"}, {"Connection admin", "Server", "To bypass connection limits and kill other users' connections"}, - {"Read_only admin", "Server", "To perform write operations even if @@read_only=ON"}, + {"Read_only admin", "Server", "To perform write operations even if @@read_only=READ_ONLY or READ_ONLY_NO_LOCK"}, {"Usage","Server Admin","No privileges - allow connect only"}, {"Show Create Routine","Databases,Functions,Procedures","To allow SHOW CREATE PROCEDURE/FUNCTION/PACKAGE"}, {NullS, NullS, NullS} diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index cc4657a73e6..8c1cbbb53e6 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -525,13 +525,8 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) */ thd->lex->sql_command= backup.sql_command; - if (opt_readonly && - !(thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY) && - !thd->slave_thread) - { - my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only"); + if (thd->check_read_only_with_error()) goto end; - } if (add_table_for_trigger_internal(thd, thd->lex->spname, if_exists, &tables, trn_path_buff)) diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 6869d271124..0479c3f4284 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -3191,9 +3191,15 @@ static bool check_read_only(sys_var *self, THD *thd, set_var *var) static bool fix_read_only(sys_var *self, THD *thd, enum_var_type type) { bool result= true; - my_bool new_read_only= read_only; // make a copy before releasing a mutex + ulong new_read_only; DBUG_ENTER("sys_var_opt_readonly::update"); + /* Change old options FALSE and TRUE to OFF and ON */ + if (read_only > READONLY_NO_LOCK_NO_ADMIN) + read_only-= ((ulong) READONLY_NO_LOCK_NO_ADMIN + 1); + + new_read_only= read_only; // make a copy before releasing a mutex + if (read_only == FALSE || read_only == opt_readonly) { opt_readonly= read_only; @@ -3258,16 +3264,28 @@ static bool fix_read_only(sys_var *self, THD *thd, enum_var_type type) fix_read_only() compares them and runs needed operations for the transition (especially when transitioning from false to true) and synchronizes both booleans in the end. + The FALSE and TRUE options are only for compatability with old config files. + They will be mapped to OFF and ON respectively. */ -static Sys_var_on_access_global Sys_readonly( "read_only", - "Make all non-temporary tables read-only, with the exception for " - "replication (slave) threads and users with the 'READ ONLY ADMIN' " - "privilege", - GLOBAL_VAR(read_only), CMD_LINE(OPT_ARG), DEFAULT(FALSE), - NO_MUTEX_GUARD, NOT_IN_BINLOG, + "Do not allow changes to non-temporary tables. Options are:" + "OFF changes allowed. " + "ON Disallow changes for users without the READ ONLY ADMIN " + "privilege. " + "NO_LOCK Additionally disallows LOCK TABLES and " + "SELECT IN SHARE MODE. " + "NO_LOCK_NO_ADMIN Disallows also for users with " + "READ_ONLY ADMIN privilege. " + "Replication (slave) threads are not affected by this option", + GLOBAL_VAR(read_only), CMD_LINE(OPT_ARG), + read_only_mode_names, DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_read_only), ON_UPDATE(fix_read_only)); // Small lower limit to be able to test MRR diff --git a/sql/transaction.cc b/sql/transaction.cc index 432943aae19..2702cb192c7 100644 --- a/sql/transaction.cc +++ b/sql/transaction.cc @@ -171,12 +171,14 @@ bool trans_begin(THD *thd, uint flags) Implicitly starting a RW transaction is allowed for backward compatibility. */ - const bool user_is_super= - MY_TEST(thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY); - if (opt_readonly && !user_is_super) + if (opt_readonly) { - my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only"); - DBUG_RETURN(true); + if (!(thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY) || + opt_readonly == READONLY_NO_LOCK_NO_ADMIN) + { + mariadb_error_read_only(); + DBUG_RETURN(true); + } } thd->tx_read_only= false; /* diff --git a/sql/xa.cc b/sql/xa.cc index 12021f9a815..951232c646c 100644 --- a/sql/xa.cc +++ b/sql/xa.cc @@ -662,9 +662,8 @@ bool trans_xa_commit(THD *thd) MDL_request mdl_request; bool rw_trans= (xs->rm_error != ER_XA_RBROLLBACK); - if (rw_trans && thd->is_read_only_ctx()) + if (rw_trans && thd->check_read_only_with_error()) { - my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only"); res= 1; goto _end_external_xid; } @@ -707,11 +706,10 @@ bool trans_xa_commit(THD *thd) DBUG_RETURN(res); } - if (thd->transaction->all.is_trx_read_write() && thd->is_read_only_ctx()) - { - my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only"); + if (thd->transaction->all.is_trx_read_write() && thd->check_read_only_with_error()) DBUG_RETURN(TRUE); - } else if (xa_trans_rolled_back(xid_state.xid_cache_element)) + + if (xa_trans_rolled_back(xid_state.xid_cache_element)) { xa_trans_force_rollback(thd); DBUG_RETURN(thd->is_error()); @@ -822,9 +820,8 @@ bool trans_xa_rollback(THD *thd) bool xid_deleted= false; bool rw_trans= (xs->rm_error != ER_XA_RBROLLBACK); - if (rw_trans && thd->is_read_only_ctx()) + if (rw_trans && thd->check_read_only_with_error()) { - my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only"); res= 1; goto _end_external_xid; } @@ -863,11 +860,10 @@ bool trans_xa_rollback(THD *thd) DBUG_RETURN(thd->get_stmt_da()->is_error()); } - if (thd->transaction->all.is_trx_read_write() && thd->is_read_only_ctx()) - { - my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only"); + if (thd->transaction->all.is_trx_read_write() && thd->check_read_only_with_error()) DBUG_RETURN(TRUE); - } else if (xid_state.xid_cache_element->xa_state == XA_ACTIVE) + + if (xid_state.xid_cache_element->xa_state == XA_ACTIVE) { xid_state.er_xaer_rmfail(); DBUG_RETURN(TRUE); diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index b0d6663522e..d26a8ca734c 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -55,8 +55,6 @@ this program; if not, write to the Free Software Foundation, Inc., #include "scope.h" #include "srv0srv.h" -extern my_bool opt_readonly; - // MYSQL_PLUGIN_IMPORT extern my_bool lower_case_file_system; // MYSQL_PLUGIN_IMPORT extern char mysql_unpacked_real_data_home[];