Below is the list of changes that have just been committed into a local
4.0 repository of mydev. When mydev does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html
ChangeSet
1.2111 05/05/31 11:08:14 ingo@stripped +10 -0
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.
sql/sql_table.cc
1.196 05/05/31 11:08:12 ingo@stripped +1 -1
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().
sql/sql_parse.cc
1.393 05/05/31 11:08:12 ingo@stripped +34 -3
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_insert.cc
1.118 05/05/31 11:08:12 ingo@stripped +18 -6
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_handler.cc
1.46 05/05/31 11:08:12 ingo@stripped +1 -1
Bug#10224 - ANALYZE TABLE crashing with simultaneous CREATE ... SELECT statement.
Adjusted mysql_lock_tables() calls to the new argument list.
sql/sql_base.cc
1.188 05/05/31 11:08:12 ingo@stripped +3 -3
Bug#10224 - ANALYZE TABLE crashing with simultaneous CREATE ... SELECT statement.
Adjusted mysql_lock_tables() calls to the new argument list.
sql/sql_acl.cc
1.135 05/05/31 11:08:12 ingo@stripped +2 -2
Bug#10224 - ANALYZE TABLE crashing with simultaneous CREATE ... SELECT statement.
Adjusted mysql_lock_tables() calls to the new argument list.
sql/mysql_priv.h
1.233 05/05/31 11:08:12 ingo@stripped +5 -2
Bug#10224 - ANALYZE TABLE crashing with simultaneous CREATE ... SELECT statement.
Changed the declaration of mysql_lock_tables().
Added definitions for the new options.
sql/lock.cc
1.51 05/05/31 11:08:12 ingo@stripped +21 -48
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().
mysql-test/t/create.test
1.22 05/05/31 11:08:12 ingo@stripped +15 -0
Bug#10224 - ANALYZE TABLE crashing with simultaneous CREATE ... SELECT statement.
Added tests which do not require concurrency.
mysql-test/r/create.result
1.25 05/05/31 11:08:12 ingo@stripped +8 -0
Bug#10224 - ANALYZE TABLE crashing with simultaneous CREATE ... SELECT statement.
Added test results.
# This is a BitKeeper patch. What follows are the unified diffs for the
# set of deltas contained in the patch. The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User: ingo
# Host: chilla.local
# Root: /home/mydev/mysql-4.0-4000
--- 1.50/sql/lock.cc Wed Apr 27 12:40:35 2005
+++ 1.51/sql/lock.cc Tue May 31 11:08:12 2005
@@ -79,8 +79,24 @@
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 @@
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 @@
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;
@@ -865,50 +882,6 @@
pthread_cond_wait(&COND_refresh, &LOCK_open);
pthread_mutex_unlock(&LOCK_open);
thd->global_read_lock= MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT;
-}
-
-
-/*
- 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);
}
--- 1.232/sql/mysql_priv.h Wed Apr 27 12:40:35 2005
+++ 1.233/sql/mysql_priv.h Tue May 31 11:08:12 2005
@@ -766,8 +766,11 @@
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);
--- 1.134/sql/sql_acl.cc Sat Mar 26 19:46:38 2005
+++ 1.135/sql/sql_acl.cc Tue May 31 11:08:12 2005
@@ -177,7 +177,7 @@
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 @@
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;
--- 1.187/sql/sql_base.cc Fri May 6 15:42:06 2005
+++ 1.188/sql/sql_base.cc Tue May 31 11:08:12 2005
@@ -1161,7 +1161,7 @@
MYSQL_LOCK *lock;
/* We should always get these locks */
thd->some_tables_deleted=0;
- if ((lock=mysql_lock_tables(thd,tables,(uint) (tables_ptr-tables))))
+ if ((lock= mysql_lock_tables(thd, tables, (uint) (tables_ptr-tables), 0)))
{
thd->locked_tables=mysql_lock_merge(thd->locked_tables,lock);
}
@@ -1602,7 +1602,7 @@
else
{
if ((table->reginfo.lock_type= lock_type) != TL_UNLOCK)
- if (!(thd->lock=mysql_lock_tables(thd,&table_list->table,1)))
+ if (! (thd->lock= mysql_lock_tables(thd, &table_list->table, 1, 0)))
table= 0;
}
}
@@ -1653,7 +1653,7 @@
return -1;
for (table = tables ; table ; table=table->next)
*(ptr++)= table->table;
- if (!(thd->lock=mysql_lock_tables(thd,start,count)))
+ if (! (thd->lock= mysql_lock_tables(thd, start, count, 0)))
return -1; /* purecov: inspected */
}
else
--- 1.117/sql/sql_insert.cc Wed Apr 27 12:40:35 2005
+++ 1.118/sql/sql_insert.cc Tue May 31 11:08:12 2005
@@ -674,10 +674,13 @@
Avoid that a global read lock steps in while we are creating the
new thread. It would block trying to open the table. Hence, the
DI thread and this thread would wait until after the global
- readlock is gone. If the read lock exists already, we leave with
- no table and then switch to non-delayed insert.
+ readlock is gone. Since the insert thread needs to wait for a
+ global read lock anyway, we do it right now. Note that
+ wait_if_global_read_lock() sets a protection against a new
+ global read lock when it succeeds. This needs to be released by
+ start_waiting_global_read_lock().
*/
- if (set_protect_against_global_read_lock())
+ if (wait_if_global_read_lock(thd, 0, 1))
goto err;
if (!(tmp=new delayed_insert()))
{
@@ -719,7 +722,11 @@
pthread_cond_wait(&tmp->cond_client,&tmp->mutex);
}
pthread_mutex_unlock(&tmp->mutex);
- unset_protect_against_global_read_lock();
+ /*
+ Release the protection against the global read lock and wake
+ everyone, who might want to set a global read lock.
+ */
+ start_waiting_global_read_lock(thd);
thd->proc_info="got old table";
if (tmp->thd.killed)
{
@@ -755,7 +762,11 @@
err1:
thd->fatal_error= 1;
- unset_protect_against_global_read_lock();
+ /*
+ Release the protection against the global read lock and wake
+ everyone, who might want to set a global read lock.
+ */
+ start_waiting_global_read_lock(thd);
err:
pthread_mutex_unlock(&LOCK_delayed_create);
DBUG_RETURN(0); // Continue with normal insert
@@ -1105,7 +1116,8 @@
handler will close the table and finish when the outstanding
inserts are done.
*/
- if (! (thd->lock= mysql_lock_tables(thd, &di->table, 1, TRUE)))
+ if (! (thd->lock= mysql_lock_tables(thd, &di->table, 1,
+ MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK)))
{
di->dead=thd->killed=1; // Fatal error
}
--- 1.392/sql/sql_parse.cc Wed Mar 30 10:43:16 2005
+++ 1.393/sql/sql_parse.cc Tue May 31 11:08:12 2005
@@ -1673,6 +1673,24 @@
break;
}
#endif
+ /*
+ The create-select command will open and read-lock the select table
+ and then create, open and write-lock the new table. If a global
+ read lock steps in, we get a deadlock. The write lock waits for
+ the global read lock, while the global read lock waits for the
+ select table to be closed. So we wait until the global readlock is
+ gone before starting both steps. Note that
+ wait_if_global_read_lock() sets a protection against a new global
+ read lock when it succeeds. This needs to be released by
+ start_waiting_global_read_lock(). We protect the normal CREATE
+ TABLE in the same way. That way we avoid that a new table is
+ created during a gobal read lock.
+ */
+ if (wait_if_global_read_lock(thd, 0, 1))
+ {
+ res= -1;
+ break;
+ }
if (select_lex->item_list.elements) // With select
{
select_result *result;
@@ -1681,7 +1699,7 @@
check_dup(tables->db, tables->real_name, tables->next))
{
net_printf(&thd->net,ER_INSERT_TABLE_USED,tables->real_name);
- DBUG_VOID_RETURN;
+ goto error1;
}
if (lex->create_info.used_fields & HA_CREATE_USED_UNION)
{
@@ -1692,7 +1710,7 @@
(TABLE_LIST*)lex->create_info.merge_list.first))
{
net_printf(&thd->net, ER_INSERT_TABLE_USED, tab->real_name);
- DBUG_VOID_RETURN;
+ goto error1;
}
}
}
@@ -1700,7 +1718,7 @@
{
TABLE_LIST *table;
if (check_table_access(thd, SELECT_ACL, tables->next))
- goto error; // Error message is given
+ goto error1; // Error message is given
/* TODO: Delete the following loop when locks is set by sql_yacc */
for (table = tables->next ; table ; table=table->next)
table->lock_type= lex->lock_option;
@@ -1737,6 +1755,11 @@
if (!res)
send_ok(&thd->net);
}
+ /*
+ Release the protection against the global read lock and wake
+ everyone, who might want to set a global read lock.
+ */
+ start_waiting_global_read_lock(thd);
break;
}
case SQLCOM_CREATE_INDEX:
@@ -2673,6 +2696,14 @@
if (thd->lock == thd->locked_tables)
thd->lock= 0;
}
+ DBUG_VOID_RETURN;
+
+ error1:
+ /*
+ Release the protection against the global read lock and wake
+ everyone, who might want to set a global read lock.
+ */
+ start_waiting_global_read_lock(thd);
DBUG_VOID_RETURN;
}
--- 1.195/sql/sql_table.cc Fri May 13 10:11:48 2005
+++ 1.196/sql/sql_table.cc Tue May 31 11:08:12 2005
@@ -896,7 +896,7 @@
if (!table)
DBUG_RETURN(0);
table->reginfo.lock_type=TL_WRITE;
- if (!((*lock)=mysql_lock_tables(thd,&table,1)))
+ if (! ((*lock)= mysql_lock_tables(thd, &table, 1, MYSQL_LOCK_IGNORE_FLUSH)))
{
VOID(pthread_mutex_lock(&LOCK_open));
hash_delete(&open_cache,(byte*) table);
--- 1.24/mysql-test/r/create.result Wed Apr 28 16:29:48 2004
+++ 1.25/mysql-test/r/create.result Tue May 31 11:08:12 2005
@@ -228,3 +228,11 @@
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;
--- 1.21/mysql-test/t/create.test Wed Apr 28 16:29:49 2004
+++ 1.22/mysql-test/t/create.test Tue May 31 11:08:12 2005
@@ -203,3 +203,18 @@
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;
--- 1.45/sql/sql_handler.cc Wed Oct 27 21:51:24 2004
+++ 1.46/sql/sql_handler.cc Tue May 31 11:08:12 2005
@@ -448,7 +448,7 @@
send_fields(thd,list,1);
HANDLER_TABLES_HACK(thd);
- lock= mysql_lock_tables(thd, &tables->table, 1);
+ lock= mysql_lock_tables(thd, &tables->table, 1, 0);
HANDLER_TABLES_HACK(thd);
byte *key;
| Thread |
|---|
| • bk commit into 4.0 tree (ingo:1.2111) BUG#10224 | ingo | 31 May |