diff options
author | unknown <ingo@mysql.com> | 2005-06-01 11:15:21 +0200 |
---|---|---|
committer | unknown <ingo@mysql.com> | 2005-06-01 11:15:21 +0200 |
commit | 046ced266a4797fa133961fe51c73a33b94504af (patch) | |
tree | 7a8b8f55be2cc9a973bed05c6c890d7d4cd3ca38 /sql | |
parent | abbdab6ac9be805170ebdae4d0c68dc9c2ab9de4 (diff) | |
parent | 10481cd0368cb2ae07efd0f05c65748c3046eff8 (diff) | |
download | mariadb-git-046ced266a4797fa133961fe51c73a33b94504af.tar.gz |
Bug#10224 - ANALYZE TABLE crashing with simultaneous CREATE ... SELECT statement.
Manual merge from 4.1.
mysql-test/r/create.result:
Auto merged
mysql-test/t/create.test:
Auto merged
sql/mysql_priv.h:
Auto merged
sql/sql_handler.cc:
Auto merged
sql/sql_insert.cc:
Auto merged
sql/sql_yacc.yy:
Auto merged
Diffstat (limited to 'sql')
-rw-r--r-- | sql/lock.cc | 70 | ||||
-rw-r--r-- | sql/mysql_priv.h | 7 | ||||
-rw-r--r-- | sql/sql_base.cc | 6 | ||||
-rw-r--r-- | sql/sql_handler.cc | 2 | ||||
-rw-r--r-- | sql/sql_insert.cc | 24 | ||||
-rw-r--r-- | sql/sql_parse.cc | 23 | ||||
-rw-r--r-- | sql/sql_table.cc | 2 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 26 |
8 files changed, 97 insertions, 63 deletions
diff --git a/sql/lock.cc b/sql/lock.cc index 83a39005cd8..e0a75a42661 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -82,8 +82,24 @@ static int unlock_external(THD *thd, TABLE **table,uint count); static void print_lock_error(int error, const char *); -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; @@ -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))) 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 @@ -128,7 +145,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; @@ -951,48 +968,3 @@ bool 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. -*/ - -bool set_protect_against_global_read_lock(void) -{ - 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); -} - - diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index a061a28d346..7adb175ed1b 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1166,8 +1166,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); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 8fbe5bbfcb7..0c6f7837235 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1384,7 +1384,7 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh) 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); } @@ -2022,7 +2022,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 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; } } @@ -2237,7 +2237,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count) thd->options|= OPTION_TABLE_LOCK; } - if (!(thd->lock=mysql_lock_tables(thd,start, (uint) (ptr - start)))) + if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start), 0))) { if (thd->lex->requires_prelocking()) { diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index bb48b7ada77..1aa034ce61c 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -433,7 +433,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, protocol->send_fields(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); 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); if (!lock) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 5d87a4ca30b..818c18ff500 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1211,10 +1211,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 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())) { @@ -1256,7 +1259,11 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list) 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) { @@ -1292,7 +1299,11 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list) err1: 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: pthread_mutex_unlock(&LOCK_delayed_create); DBUG_RETURN(0); // Continue with normal insert @@ -1650,7 +1661,8 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg) 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))) { /* Fatal error */ di->dead= 1; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 2191eba2f3e..18594568a7a 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2773,6 +2773,24 @@ mysql_execute_command(THD *thd) lex->create_info.default_table_charset= lex->create_info.table_charset; 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 { select_result *result; @@ -2846,6 +2864,11 @@ mysql_execute_command(THD *thd) if (!res) 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); lex->link_first_table_back(create_table, link_to_local); break; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index f44d3191375..acde8868583 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1756,7 +1756,7 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, } 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); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 708dcb7a0d9..27d73e9ad0a 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -7078,7 +7078,31 @@ simple_ident_q: field_ident: 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 */; table_ident: |