Commit dceb8fb2 authored by unknown's avatar unknown
Browse files

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

into  mysql.com:/home/dlenev/src/mysql-5.0-bg12704-2


sql/mysql_priv.h:
  Auto merged
sql/sp.cc:
  Auto merged
sql/sp_head.cc:
  Auto merged
sql/sql_base.cc:
  Auto merged
sql/sql_lex.h:
  Auto merged
sql/sql_prepare.cc:
  Auto merged
sql/sql_table.cc:
  Auto merged
sql/sql_update.cc:
  Auto merged
parents 11e0bdd7 d5303b8a
Loading
Loading
Loading
Loading
+113 −3
Original line number Diff line number Diff line
@@ -10,6 +10,11 @@ drop function if exists f1;
drop procedure if exists p1;
--enable_warnings

# Create additional connections used through test
connect (addconroot1, localhost, root,,);
connect (addconroot2, localhost, root,,);
connection default;

create table t1 (i int);

# let us test some very simple trigger
@@ -680,12 +685,10 @@ end|
delimiter ;|
update t1 set data = 1;

connect (addconroot, localhost, root,,);
connection addconroot;
connection addconroot1;
update t1 set data = 2;

connection default;
disconnect addconroot;
drop table t1;

#
@@ -765,3 +768,110 @@ insert into t1 values (3);
select * from t1;
drop trigger t1_bi;
drop tables t1, t2;

# Tests for bug #12704 "Server crashes during trigger execution".
# If we run DML statements and CREATE TRIGGER statements concurrently
# it may happen that trigger will be created while DML statement is
# waiting for table lock. In this case we have to reopen tables and
# recalculate prelocking set.
# Unfortunately these tests rely on the order in which tables are locked
# by statement so they are non determenistic and are disabled.
--disable_parsing
create table t1 (id int);
create table t2 (id int);
create table t3 (id int);
create function f1() returns int return (select max(id)+2 from t2);
create view v1 as select f1() as f;

# Let us check that we notice trigger at all
connection addconroot1;
lock tables t2 write;
connection default;
send insert into t1 values ((select max(id) from t2)), (2);
--sleep 1
connection addconroot2;
create trigger t1_trg before insert on t1 for each row set NEW.id:= 1;
connection addconroot1;
unlock tables;
connection default;
reap;
select * from t1;

# Check that we properly calculate new prelocking set
insert into t2 values (3);
connection addconroot1;
lock tables t2 write;
connection default;
send insert into t1 values ((select max(id) from t2)), (4);
--sleep 1
connection addconroot2;
drop trigger t1_trg;
create trigger t1_trg before insert on t1 for each row
  insert into t3 values (new.id);
connection addconroot1;
unlock tables;
connection default;
reap;
select * from t1;
select * from t3;

# We should be able to do this even if fancy views are involved
connection addconroot1;
lock tables t2 write;
connection default;
send insert into t1 values ((select max(f) from v1)), (6);
--sleep 1
connection addconroot2;
drop trigger t1_trg;
create trigger t1_trg before insert on t1 for each row
  insert into t3 values (new.id + 100);
connection addconroot1;
unlock tables;
connection default;
reap;
select * from t1;
select * from t3;

# This also should work for multi-update
# Let us drop trigger to demonstrate that prelocking set is really
# rebuilt
drop trigger t1_trg;
connection addconroot1;
lock tables t2 write;
connection default;
send update t1, t2 set t1.id=10 where t1.id=t2.id;
--sleep 1
connection addconroot2;
create trigger t1_trg before update on t1 for each row
  insert into t3 values (new.id);
connection addconroot1;
unlock tables;
connection default;
reap;
select * from t1;
select * from t3;

# And even for multi-update converted from ordinary update thanks to view
drop view v1;
drop trigger t1_trg;
create view v1 as select t1.id as id1 from t1, t2 where t1.id= t2.id;
insert into t2 values (10);
connection addconroot1;
lock tables t2 write;
connection default;
send update v1 set id1= 11;
--sleep 1
connection addconroot2;
create trigger t1_trg before update on t1 for each row
  insert into t3 values (new.id + 100);
connection addconroot1;
unlock tables;
connection default;
reap;
select * from t1;
select * from t3;

