Commit f8386dfa authored by unknown's avatar unknown

Fix for bug #13525 "Rename table does not keep info of triggers".

Let us transfer triggers associated with table when we rename it (but only if
we are not changing database to which table belongs, in the latter case we will
emit error).


mysql-test/r/trigger.result:
  Added test for bug #13525 "Rename table does not keep info of triggers".
mysql-test/t/trigger.test:
  Added test for bug #13525 "Rename table does not keep info of triggers".
sql/sql_rename.cc:
  rename_tables():
    Now after renaming table's .FRM file and updating handler data we call
    Table_triggers_list::change_table_name() which is reponsible for
    updating .TRG and .TRN files.
sql/sql_table.cc:
  mysql_alter_table():
    Now in case when ALTER should rename table we call
    Table_triggers_list::change_table_name() which is responsible
    for updating .TRG and .TRN files after renaming table.
sql/sql_trigger.cc:
  Added Table_triggers_list::change_table_name() method and
  change_table_name_in_triggers()/trignames() methods responsible for updating
  .TRG and .TRN files for table during its renaming.
  
  Two small cleanups - removed versioning for .TRG files (since it was not working
  before anyway) and emphasized that type of lock specified in tables list is
  unimportant for DROP TABLE (since this statement uses name-locking).
sql/sql_trigger.h:
  Table_triggers_list:
    Added on_table_names_list member to store pointers and lenghts of
    "ON table_name" parts in triggers' definitions to be able easily
    change them during RENAME TABLE.
    Added change_table_name() method and change_table_name_in_trignames/triggers()
    helper methods responsible for updating .TRG and .TRN files.
sql/sql_yacc.yy:
  trigger_tail:
    To be able properly update triggers' definitions with new table names
    when renaming tables we need to know where in CREATE TRIGGER statement
    "ON db_name.table_name" part resides.
    Small cleanup - let us emphasize that for CREATE TRIGGER statement 
    lock type which is specified in table list is unimportant since
    name-locking is used.
