From 24efee9100f4666ac61e1ac317e3d0fd12a5265c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 21 Dec 2017 18:00:24 +0200 Subject: [PATCH 1/2] Follow up to MDEV-12366: FLUSH privileges can break hierarchy of roles A suggestion to make role propagation simpler from serg@mariadb.org. Instead of gathering the leaf roles in an array, which for very wide graphs could potentially mean a big part of the whole roles schema, keep the previous logic. When finally merging a role, set its counter to something positive. This will effectively mean that a role has been merged, thus a random pass through roles hash that touches a previously merged role won't cause the problem described in MDEV-12366 any more, as propagate_role_grants_action will stop attempting to merge from that role. --- sql/sql_acl.cc | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 27292105dde..0e4a0cef591 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -5366,6 +5366,8 @@ static int merge_role_privileges(ACL_ROLE *role __attribute__((unused)), if (--grantee->counter) return 1; // don't recurse into grantee just yet + grantee->counter= 1; // Mark the grantee as merged. + /* if we'll do db/table/routine privileges, create a hash of role names */ role_hash_t role_hash(role_key); if (data->what != PRIVS_TO_MERGE::GLOBAL) @@ -6555,14 +6557,16 @@ end_index_init: DBUG_RETURN(return_val); } -static my_bool collect_leaf_roles(void *role_ptr, - void *roles_array) +static my_bool propagate_role_grants_action(void *role_ptr, + void *ptr __attribute__((unused))) { ACL_ROLE *role= static_cast(role_ptr); - Dynamic_array *array= - static_cast *>(roles_array); - if (!role->counter) - array->push(role); + if (role->counter) + return 0; + + mysql_mutex_assert_owner(&acl_cache->lock); + PRIVS_TO_MERGE data= { PRIVS_TO_MERGE::ALL, 0, 0 }; + traverse_role_graph_up(role, &data, NULL, merge_role_privileges); return 0; } @@ -6645,15 +6649,7 @@ my_bool grant_reload(THD *thd) } mysql_mutex_lock(&acl_cache->lock); - Dynamic_array leaf_roles; - my_hash_iterate(&acl_roles, collect_leaf_roles, &leaf_roles); - PRIVS_TO_MERGE data= { PRIVS_TO_MERGE::ALL, 0, 0 }; - for (size_t i= 0; i < leaf_roles.elements(); i++) - { - traverse_role_graph_up(leaf_roles.at(i), &data, NULL, - merge_role_privileges); - } - + my_hash_iterate(&acl_roles, propagate_role_grants_action, NULL); mysql_mutex_unlock(&acl_cache->lock); mysql_rwlock_unlock(&LOCK_grant); From 4b8cd4536a2c6376284288cd4617369a5e48236a Mon Sep 17 00:00:00 2001 From: Sergey Vojtovich Date: Fri, 22 Dec 2017 14:03:25 +0400 Subject: [PATCH 2/2] MDEV-13626 Merge InnoDB test cases from MySQL 5.7 Coverage for temporary tables modifications in read-only transactions. Introduced in 5.7 by 325cdf426 --- mysql-test/suite/innodb/r/innodb.result | 80 +++++++++++++++++++++++++ mysql-test/suite/innodb/t/innodb.test | 70 ++++++++++++++++++++++ 2 files changed, 150 insertions(+) diff --git a/mysql-test/suite/innodb/r/innodb.result b/mysql-test/suite/innodb/r/innodb.result index cded1f98fb3..159043a7170 100644 --- a/mysql-test/suite/innodb/r/innodb.result +++ b/mysql-test/suite/innodb/r/innodb.result @@ -3143,3 +3143,83 @@ show status like "handler_read_key"; Variable_name Value Handler_read_key 0 drop table t1; +CREATE TABLE t1 (c1 INT) ENGINE=InnoDB; +CREATE TEMPORARY TABLE t2 (c1 INT) ENGINE=InnoDB; +START TRANSACTION READ ONLY; +INSERT INTO t2 VALUES(0); +INSERT INTO t1 VALUES(0); +ERROR 25006: Cannot execute statement in a READ ONLY transaction. +ROLLBACK; +SELECT * FROM t1; +c1 +SELECT * FROM t2; +c1 +START TRANSACTION READ ONLY; +INSERT INTO t1 VALUES(0); +ERROR 25006: Cannot execute statement in a READ ONLY transaction. +INSERT INTO t2 VALUES(1); +COMMIT; +SET TRANSACTION READ ONLY; +START TRANSACTION; +INSERT INTO t2 VALUES(3); +INSERT INTO t1 VALUES(0); +ERROR 25006: Cannot execute statement in a READ ONLY transaction. +COMMIT; +SELECT * FROM t1; +c1 +SELECT * FROM t2; +c1 +1 +3 +DROP TABLE t2; +CREATE TEMPORARY TABLE t2 ( +c1 INT AUTO_INCREMENT PRIMARY KEY, +c2 INT, INDEX idx(c2)) ENGINE=InnoDB; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TEMPORARY TABLE `t2` ( + `c1` int(11) NOT NULL AUTO_INCREMENT, + `c2` int(11) DEFAULT NULL, + PRIMARY KEY (`c1`), + KEY `idx` (`c2`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +START TRANSACTION READ ONLY; +INSERT INTO t2 VALUES(NULL,1),(NULL,2),(NULL,3); +INSERT INTO t1 VALUES(0); +ERROR 25006: Cannot execute statement in a READ ONLY transaction. +ROLLBACK; +SELECT * FROM t1; +c1 +SELECT * FROM t2; +c1 c2 +START TRANSACTION READ ONLY; +INSERT INTO t1 VALUES(0); +ERROR 25006: Cannot execute statement in a READ ONLY transaction. +INSERT INTO t2 VALUES(NULL,1),(NULL,2),(NULL,3); +COMMIT; +SET TRANSACTION READ ONLY; +START TRANSACTION; +INSERT INTO t2 VALUES(NULL,1),(NULL,2),(NULL,3); +INSERT INTO t1 VALUES(0); +ERROR 25006: Cannot execute statement in a READ ONLY transaction. +COMMIT; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TEMPORARY TABLE `t2` ( + `c1` int(11) NOT NULL AUTO_INCREMENT, + `c2` int(11) DEFAULT NULL, + PRIMARY KEY (`c1`), + KEY `idx` (`c2`) +) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=latin1 +SELECT * FROM t1; +c1 +SELECT * FROM t2; +c1 c2 +4 1 +7 1 +5 2 +8 2 +6 3 +9 3 +DROP TABLE t1; +DROP TABLE t2; diff --git a/mysql-test/suite/innodb/t/innodb.test b/mysql-test/suite/innodb/t/innodb.test index b519c48b23d..c3b6ed7d0a7 100644 --- a/mysql-test/suite/innodb/t/innodb.test +++ b/mysql-test/suite/innodb/t/innodb.test @@ -2528,6 +2528,76 @@ select f1 from t1; show status like "handler_read_key"; drop table t1; +# +# Test handling of writes to TEMPORARY tables for read-only transactions +# +CREATE TABLE t1 (c1 INT) ENGINE=InnoDB; +CREATE TEMPORARY TABLE t2 (c1 INT) ENGINE=InnoDB; + +# Check that the rollback works +START TRANSACTION READ ONLY; +INSERT INTO t2 VALUES(0); +--error ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION +INSERT INTO t1 VALUES(0); +ROLLBACK; + +SELECT * FROM t1; +SELECT * FROM t2; + +START TRANSACTION READ ONLY; +--error ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION +INSERT INTO t1 VALUES(0); +INSERT INTO t2 VALUES(1); +COMMIT; + +SET TRANSACTION READ ONLY; +START TRANSACTION; +INSERT INTO t2 VALUES(3); +--error ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION +INSERT INTO t1 VALUES(0); +COMMIT; + +SELECT * FROM t1; +SELECT * FROM t2; + +DROP TABLE t2; + +# This time with some indexes +CREATE TEMPORARY TABLE t2 ( + c1 INT AUTO_INCREMENT PRIMARY KEY, + c2 INT, INDEX idx(c2)) ENGINE=InnoDB; + +SHOW CREATE TABLE t2; + +# Check that the rollback works +START TRANSACTION READ ONLY; +INSERT INTO t2 VALUES(NULL,1),(NULL,2),(NULL,3); +--error ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION +INSERT INTO t1 VALUES(0); +ROLLBACK; + +SELECT * FROM t1; +SELECT * FROM t2; + +START TRANSACTION READ ONLY; +--error ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION +INSERT INTO t1 VALUES(0); +INSERT INTO t2 VALUES(NULL,1),(NULL,2),(NULL,3); +COMMIT; + +SET TRANSACTION READ ONLY; +START TRANSACTION; +INSERT INTO t2 VALUES(NULL,1),(NULL,2),(NULL,3); +--error ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION +INSERT INTO t1 VALUES(0); +COMMIT; + +SHOW CREATE TABLE t2; +SELECT * FROM t1; +SELECT * FROM t2; + +DROP TABLE t1; +DROP TABLE t2; ####################################################################### # # # Please, DO NOT TOUCH this file as well as the innodb.result file. #