List:Commits« Previous MessageNext Message »
From:Chuck Bell Date:August 6 2008 10:19pm
Subject:bzr commit into mysql-6.0-backup branch (cbell:2674) WL#4073
View as plain text  
#At file:///C:/source/bzr/mysql-6.0-wl-4073/

 2674 Chuck Bell	2008-08-06
      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/t/backup_ddl_blocker.test
  mysql-test/t/backup_no_data.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/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.
  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 error messages for processing privileges and issuing warnings and errors
    during backup and restore.
  sql/si_objects.h
    Helper methods added to check to see if grantee exists and iterate
    over all grants found.
=== modified file 'mysql-test/lib/mtr_report.pl'
--- a/mysql-test/lib/mtr_report.pl	2008-07-09 07:12:43 +0000
+++ b/mysql-test/lib/mtr_report.pl	2008-08-06 20:20:17 +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-06 20:20:17 +0000
@@ -0,0 +1,130 @@
+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 ON bup_db1_pants.* TO 'bup_user1'@'%' failed.
Database not included in the backup image.
+Cleanup
+DROP USER bup_user1;
+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-06 20:20:17 +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-06 20:20:17 +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
 #

=== 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-06 20:20:17 +0000
@@ -0,0 +1,129 @@
+#
+# 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/ON bup_db_grants/ON bup_db1_pants/'
$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';
+
+--echo Cleanup
+
+DROP USER bup_user1;
+--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-06 20:20:17 +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-06 20:20:17 +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 '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-06 20:20:17 +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-07-31 10:45:02 +0000
+++ b/sql/backup/backup_info.cc	2008-08-06 20:20:17 +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,14 +1028,30 @@ 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);
+  }
   Dbobj *o= Image_info::add_db_object(db, type, *name, pos);
-  
+ 
   if (!o)
   {
     m_ctx.fatal_error(error, db.name().ptr(), name->ptr());
@@ -1188,6 +1216,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-06 20:20:17 +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-06 20:20:17 +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-06 20:20:17 +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-06 20:20:17 +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-06 20:20:17 +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-06 20:20:17 +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-06 20:20:17 +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("TblGrantIterator::next", (" 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::next", (" 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("TblGrantIterator::next", (" 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;
+}
+
 ///////////////////////////////////////////////////////////////////////////
 
 //
@@ -1536,6 +1849,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 +2944,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 +3266,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 +3314,73 @@ 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--;
+  }
+  if (strncmp(cptr+len-1, "'", 1) == 0)
+    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)
+  {
+    split_user_host((String *)grantee, &user, &host);
+    if (!user.ptr())
+      user.append("''");
+    return (is_acl_user(host.ptr(), user.ptr()));
+  }
+  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-06 20:20:17 +0000
@@ -25,9 +25,9 @@ public:
   bool serialize(THD *thd, String *serialialization);
 
   /**
-    Return the name of the object.
+    Return the user name of the object.
 
-    @return object name.
+    @return object user name.
   */
   virtual const String *get_name() = 0;
 
@@ -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:2674) WL#4073Chuck Bell6 Aug