Commit 1c93eeaa authored by unknown's avatar unknown
Browse files

Merge bk-internal.mysql.com:/home/bk/mysql-5.0-runtime

into  mysql.com:/home/dlenev/src/mysql-5.0-bg13525


sql/sql_table.cc:
  Auto merged
sql/sql_trigger.cc:
  Auto merged
sql/sql_trigger.h:
  Auto merged
sql/sql_yacc.yy:
  Auto merged
mysql-test/r/trigger.result:
  SCCS merged
mysql-test/t/trigger.test:
  SCCS merged
parents 258ad72c f8386dfa
Loading
Loading
Loading
Loading
+102 −1
Original line number Diff line number Diff line
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;
@@ -785,6 +785,107 @@ 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;
create table t1 (i int);
create trigger t1_bi before insert on t1 for each row return 0;
ERROR 42000: RETURN is only allowed in a FUNCTION
+86 −1
Original line number Diff line number Diff line
@@ -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;
@@ -959,6 +959,91 @@ create trigger test.t1_bi before insert on t1 for each row set @a:=0;
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;

# Test for bug #16829 "Firing trigger with RETURN crashes the server"
# RETURN is not supposed to be used anywhere except functions, so error
# should be returned when one attempts to create trigger with RETURN.
+21 −2
Original line number Diff line number Diff line
@@ -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:
+12 −1
Original line number Diff line number Diff line
@@ -3287,6 +3287,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));
    }
@@ -3835,7 +3842,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));
+269 −21
Original line number Diff line number Diff line
@@ -65,7 +65,6 @@ File_option sql_modes_parameters=
*/

static const int TRG_NUM_REQUIRED_PARAMETERS= 4;
static const int TRG_MAX_VERSIONS= 3;

/*
  Structure representing contents of .TRN file which are used to support
@@ -467,8 +466,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
                               definer_host->str, NullS) - trg_definer->str;

  if (!sql_create_definition_file(&dir, &file, &triggers_file_type,
                                  (gptr)this, triggers_file_parameters,
                                  TRG_MAX_VERSIONS))
                                  (gptr)this, triggers_file_parameters, 0))
    return 0;

err_with_cleanup:
@@ -492,7 +490,8 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
    True  - error
*/

static bool rm_trigger_file(char *path, char *db, char *table_name)
static bool rm_trigger_file(char *path, const char *db,
                            const char *table_name)
{
  strxnmov(path, FN_REFLEN, mysql_data_home, "/", db, "/", table_name,
           triggers_file_ext, NullS);
@@ -516,7 +515,8 @@ static bool rm_trigger_file(char *path, char *db, char *table_name)
    True  - error
*/

static bool rm_trigname_file(char *path, char *db, char *trigger_name)
static bool rm_trigname_file(char *path, const char *db,
                             const char *trigger_name)
{
  strxnmov(path, FN_REFLEN, mysql_data_home, "/", db, "/", trigger_name,
           trigname_file_ext, NullS);
@@ -525,6 +525,38 @@ static bool rm_trigname_file(char *path, char *db, char *trigger_name)
}


/*
  Helper function that saves .TRG file for Table_triggers_list object.

  SYNOPSIS
    save_trigger_file()
      triggers    Table_triggers_list object for which file should be saved
      db          Name of database for subject table
      table_name  Name of subject table

  RETURN VALUE
    FALSE  Success
    TRUE   Error
*/

static bool save_trigger_file(Table_triggers_list *triggers, const char *db,
                              const char *table_name)
{
  char dir_buff[FN_REFLEN], file_buff[FN_REFLEN];
  LEX_STRING dir, file;

  strxnmov(dir_buff, FN_REFLEN, mysql_data_home, "/", db, "/", NullS);
  dir.length= unpack_filename(dir_buff, dir_buff);
  dir.str= dir_buff;
  file.length=  strxnmov(file_buff, FN_REFLEN, table_name, triggers_file_ext,
                         NullS) - file_buff;
  file.str= file_buff;

  return sql_create_definition_file(&dir, &file, &triggers_file_type,
                                    (gptr)triggers, triggers_file_parameters, 0);
}


/*
  Drop trigger for table.

@@ -578,20 +610,7 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables)
      }
      else
      {
        char dir_buff[FN_REFLEN], file_buff[FN_REFLEN];
        LEX_STRING dir, file;

        strxnmov(dir_buff, FN_REFLEN, mysql_data_home, "/", tables->db,
                 "/", NullS);
        dir.length= unpack_filename(dir_buff, dir_buff);
        dir.str= dir_buff;
        file.length=  strxnmov(file_buff, FN_REFLEN, tables->table_name,
                               triggers_file_ext, NullS) - file_buff;
        file.str= file_buff;

        if (sql_create_definition_file(&dir, &file, &triggers_file_type,
                                       (gptr)this, triggers_file_parameters,
                                       TRG_MAX_VERSIONS))
        if (save_trigger_file(this, tables->db, tables->table_name))
          return 1;
      }

@@ -831,12 +850,12 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
      if (!names_only && triggers->prepare_record1_accessors(table))
        DBUG_RETURN(1);

      char *trg_name_buff;
      List_iterator_fast<ulonglong> itm(triggers->definition_modes_list);
      List_iterator_fast<LEX_STRING> it_definer(triggers->definers_list);
      LEX *old_lex= thd->lex, lex;
      sp_rcontext *save_spcont= thd->spcont;
      ulong save_sql_mode= thd->variables.sql_mode;
      LEX_STRING *on_table_name;

      thd->lex= &lex;

@@ -902,6 +921,21 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
                                           &table->mem_root))
            goto err_with_lex_cleanup;

        if (!(on_table_name= (LEX_STRING*) alloc_root(&table->mem_root,
                                                      sizeof(LEX_STRING))))
          goto err_with_lex_cleanup;
        *on_table_name= lex.ident;
        if (triggers->on_table_names_list.push_back(on_table_name, &table->mem_root))
          goto err_with_lex_cleanup;

        /*
          Let us check that we correctly update trigger definitions when we
          rename tables with triggers.
        */
        DBUG_ASSERT(!my_strcasecmp(table_alias_charset, lex.query_tables->db, db) &&
                    !my_strcasecmp(table_alias_charset, lex.query_tables->table_name,
                                   table_name));

        if (names_only)
        {
          lex_end(&lex);
@@ -1067,7 +1101,7 @@ static TABLE_LIST *add_table_for_trigger(THD *thd, sp_name *trig)
  lex->query_tables= 0;
  lex->query_tables_last= &lex->query_tables;
  DBUG_RETURN(sp_add_to_query_tables(thd, lex, trig->m_db.str,
                                     trigname.trigger_table.str, TL_WRITE));
                                     trigname.trigger_table.str, TL_IGNORE));
}


