#At file:///C:/source/bzr/mysql-6.0-wl-4073/
2674 Chuck Bell 2008-08-05
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:
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/r/backup_db_grants.result
New result file.
mysql-test/t/backup_db_grants.test
New test for testing backup and restore of grants.
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.
=== 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-05 20:28:54 +0000
@@ -0,0 +1,126 @@
+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 '%'
+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;
=== 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-05 20:28:54 +0000
@@ -0,0 +1,119 @@
+#
+# 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 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
=== 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-05 20:28:54 +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, "%05d", 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-05 20:28:54 +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-05 20:28:54 +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-05 20:28:54 +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-05 20:28:54 +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() - 6);
+ 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-05 20:28:54 +0000
@@ -1388,6 +1388,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 +1400,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 +1650,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 +1737,20 @@ 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)
+ if (!obs::user_exists(thd, sobj->get_name()))
+ {
+ // FIXME : Allow this warning to be posted to the user.
+ //push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ // ER_BACKUP_GRANT_SKIPPED, ER(ER_BACKUP_GRANT_SKIPPED),
+ // sobj->get_name()->ptr());
+ return BSTREAM_OK;
+ }
+
if (sobj->execute(thd))
{
info->m_ctx.fatal_error(create_err, desc);
@@ -1781,6 +1796,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-05 20:28:54 +0000
@@ -6372,3 +6372,13 @@ 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 "Grant skipped because user %-.64s does not exist."
=== 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-05 20:28:54 +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-05 20:28:54 +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#4073 | Chuck Bell | 5 Aug |