Commit 92cffda5 authored by lenz@mysql.com's avatar lenz@mysql.com

Merge lgrimmer@bk-internal.mysql.com:/home/bk/mysql-4.1

into mysql.com:/space/my/mysql-4.1
parents cc20ad01 effd338f
...@@ -563,3 +563,21 @@ select * from t2; ...@@ -563,3 +563,21 @@ select * from t2;
b b
1 1
drop table t1,t2; drop table t1,t2;
use test;
create table t1 (a int);
create table t1 select * from t1;
ERROR HY000: You can't specify target table 't1' for update in FROM clause
create table t2 union = (t1) select * from t1;
ERROR HY000: You can't specify target table 't1' for update in FROM clause
flush tables with read lock;
unlock tables;
drop table t1;
create table t1(column.name int);
ERROR 42000: Incorrect table name 'column'
create table t1(test.column.name int);
ERROR 42000: Incorrect table name 'column'
create table t1(xyz.t1.name int);
ERROR 42000: Incorrect database name 'xyz'
create table t1(t1.name int);
create table t2(test.t2.name int);
drop table t1,t2;
...@@ -460,3 +460,33 @@ insert into t2 values (); ...@@ -460,3 +460,33 @@ insert into t2 values ();
select * from t1; select * from t1;
select * from t2; select * from t2;
drop table t1,t2; drop table t1,t2;
#
# 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.
#
connection default;
use test;
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;
#
# Bug#10413: Invalid column name is not rejected
#
--error 1103
create table t1(column.name int);
--error 1103
create table t1(test.column.name int);
--error 1102
create table t1(xyz.t1.name int);
create table t1(t1.name int);
create table t2(test.t2.name int);
drop table t1,t2;
...@@ -82,8 +82,24 @@ static int unlock_external(THD *thd, TABLE **table,uint count); ...@@ -82,8 +82,24 @@ static int unlock_external(THD *thd, TABLE **table,uint count);
static void print_lock_error(int error); 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; MYSQL_LOCK *sql_lock;
TABLE *write_lock_used; TABLE *write_lock_used;
...@@ -94,7 +110,8 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, ...@@ -94,7 +110,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))) if (!(sql_lock = get_lock_data(thd,tables,count, 0,&write_lock_used)))
break; 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 Someone has issued LOCK ALL TABLES FOR READ and we want a write lock
...@@ -128,7 +145,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, ...@@ -128,7 +145,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
thd->some_tables_deleted=1; // Try again thd->some_tables_deleted=1; // Try again
sql_lock->lock_count=0; // Locks are alread freed 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; thd->locked=0;
break; break;
...@@ -914,47 +931,3 @@ void make_global_read_lock_block_commit(THD *thd) ...@@ -914,47 +931,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);
}
...@@ -978,8 +978,11 @@ extern pthread_t signal_thread; ...@@ -978,8 +978,11 @@ extern pthread_t signal_thread;
extern struct st_VioSSLAcceptorFd * ssl_acceptor_fd; extern struct st_VioSSLAcceptorFd * ssl_acceptor_fd;
#endif /* HAVE_OPENSSL */ #endif /* HAVE_OPENSSL */
MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count, MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count, uint flags);
bool ignore_global_read_lock= FALSE); /* 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_tables(THD *thd, MYSQL_LOCK *sql_lock);
void mysql_unlock_read_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); void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count);
......
...@@ -184,7 +184,7 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) ...@@ -184,7 +184,7 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
ptr[0]= tables[0].table; ptr[0]= tables[0].table;
ptr[1]= tables[1].table; ptr[1]= tables[1].table;
ptr[2]= tables[2].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", sql_print_error("Fatal error: Can't lock privilege tables: %s",
thd->net.last_error); thd->net.last_error);
...@@ -2658,7 +2658,7 @@ my_bool grant_init(THD *org_thd) ...@@ -2658,7 +2658,7 @@ my_bool grant_init(THD *org_thd)
TABLE *ptr[2]; // Lock tables for quick update TABLE *ptr[2]; // Lock tables for quick update
ptr[0]= tables[0].table; ptr[0]= tables[0].table;
ptr[1]= tables[1].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; goto end;
t_table = tables[0].table; c_table = tables[1].table; t_table = tables[0].table; c_table = tables[1].table;
......
...@@ -1163,7 +1163,7 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh) ...@@ -1163,7 +1163,7 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh)
MYSQL_LOCK *lock; MYSQL_LOCK *lock;
/* We should always get these locks */ /* We should always get these locks */
thd->some_tables_deleted=0; 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); thd->locked_tables=mysql_lock_merge(thd->locked_tables,lock);
} }
...@@ -1644,7 +1644,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type) ...@@ -1644,7 +1644,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type)
{ {
DBUG_ASSERT(thd->lock == 0); // You must lock everything at once DBUG_ASSERT(thd->lock == 0); // You must lock everything at once
if ((table->reginfo.lock_type= lock_type) != TL_UNLOCK) 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; table= 0;
} }
} }
...@@ -1794,7 +1794,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count) ...@@ -1794,7 +1794,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count)
if (!table->derived) if (!table->derived)
*(ptr++)= table->table; *(ptr++)= table->table;
} }
if (!(thd->lock=mysql_lock_tables(thd,start, (uint) (ptr - start)))) if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start), 0)))
return -1; /* purecov: inspected */ return -1; /* purecov: inspected */
} }
else else
......
...@@ -454,7 +454,7 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables, ...@@ -454,7 +454,7 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables,
protocol->send_fields(&list,1); protocol->send_fields(&list,1);
HANDLER_TABLES_HACK(thd); 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); HANDLER_TABLES_HACK(thd);
if (!lock) if (!lock)
......
...@@ -882,10 +882,13 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list) ...@@ -882,10 +882,13 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list)
Avoid that a global read lock steps in while we are creating the 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 new thread. It would block trying to open the table. Hence, the
DI thread and this thread would wait until after the global DI thread and this thread would wait until after the global
readlock is gone. If the read lock exists already, we leave with readlock is gone. Since the insert thread needs to wait for a
no table and then switch to non-delayed insert. 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; goto err;
if (!(tmp=new delayed_insert())) if (!(tmp=new delayed_insert()))
{ {
...@@ -927,7 +930,11 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list) ...@@ -927,7 +930,11 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list)
pthread_cond_wait(&tmp->cond_client,&tmp->mutex); pthread_cond_wait(&tmp->cond_client,&tmp->mutex);
} }
pthread_mutex_unlock(&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"; thd->proc_info="got old table";
if (tmp->thd.killed) if (tmp->thd.killed)
{ {
...@@ -963,7 +970,11 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list) ...@@ -963,7 +970,11 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list)
err1: err1:
thd->fatal_error(); thd->fatal_error();
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: err:
pthread_mutex_unlock(&LOCK_delayed_create); pthread_mutex_unlock(&LOCK_delayed_create);
DBUG_RETURN(0); // Continue with normal insert DBUG_RETURN(0); // Continue with normal insert
...@@ -1319,7 +1330,8 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg) ...@@ -1319,7 +1330,8 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg)
handler will close the table and finish when the outstanding handler will close the table and finish when the outstanding
inserts are done. 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= 1; // Some fatal error di->dead= 1; // Some fatal error
thd->killed= 1; thd->killed= 1;
......
...@@ -2443,6 +2443,24 @@ mysql_execute_command(THD *thd) ...@@ -2443,6 +2443,24 @@ mysql_execute_command(THD *thd)
lex->create_info.default_table_charset= lex->create_info.table_charset; lex->create_info.default_table_charset= lex->create_info.table_charset;
lex->create_info.table_charset= 0; lex->create_info.table_charset= 0;
} }
/*
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;
goto unsent_create_error;
}
if (select_lex->item_list.elements) // With select if (select_lex->item_list.elements) // With select
{ {
select_result *result; select_result *result;
...@@ -2493,13 +2511,17 @@ mysql_execute_command(THD *thd) ...@@ -2493,13 +2511,17 @@ mysql_execute_command(THD *thd)
if (!res) if (!res)
send_ok(thd); send_ok(thd);
} }
/*
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);
// put tables back for PS rexecuting // put tables back for PS rexecuting
tables= lex->link_first_table_back(tables, create_table, tables= lex->link_first_table_back(tables, create_table,
create_table_local); create_table_local);
break; break;
res= 1; //error reported
unsent_create_error: unsent_create_error:
// put tables back for PS rexecuting // put tables back for PS rexecuting
tables= lex->link_first_table_back(tables, create_table, tables= lex->link_first_table_back(tables, create_table,
...@@ -3672,6 +3694,14 @@ error: ...@@ -3672,6 +3694,14 @@ error:
thd->lock= 0; thd->lock= 0;
} }
DBUG_VOID_RETURN; 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;
} }
......
...@@ -1536,7 +1536,7 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, ...@@ -1536,7 +1536,7 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
if (!table) if (!table)
DBUG_RETURN(0); DBUG_RETURN(0);
table->reginfo.lock_type=TL_WRITE; 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)); VOID(pthread_mutex_lock(&LOCK_open));
hash_delete(&open_cache,(byte*) table); hash_delete(&open_cache,(byte*) table);
......
...@@ -5016,7 +5016,31 @@ simple_ident: ...@@ -5016,7 +5016,31 @@ simple_ident:
field_ident: field_ident:
ident { $$=$1;} ident { $$=$1;}
| ident '.' ident { $$=$3;} /* Skip schema name in create*/ | ident '.' ident '.' ident
{
TABLE_LIST *table= (TABLE_LIST*) Select->table_list.first;
if (my_strcasecmp(table_alias_charset, $1.str, table->db))
{
net_printf(YYTHD, ER_WRONG_DB_NAME, $1.str);
YYABORT;
}
if (my_strcasecmp(table_alias_charset, $3.str, table->real_name))
{
net_printf(YYTHD, ER_WRONG_TABLE_NAME, $3.str);
YYABORT;
}
$$=$5;
}
| ident '.' ident
{
TABLE_LIST *table= (TABLE_LIST*) Select->table_list.first;
if (my_strcasecmp(table_alias_charset, $1.str, table->alias))
{
net_printf(YYTHD, ER_WRONG_TABLE_NAME, $1.str);
YYABORT;
}
$$=$3;
}
| '.' ident { $$=$2;} /* For Delphi */; | '.' ident { $$=$2;} /* For Delphi */;
table_ident: table_ident:
......
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