drop function f1;
drop view v1;
drop table t1, t2, t3;
--enable_parsing
+18 −3
Original line number Diff line number Diff line
@@ -93,23 +93,33 @@ static void print_lock_error(int error, const char *);
    flags                       Options:
      MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK      Ignore a global read lock
      MYSQL_LOCK_IGNORE_FLUSH                 Ignore a flush tables.
      MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN        Instead of reopening altered
                                              or dropped tables by itself,
                                              mysql_lock_tables() should
                                              notify upper level and rely
                                              on caller doing this.
    need_reopen                 Out parameter, TRUE if some tables were altered
                                or deleted and should be reopened by caller.

  RETURN
    A lock structure pointer on success.
    NULL on error.
    NULL on error or if some tables should be reopen.
*/

/* Map the return value of thr_lock to an error from errmsg.txt */
static int thr_lock_errno_to_mysql[]=
{ 0, 1, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK };

MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags)
MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
                              uint flags, bool *need_reopen)
{
  MYSQL_LOCK *sql_lock;
  TABLE *write_lock_used;
  int rc;
  /* Map the return value of thr_lock to an error from errmsg.txt */
  DBUG_ENTER("mysql_lock_tables");

  *need_reopen= FALSE;

  for (;;)
  {
    if (!(sql_lock = get_lock_data(thd,tables,count, 0,&write_lock_used)))
@@ -178,6 +188,11 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags)
    thd->locked=0;
retry:
    sql_lock=0;
    if (flags & MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN)
    {
      *need_reopen= TRUE;
      break;
    }
    if (wait_for_tables(thd))
      break;					// Couldn't open tables
  }
+5 −2
Original line number Diff line number Diff line
@@ -952,7 +952,7 @@ int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags);
int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables);
bool open_and_lock_tables(THD *thd,TABLE_LIST *tables);
bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags);
int lock_tables(THD *thd, TABLE_LIST *tables, uint counter);
int lock_tables(THD *thd, TABLE_LIST *tables, uint counter, bool *need_reopen);
TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
			    const char *table_name, bool link_in_list);
bool rm_temporary_table(enum db_type base, char *path);
@@ -960,6 +960,7 @@ void free_io_cache(TABLE *entry);
void intern_close_table(TABLE *entry);
bool close_thread_table(THD *thd, TABLE **table_ptr);
void close_temporary_tables(THD *thd);
void close_tables_for_reopen(THD *thd, TABLE_LIST *tables);
TABLE_LIST *find_table_in_list(TABLE_LIST *table,
                               uint offset_to_list,
                               const char *db_name,
@@ -1238,10 +1239,12 @@ extern pthread_t signal_thread;
extern struct st_VioSSLAcceptorFd * ssl_acceptor_fd;
#endif /* HAVE_OPENSSL */

MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count, uint flags);
MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count,
                              uint flags, bool *need_reopen);
/* mysql_lock_tables() flags bits */
#define MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK      0x0001
#define MYSQL_LOCK_IGNORE_FLUSH                 0x0002
#define MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN        0x0004

