Chuck,
I am not not very familiar with the semantics of grant and revoke, so
I guess my review is more questions than comments. So far I have not
found anything severe, but I would like to get answers to my questions
before approving the patch.
--
Øystein
Chuck Bell wrote:
> #At file:///C:/source/bzr/mysql-6.0-wl-4073/
>
> 2676 Chuck Bell 2008-08-07
> WL#4073 : Online Backup: backup and restore database object permissions
>
> This patch adds the ability to backup privileges (grants) for databases.
> added:
> mysql-test/r/backup_db_grants.result
> mysql-test/t/backup_db_grants.test
> modified:
> mysql-test/lib/mtr_report.pl
> mysql-test/r/backup_ddl_blocker.result
> mysql-test/r/backup_no_data.result
> mysql-test/r/backup_views.result
> mysql-test/t/backup_ddl_blocker.test
> mysql-test/t/backup_no_data.test
> mysql-test/t/backup_views.test
> sql/backup/backup_aux.h
> sql/backup/backup_info.cc
> sql/backup/backup_info.h
> sql/backup/backup_test.cc
> sql/backup/image_info.cc
> sql/backup/image_info.h
> sql/backup/kernel.cc
> sql/share/errmsg.txt
> sql/si_objects.cc
> sql/si_objects.h
>
> per-file messages:
> mysql-test/lib/mtr_report.pl
> Added warning suppression for backup_db_grants test.
> mysql-test/r/backup_db_grants.result
> New result file.
> mysql-test/r/backup_ddl_blocker.result
> New result file with changes for added drop user commands.
> mysql-test/r/backup_no_data.result
> New result file with changes for added drop user commands.
> mysql-test/r/backup_views.result
> New result file with changes correct error message.
> mysql-test/t/backup_db_grants.test
> New test for testing backup and restore of grants.
> mysql-test/t/backup_ddl_blocker.test
> Added drop user ''@'%' commands to suppress warnings for anonymous user.
> mysql-test/t/backup_no_data.test
> Added drop user ''@'%' commands to suppress warnings for anonymous user.
> mysql-test/t/backup_views.test
> Corrected error message returned on restore.
> sql/backup/backup_aux.h
> Added defines for uniqueness values of grantee strings in the backup catalog.
> sql/backup/backup_info.cc
> Added code to include grants in the output stream.
> sql/backup/backup_info.h
> Added new pointer to locate end of list of events and beginning of
> privileges (in memory).
> sql/backup/backup_test.cc
> Added testing code for low-level developer testing.
> sql/backup/image_info.cc
> Added case to include privileges (grants) in restore.
> sql/backup/image_info.h
> Added new case to materialize a grant after stripping off uniqueness
> value for grantee.
> sql/backup/kernel.cc
> Added case statements to include privileges (grants) in the backup and restore.
> sql/share/errmsg.txt
> Added error messages for processing privileges and issuing warnings and errors
> during backup and restore.
> sql/si_objects.cc
> Added new classes to manage database-, table-, column-, and routine-level
> grants.
> sql/si_objects.h
> New definitions of methods to backup and restore grants.
> Helper methods added to check to see if grantee exists and iterate
> over all grants found.
> Corrected comment about user name for get_name() method.
> === modified file 'mysql-test/lib/mtr_report.pl'
> --- a/mysql-test/lib/mtr_report.pl 2008-08-05 08:04:30 +0000
> +++ b/mysql-test/lib/mtr_report.pl 2008-08-07 14:53:39 +0000
> @@ -334,6 +334,12 @@ sub mtr_report_stats ($) {
> /Backup:/ or /Restore:/ or /Can't open the online backup progress tables/
> ) or
>
> + # backup_db_grants test is supposed to trigger lots of restore warnings
> + ($testname eq 'main.backup_db_grants') and
> + (
> + /Restore:/ or /was skipped because the user does not exist/
> + ) or
> +
> # The tablespace test triggers error below on purpose
> ($testname eq 'main.backup_tablespace') and
> (
>
> === added file 'mysql-test/r/backup_db_grants.result'
> --- a/mysql-test/r/backup_db_grants.result 1970-01-01 00:00:00 +0000
> +++ b/mysql-test/r/backup_db_grants.result 2008-08-07 14:53:39 +0000
> @@ -0,0 +1,132 @@
> +DROP DATABASE IF EXISTS bup_db_grants;
> +DROP DATABASE IF EXISTS db2;
> +Create two databases and two tables.
> +CREATE DATABASE bup_db_grants;
> +CREATE TABLE bup_db_grants.t1(a INT);
> +CREATE TABLE bup_db_grants.s1(b INT);
> +INSERT INTO bup_db_grants.t1 VALUES (1), (2), (3), (4);
> +INSERT INTO bup_db_grants.s1 VALUES (10), (20), (30), (40);
> +Now create users and assign various rights to the databases
> +CREATE USER 'bup_user1'@'%';
> +CREATE USER 'bup_user2'@'%';
> +CREATE USER 'bup_user3'@'%';
> +REVOKE ALL ON *.* FROM 'bup_user1'@'%';
> +REVOKE ALL ON *.* FROM 'bup_user2'@'%';
> +GRANT ALL ON db2.* TO 'bup_user1'@'%';
> +GRANT SELECT ON bup_db_grants.* TO 'bup_user1'@'%';
> +GRANT INSERT ON bup_db_grants.* TO 'bup_user2'@'%';
> +GRANT ALL ON bup_db_grants.* TO 'no_user'@'%';
> +GRANT SELECT (a) ON bup_db_grants.t1 TO 'bup_user1'@'%';
> +GRANT SELECT (b), INSERT (b) ON bup_db_grants.s1 TO 'bup_user2'@'%';
> +FLUSH PRIVILEGES;
> +Demonstrate rights of the users.
> +SHOW GRANTS FOR 'bup_user1'@'%';
> +Grants for bup_user1@%
> +GRANT USAGE ON *.* TO 'bup_user1'@'%'
> +GRANT ALL PRIVILEGES ON `db2`.* TO 'bup_user1'@'%'
> +GRANT SELECT ON `bup_db_grants`.* TO 'bup_user1'@'%'
> +GRANT SELECT (a) ON `bup_db_grants`.`t1` TO 'bup_user1'@'%'
> +SHOW GRANTS FOR 'bup_user2'@'%';
> +Grants for bup_user2@%
> +GRANT USAGE ON *.* TO 'bup_user2'@'%'
> +GRANT INSERT ON `bup_db_grants`.* TO 'bup_user2'@'%'
> +GRANT SELECT (b), INSERT (b) ON `bup_db_grants`.`s1` TO 'bup_user2'@'%'
> +SHOW GRANTS FOR 'bup_user3'@'%';
> +Grants for bup_user3@%
> +GRANT USAGE ON *.* TO 'bup_user3'@'%'
> +SHOW GRANTS FOR 'no_user'@'%';
> +Grants for no_user@%
> +GRANT USAGE ON *.* TO 'no_user'@'%'
> +GRANT ALL PRIVILEGES ON `bup_db_grants`.* TO 'no_user'@'%'
> +Run backup
> +BACKUP DATABASE bup_db_grants TO 'bup_db_grants.bak';
> +backup_id
> +#
> +Drop users and recreate them (removes grants completely).
> +DROP USER 'bup_user1'@'%';
> +DROP USER 'bup_user2'@'%';
> +DROP USER 'bup_user3'@'%';
> +DROP USER 'no_user'@'%';
> +FLUSH PRIVILEGES;
> +CREATE USER 'bup_user1'@'%';
> +CREATE USER 'bup_user2'@'%';
> +CREATE USER 'bup_user3'@'%';
> +FLUSH PRIVILEGES;
> +Demonstrate rights of the users.
> +SHOW GRANTS FOR 'bup_user1'@'%';
> +Grants for bup_user1@%
> +GRANT USAGE ON *.* TO 'bup_user1'@'%'
> +SHOW GRANTS FOR 'bup_user2'@'%';
> +Grants for bup_user2@%
> +GRANT USAGE ON *.* TO 'bup_user2'@'%'
> +SHOW GRANTS FOR 'bup_user3'@'%';
> +Grants for bup_user3@%
> +GRANT USAGE ON *.* TO 'bup_user3'@'%'
> +Run Restore
> +RESTORE FROM 'bup_db_grants.bak';
> +backup_id
> +#
> +SHOW TABLES FROM bup_db_grants;
> +Tables_in_bup_db_grants
> +s1
> +t1
> +Demonstrate rights of the users.
> +SHOW GRANTS FOR 'bup_user1'@'%';
> +Grants for bup_user1@%
> +GRANT USAGE ON *.* TO 'bup_user1'@'%'
> +GRANT SELECT ON `bup_db_grants`.* TO 'bup_user1'@'%'
> +GRANT SELECT (a) ON `bup_db_grants`.`t1` TO 'bup_user1'@'%'
> +SHOW GRANTS FOR 'bup_user2'@'%';
> +Grants for bup_user2@%
> +GRANT USAGE ON *.* TO 'bup_user2'@'%'
> +GRANT INSERT ON `bup_db_grants`.* TO 'bup_user2'@'%'
> +GRANT SELECT (b), INSERT (b) ON `bup_db_grants`.`s1` TO 'bup_user2'@'%'
> +SHOW GRANTS FOR 'bup_user3'@'%';
> +Grants for bup_user3@%
> +GRANT USAGE ON *.* TO 'bup_user3'@'%'
> +SHOW GRANTS FOR 'no_user'@'%';
> +ERROR 42000: There is no such grant defined for user 'no_user' on host '%'
> +Now test what happens on backup if users are deleted.
> +Drop users and recreate one of them (removes grants completely).
> +DROP USER 'bup_user1'@'%';
> +DROP USER 'bup_user2'@'%';
> +DROP USER 'bup_user3'@'%';
> +FLUSH PRIVILEGES;
> +CREATE USER 'bup_user1'@'%';
> +CREATE USER 'bup_user1'@'nosuchhost';
> +Run Restore
> +RESTORE FROM 'bup_db_grants.bak';
> +backup_id
> +#
> +SHOW TABLES FROM bup_db_grants;
> +Tables_in_bup_db_grants
> +s1
> +t1
> +Demonstrate rights of the users.
> +SHOW GRANTS FOR 'bup_user1'@'%';
> +Grants for bup_user1@%
> +GRANT USAGE ON *.* TO 'bup_user1'@'%'
> +GRANT SELECT ON `bup_db_grants`.* TO 'bup_user1'@'%'
> +GRANT SELECT (a) ON `bup_db_grants`.`t1` TO 'bup_user1'@'%'
> +SHOW GRANTS FOR 'bup_user2'@'%';
> +ERROR 42000: There is no such grant defined for user 'bup_user2' on host '%'
> +SHOW GRANTS FOR 'bup_user3'@'%';
> +ERROR 42000: There is no such grant defined for user 'bup_user3' on host '%'
> +SHOW GRANTS FOR 'no_user'@'%';
> +ERROR 42000: There is no such grant defined for user 'no_user' on host '%'
> +Now demonstrate what happens when grants are altered in backup image.
> +Run Restore
> +RESTORE FROM 'bup_db_grants.bad';
> +ERROR HY000: The grant 'GRANT SELECT(a) ON boo_db_grants.t1 TO 'bup_user1'@'%'
> failed. Database not included in the backup image.
> +FLUSH PRIVILEGES;
> +Cleanup
> +DROP USER 'bup_user1'@'%';
> +DROP USER 'bup_user1'@'nosuchhost';
> +DROP USER 'bup_user2'@'%';
> +ERROR HY000: Operation DROP USER failed for 'bup_user2'@'%'
> +DROP USER 'bup_user3'@'%';
> +ERROR HY000: Operation DROP USER failed for 'bup_user3'@'%'
> +DROP USER 'no_user'@'%';
> +ERROR HY000: Operation DROP USER failed for 'no_user'@'%'
> +FLUSH PRIVILEGES;
> +DROP DATABASE bup_db_grants;
>
> === modified file 'mysql-test/r/backup_ddl_blocker.result'
> --- a/mysql-test/r/backup_ddl_blocker.result 2008-06-25 13:39:04 +0000
> +++ b/mysql-test/r/backup_ddl_blocker.result 2008-08-07 14:53:39 +0000
> @@ -33,6 +33,7 @@ con3: Activate synchronization point for
> SET DEBUG_SYNC= 'after_start_ddl SIGNAL con3_started WAIT_FOR status_shown';
> con3: Get a DDL going and stop in the middle
> ALTER TABLE bup_ddl_blocker.t2 ADD COLUMN col_b int;
> +DROP USER ''@'%';
> con1: Wait for con3 to reach its synchronization point.
> SET DEBUG_SYNC= 'now WAIT_FOR con3_started';
> con1: Activate synchronization points for backup.
> @@ -276,6 +277,7 @@ con3: Activate synchronization point for
> SET DEBUG_SYNC= 'after_start_ddl SIGNAL con3_started WAIT_FOR status_shown';
> con3: Get a DDL going and stop in the middle
> REPAIR TABLE bup_ddl_blocker.t2;
> +DROP USER ''@'%';
> con1: Wait for con3 to reach its synchronization point.
> SET DEBUG_SYNC= 'now WAIT_FOR con3_started';
> con1: Activate synchronization points for backup.
> @@ -506,6 +508,7 @@ con3: Activate synchronization point for
> SET DEBUG_SYNC= 'after_start_ddl SIGNAL con3_started WAIT_FOR status_shown';
> con3: Get a DDL going and stop in the middle
> DROP TABLE bup_ddl_blocker.t2;
> +DROP USER ''@'%';
> con1: Wait for con3 to reach its synchronization point.
> SET DEBUG_SYNC= 'now WAIT_FOR con3_started';
> con1: Activate synchronization points for backup.
> @@ -732,6 +735,7 @@ con3: Activate synchronization point for
> SET DEBUG_SYNC= 'after_start_ddl SIGNAL con3_started WAIT_FOR status_shown';
> con3: Get a DDL going and stop in the middle
> DROP DATABASE bup_ddl_blocker_2;
> +DROP USER ''@'%';
> con1: Wait for con3 to reach its synchronization point.
> SET DEBUG_SYNC= 'now WAIT_FOR con3_started';
> con1: Activate synchronization points for backup.
> @@ -953,6 +957,7 @@ con3: Activate synchronization point for
> SET DEBUG_SYNC= 'after_start_ddl SIGNAL con3_started WAIT_FOR status_shown';
> con3: Get a DDL going and stop in the middle
> ALTER DATABASE bup_ddl_blocker_2 CHARACTER SET latin2;
> +DROP USER ''@'%';
> con1: Wait for con3 to reach its synchronization point.
> SET DEBUG_SYNC= 'now WAIT_FOR con3_started';
> con1: Activate synchronization points for backup.
> @@ -1227,6 +1232,7 @@ con3: Activate synchronization point for
> SET DEBUG_SYNC= 'after_start_ddl SIGNAL con3_started WAIT_FOR status_shown';
> con3: Get a DDL going and stop in the middle
> DROP INDEX 2t1col_b ON bup_ddl_blocker_2.t1;
> +DROP USER ''@'%';
> con1: Wait for con3 to reach its synchronization point.
> SET DEBUG_SYNC= 'now WAIT_FOR con3_started';
> con1: Activate synchronization points for backup.
>
> === modified file 'mysql-test/r/backup_no_data.result'
> --- a/mysql-test/r/backup_no_data.result 2008-02-13 11:40:39 +0000
> +++ b/mysql-test/r/backup_no_data.result 2008-08-07 14:53:39 +0000
> @@ -2,6 +2,7 @@ DROP DATABASE IF EXISTS empty_db;
> DROP DATABASE IF EXISTS other_db;
> CREATE DATABASE empty_db;
> CREATE DATABASE other_db;
> +DROP USER ''@'%';
> BACKUP DATABASE empty_db TO 'empty_db.bak';
> backup_id
> #
>
> === modified file 'mysql-test/r/backup_views.result'
> --- a/mysql-test/r/backup_views.result 2008-08-05 08:04:30 +0000
> +++ b/mysql-test/r/backup_views.result 2008-08-07 14:53:39 +0000
> @@ -239,10 +239,10 @@ DROP DATABASE bup_db1;
> DROP DATABASE bup_db2;
> restore database with view dependency to other, non-existing db
> RESTORE FROM 'bup_objectview1.bak';
> -ERROR 42S02: Table 'bup_db2.t2' doesn't exist
> +ERROR HY000: Could not restore view `bup_db1`.`v5`
The ideal here would be to get an error message that contained the
information from both the old and the new message. Would that be
possible?
> DROP DATABASE bup_db1;
> RESTORE FROM 'bup_objectview2.bak';
> -ERROR 42S02: Table 'bup_db1.t3' doesn't exist
> +ERROR HY000: Could not restore view `bup_db2`.`student_details`
> DROP DATABASE bup_db2;
> RESTORE FROM 'bup_objectview.bak';
> backup_id
>
> === added file 'mysql-test/t/backup_db_grants.test'
> --- a/mysql-test/t/backup_db_grants.test 1970-01-01 00:00:00 +0000
> +++ b/mysql-test/t/backup_db_grants.test 2008-08-07 14:53:39 +0000
> @@ -0,0 +1,132 @@
> +#
> +# Test the backup of database grants
> +#
> +
> +--source include/not_embedded.inc
> +
> +--disable_warnings
> +DROP DATABASE IF EXISTS bup_db_grants;
> +DROP DATABASE IF EXISTS db2;
> +--enable_warnings
> +
> +--echo Create two databases and two tables.
Two? I see only one.
> +CREATE DATABASE bup_db_grants;
> +CREATE TABLE bup_db_grants.t1(a INT);
> +CREATE TABLE bup_db_grants.s1(b INT);
> +INSERT INTO bup_db_grants.t1 VALUES (1), (2), (3), (4);
> +INSERT INTO bup_db_grants.s1 VALUES (10), (20), (30), (40);
> +
> +--echo Now create users and assign various rights to the databases
> +CREATE USER 'bup_user1'@'%';
> +CREATE USER 'bup_user2'@'%';
> +CREATE USER 'bup_user3'@'%';
Since part of the new code is handling ticks, it would be great if you
could do some testing of user both with and without ticks.
> +REVOKE ALL ON *.* FROM 'bup_user1'@'%';
> +REVOKE ALL ON *.* FROM 'bup_user2'@'%';
> +GRANT ALL ON db2.* TO 'bup_user1'@'%';
> +GRANT SELECT ON bup_db_grants.* TO 'bup_user1'@'%';
> +GRANT INSERT ON bup_db_grants.* TO 'bup_user2'@'%';
> +GRANT ALL ON bup_db_grants.* TO 'no_user'@'%';
> +GRANT SELECT (a) ON bup_db_grants.t1 TO 'bup_user1'@'%';
> +GRANT SELECT (b), INSERT (b) ON bup_db_grants.s1 TO 'bup_user2'@'%';
> +FLUSH PRIVILEGES;
> +
> +--echo Demonstrate rights of the users.
> +SHOW GRANTS FOR 'bup_user1'@'%';
> +SHOW GRANTS FOR 'bup_user2'@'%';
> +SHOW GRANTS FOR 'bup_user3'@'%';
> +SHOW GRANTS FOR 'no_user'@'%';
> +
> +--echo Run backup
> +--replace_column 1 #
> +BACKUP DATABASE bup_db_grants TO 'bup_db_grants.bak';
> +
> +--echo Drop users and recreate them (removes grants completely).
> +DROP USER 'bup_user1'@'%';
> +DROP USER 'bup_user2'@'%';
> +DROP USER 'bup_user3'@'%';
> +DROP USER 'no_user'@'%';
> +
> +FLUSH PRIVILEGES;
> +
> +CREATE USER 'bup_user1'@'%';
> +CREATE USER 'bup_user2'@'%';
> +CREATE USER 'bup_user3'@'%';
> +
> +FLUSH PRIVILEGES;
> +
> +--echo Demonstrate rights of the users.
> +SHOW GRANTS FOR 'bup_user1'@'%';
> +SHOW GRANTS FOR 'bup_user2'@'%';
> +SHOW GRANTS FOR 'bup_user3'@'%';
> +
> +--echo Run Restore
> +--replace_column 1 #
> +RESTORE FROM 'bup_db_grants.bak';
> +
> +SHOW TABLES FROM bup_db_grants;
> +
> +--echo Demonstrate rights of the users.
> +# Note: Since db2 was not in the backup and the user was deleted prior to
> +# the backup, that privilege will not appear here.
I guess you mean 'prior to the restore'.
> +SHOW GRANTS FOR 'bup_user1'@'%';
> +SHOW GRANTS FOR 'bup_user2'@'%';
> +SHOW GRANTS FOR 'bup_user3'@'%';
> +--error ER_NONEXISTING_GRANT
> +SHOW GRANTS FOR 'no_user'@'%';
> +
> +--echo Now test what happens on backup if users are deleted.
> +
> +--echo Drop users and recreate one of them (removes grants completely).
> +DROP USER 'bup_user1'@'%';
> +DROP USER 'bup_user2'@'%';
> +DROP USER 'bup_user3'@'%';
> +
> +FLUSH PRIVILEGES;
> +
> +CREATE USER 'bup_user1'@'%';
> +CREATE USER 'bup_user1'@'nosuchhost';
> +
> +--echo Run Restore
> +--replace_column 1 #
> +RESTORE FROM 'bup_db_grants.bak';
> +
> +SHOW TABLES FROM bup_db_grants;
> +
> +--echo Demonstrate rights of the users.
> +SHOW GRANTS FOR 'bup_user1'@'%';
> +--error ER_NONEXISTING_GRANT
> +SHOW GRANTS FOR 'bup_user2'@'%';
> +--error ER_NONEXISTING_GRANT
> +SHOW GRANTS FOR 'bup_user3'@'%';
> +--error ER_NONEXISTING_GRANT
> +SHOW GRANTS FOR 'no_user'@'%';
> +
> +--echo Now demonstrate what happens when grants are altered in backup image.
> +
> +--exec sed 's/(a) ON bup/(a) ON boo/'
> $MYSQLTEST_VARDIR/master-data/bup_db_grants.bak >
> $MYSQLTEST_VARDIR/master-data/bup_db_grants.bad
> +--echo Run Restore
> +--error ER_BACKUP_GRANT_WRONG_DB
> +RESTORE FROM 'bup_db_grants.bad';
> +
> +FLUSH PRIVILEGES;
> +
> +--echo Cleanup
> +
> +DROP USER 'bup_user1'@'%';
> +DROP USER 'bup_user1'@'nosuchhost';
> +--error ER_CANNOT_USER
> +DROP USER 'bup_user2'@'%';
> +--error ER_CANNOT_USER
> +DROP USER 'bup_user3'@'%';
> +--error ER_CANNOT_USER
> +DROP USER 'no_user'@'%';
> +
> +FLUSH PRIVILEGES;
> +
> +DROP DATABASE bup_db_grants;
> +
> +--error 0,1
> +--remove_file $MYSQLTEST_VARDIR/master-data/bup_db_grants.bak
> +
> +--error 0,1
> +--remove_file $MYSQLTEST_VARDIR/master-data/bup_db_grants.bad
>
> === modified file 'mysql-test/t/backup_ddl_blocker.test'
> --- a/mysql-test/t/backup_ddl_blocker.test 2008-06-25 13:39:04 +0000
> +++ b/mysql-test/t/backup_ddl_blocker.test 2008-08-07 14:53:39 +0000
> @@ -146,6 +146,10 @@ send ALTER TABLE bup_ddl_blocker.t2 ADD
>
> connection con1;
>
> +#Drop of the user ''@'%' if it exists.
> +--error 0,ER_CANNOT_USER
> +DROP USER ''@'%';
> +
Why is this added? Did your code changes make this test fail without
it? Or are you trying to test something that was not tested before?
If the latter, how is it related to your code changes?
> --echo con1: Wait for con3 to reach its synchronization point.
> SET DEBUG_SYNC= 'now WAIT_FOR con3_started';
> --echo con1: Activate synchronization points for backup.
> @@ -419,6 +423,10 @@ send REPAIR TABLE bup_ddl_blocker.t2;
>
> connection con1;
>
> +#Drop of the user ''@'%' if it exists.
> +--error 0,ER_CANNOT_USER
> +DROP USER ''@'%';
> +
> --echo con1: Wait for con3 to reach its synchronization point.
> SET DEBUG_SYNC= 'now WAIT_FOR con3_started';
> --echo con1: Activate synchronization points for backup.
> @@ -680,6 +688,10 @@ send DROP TABLE bup_ddl_blocker.t2;
>
> connection con1;
>
> +#Drop of the user ''@'%' if it exists.
> +--error 0,ER_CANNOT_USER
> +DROP USER ''@'%';
> +
> --echo con1: Wait for con3 to reach its synchronization point.
> SET DEBUG_SYNC= 'now WAIT_FOR con3_started';
> --echo con1: Activate synchronization points for backup.
> @@ -965,6 +977,10 @@ send DROP DATABASE bup_ddl_blocker_2;
>
> connection con1;
>
> +#Drop of the user ''@'%' if it exists.
> +--error 0,ER_CANNOT_USER
> +DROP USER ''@'%';
> +
> --echo con1: Wait for con3 to reach its synchronization point.
> SET DEBUG_SYNC= 'now WAIT_FOR con3_started';
> --echo con1: Activate synchronization points for backup.
> @@ -1279,6 +1295,10 @@ send ALTER DATABASE bup_ddl_blocker_2 CH
>
> connection con1;
>
> +#Drop of the user ''@'%' if it exists.
> +--error 0,ER_CANNOT_USER
> +DROP USER ''@'%';
> +
> --echo con1: Wait for con3 to reach its synchronization point.
> SET DEBUG_SYNC= 'now WAIT_FOR con3_started';
> --echo con1: Activate synchronization points for backup.
> @@ -1628,6 +1648,10 @@ send DROP INDEX 2t1col_b ON bup_ddl_bloc
> # Start the backup and allow it to break on lock.
>
> connection con1;
> +
> +#Drop of the user ''@'%' if it exists.
> +--error 0,ER_CANNOT_USER
> +DROP USER ''@'%';
>
> --echo con1: Wait for con3 to reach its synchronization point.
> SET DEBUG_SYNC= 'now WAIT_FOR con3_started';
>
> === modified file 'mysql-test/t/backup_no_data.test'
> --- a/mysql-test/t/backup_no_data.test 2008-02-14 20:57:18 +0000
> +++ b/mysql-test/t/backup_no_data.test 2008-08-07 14:53:39 +0000
> @@ -8,6 +8,10 @@ DROP DATABASE IF EXISTS other_db;
> CREATE DATABASE empty_db;
> CREATE DATABASE other_db;
>
> +#Drop of the user ''@'%' if it exists.
> +--error 0,ER_CANNOT_USER
> +DROP USER ''@'%';
> +
> --error 0,1
> --remove_file $MYSQLTEST_VARDIR/master-data/empty_db.bak
> --replace_column 1 #
>
> === modified file 'mysql-test/t/backup_views.test'
> --- a/mysql-test/t/backup_views.test 2008-08-05 08:04:30 +0000
> +++ b/mysql-test/t/backup_views.test 2008-08-07 14:53:39 +0000
> @@ -188,14 +188,14 @@ DROP DATABASE bup_db2;
> #Individual databases cannot be restored because of VIEW DEPENDENCY
> --echo restore database with view dependency to other, non-existing db
>
> ---error ER_NO_SUCH_TABLE
> +--error ER_BACKUP_CANT_RESTORE_VIEW
> RESTORE FROM 'bup_objectview1.bak';
>
> # An incomplete bup_db1 was created by the failing restore operation.
> # Remove it before trying restore of bup_db2.
> DROP DATABASE bup_db1;
>
> ---error ER_NO_SUCH_TABLE
> +--error ER_BACKUP_CANT_RESTORE_VIEW
> RESTORE FROM 'bup_objectview2.bak';
>
> # An incomplete bup_db2 was created by the failing restore operation.
>
> === modified file 'sql/backup/backup_aux.h'
> --- a/sql/backup/backup_aux.h 2008-07-31 10:45:02 +0000
> +++ b/sql/backup/backup_aux.h 2008-08-07 14:53:39 +0000
> @@ -47,6 +47,12 @@ storage_engine_ref get_se_by_name(const
> namespace backup {
>
> /**
> + Constants for appending uniqueness to privileges in backup catalog.
> +*/
> +#define UNIQUE_PRIV_KEY_LEN 9
> +#define UNIQUE_PRIV_KEY_FORMAT "%08d"
> +
> +/**
> Local version of LEX_STRING structure.
>
> Defines various constructors for convenience.
>
> === modified file 'sql/backup/backup_info.cc'
> --- a/sql/backup/backup_info.cc 2008-08-05 08:04:30 +0000
> +++ b/sql/backup/backup_info.cc 2008-08-07 14:53:39 +0000
> @@ -309,7 +309,7 @@ Backup_info::Dep_node::get_key(const uch
> Backup_info::Backup_info(Backup_restore_ctx &ctx)
> :m_ctx(ctx), m_state(Backup_info::ERROR), native_snapshots(8),
> m_dep_list(NULL), m_dep_end(NULL),
> - m_srout_end(NULL), m_view_end(NULL), m_trigger_end(NULL)
> + m_srout_end(NULL), m_view_end(NULL), m_trigger_end(NULL), m_event_end(NULL)
> {
> using namespace backup;
>
> @@ -786,6 +786,18 @@ int Backup_info::add_db_items(Db &db)
> if (add_objects(db, BSTREAM_IT_TRIGGER, *it))
> goto error;
>
> + delete it;
> + it= get_all_db_grants(m_ctx.m_thd, &db.name());
> +
> + if (!it)
> + {
> + m_ctx.fatal_error(ER_BACKUP_LIST_DB_PRIV, db.name().ptr());
> + goto error;
> + }
> +
> + if (add_objects(db, BSTREAM_IT_PRIVILEGE, *it))
> + goto error;
> +
> goto finish;
>
> error:
> @@ -1002,7 +1014,7 @@ Backup_info::add_db_object(Db &db, const
> ulong pos= db.obj_count();
>
> DBUG_ASSERT(obj);
> - const ::String *name= obj->get_name();
> + String *name= (String *)obj->get_name();
> DBUG_ASSERT(name);
>
> switch (type) {
> @@ -1016,12 +1028,29 @@ Backup_info::add_db_object(Db &db, const
> case BSTREAM_IT_SFUNC: error= ER_BACKUP_CATALOG_ADD_SROUT; break;
> case BSTREAM_IT_EVENT: error= ER_BACKUP_CATALOG_ADD_EVENT; break;
> case BSTREAM_IT_TRIGGER: error= ER_BACKUP_CATALOG_ADD_TRIGGER; break;
> + case BSTREAM_IT_PRIVILEGE: error= ER_BACKUP_CATALOG_ADD_PRIV; break;
>
> // Only known types of objects should be added to the catalogue.
> default: DBUG_ASSERT(FALSE);
>
> }
>
> + /*
> + Generate a unique name for the privilege (grant) objects.
> + Note: this name does not alter the mechanics of the
> + grant objects in si_objects.cc
> + */
> + if (type == BSTREAM_IT_PRIVILEGE)
> + {
> + String new_name;
> + char buff[10];
> + sprintf(buff, UNIQUE_PRIV_KEY_FORMAT, pos);
> + new_name.append(*name);
> + new_name.append(" ");
> + new_name.append(buff);
> + name->copy(new_name);
> + }
> +
> /*
> Add new object to the dependency list. If it is a view, add its
> dependencies first.
> @@ -1068,9 +1097,8 @@ Backup_info::add_db_object(Db &db, const
> objects.
> */
>
> - // Add object to catalogue
> Dbobj *o= Image_info::add_db_object(db, type, *name, pos);
> -
> +
> if (!o)
> {
> m_ctx.fatal_error(error, db.name().ptr(), name->ptr());
> @@ -1199,6 +1227,12 @@ int Backup_info::add_to_dep_list(const o
> break;
>
> case BSTREAM_IT_EVENT:
> + end= &m_event_end;
> + if (!m_event_end)
> + m_event_end= m_trigger_end ? m_trigger_end : m_view_end ? m_view_end :
> m_srout_end;
> + break;
> +
> + case BSTREAM_IT_PRIVILEGE:
> end= &m_dep_end;
> break;
>
>
> === modified file 'sql/backup/backup_info.h'
> --- a/sql/backup/backup_info.h 2008-05-17 15:54:19 +0000
> +++ b/sql/backup/backup_info.h 2008-08-07 14:53:39 +0000
> @@ -141,6 +141,12 @@ class Backup_info: public backup::Image_
> */
> Dep_node *m_trigger_end;
>
> + /**
> + Points at the last event on the dependency list. NULL if events section
> + is empty.
> + */
> + Dep_node *m_event_end;
> +
> /**
> Hash keeping all elements stored in the dependency list.
>
>
> === modified file 'sql/backup/backup_test.cc'
> --- a/sql/backup/backup_test.cc 2008-06-25 13:39:04 +0000
> +++ b/sql/backup/backup_test.cc 2008-08-07 14:53:39 +0000
> @@ -45,14 +45,20 @@ int execute_backup_test_command(THD *thd
> field_list.push_back(new Item_empty_string("serialization", 13));
> protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
> Protocol::SEND_EOF);
>
> - obs::ObjIterator *it= obs::get_databases(thd);
> -
> - if (it)
> + //obs::ObjIterator *it= obs::get_databases(thd);
> + List_iterator<LEX_STRING> it(*db_list);
> +
> + //if (it)
> {
> obs::Obj *db;
>
> - while ((db= it->next()))
> + //while ((db= it->next()))
> + LEX_STRING *dbname;
> + while ((dbname= it++))
> {
> + String dir;
> + dir.copy(dbname->str, dbname->length, system_charset_info);
> + db= get_database(&dir);
>
> if (is_internal_db_name(db->get_db_name()))
> continue;
> @@ -224,20 +230,49 @@ int execute_backup_test_command(THD *thd
>
> delete event;
> }
> + }
>
> - delete tit;
> + //
> + // List db grants.
> + //
> + tit= obs::get_all_db_grants(thd, db->get_name());
> +
> + if (tit)
> + {
> + obs::Obj *grant;
> +
> + while ((grant= tit->next()))
> + {
> + String serial;
> + serial.length(0);
> + protocol->prepare_for_resend();
> + protocol->store(const_cast<String*>(db->get_name()));
> + protocol->store(const_cast<String*>(grant->get_name()));
> + String user;
> + String host;
> + String *user_host= (String *)grant->get_name();
> + user_exists(thd, user_host);
> + protocol->store(C_STRING_WITH_LEN("GRANT"),
> + system_charset_info);
> + grant->serialize(thd, &serial);
> + protocol->store(&serial);
> + protocol->write();
> +
> + delete grant;
> + }
> +
> + delete tit;
> }
>
> - // That's it.
> + // // That's it.
>
> - delete db;
> + // delete db;
> }
> }
> -
> - delete it;
> + thd->main_da.reset_diagnostics_area();
> +// delete it;
>
> my_eof(thd);
> -// delete i;
> DBUG_RETURN(res);
> }
>
>
> === modified file 'sql/backup/image_info.cc'
> --- a/sql/backup/image_info.cc 2008-07-31 10:45:02 +0000
> +++ b/sql/backup/image_info.cc 2008-08-07 14:53:39 +0000
> @@ -384,6 +384,7 @@ Image_info::Obj *find_obj(const Image_in
> case BSTREAM_IT_SFUNC:
> case BSTREAM_IT_EVENT:
> case BSTREAM_IT_TRIGGER:
> + case BSTREAM_IT_PRIVILEGE:
> {
> const st_bstream_dbitem_info &it=
> reinterpret_cast<const
> st_bstream_dbitem_info&>(item);
>
> === modified file 'sql/backup/image_info.h'
> --- a/sql/backup/image_info.h 2008-07-31 10:45:02 +0000
> +++ b/sql/backup/image_info.h 2008-08-07 14:53:39 +0000
> @@ -1012,6 +1012,17 @@ obs::Obj* Image_info::Dbobj::materialize
> case BSTREAM_IT_TRIGGER:
> m_obj_ptr= obs::materialize_trigger(db_name, name, ver, &sdata);
> break;
> + case BSTREAM_IT_PRIVILEGE:
> + {
> + /*
> + Here we undo the uniqueness suffix for grants.
> + */
> + String new_name;
> + new_name.copy(*name);
> + new_name.length(new_name.length() - UNIQUE_PRIV_KEY_LEN);
> + m_obj_ptr= obs::materialize_db_grant(db_name, &new_name, ver, &sdata);
> + break;
> + }
> default: m_obj_ptr= NULL;
> }
>
>
> === modified file 'sql/backup/kernel.cc'
> --- a/sql/backup/kernel.cc 2008-07-31 10:45:02 +0000
> +++ b/sql/backup/kernel.cc 2008-08-07 14:53:39 +0000
> @@ -1016,6 +1016,10 @@ int Backup_restore_ctx::do_restore()
>
> if (read_meta_data(info, s))
> {
> + // FIXME: detect errors.
> + // FIXME: error logging.
> + m_thd->main_da.reset_diagnostics_area();
> +
> fatal_error(ER_BACKUP_READ_META);
> DBUG_RETURN(m_error);
> }
> @@ -1388,6 +1392,7 @@ int bcat_add_item(st_bstream_image_heade
> case BSTREAM_IT_SFUNC:
> case BSTREAM_IT_EVENT:
> case BSTREAM_IT_TRIGGER:
> + case BSTREAM_IT_PRIVILEGE:
> {
> st_bstream_dbitem_info *it= (st_bstream_dbitem_info*)item;
>
> @@ -1399,7 +1404,6 @@ int bcat_add_item(st_bstream_image_heade
>
> Image_info::Dbobj *it1= info->add_db_object(*db, item->type, name_str,
> item->pos);
> -
> if (!it1)
> return BSTREAM_ERROR;
>
> @@ -1650,6 +1654,7 @@ int bcat_create_item(st_bstream_image_he
> case BSTREAM_IT_EVENT: create_err= ER_BACKUP_CANT_RESTORE_EVENT; break;
> case BSTREAM_IT_TRIGGER: create_err= ER_BACKUP_CANT_RESTORE_TRIGGER; break;
> case BSTREAM_IT_TABLESPACE: create_err= ER_BACKUP_CANT_RESTORE_TS; break;
> + case BSTREAM_IT_PRIVILEGE: create_err= ER_BACKUP_CANT_RESTORE_PRIV; break;
>
> /*
> TODO: Decide what to do when we come across unknown item:
> @@ -1736,6 +1741,48 @@ int bcat_create_item(st_bstream_image_he
>
> // Create the object.
>
> + /*
> + We need to check to see if the user exists (grantee) and if not,
> + do not execute the grant.
> + */
> + if (item->type == BSTREAM_IT_PRIVILEGE)
> + {
> + /*
> + Issue warning to the user that grant was skipped.
> +
> + @todo Replace write_message() call with the result of the revised
> + error handling work in WL#4384 with possible implementation
> + via a related bug report.
> + */
> + if (!obs::user_exists(thd, sobj->get_name()))
> + {
> + info->m_ctx.write_message(log_level::WARNING,
> + ER(ER_BACKUP_GRANT_SKIPPED),
> + create_stmt);
> + return BSTREAM_OK;
> + }
> + /*
> + We need to check the grant against the database list to ensure the
> + grants have not been altered to apply to another database.
> + */
> + ::String db_name; // db name extracted from grant statement
> + char *start;
> + char *end;
> + int size= 0;
> +
> + start= strstr((char *)create_stmt.begin, "ON ") + 3;
> + end= strstr(start, ".");
> + size= end - start;
> + db_name.alloc(size);
> + db_name.length(0);
> + db_name.append(start, size);
> + if (!info->has_db(db_name))
> + {
> + info->m_ctx.fatal_error(ER_BACKUP_GRANT_WRONG_DB, create_stmt);
> + return BSTREAM_ERROR;
> + }
> + }
> +
> if (sobj->execute(thd))
> {
> info->m_ctx.fatal_error(create_err, desc);
> @@ -1781,6 +1828,7 @@ int bcat_get_item_create_query(st_bstrea
> case BSTREAM_IT_EVENT: meta_err= ER_BACKUP_GET_META_EVENT; break;
> case BSTREAM_IT_TRIGGER: meta_err= ER_BACKUP_GET_META_TRIGGER; break;
> case BSTREAM_IT_TABLESPACE: meta_err= ER_BACKUP_GET_META_TS; break;
> + case BSTREAM_IT_PRIVILEGE: meta_err= ER_BACKUP_GET_META_PRIV; break;
>
> /*
> This can't happen - the item was obtained from the backup kernel.
>
> === modified file 'sql/share/errmsg.txt'
> --- a/sql/share/errmsg.txt 2008-07-09 07:12:43 +0000
> +++ b/sql/share/errmsg.txt 2008-08-07 14:53:39 +0000
> @@ -6372,3 +6372,15 @@ ER_BACKUP_OBTAIN_NAME_LOCK_FAILED
> eng "Restore failed to obtain the name locks on the tables."
> ER_BACKUP_RELEASE_NAME_LOCK_FAILED
> eng "Restore failed to release the name locks on the tables."
> +ER_BACKUP_LIST_DB_PRIV
> + eng "Can't enumerate grants in database %-.64s"
> +ER_BACKUP_CATALOG_ADD_PRIV
> + eng "Failed to add grant `%-.64s` to the catalog"
> +ER_BACKUP_GET_META_PRIV
> + eng "Failed to obtain meta-data for grant %-.64s."
> +ER_BACKUP_CANT_RESTORE_PRIV
> + eng "Could not execute grant %-.64s."
> +ER_BACKUP_GRANT_SKIPPED
> + eng "The grant '%-.64s' was skipped because the user does not exist."
> +ER_BACKUP_GRANT_WRONG_DB
> + eng "The grant '%-.64s' failed. Database not included in the backup image."
>
Inconsistent use of ticks in these messages. Both `, ', and no ticks
around %-.64s.
> === modified file 'sql/si_objects.cc'
> --- a/sql/si_objects.cc 2008-07-09 07:12:43 +0000
> +++ b/sql/si_objects.cc 2008-08-07 14:53:39 +0000
> @@ -759,6 +759,123 @@ private:
> String m_create_stmt;
> };
>
> +/**
> + @class DbGrantObj
> +
> + This class provides an abstraction to database-level grants.
> + This class will permit the recording and replaying of these
> + grants.
> +*/
> +class DbGrantObj : public Obj
> +{
> +public:
> + DbGrantObj(const String *grantee,
> + const String *db_name,
> + const String *priv_type);
> +
> +public:
> + virtual bool materialize(uint serialization_version,
> + const String *serialization);
> +
> + const String* get_name()
> + {
> + return &m_name;
> + }
> +
> + const String *get_db_name()
> + {
> + return &m_db_name;
> + }
> +
> + const String *get_priv_type()
> + {
> + return &m_priv_type;
> + }
> +
> +protected:
> + // These attributes are to be used only for serialization.
> + String m_db_name; ///< corresponds with TABLE_SCHEMA in IS tables.
> + String m_name; ///< name used to list in catalog.
> + String m_grantee; ///< corresponds with GRANTEE in IS tables.
> + String m_priv_type; ///< corresponds with PRIVILEGE_TYPE in IS tables.
> +
> + bool drop(THD *thd) { return 0; }; // Drop not supported.
> + virtual bool do_execute(THD *thd);
> +
> +private:
> + virtual bool do_serialize(THD *thd, String *serialization);
> + // These attributes are to be used only for materialization.
> + String m_grant_stmt;
> +};
> +
> +/**
> + @class TblGrantObj
> +
> + This class provides an abstraction to table-level and routine-level grants.
> + This class will permit the recording and replaying of these
> + grants.
> +*/
> +class TblGrantObj : public DbGrantObj
> +{
> +public:
> + TblGrantObj(const String *grantee,
> + const String *db_name,
> + const String *table_name,
> + const String *priv_type);
> +
> +public:
> +
> + const String *get_table_name()
> + {
> + return &m_table_name;
> + }
> +
> +protected:
> + // These attributes are to be used only for serialization.
> + String m_table_name; ///< corresponds with TABLE_NAME in IS tables.
> +
> +
> +private:
> + virtual bool do_serialize(THD *thd, String *serialization);
> +
> + // These attributes are to be used only for materialization.
> + String m_grant_stmt;
> +};
> +
> +/**
> + @class ColGrantObj
> +
> + This class provides an abstraction to column-level grants.
> + This class will permit the recording and replaying of these
> + grants.
> +*/
> +class ColGrantObj : public TblGrantObj
> +{
> +public:
> + ColGrantObj(const String *grantee,
> + const String *db_name,
> + const String *table_name,
> + const String *col_name,
> + const String *priv_type);
> +
> +public:
> +
> + const String *get_col_name()
> + {
> + return &m_col_name;
> + }
> +
> +protected:
> + // These attributes are to be used only for serialization.
> + String m_col_name; ///< corresponds with COLUMN_NAME in IS tables.
> +
> +private:
> + virtual bool do_serialize(THD *thd, String *serialization);
> +
> + // These attributes are to be used only for materialization.
> + String m_grant_stmt;
> +};
> +
> ///////////////////////////////////////////////////////////////////////////
>
> //
> @@ -938,6 +1055,67 @@ private:
> String m_db_name;
> };
>
> +class DbGrantIterator : public InformationSchemaIterator
> +{
> +public:
> + DbGrantIterator(THD *thd,
> + const String *db_name,
> + TABLE *is_table,
> + handler *ha,
> + my_bitmap_map *orig_columns) :
> + InformationSchemaIterator(thd, is_table, ha, orig_columns)
> + {
> + m_db_name.copy(*db_name);
> + }
> +
> +protected:
> + virtual DbGrantObj *create_obj(TABLE *t);
> +
> +private:
> + String m_db_name;
> +};
> +
> +class TblGrantIterator : public InformationSchemaIterator
> +{
> +public:
> + TblGrantIterator(THD *thd,
> + const String *db_name,
> + TABLE *is_table,
> + handler *ha,
> + my_bitmap_map *orig_columns) :
> + InformationSchemaIterator(thd, is_table, ha, orig_columns)
> + {
> + m_db_name.copy(*db_name);
> + }
> +
> +protected:
> + virtual TblGrantObj *create_obj(TABLE *t);
> +
> +private:
> + String m_db_name;
> +};
> +
> +class ColGrantIterator : public InformationSchemaIterator
> +{
> +public:
> + ColGrantIterator(THD *thd,
> + const String *db_name,
> + TABLE *is_table,
> + handler *ha,
> + my_bitmap_map *orig_columns) :
> + InformationSchemaIterator(thd, is_table, ha, orig_columns)
> + {
> + m_db_name.copy(*db_name);
> + }
> +
> +protected:
> + virtual ColGrantObj *create_obj(TABLE *t);
> +
> +private:
> + String m_db_name;
> +};
> +
> +
> ///////////////////////////////////////////////////////////////////////////
>
> class DbStoredFuncIterator : public DbStoredProcIterator
> @@ -1055,6 +1233,24 @@ bool InformationSchemaIterator::prepare_
> *is_table= open_schema_table(thd, st, NULL);
> break;
> }
> + case SCH_SCHEMA_PRIVILEGES:
> + {
> + st= find_schema_table(thd, "SCHEMA_PRIVILEGES");
> + *is_table= open_schema_table(thd, st, NULL);
> + break;
> + }
> + case SCH_TABLE_PRIVILEGES:
> + {
> + st= find_schema_table(thd, "TABLE_PRIVILEGES");
> + *is_table= open_schema_table(thd, st, NULL);
> + break;
> + }
> + case SCH_COLUMN_PRIVILEGES:
> + {
> + st= find_schema_table(thd, "COLUMN_PRIVILEGES");
> + *is_table= open_schema_table(thd, st, NULL);
> + break;
> + }
> default:
> {
> st= get_schema_table(is_table_idx);
> @@ -1320,6 +1516,123 @@ EventObj *DbEventIterator::create_obj(TA
> return new EventObj(&db_name, &event_name);
> }
> #endif
> +
> +///////////////////////////////////////////////////////////////////////////
> +
> +//
> +// Implementation: DbGrantIterator class.
> +//
> +
> +///////////////////////////////////////////////////////////////////////////
> +
> +DbGrantObj* DbGrantIterator::create_obj(TABLE *t)
> +{
> + String grantee; // corresponds with GRANTEE
> + String db_name; // corresponds with TABLE_SCHEMA
> + String priv_type; // corresponds with PRIVILEGE_TYPE
> +
> + t->field[0]->val_str(&grantee);
> + t->field[2]->val_str(&db_name);
> + t->field[3]->val_str(&priv_type);
> +
> + /*
> + The fill method for SCHEMA_PRIVILEGES does not use the COND portion
> + of the generic fill() method. Thus, we have to do the restriction here.
> +
> + Ensure the only rows sent back from iterator are the ones that match the
> + database specified.
> + */
> + if (db_name == m_db_name)
> + {
> + DBUG_PRINT("DbGrantIterator::create", (" Found grant %s %s %s",
> + db_name.ptr(), grantee.ptr(), priv_type.ptr()));
> +
> + return new DbGrantObj(&grantee, &db_name, &priv_type);
> + }
> + else
> + return NULL;
> +}
> +
> +///////////////////////////////////////////////////////////////////////////
> +
> +//
> +// Implementation: TblGrantIterator class.
> +//
> +
> +///////////////////////////////////////////////////////////////////////////
> +
> +TblGrantObj* TblGrantIterator::create_obj(TABLE *t)
> +{
> + String grantee; // corresponds with GRANTEE
> + String db_name; // corresponds with TABLE_SCHEMA
> + String tbl_name; // corresponds with TABLE_NAME
> + String priv_type; // corresponds with PRIVILEGE_TYPE
> +
> + t->field[0]->val_str(&grantee);
> + t->field[2]->val_str(&db_name);
> + t->field[3]->val_str(&tbl_name);
> + t->field[4]->val_str(&priv_type);
> +
> + /*
> + The fill method for TABLE_PRIVILEGES does not use the COND portion
> + of the generic fill() method. Thus, we have to do the restriction here.
> +
> + Ensure the only rows sent back from iterator are the ones that match the
> + database specified.
> + */
> + if (db_name == m_db_name)
> + {
> + DBUG_PRINT("TblGrantIterator::create", (" Found grant %s %s %s %s",
> + db_name.ptr(), grantee.ptr(), tbl_name.ptr(), priv_type.ptr()));
> +
> + return new TblGrantObj(&grantee, &db_name, &tbl_name,
> &priv_type);
> + }
> + else
> + return NULL;
> +}
> +
> +///////////////////////////////////////////////////////////////////////////
> +
> +//
> +// Implementation: ColGrantIterator class.
> +//
> +
> +///////////////////////////////////////////////////////////////////////////
> +
> +ColGrantObj* ColGrantIterator::create_obj(TABLE *t)
> +{
> + String grantee; // corresponds with GRANTEE
> + String db_name; // corresponds with TABLE_SCHEMA
> + String tbl_name; // corresponds with TABLE_NAME
> + String col_name; // corresponds with COLUMN_NAME
> + String priv_type; // corresponds with PRIVILEGE_TYPE
> +
> + t->field[0]->val_str(&grantee);
> + t->field[2]->val_str(&db_name);
> + t->field[3]->val_str(&tbl_name);
> + t->field[4]->val_str(&col_name);
> + t->field[5]->val_str(&priv_type);
> +
> + /*
> + The fill method for COLUMN_PRIVILEGES does not use the COND portion
> + of the generic fill() method. Thus, we have to do the restriction here.
> +
> + Ensure the only rows sent back from iterator are the ones that match the
> + database specified.
> + */
> + if (db_name == m_db_name)
> + {
> + DBUG_PRINT("ColGrantIterator::create", (" Found grant %s %s %s %s %s",
> + db_name.ptr(), grantee.ptr(), tbl_name.ptr(), col_name.ptr(),
> + priv_type.ptr()));
> +
> + return new ColGrantObj(&grantee, &db_name, &tbl_name,
> + &col_name, &priv_type);
> + }
> + else
> + return NULL;
> +}
> +
> ///////////////////////////////////////////////////////////////////////////
>
> //
> @@ -1502,6 +1815,18 @@ DbEventIterator *
> create_is_iterator<DbEventIterator>(THD *, enum_schema_tables, const String
> *);
> #endif
>
> +template
> +DbGrantIterator *
> +create_is_iterator<DbGrantIterator>(THD *, enum_schema_tables, const String
> *);
> +
> +template
> +TblGrantIterator *
> +create_is_iterator<TblGrantIterator>(THD *, enum_schema_tables, const String
> *);
> +
> +template
> +ColGrantIterator *
> +create_is_iterator<ColGrantIterator>(THD *, enum_schema_tables, const String
> *);
> +
> ObjIterator *get_db_tables(THD *thd, const String *db_name)
> {
> return create_is_iterator<DbTablesIterator>(thd, SCH_TABLES, db_name);
> @@ -1536,6 +1861,56 @@ ObjIterator *get_db_events(THD *thd, con
> #endif
> }
>
> +/**
> + GrantObjIterator constructor
> +
> + This constructor initializes iterators for the grants supported.
> + These include database-, table- and routine-, and column-level grants.
> + The iterators return all of the grants for the database specified.
> +*/
> +GrantObjIterator::GrantObjIterator(THD *thd, const String *db_name)
> +: ObjIterator()
> +{
> + db_grants= create_is_iterator<DbGrantIterator>(thd,
> + SCH_SCHEMA_PRIVILEGES,
> + db_name);
> + tbl_grants= create_is_iterator<TblGrantIterator>(thd,
> + SCH_TABLE_PRIVILEGES,
> + db_name);
> + col_grants= create_is_iterator<ColGrantIterator>(thd,
> + SCH_COLUMN_PRIVILEGES,
> + db_name);
> +}
> +
> +Obj *GrantObjIterator::next()
> +{
> + Obj *obj= 0;
> + obj= db_grants->next();
> + if (!obj)
> + obj= tbl_grants->next();
> + if (!obj)
> + obj= col_grants->next();
> + return obj;
> +}
> +
> +/**
> + Creates a high-level iterator that iterates over database-, table-,
> + routine-, and column-level privileges which shall permit a single
> + iterator from the si_objects to retrieve all of the privileges for
> + a given database.
> +
> + @param[IN] thd Current THD object
> + @param[IN] db_name Name of database to get grants
> +
> + @Note The client is responsible for destroying the returned iterator.
> +
> + @return a pointer to an iterator object.
> + @retval NULL in case of error.
> +*/
> +ObjIterator *get_all_db_grants(THD *thd, const String *db_name)
> +{
> + return new GrantObjIterator(thd, db_name);
> +}
>
> ///////////////////////////////////////////////////////////////////////////
>
> @@ -2581,6 +2956,191 @@ bool TablespaceObj::do_execute(THD *thd)
> }
>
> ///////////////////////////////////////////////////////////////////////////
> +//
> +// Implementation: DbGrantObj class.
> +//
> +/////////////////////////////////////////////////////////////////////////////
> +
> +DbGrantObj::DbGrantObj(const String *grantee,
> + const String *db_name,
> + const String *priv_type)
> +{
> + // copy strings to newly allocated memory
> + m_db_name.copy(*db_name);
> + m_grantee.copy(*grantee);
> + m_name.copy(*grantee);
> + m_priv_type.copy(*priv_type);
> +}
> +
> +/**
> + Serialize the object.
> +
> + This method produces the data necessary for materializing the object
> + on restore (creates object).
> +
> + @param[in] thd Thread context.
> + @param[out] serialization The data needed to recreate this object.
> +
> + @note this method will return an error if the db_name is either
> + mysql or information_schema as these are not objects that
> + should be recreated using this interface.
> +
> + @returns Error status.
> + @retval FALSE on success
> + @retval TRUE on error
> +*/
> +bool DbGrantObj::do_serialize(THD *thd, String *serialization)
> +{
> + DBUG_ENTER("DbGrantObj::do_serialize()");
> + serialization->length(0);
> + serialization->append("GRANT ");
> + serialization->append(m_priv_type);
> + serialization->append(" ON ");
> + serialization->append(m_db_name);
> + serialization->append(".* TO ");
> + serialization->append(m_grantee);
> + DBUG_RETURN(0);
> +}
> +
> +/**
> + Materialize the serialization string.
> +
> + This method saves serialization string into a member variable.
> +
> + @param[in] serialization_version version number of this interface
> + @param[in] serialization the string from serialize()
> +
> + @todo take serialization_version into account
> +
> + @returns Error status.
> + @retval FALSE on success
> + @retval TRUE on error
> +*/
> +bool DbGrantObj::materialize(uint serialization_version,
> + const String *serialization)
> +{
> + DBUG_ENTER("DbGrantObj::materialize()");
> + m_grant_stmt.copy(*serialization);
> + DBUG_RETURN(0);
> +}
> +
> +/**
> + Create the object.
> +
> + This method uses serialization string in a query and executes it.
> +
> + @param[in] thd Thread context.
> +
> + @returns Error status.
> + @retval FALSE on success
> + @retval TRUE on error
> +*/
> +bool DbGrantObj::do_execute(THD *thd)
> +{
> + DBUG_ENTER("DbGrantObj::do_execute()");
> + DBUG_RETURN(execute_with_ctx(thd, &m_grant_stmt, true));
> +}
> +
> +///////////////////////////////////////////////////////////////////////////
> +//
> +// Implementation: TblGrantObj class.
> +//
> +/////////////////////////////////////////////////////////////////////////////
> +
> +TblGrantObj::TblGrantObj(const String *grantee,
> + const String *db_name,
> + const String *table_name,
> + const String *priv_type)
> +: DbGrantObj(grantee, db_name, priv_type)
> +{
> + // copy strings to newly allocated memory
> + m_table_name.copy(*table_name);
> +}
> +
> +/**
> + Serialize the object.
> +
> + This method produces the data necessary for materializing the object
> + on restore (creates object).
> +
> + @param[in] thd Thread context.
> + @param[out] serialization The data needed to recreate this object.
> +
> + @note this method will return an error if the db_name is either
> + mysql or information_schema as these are not objects that
> + should be recreated using this interface.
> +
> + @returns Error status.
> + @retval FALSE on success
> + @retval TRUE on error
> +*/
> +bool TblGrantObj::do_serialize(THD *thd, String *serialization)
> +{
> + DBUG_ENTER("TblGrantObj::do_serialize()");
> + serialization->length(0);
> + serialization->append("GRANT ");
> + serialization->append(m_priv_type);
> + serialization->append(" ON ");
> + serialization->append(m_db_name);
> + serialization->append(".");
> + serialization->append(m_table_name);
> + serialization->append(" TO ");
> + serialization->append(m_grantee);
> + DBUG_RETURN(0);
> +}
> +
> +///////////////////////////////////////////////////////////////////////////
> +//
> +// Implementation: ColGrantObj class.
> +//
> +/////////////////////////////////////////////////////////////////////////////
> +
> +ColGrantObj::ColGrantObj(const String *grantee,
> + const String *db_name,
> + const String *table_name,
> + const String *col_name,
> + const String *priv_type)
> +: TblGrantObj(grantee, db_name, table_name, priv_type)
> +{
> + // copy strings to newly allocated memory
> + m_col_name.copy(*col_name);
> +}
> +
> +/**
> + Serialize the object.
> +
> + This method produces the data necessary for materializing the object
> + on restore (creates object).
> +
> + @param[in] thd Thread context.
> + @param[out] serialization The data needed to recreate this object.
> +
> + @note this method will return an error if the db_name is either
> + mysql or information_schema as these are not objects that
> + should be recreated using this interface.
> +
> + @returns Error status.
> + @retval FALSE on success
> + @retval TRUE on error
> +*/
> +bool ColGrantObj::do_serialize(THD *thd, String *serialization)
> +{
> + DBUG_ENTER("ColGrantObj::do_serialize()");
> + serialization->length(0);
> + serialization->append("GRANT ");
> + serialization->append(m_priv_type);
> + serialization->append("(");
> + serialization->append(m_col_name);
> + serialization->append(") ON ");
> + serialization->append(m_db_name);
> + serialization->append(".");
> + serialization->append(m_table_name);
> + serialization->append(" TO ");
> + serialization->append(m_grantee);
> + DBUG_RETURN(0);
> +}
> +
> +///////////////////////////////////////////////////////////////////////////
>
> Obj *get_database(const String *db_name)
> {
> @@ -2718,6 +3278,33 @@ Obj *materialize_tablespace(const String
> return obj;
> }
>
> +Obj *get_db_grant(const String *grantee,
> + const String *db_name)
> +{
> + String priv_type;
> + priv_type.length(0);
> +
> + return new DbGrantObj(grantee, db_name, &priv_type);
> +}
Why are you using a dummy priv_type here?
> +
> +Obj *materialize_db_grant(const String *db_name,
> + const String *grantee,
> + uint serialization_version,
> + const String *serialization)
> +{
> + /*
> + Here we create a grant for the purposes of applying the
> + grants. We use DbGrantObj for all types of grants because
> + we only have the GRANT statement in the serialization
> + string and therefore do not that the 'parts' to create
> + the specific types.
> + */
> + Obj *obj= get_db_grant(grantee, db_name);
> + obj->materialize(serialization_version, serialization);
> +
> + return obj;
> +}
> +
> ///////////////////////////////////////////////////////////////////////////
>
> bool is_internal_db_name(const String *db_name)
> @@ -2739,6 +3326,76 @@ bool is_internal_db_name(const String *d
> bool check_db_existence(const String *db_name)
> {
> return check_db_dir_existence(((String *) db_name)->c_ptr_safe());
> +}
> +
> +/*
> + Splits grantee clause into user and host portions. Needed for checking
> + to see if user exists on system.
> +*/
> +int split_user_host(String *grantee, String *user, String *host)
> +{
> + int len= 0;
> + int tics= 0;
> + char *ptr= strchr(grantee->c_ptr(), '@');
> +
> + if (ptr == 0)
> + return -1;
> + len= ptr - grantee->c_ptr();
> + user->length(0);
> + char *cptr= grantee->c_ptr();
> +
> + /*
> + String ' from strings.
> + */
> + if (strncmp(cptr, "'", 1) == 0)
> + {
> + cptr++;
> + len--;
> + tics++;
> + }
> + user->append(cptr, len - tics);
> + len= grantee->length() - len - 1 - tics;
> + host->length(0);
> +
> + /*
> + String ' from strings.
> + */
> + cptr= ptr + 1;
> + tics= 0;
> + if (strncmp(cptr, "'", 1) == 0)
> + {
> + cptr++;
> + len--;
> + }
> + if (strncmp(cptr+len-1, "'", 1) == 0)
> + tics++;
> + host->append(cptr, len - tics);
> + return 0;
> +}
Are there any encoding issues here, or is user name always ascii? I
guess the check for tics is not safe if there may be multibyte
characters in user names.
> +
> +/*
> + Returns TRUE if user is defined on the system.
> +*/
> +bool user_exists(THD *thd, const String *grantee)
> +{
> + String user;
> + String host;
> + bool user_exists= FALSE;
> +
> + user.length(0);
> + host.length(0);
> + if (grantee)
> + {
> +#ifndef EMBEDDED_LIBRARY
> + split_user_host((String *)grantee, &user, &host);
> + if (!user.ptr())
> + user.append("''");
> + user_exists= is_acl_user(host.ptr(), user.ptr());
> +#else
> + user_exists= TRUE;
> +#endif
> + }
> + return user_exists;
> }
>
> /**
>
> === modified file 'sql/si_objects.h'
> --- a/sql/si_objects.h 2008-07-05 08:41:26 +0000
> +++ b/sql/si_objects.h 2008-08-07 14:53:39 +0000
> @@ -115,6 +115,12 @@ private:
> friend Obj *materialize_tablespace(const String *,
> uint,
> const String *);
> +
> + friend Obj *materialize_db_grant(const String *,
> + const String *,
> + uint,
> + const String *);
> +
> };
>
>
> @@ -222,6 +228,39 @@ public:
>
> };
>
> +/**
> + GrantObjIternator is an encapsulation of the three iterators for each level
> + of grant supported: database-, table- and routine-, and column-level.
> +*/
> +class GrantObjIterator : public ObjIterator
> +{
> +public:
> + GrantObjIterator(THD *thd, const String *db_name);
> +
> + ~GrantObjIterator()
> + {
> + delete db_grants;
> + delete tbl_grants;
> + delete col_grants;
> + }
> +
> + /**
> + This operation returns a pointer to the next object in an enumeration.
> + It returns NULL if there is no more objects.
> +
> + The client is responsible to destroy the returned object.
> +
> + @return a pointer to the object
> + @retval NULL if there is no more objects in an enumeration.
> + */
> + Obj *next();
> +
> +private:
> + ObjIterator *db_grants; ///< database-level grants
> + ObjIterator *tbl_grants; ///< table- and routine-level grants
> + ObjIterator *col_grants; ///< column-level grants
> +};
> +
> ///////////////////////////////////////////////////////////////////////////
>
> // The functions in this section are intended to construct an instance of
> @@ -429,6 +468,14 @@ ObjIterator *get_db_stored_functions(THD
>
> ObjIterator *get_db_events(THD *thd, const String *db_name);
>
> +/*
> + Creates a high-level iterator that iterates over database-, table-,
> + routine-, and column-level privileges which shall permit a single
> + iterator from the si_objects to retrieve all of the privileges for
> + a given database.
> +*/
> +ObjIterator *get_all_db_grants(THD *thd, const String *db_name);
> +
> ///////////////////////////////////////////////////////////////////////////
>
> // The functions are intended to enumerate dependent objects.
> @@ -506,6 +553,11 @@ Obj *materialize_tablespace(const String
> uint serialization_version,
> const String *serialialization);
>
> +Obj *materialize_db_grant(const String *grantee,
> + const String *db_name,
> + uint serialization_version,
> + const String *serialization);
> +
> ///////////////////////////////////////////////////////////////////////////
>
> bool is_internal_db_name(const String *db_name);
> @@ -520,6 +572,11 @@ bool is_internal_db_name(const String *d
> @retval TRUE on error (the database either not exists, or not accessible).
> */
> bool check_db_existence(const String *db_name);
> +
> +/*
> + Returns TRUE if user is defined on the system.
> +*/
> +bool user_exists(THD *thd, const String *grantee);
>
> /*
> This method returns a @c TablespaceObj object if the table has a tablespace.
>
>