parent b479c27f
drop table if exists t1, t2, t3;
drop table if exists t1, t2, t3, t4;
drop view if exists v1;
drop database if exists mysqltest;
drop function if exists f1;
......@@ -786,3 +786,104 @@ create trigger test.t1_bi before insert on t1 for each row set @a:=0;
ERROR 3D000: No database selected
drop trigger t1_bi;
ERROR 3D000: No database selected
create table t1 (id int);
create trigger t1_bi before insert on t1 for each row set @a:=new.id;
insert into t1 values (101);
select @a;
@a
101
select trigger_schema, trigger_name, event_object_schema,
event_object_table, action_statement from information_schema.triggers
where event_object_schema = 'test';
trigger_schema trigger_name event_object_schema event_object_table action_statement
test t1_bi test t1 set @a:=new.id
rename table t1 to t2;
insert into t2 values (102);
select @a;
@a
102
select trigger_schema, trigger_name, event_object_schema,
event_object_table, action_statement from information_schema.triggers
where event_object_schema = 'test';
trigger_schema trigger_name event_object_schema event_object_table action_statement
test t1_bi test t2 set @a:=new.id
alter table t2 rename to t3;
insert into t3 values (103);
select @a;
@a
103
select trigger_schema, trigger_name, event_object_schema,
event_object_table, action_statement from information_schema.triggers
where event_object_schema = 'test';
trigger_schema trigger_name event_object_schema event_object_table action_statement
test t1_bi test t3 set @a:=new.id
alter table t3 rename to t4, add column val int default 0;
insert into t4 values (104, 1);
select @a;
@a
104
select trigger_schema, trigger_name, event_object_schema,
event_object_table, action_statement from information_schema.triggers
where event_object_schema = 'test';
trigger_schema trigger_name event_object_schema event_object_table action_statement
test t1_bi test t4 set @a:=new.id
drop trigger t1_bi;
drop table t4;
create database mysqltest;
use mysqltest;
create table t1 (id int);
create trigger t1_bi before insert on t1 for each row set @a:=new.id;
insert into t1 values (101);
select @a;
@a
101
select trigger_schema, trigger_name, event_object_schema,
event_object_table, action_statement from information_schema.triggers
where event_object_schema = 'test' or event_object_schema = 'mysqltest';
trigger_schema trigger_name event_object_schema event_object_table action_statement
mysqltest t1_bi mysqltest t1 set @a:=new.id
rename table t1 to test.t2;
ERROR HY000: Trigger in wrong schema
insert into t1 values (102);
select @a;
@a
102
select trigger_schema, trigger_name, event_object_schema,
event_object_table, action_statement from information_schema.triggers
where event_object_schema = 'test' or event_object_schema = 'mysqltest';
trigger_schema trigger_name event_object_schema event_object_table action_statement
mysqltest t1_bi mysqltest t1 set @a:=new.id
drop trigger test.t1_bi;
ERROR HY000: Trigger does not exist
drop trigger t1_bi;
drop table t1;
drop database mysqltest;
use test;
create table t1 (id int);
create trigger t1_bi before insert on t1 for each row set @a:=new.id;
create trigger t1_ai after insert on t1 for each row set @b:=new.id;
insert into t1 values (101);
select @a, @b;
@a @b
101 101
select trigger_schema, trigger_name, event_object_schema,
event_object_table, action_statement from information_schema.triggers
where event_object_schema = 'test';
trigger_schema trigger_name event_object_schema event_object_table action_statement
test t1_bi test t1 set @a:=new.id
test t1_ai test t1 set @b:=new.id
rename table t1 to t2;
ERROR HY000: Can't create/write to file './test/t1_ai.TRN~' (Errcode: 13)
insert into t1 values (102);
select @a, @b;
@a @b
102 102
select trigger_schema, trigger_name, event_object_schema,
event_object_table, action_statement from information_schema.triggers
where event_object_schema = 'test';
trigger_schema trigger_name event_object_schema event_object_table action_statement
test t1_bi test t1 set @a:=new.id
test t1_ai test t1 set @b:=new.id
drop trigger t1_bi;
drop trigger t1_ai;
drop table t1;
......@@ -3,7 +3,7 @@
#
--disable_warnings
drop table if exists t1, t2, t3;
drop table if exists t1, t2, t3, t4;
drop view if exists v1;
drop database if exists mysqltest;
drop function if exists f1;
......@@ -961,3 +961,88 @@ create trigger test.t1_bi before insert on t1 for each row set @a:=0;
--error ER_NO_DB_ERROR
drop trigger t1_bi;
connection default;
#
# Test for bug #13525 "Rename table does not keep info of triggers"
#
create table t1 (id int);
create trigger t1_bi before insert on t1 for each row set @a:=new.id;
insert into t1 values (101);
select @a;
select trigger_schema, trigger_name, event_object_schema,
event_object_table, action_statement from information_schema.triggers
where event_object_schema = 'test';
rename table t1 to t2;
# Trigger should work after rename
insert into t2 values (102);
select @a;
select trigger_schema, trigger_name, event_object_schema,
event_object_table, action_statement from information_schema.triggers
where event_object_schema = 'test';
# Let us check that the same works for simple ALTER TABLE ... RENAME
alter table t2 rename to t3;
insert into t3 values (103);
select @a;
select trigger_schema, trigger_name, event_object_schema,
event_object_table, action_statement from information_schema.triggers
where event_object_schema = 'test';
# And for more complex ALTER TABLE
alter table t3 rename to t4, add column val int default 0;
insert into t4 values (104, 1);
select @a;
select trigger_schema, trigger_name, event_object_schema,
event_object_table, action_statement from information_schema.triggers
where event_object_schema = 'test';
# .TRN file should be updated with new table name
drop trigger t1_bi;
drop table t4;
# Rename between different databases if triggers exist should fail
create database mysqltest;
use mysqltest;
create table t1 (id int);
create trigger t1_bi before insert on t1 for each row set @a:=new.id;
insert into t1 values (101);
select @a;
select trigger_schema, trigger_name, event_object_schema,
event_object_table, action_statement from information_schema.triggers
where event_object_schema = 'test' or event_object_schema = 'mysqltest';
--error ER_TRG_IN_WRONG_SCHEMA
rename table t1 to test.t2;
insert into t1 values (102);
select @a;
select trigger_schema, trigger_name, event_object_schema,
event_object_table, action_statement from information_schema.triggers
where event_object_schema = 'test' or event_object_schema = 'mysqltest';
# There should be no fantom .TRN files
--error ER_TRG_DOES_NOT_EXIST
drop trigger test.t1_bi;
drop trigger t1_bi;
drop table t1;
drop database mysqltest;
use test;
# And now let us check that the properly handle rename if there is some
# error during it (that we rollback such renames completely).
create table t1 (id int);
create trigger t1_bi before insert on t1 for each row set @a:=new.id;
create trigger t1_ai after insert on t1 for each row set @b:=new.id;
insert into t1 values (101);
select @a, @b;
select trigger_schema, trigger_name, event_object_schema,
event_object_table, action_statement from information_schema.triggers
where event_object_schema = 'test';
# Trick which makes update of second .TRN file impossible
system echo dummy >var/master-data/test/t1_ai.TRN~;
system chmod 000 var/master-data/test/t1_ai.TRN~;
--error 1
rename table t1 to t2;
# 't1' should be still there and triggers should work correctly
insert into t1 values (102);
select @a, @b;
select trigger_schema, trigger_name, event_object_schema,
event_object_table, action_statement from information_schema.triggers
where event_object_schema = 'test';
system chmod 600 var/master-data/test/t1_ai.TRN;
# Let us check that updates to .TRN files were rolled back too
drop trigger t1_bi;
drop trigger t1_ai;
drop table t1;
......@@ -19,6 +19,7 @@
*/
#include "mysql_priv.h"
#include "sql_trigger.h"
static TABLE_LIST *rename_tables(THD *thd, TABLE_LIST *table_list,
......@@ -176,8 +177,26 @@ rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error)
if (table_type == DB_TYPE_UNKNOWN)
my_error(ER_FILE_NOT_FOUND, MYF(0), name, my_errno);
else
rc= mysql_rename_table(table_type, ren_table->db, old_alias,
new_table->db, new_alias);
{
if (!(rc= mysql_rename_table(table_type, ren_table->db, old_alias,
new_table->db, new_alias)))
{
if ((rc= Table_triggers_list::change_table_name(thd, ren_table->db,
old_alias,
new_table->db,
new_alias)))
{
/*
We've succeeded in renaming table's .frm and in updating
corresponding handler data, but have failed to update table's
triggers appropriately. So let us revert operations on .frm
and handler's data and report about failure to rename table.
*/
(void) mysql_rename_table(table_type, new_table->db, new_alias,
ren_table->db, old_alias);
}
}
}
break;
}
case FRMTYPE_VIEW:
......
......@@ -3258,6 +3258,13 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
close_cached_table(thd, table);
if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias))
error= -1;
else if (Table_triggers_list::change_table_name(thd, db, table_name,
new_db, new_alias))
{
VOID(mysql_rename_table(old_db_type, new_db, new_alias, db,
table_name));
error= -1;
}
}
VOID(pthread_mutex_unlock(&LOCK_open));
}
......@@ -3806,7 +3813,11 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
VOID(quick_rm_table(new_db_type,new_db,tmp_name));
}
else if (mysql_rename_table(new_db_type,new_db,tmp_name,new_db,
new_alias))
new_alias) ||
(new_name != table_name || new_db != db) && // we also do rename
Table_triggers_list::change_table_name(thd, db, table_name,
new_db, new_alias))
{ // Try to get everything back
error=1;
VOID(quick_rm_table(new_db_type,new_db,new_alias));
......
This diff is collapsed.
......@@ -46,6 +46,11 @@ class Table_triggers_list: public Sql_alloc
used in CREATE/DROP TRIGGER for looking up trigger by name.
*/
List<LEX_STRING> names_list;
/*
List of "ON table_name" parts in trigger definitions, used for
updating trigger definitions during RENAME TABLE.
*/
List<LEX_STRING> on_table_names_list;
/*
Key representing triggers for this table in set of all stored
routines used by statement.
......@@ -97,7 +102,10 @@ public:
static bool check_n_load(THD *thd, const char *db, const char *table_name,
TABLE *table, bool names_only);
static bool drop_all_triggers(THD *thd, char *db, char *table_name);
static bool change_table_name(THD *thd, const char *db,
const char *old_table,
const char *new_db,
const char *new_table);
bool has_delete_triggers()
{
return (bodies[TRG_EVENT_DELETE][TRG_ACTION_BEFORE] ||
......@@ -122,6 +130,13 @@ public:
private:
bool prepare_record1_accessors(TABLE *table);
LEX_STRING* change_table_name_in_trignames(const char *db_name,
LEX_STRING *new_table_name,
LEX_STRING *stopper);
bool change_table_name_in_triggers(THD *thd,
const char *db_name,
LEX_STRING *old_table_name,
LEX_STRING *new_table_name);
};
extern const LEX_STRING trg_action_time_type_names[];
......
......@@ -9070,8 +9070,8 @@ view_check_option:
**************************************************************************/
trigger_tail:
TRIGGER_SYM remember_name sp_name trg_action_time trg_event
ON table_ident FOR_SYM EACH_SYM ROW_SYM
TRIGGER_SYM remember_name sp_name trg_action_time trg_event
ON remember_name table_ident remember_end FOR_SYM EACH_SYM ROW_SYM
{
LEX *lex= Lex;
sp_head *sp;
......@@ -9088,7 +9088,9 @@ trigger_tail:
sp->init(lex);
lex->trigger_definition_begin= $2;
lex->ident.str= $7;
lex->ident.length= $9 - $7;
sp->m_type= TYPE_ENUM_TRIGGER;
lex->sphead= sp;
lex->spname= $3;
......@@ -9123,15 +9125,11 @@ trigger_tail:
We have to do it after parsing trigger body, because some of
sp_proc_stmt alternatives are not saving/restoring LEX, so
lex->query_tables can be wiped out.
QQ: What are other consequences of this?
QQ: Could we loosen lock type in certain cases ?
*/
if (!lex->select_lex.add_table_to_list(YYTHD, $7,
if (!lex->select_lex.add_table_to_list(YYTHD, $8,
(LEX_STRING*) 0,
TL_OPTION_UPDATING,
TL_WRITE))
TL_IGNORE))
YYABORT;
}
;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment