Commit cf2188ca authored by unknown's avatar unknown
Browse files

Bug#10224 - ANALYZE TABLE crashing with simultaneous CREATE ... SELECT statement.

1.) Added a new option to mysql_lock_tables() for ignoring FLUSH TABLES.
Used the new option in create_table_from_items().
It is necessary to prevent the SELECT table from being reopend.
It would get new storage assigned for its fields, while the
SELECT part of the command would still use the old (freed) storage.
2.) Protected the CREATE TABLE and CREATE TABLE ... SELECT commands
against a global read lock. This prevents a deadlock in
CREATE TABLE ... SELECT in conjunction with FLUSH TABLES WITH READ LOCK
and avoids the creation of new tables during a global read lock.
3.) Replaced set_protect_against_global_read_lock() and
unset_protect_against_global_read_lock() by
wait_if_global_read_lock() and start_waiting_global_read_lock()
in the INSERT DELAYED handling.


mysql-test/r/create.result:
  Bug#10224 - ANALYZE TABLE crashing with simultaneous CREATE ... SELECT statement.
  Added test results.
mysql-test/t/create.test:
  Bug#10224 - ANALYZE TABLE crashing with simultaneous CREATE ... SELECT statement.
  Added tests which do not require concurrency.
sql/lock.cc:
  Bug#10224 - ANALYZE TABLE crashing with simultaneous CREATE ... SELECT statement.
  Added a new option to mysql_lock_tables() for ignoring FLUSH TABLES.
  Changed the parameter list.
  Removed two unnecessary functions. Their functionality is included in
  wait_if_global_read_lock() and start_waiting_global_read_lock().
sql/mysql_priv.h:
  Bug#10224 - ANALYZE TABLE crashing with simultaneous CREATE ... SELECT statement.
  Changed the declaration of mysql_lock_tables().
  Added definitions for the new options.
sql/sql_acl.cc:
  Bug#10224 - ANALYZE TABLE crashing with simultaneous CREATE ... SELECT statement.
  Adjusted mysql_lock_tables() calls to the new argument list.
sql/sql_base.cc:
  Bug#10224 - ANALYZE TABLE crashing with simultaneous CREATE ... SELECT statement.
  Adjusted mysql_lock_tables() calls to the new argument list.
sql/sql_handler.cc:
  Bug#10224 - ANALYZE TABLE crashing with simultaneous CREATE ... SELECT statement.
  Adjusted mysql_lock_tables() calls to the new argument list.
sql/sql_insert.cc:
  Bug#10224 - ANALYZE TABLE crashing with simultaneous CREATE ... SELECT statement.
  Replaced set_protect_against_global_read_lock() and
  unset_protect_against_global_read_lock() by
  wait_if_global_read_lock() and start_waiting_global_read_lock()
  in the INSERT DELAYED handling.
  Adjusted mysql_lock_tables() calls to the new argument list.
sql/sql_parse.cc:
  Bug#10224 - ANALYZE TABLE crashing with simultaneous CREATE ... SELECT statement.
  Protected the CREATE TABLE and CREATE TABLE ... SELECT commands
  against a global read lock. This prevents a deadlock in
  CREATE TABLE ... SELECT in conjunction with FLUSH TABLES WITH READ LOCK
  and avoids the creation of new tables during a global read lock.
sql/sql_table.cc:
  Bug#10224 - ANALYZE TABLE crashing with simultaneous CREATE ... SELECT statement.
  Adjusted mysql_lock_tables() calls to the new argument list.
  Used the new option in create_table_from_items().
parent e1e1e3a8
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -228,3 +228,11 @@ create table t1 (a int,,b int);
You have an error in your SQL syntax.  Check the manual that corresponds to your MySQL server version for the right syntax to use near 'b int)' at line 1
create table t1 (,b int);
You have an error in your SQL syntax.  Check the manual that corresponds to your MySQL server version for the right syntax to use near 'b int)' at line 1
create table t1 (a int);
create table t1 select * from t1;
INSERT TABLE 't1' isn't allowed in FROM table list
create table t2 union = (t1) select * from t1;
INSERT TABLE 't1' isn't allowed in FROM table list
flush tables with read lock;
unlock tables;
drop table t1;
+15 −0
Original line number Diff line number Diff line
@@ -203,3 +203,18 @@ create table t1 (a int,);
create table t1 (a int,,b int);
--error 1064
create table t1 (,b int);