@@ -1137,6 +1171,220 @@ bool Table_triggers_list::drop_all_triggers(THD *thd, char *db, char *name)
}


/*
  Update .TRG file after renaming triggers' subject table
  (change name of table in triggers' definitions).

  SYNOPSIS
    change_table_name_in_triggers()
      thd                 Thread context
      db_name             Database of subject table
      old_table_name      Old subject table's name
      new_table_name      New subject table's name

  RETURN VALUE
    FALSE  Success
    TRUE   Failure
*/

bool
Table_triggers_list::change_table_name_in_triggers(THD *thd,
                                                   const char *db_name,
                                                   LEX_STRING *old_table_name,
                                                   LEX_STRING *new_table_name)
{
  char path_buff[FN_REFLEN];
  LEX_STRING *def, *on_table_name, new_def;
  ulonglong *sql_mode;
  ulong save_sql_mode= thd->variables.sql_mode;
  List_iterator_fast<LEX_STRING> it_def(definitions_list);
  List_iterator_fast<LEX_STRING> it_on_table_name(on_table_names_list);
  List_iterator_fast<ulonglong> it_mode(definition_modes_list);
  uint on_q_table_name_len, before_on_len;
  String buff;

  DBUG_ASSERT(definitions_list.elements == on_table_names_list.elements &&
              definitions_list.elements == definition_modes_list.elements);

  while ((def= it_def++))
  {
    on_table_name= it_on_table_name++;
    thd->variables.sql_mode= *(it_mode++);

    /* Construct CREATE TRIGGER statement with new table name. */
    buff.length(0);
    before_on_len= on_table_name->str - def->str;
    buff.append(def->str, before_on_len);
    buff.append(STRING_WITH_LEN("ON "));
    append_identifier(thd, &buff, new_table_name->str, new_table_name->length);
    on_q_table_name_len= buff.length() - before_on_len;
    buff.append(on_table_name->str + on_table_name->length,
                def->length - (before_on_len + on_table_name->length));
    /*
      It is OK to allocate some memory on table's MEM_ROOT since this
      table instance will be thrown out at the end of rename anyway.
    */
    new_def.str= memdup_root(&table->mem_root, buff.ptr(), buff.length());
    new_def.length= buff.length();
    on_table_name->str= new_def.str + before_on_len;
    on_table_name->length= on_q_table_name_len;
    *def= new_def;
  }

  thd->variables.sql_mode= save_sql_mode;

  if (thd->is_fatal_error)
    return TRUE; /* OOM */

  if (save_trigger_file(this, db_name, new_table_name->str))
    return TRUE;
  if (rm_trigger_file(path_buff, db_name, old_table_name->str))
  {
    (void) rm_trigger_file(path_buff, db_name, new_table_name->str);
    return TRUE;
  }
  return FALSE;
}


