Merge lp:~sergei.glushchenko/percona-server/ST-28169-Bug1127008 into lp:percona-server/5.5

Proposed by Sergei Glushchenko
Status: Merged
Approved by: Alexey Kopytov
Approved revision: no longer in the source branch.
Merged at revision: 467
Proposed branch: lp:~sergei.glushchenko/percona-server/ST-28169-Bug1127008
Merge into: lp:percona-server/5.5
Diff against target: 405 lines (+214/-21)
11 files modified
Percona-Server/mysql-test/r/create_delayed.result (+3/-0)
Percona-Server/mysql-test/r/percona_bug1127008.result (+28/-0)
Percona-Server/mysql-test/t/create_delayed.test (+34/-0)
Percona-Server/mysql-test/t/percona_bug1127008.test (+28/-0)
Percona-Server/sql/mdl.cc (+5/-1)
Percona-Server/sql/sql_base.cc (+96/-16)
Percona-Server/sql/sql_base.h (+2/-1)
Percona-Server/sql/sql_db.cc (+1/-1)
Percona-Server/sql/sql_insert.cc (+4/-0)
Percona-Server/sql/sql_parse.cc (+8/-1)
Percona-Server/sql/sql_table.cc (+5/-1)
To merge this branch: bzr merge lp:~sergei.glushchenko/percona-server/ST-28169-Bug1127008
Reviewer Review Type Date Requested Status
Alexey Kopytov (community) Approve
Review via email: mp+149294@code.launchpad.net

Description of the change

Bug 1127008 CREATE TABLE for a table that already exists does
not fail immediately
Upstream bug http://bugs.mysql.com/bug.php?id=63144
CREATE TABLE IF NOT EXISTS metadata lock is too restrictive.
Fix is just a port of https://mariadb.atlassian.net/browse/MDEV-3941
Original commit comment:
- Added option to check_if_table_exists() to quickly check if table exists (either SHARE or .FRM)
- Extended lock_table_names() to not wait for meta data locks if CREATE IF NOT EXISTS is used.
Also ported fix for https://mariadb.atlassian.net/browse/MDEV-4009
Fix for MDEV-4009: main.delayed sporadically fails with "query 'REPLACE DELAYED t1 VALUES (5)' failed: 1317: Query execution was interrupted"
- Fixed broadcast without a proper mutex
- Don't break existing locks if we are just testing if we can get the lock

To post a comment you must log in.
Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :
Revision history for this message
Alexey Kopytov (akopytov) wrote :