#
# Bug#10224 - ANALYZE TABLE crashing with simultaneous
# CREATE ... SELECT statement.
# This tests two additional possible errors and a hang if 
# an improper fix is present.
#
create table t1 (a int);
--error 1093
create table t1 select * from t1;
--error 1093
create table t2 union = (t1) select * from t1;
flush tables with read lock;
unlock tables;
drop table t1;
+21 −48
Original line number Diff line number Diff line
@@ -79,8 +79,24 @@ static int unlock_external(THD *thd, TABLE **table,uint count);
static void print_lock_error(int error);


MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
                              bool ignore_global_read_lock)
/*
  Lock tables.

  SYNOPSIS
    mysql_lock_tables()
    thd                         The current thread.
    tables                      An array of pointers to the tables to lock.
    count                       The number of tables to lock.
    flags                       Options:
      MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK      Ignore a global read lock
      MYSQL_LOCK_IGNORE_FLUSH                 Ignore a flush tables.

  RETURN
    A lock structure pointer on success.
    NULL on error.
*/

MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags)
{
  MYSQL_LOCK *sql_lock;
  TABLE *write_lock_used;
@@ -91,7 +107,8 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
    if (!(sql_lock = get_lock_data(thd,tables,count, 0,&write_lock_used)))
      break;

    if (global_read_lock && write_lock_used && ! ignore_global_read_lock)
    if (global_read_lock && write_lock_used &&
        ! (flags & MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK))
    {
      /*
	Someone has issued LOCK ALL TABLES FOR READ and we want a write lock
@@ -125,7 +142,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
      thd->some_tables_deleted=1;		// Try again
      sql_lock->lock_count=0;			// Locks are alread freed
    }
    else if (!thd->some_tables_deleted)
    else if (!thd->some_tables_deleted || (flags & MYSQL_LOCK_IGNORE_FLUSH))
    {
      thd->locked=0;
      break;
@@ -868,47 +885,3 @@ void make_global_read_lock_block_commit(THD *thd)
}

/*
  Set protection against global read lock.

  SYNOPSIS
    set_protect_against_global_read_lock()
    void

  RETURN
    FALSE       OK, no global read lock exists.
    TRUE        Error, global read lock exists already.
*/

my_bool set_protect_against_global_read_lock(void)
{
  my_bool       global_read_lock_exists;

  pthread_mutex_lock(&LOCK_open);
  if (! (global_read_lock_exists= test(global_read_lock)))
    protect_against_global_read_lock++;
  pthread_mutex_unlock(&LOCK_open);
  return global_read_lock_exists;
}


/*
  Unset protection against global read lock.

  SYNOPSIS
    unset_protect_against_global_read_lock()
    void

  RETURN
    void
*/

void unset_protect_against_global_read_lock(void)
{
  pthread_mutex_lock(&LOCK_open);
  protect_against_global_read_lock--;
  pthread_mutex_unlock(&LOCK_open);
  pthread_cond_broadcast(&COND_refresh);
}

+5 −2
Original line number Diff line number Diff line
@@ -766,8 +766,11 @@ 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,
                              bool ignore_global_read_lock= FALSE);
MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count, uint flags);
/* mysql_lock_tables() flags bits */
#define MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK      0x0001
#define MYSQL_LOCK_IGNORE_FLUSH                 0x0002

void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock);
void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock);
void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count);
+2 −2
Original line number Diff line number Diff line
@@ -177,7 +177,7 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
  ptr[0]= tables[0].table;
  ptr[1]= tables[1].table;
  ptr[2]= tables[2].table;
  if (!(lock=mysql_lock_tables(thd,ptr,3)))
  if (! (lock= mysql_lock_tables(thd, ptr, 3, 0)))
  {
    sql_print_error("Fatal error: Can't lock privilege tables: %s",
		    thd->net.last_error);
@@ -2514,7 +2514,7 @@ my_bool grant_init(THD *org_thd)
  TABLE *ptr[2];				// Lock tables for quick update
  ptr[0]= tables[0].table;
  ptr[1]= tables[1].table;
  if (!(lock=mysql_lock_tables(thd,ptr,2)))
  if (! (lock= mysql_lock_tables(thd, ptr, 2, 0)))
    goto end;

  t_table = tables[0].table; c_table = tables[1].table;
Loading