List:Commits« Previous MessageNext Message »
From:Chuck Bell Date:August 7 2008 2:53pm
Subject:bzr commit into mysql-6.0-backup branch (cbell:2676) WL#4073
View as plain text  
#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`
 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.
+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'@'%';
+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.
+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 ''@'%';
+
 --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."

=== 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);
+}
+
+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;
+}
+
+/*
+  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.

Thread
bzr commit into mysql-6.0-backup branch (cbell:2676) WL#4073Chuck Bell7 Aug
  • Re: bzr commit into mysql-6.0-backup branch (cbell:2676) WL#4073Øystein Grøvlen18 Aug
    • RE: bzr commit into mysql-6.0-backup branch (cbell:2676) WL#4073Chuck Bell18 Aug
      • Re: bzr commit into mysql-6.0-backup branch (cbell:2676) WL#4073Øystein Grøvlen18 Aug
        • Re: bzr commit into mysql-6.0-backup branch (cbell:2676) WL#4073Øystein Grøvlen18 Aug
        • RE: bzr commit into mysql-6.0-backup branch (cbell:2676) WL#4073Chuck Bell18 Aug
          • Re: bzr commit into mysql-6.0-backup branch (cbell:2676) WL#4073Øystein Grøvlen19 Aug
            • RE: bzr commit into mysql-6.0-backup branch (cbell:2676) WL#4073Chuck Bell19 Aug
            • RE: bzr commit into mysql-6.0-backup branch (cbell:2676) WL#4073Chuck Bell19 Aug
RE: bzr commit into mysql-6.0-backup branch (cbell:2676) WL#4073Chuck Bell18 Aug