List:Commits« Previous MessageNext Message »
From:Kristofer Pettersson Date:October 19 2009 2:58pm
Subject:bzr commit into mysql-5.4.5-next-mr branch (kristofer.pettersson:2888)
Bug#27145
View as plain text  
#At file:///Users/thek/Development/mysql-next-mr-runtime/ based on
revid:alik@stripped

 2888 Kristofer Pettersson	2009-10-19
      Bug#27145 EXTRA_ACL troubles
      
      The flag EXTRA_ACL is used in conjugation with our access checks, yet it is
      not clear what impact this flag has.
      This is a code clean up which replaces use of EXTRA_ACL with an explicit
      function parameter.
      The patch also fixes privilege checks for:
      - SHOW CREATE TABLE: The new privilege requirement is any privilege on
        the table-level.
      - CHECKSUM TABLE: Requires SELECT on the table level.
      - SHOW CREATE VIEW: Requires SHOW_VIEW and SELECT on the table level
        (just as the manual claims)
      - SHOW INDEX: Requires any privilege on any column combination.
     @ mysql-test/r/grant.result
        * Error message now shows correct command (SHOW instead of SELECT)
     @ mysql-test/r/grant2.result
        * Error message now shows correct command (SHOW instead of SELECT)
     @ mysql-test/r/grant4.result
        * This test file tests privilege requirements for
          SHOW COLUMNS
          CREATE TABLE .. LIKE
          SHOW CREATE TABLE
          SHOW INDEX
          CHECKSUM TABLE
          SHOW CREATE VIEW
     @ mysql-test/r/information_schema_db.result
        * Added SELECT privilege to testdb_2 as
          SHOW CREATE VIEW now demands this privilege
          as well as SHOW VIEW.
     @ mysql-test/r/outfile.result
        * Changed error code
     @ mysql-test/r/view_grant.result
        * Additional SELECT privilege is now needed
          for SHOW CREATE VIEW
     @ mysql-test/t/grant4.test
        * This test file tests privilege requirements for
          SHOW COLUMNS
          CREATE TABLE .. LIKE
          SHOW CREATE TABLE
          SHOW INDEX
          CHECKSUM TABLE
          SHOW CREATE VIEW
     @ mysql-test/t/information_schema_db.test
        * Added SELECT privilege to testdb_2 as
          SHOW CREATE VIEW now demands this privilege
          as well as SHOW VIEW.
     @ mysql-test/t/outfile.test
        * Changed error code
     @ mysql-test/t/view_grant.test
        * Additional SELECT privilege is now needed
          for SHOW CREATE VIEW
     @ sql/mysql_priv.h
        * Replaced EXTRA_ACL with a parameter
     @ sql/sp_head.cc
        * Replaced EXTRA_ACL with a parameter
     @ sql/sql_acl.cc
        * Converted function documentation to doxygen and clarified some behaviors.
        * Changed value from uint to bool to better reflect its meaning.
        * Removed pointless variable orig_want_access
        * Added function has_any_table_level_privileges to help with requirements
          checks during SHOW CREATE TABLE.
     @ sql/sql_acl.h
        * changed signature of check_grant()
        * introduced access control function has_any_table_leevl_privileges()
     @ sql/sql_base.cc
        * Check_table_access has new signature
     @ sql/sql_cache.cc
        * Check_table_access has new signature
     @ sql/sql_parse.cc
        * Rewrote function documentation in doxygen comments for: check_access,
          check_table_acces, check_grant.
        * Removed EXTRA_ACL flag where it doesn't hold any meaningful purpose anymore
          and replaced it with a function parameter where any privileges on any column
          combination would satisfy the requirement.
        * Fixed privilege check for SHOW COLUMNS and SHOW INDEX
        * Modified check_table_access to gain clarity in what EXTRA_ACL actually does.
        * Modified check_access to gain clarity in what EXTRA_ACL actually does.
        * Fixed privilege check for CREATE TABLE .. LIKE .. ; It now requires SELECT
          privileges on the table.
        * Fixed privilege check for SHOW CREATE TABLE ..; It now requires any privilege
          on the table level.
     @ sql/sql_plugin.cc
        * check_table_access has new signature
     @ sql/sql_prepare.cc
        * check_table_access has new signature
     @ sql/sql_show.cc
        * check_table_access has new signature
     @ sql/sql_trigger.cc
        * check_table_access has new signature
     @ sql/sql_update.cc
        * check grant has new signature
     @ sql/sql_view.cc
        * check_table_access has new signature

    added:
      mysql-test/r/grant4.result
      mysql-test/t/grant4.test
    modified:
      mysql-test/r/grant.result
      mysql-test/r/grant2.result
      mysql-test/r/information_schema_db.result
      mysql-test/r/outfile.result
      mysql-test/r/view_grant.result
      mysql-test/t/information_schema_db.test
      mysql-test/t/outfile.test
      mysql-test/t/view_grant.test
      sql/mysql_priv.h
      sql/sp_head.cc
      sql/sql_acl.cc
      sql/sql_acl.h
      sql/sql_base.cc
      sql/sql_cache.cc
      sql/sql_parse.cc
      sql/sql_plugin.cc
      sql/sql_prepare.cc
      sql/sql_show.cc
      sql/sql_trigger.cc
      sql/sql_update.cc
      sql/sql_view.cc
=== modified file 'mysql-test/r/grant.result'
--- a/mysql-test/r/grant.result	2009-06-15 15:53:45 +0000
+++ b/mysql-test/r/grant.result	2009-10-19 12:58:13 +0000
@@ -914,13 +914,13 @@ SHOW CREATE VIEW  mysqltest2.v_ny;
 View	Create View	character_set_client	collation_connection
 v_ny	CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW
`mysqltest2`.`v_ny` AS select `mysqltest2`.`t_nn`.`c1` AS `c1` from
`mysqltest2`.`t_nn`	latin1	latin1_swedish_ci
 SHOW CREATE TABLE mysqltest3.t_nn;
-ERROR 42000: SELECT command denied to user 'mysqltest_1'@'localhost' for table 't_nn'
+ERROR 42000: SHOW command denied to user 'mysqltest_1'@'localhost' for table 't_nn'
 SHOW CREATE VIEW  mysqltest3.t_nn;
 ERROR 42000: SELECT command denied to user 'mysqltest_1'@'localhost' for table 't_nn'
 SHOW CREATE VIEW  mysqltest3.v_nn;
 ERROR 42000: SELECT command denied to user 'mysqltest_1'@'localhost' for table 'v_nn'
 SHOW CREATE TABLE mysqltest3.v_nn;