/*
  Iterate though Table_triggers_list::names_list list and update .TRN files
  after renaming triggers' subject table.

  SYNOPSIS
    change_table_name_in_trignames()
      db_name             Database of subject table
      new_table_name      New subject table's name
      stopper             Pointer to Table_triggers_list::names_list at
                          which we should stop updating.

  RETURN VALUE
    0      Success
    non-0  Failure, pointer to Table_triggers_list::names_list element
           for which update failed.
*/

LEX_STRING*
Table_triggers_list::change_table_name_in_trignames(const char *db_name,
                                                    LEX_STRING *new_table_name,
                                                    LEX_STRING *stopper)
{
  char dir_buff[FN_REFLEN], trigname_buff[FN_REFLEN];
  struct st_trigname trigname;
  LEX_STRING dir, trigname_file;
  LEX_STRING *trigger;
  List_iterator_fast<LEX_STRING> it_name(names_list);

  strxnmov(dir_buff, FN_REFLEN, mysql_data_home, "/", db_name, "/", NullS);
  dir.length= unpack_filename(dir_buff, dir_buff);
  dir.str= dir_buff;

  while ((trigger= it_name++) != stopper)
  {
    trigname_file.length= strxnmov(trigname_buff, FN_REFLEN, trigger->str,
                                   trigname_file_ext, NullS) - trigname_buff;
    trigname_file.str= trigname_buff;

    trigname.trigger_table= *new_table_name;

    if (sql_create_definition_file(&dir, &trigname_file, &trigname_file_type,
        (gptr)&trigname, trigname_file_parameters, 0))
      return trigger;
  }

  return 0;
}


/*
  Update .TRG and .TRN files after renaming triggers' subject table.

  SYNOPSIS
    change_table_name()
      thd        Thread context
      db         Old database of subject table
      old_table  Old name of subject table
      new_db     New database for subject table
      new_table  New name of subject table

  NOTE
    This method tries to leave trigger related files in consistent state,
    i.e. it either will complete successfully, or will fail leaving files
    in their initial state.

  RETURN VALUE
    FALSE  Success
    TRUE   Error
*/

bool Table_triggers_list::change_table_name(THD *thd, const char *db,
                                            const char *old_table,
                                            const char *new_db,
                                            const char *new_table)
{
  TABLE table;
  bool result= 0;
  LEX_STRING *err_trigname;
  DBUG_ENTER("change_table_name");

  bzero(&table, sizeof(table));
  init_alloc_root(&table.mem_root, 8192, 0);

  safe_mutex_assert_owner(&LOCK_open);

  if (Table_triggers_list::check_n_load(thd, db, old_table, &table, TRUE))
  {
    result= 1;
    goto end;
  }
  if (table.triggers)
  {
    LEX_STRING_WITH_INIT old_table_name(old_table, strlen(old_table));
    LEX_STRING_WITH_INIT new_table_name(new_table, strlen(new_table));
    /*
      Since triggers should be in the same schema as their subject tables
      moving table with them between two schemas raises too many questions.
      (E.g. what should happen if in new schema we already have trigger
       with same name ?).
    */
    if (my_strcasecmp(table_alias_charset, db, new_db))
    {
      my_error(ER_TRG_IN_WRONG_SCHEMA, MYF(0));
      result= 1;
      goto end;
    }
    if (table.triggers->change_table_name_in_triggers(thd, db,
                                                      &old_table_name,
                                                      &new_table_name))
    {
      result= 1;
      goto end;
    }
    if ((err_trigname= table.triggers->change_table_name_in_trignames(
                                         db, &new_table_name, 0)))
    {
      /*
        If we were unable to update one of .TRN files properly we will
        revert all changes that we have done and report about error.
        We assume that we will be able to undo our changes without errors
        (we can't do much if there will be an error anyway).
      */
      (void) table.triggers->change_table_name_in_trignames(db,
                                                            &old_table_name,
                                                            err_trigname);
      (void) table.triggers->change_table_name_in_triggers(thd, db,
                                                           &new_table_name,
                                                           &old_table_name);
      result= 1;
      goto end;
    }
  }
end:
  delete table.triggers;
  free_root(&table.mem_root, MYF(0));
  DBUG_RETURN(result);
}


bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event,
                                           trg_action_time_type time_type,
Loading