From: rsomla Date: May 7 2008 1:07pm Subject: bk commit into 6.0 tree (rafal:1.2614) WL#4211 List-Archive: http://lists.mysql.com/commits/46453 Message-Id: Below is the list of changes that have just been committed into a local 6.0 repository of rafal. When rafal does a push these changes will be propagated to the main repository and, within 24 hours after the push, to the public repository. For information on how to access the public repository see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html ChangeSet@stripped, 2008-05-07 15:07:31+02:00, rafal@quant.(none) +6 -0 WL#4211 (Online Backup: Dependency checking) The patch implements design described in the worklog. In short, all objects added to the backup catalogue are also put on a dependency list ensuring correct ordering among them. This list determines the order in which object metadata is saved in the backup image and thus the order in which they are restored. mysql-test/r/backup_objects.result@stripped, 2008-05-07 15:07:24+02:00, rafal@quant.(none) +229 -243 Result set update. mysql-test/t/backup_objects.test@stripped, 2008-05-07 15:07:25+02:00, rafal@quant.(none) +153 -106 Test is modified to also check if dependencies between objects being backed up and restored are handled correctly. For that, more complex dependencies between the objects are intorduced. sql/backup/backup_info.cc@stripped, 2008-05-07 15:07:25+02:00, rafal@quant.(none) +530 -2 - Definition of Backup_info::Dep_node structure used also as hash entry. - Initialize dependency list pointers in Backup_info constructor. - Initialize/deinitialize dep_hash in Backup_info constructor/destructor. - Implementation of the view dependencies handling algorithm in Backup_info::add_view_deps(). - Modify add_db_object() method so that: all objects are added to the correct section of the dependency list using add_to_dep_list(); in case of a view, its dependencies are handled using add_view_deps(). - Implementation of Backup_info::get_dep_node() and Backup_info::add_to_dep_list(). - Implementation of Global_iterator and Perdb_iterator. The latter uses the dependency list created by add_db_object(). sql/backup/backup_info.h@stripped, 2008-05-07 15:07:25+02:00, rafal@quant.(none) +64 -0 - Mave Global_iterator and Perdb_iterator from Image_info class to Backup_info. - Add to Backup_info datatypes, variables and functions implementing the dependency list: Dep_node structure for list nodes; m_dep_list and other pointers marking sections of the list; dep_hash hash; functions get_dep_node() and add_to_dep_list(). - Add to Backup_info function add_view_deps() for handling view dependencies. sql/backup/image_info.h@stripped, 2008-05-07 15:07:25+02:00, rafal@quant.(none) +2 -179 - Make mem_root protected so that it can be used in derived clasess (e.g. Backup_info). - Move Global_iterator and Perdb_iterator to Backup_info class. sql/backup/kernel.cc@stripped, 2008-05-07 15:07:25+02:00, rafal@quant.(none) +8 -6 Functions such as bcat_iterator_get() do not need to know about different iterator classes. It is enough to use the base class Image_info::Iterator whose virtual methods are redefined in the derived classes. diff -Nrup a/mysql-test/r/backup_objects.result b/mysql-test/r/backup_objects.result --- a/mysql-test/r/backup_objects.result 2008-02-13 12:40:24 +01:00 +++ b/mysql-test/r/backup_objects.result 2008-05-07 15:07:24 +02:00 @@ -1,3 +1,4 @@ +SET storage_engine=MyISAM; Starting Test - Backup @@ -5,289 +6,274 @@ Setting SQL_MODE = PIPES_AS_CONCAT SET SQL_MODE = 'PIPES_AS_CONCAT'; Change client connection charset SET character_set_client = 'latin2'; -DROP DATABASE IF EXISTS bup_objects; -CREATE DATABASE bup_objects; -con1: Creating table -CREATE TABLE bup_objects.t1 (col_a int, col_b CHAR(40)) ENGINE=INNODB; -CREATE TABLE bup_objects.t2 (col_a int, col_b CHAR(40)) ENGINE=INNODB; -con1: Loading data -INSERT INTO bup_objects.t1 VALUES (01, "101 Some data to test"); -INSERT INTO bup_objects.t1 VALUES (02, "102 Some data to test"); -INSERT INTO bup_objects.t1 VALUES (03, "103 Some data to test"); -INSERT INTO bup_objects.t1 VALUES (04, "201 Some data to test"); -INSERT INTO bup_objects.t1 VALUES (05, "202 Some data to test"); -INSERT INTO bup_objects.t1 VALUES (06, "203 Some data to test"); -INSERT INTO bup_objects.t1 VALUES (07, "301 Some data to test"); -INSERT INTO bup_objects.t1 VALUES (08, "302 Some data to test"); -INSERT INTO bup_objects.t1 VALUES (09, "303 Some data to test"); -INSERT INTO bup_objects.t1 VALUES (10, "401 Some data to test"); -INSERT INTO bup_objects.t1 VALUES (11, "402 Some data to test"); -INSERT INTO bup_objects.t1 VALUES (12, "403 Some data to test"); -INSERT INTO bup_objects.t2 VALUES (01, "101 Some data to test"); -INSERT INTO bup_objects.t2 VALUES (02, "102 Some data to test"); -INSERT INTO bup_objects.t2 VALUES (03, "103 Some data to test"); -CREATE VIEW bup_objects.v1 AS SELECT * FROM bup_objects.t1 WHERE col_a < 5; -CREATE VIEW bup_objects.v2 AS SELECT * FROM bup_objects.t1 WHERE col_a >= 5; -CREATE TRIGGER bup_objects.ins_t1 AFTER INSERT ON bup_objects.t1 FOR EACH ROW +DROP DATABASE IF EXISTS db1; +DROP DATABASE IF EXISTS db2; +CREATE DATABASE db1; +CREATE DATABASE db2; +CREATE PROCEDURE db1.p11() BEGIN -DELETE FROM bup_objects.t2 WHERE col_a > 1; +UPDATE db2.t21 SET b = (SELECT b FROM db1.v11 WHERE a = 1); +SELECT db2.f21(7); END; || -CREATE EVENT bup_objects.e1 ON SCHEDULE EVERY 1 YEAR DO -DELETE FROM bup_objects.t2 WHERE col_a > 100; +CREATE FUNCTION db2.f21(a int) RETURNS INTEGER +BEGIN +RETURN a; +END; || -CREATE PROCEDURE bup_objects.p1() +CREATE PROCEDURE db2.p21() BEGIN -UPDATE bup_objects.t1 SET col_b = "Procedure p1 was here." WHERE col_a < 3; +CALL db1.p11(); +SELECT x.a FROM v22 as x, db1.t13 as y WHERE x.b = y.a; END; || -CREATE FUNCTION bup_objects.f1() RETURNS INTEGER +CREATE TABLE db1.t11(a int, primary key (a)); +CREATE TABLE db1.t12(a int, b int, foreign key (b) references db1.t11(a)); +CREATE TABLE db2.t21(a int, b int, primary key(a), foreign key (b) references db1.t11(a)); +CREATE TABLE db1.t13(a int, b int, foreign key (b) references db2.t21(a)); +CREATE VIEW db1.v12 AS SELECT * FROM db2.t21; +CREATE VIEW db2.v22 AS SELECT * FROM db1.t12; +CREATE VIEW db1.v11 AS SELECT db2.f21(x.a) AS a, y.b FROM db2.v22 AS x, db1.t13 AS y WHERE x.b = y.a; +CREATE VIEW db2.v21 AS SELECT x.b, db2.f21(y.a) AS a FROM db1.v11 AS x, db2.v22 AS y WHERE x.a = y.b; +CREATE TRIGGER db1.tr11 AFTER DELETE ON db1.t11 FOR EACH ROW BEGIN -DECLARE v_out INT; -SELECT count(*) FROM bup_objects.t1 INTO v_out; -RETURN v_out; +DELETE FROM db2.t1 WHERE a > 1; END; || -Using the objects. -INSERT INTO bup_objects.t1 VALUES (30, "a new row."); -CALL bup_objects.p1(); -SELECT bup_objects.f1(); -bup_objects.f1() -13 -Showing objects and create statements. -SHOW CREATE DATABASE bup_objects;; -Database bup_objects -Create Database CREATE DATABASE `bup_objects` /*!40100 DEFAULT CHARACTER SET latin1 */ -SHOW CREATE TABLE bup_objects.t1;; -Table t1 -Create Table CREATE TABLE `t1` ( - `col_a` int(11) DEFAULT NULL, - `col_b` char(40) DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=latin1 -SHOW CREATE TABLE bup_objects.t2;; -Table t2 -Create Table CREATE TABLE `t2` ( - `col_a` int(11) DEFAULT NULL, - `col_b` char(40) DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=latin1 -SHOW CREATE VIEW bup_objects.v1;; -View v1 -Create View CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `bup_objects`.`v1` AS select `bup_objects`.`t1`.`col_a` AS `col_a`,`bup_objects`.`t1`.`col_b` AS `col_b` from `bup_objects`.`t1` where (`bup_objects`.`t1`.`col_a` < 5) -character_set_client latin2 -collation_connection latin1_swedish_ci -SHOW CREATE VIEW bup_objects.v2;; -View v2 -Create View CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `bup_objects`.`v2` AS select `bup_objects`.`t1`.`col_a` AS `col_a`,`bup_objects`.`t1`.`col_b` AS `col_b` from `bup_objects`.`t1` where (`bup_objects`.`t1`.`col_a` >= 5) -character_set_client latin2 -collation_connection latin1_swedish_ci -SHOW CREATE PROCEDURE bup_objects.p1;; -Procedure p1 -sql_mode PIPES_AS_CONCAT -Create Procedure CREATE DEFINER=`root`@`localhost` PROCEDURE `p1`() +CREATE TRIGGER db2.tr21 AFTER DELETE ON db2.t21 FOR EACH ROW BEGIN -UPDATE bup_objects.t1 SET col_b = "Procedure p1 was here." WHERE col_a < 3; +DELETE FROM db1.t2 WHERE a > 1; +END; +|| +CREATE EVENT db1.e11 ON SCHEDULE EVERY 1 YEAR DO +DELETE FROM db2.t21 WHERE b > 100; +|| +CREATE EVENT db2.e21 ON SCHEDULE EVERY 1 YEAR DO +DELETE FROM db1.t12 WHERE b > 100; +|| +SET foreign_key_checks = 1; +SHOW VARIABLES LIKE 'foreign_key_checks%'; +Variable_name Value +foreign_key_checks ON +INSERT INTO db1.t11 VALUES (1),(2),(3); +INSERT INTO db1.t12 VALUES (3,1), (2,2), (1,3); +INSERT INTO db2.t21 VALUES (100,1), (200,2); +INSERT INTO db1.t13 VALUES (1,200), (2,100); +SELECT * FROM db1.v11; +a b +3 200 +2 100 +SELECT * FROM db1.v12; +a b +100 1 +200 2 +SELECT * FROM db2.v21; +b a +100 2 +200 1 +SELECT * FROM db2.v22; +a b +3 1 +2 2 +1 3 +Backup databases. +BACKUP DATABASE db1, db2 TO 'bup_objects.bak'; +backup_id +# +Dropping databases. +DROP DATABASE db1; +DROP DATABASE db2; +Setting SQL_MODE = '' +SET SQL_MODE = ''; +Change client connection charset +SET character_set_client = 'latin1'; +RESTORE FROM 'bup_objects.bak'; +backup_id +# +SHOW TABLES IN db1; +Tables_in_db1 +t11 +t12 +t13 +v11 +v12 +SHOW TABLES IN db2; +Tables_in_db2 +t21 +v21 +v22 +SHOW TRIGGERS IN db1;; +Trigger tr11 +Event DELETE +Table t11 +Statement BEGIN +DELETE FROM db2.t1 WHERE a > 1; END +Timing AFTER +Created NULL +sql_mode PIPES_AS_CONCAT +Definer root@localhost character_set_client latin2 collation_connection latin1_swedish_ci Database Collation latin1_swedish_ci -SHOW CREATE FUNCTION bup_objects.f1;; -Function f1 -sql_mode PIPES_AS_CONCAT -Create Function CREATE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS int(11) -BEGIN -DECLARE v_out INT; -SELECT count(*) FROM bup_objects.t1 INTO v_out; -RETURN v_out; +SHOW TRIGGERS IN db2;; +Trigger tr21 +Event DELETE +Table t21 +Statement BEGIN +DELETE FROM db1.t2 WHERE a > 1; END +Timing AFTER +Created NULL +sql_mode PIPES_AS_CONCAT +Definer root@localhost character_set_client latin2 collation_connection latin1_swedish_ci Database Collation latin1_swedish_ci -SHOW CREATE TRIGGER bup_objects.ins_t1;; -Trigger ins_t1 -sql_mode PIPES_AS_CONCAT -SQL Original Statement CREATE DEFINER=`root`@`localhost` TRIGGER bup_objects.ins_t1 AFTER INSERT ON bup_objects.t1 FOR EACH ROW -BEGIN -DELETE FROM bup_objects.t2 WHERE col_a > 1; -END +SHOW EVENTS IN db1;; +Db db1 +Name e11 +Definer root@localhost +Time zone SYSTEM +Type RECURRING +Execute at NULL +Interval value 1 +Interval field YEAR +Starts # +Ends NULL +Status ENABLED +Originator 1 +character_set_client latin2 +collation_connection latin1_swedish_ci +Database Collation latin1_swedish_ci +SHOW EVENTS IN db2;; +Db db2 +Name e21 +Definer root@localhost +Time zone SYSTEM +Type RECURRING +Execute at NULL +Interval value 1 +Interval field YEAR +Starts # +Ends NULL +Status ENABLED +Originator 1 +character_set_client latin2 +collation_connection latin1_swedish_ci +Database Collation latin1_swedish_ci +SHOW PROCEDURE STATUS WHERE Db='db1' OR Db='db2';; +Db db1 +Name p11 +Type PROCEDURE +Definer root@localhost +Modified # +Created # +Security_type DEFINER +Comment +character_set_client latin2 +collation_connection latin1_swedish_ci +Database Collation latin1_swedish_ci +Db db2 +Name p21 +Type PROCEDURE +Definer root@localhost +Modified # +Created # +Security_type DEFINER +Comment +character_set_client latin2 +collation_connection latin1_swedish_ci +Database Collation latin1_swedish_ci +SHOW FUNCTION STATUS WHERE Db='db1' OR Db='db2';; +Db db2 +Name f21 +Type FUNCTION +Definer root@localhost +Modified # +Created # +Security_type DEFINER +Comment character_set_client latin2 collation_connection latin1_swedish_ci Database Collation latin1_swedish_ci -SELECT * FROM INFORMATION_SCHEMA.EVENTS WHERE event_schema = 'bup_objects' AND event_name = 'e1';; -EVENT_CATALOG NULL -EVENT_SCHEMA bup_objects -EVENT_NAME e1 -DEFINER root@localhost -TIME_ZONE SYSTEM -EVENT_BODY SQL -EVENT_DEFINITION DELETE FROM bup_objects.t2 WHERE col_a > 100 -EVENT_TYPE RECURRING -EXECUTE_AT NULL -INTERVAL_VALUE 1 -INTERVAL_FIELD YEAR -SQL_MODE PIPES_AS_CONCAT -STARTS # -ENDS # -STATUS ENABLED -ON_COMPLETION NOT PRESERVE -CREATED # -LAST_ALTERED # -LAST_EXECUTED # -EVENT_COMMENT -ORIGINATOR 1 -CHARACTER_SET_CLIENT latin2 -COLLATION_CONNECTION latin1_swedish_ci -DATABASE_COLLATION latin1_swedish_ci -SELECT table_name FROM INFORMATION_SCHEMA.TABLES -WHERE table_schema = 'bup_objects'; -table_name -t1 -t2 -v1 -v2 -SELECT table_name as view_name FROM INFORMATION_SCHEMA.VIEWS -WHERE table_schema = 'bup_objects'; -view_name -v1 -v2 -SELECT routine_name as proc_name FROM INFORMATION_SCHEMA.ROUTINES -WHERE routine_schema = 'bup_objects' AND routine_type = 'PROCEDURE'; -proc_name -p1 -SELECT routine_name as func_name FROM INFORMATION_SCHEMA.ROUTINES -WHERE routine_schema = 'bup_objects' AND routine_type = 'FUNCTION'; -func_name -f1 -SELECT trigger_name, event_manipulation, event_object_table -FROM INFORMATION_SCHEMA.TRIGGERS WHERE trigger_schema = 'bup_objects'; -trigger_name event_manipulation event_object_table -ins_t1 INSERT t1 -SELECT event_name FROM INFORMATION_SCHEMA.EVENTS -WHERE event_schema = 'bup_objects'; -event_name -e1 -Backup data. -BACKUP DATABASE bup_objects TO 'bup_objects.bak'; -backup_id -# -Dropping database. -DROP DATABASE bup_objects; -Setting SQL_MODE = '' -SET SQL_MODE = ''; -Change client connection charset -SET character_set_client = 'latin1'; -RESTORE FROM 'bup_objects.bak'; -backup_id -# Showing objects and create statements. -SHOW CREATE DATABASE bup_objects;; -Database bup_objects -Create Database CREATE DATABASE `bup_objects` /*!40100 DEFAULT CHARACTER SET latin1 */ -SHOW CREATE TABLE bup_objects.t1;; -Table t1 -Create Table CREATE TABLE `t1` ( - `col_a` int(11) DEFAULT NULL, - `col_b` char(40) DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=latin1 -SHOW CREATE TABLE bup_objects.t2;; -Table t2 -Create Table CREATE TABLE `t2` ( - `col_a` int(11) DEFAULT NULL, - `col_b` char(40) DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=latin1 -SHOW CREATE VIEW bup_objects.v1;; -View v1 -Create View CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`col_a` AS `col_a`,`t1`.`col_b` AS `col_b` from `t1` where (`t1`.`col_a` < 5) -character_set_client latin1 -collation_connection latin1_swedish_ci -SHOW CREATE VIEW bup_objects.v2;; -View v2 -Create View CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v2` AS select `t1`.`col_a` AS `col_a`,`t1`.`col_b` AS `col_b` from `t1` where (`t1`.`col_a` >= 5) -character_set_client latin1 +SHOW CREATE DATABASE db1;; +Database db1 +Create Database CREATE DATABASE `db1` /*!40100 DEFAULT CHARACTER SET latin1 */ +SHOW CREATE TABLE db1.t13;; +Table t13 +Create Table CREATE TABLE `t13` ( + `a` int(11) DEFAULT NULL, + `b` int(11) DEFAULT NULL, + KEY `b` (`b`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SHOW CREATE TABLE db2.t21;; +Table t21 +Create Table CREATE TABLE `t21` ( + `a` int(11) NOT NULL DEFAULT '0', + `b` int(11) DEFAULT NULL, + PRIMARY KEY (`a`), + KEY `b` (`b`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SHOW CREATE VIEW db1.v11;; +View v11 +Create View CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `db1`.`v11` AS select `db2`.`f21`(`x`.`a`) AS `a`,`y`.`b` AS `b` from (`db2`.`v22` `x` join `db1`.`t13` `y`) where (`x`.`b` = `y`.`a`) +character_set_client latin2 +collation_connection latin1_swedish_ci +SHOW CREATE VIEW db2.v21;; +View v21 +Create View CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `db2`.`v21` AS select `x`.`b` AS `b`,`db2`.`f21`(`y`.`a`) AS `a` from (`db1`.`v11` `x` join `db2`.`v22` `y`) where (`x`.`a` = `y`.`b`) +character_set_client latin2 collation_connection latin1_swedish_ci -SHOW CREATE PROCEDURE bup_objects.p1;; -Procedure p1 +SHOW CREATE PROCEDURE db1.p11;; +Procedure p11 sql_mode PIPES_AS_CONCAT -Create Procedure CREATE DEFINER=`root`@`localhost` PROCEDURE `p1`() +Create Procedure CREATE DEFINER=`root`@`localhost` PROCEDURE `p11`() BEGIN -UPDATE bup_objects.t1 SET col_b = "Procedure p1 was here." WHERE col_a < 3; +UPDATE db2.t21 SET b = (SELECT b FROM db1.v11 WHERE a = 1); +SELECT db2.f21(7); END character_set_client latin2 collation_connection latin1_swedish_ci Database Collation latin1_swedish_ci -SHOW CREATE FUNCTION bup_objects.f1;; -Function f1 +SHOW CREATE FUNCTION db2.f21;; +Function f21 sql_mode PIPES_AS_CONCAT -Create Function CREATE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS int(11) +Create Function CREATE DEFINER=`root`@`localhost` FUNCTION `f21`(a int) RETURNS int(11) BEGIN -DECLARE v_out INT; -SELECT count(*) FROM bup_objects.t1 INTO v_out; -RETURN v_out; +RETURN a; END character_set_client latin2 collation_connection latin1_swedish_ci Database Collation latin1_swedish_ci -SHOW CREATE TRIGGER bup_objects.ins_t1;; -Trigger ins_t1 +SHOW CREATE TRIGGER db2.tr21;; +Trigger tr21 sql_mode PIPES_AS_CONCAT -SQL Original Statement CREATE DEFINER=`root`@`localhost` TRIGGER bup_objects.ins_t1 AFTER INSERT ON bup_objects.t1 FOR EACH ROW +SQL Original Statement CREATE DEFINER=`root`@`localhost` TRIGGER db2.tr21 AFTER DELETE ON db2.t21 FOR EACH ROW BEGIN -DELETE FROM bup_objects.t2 WHERE col_a > 1; +DELETE FROM db1.t2 WHERE a > 1; END character_set_client latin2 collation_connection latin1_swedish_ci Database Collation latin1_swedish_ci -SELECT * FROM INFORMATION_SCHEMA.EVENTS WHERE event_schema = 'bup_objects' AND event_name = 'e1';; -EVENT_CATALOG NULL -EVENT_SCHEMA bup_objects -EVENT_NAME e1 -DEFINER root@localhost -TIME_ZONE SYSTEM -EVENT_BODY SQL -EVENT_DEFINITION DELETE FROM bup_objects.t2 WHERE col_a > 100 -EVENT_TYPE RECURRING -EXECUTE_AT NULL -INTERVAL_VALUE 1 -INTERVAL_FIELD YEAR -SQL_MODE PIPES_AS_CONCAT -STARTS # -ENDS # -STATUS ENABLED -ON_COMPLETION NOT PRESERVE -CREATED # -LAST_ALTERED # -LAST_EXECUTED # -EVENT_COMMENT -ORIGINATOR 1 -CHARACTER_SET_CLIENT latin2 -COLLATION_CONNECTION latin1_swedish_ci -DATABASE_COLLATION latin1_swedish_ci -SELECT table_name FROM INFORMATION_SCHEMA.TABLES -WHERE table_schema = 'bup_objects'; -table_name -t1 -t2 -v1 -v2 -SELECT table_name as view_name FROM INFORMATION_SCHEMA.VIEWS -WHERE table_schema = 'bup_objects'; -view_name -v1 -v2 -SELECT routine_name as proc_name FROM INFORMATION_SCHEMA.ROUTINES -WHERE routine_schema = 'bup_objects' AND routine_type = 'PROCEDURE'; -proc_name -p1 -SELECT routine_name as func_name FROM INFORMATION_SCHEMA.ROUTINES -WHERE routine_schema = 'bup_objects' AND routine_type = 'FUNCTION'; -func_name -f1 -SELECT trigger_name, event_manipulation, event_object_table -FROM INFORMATION_SCHEMA.TRIGGERS WHERE trigger_schema = 'bup_objects'; -trigger_name event_manipulation event_object_table -ins_t1 INSERT t1 -SELECT event_name FROM INFORMATION_SCHEMA.EVENTS -WHERE event_schema = 'bup_objects'; -event_name -e1 +SELECT * FROM db1.v11; +a b +3 200 +2 100 +SELECT * FROM db1.v12; +a b +100 1 +200 2 +SELECT * FROM db2.v21; +b a +100 2 +200 1 +SELECT * FROM db2.v22; +a b +3 1 +2 2 +1 3 Cleanup -DROP DATABASE bup_objects; +DROP DATABASE db1; +DROP DATABASE db2; diff -Nrup a/mysql-test/t/backup_objects.test b/mysql-test/t/backup_objects.test --- a/mysql-test/t/backup_objects.test 2008-02-13 12:40:24 +01:00 +++ b/mysql-test/t/backup_objects.test 2008-05-07 15:07:25 +02:00 @@ -1,12 +1,36 @@ # -# This test testing backup and restore for database objects. +# This test is testing backup and restore of database objects. # +# The following objects are tested: +# - tables +# - views +# - stored procedures and functions +# - triggers +# - events +# +# It is also tested that backup and restore works correctly when there are +# dependencies between objects such as: +# - one table depends on another table via foreign key constraint, +# - a view depends on a table or another view, +# - stored routine or event refers to another object. --source include/have_innodb.inc --source include/not_embedded.inc -connect (con1,localhost,root,,); -connection con1; +# Note: BACKUP crashes server when InnoDB engine is used and views are +# included in the image (see BUG#34758). Until this issue is resolved we +# can't use InnoDB engine here and thus define table dependencies using +# foreign key constraints. +# +# If tables use MyISAM storage engine the test works but all foreign +# key constraints are silently ignored. When BUG#34758 is fixed, the line +# below should be changed to make InnoDB the default storage engine so that +# handling of dependencies introduced by foreign keys can also be tested. +# +# Note that there is a separate test backup_fkey testing foreign key +# constraints. + +SET storage_engine=MyISAM; ############################################################## --echo @@ -26,108 +50,126 @@ SET character_set_client = 'latin2'; # Create database and objects for this test and tailor it to the test. --disable_warnings -DROP DATABASE IF EXISTS bup_objects; +DROP DATABASE IF EXISTS db1; +DROP DATABASE IF EXISTS db2; --enable_warnings -CREATE DATABASE bup_objects; +CREATE DATABASE db1; +CREATE DATABASE db2; -# Create table and load it with data. ---echo con1: Creating table -CREATE TABLE bup_objects.t1 (col_a int, col_b CHAR(40)) ENGINE=INNODB; -CREATE TABLE bup_objects.t2 (col_a int, col_b CHAR(40)) ENGINE=INNODB; - ---echo con1: Loading data -INSERT INTO bup_objects.t1 VALUES (01, "101 Some data to test"); -INSERT INTO bup_objects.t1 VALUES (02, "102 Some data to test"); -INSERT INTO bup_objects.t1 VALUES (03, "103 Some data to test"); -INSERT INTO bup_objects.t1 VALUES (04, "201 Some data to test"); -INSERT INTO bup_objects.t1 VALUES (05, "202 Some data to test"); -INSERT INTO bup_objects.t1 VALUES (06, "203 Some data to test"); -INSERT INTO bup_objects.t1 VALUES (07, "301 Some data to test"); -INSERT INTO bup_objects.t1 VALUES (08, "302 Some data to test"); -INSERT INTO bup_objects.t1 VALUES (09, "303 Some data to test"); -INSERT INTO bup_objects.t1 VALUES (10, "401 Some data to test"); -INSERT INTO bup_objects.t1 VALUES (11, "402 Some data to test"); -INSERT INTO bup_objects.t1 VALUES (12, "403 Some data to test"); - -INSERT INTO bup_objects.t2 VALUES (01, "101 Some data to test"); -INSERT INTO bup_objects.t2 VALUES (02, "102 Some data to test"); -INSERT INTO bup_objects.t2 VALUES (03, "103 Some data to test"); - -# Create views of the table. -CREATE VIEW bup_objects.v1 AS SELECT * FROM bup_objects.t1 WHERE col_a < 5; -CREATE VIEW bup_objects.v2 AS SELECT * FROM bup_objects.t1 WHERE col_a >= 5; +# Create functions and procedures. Note that they operate on tables and +# views defined below and also call each other. -# Create trigger for table. delimiter ||; -CREATE TRIGGER bup_objects.ins_t1 AFTER INSERT ON bup_objects.t1 FOR EACH ROW + +CREATE PROCEDURE db1.p11() BEGIN - DELETE FROM bup_objects.t2 WHERE col_a > 1; + UPDATE db2.t21 SET b = (SELECT b FROM db1.v11 WHERE a = 1); + SELECT db2.f21(7); END; || -# Create event. -CREATE EVENT bup_objects.e1 ON SCHEDULE EVERY 1 YEAR DO - DELETE FROM bup_objects.t2 WHERE col_a > 100; +CREATE FUNCTION db2.f21(a int) RETURNS INTEGER +BEGIN + RETURN a; +END; || - -# Create procedure. -CREATE PROCEDURE bup_objects.p1() + +CREATE PROCEDURE db2.p21() +BEGIN + CALL db1.p11(); + SELECT x.a FROM v22 as x, db1.t13 as y WHERE x.b = y.a; +END; +|| + +delimiter ;|| + +# Create tables which refer to each other with foreign keys. + +CREATE TABLE db1.t11(a int, primary key (a)); +CREATE TABLE db1.t12(a int, b int, foreign key (b) references db1.t11(a)); +CREATE TABLE db2.t21(a int, b int, primary key(a), foreign key (b) references db1.t11(a)); +CREATE TABLE db1.t13(a int, b int, foreign key (b) references db2.t21(a)); + +# Create views with non-trivial dependencies. +# +# Notes: +# +# - It is important that each database contains a view which depends on a view from the +# other one. Thanks to that the correct order of restoring these views is non-trivial +# and mixes views from different databases. If, for example, we would first create +# views from one database and then from the other one we would always get a failure +# regardless of the order in which databases are processed. +# +# - Views use the function defined above to check that this is handled correctly. + +CREATE VIEW db1.v12 AS SELECT * FROM db2.t21; +CREATE VIEW db2.v22 AS SELECT * FROM db1.t12; +CREATE VIEW db1.v11 AS SELECT db2.f21(x.a) AS a, y.b FROM db2.v22 AS x, db1.t13 AS y WHERE x.b = y.a; +CREATE VIEW db2.v21 AS SELECT x.b, db2.f21(y.a) AS a FROM db1.v11 AS x, db2.v22 AS y WHERE x.a = y.b; + +# The dependencies between views are as follows: +# +# v11 ------- t13 +# / \ v12 -- t21 +# v21 \ +# \_____ v22 __ t12 +# +# Therefore they must be created in such order: v22,v11,v21 (position of v12 +# is arbitrary). Grouping them by database or restoring in alphabetical order would not +# work. + +# Create other types of objects + +delimiter ||; + +CREATE TRIGGER db1.tr11 AFTER DELETE ON db1.t11 FOR EACH ROW BEGIN - UPDATE bup_objects.t1 SET col_b = "Procedure p1 was here." WHERE col_a < 3; + DELETE FROM db2.t1 WHERE a > 1; END; || -# Create function. -CREATE FUNCTION bup_objects.f1() RETURNS INTEGER +CREATE TRIGGER db2.tr21 AFTER DELETE ON db2.t21 FOR EACH ROW BEGIN - DECLARE v_out INT; - SELECT count(*) FROM bup_objects.t1 INTO v_out; - RETURN v_out; + DELETE FROM db1.t2 WHERE a > 1; END; || +CREATE EVENT db1.e11 ON SCHEDULE EVERY 1 YEAR DO + DELETE FROM db2.t21 WHERE b > 100; +|| + +CREATE EVENT db2.e21 ON SCHEDULE EVERY 1 YEAR DO + DELETE FROM db1.t12 WHERE b > 100; +|| + delimiter ;|| -# Exercise the objects. ---echo Using the objects. -INSERT INTO bup_objects.t1 VALUES (30, "a new row."); -CALL bup_objects.p1(); -SELECT bup_objects.f1(); +# insert some data -# Show the data and CREATE statements ---echo Showing objects and create statements. ---query_vertical SHOW CREATE DATABASE bup_objects; ---query_vertical SHOW CREATE TABLE bup_objects.t1; ---query_vertical SHOW CREATE TABLE bup_objects.t2; ---query_vertical SHOW CREATE VIEW bup_objects.v1; ---query_vertical SHOW CREATE VIEW bup_objects.v2; ---query_vertical SHOW CREATE PROCEDURE bup_objects.p1; ---query_vertical SHOW CREATE FUNCTION bup_objects.f1; ---query_vertical SHOW CREATE TRIGGER bup_objects.ins_t1; ---replace_column 13 # 14 # 17 # 18 # 19 # ---query_vertical SELECT * FROM INFORMATION_SCHEMA.EVENTS WHERE event_schema = 'bup_objects' AND event_name = 'e1'; - -SELECT table_name FROM INFORMATION_SCHEMA.TABLES - WHERE table_schema = 'bup_objects'; -SELECT table_name as view_name FROM INFORMATION_SCHEMA.VIEWS - WHERE table_schema = 'bup_objects'; -SELECT routine_name as proc_name FROM INFORMATION_SCHEMA.ROUTINES - WHERE routine_schema = 'bup_objects' AND routine_type = 'PROCEDURE'; -SELECT routine_name as func_name FROM INFORMATION_SCHEMA.ROUTINES - WHERE routine_schema = 'bup_objects' AND routine_type = 'FUNCTION'; -SELECT trigger_name, event_manipulation, event_object_table - FROM INFORMATION_SCHEMA.TRIGGERS WHERE trigger_schema = 'bup_objects'; -SELECT event_name FROM INFORMATION_SCHEMA.EVENTS - WHERE event_schema = 'bup_objects'; +SET foreign_key_checks = 1; +SHOW VARIABLES LIKE 'foreign_key_checks%'; + +INSERT INTO db1.t11 VALUES (1),(2),(3); +INSERT INTO db1.t12 VALUES (3,1), (2,2), (1,3); +INSERT INTO db2.t21 VALUES (100,1), (200,2); +INSERT INTO db1.t13 VALUES (1,200), (2,100); -# Backup and restore the data. ---echo Backup data. +# Check what is in views + +SELECT * FROM db1.v11; +SELECT * FROM db1.v12; +SELECT * FROM db2.v21; +SELECT * FROM db2.v22; + +# Backup and restore everything. +--echo Backup databases. --replace_column 1 # -BACKUP DATABASE bup_objects TO 'bup_objects.bak'; +BACKUP DATABASE db1, db2 TO 'bup_objects.bak'; ---echo Dropping database. -DROP DATABASE bup_objects; +--echo Dropping databases. +DROP DATABASE db1; +DROP DATABASE db2; # Set SQL_MODE and client charset --echo Setting SQL_MODE = '' @@ -139,33 +181,38 @@ SET character_set_client = 'latin1'; --replace_column 1 # RESTORE FROM 'bup_objects.bak'; +# See that all objects have been restored + +SHOW TABLES IN db1; +SHOW TABLES IN db2; +--query_vertical SHOW TRIGGERS IN db1; +--query_vertical SHOW TRIGGERS IN db2; +--replace_column 9 # +--query_vertical SHOW EVENTS IN db1; +--replace_column 9 # +--query_vertical SHOW EVENTS IN db2; +--replace_column 5 # 6 # +--query_vertical SHOW PROCEDURE STATUS WHERE Db='db1' OR Db='db2'; +--replace_column 5 # 6 # +--query_vertical SHOW FUNCTION STATUS WHERE Db='db1' OR Db='db2'; + # Show the data and CREATE statements --echo Showing objects and create statements. ---query_vertical SHOW CREATE DATABASE bup_objects; ---query_vertical SHOW CREATE TABLE bup_objects.t1; ---query_vertical SHOW CREATE TABLE bup_objects.t2; ---query_vertical SHOW CREATE VIEW bup_objects.v1; ---query_vertical SHOW CREATE VIEW bup_objects.v2; ---query_vertical SHOW CREATE PROCEDURE bup_objects.p1; ---query_vertical SHOW CREATE FUNCTION bup_objects.f1; ---query_vertical SHOW CREATE TRIGGER bup_objects.ins_t1; ---replace_column 13 # 14 # 17 # 18 # 19 # ---query_vertical SELECT * FROM INFORMATION_SCHEMA.EVENTS WHERE event_schema = 'bup_objects' AND event_name = 'e1'; - -SELECT table_name FROM INFORMATION_SCHEMA.TABLES - WHERE table_schema = 'bup_objects'; -SELECT table_name as view_name FROM INFORMATION_SCHEMA.VIEWS - WHERE table_schema = 'bup_objects'; -SELECT routine_name as proc_name FROM INFORMATION_SCHEMA.ROUTINES - WHERE routine_schema = 'bup_objects' AND routine_type = 'PROCEDURE'; -SELECT routine_name as func_name FROM INFORMATION_SCHEMA.ROUTINES - WHERE routine_schema = 'bup_objects' AND routine_type = 'FUNCTION'; -SELECT trigger_name, event_manipulation, event_object_table - FROM INFORMATION_SCHEMA.TRIGGERS WHERE trigger_schema = 'bup_objects'; -SELECT event_name FROM INFORMATION_SCHEMA.EVENTS - WHERE event_schema = 'bup_objects'; +--query_vertical SHOW CREATE DATABASE db1; +--query_vertical SHOW CREATE TABLE db1.t13; +--query_vertical SHOW CREATE TABLE db2.t21; +--query_vertical SHOW CREATE VIEW db1.v11; +--query_vertical SHOW CREATE VIEW db2.v21; +--query_vertical SHOW CREATE PROCEDURE db1.p11; +--query_vertical SHOW CREATE FUNCTION db2.f21; +--query_vertical SHOW CREATE TRIGGER db2.tr21; + +SELECT * FROM db1.v11; +SELECT * FROM db1.v12; +SELECT * FROM db2.v21; +SELECT * FROM db2.v22; --echo Cleanup -DROP DATABASE bup_objects; - +DROP DATABASE db1; +DROP DATABASE db2; --remove_file $MYSQLTEST_VARDIR/master-data/bup_objects.bak diff -Nrup a/sql/backup/backup_info.cc b/sql/backup/backup_info.cc --- a/sql/backup/backup_info.cc 2008-04-24 11:18:47 +02:00 +++ b/sql/backup/backup_info.cc 2008-05-07 15:07:25 +02:00 @@ -223,6 +223,68 @@ uchar* Backup_info::Ts_hash_node::get_ke return (uchar*)(n->name->ptr()); } +/** + Represents a node in the dependency list. + + Such node can be an empty placeholder or store a pointer to a catalogue item + in @c obj member. Dependency list nodes are kept in a hash and thus + @c Dep_node contains all required infrastructure: the @c key member to store + a key string plus @c get_key() and @c free() functions used in @c HASH + operations. + */ +struct Backup_info::Dep_node: public Sql_alloc +{ + Dep_node *next; + Dbobj *obj; + String key; + + Dep_node(const ::String &db_name, const ::String &name); + Dep_node(const Dep_node&); + + static uchar* get_key(const uchar *record, size_t *key_length, my_bool); + static void free(void *record); +}; + +/** + Create an empty dependency list node for a given per-database object. + + The object is identified by its name and the name of the database to which + it belongs. + */ +inline +Backup_info::Dep_node::Dep_node(const ::String &db_name, const ::String &name) + :next(NULL), obj(NULL) +{ + key.length(0); + key.append(db_name); + key.append("."); + key.append(name); +} + +inline +Backup_info::Dep_node::Dep_node(const Dep_node &n) + :Sql_alloc(n), next(n.next), obj(n.obj) +{ + key.copy(n.key); +} + +inline +void Backup_info::Dep_node::free(void *record) +{ + ((Dep_node*)record)->~Dep_node(); +} + +inline +uchar* +Backup_info::Dep_node::get_key(const uchar *record, + size_t *key_length, + my_bool) // not_used __attribute__((unused))) +{ + Dep_node *n= (Dep_node*)record; + if (key_length) + *key_length= n->key.length(); + return (uchar*)n->key.ptr(); +} /** @@ -233,7 +295,9 @@ uchar* Backup_info::Ts_hash_node::get_ke @c find_backup_engine(). */ Backup_info::Backup_info(Backup_restore_ctx &ctx) - :m_ctx(ctx), m_state(Backup_info::ERROR), native_snapshots(8) + :m_ctx(ctx), m_state(Backup_info::ERROR), native_snapshots(8), + m_dep_list(NULL), m_dep_end(NULL), + m_sr_end(NULL), m_v_end(NULL), m_tr_end(NULL) { using namespace backup; @@ -243,6 +307,8 @@ Backup_info::Backup_info(Backup_restore_ hash_init(&ts_hash, &::my_charset_bin, 16, 0, 0, Ts_hash_node::get_key, Ts_hash_node::free, MYF(0)); + hash_init(&dep_hash, &::my_charset_bin, 16, 0, 0, + Dep_node::get_key, Dep_node::free, MYF(0)); /* Create default and CS snapshot objects and add them to the snapshots list. @@ -263,7 +329,7 @@ Backup_info::Backup_info(Backup_restore_ return; snapshots.push_back(snap); - + m_state= CREATED; } @@ -282,6 +348,7 @@ Backup_info::~Backup_info() delete snap; hash_free(&ts_hash); + hash_free(&dep_hash); } /** @@ -770,6 +837,112 @@ backup::Image_info::Table* Backup_info:: } /** + For all views on which the given one depends directly or indirectly, add + placeholders to the dependency list ensuring correct order among them. + + The main view being processed in this function should be added to the + dependency list after all the placeholders created here. This way if one of + the views on which the given one depends is added to the list later, it will + be placed at the correct position indicated by the placeholder. + + @param[in] obj server object for the view to be processed + + A recursive algorithm is used to insert placeholders in correct order. + That is, for each base view @c bv of the given one, @c add_view_deps(bv) + is called to insert all dependencies of @c bv before @c bv itself is appended + to the list. + + Function @c get_dep_node() used to create a node to be inserted into the + dependency list detects if the node was already created earlier. This ensures + correct behaviour of the algorithm even if the same view is visited several + times during the depth-first walk of the dependency graph performed by the + recursive algorithm. If it is detected that a node for a given view already + exists then this view is not processed for the second time. + + This also ensures termination of the algorithm even when there are + circular dependencies. Suppose that view @c v has itself as an (indirect) + dependency. When processing @c v, a node will be created for it first + and then its dependencies will be processed. When add_view_deps() comes across + @c v for the second time it will see that a corresponding mode already + exists and thus will break the recursion. + + @return Non-zero value if an error happened. + */ +int Backup_info::add_view_deps(obs::Obj &obj) +{ + const ::String *name= obj.get_name(); + const ::String *db_name= obj.get_db_name(); + + DBUG_ASSERT(name); + DBUG_ASSERT(db_name); + + // Get an iterator to iterate over base views of the given one. + + obs::ObjIterator *it= obs::get_view_base_views(m_ctx.m_thd, db_name, name); + + if (!it) + return ERROR; + + /* + Iterate over base views and for each of them add it and its dependencies + to the dependency list (first its dependecies, then the base view). + */ + + obs::Obj *bv; + + while ((bv= it->next())) + { + Dep_node *n= NULL; + const ::String *name= bv->get_name(); + const ::String *db_name= bv->get_db_name(); + + DBUG_ASSERT(name); + DBUG_ASSERT(db_name); + + // Locate or create a dependency list node for the base view. + + int res= get_dep_node(*db_name, *name, n); + + if (res == get_dep_node_res::ERROR) + goto error; + + DBUG_ASSERT(n); + + /* + If a node for this view already exists, the view has been processed + (or is being processed now). Hence we skip it and continue with other + base views. + */ + if (res == get_dep_node_res::EXISTING_NODE) + continue; + + // Recursively add all dependencies of bv to the list. + + if (add_view_deps(*bv)) + goto error; + + /* + Now add bv itself to the list (we keep in n a pointer to the dep. list + node obtained earlier). + */ + if (add_to_dep_list(BSTREAM_IT_VIEW, n)) + goto error; + + delete bv; // Server object instance is not needed any more. + } + + delete it; + return 0; + +error: + + delete bv; + delete it; + + return ERROR; +} + +/** Select a per database object for backup. This method is used for objects other than tables - tables are handled @@ -780,6 +953,10 @@ backup::Image_info::Table* Backup_info:: @param[in] type type of the object @param[in] obj the object + The object is also added to the dependency list with @c add_to_dep_list() + method. If it is a view, its dependencies are handled first using + @c add_view_deps(). + @returns Pointer to @c Image_info::Dbobj instance storing information about the object or NULL in case of error. */ @@ -820,8 +997,359 @@ Backup_info::add_db_object(Db &db, const o->m_obj_ptr= obj; + /* + Add new object to the dependency list. If it is a view, add its + dependencies first. + */ + + Dep_node *n= NULL; /* Note: set to NULL to make sure that get_dep_node() has + set the pointer. */ + + // Get a dep. list node for the object. + + int res= get_dep_node(db.name(), *name, n); + + if (res == get_dep_node_res::ERROR) + { + m_ctx.fatal_error(error, db.name().ptr(), name->ptr()); + return NULL; + } + + /* + Store a pointer to the catalogue item in the dep. list node. If this node + was a placeholder inserted into the list before, now it will be filled with + the object we are adding to the catalogue. + */ + + DBUG_ASSERT(n); + n->obj= o; + + /* + If a new node was created, it must be added to the dependency list with + add_to_dep_list(). However, if the object is a view, we must first add + placeholders for all its dependencies which is done using add_view_deps(). + */ + + if (res == get_dep_node_res::NEW_NODE) + { + if (type == BSTREAM_IT_VIEW) + if (add_view_deps(*obj)) + { + m_ctx.fatal_error(error, db.name().ptr(), name->ptr()); + return NULL; + } + + add_to_dep_list(type, n); + } + DBUG_PRINT("backup",("Added object %s of type %d from database %s (pos=%lu)", name->ptr(), type, db.name().ptr(), pos)); return o; } +/* + Find or create a dependency list node for an object with a given name. + + @param[in] db_name name of the database to which this object belongs + @param[in] name name of the object + @param[out] node pointer to the created or located node + + All nodes created using that function are keept inside @c dep_node HASH + indexed by pairs. This is used to detect that a node for + a given object already exists. All created nodes are deleted when Backup_info + instance is destroyed. + + The node created by this function is not placed on the dependency list. This + must be done explicitly using @c add_to_dep_list(). The node has @c obj member + set to NULL which means that it can be used as an empty placeholder in the + dependency list. + + @return A code indicating whether the node was found or created. + @retval NEW_NODE a new node has been created + @retval EXISTING_NODE a node for this object was created before and @c node + contains a pointer to it + @retval ERROR it was not possible to create or locate the node + */ +int Backup_info::get_dep_node(const ::String &db_name, + const ::String &name, + Dep_node* &node) +{ + Dep_node n(db_name, name); + size_t klen; + uchar *key= Dep_node::get_key((const uchar*)&n, &klen, TRUE); + + node= (Dep_node*) hash_search(&dep_hash, key, klen); + + // if we have found node in the hash there is nothing more to do + if (node) + return get_dep_node_res::EXISTING_NODE; + + /* + Otherwise insert node into the hash and return. + */ + + node= new (&mem_root) Dep_node(n); + + if (!node) + return get_dep_node_res::ERROR; + + return my_hash_insert(&dep_hash, (uchar*)node) ? + get_dep_node_res::ERROR : get_dep_node_res::NEW_NODE; +} + +/** + Append node to the correct section of the dependency list, based on the + type of the object. + + @param[in] type type of the object indicating the section of the list + to which node will be appended + @param[in] node points at the node to be appended + + The node is appended at the end of the corresponding section of the + dependency list. Pointers m_dep_list, m_end_dep and the ones indicating end of + each section are updated as necessary. + + A node added to the list is just a placeholder for an object from backup + catalogue. To insert such object into the list a pointer to the corresponding + @c Dbobj instance should be stored in the @c obj member of a list node. + + @return Non-zero value in case of error. Currently should always succeed. + */ +int Backup_info::add_to_dep_list(const obj_type type, Dep_node *node) +{ + Dep_node* *end= NULL; + + DBUG_ASSERT(node); + + /* + Set end to point at m_sr_end, m_et_end or m_v_end depending on the + type of the item. + + If the corresponding section is empty, the *end pointer is set up to point + at the node after which this section should start, or to NULL if section + should be at the beginning of the dependency list. + + After inserting the new node it becomes the last node in the section so + the pointer is updated to point at it. + */ + + switch (type) { + + case BSTREAM_IT_SPROC: + case BSTREAM_IT_SFUNC: + end= &m_sr_end; + break; + + case BSTREAM_IT_VIEW: + end= &m_v_end; + if (!m_v_end) + m_v_end= m_sr_end; + break; + + case BSTREAM_IT_TRIGGER: + end= &m_tr_end; + if (!m_tr_end) + m_tr_end= m_v_end ? m_v_end : m_sr_end; + break; + + case BSTREAM_IT_EVENT: + end= &m_dep_end; + break; + + default: DBUG_ASSERT(FALSE); // only known object types should be added + + } + + DBUG_ASSERT(end); // end should point now at one of m_*_end pointers + + /* + The new node should be inserted after the node pointed by end or at + the begginging of the list if end == NULL. + */ + + if (*end) + { + node->next= (*end)->next; + (*end)->next= node; + } + else + { + node->next= m_dep_list; + m_dep_list= node; + } + + /* + Update m_dep_end pointer if appending to the end of the dependency list. + + There are two cases: + + - either the list is empty in which case both m_dep_end and *end are NULL + - or it is not empty and *end points at the last node in the list. + + In either case *end equals m_dep_end. + */ + + if (*end == m_dep_end) + m_dep_end= node; + + *end= node; + + return 0; +} + + +/** + Used to iterate over all global objects for which we store information in the + neta-data section of backup image. + + Currently only global objects handled are tablespaces and databases. + */ +class Backup_info::Global_iterator + : public Image_info::Iterator +{ + /** + Indicates whether tablespaces or databases are being currently enumerated. + */ + enum { TABLESPACES, DATABASES, DONE } mode; + + Iterator *m_it; ///< Points at the currently used iterator. + Obj *m_obj; ///< Points at next object to be returned by this iterator. + + public: + + Global_iterator(const Image_info&); + + private: + + Obj* get_ptr() const; + bool next(); +}; + +inline +Backup_info::Global_iterator::Global_iterator(const Image_info &info) + :Iterator(info), mode(TABLESPACES), m_it(NULL), m_obj(NULL) +{ + m_it= m_info.get_tablespaces(); + next(); +} + + +inline +backup::Image_info::Obj* +Backup_info::Global_iterator::get_ptr() const +{ + return m_obj; +} + +inline +bool +Backup_info::Global_iterator::next() +{ + if (mode == DONE) + return FALSE; + + DBUG_ASSERT(m_it); + + // get next object from the current iterator + m_obj= (*m_it)++; + + if (m_obj) + return TRUE; + + /* + If the current iterator has finished (m_obj == NULL) then, depending on + the mode, either switch to the next iterator or mark end of the sequence. + */ + + delete m_it; + + switch (mode) { + + case TABLESPACES: + + // We have finished enumerating tablespaces, move on to databases. + mode= DATABASES; + m_it= m_info.get_dbs(); + m_obj= (*m_it)++; + return m_obj != NULL; + + case DATABASES: + + // We have finished enumerating databases - nothing more to do. + mode= DONE; + + case DONE: + + break; + } + + return FALSE; +} + +/** + Used to iterate over all per database objects, except tables. + + This iterator uses the dependency list maintained inside Backup_info + instance to list objects in a dependency-respecting order. + */ +class Backup_info::Perdb_iterator : public Image_info::Iterator +{ + Dep_node *ptr; + + public: + + Perdb_iterator(const Backup_info&); + + private: + + Obj* get_ptr() const; + bool next(); +}; + +inline +Backup_info::Perdb_iterator::Perdb_iterator(const Backup_info &info) + :Iterator(info), ptr(info.m_dep_list) +{ + // Find first non-empty node in the dependency list. + + while (ptr && !ptr->obj) + ptr= ptr->next; +} + +/// Implementation of @c Image_info::Iterator virtual method. +inline +backup::Image_info::Obj* Backup_info::Perdb_iterator::get_ptr() const +{ + return ptr ? ptr->obj : NULL; +} + +/// Implementation of @c Image_info::Iterator virtual method. +inline +bool Backup_info::Perdb_iterator::next() +{ + // Return FALSE if we are at the end of dependency list. + + if (!ptr) + return FALSE; + + // Otherwise move ptr to the next non-empty node on the list. + + do { + ptr= ptr->next; + } while (ptr && !ptr->obj); + + return ptr != NULL; +} + + +/// Wrapper to return global iterator. +backup::Image_info::Iterator* Backup_info::get_global() const +{ + return new Global_iterator(*this); +} + +/// Wrapper to return iterator for per-database objects. +backup::Image_info::Iterator* Backup_info::get_perdb() const +{ + return new Perdb_iterator(*this); +} diff -Nrup a/sql/backup/backup_info.h b/sql/backup/backup_info.h --- a/sql/backup/backup_info.h 2008-04-21 12:45:26 +02:00 +++ b/sql/backup/backup_info.h 2008-05-07 15:07:25 +02:00 @@ -43,8 +43,14 @@ class Backup_info: public backup::Image_ int close(); + Iterator* get_global() const; + Iterator* get_perdb() const; + private: + class Global_iterator; ///< Iterates over global items (for which meta-data is stored). + class Perdb_iterator; ///< Iterates over all per-database objects (except tables). + /// State of the info structure. enum {CREATED, CLOSED, @@ -59,6 +65,21 @@ class Backup_info: public backup::Image_ int add_db_items(Db&); int add_objects(Db&, const obj_type, obs::ObjIterator&); + int add_view_deps(obs::Obj&); + + struct Dep_node; + + int get_dep_node(const ::String&, const ::String&, Dep_node*&); + int add_to_dep_list(const obj_type, Dep_node*); + + struct get_dep_node_res + { + enum value { + NEW_NODE, + EXISTING_NODE, + ERROR + }; + }; /** List of existing @c Snapshot_info objects. @@ -85,6 +106,49 @@ class Backup_info: public backup::Image_ tablespace or not. */ HASH ts_hash; + + + /** + Pointer to the first element on the dependency list. + + Dependency list lists all per-database objects in the order which takes + into account possible dependencies between them. The list is divided into + three sections: + + -# stored routines (functions and procedures) + -# views + -# triggers + -# events + */ + Dep_node *m_dep_list; + Dep_node *m_dep_end; ///< Pointer to the last element on the dependency list. + + /** + Points at the last element in the stored routines section of the dependency + list. NuLL if this section is empty. + */ + Dep_node *m_sr_end; + + /** + Points at the last view on the dependency list. NuLL if views section is + empty. + */ + Dep_node *m_v_end; + + /** + Points at the last trigger on the dependency list. NULL if triggers section + is empty. + */ + Dep_node *m_tr_end; + + /** + Hash keeping all elements stored in the dependency list. + + It is used to quickly check if a given object is on the list. Hash is + indexed by pairs. + */ + HASH dep_hash; + String serialization_buf; ///< Used to store serialization strings of objects. diff -Nrup a/sql/backup/image_info.h b/sql/backup/image_info.h --- a/sql/backup/image_info.h 2008-04-21 12:45:27 +02:00 +++ b/sql/backup/image_info.h 2008-05-07 15:07:25 +02:00 @@ -61,8 +61,6 @@ public: // public interface class Iterator; ///< Base for all iterators. class Ts_iterator; ///< Iterates over all tablespaces. class Db_iterator; ///< Iterates over all databases. - class Global_iterator; ///< Iterates over global items (for which meta-data is stored). - class Perdb_iterator; ///< Iterates over all per-database objects (except tables). class Dbobj_iterator; ///< Iterates over objects in a database. virtual ~Image_info(); @@ -91,8 +89,6 @@ public: // public interface Db_iterator* get_dbs() const; Ts_iterator* get_tablespaces() const; - Global_iterator* get_global() const; - Perdb_iterator* get_perdb() const; Dbobj_iterator* get_db_objects(const Db &db) const; /** @@ -131,14 +127,12 @@ public: // public interface Image_info(); uint m_table_count; + MEM_ROOT mem_root; ///< Memory root for storage of catalogue items. private: class Tables; ///< Implementation of Table_list interface. - // storage - - MEM_ROOT mem_root; ///< Memory root used to allocate @c Obj instances. Map m_dbs; ///< Pointers to Db instances. Map m_ts_map; ///< Pointers to Ts instances. String m_binlog_file; ///< To store binlog file name at VP time. @@ -633,32 +627,6 @@ Image_info::Db_iterator::Db_iterator(con /** - Used to iterate over all per database objects, except tables. - - @todo Modify this iterator so that it enumerates objects in order - respecting dependencies. - */ -class Image_info::Perdb_iterator - : public Image_info::Db_iterator -{ - ulong pos; - - public: - - Perdb_iterator(const Image_info&); - - private: - - Obj* get_ptr() const; - bool next(); -}; - -inline -Image_info::Perdb_iterator::Perdb_iterator(const Image_info &info) - :Db_iterator(info), pos(0) -{} - -/** Used to iterate over all objects belonging to a given database. @note Backup stream library infers position of each non-table object within @@ -690,92 +658,6 @@ Image_info::Dbobj_iterator::Dbobj_iterat {} -/** - Used to iterate over all global objects for which we store information in the - neta-data section of backup image. - - Currently only global objects handled are tablespaces and databases. - */ -class Image_info::Global_iterator - : public Image_info::Iterator -{ - /** - Indicates whether tablespaces or databases are being currently enumerated. - */ - enum { TABLESPACES, DATABASES, DONE } mode; - - Iterator *m_it; ///< Points at the currently used iterator. - Obj *m_obj; ///< Points at next object to be returned by this iterator. - - public: - - Global_iterator(const Image_info&); - - private: - - Obj* get_ptr() const; - bool next(); -}; - -inline -Image_info::Global_iterator::Global_iterator(const Image_info &info) - :Iterator(info), mode(TABLESPACES), m_it(NULL), m_obj(NULL) -{ - m_it= m_info.get_tablespaces(); - next(); -} - - -inline -Image_info::Obj* -Image_info::Global_iterator::get_ptr() const -{ - return m_obj; -} - -inline -bool -Image_info::Global_iterator::next() -{ - if (mode == DONE) - return FALSE; - - DBUG_ASSERT(m_it); - - // get next object from the current iterator - m_obj= (*m_it)++; - - if (m_obj) - return TRUE; - - /* - If the current iterator has finished (m_obj == NULL) then, depending on - the mode, either switch to the next iterator or mark end of the sequence. - */ - - delete m_it; - - switch (mode) { - - case TABLESPACES: - - // We have finished enumerating tablespaces, move on to databases. - mode= DATABASES; - m_it= m_info.get_dbs(); - m_obj= (*m_it)++; - return m_obj != NULL; - - case DATABASES: - - mode= DONE; - - case DONE: - - break; - } - - return FALSE; -} /******************************************************************** @@ -914,19 +796,6 @@ Image_info::Ts_iterator* Image_info::get return new Ts_iterator(*this); } -/** - Returns an iterator enumerating all per-database objects (from all databases) - for which we store meta-data. - - @note This iterator enumerates only non-table objects - tables are handled - separately. - */ -inline -Image_info::Perdb_iterator* Image_info::get_perdb() const -{ - return new Perdb_iterator(*this); -} - /// Returns an iterator enumerating all objects in a given database. inline Image_info::Dbobj_iterator* Image_info::get_db_objects(const Db &db) const @@ -934,16 +803,6 @@ Image_info::Dbobj_iterator* Image_info:: return new Dbobj_iterator(*this, db); } -/** - Returns an iterator enumerating all global objects for which we need to store - some meta-data. - */ -inline -Image_info::Global_iterator* Image_info::get_global() const -{ - return new Global_iterator(*this); -} - /******************************************************************** Inline members of Image_info::Tables class. @@ -978,7 +837,7 @@ Table_ref Image_info::Tables::operator[] /******************************************************************** - Inline members of Snapshot_info::Obj and derived classes. + Inline members of Image_info::Obj and derived classes. ********************************************************************/ @@ -1281,42 +1140,6 @@ bool Image_info::Ts_iterator::next() } else return FALSE; -} - - -/// Implementation of @c Image_info::Iterator virtual method. -inline -Image_info::Obj* Image_info::Perdb_iterator::get_ptr() const -{ - const Db *db= static_cast(Db_iterator::get_ptr()); - - if (!db) - return NULL; - - return db->m_objs[pos]; -} - -/// Implementation of @c Image_info::Iterator virtual method. -inline -bool Image_info::Perdb_iterator::next() -{ - while (TRUE) - { - const Db *db= static_cast(Db_iterator::get_ptr()); - - if (!db) - return FALSE; - - // Look for next non-NULL pointer in m_objs. - while (pos < db->obj_count() && !db->m_objs[++pos]); - - // If next object found in the current database, return. - if (pos < db->obj_count()) - return TRUE; - - // Otherwise, move to next database. - Db_iterator::next(); - } } diff -Nrup a/sql/backup/kernel.cc b/sql/backup/kernel.cc --- a/sql/backup/kernel.cc 2008-04-21 12:45:27 +02:00 +++ b/sql/backup/kernel.cc 2008-05-07 15:07:25 +02:00 @@ -1163,6 +1163,8 @@ static uint null_iter; ///< Used to imp extern "C" void* bcat_iterator_get(st_bstream_image_header *catalogue, unsigned int type) { + typedef backup::Image_info::Iterator Iterator; // to save some typing + DBUG_ASSERT(catalogue); Backup_info *info= static_cast(catalogue); @@ -1181,7 +1183,7 @@ void* bcat_iterator_get(st_bstream_image case BSTREAM_IT_TABLESPACE: // table spaces { - Backup_info::Ts_iterator *it= info->get_tablespaces(); + Iterator *it= info->get_tablespaces(); if (!it) { @@ -1194,7 +1196,7 @@ void* bcat_iterator_get(st_bstream_image case BSTREAM_IT_DB: // all databases { - Backup_info::Db_iterator *it= info->get_dbs(); + Iterator *it= info->get_dbs(); if (!it) { @@ -1207,7 +1209,7 @@ void* bcat_iterator_get(st_bstream_image case BSTREAM_IT_PERDB: // per-db objects, except tables { - Backup_info::Perdb_iterator *it= info->get_perdb(); + Iterator *it= info->get_perdb(); if (!it) { @@ -1220,7 +1222,7 @@ void* bcat_iterator_get(st_bstream_image case BSTREAM_IT_GLOBAL: // all global objects { - Backup_info::Global_iterator *it= info->get_global(); + Iterator *it= info->get_global(); if (!it) { @@ -1316,7 +1318,7 @@ void* bcat_db_iterator_get(st_bstream_im return NULL; } - Backup_info::Dbobj_iterator *it= info->get_db_objects(*db); + backup::Image_info::Iterator *it= info->get_db_objects(*db); if (!it) { @@ -1343,7 +1345,7 @@ void bcat_db_iterator_free(st_bstream_i st_bstream_db_info *db, void *iter) { - delete (backup::Image_info::Dbobj_iterator*)iter; + delete (backup::Image_info::Iterator*)iter; }