Looks good (and thanks for taking care of all the follow-up fixes). One
minor comment: I would follow our practice of creating testcases in
separate files (i.e. percona_bug#.test) instead of modifying existing
tests to reduce merge conflicts.

review: Needs Fixing
Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :

Change from create.test as been moved to separate testcase percona_bug1127008

Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :
Revision history for this message
Alexey Kopytov (akopytov) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'Percona-Server/mysql-test/r/create_delayed.result'
--- Percona-Server/mysql-test/r/create_delayed.result 1970-01-01 00:00:00 +0000
+++ Percona-Server/mysql-test/r/create_delayed.result 2013-03-18 11:52:26 +0000
@@ -0,0 +1,3 @@
1drop table if exists t1;
2Starting test
3# All done
04
=== added file 'Percona-Server/mysql-test/r/percona_bug1127008.result'
--- Percona-Server/mysql-test/r/percona_bug1127008.result 1970-01-01 00:00:00 +0000
+++ Percona-Server/mysql-test/r/percona_bug1127008.result 2013-03-18 11:52:26 +0000
@@ -0,0 +1,28 @@
1#
2# Checking that CREATE IF NOT EXISTS is not blocked by running SELECT
3#
4create table t1 (a int, b int) engine=myisam;
5create table t2 (a int, b int) engine=myisam;
6insert into t1 values (1,1);
7lock tables t1 read;
8set @@lock_wait_timeout=5;
9create table if not exists t1 (a int, b int);
10Warnings:
11Note 1050 Table 't1' already exists
12create table if not exists t1 (a int, b int) select 2,2;
13Warnings:
14Note 1050 Table 't1' already exists
15create table if not exists t1 like t2;
16Warnings:
17Note 1050 Table 't1' already exists
18create table t1 (a int, b int);
19ERROR 42S01: Table 't1' already exists
20create table t1 (a int, b int) select 2,2;
21ERROR 42S01: Table 't1' already exists
22create table t1 like t2;
23ERROR 42S01: Table 't1' already exists
24select * from t1;
25a b
261 1
27unlock tables;
28drop table t1,t2;
029
=== added file 'Percona-Server/mysql-test/t/create_delayed.test'
--- Percona-Server/mysql-test/t/create_delayed.test 1970-01-01 00:00:00 +0000
+++ Percona-Server/mysql-test/t/create_delayed.test 2013-03-18 11:52:26 +0000
@@ -0,0 +1,34 @@
1#
2# Ensure that INSERT DELAYED works with CREATE TABLE on existing table
3#
4
5-- source include/big_test.inc
6
7--disable_warnings
8drop table if exists t1;
9--enable_warnings
10
11--disable_query_log
12--disable_result_log
13
14--let $run=1000
15
16--echo Starting test
17
18while ($run)
19{
20# --echo # $run attempts left...
21 CREATE TABLE t1 ( f1 INTEGER AUTO_INCREMENT, PRIMARY KEY (f1)) ENGINE=MyISAM;
22 INSERT DELAYED t1 VALUES (4);
23--error ER_TABLE_EXISTS_ERROR
24 CREATE TABLE t1 AS SELECT 1 AS f1;
25
26 REPLACE DELAYED t1 VALUES (5);
27 DROP TABLE t1;
28--dec $run
29}
30
31--enable_query_log
32--enable_result_log
33
34--echo # All done
035
=== added file 'Percona-Server/mysql-test/t/percona_bug1127008.test'
--- Percona-Server/mysql-test/t/percona_bug1127008.test 1970-01-01 00:00:00 +0000
+++ Percona-Server/mysql-test/t/percona_bug1127008.test 2013-03-18 11:52:26 +0000
@@ -0,0 +1,28 @@
1######################################################################
2# Bug #1127008: CREATE TABLE for a table that already exists does not
3# fail immediately
4######################################################################
5
6--echo #
7--echo # Checking that CREATE IF NOT EXISTS is not blocked by running SELECT
8--echo #
9create table t1 (a int, b int) engine=myisam;
10create table t2 (a int, b int) engine=myisam;
11insert into t1 values (1,1);
12lock tables t1 read;
13connect (user1,localhost,root,,test);
14set @@lock_wait_timeout=5;
15create table if not exists t1 (a int, b int);
16create table if not exists t1 (a int, b int) select 2,2;
17create table if not exists t1 like t2;
18--error ER_TABLE_EXISTS_ERROR
19create table t1 (a int, b int);
20--error ER_TABLE_EXISTS_ERROR
21create table t1 (a int, b int) select 2,2;
22--error ER_TABLE_EXISTS_ERROR
23create table t1 like t2;
24disconnect user1;
25connection default;
26select * from t1;
27unlock tables;
28drop table t1,t2;
029
=== modified file 'Percona-Server/sql/mdl.cc'
--- Percona-Server/sql/mdl.cc 2012-08-07 06:18:36 +0000
+++ Percona-Server/sql/mdl.cc 2013-03-18 11:52:26 +0000
@@ -2075,7 +2075,11 @@
2075 */2075 */
2076 m_wait.reset_status();2076 m_wait.reset_status();
20772077
2078 if (lock->needs_notification(ticket))2078 /*
2079 Don't break conflicting locks if timeout is 0 as 0 is used
2080 To check if there is any conflicting locks...
2081 */
2082 if (lock->needs_notification(ticket) && lock_wait_timeout)
2079 lock->notify_conflicting_locks(this);2083 lock->notify_conflicting_locks(this);
20802084
2081 mysql_prlock_unlock(&lock->m_rwlock);2085 mysql_prlock_unlock(&lock->m_rwlock);
20822086
=== modified file 'Percona-Server/sql/sql_base.cc'
--- Percona-Server/sql/sql_base.cc 2013-02-18 04:48:10 +0000
+++ Percona-Server/sql/sql_base.cc 2013-03-18 11:52:26 +0000
@@ -2358,10 +2358,11 @@
2358 Check that table exists in table definition cache, on disk2358 Check that table exists in table definition cache, on disk
2359 or in some storage engine.2359 or in some storage engine.
23602360
2361 @param thd Thread context2361 @param thd Thread context
2362 @param table Table list element2362 @param table Table list element
2363 @param[out] exists Out parameter which is set to TRUE if table2363 @param fast_check Check only if share or .frm file exists
2364 exists and to FALSE otherwise.2364 @param[out] exists Out parameter which is set to TRUE if table
2365 exists and to FALSE otherwise.
23652366
2366 @note This function acquires LOCK_open internally.2367 @note This function acquires LOCK_open internally.
23672368
@@ -2373,7 +2374,8 @@
2373 @retval FALSE No error. 'exists' out parameter set accordingly.2374 @retval FALSE No error. 'exists' out parameter set accordingly.
2374*/2375*/
23752376
2376bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool *exists)2377bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool fast_check,
2378 bool *exists)
2377{2379{
2378 char path[FN_REFLEN + 1];2380 char path[FN_REFLEN + 1];
2379 TABLE_SHARE *share;2381 TABLE_SHARE *share;
@@ -2381,7 +2383,8 @@
23812383
2382 *exists= TRUE;2384 *exists= TRUE;
23832385
2384 DBUG_ASSERT(thd->mdl_context.2386 DBUG_ASSERT(fast_check ||
2387 thd->mdl_context.
2385 is_lock_owner(MDL_key::TABLE, table->db,2388 is_lock_owner(MDL_key::TABLE, table->db,
2386 table->table_name, MDL_SHARED));2389 table->table_name, MDL_SHARED));
23872390
@@ -2398,6 +2401,12 @@
2398 if (!access(path, F_OK))2401 if (!access(path, F_OK))
2399 goto end;2402 goto end;
24002403
2404 if (fast_check)
2405 {
2406 *exists= FALSE;
2407 goto end;
2408 }
2409
2401 /* .FRM file doesn't exist. Check if some engine can provide it. */2410 /* .FRM file doesn't exist. Check if some engine can provide it. */
2402 if (ha_check_if_table_exists(thd, table->db, table->table_name, exists))2411 if (ha_check_if_table_exists(thd, table->db, table->table_name, exists))
2403 {2412 {
@@ -2947,7 +2956,7 @@
2947 {2956 {
2948 bool exists;2957 bool exists;
29492958
2950 if (check_if_table_exists(thd, table_list, &exists))2959 if (check_if_table_exists(thd, table_list, 0, &exists))
2951 DBUG_RETURN(TRUE);2960 DBUG_RETURN(TRUE);
29522961
2953 if (!exists)2962 if (!exists)
@@ -4653,7 +4662,18 @@
4653 open, see open_table() description for details.4662 open, see open_table() description for details.
46544663
4655 @retval FALSE Success.4664 @retval FALSE Success.
4656 @retval TRUE Failure (e.g. connection was killed)4665 @retval TRUE Failure (e.g. connection was killed) or table existed
4666 for a CREATE TABLE.
4667
4668 @notes
4669 In case of CREATE TABLE we avoid a wait for tables that are in use
4670 by first trying to do a meta data lock with timeout == 0. If we get a
4671 timeout we will check if table exists (it should) and retry with
4672 normal timeout if it didn't exists.
4673 Note that for CREATE TABLE IF EXISTS we only generate a warning
4674 but still return TRUE (to abort the calling open_table() function).
4675 On must check THD->is_error() if one wants to distinguish between warning
4676 and error.
4657*/4677*/
46584678
4659bool4679bool
@@ -4665,6 +4685,10 @@
4665 TABLE_LIST *table;4685 TABLE_LIST *table;
4666 MDL_request global_request;4686 MDL_request global_request;
4667 Hash_set<TABLE_LIST, schema_set_get_key> schema_set;4687 Hash_set<TABLE_LIST, schema_set_get_key> schema_set;
4688 ulong org_lock_wait_timeout= lock_wait_timeout;
4689 /* Check if we are using CREATE TABLE ... IF NOT EXISTS */
4690 bool create_table;
4691 Dummy_error_handler error_handler;
46684692
4669 DBUG_ASSERT(!thd->locked_tables_mode);4693 DBUG_ASSERT(!thd->locked_tables_mode);
46704694
@@ -4685,8 +4709,14 @@
4685 }4709 }
4686 }4710 }
46874711
4688 if (! (flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK) &&4712 if (mdl_requests.is_empty())
4689 ! mdl_requests.is_empty())4713 return FALSE;
4714
4715 /* Check if CREATE TABLE IF NOT EXISTS was used */
4716 create_table= (tables_start && tables_start->open_strategy ==
4717 TABLE_LIST::OPEN_IF_EXISTS);
4718
4719 if (!(flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK))
4690 {4720 {
4691 /*4721 /*
4692 Scoped locks: Take intention exclusive locks on all involved4722 Scoped locks: Take intention exclusive locks on all involved
@@ -4714,12 +4744,58 @@
4714 global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,4744 global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
4715 MDL_STATEMENT);4745 MDL_STATEMENT);
4716 mdl_requests.push_front(&global_request);4746 mdl_requests.push_front(&global_request);
4717 }4747
47184748 if (create_table)
4719 if (thd->mdl_context.acquire_locks(&mdl_requests, lock_wait_timeout))4749 lock_wait_timeout= 0; // Don't wait for timeout
4720 return TRUE;4750 }
47214751
4722 return FALSE;4752 for (;;)
4753 {
4754 bool exists= TRUE;
4755 bool res;
4756
4757 if (create_table)
4758 thd->push_internal_handler(&error_handler); // Avoid warnings & errors
4759 res= thd->mdl_context.acquire_locks(&mdl_requests, lock_wait_timeout);
4760 if (create_table)
4761 thd->pop_internal_handler();
4762 if (!res)
4763 return FALSE; // Got locks
4764
4765 if (!create_table)
4766 return TRUE; // Return original error
4767
4768 /*
4769 We come here in the case of lock timeout when executing
4770 CREATE TABLE IF NOT EXISTS.
4771 Verify that table really exists (it should as we got a lock conflict)
4772 */
4773 if (check_if_table_exists(thd, tables_start, 1, &exists))
4774 return TRUE; // Should never happen
4775 if (exists)
4776 {
4777 if (thd->lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS)
4778 {
4779 push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
4780 ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
4781 tables_start->table_name);
4782 }
4783 else
4784 my_error(ER_TABLE_EXISTS_ERROR, MYF(0), tables_start->table_name);
4785 return TRUE;
4786 }
4787 /* purecov: begin inspected */
4788 /*
4789 We got error from acquire_locks but table didn't exists.
4790 In theory this should never happen, except maybe in
4791 CREATE or DROP DATABASE scenario.
4792 We play safe and restart the original acquire_locks with the
4793 original timeout
4794 */
4795 create_table= 0;
4796 lock_wait_timeout= org_lock_wait_timeout;
4797 /* purecov: end */
4798 }
4723}4799}
47244800
47254801
@@ -8912,7 +8988,11 @@
8912 in_use->killed= THD::KILL_CONNECTION;8988 in_use->killed= THD::KILL_CONNECTION;
8913 mysql_mutex_lock(&in_use->mysys_var->mutex);8989 mysql_mutex_lock(&in_use->mysys_var->mutex);
8914 if (in_use->mysys_var->current_cond)8990 if (in_use->mysys_var->current_cond)
8991 {
8992 mysql_mutex_lock(in_use->mysys_var->current_mutex);
8915 mysql_cond_broadcast(in_use->mysys_var->current_cond);8993 mysql_cond_broadcast(in_use->mysys_var->current_cond);
8994 mysql_mutex_unlock(in_use->mysys_var->current_mutex);
8995 }
8916 mysql_mutex_unlock(&in_use->mysys_var->mutex);8996 mysql_mutex_unlock(&in_use->mysys_var->mutex);
8917 signalled= TRUE;8997 signalled= TRUE;
8918 }8998 }
89198999
=== modified file 'Percona-Server/sql/sql_base.h'
--- Percona-Server/sql/sql_base.h 2012-12-11 18:04:30 +0000
+++ Percona-Server/sql/sql_base.h 2013-03-18 11:52:26 +0000
@@ -319,7 +319,8 @@
319 const char *table_name,319 const char *table_name,
320 bool no_error);320 bool no_error);
321void mark_tmp_table_for_reuse(TABLE *table);321void mark_tmp_table_for_reuse(TABLE *table);
322bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool *exists);322bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool fast_check,
323 bool *exists);
323324
324extern TABLE *unused_tables;325extern TABLE *unused_tables;
325extern Item **not_found_item;326extern Item **not_found_item;
326327
=== modified file 'Percona-Server/sql/sql_db.cc'
--- Percona-Server/sql/sql_db.cc 2013-01-09 23:45:25 +0000
+++ Percona-Server/sql/sql_db.cc 2013-03-18 11:52:26 +0000
@@ -924,7 +924,7 @@
924 bool exists;924 bool exists;
925925
926 // Only write drop table to the binlog for tables that no longer exist.926 // Only write drop table to the binlog for tables that no longer exist.
927 if (check_if_table_exists(thd, tbl, &exists))927 if (check_if_table_exists(thd, tbl, 0, &exists))
928 {928 {
929 error= true;929 error= true;
930 goto exit;930 goto exit;
931931
=== modified file 'Percona-Server/sql/sql_insert.cc'
--- Percona-Server/sql/sql_insert.cc 2013-01-09 23:45:25 +0000
+++ Percona-Server/sql/sql_insert.cc 2013-03-18 11:52:26 +0000
@@ -2748,8 +2748,12 @@
2748 set_timespec(abstime, delayed_insert_timeout);2748 set_timespec(abstime, delayed_insert_timeout);
27492749
2750 /* Information for pthread_kill */2750 /* Information for pthread_kill */
2751 mysql_mutex_unlock(&di->mutex);
2752 mysql_mutex_lock(&di->thd.mysys_var->mutex);
2751 di->thd.mysys_var->current_mutex= &di->mutex;2753 di->thd.mysys_var->current_mutex= &di->mutex;
2752 di->thd.mysys_var->current_cond= &di->cond;2754 di->thd.mysys_var->current_cond= &di->cond;
2755 mysql_mutex_unlock(&di->thd.mysys_var->mutex);
2756 mysql_mutex_lock(&di->mutex);
2753 thd_proc_info(&(di->thd), "Waiting for INSERT");2757 thd_proc_info(&(di->thd), "Waiting for INSERT");
27542758
2755 DBUG_PRINT("info",("Waiting for someone to insert rows"));2759 DBUG_PRINT("info",("Waiting for someone to insert rows"));
27562760
=== modified file 'Percona-Server/sql/sql_parse.cc'
--- Percona-Server/sql/sql_parse.cc 2013-03-05 12:16:18 +0000
+++ Percona-Server/sql/sql_parse.cc 2013-03-18 11:52:26 +0000
@@ -2677,7 +2677,14 @@
2677 goto end_with_restore_list;2677 goto end_with_restore_list;
2678 }2678 }
26792679
2680 if (!(res= open_and_lock_tables(thd, lex->query_tables, TRUE, 0)))2680 res= open_and_lock_tables(thd, lex->query_tables, TRUE, 0);
2681 if (res)
2682 {
2683 /* Got error or warning. Set res to 1 if error */
2684 if (!(res= thd->is_error()))
2685 my_ok(thd); // CREATE ... IF NOT EXISTS
2686 }
2687 else
2681 {2688 {
2682 /* The table already exists */2689 /* The table already exists */
2683 if (create_table->table)2690 if (create_table->table)
26842691
=== modified file 'Percona-Server/sql/sql_table.cc'
--- Percona-Server/sql/sql_table.cc 2013-02-18 04:48:10 +0000
+++ Percona-Server/sql/sql_table.cc 2013-03-18 11:52:26 +0000
@@ -4428,7 +4428,8 @@
4428 */4428 */
4429 if (open_and_lock_tables(thd, thd->lex->query_tables, FALSE, 0))4429 if (open_and_lock_tables(thd, thd->lex->query_tables, FALSE, 0))
4430 {4430 {
4431 result= TRUE;4431 /* is_error() may be 0 if table existed and we generated a warning */
4432 result= thd->is_error();
4432 goto end;4433 goto end;
4433 }4434 }
44344435
@@ -4623,7 +4624,10 @@
4623 properly isolated from all concurrent operations which matter.4624 properly isolated from all concurrent operations which matter.
4624 */4625 */
4625 if (open_tables(thd, &thd->lex->query_tables, &not_used, 0))4626 if (open_tables(thd, &thd->lex->query_tables, &not_used, 0))
4627 {
4628 res= thd->is_error();
4626 goto err;4629 goto err;
4630 }
4627 src_table->table->use_all_columns();4631 src_table->table->use_all_columns();
46284632
4629 DEBUG_SYNC(thd, "create_table_like_after_open");4633 DEBUG_SYNC(thd, "create_table_like_after_open");

Subscribers

People subscribed via source and target branches