void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock);
void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock);
+61 −6
Original line number Diff line number Diff line
@@ -107,7 +107,7 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup)
{
  TABLE_LIST tables;
  TABLE *table;
  bool refresh;
  bool not_used;
  DBUG_ENTER("open_proc_table");

  /*
@@ -122,7 +122,7 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup)
  bzero((char*) &tables, sizeof(tables));
  tables.db= (char*) "mysql";
  tables.table_name= tables.alias= (char*)"proc";
  if (!(table= open_table(thd, &tables, thd->mem_root, &refresh,
  if (!(table= open_table(thd, &tables, thd->mem_root, &not_used,
                          MYSQL_LOCK_IGNORE_FLUSH)))
  {
    thd->restore_backup_open_tables_state(backup);
@@ -138,7 +138,7 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup)
    could lead to a deadlock if we have other tables opened.
  */
  if (!(thd->lock= mysql_lock_tables(thd, &table, 1,
                                     MYSQL_LOCK_IGNORE_FLUSH)))
                                     MYSQL_LOCK_IGNORE_FLUSH, &not_used)))
  {
    close_proc_table(thd, backup);
    DBUG_RETURN(0);
@@ -1265,7 +1265,8 @@ static bool add_used_routine(LEX *lex, Query_arena *arena,


/*
  Add routine to the set of stored routines used by statement.
  Add routine which is explicitly used by statement to the set of stored
  routines used by this statement.

  SYNOPSIS
    sp_add_used_routine()
@@ -1276,7 +1277,8 @@ static bool add_used_routine(LEX *lex, Query_arena *arena,
      rt_type - routine type (one of TYPE_ENUM_PROCEDURE/...)

  NOTES
    Will also add element to end of 'LEX::sroutines_list' list.
    Will also add element to end of 'LEX::sroutines_list' list (and will
    take into account that this is explicitly used routine).

    To be friendly towards prepared statements one should pass
    persistent arena as second argument.
@@ -1287,6 +1289,37 @@ void sp_add_used_routine(LEX *lex, Query_arena *arena,
{
  rt->set_routine_type(rt_type);
  (void)add_used_routine(lex, arena, &rt->m_sroutines_key);
  lex->sroutines_list_own_last= lex->sroutines_list.next;
  lex->sroutines_list_own_elements= lex->sroutines_list.elements;
}


/*
  Remove routines which are only indirectly used by statement from
  the set of routines used by this statement.

  SYNOPSIS
    sp_remove_not_own_routines()
      lex  LEX representing statement
*/

void sp_remove_not_own_routines(LEX *lex)
{
  Sroutine_hash_entry *not_own_rt, *next_rt;
  for (not_own_rt= *(Sroutine_hash_entry **)lex->sroutines_list_own_last;
       not_own_rt; not_own_rt= next_rt)
  {
    /*
      It is safe to obtain not_own_rt->next after calling hash_delete() now
      but we want to be more future-proof.
    */
    next_rt= not_own_rt->next;
    hash_delete(&lex->sroutines, (byte *)not_own_rt);
  }

  *(Sroutine_hash_entry **)lex->sroutines_list_own_last= NULL;
  lex->sroutines_list.next= lex->sroutines_list_own_last;
  lex->sroutines_list.elements= lex->sroutines_list_own_elements;
}


@@ -1343,6 +1376,28 @@ static void sp_update_stmt_used_routines(THD *thd, LEX *lex, HASH *src)
}


/*
  Add contents of list representing set of routines to the set of
  routines used by statement.

  SYNOPSIS
    sp_update_stmt_used_routines()
      thd  Thread context
      lex  LEX representing statement
      src  List representing set from which routines will be added

  NOTE
    It will also add elements to end of 'LEX::sroutines_list' list.
*/

static void sp_update_stmt_used_routines(THD *thd, LEX *lex, SQL_LIST *src)
{
  for (Sroutine_hash_entry *rt= (Sroutine_hash_entry *)src->first;
       rt; rt= rt->next)
    (void)add_used_routine(lex, thd->stmt_arena, &rt->key);
}


/*
  Cache sub-set of routines used by statement, add tables used by these
  routines to statement table list. Do the same for all routines used
@@ -1463,7 +1518,7 @@ sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex, LEX *aux_lex)
{
  Sroutine_hash_entry **last_cached_routine_ptr=
                          (Sroutine_hash_entry **)lex->sroutines_list.next;
  sp_update_stmt_used_routines(thd, lex, &aux_lex->sroutines);
  sp_update_stmt_used_routines(thd, lex, &aux_lex->sroutines_list);
  (void)sp_cache_routines_and_add_tables_aux(thd, lex, 
                                             *last_cached_routine_ptr, FALSE);
}
+1 −0
Original line number Diff line number Diff line
@@ -84,6 +84,7 @@ void sp_get_prelocking_info(THD *thd, bool *need_prelocking,
                            bool *first_no_prelocking);
void sp_add_used_routine(LEX *lex, Query_arena *arena,
                         sp_name *rt, char rt_type);
void sp_remove_not_own_routines(LEX *lex);
void sp_update_sp_used_routines(HASH *dst, HASH *src);
bool sp_cache_routines_and_add_tables(THD *thd, LEX *lex, 
                                      bool first_no_prelock);
Loading