List:Commits« Previous MessageNext Message »
From:Jørgen Løland Date:June 25 2008 11:37am
Subject:Re: bzr commit into mysql-6.0-backup branch (rsomla:2637) WL#4326
View as plain text  
Patch looks good. OK to push.

Rafal Somla wrote:
> #At file:///ext/mysql/bzr/backup/wl4326/
> 
>  2637 Rafal Somla	2008-06-25
>       WL#4326 (Disable events and triggers during restore)
>       
>       This patch implements design described in the WL. The events and triggers are
> not created
>       when the metadata section of backup image is read but stored in a restore
> catalogue.
>       They are re-created after all other objects and all table data is restored.
> added:
>   mysql-test/r/backup_triggers_and_events.result
>   mysql-test/t/backup_triggers_and_events.test
> modified:
>   sql/backup/backup_kernel.h
>   sql/backup/data_backup.cc
>   sql/backup/kernel.cc
> 
> per-file messages:
>   mysql-test/t/backup_triggers_and_events.test
>     New test checking behaviour of events and triggers during restore process.
>   sql/backup/backup_kernel.h
>     Added new helper method restore_triggers_and_events().
>   sql/backup/data_backup.cc
>     Added synchronization point in restore_table_data(), after all restore drivers
> have finished
>     their job and before they are shut down.
>   sql/backup/kernel.cc
>     - Implementation of Backup_restore_ctx::restore_triggers_and_events()
>     - In do_restore(), re-create triggers and events after table data has been
> restored.
>     - In bcat_create_item() (called when metadata is read) do not re-create events
> and triggers,
>       only store them in the catalogue.
> === added file 'mysql-test/r/backup_triggers_and_events.result'
> --- a/mysql-test/r/backup_triggers_and_events.result	1970-01-01 00:00:00 +0000
> +++ b/mysql-test/r/backup_triggers_and_events.result	2008-06-25 10:03:42 +0000
> @@ -0,0 +1,202 @@
> +SET GLOBAL event_scheduler=off;
> +SET DEBUG_SYNC = 'RESET';
> +Creating log table.
> +DROP TABLE IF EXISTS test.logt;
> +CREATE TABLE test.logt(ts timestamp, db char(8), msg text);
> +Creating database db and its objects.
> +DROP DATABASE IF EXISTS db;
> +CREATE DATABASE db;
> +USE db;
> +CREATE TABLE t1 (a int);
> +INSERT INTO t1 VALUES (0),(1),(2),(3),(4),(5),(6);
> +CREATE EVENT ev ON SCHEDULE EVERY 1 second DO 
> +BEGIN
> +INSERT INTO test.logt(db, msg) VALUES ('db','Db event fired!');
> +END;
> +||
> +CREATE PROCEDURE trg_msg(a int)
> +BEGIN
> +INSERT INTO test.logt(db, msg) VALUES ('db','Db trigger fired!');
> +END;
> +||
> +CREATE TRIGGER after_ins AFTER INSERT ON t1 FOR EACH ROW 
> +CALL trg_msg(NEW.a);
> +||
> +CREATE TRIGGER after_upd AFTER UPDATE ON t1 FOR EACH ROW 
> +CALL trg_msg(NEW.a);
> +||
> +CREATE TRIGGER after_del AFTER DELETE ON t1 FOR EACH ROW 
> +CALL trg_msg(OLD.a);
> +||
> +CREATE TRIGGER before_ins BEFORE INSERT ON t1 FOR EACH ROW 
> +CALL trg_msg(NEW.a);
> +||
> +CREATE TRIGGER before_upd BEFORE UPDATE ON t1 FOR EACH ROW 
> +CALL trg_msg(NEW.a);
> +||
> +CREATE TRIGGER before_del BEFORE DELETE ON t1 FOR EACH ROW 
> +CALL trg_msg(OLD.a);
> +||
> +USE test||
> +DROP EVENT IF EXISTS ev||
> +Warnings:
> +Note	1305	Event ev does not exist
> +DROP TABLE IF EXISTS t1||
> +Warnings:
> +Note	1051	Unknown table 't1'
> +DROP TRIGGER IF EXISTS trg||
> +Warnings:
> +Note	1360	Trigger does not exist
> +CREATE EVENT ev ON SCHEDULE EVERY 1 second DO
> +BEGIN
> +INSERT INTO test.logt(db, msg) VALUES ('test','Test event fired!');
> +END;
> +||
> +CREATE TABLE t1 (a int)||
> +CREATE TRIGGER trg AFTER INSERT ON t1 FOR EACH ROW
> +BEGIN
> +INSERT INTO test.logt(db, msg) VALUES ('test','Test trigger fired');   
> +END;
> +||
> +Backing-up database db and dropping it.
> +BACKUP DATABASE db TO 'db.bak';
> +backup_id
> +#
> +DROP DATABASE db;
> +Enabling event scheduler.
> +SET GLOBAL event_scheduler=on;
> +con1: clearing log table and starting RESTORE operation.
> +con1: RESTORE will pause after restoring table data.
> +SET DEBUG_SYNC = 'restore_table_data_before_end SIGNAL waiting WAIT_FOR continue';
> +DELETE FROM test.logt;
> +RESTORE FROM 'db.bak';
> +SELECT now() INTO @start;
> +con2: checking that there are no triggers and events at the end of RESTORE
> execution.
> +SET DEBUG_SYNC = 'now WAIT_FOR waiting';
> +SHOW TRIGGERS FROM db;
> +SHOW EVENTS IN db;
> +con2: activating trigger in test database.
> +INSERT INTO test.t1 VALUES (1);
> +con2: ensuring that RESTORE takes at least 3 secs.
> +SET DEBUG_SYNC = 'now SIGNAL continue';
> +con1: finishing RESTORE operation.
> +backup_id
> +#
> +SET GLOBAL event_scheduler=off;
> +con2: checking that RESTORE took more than 2 secs.
> +SELECT timediff(now(),@start) > 2;
> +timediff(now(),@start) > 2
> +1
> +Checking that objects have been restored.
> +USE db;
> +SHOW TABLES IN db;
> +Tables_in_db
> +t1
> +SELECT count(*) FROM db.t1;
> +count(*)
> +7
> +SHOW TRIGGERS FROM db;
> +Trigger	before_ins
> +Event	INSERT
> +Table	t1
> +Statement	CALL trg_msg(NEW.a)
> +Timing	BEFORE
> +Created	NULL
> +sql_mode	
> +Definer	root@localhost
> +character_set_client	#
> +collation_connection	latin1_swedish_ci
> +Database Collation	latin1_swedish_ci
> +Trigger	after_ins
> +Event	INSERT
> +Table	t1
> +Statement	CALL trg_msg(NEW.a)
> +Timing	AFTER
> +Created	NULL
> +sql_mode	
> +Definer	root@localhost
> +character_set_client	#
> +collation_connection	latin1_swedish_ci
> +Database Collation	latin1_swedish_ci
> +Trigger	before_upd
> +Event	UPDATE
> +Table	t1
> +Statement	CALL trg_msg(NEW.a)
> +Timing	BEFORE
> +Created	NULL
> +sql_mode	
> +Definer	root@localhost
> +character_set_client	#
> +collation_connection	latin1_swedish_ci
> +Database Collation	latin1_swedish_ci
> +Trigger	after_upd
> +Event	UPDATE
> +Table	t1
> +Statement	CALL trg_msg(NEW.a)
> +Timing	AFTER
> +Created	NULL
> +sql_mode	
> +Definer	root@localhost
> +character_set_client	#
> +collation_connection	latin1_swedish_ci
> +Database Collation	latin1_swedish_ci
> +Trigger	before_del
> +Event	DELETE
> +Table	t1
> +Statement	CALL trg_msg(OLD.a)
> +Timing	BEFORE
> +Created	NULL
> +sql_mode	
> +Definer	root@localhost
> +character_set_client	#
> +collation_connection	latin1_swedish_ci
> +Database Collation	latin1_swedish_ci
> +Trigger	after_del
> +Event	DELETE
> +Table	t1
> +Statement	CALL trg_msg(OLD.a)
> +Timing	AFTER
> +Created	NULL
> +sql_mode	
> +Definer	root@localhost
> +character_set_client	#
> +collation_connection	latin1_swedish_ci
> +Database Collation	latin1_swedish_ci
> +SHOW EVENTS IN db;
> +Db	db
> +Name	ev
> +Definer	root@localhost
> +Time zone	SYSTEM
> +Type	RECURRING
> +Execute at	NULL
> +Interval value	1
> +Interval field	SECOND
> +Starts	#
> +Ends	NULL
> +Status	ENABLED
> +Originator	1
> +character_set_client	latin1
> +collation_connection	latin1_swedish_ci
> +Database Collation	latin1_swedish_ci
> +Checking that no db event or trigger fired during RESTORE.
> +SELECT * FROM test.logt WHERE db = 'db' AND timediff(ts,@start) < 2;
> +ts	db	msg
> +Checking that test event and trigger could fire.
> +SELECT count(*) > 0 FROM test.logt 
> +WHERE db = 'test'
> +AND msg LIKE '%trigger fired%'
> +AND timediff(ts,@start) < 2;
> +count(*) > 0
> +1
> +SELECT count(*) > 0 FROM test.logt 
> +WHERE db = 'test'
> +AND msg LIKE '%event fired%'
> +AND timediff(ts,@start) < 2;
> +count(*) > 0
> +1
> +Cleaning up.
> +DROP EVENT test.ev;
> +DROP TRIGGER test.trg;
> +DROP TABLE test.logt;
> +DROP TABLE test.t1;
> +DROP DATABASE db;
> 
> === added file 'mysql-test/t/backup_triggers_and_events.test'
> --- a/mysql-test/t/backup_triggers_and_events.test	1970-01-01 00:00:00 +0000
> +++ b/mysql-test/t/backup_triggers_and_events.test	2008-06-25 10:03:42 +0000
> @@ -0,0 +1,214 @@
> +--source include/have_debug_sync.inc
> +--source include/not_embedded.inc
> +
> +# This test checks that re-created events or triggers are not fired during
> +# RESTORE operation.
> +#
> +# Author: Rafal Somla
> +
> +--disable_warnings
> +--error 0,1
> +--remove_file $MYSQL_TEST_DIR/var/master-data/db.bak
> +--enable_warnings
> +
> +SET GLOBAL event_scheduler=off;
> +SET DEBUG_SYNC = 'RESET';
> +
> +# We need a separate connection to measure timing for RESTORE command. This is
> +# because of BUG#35806: time stops in a thread executing RESTORE command.
> +
> +connect(con1, localhost, root,,);
> +connect(con2, localhost, root,,);
> +
> +--connection con1
> +
> +# Events and triggers will insert entries into a log table so that we can see
> +# if they have fired.
> +
> +--echo Creating log table.
> +
> +--disable_warnings
> +DROP TABLE IF EXISTS test.logt;
> +--enable_warnings
> +
> +CREATE TABLE test.logt(ts timestamp, db char(8), msg text);
> +
> +--echo Creating database db and its objects.
> +
> +--disable_warnings
> +DROP DATABASE IF EXISTS db;
> +--enable_warnings
> +
> +CREATE DATABASE db;
> +USE db;
> +
> +CREATE TABLE t1 (a int); 
> +INSERT INTO t1 VALUES (0),(1),(2),(3),(4),(5),(6);
> +
> +delimiter ||;
> +
> +CREATE EVENT ev ON SCHEDULE EVERY 1 second DO 
> +BEGIN
> +  INSERT INTO test.logt(db, msg) VALUES ('db','Db event fired!');
> +END;
> +||
> +
> +CREATE PROCEDURE trg_msg(a int)
> +BEGIN
> +    INSERT INTO test.logt(db, msg) VALUES ('db','Db trigger fired!');
> +END;
> +||
> +
> +CREATE TRIGGER after_ins AFTER INSERT ON t1 FOR EACH ROW 
> +CALL trg_msg(NEW.a);
> +||
> +
> +CREATE TRIGGER after_upd AFTER UPDATE ON t1 FOR EACH ROW 
> +CALL trg_msg(NEW.a);
> +||
> +
> +CREATE TRIGGER after_del AFTER DELETE ON t1 FOR EACH ROW 
> +CALL trg_msg(OLD.a);
> +||
> +
> +CREATE TRIGGER before_ins BEFORE INSERT ON t1 FOR EACH ROW 
> +CALL trg_msg(NEW.a);
> +||
> +
> +CREATE TRIGGER before_upd BEFORE UPDATE ON t1 FOR EACH ROW 
> +CALL trg_msg(NEW.a);
> +||
> +
> +CREATE TRIGGER before_del BEFORE DELETE ON t1 FOR EACH ROW 
> +CALL trg_msg(OLD.a);
> +||
> +
> +# Create an event and trigger in test database to see that they are not
> +# affected by RESTORE of another database.
> +
> +USE test||
> +
> +--disable_warnigns
> +DROP EVENT IF EXISTS ev||
> +DROP TABLE IF EXISTS t1||
> +DROP TRIGGER IF EXISTS trg||
> +--enable_warnings
> +
> +CREATE EVENT ev ON SCHEDULE EVERY 1 second DO
> +BEGIN
> +  INSERT INTO test.logt(db, msg) VALUES ('test','Test event fired!');
> +END;
> +||
> +
> +CREATE TABLE t1 (a int)||
> +
> +CREATE TRIGGER trg AFTER INSERT ON t1 FOR EACH ROW
> +BEGIN
> +  INSERT INTO test.logt(db, msg) VALUES ('test','Test trigger fired');   
> +END;
> +||
> +
> +delimiter ;||
> +
> +--echo Backing-up database db and dropping it.
> +
> +--replace_column 1 #
> +BACKUP DATABASE db TO 'db.bak';
> +DROP DATABASE db;
> +
> +--echo Enabling event scheduler.
> +SET GLOBAL event_scheduler=on;
> +
> +--connection con1
> +
> +--echo con1: clearing log table and starting RESTORE operation.
> +--echo con1: RESTORE will pause after restoring table data.
> +
> +# Synchronization point 'restore_table_data_before_end' is inside RESTORE code,
> +# after restore drivers have finished their job but before they have been shoot
> +# down.
> +
> +SET DEBUG_SYNC = 'restore_table_data_before_end SIGNAL waiting WAIT_FOR continue';
> +DELETE FROM test.logt;
> +--send RESTORE FROM 'db.bak'
> +
> +--connection con2
> +
> +# Record the time when RESTORE has started.
> +SELECT now() INTO @start;
> +
> +--echo con2: checking that there are no triggers and events at the end of RESTORE
> execution.
> +
> +# Wait until RESTORE reaches the moment when all table data is restored.
> +SET DEBUG_SYNC = 'now WAIT_FOR waiting';
> +# There should be no triggers and no events at this moment (they are created
> +# after table data is restored)
> +--query_vertical SHOW TRIGGERS FROM db
> +--query_vertical SHOW EVENTS IN db
> +
> +--echo con2: activating trigger in test database.
> +INSERT INTO test.t1 VALUES (1);
> +
> +--echo con2: ensuring that RESTORE takes at least 3 secs.
> +
> +# This is so that db.ev event has chance to fire if it is not correctly handled
> +# (e.g. enabled during table data restore).
> +--sleep 3
> +SET DEBUG_SYNC = 'now SIGNAL continue';
> +
> +--connection con1
> +
> +--echo con1: finishing RESTORE operation.
> +--replace_column 1 #
> +--reap
> +SET GLOBAL event_scheduler=off;
> +
> +--connection con2
> +
> +-- echo con2: checking that RESTORE took more than 2 secs.
> +
> +SELECT timediff(now(),@start) > 2;
> +
> +--echo Checking that objects have been restored.
> +
> +USE db;
> +
> +SHOW TABLES IN db;
> +SELECT count(*) FROM db.t1;
> +--replace_column 9 #
> +--query_vertical SHOW TRIGGERS FROM db
> +--replace_column 9 #
> +--query_vertical SHOW EVENTS IN db
> +
> +--echo Checking that no db event or trigger fired during RESTORE.
> +
> +# There should be no entries in the log table from the time when RESTORE
> +# was running (but there could be entries inserted by event firing *after*
> +# RESTORE has completed). We know that RESTORE took at least 3 sec and we 
> +# take 2 sec window form the beginning of the operation. This is enough
> +# to see db.ev in case it fired during RESTORE operation (this event is sheduled
> +# to fire every second).
> +
> +SELECT * FROM test.logt WHERE db = 'db' AND timediff(ts,@start) < 2;
> +
> +--echo Checking that test event and trigger could fire.
> +
> +# Checking that the trigger has fired.
> +SELECT count(*) > 0 FROM test.logt 
> +WHERE db = 'test'
> +AND msg LIKE '%trigger fired%'
> +AND timediff(ts,@start) < 2;
> +
> +# Checking that the event has fired.
> +SELECT count(*) > 0 FROM test.logt 
> +WHERE db = 'test'
> +AND msg LIKE '%event fired%'
> +AND timediff(ts,@start) < 2;
> +
> +--echo Cleaning up.
> +DROP EVENT test.ev;
> +DROP TRIGGER test.trg;
> +DROP TABLE test.logt;
> +DROP TABLE test.t1;
> +DROP DATABASE db;
> +--remove_file $MYSQL_TEST_DIR/var/master-data/db.bak
> 
> === modified file 'sql/backup/backup_kernel.h'
> --- a/sql/backup/backup_kernel.h	2008-05-05 15:06:40 +0000
> +++ b/sql/backup/backup_kernel.h	2008-06-25 10:03:42 +0000
> @@ -113,6 +113,7 @@ class Backup_restore_ctx: public backup:
>  
>    int prepare(LEX_STRING location);
>    void disable_fkey_constraints();
> +  int  restore_triggers_and_events();
>    
>    friend class Backup_info;
>    friend class Restore_info;
> 
> === modified file 'sql/backup/data_backup.cc'
> --- a/sql/backup/data_backup.cc	2008-06-05 12:26:31 +0000
> +++ b/sql/backup/data_backup.cc	2008-06-25 10:03:42 +0000
> @@ -1569,6 +1569,8 @@ int restore_table_data(THD*, Restore_inf
>        DBUG_PRINT("restore",("state is %d", state));
>    }
>  
> +  DEBUG_SYNC(::current_thd, "restore_table_data_before_end");
> +  
>    { // Shutting down drivers
>  
>      String bad_drivers;
> 
> === modified file 'sql/backup/kernel.cc'
> --- a/sql/backup/kernel.cc	2008-05-21 10:45:55 +0000
> +++ b/sql/backup/kernel.cc	2008-06-25 10:03:42 +0000
> @@ -769,6 +769,81 @@ int Backup_restore_ctx::do_backup()
>    DBUG_RETURN(0);
>  }
>  
> +/**
> +  Create all triggers and events from restore catalogue.
> +
> +  This helper method iterates over all triggers and events stored in the 
> +  restore catalogue and creates them. When metadata section of the backup image 
> +  is read, trigger and event objects are materialized and stored in the 
> +  catalogue but they are not executed then (see @c bcat_create_item()). 
> +  This method can be used to re-create the corresponding server objects after 
> +  all other objects and table data have been restored.
> +
> +  Note that we first restore all triggers and then the events.
> +
> +  @returns 0 on success, error code otherwise.
> +*/ 
> +int Backup_restore_ctx::restore_triggers_and_events()
> +{
> +  using namespace backup;
> +
> +  DBUG_ASSERT(m_catalog);
> +
> +  Image_info::Iterator *dbit= m_catalog->get_dbs();
> +  Image_info::Obj *obj;
> +  List<Image_info::Obj> events;
> +  Image_info::Obj::describe_buf buf;
> +
> +  DBUG_ENTER("restore_triggers_and_events");
> +
> +  // create all trigers and collect events in the events list
> +  
> +  while ((obj= (*dbit)++)) 
> +  {
> +    Image_info::Iterator *it= 
> +                   
> m_catalog->get_db_objects(*static_cast<Image_info::Db*>(obj));
> +
> +    while ((obj= (*it)++))
> +      switch (obj->type()) {
> +      
> +      case BSTREAM_IT_EVENT:
> +        DBUG_ASSERT(obj->m_obj_ptr);
> +        events.push_back(obj);
> +        break;
> +      
> +      case BSTREAM_IT_TRIGGER:
> +        DBUG_ASSERT(obj->m_obj_ptr);
> +        if (obj->m_obj_ptr->execute(m_thd))
> +        {
> +          delete it;
> +          delete dbit;
> +          fatal_error(ER_BACKUP_CANT_RESTORE_TRIGGER,obj->describe(buf));
> +          DBUG_RETURN(m_error);
> +        }
> +        break;
> +
> +      default: break;      
> +      }
> +
> +    delete it;
> +  }
> +
> +  delete dbit;
> +
> +  // now create all events
> +
> +  List_iterator<Image_info::Obj> it(events);
> +  Image_info::Obj *ev;
> +
> +  while ((ev= it++)) 
> +    if (ev->m_obj_ptr->execute(m_thd))
> +    {
> +      fatal_error(ER_BACKUP_CANT_RESTORE_EVENT,ev->describe(buf));
> +      DBUG_RETURN(m_error);
> +    };
> +
> +  DBUG_RETURN(0);
> +}
>  
>  /**
>    Restore objects saved in backup image.
> @@ -830,6 +905,21 @@ int Backup_restore_ctx::do_restore()
>      DBUG_RETURN(m_error);
>    }
>  
> +  /* 
> +   Re-create all triggers and events (it was not done in @c bcat_create_item()).
> +  */
> +
> +  if (restore_triggers_and_events())
> +     DBUG_RETURN(ER_BACKUP_RESTORE);
> +  
> +  /* 
> +    FIXME: this call is here because object services doesn't clean the
> +    statement execution context properly, which leads to assertion failure.
> +    It should be fixed inside object services implementation and then the
> +    following line should be removed.
> +   */
> +  m_thd->main_da.reset_diagnostics_area();
> +
>    report_stats_post(info);
>  
>    DBUG_RETURN(0);
> @@ -1436,6 +1526,22 @@ int bcat_create_item(st_bstream_image_he
>      return BSTREAM_ERROR;
>    }
>  
> +  /*
> +    If the item we are creating is an event or trigger, we don't execute it
> +    yet. It will be done in @c Backup_restore_ctx::do_restore() after table
> +    data has been restored.
> +   */ 
> +  
> +  switch (item->type) {
> +
> +  case BSTREAM_IT_EVENT:
> +  case BSTREAM_IT_TRIGGER:
> +    return BSTREAM_OK;
> +
> +  default: break;
> +  
> +  }
> +
>    // If we are to create a tablespace, first check if it already exists.
>  
>    if (item->type == BSTREAM_IT_TABLESPACE)
> 
> 


-- 
Jørgen Løland
Thread
bzr commit into mysql-6.0-backup branch (rsomla:2637) WL#4326Rafal Somla25 Jun
  • Re: bzr commit into mysql-6.0-backup branch (rsomla:2637) WL#4326Jørgen Løland25 Jun