-ERROR 42000: SELECT command denied to user 'mysqltest_1'@'localhost' for table 'v_nn'
+ERROR 42000: SHOW command denied to user 'mysqltest_1'@'localhost' for table 'v_nn'
 SHOW CREATE TABLE mysqltest2.t_nn;
 Table	Create Table
 t_nn	CREATE TABLE `t_nn` (

=== modified file 'mysql-test/r/grant2.result'
--- a/mysql-test/r/grant2.result	2009-02-27 08:03:47 +0000
+++ b/mysql-test/r/grant2.result	2009-10-19 12:58:13 +0000
@@ -391,7 +391,7 @@ grant all on mysqltest_1.* to mysqltest_
 use mysqltest_2;
 create table t1 (i int);
 show create table mysqltest_2.t1;
-ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't1'
+ERROR 42000: SHOW command denied to user 'mysqltest_u1'@'localhost' for table 't1'
 create table t1 like mysqltest_2.t1;
 ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't1'
 grant select on mysqltest_2.t1 to mysqltest_u1@localhost;

=== added file 'mysql-test/r/grant4.result'
--- a/mysql-test/r/grant4.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/r/grant4.result	2009-10-19 12:58:13 +0000
@@ -0,0 +1,123 @@
+drop database if exists mysqltest_db1;
+create database mysqltest_db1;
+use mysqltest_db1;
+create table t_column_priv_only (a int, b int);
+create table t_select_priv like t_column_priv_only;
+create table t_no_priv like t_column_priv_only;
+grant all privileges on test.* to mysqltest_u1@localhost;
+grant insert (a) on mysqltest_db1.t_column_priv_only to mysqltest_u1@localhost;
+grant select on mysqltest_db1.t_select_priv to mysqltest_u1@localhost;
+** Connect as restricted user mysqltest_u1.
+
+** Test column level privileges only. No SELECT privileges on the table.
+** INSERT INTO ... VALUES ...
+** Attempting to insert values to a table with only column privileges
+** should work.
+insert into mysqltest_db1.t_column_priv_only (a) VALUES (1);
+
+** SHOW COLUMNS
+** Should succeed because we have privileges (any) on at least one of the columns.
+select column_name as 'Field',column_type as 'Type',is_nullable as 'Null',column_key as
'Key',column_default as 'Default',extra as 'Extra' from information_schema.columns where
table_schema='mysqltest_db1' and table_name='t_column_priv_only';
+Field	Type	Null	Key	Default	Extra
+a	int(11)	YES		NULL	
+show columns from mysqltest_db1.t_column_priv_only;
+Field	Type	Null	Key	Default	Extra
+a	int(11)	YES		NULL	
+** SHOW COLUMNS
+** Should fail because there are no privileges on any column combination.
+show columns from mysqltest_db1.t_no_priv;
+ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table
't_no_priv'
+** However, select from I_S.COLUMNS will succeed but not show anything:
+select column_name as 'Field',column_type as 'Type',is_nullable as 'Null',column_key as
'Key',column_default as 'Default',extra as 'Extra' from information_schema.columns where
table_schema='mysqltest_db1' and table_name='t_no_priv';
+Field	Type	Null	Key	Default	Extra
+
+** CREATE TABLE ... LIKE ... require SELECT privleges and will fail.
+create table test.t_no_priv like mysqltest_db1.column_priv_only;
+ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table
'column_priv_only'
+
+** Just to be sure... SELECT also fails.
+select * from mysqltest_db1.t_column_priv_only;
+ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table
't_column_priv_only'
+
+** SHOW CREATE TABLE ... require any privileges on all columns (the entire table).
+** First we try and fail on a table with only one column privilege.
+show create table mysqltest_db1.t_column_priv_only;
+ERROR 42000: SHOW command denied to user 'mysqltest_u1'@'localhost' for table
't_column_priv_only'
+
+** Now we do the same on a table with SELECT privileges.
+
+** SHOW COLUMNS
+** Success because we got some privileges on the table (SELECT_ACL)
+show columns from mysqltest_db1.t_select_priv;
+Field	Type	Null	Key	Default	Extra
+a	int(11)	YES		NULL	
+b	int(11)	YES		NULL	
+
+** CREATE TABLE ... LIKE ... require SELECT privleges and will SUCCEED.
+drop table if exists test.t_duplicated;
+create table test.t_duplicated like mysqltest_db1.t_select_priv;
+drop table test.t_duplicated;
+
+** SHOW CREATE TABLE will succeed because we have a privilege on all columns in the table
(table-level privilege).
+show create table mysqltest_db1.t_select_priv;
+Table	Create Table
+t_select_priv	CREATE TABLE `t_select_priv` (
+  `a` int(11) DEFAULT NULL,
+  `b` int(11) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+
+** SHOW CREATE TABLE will fail if there is no grants at all: 
+show create table mysqltest_db1.t_no_priv;
+ERROR 42000: SHOW command denied to user 'mysqltest_u1'@'localhost' for table 't_no_priv'
+
+use mysqltest_db1;
+CREATE TABLE t5 (s1 INT);
+CREATE INDEX i ON t5 (s1);
+CREATE TABLE t6 (s1 INT, s2 INT);
+CREATE VIEW v5 AS SELECT * FROM t5;
+CREATE VIEW v6 AS SELECT * FROM t6;
+CREATE VIEW v2 AS SELECT * FROM t_select_priv;
+CREATE VIEW v3 AS SELECT * FROM t_select_priv;
+CREATE INDEX i ON t6 (s1);
+GRANT UPDATE (s2) ON t6 to mysqltest_u1@localhost;
+GRANT UPDATE (s2) ON v6 to mysqltest_u1@localhost;
+GRANT SHOW VIEW ON v2 to mysqltest_u1@localhost;
+GRANT SHOW VIEW, SELECT ON v3 to mysqltest_u1@localhost;
+use mysqltest_db1;
+** Connect as restricted user mysqltest_u1.
+** SELECT FROM INFORMATION_SCHEMA.STATISTICS will succeed because any privileges will do
(authentication is enough).
+SELECT * FROM INFORMATION_SCHEMA.STATISTICS WHERE table_name='t5';
+TABLE_CATALOG	TABLE_SCHEMA	TABLE_NAME	NON_UNIQUE	INDEX_SCHEMA	INDEX_NAME	SEQ_IN_INDEX	COLUMN_NAME	COLLATION	CARDINALITY	SUB_PART	PACKED	NULLABLE	INDEX_TYPE	COMMENT
+NULL	mysqltest_db1	t5	1	mysqltest_db1	i	1	s1	A	NULL	NULL	NULL	YES	BTREE	
+** SHOW INDEX FROM t5 will fail because we don't have any privileges on any column
combination.
+SHOW INDEX FROM t5;
+ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't5'
+** SHOW INDEX FROM t6 will succeed because there exist a privilege on a column
combination on t6.
+SHOW INDEX FROM t6;
+Table	Non_unique	Key_name	Seq_in_index	Column_name	Collation	Cardinality	Sub_part	Packed	Null	Index_type	Comment
+t6	1	i	1	s1	A	NULL	NULL	NULL	YES	BTREE	
+** CHECK TABLE requires any privilege on any column combination and should succeed for
t6:
+CHECK TABLE t6;
+Table	Op	Msg_type	Msg_text
+mysqltest_db1.t6	check	status	OK
+** With no privileges access is naturally denied:
+CHECK TABLE t5;
+ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't5'
+** CHECKSUM TABLE requires SELECT privileges on the table. The following should fail:
+CHECKSUM TABLE t6;
+ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't6'
+** And this should work:
+CHECKSUM TABLE t_select_priv;
+Table	Checksum
+mysqltest_db1.t_select_priv	0
+SHOW CREATE VIEW v5;
+ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 'v5'
+SHOW CREATE VIEW v6;
+ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 'v6'
+SHOW CREATE VIEW v2;
+ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 'v2'
+SHOW CREATE VIEW v3;
+View	Create View	character_set_client	collation_connection
+v3	CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v3`
AS select `t_select_priv`.`a` AS `a`,`t_select_priv`.`b` AS `b` from
`t_select_priv`	latin1	latin1_swedish_ci
+drop database mysqltest_db1;
+drop user mysqltest_u1@localhost;

=== modified file 'mysql-test/r/information_schema_db.result'
--- a/mysql-test/r/information_schema_db.result	2009-08-07 20:04:53 +0000
+++ b/mysql-test/r/information_schema_db.result	2009-10-19 12:58:13 +0000
@@ -3,7 +3,7 @@ drop view if exists v1,v2;
 drop function if exists f1;
 drop function if exists f2;
 use INFORMATION_SCHEMA;
-show tables where Tables_in_information_schema not like "Innodb%";
+show tables where Tables_in_information_schema NOT LIKE 'Innodb%';
 Tables_in_information_schema
 CHARACTER_SETS
 COLLATIONS
@@ -119,12 +119,12 @@ create table t1 (f1 char(4));
 create view v1 as select f1 from t1;
 grant insert on v1 to testdb_2@localhost;
 create view v5 as select f1 from t1;
-grant show view on v5 to testdb_2@localhost;
+grant select, show view on v5 to testdb_2@localhost;
 create definer=`no_such_user`@`no_such_host` view v6 as select f1 from t1;
 ERROR 42000: Access denied; you need the SUPER privilege for this operation
 use testdb_1;
 create view v6 as select f1 from t1;
-grant show view on v6 to testdb_2@localhost;
+grant select, show view on v6 to testdb_2@localhost;
 create table t2 (f1 char(4));
 create definer=`no_such_user`@`no_such_host` view v7 as select * from t2;
 Warnings:
@@ -152,11 +152,13 @@ create view v2 as select f1 from testdb_
 create view v4 as select f1,f2 from testdb_1.v3;
 show fields from testdb_1.v5;
 Field	Type	Null	Key	Default	Extra
+f1	char(4)	YES		NULL	
 show create view testdb_1.v5;
 View	Create View	character_set_client	collation_connection
 v5	CREATE ALGORITHM=UNDEFINED DEFINER=`testdb_1`@`localhost` SQL SECURITY DEFINER VIEW
`testdb_1`.`v5` AS select `testdb_1`.`t1`.`f1` AS `f1` from
`testdb_1`.`t1`	latin1	latin1_swedish_ci
 show fields from testdb_1.v6;
 Field	Type	Null	Key	Default	Extra
+f1	char(4)	YES		NULL	
 show create view testdb_1.v6;
 View	Create View	character_set_client	collation_connection
 v6	CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW
`testdb_1`.`v6` AS select `testdb_1`.`t1`.`f1` AS `f1` from
`testdb_1`.`t1`	latin1	latin1_swedish_ci
@@ -171,9 +173,9 @@ v7	CREATE ALGORITHM=UNDEFINED DEFINER=`n
 Warnings:
 Warning	1356	View 'testdb_1.v7' references invalid table(s) or column(s) or function(s)
or definer/invoker of view lack rights to use them
 revoke insert(f1) on v3 from testdb_2@localhost;
-revoke show view on v5 from testdb_2@localhost;
+revoke select,show view on v5 from testdb_2@localhost;
 use testdb_1;
-revoke show view on v6 from testdb_2@localhost;
+revoke select,show view on v6 from testdb_2@localhost;
 show fields from testdb_1.v5;
 ERROR 42000: SELECT command denied to user 'testdb_2'@'localhost' for table 'v5'
 show create view testdb_1.v5;
@@ -202,7 +204,7 @@ show create view v2;
 View	Create View	character_set_client	collation_connection
 v2	CREATE ALGORITHM=UNDEFINED DEFINER=`testdb_2`@`localhost` SQL SECURITY DEFINER VIEW
`v2` AS select `v1`.`f1` AS `f1` from `testdb_1`.`v1`	latin1	latin1_swedish_ci
 show create view testdb_1.v1;
-ERROR 42000: SHOW VIEW command denied to user 'testdb_2'@'localhost' for table 'v1'
+ERROR 42000: SELECT command denied to user 'testdb_2'@'localhost' for table 'v1'
 select table_name from information_schema.columns a
 where a.table_name = 'v2';
 table_name

=== modified file 'mysql-test/r/outfile.result'
Binary files a/mysql-test/r/outfile.result	2009-02-03 13:45:17 +0000 and
b/mysql-test/r/outfile.result	2009-10-19 12:58:13 +0000 differ

=== modified file 'mysql-test/r/view_grant.result'
--- a/mysql-test/r/view_grant.result	2009-08-21 14:41:48 +0000
+++ b/mysql-test/r/view_grant.result	2009-10-19 12:58:13 +0000
@@ -799,12 +799,12 @@ CREATE USER u29908_1@localhost;
 CREATE DEFINER = u29908_1@localhost VIEW v1 AS SELECT f1 FROM t1;
 CREATE DEFINER = u29908_1@localhost SQL SECURITY INVOKER VIEW v2 AS
 SELECT f1 FROM t1;
-GRANT DROP, CREATE VIEW, SHOW VIEW ON mysqltest_29908.v1 TO u29908_1@localhost;
-GRANT DROP, CREATE VIEW, SHOW VIEW ON mysqltest_29908.v2 TO u29908_1@localhost;
+GRANT SELECT, DROP, CREATE VIEW, SHOW VIEW ON mysqltest_29908.v1 TO u29908_1@localhost;
+GRANT SELECT, DROP, CREATE VIEW, SHOW VIEW ON mysqltest_29908.v2 TO u29908_1@localhost;
 GRANT SELECT ON mysqltest_29908.t1 TO u29908_1@localhost;
 CREATE USER u29908_2@localhost;
-GRANT DROP, CREATE VIEW ON mysqltest_29908.v1 TO u29908_2@localhost;
-GRANT DROP, CREATE VIEW, SHOW VIEW ON mysqltest_29908.v2 TO u29908_2@localhost;
+GRANT SELECT, DROP, CREATE VIEW ON mysqltest_29908.v1 TO u29908_2@localhost;
+GRANT SELECT, DROP, CREATE VIEW, SHOW VIEW ON mysqltest_29908.v2 TO u29908_2@localhost;
 GRANT SELECT ON mysqltest_29908.t1 TO u29908_2@localhost;
 ALTER VIEW v1 AS SELECT f2 FROM t1;
 ERROR 42000: Access denied; you need the SUPER privilege for this operation

=== added file 'mysql-test/t/grant4.test'
--- a/mysql-test/t/grant4.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/grant4.test	2009-10-19 12:58:13 +0000
@@ -0,0 +1,146 @@
+--source include/not_embedded.inc
+
+# Setup database, tables and user accounts
+--disable_warnings
+drop database if exists mysqltest_db1;
+--enable_warnings
+create database mysqltest_db1;
+use mysqltest_db1;
+create table t_column_priv_only (a int, b int);
+create table t_select_priv like t_column_priv_only;
+create table t_no_priv like t_column_priv_only;
+grant all privileges on test.* to mysqltest_u1@localhost;
+grant insert (a) on mysqltest_db1.t_column_priv_only to mysqltest_u1@localhost;
+grant select on mysqltest_db1.t_select_priv to mysqltest_u1@localhost;
+
+--echo ** Connect as restricted user mysqltest_u1.
+--echo
+connect (con1,localhost,mysqltest_u1,,);
+connection con1;
+
+########################################################################
+--echo ** Test column level privileges only. No SELECT privileges on the table.
+--echo ** INSERT INTO ... VALUES ...
+--echo ** Attempting to insert values to a table with only column privileges
+--echo ** should work.
+insert into mysqltest_db1.t_column_priv_only (a) VALUES (1);
+--echo
+
+#########################################################################
+--echo ** SHOW COLUMNS
+--echo ** Should succeed because we have privileges (any) on at least one of the columns.
+select column_name as 'Field',column_type as 'Type',is_nullable as 'Null',column_key as
'Key',column_default as 'Default',extra as 'Extra' from information_schema.columns where
table_schema='mysqltest_db1' and table_name='t_column_priv_only';
+show columns from mysqltest_db1.t_column_priv_only;
+#########################################################################
+--echo ** SHOW COLUMNS
+--echo ** Should fail because there are no privileges on any column combination.
+--error 1142
+show columns from mysqltest_db1.t_no_priv;
+--echo ** However, select from I_S.COLUMNS will succeed but not show anything:
+select column_name as 'Field',column_type as 'Type',is_nullable as 'Null',column_key as
'Key',column_default as 'Default',extra as 'Extra' from information_schema.columns where
table_schema='mysqltest_db1' and table_name='t_no_priv';
+--echo 
+#########################################################################
+--echo ** CREATE TABLE ... LIKE ... require SELECT privleges and will fail.
+--error 1142
+create table test.t_no_priv like mysqltest_db1.column_priv_only;
+--echo  
+#########################################################################
+--echo ** Just to be sure... SELECT also fails.
+--error 1142
+select * from mysqltest_db1.t_column_priv_only;
+--echo 
+#########################################################################
+--echo ** SHOW CREATE TABLE ... require any privileges on all columns (the entire table).
+--echo ** First we try and fail on a table with only one column privilege.
+--error 1142
+show create table mysqltest_db1.t_column_priv_only;
+--echo
+#########################################################################
+--echo ** Now we do the same on a table with SELECT privileges.
+--echo
+#########################################################################
+--echo ** SHOW COLUMNS
+--echo ** Success because we got some privileges on the table (SELECT_ACL)
+show columns from mysqltest_db1.t_select_priv;
+--echo
+#########################################################################
+--echo ** CREATE TABLE ... LIKE ... require SELECT privleges and will SUCCEED.
+--disable_warnings
+drop table if exists test.t_duplicated;
+--enable_warnings
+create table test.t_duplicated like mysqltest_db1.t_select_priv;
+drop table test.t_duplicated;
+--echo
+#########################################################################
+--echo ** SHOW CREATE TABLE will succeed because we have a privilege on all columns in
the table (table-level privilege).
+show create table mysqltest_db1.t_select_priv;
+--echo
+#########################################################################
+--echo ** SHOW CREATE TABLE will fail if there is no grants at all: 
+--error 1142
+show create table mysqltest_db1.t_no_priv;
+--echo
+
+connection default;
+
+#
+# SHOW INDEX
+#
+use mysqltest_db1;
+CREATE TABLE t5 (s1 INT);
+CREATE INDEX i ON t5 (s1);
+CREATE TABLE t6 (s1 INT, s2 INT);
+CREATE VIEW v5 AS SELECT * FROM t5;
+CREATE VIEW v6 AS SELECT * FROM t6;
+CREATE VIEW v2 AS SELECT * FROM t_select_priv;
+CREATE VIEW v3 AS SELECT * FROM t_select_priv;
+CREATE INDEX i ON t6 (s1);
+GRANT UPDATE (s2) ON t6 to mysqltest_u1@localhost;
+GRANT UPDATE (s2) ON v6 to mysqltest_u1@localhost;
+GRANT SHOW VIEW ON v2 to mysqltest_u1@localhost;
+GRANT SHOW VIEW, SELECT ON v3 to mysqltest_u1@localhost;
+
+connection con1;
+use mysqltest_db1;
+--echo ** Connect as restricted user mysqltest_u1.
+--echo ** SELECT FROM INFORMATION_SCHEMA.STATISTICS will succeed because any privileges
will do (authentication is enough).
+#
+# this result is wrong. reported as bug#34104
+#
+SELECT * FROM INFORMATION_SCHEMA.STATISTICS WHERE table_name='t5';
+#
+# Bug27145 EXTRA_ACL trouble
+#
+--echo ** SHOW INDEX FROM t5 will fail because we don't have any privileges on any column
combination.
+--error 1142
+SHOW INDEX FROM t5;
+--echo ** SHOW INDEX FROM t6 will succeed because there exist a privilege on a column
combination on t6.
+SHOW INDEX FROM t6;
+
+# CHECK TABLE
+--echo ** CHECK TABLE requires any privilege on any column combination and should succeed
for t6:
+CHECK TABLE t6;
+--echo ** With no privileges access is naturally denied:
+--error 1142
+CHECK TABLE t5;
+
+# CHECKSUM
+--echo ** CHECKSUM TABLE requires SELECT privileges on the table. The following should
fail:
+--error 1142
+CHECKSUM TABLE t6;
+--echo ** And this should work:
+CHECKSUM TABLE t_select_priv;
+
+# SHOW CREATE VIEW
+--error 1142
+SHOW CREATE VIEW v5;
+--error 1142
+SHOW CREATE VIEW v6;
+--error 1142
+SHOW CREATE VIEW v2;
+SHOW CREATE VIEW v3;
+
+connection default;
+disconnect con1;
+drop database mysqltest_db1;
+drop user mysqltest_u1@localhost;

=== modified file 'mysql-test/t/information_schema_db.test'
--- a/mysql-test/t/information_schema_db.test	2009-08-07 20:04:53 +0000
+++ b/mysql-test/t/information_schema_db.test	2009-10-19 12:58:13 +0000
@@ -1,5 +1,5 @@
 # this test mostly test privilege control (what doesn't work
-# in the embedded server by default). So disabled in embedded-server mode
+# in the embedded server by default). So skip the test in embedded-server mode.
 -- source include/not_embedded.inc
 
 -- source include/testdb_only.inc
@@ -13,7 +13,7 @@ drop function if exists f2;
 
 use INFORMATION_SCHEMA;
 --replace_result Tables_in_INFORMATION_SCHEMA Tables_in_information_schema
-show tables where Tables_in_information_schema not like "Innodb%";
+show tables where Tables_in_INFORMATION_SCHEMA NOT LIKE 'Innodb%';
 --replace_result 'Tables_in_INFORMATION_SCHEMA (T%)' 'Tables_in_information_schema (T%)'
 show tables from INFORMATION_SCHEMA like 'T%';
 create database `inf%`;
@@ -123,7 +123,7 @@ create view v1 as select f1 from t1;
 grant insert on v1 to testdb_2@localhost;
 
 create view v5 as select f1 from t1;
-grant show view on v5 to testdb_2@localhost;
+grant select, show view on v5 to testdb_2@localhost;
 
 --error ER_SPECIFIC_ACCESS_DENIED_ERROR
 create definer=`no_such_user`@`no_such_host` view v6 as select f1 from t1;
@@ -131,7 +131,7 @@ create definer=`no_such_user`@`no_such_h
 connection default;
 use testdb_1;
 create view v6 as select f1 from t1;
-grant show view on v6 to testdb_2@localhost;
+grant select, show view on v6 to testdb_2@localhost;
 
 create table t2 (f1 char(4));
 create definer=`no_such_user`@`no_such_host` view v7 as select * from t2;
@@ -163,10 +163,10 @@ show fields from testdb_1.v7;
 show create view testdb_1.v7;
 
 revoke insert(f1) on v3 from testdb_2@localhost;
-revoke show view on v5 from testdb_2@localhost;
+revoke select,show view on v5 from testdb_2@localhost;
 connection default;
 use testdb_1;
-revoke show view on v6 from testdb_2@localhost;
+revoke select,show view on v6 from testdb_2@localhost;
 connection testdb_2;
 
 --error ER_TABLEACCESS_DENIED_ERROR

=== modified file 'mysql-test/t/outfile.test'
--- a/mysql-test/t/outfile.test	2009-02-03 13:45:17 +0000
+++ b/mysql-test/t/outfile.test	2009-10-19 12:58:13 +0000
@@ -109,7 +109,7 @@ create user user_1@localhost;
 grant all on mysqltest.* to user_1@localhost;
 connect (con28181_1,localhost,user_1,,mysqltest);
 
---error ER_DBACCESS_DENIED_ERROR
+--error ER_ACCESS_DENIED_ERROR
 eval select schema_name
 into outfile "../../tmp/outfile-test.4"
 fields terminated by ',' optionally enclosed by '"'

=== modified file 'mysql-test/t/view_grant.test'
--- a/mysql-test/t/view_grant.test	2009-08-21 14:41:48 +0000
+++ b/mysql-test/t/view_grant.test	2009-10-19 12:58:13 +0000
@@ -1072,12 +1072,12 @@ CREATE USER u29908_1@localhost;
 CREATE DEFINER = u29908_1@localhost VIEW v1 AS SELECT f1 FROM t1;
 CREATE DEFINER = u29908_1@localhost SQL SECURITY INVOKER VIEW v2 AS
   SELECT f1 FROM t1;
-GRANT DROP, CREATE VIEW, SHOW VIEW ON mysqltest_29908.v1 TO u29908_1@localhost;
-GRANT DROP, CREATE VIEW, SHOW VIEW ON mysqltest_29908.v2 TO u29908_1@localhost;
+GRANT SELECT, DROP, CREATE VIEW, SHOW VIEW ON mysqltest_29908.v1 TO u29908_1@localhost;
+GRANT SELECT, DROP, CREATE VIEW, SHOW VIEW ON mysqltest_29908.v2 TO u29908_1@localhost;
 GRANT SELECT ON mysqltest_29908.t1 TO u29908_1@localhost;
 CREATE USER u29908_2@localhost;
-GRANT DROP, CREATE VIEW ON mysqltest_29908.v1 TO u29908_2@localhost;
-GRANT DROP, CREATE VIEW, SHOW VIEW ON mysqltest_29908.v2 TO u29908_2@localhost;
+GRANT SELECT, DROP, CREATE VIEW ON mysqltest_29908.v1 TO u29908_2@localhost;
+GRANT SELECT, DROP, CREATE VIEW, SHOW VIEW ON mysqltest_29908.v2 TO u29908_2@localhost;
 GRANT SELECT ON mysqltest_29908.t1 TO u29908_2@localhost;
 
 connect (u2,localhost,u29908_2,,mysqltest_29908);

=== modified file 'sql/mysql_priv.h'
--- a/sql/mysql_priv.h	2009-09-17 09:20:11 +0000
+++ b/sql/mysql_priv.h	2009-10-19 12:58:13 +0000
@@ -1120,9 +1120,11 @@ bool reload_acl_and_cache(THD *thd, ulon
                           bool *write_to_binlog);
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
 bool check_access(THD *thd, ulong access, const char *db, ulong *save_priv,
-		  bool no_grant, bool no_errors, bool schema_db);
-bool check_table_access(THD *thd, ulong want_access, TABLE_LIST *tables,
-			uint number, bool no_errors);
+                  bool no_grant, bool no_errors, bool schema_db);
+bool check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
+                        bool any_combination_of_privileges_will_do,
+                        uint number,
+                        bool no_errors);
 #else
 inline bool check_access(THD *thd, ulong access, const char *db,
                          ulong *save_priv, bool no_grant, bool no_errors,
@@ -1132,8 +1134,10 @@ inline bool check_access(THD *thd, ulong
     *save_priv= GLOBAL_ACLS;
   return false;
 }
-inline bool check_table_access(THD *thd, ulong want_access, TABLE_LIST *tables,
-			uint number, bool no_errors)
+inline bool check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
+                               bool no_errors,
+                               bool any_combination_of_privileges_will_do,
+                               uint number)
 { return false; }
 #endif /*NO_EMBEDDED_ACCESS_CHECKS*/
 

=== modified file 'sql/sp_head.cc'
--- a/sql/sp_head.cc	2009-09-10 09:18:29 +0000
+++ b/sql/sp_head.cc	2009-10-19 12:58:13 +0000
@@ -2362,7 +2362,8 @@ bool check_show_routine_access(THD *thd,
   bzero((char*) &tables,sizeof(tables));
   tables.db= (char*) "mysql";
   tables.table_name= tables.alias= (char*) "proc";
-  *full_access= (!check_table_access(thd, SELECT_ACL, &tables, 1, TRUE) ||
+  *full_access= (!check_table_access(thd, SELECT_ACL, &tables, FALSE,
+                                     1, TRUE) ||
                  (!strcmp(sp->m_definer_user.str,
                           thd->security_ctx->priv_user) &&
                   !strcmp(sp->m_definer_host.str,
@@ -2808,7 +2809,7 @@ int sp_instr::exec_open_and_lock_tables(
     Check whenever we have access to tables for this statement
     and open and lock them before executing instructions core function.
   */
-  if (check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)
+  if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE)
       || open_and_lock_tables(thd, tables))
     result= -1;
   else

=== modified file 'sql/sql_acl.cc'
--- a/sql/sql_acl.cc	2009-09-17 09:20:11 +0000
+++ b/sql/sql_acl.cc	2009-10-19 12:58:13 +0000
@@ -1879,7 +1879,7 @@ static bool test_if_create_new_users(THD
 		      sctx->priv_user, tl.db, 0);
     if (!(db_access & INSERT_ACL))
     {
-      if (check_grant(thd, INSERT_ACL, &tl, 0, UINT_MAX, 1))
+      if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE))
 	create_new_users=0;
     }
   }
@@ -3835,40 +3835,52 @@ end:
   DBUG_RETURN(return_val);
 }
 
-/****************************************************************************
-  Check table level grants
 
-  SYNOPSIS
-   bool check_grant()
-   thd		Thread handler
-   want_access  Bits of privileges user needs to have
-   tables	List of tables to check. The user should have 'want_access'
-		to all tables in list.
-   show_table	<> 0 if we are in show table. In this case it's enough to have
-	        any privilege for the table
-   number	Check at most this number of tables.
-   no_errors	If 0 then we write an error. The error is sent directly to
-		the client
+/**
+  @brief Check table level grants
 
-   RETURN
-     0  ok
-     1  Error: User did not have the requested privileges
+  @param thd          Thread handler
+  @param want_access  Bits of privileges user needs to have.
+  @param tables       List of tables to check. The user should have
+                      'want_access' to all tables in list.
+  @param any_combination_will_do TRUE if it's enough to have any privilege for
+    any combination of the table columns.
+  @param number       Check at most this number of tables.
+  @param no_errors    TRUE if no error should be sent directly to the client.
+
+  If table->grant.want_privilege != 0 then the requested privileges where
+  in the set of COL_ACLS but access was not granted on the table level. As
+  a consequence an extra check of column privileges is required.
+
+  Specifically if this function returns FALSE the user has some kind of
+  privilege on a combination of columns in each table.
 
-   NOTE
-     This functions assumes that either number of tables to be inspected
+  This function is usually preceeded by check_access which establish the
+  User-, Db- and Host access rights.
+
+  @see check_access
+  @see check_table_access
+
+  @note This functions assumes that either number of tables to be inspected
      by it is limited explicitly (i.e. is is not UINT_MAX) or table list
      used and thd->lex->query_tables_own_last value correspond to each
      other (the latter should be either 0 or point to next_global member
      of one of elements of this table list).
-****************************************************************************/
+
+   @return Access status
+     @retval FALSE Access granted; But column privileges might need to be
+      checked.
+     @retval TRUE The user did not have the requested privileges on any of the
+      tables.
+
+*/
 
 bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
-		 uint show_table, uint number, bool no_errors)
+                 bool any_combination_will_do, uint number, bool no_errors)
 {
   TABLE_LIST *table, *first_not_own_table= thd->lex->first_not_own_table();
   Security_context *sctx= thd->security_ctx;
   uint i;
-  ulong orig_want_access= want_access;
   DBUG_ENTER("check_grant");
   DBUG_ASSERT(number > 0);
 
@@ -3886,7 +3898,10 @@ bool check_grant(THD *thd, ulong want_ac
        i < number  && table != first_not_own_table;
        table= table->next_global, i++)
   {
-    /* Remove SHOW_VIEW_ACL, because it will be checked during making view */
+    /*
+      Save a copy of the privileges without the SHOW_VIEW_ACL attribute.
+      It will be checked during making view.
+    */
     table->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
   }
 
@@ -3899,7 +3914,6 @@ bool check_grant(THD *thd, ulong want_ac
     sctx = test(table->security_ctx) ?
       table->security_ctx : thd->security_ctx;
 
-    want_access= orig_want_access;
     want_access&= ~sctx->master_access;
     if (!want_access)
       continue;                                 // ok
@@ -3929,8 +3943,13 @@ bool check_grant(THD *thd, ulong want_ac
       want_access &= ~table->grant.privilege;
       goto err;					// No grants
     }
-    if (show_table)
-      continue;					// We have some priv on this
+
+    /*
+      For SHOW COLUMNS, SHOW INDEX it is enough to have some
+      privileges on any column combination on the table.
+    */
+    if (any_combination_will_do)
+      continue;
 
     table->grant.grant_table=grant_table;	// Remember for column test
     table->grant.version=grant_version;
@@ -3948,7 +3967,7 @@ bool check_grant(THD *thd, ulong want_ac
     }
   }
   rw_unlock(&LOCK_grant);
-  DBUG_RETURN(0);
+  DBUG_RETURN(FALSE);
 
 err:
   rw_unlock(&LOCK_grant);
@@ -3962,7 +3981,97 @@ err:
              sctx->host_or_ip,
              table ? table->get_table_name() : "unknown");
   }
-  DBUG_RETURN(1);
+  DBUG_RETURN(TRUE);
+}
+
+
+/**
+  Check if all tables in the table list has any of the requested table level
+  privileges matching the current user.
+
+  @param thd              A pointer to the thread context.
+  @param required_access  Set of privileges to compare against.
+  @param tables[in,out]   A list of tables to be checked.
+
+  @note If the table grant hash contains any grant table, this table will be
+  attached to the corresponding TABLE_LIST object in 'tables'.
+
+  @return
+    @retval TRUE  There is a privilege on the table level granted to the
+      current user.
+    @retval FALSE There are no privileges on the table level granted to the
+     current user.
+*/
+
+bool has_any_table_level_privileges(THD *thd, ulong required_access,
+                                    TABLE_LIST *tables)
+{
+
+  Security_context *sctx;
+  GRANT_TABLE *grant_table;
+  TABLE_LIST *table;
+
+  /* For each table in tables */
+  for (table= tables; table; table= table->next_global)
+  {
+    /*
+      If this table is a VIEW, then it will supply its own security context.
+      This is because VIEWs can have a DEFINER or an INVOKER security role.
+    */
+    sctx= table->security_ctx ? table->security_ctx : thd->security_ctx;
+
+    /*
+      Get privileges from table_priv and column_priv tables by searching
+      the cache.
+    */
+    rw_rdlock(&LOCK_grant);
+    grant_table= table_hash_search(sctx->host, sctx->ip,
+                                  table->db, sctx->priv_user,
+                                  table->table_name,0);
+    rw_unlock(&LOCK_grant);
+
+    /* Stop if there are no grants for the current user */
+    if (!grant_table)
+      return FALSE;
+
+    /*
+      Save a pointer to the found grant_table in the table object.
+      This pointer can later be used to verify other access requirements
+      without having to look up the grant table in the hash.
+    */
+    table->grant.grant_table= grant_table;
+    table->grant.version=     grant_version;
+    table->grant.privilege|=  grant_table->privs;
+    /*
+      Save all privileges which might be subject to column privileges
+      but not which aren't yet granted by table level ACLs.
+      This is can later be used for column privilege checks.
+    */
+    table->grant.want_privilege= ((required_access & COL_ACLS)
+                                  & ~table->grant.privilege);
+
+    /*
+      If the requested privileges share any intersection with the current
+      table privileges we have found at least one common privilege on the
+      table level.
+    */
+    if (grant_table->privs & required_access)
+      continue; /* Check next table */
+
+    /*
+      There are no table level privileges which satisfies any of the
+      requested privileges. There might still be column privileges which
+      does though.
+    */
+    return FALSE;
+  }
+
+  /*
+    All tables in TABLE_LIST satisfy the requirement of having any
+    privilege on the table level.
+  */
+
+  return TRUE;
 }
 
 

=== modified file 'sql/sql_acl.h'
--- a/sql/sql_acl.h	2009-05-29 13:37:54 +0000
+++ b/sql/sql_acl.h	2009-10-19 12:58:13 +0000
@@ -51,7 +51,6 @@
   4. acl_init() or whatever - to define behaviour for old privilege tables
   5. sql_yacc.yy - for GRANT/REVOKE to work
 */
-#define EXTRA_ACL	(1L << 29)
 #define NO_ACCESS	(1L << 30)
 #define DB_ACLS \
 (UPDATE_ACL | SELECT_ACL | INSERT_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \
@@ -238,7 +237,7 @@ my_bool grant_init();
 void grant_free(void);
 my_bool grant_reload(THD *thd);
 bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
-		 uint show_command, uint number, bool dont_print_error);
+                 bool any_combination_will_do, uint number, bool no_errors);
 bool check_grant_column (THD *thd, GRANT_INFO *grant,
 			 const char *db_name, const char *table_name,
 			 const char *name, uint length, Security_context *sctx);
@@ -269,6 +268,9 @@ bool sp_grant_privileges(THD *thd, const
 bool check_routine_level_acl(THD *thd, const char *db, const char *name,
                              bool is_proc);
 bool is_acl_user(const char *host, const char *user);
+bool has_any_table_level_privileges(THD *thd, ulong required_access,
+                                    TABLE_LIST *tables);
+
 #ifdef NO_EMBEDDED_ACCESS_CHECKS
 #define check_grant(A,B,C,D,E,F) 0
 #define check_grant_db(A,B) 0

=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc	2009-09-17 09:20:11 +0000
+++ b/sql/sql_base.cc	2009-10-19 12:58:13 +0000
@@ -731,7 +731,7 @@ OPEN_TABLE_LIST *list_open_tables(THD *t
     table_list.table_name= share->table_name.str;
     table_list.grant.privilege=0;
 
-    if (check_table_access(thd,SELECT_ACL | EXTRA_ACL,&table_list, 1, TRUE))
+    if (check_table_access(thd,SELECT_ACL,&table_list, TRUE, 1, TRUE))
       continue;
     /* need to check if we haven't already listed it */
     for (table= open_list  ; table ; table=table->next)

=== modified file 'sql/sql_cache.cc'
--- a/sql/sql_cache.cc	2009-09-10 09:18:29 +0000
+++ b/sql/sql_cache.cc	2009-10-19 12:58:13 +0000
@@ -1545,7 +1545,7 @@ def_week_frmt: %lu, in_trans: %d, autoco
     table_list.db = table->db();
     table_list.alias= table_list.table_name= table->table();
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
-    if (check_table_access(thd,SELECT_ACL,&table_list, 1, TRUE))
+    if (check_table_access(thd,SELECT_ACL,&table_list, FALSE, 1,TRUE))
     {
       DBUG_PRINT("qcache",
 		 ("probably no SELECT access to %s.%s =>  return to normal processing",

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2009-09-17 09:20:11 +0000
+++ b/sql/sql_parse.cc	2009-10-19 12:58:13 +0000
@@ -45,7 +45,6 @@
    "FUNCTION" : "PROCEDURE")
 
 static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables);
-static bool check_show_create_table_access(THD *thd, TABLE_LIST *table);
 
 const char *any_db="*any*";	// Special symbol for check_access
 
@@ -589,7 +588,7 @@ static bool check_merge_table_access(THD
         tlist->db= db; /* purecov: inspected */
     }
     error= check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
-                              table_list, UINT_MAX, FALSE);
+                              table_list, FALSE, UINT_MAX, FALSE);
   }
   return error;
 }
@@ -1334,7 +1333,7 @@ bool dispatch_command(enum enum_server_c
     if (check_access(thd,SELECT_ACL,table_list.db,&table_list.grant.privilege,
 		     0, 0, test(table_list.schema_table)))
       break;
-    if (check_grant(thd, SELECT_ACL, &table_list, 2, UINT_MAX, 0))
+    if (check_grant(thd, SELECT_ACL, &table_list, TRUE, UINT_MAX, FALSE))
       break;
     /* init structures for VIEW processing */
     table_list.select_lex= &(thd->lex->select_lex);
@@ -2193,14 +2192,16 @@ mysql_execute_command(THD *thd)
 #endif
   case SQLCOM_SHOW_STATUS_PROC:
   case SQLCOM_SHOW_STATUS_FUNC:
-    if (!(res= check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE)))
+    if (!(res= check_table_access(thd, SELECT_ACL, all_tables, FALSE,
+                                  UINT_MAX, FALSE)))
       res= execute_sqlcom_select(thd, all_tables);
     break;
   case SQLCOM_SHOW_STATUS:
   {
     system_status_var old_status_var= thd->status_var;
     thd->initial_status_var= &old_status_var;
-    if (!(res= check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE)))
+    if (!(res= check_table_access(thd, SELECT_ACL, all_tables, FALSE,
+                                  UINT_MAX, FALSE)))
       res= execute_sqlcom_select(thd, all_tables);
     /* Don't log SHOW STATUS commands to slow query log */
     thd->server_status&= ~(SERVER_QUERY_NO_INDEX_USED |
@@ -2230,18 +2231,22 @@ mysql_execute_command(THD *thd)
   case SQLCOM_SHOW_STORAGE_ENGINES:
   case SQLCOM_SHOW_PROFILE:
   case SQLCOM_SELECT:
+  {
     thd->status_var.last_query_cost= 0.0;
+
+    /*
+      lex->exchange != NULL implies SELECT .. INTO OUTFILE and this
+      requires FILE_ACL access.
+    */
+    ulong privileges_requested= lex->exchange ? SELECT_ACL | FILE_ACL :
+      SELECT_ACL;
+
     if (all_tables)
-    {
       res= check_table_access(thd,
-                              lex->exchange ? SELECT_ACL | FILE_ACL :
-                              SELECT_ACL,
-                              all_tables, UINT_MAX, FALSE);
-    }
+                              privileges_requested,
+                              all_tables, FALSE, UINT_MAX, FALSE);
     else
-      res= check_access(thd,
-                        lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL,
-                        any_db, 0, 0, 0, 0);
+      res= check_access(thd, privileges_requested, any_db, 0, 0, 0, UINT_MAX);
 
     if (res)
       break;
@@ -2252,7 +2257,8 @@ mysql_execute_command(THD *thd)
 
     res= execute_sqlcom_select(thd, all_tables);
     break;
-  case SQLCOM_PREPARE:
+  }
+case SQLCOM_PREPARE:
   {
     mysql_sql_stmt_prepare(thd);
     break;
@@ -2268,8 +2274,8 @@ mysql_execute_command(THD *thd)
     break;
   }
   case SQLCOM_DO:
-    if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) ||
-        open_and_lock_tables(thd, all_tables))
+    if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE)
+        || open_and_lock_tables(thd, all_tables))
       goto error;
 
     res= mysql_do(thd, *lex->insert_list);
@@ -2378,8 +2384,8 @@ mysql_execute_command(THD *thd)
   case SQLCOM_BACKUP_TABLE:
   {
     DBUG_ASSERT(first_table == all_tables && first_table != 0);
-    if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) ||
-	check_global_access(thd, FILE_ACL))
+    if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE)
+        || check_global_access(thd, FILE_ACL))
       goto error; /* purecov: inspected */
     thd->enable_slow_log= opt_log_slow_admin_statements;
     res = mysql_backup_table(thd, first_table);
@@ -2390,8 +2396,8 @@ mysql_execute_command(THD *thd)
   case SQLCOM_RESTORE_TABLE:
   {
     DBUG_ASSERT(first_table == all_tables && first_table != 0);
-    if (check_table_access(thd, INSERT_ACL, all_tables, UINT_MAX, FALSE) ||
-	check_global_access(thd, FILE_ACL))
+    if (check_table_access(thd, INSERT_ACL, all_tables, FALSE, UINT_MAX, FALSE)
+        || check_global_access(thd, FILE_ACL))
       goto error; /* purecov: inspected */
     thd->enable_slow_log= opt_log_slow_admin_statements;
     res = mysql_restore_table(thd, first_table);
@@ -2490,7 +2496,7 @@ mysql_execute_command(THD *thd)
                      test(first_table->schema_table)))
       goto error;				/* purecov: inspected */
     /* Check that the first table has CREATE privilege */
-    if (check_grant(thd, CREATE_ACL, all_tables, 0, 1, 0))
+    if (check_grant(thd, CREATE_ACL, all_tables, FALSE, 1, FALSE))
       goto error;
 
     pthread_mutex_lock(&LOCK_active_mi);
@@ -2860,7 +2866,7 @@ end_with_restore_list:
 				   (TABLE_LIST *)
 				   create_info.merge_list.first))
 	goto error;				/* purecov: inspected */
-      if (check_grant(thd, priv_needed, all_tables, 0, UINT_MAX, 0))
+      if (check_grant(thd, priv_needed, all_tables, FALSE, UINT_MAX, FALSE))
         goto error;
       if (lex->name.str && !test_all_bits(priv,INSERT_ACL | CREATE_ACL))
       { // Rename of table
@@ -2869,8 +2875,8 @@ end_with_restore_list:
           tmp_table.table_name= lex->name.str;
           tmp_table.db=select_lex->db;
           tmp_table.grant.privilege=priv;
-          if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, 0,
-              UINT_MAX, 0))
+          if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, FALSE,
+              UINT_MAX, FALSE))
             goto error;
       }
 
@@ -2924,10 +2930,11 @@ end_with_restore_list:
       */
       old_list= table[0];
       new_list= table->next_local[0];
-      if (check_grant(thd, ALTER_ACL | DROP_ACL, &old_list, 0, 1, 0) ||
+      if (check_grant(thd, ALTER_ACL | DROP_ACL, &old_list, FALSE, 1, FALSE) ||
          (!test_all_bits(table->next_local->grant.privilege,
                          INSERT_ACL | CREATE_ACL) &&
-          check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0, 1, 0)))
+          check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, FALSE, 1,
+                      FALSE)))
         goto error;
     }
 
@@ -2958,11 +2965,54 @@ end_with_restore_list:
     goto error;
 #else
     {
-      /* Ignore temporary tables if this is "SHOW CREATE VIEW" */
+     /*
+        Access check:
+        SHOW CREATE TABLE require any privileges on the table level (ie
+        effecting all columns in the table).
+        SHOW CREATE VIEW require the SHOW_VIEW and SELECT ACLs on the table
+        level.
+        NOTE: SHOW_VIEW ACL is checked when the view is created.
+      */
+
       if (lex->only_view)
+      {
+        if (check_table_access(thd, SELECT_ACL, first_table, FALSE, 1, FALSE))
+        {
+          my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
+                  "SHOW", thd->security_ctx->priv_user,
+                  thd->security_ctx->host_or_ip, first_table->alias);
+          goto error;
+        }
+
+        /* Ignore temporary tables if this is "SHOW CREATE VIEW" */
         first_table->skip_temporary= 1;
-      if (check_show_create_table_access(thd, first_table))
-	goto error;
+      }
+      else
+      {
+        ulong save_priv;
+        if (check_access(thd, SELECT_ACL, first_table->db,
+                         &save_priv, FALSE, FALSE,
+                         test(first_table->schema_table)))
+          goto error;
+        /*
+          save_priv contains any privileges actually granted by check_access.
+          If there are no global privileges (save_priv == 0) and no table level
+          privileges, access is denied.
+        */
+        if (!save_priv &&
+            !has_any_table_level_privileges(thd, TABLE_ACLS,
+                                            first_table))
+        {
+          my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
+                  "SHOW", thd->security_ctx->priv_user,
+                  thd->security_ctx->host_or_ip, first_table->alias);
+          goto error;
+        }
+      }
+
+      /*
+        Access is granted. Execute command.
+      */
       res= mysqld_show_create(thd, first_table);
       break;
     }
@@ -2970,8 +3020,8 @@ end_with_restore_list:
   case SQLCOM_CHECKSUM:
   {
     DBUG_ASSERT(first_table == all_tables && first_table != 0);
-    if (check_table_access(thd, SELECT_ACL | EXTRA_ACL, all_tables,
-                           UINT_MAX, FALSE))
+    if (check_table_access(thd, SELECT_ACL, all_tables,
+                           FALSE, UINT_MAX, FALSE))
       goto error; /* purecov: inspected */
     res = mysql_checksum_table(thd, first_table, &lex->check_opt);
     break;
@@ -2980,7 +3030,7 @@ end_with_restore_list:
   {
     DBUG_ASSERT(first_table == all_tables && first_table != 0);
     if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables,
-                           UINT_MAX, FALSE))
+                           FALSE, UINT_MAX, FALSE))
       goto error; /* purecov: inspected */
     thd->enable_slow_log= opt_log_slow_admin_statements;
     res= mysql_repair_table(thd, first_table, &lex->check_opt);
@@ -2999,8 +3049,8 @@ end_with_restore_list:
   case SQLCOM_CHECK:
   {
     DBUG_ASSERT(first_table == all_tables && first_table != 0);
-    if (check_table_access(thd, SELECT_ACL | EXTRA_ACL , all_tables,
-                           UINT_MAX, FALSE))
+    if (check_table_access(thd, SELECT_ACL, all_tables,
+                           TRUE, UINT_MAX, FALSE))
       goto error; /* purecov: inspected */
     thd->enable_slow_log= opt_log_slow_admin_statements;
     res = mysql_check_table(thd, first_table, &lex->check_opt);
@@ -3012,7 +3062,7 @@ end_with_restore_list:
   {
     DBUG_ASSERT(first_table == all_tables && first_table != 0);
     if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables,
-                           UINT_MAX, FALSE))
+                           FALSE, UINT_MAX, FALSE))
       goto error; /* purecov: inspected */
     thd->enable_slow_log= opt_log_slow_admin_statements;
     res= mysql_analyze_table(thd, first_table, &lex->check_opt);
@@ -3033,7 +3083,7 @@ end_with_restore_list:
   {
     DBUG_ASSERT(first_table == all_tables && first_table != 0);
     if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables,
-                           UINT_MAX, FALSE))
+                           FALSE, UINT_MAX, FALSE))
       goto error; /* purecov: inspected */
     thd->enable_slow_log= opt_log_slow_admin_statements;
     res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
@@ -3406,7 +3456,7 @@ end_with_restore_list:
     DBUG_ASSERT(first_table == all_tables && first_table != 0);
     if (!lex->drop_temporary)
     {
-      if (check_table_access(thd, DROP_ACL, all_tables, UINT_MAX, FALSE))
+      if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE))
 	goto error;				/* purecov: inspected */
       if (end_active_trans(thd))
         goto error;
@@ -3514,8 +3564,8 @@ end_with_restore_list:
     if (lex->autocommit && end_active_trans(thd))
       goto error;
 
-    if ((check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) ||
-	 open_and_lock_tables(thd, all_tables)))
+    if ((check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE)
+         || open_and_lock_tables(thd, all_tables)))
       goto error;
     if (lex->one_shot_set && not_all_support_one_shot(lex_var_list))
     {
@@ -3569,7 +3619,7 @@ end_with_restore_list:
     if (end_active_trans(thd))
       goto error;
     if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
-                           UINT_MAX, FALSE))
+                           FALSE, UINT_MAX, FALSE))
       goto error;
     if (lex->protect_against_global_read_lock &&
         !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
@@ -3965,7 +4015,7 @@ end_with_restore_list:
       else
       {
 	if (check_grant(thd,(lex->grant | lex->grant_tot_col | GRANT_ACL),
-                        all_tables, 0, UINT_MAX, 0))
+                        all_tables, FALSE, UINT_MAX, FALSE))
 	  goto error;
         /* Conditionally writes to binlog */
         res= mysql_table_grant(thd, all_tables, lex->users_list,
@@ -4074,7 +4124,7 @@ end_with_restore_list:
 #endif
   case SQLCOM_HA_OPEN:
     DBUG_ASSERT(first_table == all_tables && first_table != 0);
-    if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE))
+    if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE))
       goto error;
     res= mysql_ha_open(thd, first_table, 0);
     break;
@@ -4360,7 +4410,8 @@ create_sp_error:
         This will cache all SP and SF and open and lock all tables
         required for execution.
       */
-      if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) ||
+      if (check_table_access(thd, SELECT_ACL, all_tables, FALSE,
+                             UINT_MAX, FALSE) ||
 	  open_and_lock_tables(thd, all_tables))
        goto error;
 
@@ -4688,8 +4739,8 @@ create_sp_error:
     }
   case SQLCOM_DROP_VIEW:
     {
-      if (check_table_access(thd, DROP_ACL, all_tables, UINT_MAX, FALSE) ||
-          end_active_trans(thd))
+      if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE)
+          || end_active_trans(thd))
         goto error;
       /* Conditionally writes to binlog. */
       res= mysql_drop_view(thd, first_table, thd->lex->drop_mode);
@@ -5138,7 +5189,7 @@ bool check_single_table_access(THD *thd,
         (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) &&
       !(all_tables->view &&
         all_tables->effective_algorithm == VIEW_ALGORITHM_TMPTABLE) &&
-      check_grant(thd, privilege, all_tables, 0, 1, no_errors))
+      check_grant(thd, privilege, all_tables, FALSE, 1, no_errors))
     goto deny;
 
   thd->security_ctx= backup_ctx;
@@ -5183,7 +5234,8 @@ bool check_one_table_access(THD *thd, ul
       subselects_tables= subselects_tables->next_global;
     }
     if (subselects_tables &&
-        (check_table_access(thd, SELECT_ACL, subselects_tables, UINT_MAX, FALSE)))
+        (check_table_access(thd, SELECT_ACL, subselects_tables, FALSE,
+                            UINT_MAX, FALSE)))
       return 1;
   }
   return 0;
@@ -5191,46 +5243,54 @@ bool check_one_table_access(THD *thd, ul
 
 
 /**
-  Get the user (global) and database privileges for all used tables.
-
-  @param save_priv    In this we store global and db level grants for the
-                      table. Note that we don't store db level grants if the
-                      global grants is enough to satisfy the request and the
-                      global grants contains a SELECT grant.
-
-  @note
-    The idea of EXTRA_ACL is that one will be granted access to the table if
-    one has the asked privilege on any column combination of the table; For
-    example to be able to check a table one needs to have SELECT privilege on
-    any column of the table.
-
-  @retval
-    0  ok
-  @retval
-    1  If we can't get the privileges and we don't use table/column
-    grants.
+  @brief Compare requested privileges with the privileges acquired from the
+    User- and Db-tables.
+  @param thd          Thread handler
+  @param want_access  The requested access privileges.
+  @param db           A pointer to the Db name.
+  @param[out] save_priv A pointer to the granted privileges will be stored.
+  @param dont_check_global_grants True if no global grants are checked.
+  @param no_error     True if no errors should be sent to the client.
+  @param schema_db    True if the db specified belongs to the meta data tables.
+
+  'save_priv' is used to save the User-table (global) and Db-table grants for
+  the supplied db name. Note that we don't store db level grants if the global
+  grants is enough to satisfy the request AND the global grants contains a
+  SELECT grant.
+
+  A meta data table (from INFORMATION_SCHEMA) can always be accessed with
+  a SELECT_ACL.
+
+  @see check_grant
+
+  @return Status of denial of access by exclusive ACLs.
+    @retval FALSE Access can't exclusively be denied by Db- and User-table
+      access unless Column- and Table-grants are checked too.
+    @retval TRUE Access denied.
 */
+
 bool
 check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
 	     bool dont_check_global_grants, bool no_errors, bool schema_db)
 {
   Security_context *sctx= thd->security_ctx;
   ulong db_access;
+
   /*
     GRANT command:
     In case of database level grant the database name may be a pattern,
     in case of table|column level grant the database name can not be a pattern.
     We use 'dont_check_global_grants' as a flag to determine
-    if it's database level grant command 
+    if it's database level grant command
     (see SQLCOM_GRANT case, mysql_execute_command() function) and
     set db_is_pattern according to 'dont_check_global_grants' value.
   */
-  bool  db_is_pattern= (test(want_access & GRANT_ACL) &&
-                        dont_check_global_grants);
+  bool  db_is_pattern= ((want_access & GRANT_ACL) &&
dont_check_global_grants);
   ulong dummy;
   DBUG_ENTER("check_access");
   DBUG_PRINT("enter",("db: %s  want_access: %lu  master_access: %lu",
                       db ? db : "", want_access, sctx->master_access));
+
   if (save_priv)
     *save_priv=0;
   else
@@ -5248,8 +5308,12 @@ check_access(THD *thd, ulong want_access
 
   if (schema_db)
   {
-    if ((!(sctx->master_access & FILE_ACL) && (want_access &
FILE_ACL)) ||
-        (want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL)))
+    /*
+      We don't allow any simple privileges but SELECT_ACL or CREATE_VIEW_ACL
+      on the information_schema database.
+    */
+    want_access &= ~SELECT_ACL;
+    if (want_access & DB_ACLS)
     {
       if (!no_errors)
       {
@@ -5257,10 +5321,15 @@ check_access(THD *thd, ulong want_access
         my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
                  sctx->priv_user, sctx->priv_host, db_name);
       }
+      /*
+        Access denied;
+        [out] *save_privileges= 0
+      */
       DBUG_RETURN(TRUE);
     }
     else
     {
+      /* Access granted */
       *save_priv= SELECT_ACL;
       DBUG_RETURN(FALSE);
     }
@@ -5268,20 +5337,27 @@ check_access(THD *thd, ulong want_access
 
   if ((sctx->master_access & want_access) == want_access)
   {
+    /* get access for current db */
+    db_access= sctx->db_access;
     /*
-      If we don't have a global SELECT privilege, we have to get the database
-      specific access rights to be able to handle queries of type
+      1. If we don't have a global SELECT privilege, we have to get the
+      database specific access rights to be able to handle queries of type
       UPDATE t1 SET a=1 WHERE b > 0
+      2. Change db access if it isn't current db which is being addressed
     */
-    db_access= sctx->db_access;
     if (!(sctx->master_access & SELECT_ACL) &&
 	(db && (!thd->db || db_is_pattern || strcmp(db,thd->db))))
       db_access=acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
                         db_is_pattern);
+
+    /*
+      The effective privileges are the union of the global privileges
+      and the the intersection of db- and host-privileges.
+    */
     *save_priv=sctx->master_access | db_access;
     DBUG_RETURN(FALSE);
   }
-  if (((want_access & ~sctx->master_access) & ~(DB_ACLS | EXTRA_ACL)) ||
+  if (((want_access & ~sctx->master_access) & ~DB_ACLS) ||
       (! db && dont_check_global_grants))
   {						// We can never grant this
     DBUG_PRINT("error",("No possible access"));
@@ -5296,33 +5372,66 @@ check_access(THD *thd, ulong want_access
   }
 
   if (db == any_db)
-    DBUG_RETURN(FALSE);				// Allow select on anything
+  {
+    /*
+      Access granted; Allow select on *any* db.
+      [out] *save_privileges= 0
+    */
+    DBUG_RETURN(FALSE);
+  }
 
   if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db)))
     db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
                        db_is_pattern);
   else
     db_access= sctx->db_access;
-  DBUG_PRINT("info",("db_access: %lu", db_access));
-  /* Remove SHOW attribute and access rights we already have */
-  want_access &= ~(sctx->master_access | EXTRA_ACL);
   DBUG_PRINT("info",("db_access: %lu  want_access: %lu",
                      db_access, want_access));
-  db_access= ((*save_priv=(db_access | sctx->master_access)) & want_access);
 
-  if (db_access == want_access ||
+  /*
+    Save the union of User-table and the intersection between Db-table and
+    Host-table privileges.
+  */
+  db_access= (db_access | sctx->master_access);
+  *save_priv= db_access;
+
+  /*
+    We need to investigate column- and table access if all requested privileges
+    belongs to the bit set of .
+  */
+  bool need_table_or_column_check=
+    (want_access & (TABLE_ACLS | PROC_ACLS | db_access)) == want_access;
+
+  /*
+    Grant access if the requested access is in the intersection of
+    host- and db-privileges (as retrieved from the acl cache),
+    also grant access if all the requested privileges are in the union of
+    TABLES_ACLS and PROC_ACLS; see check_grant.
+  */
+  if ( (db_access & want_access) == want_access ||
       (!dont_check_global_grants &&
-       !(want_access & ~(db_access | TABLE_ACLS | PROC_ACLS))))
-    DBUG_RETURN(FALSE);				/* Ok */
+       need_table_or_column_check))
+  {
+    /*
+       Ok; but need to check table- and column privileges.
+       [out] *save_privileges is (User-priv | (Db-priv & Host-priv))
+    */
+    DBUG_RETURN(FALSE);
+  }
 
+  /*
+    Access is denied;
+    [out] *save_privileges is (User-priv | (Db-priv & Host-priv))
+  */
   DBUG_PRINT("error",("Access denied"));
   if (!no_errors)
     my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
              sctx->priv_user, sctx->priv_host,
              (db ? db : (thd->db ?
                          thd->db :
-                         "unknown")));          /* purecov: tested */
-  DBUG_RETURN(TRUE);				/* purecov: tested */
+                         "unknown")));
+  DBUG_RETURN(TRUE);
+
 }
 
 
@@ -5368,14 +5477,20 @@ static bool check_show_access(THD *thd, 
 
     DBUG_ASSERT(dst_table);
 
-    if (check_access(thd, SELECT_ACL | EXTRA_ACL,
-                     dst_table->db,
-                     &dst_table->grant.privilege,
-                     FALSE, FALSE,
+    if (check_access(thd, SELECT_ACL, dst_table->db,
+                     &dst_table->grant.privilege, FALSE, FALSE,
                      test(dst_table->schema_table)))
-      return FALSE;
+          return TRUE; /* Access denied */
+
+    /*
+      Check_grant will grant access if there is any column privileges on
+      all of the tables thanks to the fourth parameter (bool show_table).
+    */
+    if (check_grant(thd, SELECT_ACL, dst_table, TRUE, UINT_MAX, FALSE))
+      return TRUE; /* Access denied */
 
-    return (check_grant(thd, SELECT_ACL, dst_table, 2, UINT_MAX, FALSE));
+    /* Access granted */
+    return FALSE;
   }
   default:
     break;
@@ -5385,30 +5500,46 @@ static bool check_show_access(THD *thd, 
 }
 
 
-/**
-  Check the privilege for all used tables.
-
-  @param    thd          Thread context
-  @param    want_access  Privileges requested
-  @param    tables       List of tables to be checked
-  @param    number       Check at most this number of tables.
-  @param    no_errors    FALSE/TRUE - report/don't report error to
-                         the client (using my_error() call).
 
-  @note
-    Table privileges are cached in the table list for GRANT checking.
-    This functions assumes that table list used and
-    thd->lex->query_tables_own_last value correspond to each other
-    (the latter should be either 0 or point to next_global member
-    of one of elements of this table list).
+/**
+  @brief Check if the requested privileges exists in either User-, Host- or
+    Db-tables.
+  @param thd          Thread context
+  @param want_access  Privileges requested
+  @param tables       List of tables to be compared against
+  @param no_errors    Don't report error to the client (using my_error() call).
+  @param any_combination_of_privileges_will_do TRUE if any privileges on any
+    column combination is enough.
+  @param number       Only the first 'number' tables in the linked list are
+                      relevant.
+
+  The suppled table list contains cached privileges. This functions calls the
+  help functions check_access and check_grant to verify the first three steps
+  in the privileges check queue:
+  1. Global privileges
+  2. OR (db privileges AND host privileges)
+  3. OR table privileges
+  4. OR column privileges (not checked by this function!)
+  5. OR routine privileges (not checked by this function!)
+
+  @see check_access
+  @see check_grant
+
+  @note This functions assumes that table list used and
+  thd->lex->query_tables_own_last value correspond to each other
+  (the latter should be either 0 or point to next_global member
+  of one of elements of this table list).
 
-  @retval  FALSE   OK
-  @retval  TRUE    Access denied
+  @return
+    @retval FALSE OK
+    @retval TRUE  Access denied; But column or routine privileges might need to
+      be checked also.
 */
 
 bool
-check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
-		   uint number, bool no_errors)
+check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
+		   bool any_combination_of_privileges_will_do,
+                   uint number, bool no_errors)
 {
   TABLE_LIST *org_tables= tables;
   TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
@@ -5419,22 +5550,31 @@ check_table_access(THD *thd, ulong want_
     the given table list refers to the list for prelocking (contains tables
     of other queries). For simple queries first_not_own_table is 0.
   */
-  for (; i < number && tables != first_not_own_table;
+  for (; i < number && tables != first_not_own_table && tables;
        tables= tables->next_global, i++)
   {
+    ulong want_access= requirements;
     if (tables->security_ctx)
       sctx= tables->security_ctx;
     else
       sctx= backup_ctx;
 
-    if (tables->schema_table && 
-        (want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL)))
-    {
-      if (!no_errors)
-        my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
-                 sctx->priv_user, sctx->priv_host,
-                 INFORMATION_SCHEMA_NAME.str);
-      return TRUE;
+    /*
+      Always allow SELECT on schema tables. This is done by removing the
+      required SELECT_ACL privilege in the want_access parameter.
+      Disallow any other DDL or DML operation on any schema table.
+    */
+    if (tables->schema_table)
+    {
+      want_access &= ~SELECT_ACL;
+      if (want_access & DB_ACLS)
+      {
+        if (!no_errors)
+          my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
+                  sctx->priv_user, sctx->priv_host,
+                  INFORMATION_SCHEMA_NAME.str);
+        goto deny;
+      }
     }
     /*
        Register access for view underlying table.
@@ -5446,33 +5586,34 @@ check_table_access(THD *thd, ulong want_
     {
       if (check_show_access(thd, tables))
         goto deny;
-
       continue;
     }
 
+    DBUG_PRINT("info", ("derived: %d  view: %d", tables->derived != 0,
+                        tables->view != 0));
     if (tables->is_anonymous_derived_table() ||
-        (tables->table && (int)tables->table->s->tmp_table))
+        (tables->table && tables->table->s &&
+         (int)tables->table->s->tmp_table))
       continue;
     thd->security_ctx= sctx;
-    if ((sctx->master_access & want_access) ==
-        (want_access & ~EXTRA_ACL) &&
-	thd->db)
+    if ((sctx->master_access & want_access) == want_access &&
+        thd->db)
       tables->grant.privilege= want_access;
     else if (tables->db && thd->db && strcmp(tables->db,
thd->db) == 0)
     {
       if (check_access(thd, want_access, tables->get_db_name(),
-                       &tables->grant.privilege, 0, no_errors, 
+                       &tables->grant.privilege, 0, no_errors,
                        test(tables->schema_table)))
         goto deny;                            // Access denied
     }
     else if (check_access(thd, want_access, tables->get_db_name(),
-                          &tables->grant.privilege, 0, no_errors, 
-                          test(tables->schema_table)))
+                          &tables->grant.privilege, 0, no_errors, 0))
       goto deny;
   }
   thd->security_ctx= backup_ctx;
-  return check_grant(thd,want_access & ~EXTRA_ACL,org_tables,
-		       test(want_access & EXTRA_ACL), number, no_errors);
+  return check_grant(thd,requirements,org_tables,
+                     any_combination_of_privileges_will_do,
+                     number, no_errors);
 deny:
   thd->security_ctx= backup_ctx;
   return TRUE;
@@ -5560,7 +5701,7 @@ bool check_some_access(THD *thd, ulong w
       if (!check_access(thd, access, table->db,
                         &table->grant.privilege, 0, 1,
                         test(table->schema_table)) &&
-           !check_grant(thd, access, table, 0, 1, 1))
+           !check_grant(thd, access, table, FALSE, 1, TRUE))
         DBUG_RETURN(0);
     }
   }
@@ -7253,11 +7394,11 @@ bool multi_update_precheck(THD *thd, TAB
     else if ((check_access(thd, UPDATE_ACL, table->db,
                            &table->grant.privilege, 0, 1,
                            test(table->schema_table)) ||
-              check_grant(thd, UPDATE_ACL, table, 0, 1, 1)) &&
+              check_grant(thd, UPDATE_ACL, table, FALSE, 1, TRUE)) &&
              (check_access(thd, SELECT_ACL, table->db,
                            &table->grant.privilege, 0, 0,
                            test(table->schema_table)) ||
-              check_grant(thd, SELECT_ACL, table, 0, 1, 0)))
+              check_grant(thd, SELECT_ACL, table, FALSE, 1, FALSE)))
       DBUG_RETURN(TRUE);
 
     table->table_in_first_from_clause= 1;
@@ -7275,7 +7416,7 @@ bool multi_update_precheck(THD *thd, TAB
 	if (check_access(thd, SELECT_ACL, table->db,
 			 &table->grant.privilege, 0, 0,
                          test(table->schema_table)) ||
-	    check_grant(thd, SELECT_ACL, table, 0, 1, 0))
+	    check_grant(thd, SELECT_ACL, table, FALSE, 1, FALSE))
 	  DBUG_RETURN(TRUE);
       }
     }
@@ -7315,7 +7456,7 @@ bool multi_delete_precheck(THD *thd, TAB
 
   /* sql_yacc guarantees that tables and aux_tables are not zero */
   DBUG_ASSERT(aux_tables != 0);
-  if (check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE))
+  if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE))
     DBUG_RETURN(TRUE);
 
   /*
@@ -7324,7 +7465,7 @@ bool multi_delete_precheck(THD *thd, TAB
     call check_table_access() safely.
   */
   thd->lex->query_tables_own_last= 0;
-  if (check_table_access(thd, DELETE_ACL, aux_tables, UINT_MAX, FALSE))
+  if (check_table_access(thd, DELETE_ACL, aux_tables, FALSE, UINT_MAX, FALSE))
   {
     thd->lex->query_tables_own_last= save_query_tables_own_last;
     DBUG_RETURN(TRUE);
@@ -7478,25 +7619,6 @@ bool insert_precheck(THD *thd, TABLE_LIS
 
 
 /**
-    @brief  Check privileges for SHOW CREATE TABLE statement.
-
-    @param  thd    Thread context
-    @param  table  Target table
-
-    @retval TRUE  Failure
-    @retval FALSE Success
-*/
-
-static bool check_show_create_table_access(THD *thd, TABLE_LIST *table)
-{
-  return check_access(thd, SELECT_ACL | EXTRA_ACL, table->db,
-                      &table->grant.privilege, 0, 0,
-                      test(table->schema_table)) ||
-         check_grant(thd, SELECT_ACL, table, 2, UINT_MAX, 0);
-}
-
-
-/**
   CREATE TABLE query pre-check.
 
   @param thd			Thread handler
@@ -7535,7 +7657,7 @@ bool create_table_precheck(THD *thd, TAB
 			       lex->create_info.merge_list.first))
     goto err;
   if (want_priv != CREATE_TMP_ACL &&
-      check_grant(thd, want_priv, create_table, 0, 1, 0))
+      check_grant(thd, want_priv, create_table, FALSE, 1, FALSE))
     goto err;
 
   if (select_lex->item_list.elements)
@@ -7563,12 +7685,13 @@ bool create_table_precheck(THD *thd, TAB
       }
     }
 #endif
-    if (tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE))
+    if (tables && check_table_access(thd, SELECT_ACL, tables, FALSE,
+                                     UINT_MAX, FALSE))
       goto err;
   }
   else if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE)
   {
-    if (check_show_create_table_access(thd, tables))
+    if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE))
       goto err;
   }
   error= FALSE;

=== modified file 'sql/sql_plugin.cc'
--- a/sql/sql_plugin.cc	2009-09-04 10:32:21 +0000
+++ b/sql/sql_plugin.cc	2009-10-19 12:58:13 +0000
@@ -1660,7 +1660,7 @@ bool mysql_install_plugin(THD *thd, cons
   bzero(&tables, sizeof(tables));
   tables.db= (char *)"mysql";
   tables.table_name= tables.alias= (char *)"plugin";
-  if (check_table_access(thd, INSERT_ACL, &tables, 1, FALSE))
+  if (check_table_access(thd, INSERT_ACL, &tables, FALSE, 1, FALSE))
     DBUG_RETURN(TRUE);
 
   /* need to open before acquiring LOCK_plugin or it will deadlock */

=== modified file 'sql/sql_prepare.cc'
--- a/sql/sql_prepare.cc	2009-09-17 09:20:11 +0000
+++ b/sql/sql_prepare.cc	2009-10-19 12:58:13 +0000
@@ -1336,7 +1336,7 @@ static int mysql_test_select(Prepared_st
   ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL;
   if (tables)
   {
-    if (check_table_access(thd, privilege, tables, UINT_MAX, FALSE))
+    if (check_table_access(thd, privilege, tables, FALSE, UINT_MAX, FALSE))
       goto error;
   }
   else if (check_access(thd, privilege, any_db,0,0,0,0))
@@ -1405,7 +1405,8 @@ static bool mysql_test_do_fields(Prepare
   THD *thd= stmt->thd;
 
   DBUG_ENTER("mysql_test_do_fields");
-  if (tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE))
+  if (tables && check_table_access(thd, SELECT_ACL, tables, FALSE,
+                                   UINT_MAX, FALSE))
     DBUG_RETURN(TRUE);
 
   if (open_normal_and_derived_tables(thd, tables, 0))
@@ -1436,8 +1437,9 @@ static bool mysql_test_set_fields(Prepar
   THD *thd= stmt->thd;
   set_var_base *var;
 
-  if ((tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)) 
-      || open_normal_and_derived_tables(thd, tables, 0))
+  if ((tables && check_table_access(thd, SELECT_ACL, tables, FALSE,
+                                    UINT_MAX, FALSE)) ||
+      open_normal_and_derived_tables(thd, tables, 0))
     goto error;
 
   while ((var= it++))
@@ -1472,7 +1474,8 @@ static bool mysql_test_call_fields(Prepa
   THD *thd= stmt->thd;
   Item *item;
 
-  if ((tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)) ||
+  if ((tables && check_table_access(thd, SELECT_ACL, tables, FALSE,
+                                    UINT_MAX, FALSE)) ||
       open_normal_and_derived_tables(thd, tables, 0))
     goto err;
 

=== modified file 'sql/sql_show.cc'
--- a/sql/sql_show.cc	2009-09-17 09:20:11 +0000
+++ b/sql/sql_show.cc	2009-10-19 12:58:13 +0000
@@ -560,7 +560,7 @@ find_files(THD *thd, List<LEX_STRING> *f
       table_list.table_name= uname;
       table_list.table_name_length= file_name_len;
       table_list.grant.privilege=col_access;
-      if (check_grant(thd, TABLE_ACLS, &table_list, 1, 1, 1))
+      if (check_grant(thd, TABLE_ACLS, &table_list, TRUE, 1, TRUE))
         continue;
     }
 #endif
@@ -3745,8 +3745,9 @@ static int get_schema_column_record(THD 
 
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
     uint col_access;
-    check_access(thd,SELECT_ACL | EXTRA_ACL, db_name->str,
-                 &tables->grant.privilege, 0, 0, test(tables->schema_table));
+    check_access(thd,SELECT_ACL, db_name->str,
+                 &tables->grant.privilege, FALSE, FALSE,
+                 test(tables->schema_table));
     col_access= get_column_grant(thd, &tables->grant, 
                                  db_name->str, table_name->str,
                                  field->field_name) & COL_ACLS;
@@ -4174,7 +4175,8 @@ int fill_schema_proc(THD *thd, TABLE_LIS
   proc_tables.table_name= proc_tables.alias= (char*) "proc";
   proc_tables.table_name_length= 4;
   proc_tables.lock_type= TL_READ;
-  full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, 1, TRUE);
+  full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, FALSE,
+                                   1, TRUE);
   if (!(proc_table= open_proc_table_for_read(thd, &open_tables_state_backup)))
   {
     DBUG_RETURN(1);
@@ -4583,7 +4585,7 @@ static int get_schema_triggers_record(TH
     Table_triggers_list *triggers= tables->table->triggers;
     int event, timing;
 
-    if (check_table_access(thd, TRIGGER_ACL, tables, 1, TRUE))
+    if (check_table_access(thd, TRIGGER_ACL, tables, FALSE, 1, TRUE))
       goto ret;
 
     for (event= 0; event < (int)TRG_EVENT_MAX; event++)
@@ -7044,7 +7046,7 @@ bool show_create_trigger(THD *thd, const
   if (!lst)
     return TRUE;
 
-  if (check_table_access(thd, TRIGGER_ACL, lst, 1, TRUE))
+  if (check_table_access(thd, TRIGGER_ACL, lst, FALSE, 1, TRUE))
   {
     my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "TRIGGER");
     return TRUE;

=== modified file 'sql/sql_trigger.cc'
--- a/sql/sql_trigger.cc	2009-06-17 14:56:44 +0000
+++ b/sql/sql_trigger.cc	2009-10-19 12:58:13 +0000
@@ -422,7 +422,7 @@ bool mysql_create_or_drop_trigger(THD *t
     TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last;
     thd->lex->query_tables_own_last= 0;
 
-    err_status= check_table_access(thd, TRIGGER_ACL, tables, 1, FALSE);
+    err_status= check_table_access(thd, TRIGGER_ACL, tables, FALSE, 1, FALSE);
 
     thd->lex->query_tables_own_last= save_query_tables_own_last;
 

=== modified file 'sql/sql_update.cc'
--- a/sql/sql_update.cc	2009-09-17 09:20:11 +0000
+++ b/sql/sql_update.cc	2009-10-19 12:58:13 +0000
@@ -1077,7 +1077,7 @@ reopen_tables:
       if (check_access(thd, want_privilege,
                        tl->db, &tl->grant.privilege, 0, 0, 
                        test(tl->schema_table)) ||
-          check_grant(thd, want_privilege, tl, 0, 1, 0))
+          check_grant(thd, want_privilege, tl, FALSE, 1, FALSE))
         DBUG_RETURN(TRUE);
     }
   }

=== modified file 'sql/sql_view.cc'
--- a/sql/sql_view.cc	2009-08-28 16:21:54 +0000
+++ b/sql/sql_view.cc	2009-10-19 12:58:13 +0000
@@ -269,11 +269,11 @@ bool create_view_precheck(THD *thd, TABL
   */
   if ((check_access(thd, CREATE_VIEW_ACL, view->db, &view->grant.privilege,
                     0, 0, is_schema_db(view->db)) ||
-       check_grant(thd, CREATE_VIEW_ACL, view, 0, 1, 0)) ||
+       check_grant(thd, CREATE_VIEW_ACL, view, FALSE, 1, FALSE)) ||
       (mode != VIEW_CREATE_NEW &&
        (check_access(thd, DROP_ACL, view->db, &view->grant.privilege,
                      0, 0, is_schema_db(view->db)) ||
-        check_grant(thd, DROP_ACL, view, 0, 1, 0))))
+        check_grant(thd, DROP_ACL, view, FALSE, 1, FALSE))))
     goto err;
 
   for (sl= select_lex; sl; sl= sl->next_select())
@@ -323,7 +323,7 @@ bool create_view_precheck(THD *thd, TABL
       {
         if (check_access(thd, SELECT_ACL, tbl->db,
                          &tbl->grant.privilege, 0, 0, test(tbl->schema_table))
||
-            check_grant(thd, SELECT_ACL, tbl, 0, 1, 0))
+            check_grant(thd, SELECT_ACL, tbl, FALSE, 1, FALSE))
           goto err;
       }
     }
@@ -1233,8 +1233,9 @@ bool mysql_make_view(THD *thd, File_pars
     if (!table->prelocking_placeholder &&
         (old_lex->sql_command == SQLCOM_SELECT && old_lex->describe))
     {
-      if (check_table_access(thd, SELECT_ACL, view_tables, UINT_MAX, TRUE) &&
-          check_table_access(thd, SHOW_VIEW_ACL, table, UINT_MAX, TRUE))
+      if (check_table_access(thd, SELECT_ACL, view_tables, FALSE,
+                             UINT_MAX, TRUE) &&
+          check_table_access(thd, SHOW_VIEW_ACL, table, FALSE, UINT_MAX, TRUE))
       {
         my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0));
         goto err;
@@ -1244,7 +1245,7 @@ bool mysql_make_view(THD *thd, File_pars
              (old_lex->sql_command == SQLCOM_SHOW_CREATE) &&
              !table->belong_to_view)
     {
-      if (check_table_access(thd, SHOW_VIEW_ACL, table, UINT_MAX, FALSE))
+      if (check_table_access(thd, SHOW_VIEW_ACL, table, FALSE, UINT_MAX, FALSE))
         goto err;
     }
 


Attachment: [text/bzr-bundle]
Thread
bzr commit into mysql-5.4.5-next-mr branch (kristofer.pettersson:2888)Bug#27145Kristofer Pettersson19 Oct 2009