1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-08 11:22:35 +03:00

Backport of revno: 2617.71.1

Bug#42546 Backup: RESTORE fails, thinking it finds an existing table

The problem occured when a MDL locking conflict happened for a non-existent 
table between a CREATE and a INSERT statement. The code for CREATE 
interpreted this lock conflict to mean that the table existed, 
which meant that the statement failed when it should not have.
The problem could occur for CREATE TABLE, CREATE TABLE LIKE and
ALTER TABLE RENAME.

This patch fixes the problem for CREATE TABLE and CREATE TABLE LIKE.
It is based on code backported from the mysql-6.1-fk tree written
by Dmitry Lenev. CREATE now uses normal open_and_lock_tables() code 
to acquire exclusive locks. This means that for the test case in the bug 
description, CREATE will wait until INSERT completes so that it can 
get the exclusive lock. This resolves the reported bug.

The patch also prohibits CREATE TABLE and CREATE TABLE LIKE under 
LOCK TABLES. Note that this is an incompatible change and must 
be reflected in the documentation. Affected test cases have been
updated.

mdl_sync.test contains tests for CREATE TABLE and CREATE TABLE LIKE.

Fixing the issue for ALTER TABLE RENAME is beyond the scope of this
patch. ALTER TABLE cannot be prohibited from working under LOCK TABLES
as this could seriously impact customers and a proper fix would require
a significant rewrite.
This commit is contained in:
Jon Olav Hauglid
2009-12-10 11:53:20 +01:00
parent 6c25664eb3
commit b20a409c38
21 changed files with 465 additions and 415 deletions

View File

@@ -72,9 +72,10 @@ CREATE TABLE t1 (c1 int);
LOCK TABLE t1 WRITE; LOCK TABLE t1 WRITE;
FLUSH TABLES WITH READ LOCK; FLUSH TABLES WITH READ LOCK;
CREATE TABLE t2 (c1 int); CREATE TABLE t2 (c1 int);
ERROR HY000: Table 't2' was not locked with LOCK TABLES
UNLOCK TABLES; UNLOCK TABLES;
UNLOCK TABLES; UNLOCK TABLES;
DROP TABLE t1, t2; DROP TABLE t1;
CREATE TABLE t1 (c1 int); CREATE TABLE t1 (c1 int);
LOCK TABLE t1 WRITE; LOCK TABLE t1 WRITE;
FLUSH TABLES WITH READ LOCK; FLUSH TABLES WITH READ LOCK;

View File

@@ -63,6 +63,66 @@ unlock tables;
# Clean-up. # Clean-up.
drop tables t1, t2, t3, t5; drop tables t1, t2, t3, t5;
# #
# Bug#42546 - Backup: RESTORE fails, thinking it finds an existing table
#
DROP TABLE IF EXISTS t1;
set @save_log_output=@@global.log_output;
set global log_output=file;
#
# Test 1: CREATE TABLE
#
# Connection 2
# Start insert on the not-yet existing table
# Wait after taking the MDL lock
SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish';
INSERT INTO t1 VALUES(1,"def");
# Connection 1
SET DEBUG_SYNC= 'now WAIT_FOR locked';
# Now INSERT has a MDL on the non-existent table t1.
#
# Continue the INSERT once CREATE waits for exclusive lock
SET DEBUG_SYNC= 'mdl_acquire_exclusive_locks_wait SIGNAL finish';
# Try to create that table.
CREATE TABLE t1 (c1 INT, c2 VARCHAR(100), KEY(c1));
# Connection 2
# Insert fails
ERROR 42S02: Table 'test.t1' doesn't exist
# Connection 1
SET DEBUG_SYNC= 'RESET';
SHOW TABLES;
Tables_in_test
t1
DROP TABLE IF EXISTS t1;
#
# Test 2: CREATE TABLE LIKE
#
CREATE TABLE t2 (c1 INT, c2 VARCHAR(100), KEY(c1));
# Connection 2
# Start insert on the not-yet existing table
# Wait after taking the MDL
SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish';
INSERT INTO t1 VALUES(1,"def");
# Connection 1
SET DEBUG_SYNC= 'now WAIT_FOR locked';
# Now INSERT has a MDL on the non-existent table t1.
#
# Continue the INSERT once CREATE waits for exclusive lock
SET DEBUG_SYNC= 'mdl_acquire_exclusive_locks_wait SIGNAL finish';
# Try to create that table.
CREATE TABLE t1 LIKE t2;
# Connection 2
# Insert fails
ERROR 42S02: Table 'test.t1' doesn't exist
# Connection 1
SET DEBUG_SYNC= 'RESET';
SHOW TABLES;
Tables_in_test
t1
t2
DROP TABLE t2;
DROP TABLE IF EXISTS t1;
set global log_output=@save_log_output;
#
# Bug #46044 "MDL deadlock on LOCK TABLE + CREATE TABLE HIGH_PRIORITY # Bug #46044 "MDL deadlock on LOCK TABLE + CREATE TABLE HIGH_PRIORITY
# FOR UPDATE" # FOR UPDATE"
# #

View File

@@ -1149,7 +1149,8 @@ SHOW CREATE TABLE t3;
ERROR 42S02: Table 'test.t3' doesn't exist ERROR 42S02: Table 'test.t3' doesn't exist
DROP TABLE t1, t2; DROP TABLE t1, t2;
# #
# CREATE ... LIKE # Bug#37371 "CREATE TABLE LIKE merge loses UNION parameter"
# Demonstrate that this is no longer the case.
# #
# 1. Create like. # 1. Create like.
CREATE TABLE t1 (c1 INT); CREATE TABLE t1 (c1 INT);
@@ -1164,26 +1165,26 @@ SHOW CREATE TABLE t4;
Table Create Table Table Create Table
t4 CREATE TABLE `t4` ( t4 CREATE TABLE `t4` (
`c1` int(11) DEFAULT NULL `c1` int(11) DEFAULT NULL
) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 ) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`)
INSERT INTO t4 VALUES (4); INSERT INTO t4 VALUES (4);
ERROR HY000: Table 't4' is read only
DROP TABLE t4; DROP TABLE t4;
# #
# 1. Create like with locked tables. # 1. Create like with locked tables.
LOCK TABLES t3 WRITE, t2 WRITE, t1 WRITE; LOCK TABLES t3 WRITE, t2 WRITE, t1 WRITE;
CREATE TABLE t4 LIKE t3; CREATE TABLE t4 LIKE t3;
ERROR HY000: Table 't4' was not locked with LOCK TABLES
SHOW CREATE TABLE t4; SHOW CREATE TABLE t4;
ERROR HY000: Table 't4' was not locked with LOCK TABLES ERROR HY000: Table 't4' was not locked with LOCK TABLES
INSERT INTO t4 VALUES (4); INSERT INTO t4 VALUES (4);
ERROR HY000: Table 't4' was not locked with LOCK TABLES ERROR HY000: Table 't4' was not locked with LOCK TABLES
CREATE TEMPORARY TABLE t4 LIKE t3;
SHOW CREATE TABLE t4;
ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist
INSERT INTO t4 VALUES (4);
ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist
UNLOCK TABLES; UNLOCK TABLES;
SHOW CREATE TABLE t4;
Table Create Table
t4 CREATE TABLE `t4` (
`c1` int(11) DEFAULT NULL
) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1
INSERT INTO t4 VALUES (4); INSERT INTO t4 VALUES (4);
ERROR HY000: Table 't4' is read only ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist
DROP TABLE t4; DROP TABLE t4;
# #
# Rename child. # Rename child.
@@ -1210,6 +1211,7 @@ c1
1 1
2 2
3 3
4
RENAME TABLE t2 TO t5; RENAME TABLE t2 TO t5;
SELECT * FROM t3 ORDER BY c1; SELECT * FROM t3 ORDER BY c1;
ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist
@@ -1219,6 +1221,7 @@ c1
1 1
2 2
3 3
4
# #
# 3. Normal rename with locked tables. # 3. Normal rename with locked tables.
LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE; LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE;
@@ -1227,6 +1230,7 @@ c1
1 1
2 2
3 3
4
RENAME TABLE t2 TO t5; RENAME TABLE t2 TO t5;
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
SELECT * FROM t3 ORDER BY c1; SELECT * FROM t3 ORDER BY c1;
@@ -1234,6 +1238,7 @@ c1
1 1
2 2
3 3
4
RENAME TABLE t5 TO t2; RENAME TABLE t5 TO t2;
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
SELECT * FROM t3 ORDER BY c1; SELECT * FROM t3 ORDER BY c1;
@@ -1241,6 +1246,7 @@ c1
1 1
2 2
3 3
4
UNLOCK TABLES; UNLOCK TABLES;
# #
# 4. Alter table rename. # 4. Alter table rename.
@@ -1253,6 +1259,7 @@ c1
1 1
2 2
3 3
4
# #
# 5. Alter table rename with locked tables. # 5. Alter table rename with locked tables.
LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE; LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE;
@@ -1268,6 +1275,7 @@ c1
1 1
2 2
3 3
4
# #
# Rename parent. # Rename parent.
# #
@@ -1278,6 +1286,7 @@ c1
1 1
2 2
3 3
4
RENAME TABLE t3 TO t5; RENAME TABLE t3 TO t5;
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
SELECT * FROM t3 ORDER BY c1; SELECT * FROM t3 ORDER BY c1;
@@ -1285,6 +1294,7 @@ c1
1 1
2 2
3 3
4
RENAME TABLE t5 TO t3; RENAME TABLE t5 TO t3;
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
SELECT * FROM t3 ORDER BY c1; SELECT * FROM t3 ORDER BY c1;
@@ -1292,6 +1302,7 @@ c1
1 1
2 2
3 3
4
# #
# 5. Alter table rename with locked tables. # 5. Alter table rename with locked tables.
ALTER TABLE t3 RENAME TO t5; ALTER TABLE t3 RENAME TO t5;
@@ -1306,6 +1317,7 @@ c1
1 1
2 2
3 3
4
DROP TABLE t1, t2, t3; DROP TABLE t1, t2, t3;
# #
# Drop locked tables. # Drop locked tables.

View File

@@ -1755,21 +1755,21 @@ SUCCESS
drop table t1; drop table t1;
deallocate prepare stmt; deallocate prepare stmt;
# XXX: no validation of the first table in case of
# CREATE TEMPORARY TABLE. This is a shortcoming of the current code,
# but since validation is not strictly necessary, nothing is done
# about it.
# Will be fixed as part of work on Bug#21431 "Incomplete support of
# temporary tables"
create table t1 (a int); create table t1 (a int);
insert into t1 (a) values (1); insert into t1 (a) values (1);
prepare stmt from "create temporary table if not exists t2 as select * from t1"; prepare stmt from "create temporary table if not exists t2 as select * from t1";
execute stmt; execute stmt;
drop table t2; drop table t2;
execute stmt; execute stmt;
call p_verify_reprepare_count(0);
SUCCESS
execute stmt; execute stmt;
Warnings: Warnings:
Note 1050 Table 't2' already exists Note 1050 Table 't2' already exists
call p_verify_reprepare_count(1);
SUCCESS
select * from t2; select * from t2;
a a
1 1
@@ -1777,6 +1777,9 @@ a
execute stmt; execute stmt;
Warnings: Warnings:
Note 1050 Table 't2' already exists Note 1050 Table 't2' already exists
call p_verify_reprepare_count(0);
SUCCESS
select * from t2; select * from t2;
a a
1 1
@@ -1790,7 +1793,7 @@ Note 1050 Table 't2' already exists
select * from t2; select * from t2;
a a
1 1
call p_verify_reprepare_count(0); call p_verify_reprepare_count(1);
SUCCESS SUCCESS
drop table t1; drop table t1;

View File

@@ -199,6 +199,7 @@ let $wait_condition=
where state = "Waiting for table" and info = "FLUSH TABLES WITH READ LOCK"; where state = "Waiting for table" and info = "FLUSH TABLES WITH READ LOCK";
--source include/wait_condition.inc --source include/wait_condition.inc
# This must not block. # This must not block.
--error ER_TABLE_NOT_LOCKED
CREATE TABLE t2 (c1 int); CREATE TABLE t2 (c1 int);
UNLOCK TABLES; UNLOCK TABLES;
# #
@@ -208,7 +209,7 @@ reap;
UNLOCK TABLES; UNLOCK TABLES;
# #
connection default; connection default;
DROP TABLE t1, t2; DROP TABLE t1;
# #
# Test if CREATE TABLE SELECT with LOCK TABLE deadlocks. # Test if CREATE TABLE SELECT with LOCK TABLE deadlocks.
# #

View File

@@ -141,6 +141,101 @@ disconnect con2root;
drop tables t1, t2, t3, t5; drop tables t1, t2, t3, t5;
--echo #
--echo # Bug#42546 - Backup: RESTORE fails, thinking it finds an existing table
--echo #
--disable_warnings
DROP TABLE IF EXISTS t1;
--enable_warnings
set @save_log_output=@@global.log_output;
set global log_output=file;
connect(con2, localhost, root,,);
--echo #
--echo # Test 1: CREATE TABLE
--echo #
--echo # Connection 2
connection con2;
--echo # Start insert on the not-yet existing table
--echo # Wait after taking the MDL lock
SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish';
--send INSERT INTO t1 VALUES(1,"def")
--echo # Connection 1
connection default;
SET DEBUG_SYNC= 'now WAIT_FOR locked';
--echo # Now INSERT has a MDL on the non-existent table t1.
--echo #
--echo # Continue the INSERT once CREATE waits for exclusive lock
SET DEBUG_SYNC= 'mdl_acquire_exclusive_locks_wait SIGNAL finish';
--echo # Try to create that table.
--send CREATE TABLE t1 (c1 INT, c2 VARCHAR(100), KEY(c1))
--echo # Connection 2
--echo # Insert fails
connection con2;
--error ER_NO_SUCH_TABLE
--reap
--echo # Connection 1
connection default;
--reap;
SET DEBUG_SYNC= 'RESET';
SHOW TABLES;
--disable_warnings
DROP TABLE IF EXISTS t1;
--enable_warnings
--echo #
--echo # Test 2: CREATE TABLE LIKE
--echo #
CREATE TABLE t2 (c1 INT, c2 VARCHAR(100), KEY(c1));
--echo # Connection 2
connection con2;
--echo # Start insert on the not-yet existing table
--echo # Wait after taking the MDL
SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish';
--send INSERT INTO t1 VALUES(1,"def")
--echo # Connection 1
connection default;
SET DEBUG_SYNC= 'now WAIT_FOR locked';
--echo # Now INSERT has a MDL on the non-existent table t1.
--echo #
--echo # Continue the INSERT once CREATE waits for exclusive lock
SET DEBUG_SYNC= 'mdl_acquire_exclusive_locks_wait SIGNAL finish';
--echo # Try to create that table.
--send CREATE TABLE t1 LIKE t2
--echo # Connection 2
--echo # Insert fails
connection con2;
--error ER_NO_SUCH_TABLE
--reap
--echo # Connection 1
connection default;
--reap
SET DEBUG_SYNC= 'RESET';
SHOW TABLES;
DROP TABLE t2;
disconnect con2;
--disable_warnings
DROP TABLE IF EXISTS t1;
--enable_warnings
set global log_output=@save_log_output;
--echo # --echo #
--echo # Bug #46044 "MDL deadlock on LOCK TABLE + CREATE TABLE HIGH_PRIORITY --echo # Bug #46044 "MDL deadlock on LOCK TABLE + CREATE TABLE HIGH_PRIORITY
--echo # FOR UPDATE" --echo # FOR UPDATE"

View File

@@ -846,7 +846,8 @@ SHOW CREATE TABLE t3;
DROP TABLE t1, t2; DROP TABLE t1, t2;
# #
--echo # --echo #
--echo # CREATE ... LIKE --echo # Bug#37371 "CREATE TABLE LIKE merge loses UNION parameter"
--echo # Demonstrate that this is no longer the case.
--echo # --echo #
--echo # 1. Create like. --echo # 1. Create like.
CREATE TABLE t1 (c1 INT); CREATE TABLE t1 (c1 INT);
@@ -858,20 +859,24 @@ INSERT INTO t2 VALUES (2);
INSERT INTO t3 VALUES (3); INSERT INTO t3 VALUES (3);
CREATE TABLE t4 LIKE t3; CREATE TABLE t4 LIKE t3;
SHOW CREATE TABLE t4; SHOW CREATE TABLE t4;
--error ER_OPEN_AS_READONLY
INSERT INTO t4 VALUES (4); INSERT INTO t4 VALUES (4);
DROP TABLE t4; DROP TABLE t4;
--echo # --echo #
--echo # 1. Create like with locked tables. --echo # 1. Create like with locked tables.
LOCK TABLES t3 WRITE, t2 WRITE, t1 WRITE; LOCK TABLES t3 WRITE, t2 WRITE, t1 WRITE;
--error ER_TABLE_NOT_LOCKED
CREATE TABLE t4 LIKE t3; CREATE TABLE t4 LIKE t3;
--error ER_TABLE_NOT_LOCKED --error ER_TABLE_NOT_LOCKED
SHOW CREATE TABLE t4; SHOW CREATE TABLE t4;
--error ER_TABLE_NOT_LOCKED --error ER_TABLE_NOT_LOCKED
INSERT INTO t4 VALUES (4); INSERT INTO t4 VALUES (4);
UNLOCK TABLES; CREATE TEMPORARY TABLE t4 LIKE t3;
--error ER_WRONG_MRG_TABLE
SHOW CREATE TABLE t4; SHOW CREATE TABLE t4;
--error ER_OPEN_AS_READONLY --error ER_WRONG_MRG_TABLE
INSERT INTO t4 VALUES (4);
UNLOCK TABLES;
--error ER_WRONG_MRG_TABLE
INSERT INTO t4 VALUES (4); INSERT INTO t4 VALUES (4);
DROP TABLE t4; DROP TABLE t4;
# #

View File

@@ -1493,27 +1493,24 @@ execute stmt;
call p_verify_reprepare_count(0); call p_verify_reprepare_count(0);
drop table t1; drop table t1;
deallocate prepare stmt; deallocate prepare stmt;
--echo # XXX: no validation of the first table in case of
--echo # CREATE TEMPORARY TABLE. This is a shortcoming of the current code,
--echo # but since validation is not strictly necessary, nothing is done
--echo # about it.
--echo # Will be fixed as part of work on Bug#21431 "Incomplete support of
--echo # temporary tables"
create table t1 (a int); create table t1 (a int);
insert into t1 (a) values (1); insert into t1 (a) values (1);
prepare stmt from "create temporary table if not exists t2 as select * from t1"; prepare stmt from "create temporary table if not exists t2 as select * from t1";
execute stmt; execute stmt;
drop table t2; drop table t2;
execute stmt; execute stmt;
call p_verify_reprepare_count(0);
execute stmt; execute stmt;
call p_verify_reprepare_count(1);
select * from t2; select * from t2;
execute stmt; execute stmt;
call p_verify_reprepare_count(0);
select * from t2; select * from t2;
drop table t2; drop table t2;
create temporary table t2 (a varchar(10)); create temporary table t2 (a varchar(10));
execute stmt; execute stmt;
select * from t2; select * from t2;
call p_verify_reprepare_count(0); call p_verify_reprepare_count(1);
drop table t1; drop table t1;
create table t1 (x int); create table t1 (x int);
execute stmt; execute stmt;

View File

@@ -1051,6 +1051,9 @@ MDL_ticket::upgrade_shared_lock_to_exclusive()
signalled|= notify_shared_lock(thd, conflicting_ticket); signalled|= notify_shared_lock(thd, conflicting_ticket);
} }
/* There is a shared or exclusive lock on the object. */
DEBUG_SYNC(thd, "mdl_upgrade_shared_lock_to_exclusive_wait");
if (signalled) if (signalled)
pthread_cond_wait(&COND_mdl, &LOCK_mdl); pthread_cond_wait(&COND_mdl, &LOCK_mdl);
else else

View File

@@ -1033,6 +1033,7 @@ bool mysql_new_select(LEX *lex, bool move_down);
void create_select_for_variable(const char *var_name); void create_select_for_variable(const char *var_name);
void mysql_init_multi_delete(LEX *lex); void mysql_init_multi_delete(LEX *lex);
bool multi_delete_set_locks_and_link_aux_tables(LEX *lex); bool multi_delete_set_locks_and_link_aux_tables(LEX *lex);
void create_table_set_open_action_and_adjust_tables(LEX *lex);
void init_max_user_conn(void); void init_max_user_conn(void);
void init_update_queries(void); void init_update_queries(void);
void free_max_user_conn(void); void free_max_user_conn(void);
@@ -1170,10 +1171,9 @@ int prepare_create_field(Create_field *sql_field,
longlong table_flags); longlong table_flags);
CHARSET_INFO* get_sql_field_charset(Create_field *sql_field, CHARSET_INFO* get_sql_field_charset(Create_field *sql_field,
HA_CREATE_INFO *create_info); HA_CREATE_INFO *create_info);
bool mysql_create_table(THD *thd,const char *db, const char *table_name, bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
HA_CREATE_INFO *create_info, HA_CREATE_INFO *create_info,
Alter_info *alter_info, Alter_info *alter_info);
bool tmp_table, uint select_field_count);
bool mysql_create_table_no_lock(THD *thd, const char *db, bool mysql_create_table_no_lock(THD *thd, const char *db,
const char *table_name, const char *table_name,
HA_CREATE_INFO *create_info, HA_CREATE_INFO *create_info,

View File

@@ -689,8 +689,7 @@ my_bool acl_reload(THD *thd)
tables[0].next_local= tables[0].next_global= tables+1; tables[0].next_local= tables[0].next_global= tables+1;
tables[1].next_local= tables[1].next_global= tables+2; tables[1].next_local= tables[1].next_global= tables+2;
tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ; tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ;
tables[0].skip_temporary= tables[1].skip_temporary= tables[0].open_type= tables[1].open_type= tables[2].open_type= OT_BASE_ONLY;
tables[2].skip_temporary= TRUE;
init_mdl_requests(tables); init_mdl_requests(tables);
if (simple_open_n_lock_tables(thd, tables)) if (simple_open_n_lock_tables(thd, tables))
@@ -3797,7 +3796,7 @@ static my_bool grant_reload_procs_priv(THD *thd)
table.init_one_table("mysql", 5, "procs_priv", table.init_one_table("mysql", 5, "procs_priv",
strlen("procs_priv"), "procs_priv", strlen("procs_priv"), "procs_priv",
TL_READ); TL_READ);
table.skip_temporary= 1; table.open_type= OT_BASE_ONLY;
if (simple_open_n_lock_tables(thd, &table)) if (simple_open_n_lock_tables(thd, &table))
{ {
@@ -3863,7 +3862,7 @@ my_bool grant_reload(THD *thd)
tables[0].db= tables[1].db= (char *) "mysql"; tables[0].db= tables[1].db= (char *) "mysql";
tables[0].next_local= tables[0].next_global= tables+1; tables[0].next_local= tables[0].next_global= tables+1;
tables[0].lock_type= tables[1].lock_type= TL_READ; tables[0].lock_type= tables[1].lock_type= TL_READ;
tables[0].skip_temporary= tables[1].skip_temporary= TRUE; tables[0].open_type= tables[1].open_type= OT_BASE_ONLY;
init_mdl_requests(tables); init_mdl_requests(tables);
/* /*

View File

@@ -2437,7 +2437,8 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
same name. This block implements the behaviour. same name. This block implements the behaviour.
TODO: move this block into a separate function. TODO: move this block into a separate function.
*/ */
if (!table_list->skip_temporary && ! (flags & MYSQL_OPEN_SKIP_TEMPORARY)) if (table_list->open_type != OT_BASE_ONLY &&
! (flags & MYSQL_OPEN_SKIP_TEMPORARY))
{ {
for (table= thd->temporary_tables; table ; table=table->next) for (table= thd->temporary_tables; table ; table=table->next)
{ {
@@ -2469,10 +2470,16 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
} }
} }
if (flags & MYSQL_OPEN_TEMPORARY_ONLY) if (table_list->open_type == OT_TEMPORARY_ONLY ||
(flags & MYSQL_OPEN_TEMPORARY_ONLY))
{ {
my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->table_name); if (table_list->open_strategy == TABLE_LIST::OPEN_NORMAL)
DBUG_RETURN(TRUE); {
my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->table_name);
DBUG_RETURN(TRUE);
}
else
DBUG_RETURN(FALSE);
} }
/* /*

View File

@@ -3415,46 +3415,43 @@ void select_insert::abort() {
CREATE TABLE (SELECT) ... CREATE TABLE (SELECT) ...
***************************************************************************/ ***************************************************************************/
/* /**
Create table from lists of fields and items (or just return TABLE Create table from lists of fields and items (or just return TABLE
object for pre-opened existing table). object for pre-opened existing table).
SYNOPSIS @param thd [in] Thread object
create_table_from_items() @param create_info [in] Create information (like MAX_ROWS, ENGINE or
thd in Thread object temporary table flag)
create_info in Create information (like MAX_ROWS, ENGINE or @param create_table [in] Pointer to TABLE_LIST object providing database
temporary table flag) and name for table to be created or to be open
create_table in Pointer to TABLE_LIST object providing database @param alter_info [in/out] Initial list of columns and indexes for the
and name for table to be created or to be open table to be created
alter_info in/out Initial list of columns and indexes for the table @param items [in] List of items which should be used to produce
to be created rest of fields for the table (corresponding
items in List of items which should be used to produce rest fields will be added to the end of
of fields for the table (corresponding fields will alter_info->create_list)
be added to the end of alter_info->create_list) @param lock [out] Pointer to the MYSQL_LOCK object for table
lock out Pointer to the MYSQL_LOCK object for table created created will be returned in this parameter.
(or open temporary table) will be returned in this Since this table is not included in THD::lock
parameter. Since this table is not included in caller is responsible for explicitly unlocking
THD::lock caller is responsible for explicitly this table.
unlocking this table. @param hooks [in] Hooks to be invoked before and after obtaining
hooks table lock on the table being created.
NOTES @note
This function behaves differently for base and temporary tables: This function assumes that either table exists and was pre-opened and
- For base table we assume that either table exists and was pre-opened locked at open_and_lock_tables() stage (and in this case we just emit
and locked at open_and_lock_tables() stage (and in this case we just error or warning and return pre-opened TABLE object) or an exclusive
emit error or warning and return pre-opened TABLE object) or special metadata lock was acquired on table so we can safely create, open and
placeholder was put in table cache that guarantees that this table lock table in it (we don't acquire metadata lock if this create is
won't be created or opened until the placeholder will be removed for temporary table).
(so there is an exclusive lock on this table).
- We don't pre-open existing temporary table, instead we either open
or create and then open table in this function.
@note
Since this function contains some logic specific to CREATE TABLE ... Since this function contains some logic specific to CREATE TABLE ...
SELECT it should be changed before it can be used in other contexts. SELECT it should be changed before it can be used in other contexts.
RETURN VALUES @retval non-zero Pointer to TABLE object for table created or opened
non-zero Pointer to TABLE object for table created or opened @retval 0 Error
0 Error
*/ */
static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
@@ -3529,14 +3526,12 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
open_table(). open_table().
*/ */
{ {
tmp_disable_binlog(thd);
if (!mysql_create_table_no_lock(thd, create_table->db, if (!mysql_create_table_no_lock(thd, create_table->db,
create_table->table_name, create_table->table_name,
create_info, alter_info, 0, create_info, alter_info, 0,
select_field_count)) select_field_count))
{ {
if (create_info->table_existed && if (create_info->table_existed)
!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
{ {
/* /*
This means that someone created table underneath server This means that someone created table underneath server
@@ -3572,8 +3567,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
{ {
Open_table_context ot_ctx_unused(thd); Open_table_context ot_ctx_unused(thd);
if (open_table(thd, create_table, thd->mem_root, &ot_ctx_unused, if (open_table(thd, create_table, thd->mem_root, &ot_ctx_unused,
MYSQL_OPEN_TEMPORARY_ONLY) && MYSQL_OPEN_TEMPORARY_ONLY))
!create_info->table_existed)
{ {
/* /*
This shouldn't happen as creation of temporary table should make This shouldn't happen as creation of temporary table should make
@@ -3586,7 +3580,6 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
table= create_table->table; table= create_table->table;
} }
} }
reenable_binlog(thd);
if (!table) // open failed if (!table) // open failed
DBUG_RETURN(0); DBUG_RETURN(0);
} }
@@ -3610,9 +3603,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
mysql_unlock_tables(thd, *lock); mysql_unlock_tables(thd, *lock);
*lock= 0; *lock= 0;
} }
drop_open_table(thd, table, create_table->db, create_table->table_name);
if (!create_info->table_existed)
drop_open_table(thd, table, create_table->db, create_table->table_name);
DBUG_RETURN(0); DBUG_RETURN(0);
} }
DBUG_RETURN(table); DBUG_RETURN(table);
@@ -3704,7 +3695,7 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
DBUG_EXECUTE_IF("sleep_create_select_before_check_if_exists", my_sleep(6000000);); DBUG_EXECUTE_IF("sleep_create_select_before_check_if_exists", my_sleep(6000000););
if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE) && create_table->table) if (create_table->table)
{ {
/* Table already exists and was open at open_and_lock_tables() stage. */ /* Table already exists and was open at open_and_lock_tables() stage. */
if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)

View File

@@ -2245,9 +2245,9 @@ case SQLCOM_PREPARE:
{ {
DBUG_ASSERT(first_table == all_tables && first_table != 0); DBUG_ASSERT(first_table == all_tables && first_table != 0);
bool link_to_local; bool link_to_local;
// Skip first table, which is the table we are creating TABLE_LIST *create_table= first_table;
TABLE_LIST *create_table= lex->unlink_first_table(&link_to_local); TABLE_LIST *select_tables= lex->create_last_non_select_table->next_global;
TABLE_LIST *select_tables= lex->query_tables;
/* /*
Code below (especially in mysql_create_table() and select_create Code below (especially in mysql_create_table() and select_create
methods) may modify HA_CREATE_INFO structure in LEX, so we have to methods) may modify HA_CREATE_INFO structure in LEX, so we have to
@@ -2327,6 +2327,10 @@ case SQLCOM_PREPARE:
} }
#endif #endif
/* Set strategies: reset default or 'prepared' values. */
create_table->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
create_table->lock_strategy= TABLE_LIST::EXCLUSIVE_DOWNGRADABLE_MDL;
/* /*
Close any open handlers for the table Close any open handlers for the table
*/ */
@@ -2389,15 +2393,8 @@ case SQLCOM_PREPARE:
goto end_with_restore_list; goto end_with_restore_list;
} }
if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE)) if (!(res= open_and_lock_tables_derived(thd, lex->query_tables, TRUE,
{ MYSQL_OPEN_TAKE_UPGRADABLE_MDL)))
lex->link_first_table_back(create_table, link_to_local);
/* Set strategies: reset default or 'prepared' values. */
create_table->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
create_table->lock_strategy= TABLE_LIST::EXCLUSIVE_DOWNGRADABLE_MDL;
}
if (!(res= open_and_lock_tables(thd, lex->query_tables)))
{ {
/* /*
Is table which we are changing used somewhere in other parts Is table which we are changing used somewhere in other parts
@@ -2406,7 +2403,6 @@ case SQLCOM_PREPARE:
if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE)) if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
{ {
TABLE_LIST *duplicate; TABLE_LIST *duplicate;
create_table= lex->unlink_first_table(&link_to_local);
if ((duplicate= unique_table(thd, create_table, select_tables, 0))) if ((duplicate= unique_table(thd, create_table, select_tables, 0)))
{ {
update_non_unique_table_error(create_table, "CREATE", duplicate); update_non_unique_table_error(create_table, "CREATE", duplicate);
@@ -2432,6 +2428,13 @@ case SQLCOM_PREPARE:
} }
} }
/*
Remove target table from main select and name resolution
context. This can't be done earlier as it will break view merging in
statements like "CREATE TABLE IF NOT EXISTS existing_view SELECT".
*/
lex->unlink_first_table(&link_to_local);
/* /*
select_create is currently not re-execution friendly and select_create is currently not re-execution friendly and
needs to be created for every execution of a PS/SP. needs to be created for every execution of a PS/SP.
@@ -2451,33 +2454,32 @@ case SQLCOM_PREPARE:
res= handle_select(thd, lex, result, 0); res= handle_select(thd, lex, result, 0);
delete result; delete result;
} }
}
else if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
create_table= lex->unlink_first_table(&link_to_local);
lex->link_first_table_back(create_table, link_to_local);
}
} }
else else
{ {
/* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */ /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
if (create_info.options & HA_LEX_CREATE_TMP_TABLE) if (create_info.options & HA_LEX_CREATE_TMP_TABLE)
thd->options|= OPTION_KEEP_LOG; thd->options|= OPTION_KEEP_LOG;
/* regular create */
if (create_info.options & HA_LEX_CREATE_TABLE_LIKE) if (create_info.options & HA_LEX_CREATE_TABLE_LIKE)
{
/* CREATE TABLE ... LIKE ... */
res= mysql_create_like_table(thd, create_table, select_tables, res= mysql_create_like_table(thd, create_table, select_tables,
&create_info); &create_info);
}
else else
{ {
res= mysql_create_table(thd, create_table->db, /* Regular CREATE TABLE */
create_table->table_name, &create_info, res= mysql_create_table(thd, create_table,
&alter_info, 0, 0); &create_info, &alter_info);
} }
if (!res) if (!res)
my_ok(thd); my_ok(thd);
} }
/* put tables back for PS rexecuting */
end_with_restore_list: end_with_restore_list:
lex->link_first_table_back(create_table, link_to_local);
break; break;
} }
case SQLCOM_CREATE_INDEX: case SQLCOM_CREATE_INDEX:
@@ -2705,7 +2707,8 @@ end_with_restore_list:
} }
/* Ignore temporary tables if this is "SHOW CREATE VIEW" */ /* Ignore temporary tables if this is "SHOW CREATE VIEW" */
first_table->skip_temporary= 1; first_table->open_type= OT_BASE_ONLY;
} }
else else
{ {
@@ -7194,6 +7197,34 @@ bool insert_precheck(THD *thd, TABLE_LIST *tables)
} }
/**
Set proper open mode and table type for element representing target table
of CREATE TABLE statement, also adjust statement table list if necessary.
*/
void create_table_set_open_action_and_adjust_tables(LEX *lex)
{
TABLE_LIST *create_table= lex->query_tables;
if (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)
create_table->open_type= OT_TEMPORARY_ONLY;
else if (!lex->select_lex.item_list.elements)
create_table->open_type= OT_BASE_ONLY;
if (!lex->select_lex.item_list.elements)
{
/*
Avoid opening and locking target table for ordinary CREATE TABLE
or CREATE TABLE LIKE for write (unlike in CREATE ... SELECT we
won't do any insertions in it anyway). Not doing this causes
problems when running CREATE TABLE IF NOT EXISTS for already
existing log table.
*/
create_table->lock_type= TL_READ;
}
}
/** /**
CREATE TABLE query pre-check. CREATE TABLE query pre-check.

View File

@@ -4175,39 +4175,22 @@ bool mysql_unpack_partition(THD *thd,
ha_resolve_storage_engine_name(default_db_type))); ha_resolve_storage_engine_name(default_db_type)));
if (is_create_table_ind && old_lex->sql_command == SQLCOM_CREATE_TABLE) if (is_create_table_ind && old_lex->sql_command == SQLCOM_CREATE_TABLE)
{ {
if (old_lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE) /*
{ When we come here we are doing a create table. In this case we
/* have already done some preparatory work on the old part_info
This code is executed when we create table in CREATE TABLE t1 LIKE t2. object. We don't really need this new partition_info object.
old_lex->query_tables contains table list element for t2 and the table Thus we go back to the old partition info object.
we are opening has name t1. We need to free any memory objects allocated on item_free_list
*/ by the parser since we are keeping the old info from the first
if (partition_default_handling(table, part_info, FALSE, parser call in CREATE TABLE.
old_lex->query_tables->table->s->path.str)) We'll ensure that this object isn't put into table cache also
{ just to ensure we don't get into strange situations with the
result= TRUE; item objects.
goto end; */
} thd->free_items();
} part_info= thd->work_part_info;
else table->s->version= 0UL;
{ *work_part_info_used= true;
/*
When we come here we are doing a create table. In this case we
have already done some preparatory work on the old part_info
object. We don't really need this new partition_info object.
Thus we go back to the old partition info object.
We need to free any memory objects allocated on item_free_list
by the parser since we are keeping the old info from the first
parser call in CREATE TABLE.
We'll ensure that this object isn't put into table cache also
just to ensure we don't get into strange situations with the
item objects.
*/
thd->free_items();
part_info= thd->work_part_info;
table->s->version= 0UL;
*work_part_info_used= true;
}
} }
table->part_info= part_info; table->part_info= part_info;
table->file->set_part_info(part_info); table->file->set_part_info(part_info);

View File

@@ -1660,39 +1660,35 @@ static bool mysql_test_create_table(Prepared_statement *stmt)
LEX *lex= stmt->lex; LEX *lex= stmt->lex;
SELECT_LEX *select_lex= &lex->select_lex; SELECT_LEX *select_lex= &lex->select_lex;
bool res= FALSE; bool res= FALSE;
/* Skip first table, which is the table we are creating */
bool link_to_local; bool link_to_local;
TABLE_LIST *create_table= lex->unlink_first_table(&link_to_local); TABLE_LIST *create_table= lex->query_tables;
TABLE_LIST *tables= lex->query_tables; TABLE_LIST *tables= lex->create_last_non_select_table->next_global;
if (create_table_precheck(thd, tables, create_table)) if (create_table_precheck(thd, tables, create_table))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
/*
The open and lock strategies will be set again once the
statement is executed. These values are only meaningful
for the prepare phase.
*/
create_table->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
create_table->lock_strategy= TABLE_LIST::SHARED_MDL;
if (select_lex->item_list.elements) if (select_lex->item_list.elements)
{ {
if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
{
lex->link_first_table_back(create_table, link_to_local);
/*
The open and lock strategies will be set again once the
statement is executed. These values are only meaningful
for the prepare phase.
*/
create_table->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
create_table->lock_strategy= TABLE_LIST::SHARED_MDL;
}
if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, 0)) if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, 0))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
create_table= lex->unlink_first_table(&link_to_local);
select_lex->context.resolve_in_select_list= TRUE; select_lex->context.resolve_in_select_list= TRUE;
lex->unlink_first_table(&link_to_local);
res= select_like_stmt_test(stmt, 0, 0); res= select_like_stmt_test(stmt, 0, 0);
lex->link_first_table_back(create_table, &link_to_local);
} }
else if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE) else
{ {
/* /*
Check that the source table exist, and also record Check that the source table exist, and also record
@@ -1704,8 +1700,6 @@ static bool mysql_test_create_table(Prepared_statement *stmt)
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
} }
/* put tables back for PS rexecuting */
lex->link_first_table_back(create_table, link_to_local);
DBUG_RETURN(res); DBUG_RETURN(res);
} }

View File

@@ -4084,7 +4084,6 @@ bool mysql_create_table_no_lock(THD *thd,
thd->thread_specific_used= TRUE; thd->thread_specific_used= TRUE;
} }
write_create_table_bin_log(thd, create_info, internal_tmp_table);
error= FALSE; error= FALSE;
unlock_and_end: unlock_and_end:
pthread_mutex_unlock(&LOCK_open); pthread_mutex_unlock(&LOCK_open);
@@ -4109,21 +4108,18 @@ warn:
Database and name-locking aware wrapper for mysql_create_table_no_lock(), Database and name-locking aware wrapper for mysql_create_table_no_lock(),
*/ */
bool mysql_create_table(THD *thd, const char *db, const char *table_name, bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
HA_CREATE_INFO *create_info, HA_CREATE_INFO *create_info,
Alter_info *alter_info, Alter_info *alter_info)
bool internal_tmp_table,
uint select_field_count)
{ {
MDL_request target_mdl_request;
bool has_target_mdl_lock= FALSE;
bool result; bool result;
DBUG_ENTER("mysql_create_table"); DBUG_ENTER("mysql_create_table");
/* Wait for any database locks */ /* Wait for any database locks */
pthread_mutex_lock(&LOCK_lock_db); pthread_mutex_lock(&LOCK_lock_db);
while (!thd->killed && while (!thd->killed &&
my_hash_search(&lock_db_cache,(uchar*) db, strlen(db))) my_hash_search(&lock_db_cache, (uchar*)create_table->db,
create_table->db_length))
{ {
wait_for_condition(thd, &LOCK_lock_db, &COND_refresh); wait_for_condition(thd, &LOCK_lock_db, &COND_refresh);
pthread_mutex_lock(&LOCK_lock_db); pthread_mutex_lock(&LOCK_lock_db);
@@ -4137,47 +4133,47 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name,
creating_table++; creating_table++;
pthread_mutex_unlock(&LOCK_lock_db); pthread_mutex_unlock(&LOCK_lock_db);
if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE)) /*
Open or obtain an exclusive metadata lock on table being created.
*/
if (open_and_lock_tables_derived(thd, thd->lex->query_tables, FALSE,
MYSQL_OPEN_TAKE_UPGRADABLE_MDL))
{ {
target_mdl_request.init(MDL_key::TABLE, db, table_name, MDL_EXCLUSIVE); result= TRUE;
if (thd->mdl_context.try_acquire_exclusive_lock(&target_mdl_request)) goto unlock;
{
result= TRUE;
goto unlock;
}
if (target_mdl_request.ticket == NULL)
{
/* Table exists and is locked by some other thread. */
if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
{
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
table_name);
create_info->table_existed= 1;
result= FALSE;
write_create_table_bin_log(thd, create_info, internal_tmp_table);
}
else
{
my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
result= TRUE;
}
goto unlock;
}
/* Got lock. */
DEBUG_SYNC(thd, "locked_table_name");
has_target_mdl_lock= TRUE;
} }
result= mysql_create_table_no_lock(thd, db, table_name, create_info, /* Got lock. */
alter_info, DEBUG_SYNC(thd, "locked_table_name");
internal_tmp_table,
select_field_count); result= mysql_create_table_no_lock(thd, create_table->db,
create_table->table_name, create_info,
alter_info, FALSE, 0);
/*
Don't write statement if:
- Table creation has failed
- Table has already existed
- Row-based logging is used and we are creating a temporary table
Otherwise, the statement shall be binlogged.
*/
if (!result &&
!create_info->table_existed &&
(!thd->current_stmt_binlog_row_based ||
(thd->current_stmt_binlog_row_based &&
!(create_info->options & HA_LEX_CREATE_TMP_TABLE))))
write_bin_log(thd, TRUE, thd->query(), thd->query_length());
if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
{
/*
close_thread_tables() takes care about both closing open tables (which
might be still around in case of error) and releasing metadata locks.
*/
close_thread_tables(thd);
}
unlock: unlock:
if (has_target_mdl_lock)
thd->mdl_context.release_lock(target_mdl_request.ticket);
pthread_mutex_lock(&LOCK_lock_db); pthread_mutex_lock(&LOCK_lock_db);
if (!--creating_table && creating_database) if (!--creating_table && creating_database)
pthread_cond_signal(&COND_refresh); pthread_cond_signal(&COND_refresh);
@@ -5158,55 +5154,6 @@ bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
} }
/**
@brief Create frm file based on I_S table
@param[in] thd thread handler
@param[in] schema_table I_S table
@param[in] dst_path path where frm should be created
@param[in] create_info Create info
@return Operation status
@retval 0 success
@retval 1 error
*/
bool mysql_create_like_schema_frm(THD* thd, TABLE_LIST* schema_table,
char *dst_path, HA_CREATE_INFO *create_info)
{
HA_CREATE_INFO local_create_info;
Alter_info alter_info;
bool tmp_table= (create_info->options & HA_LEX_CREATE_TMP_TABLE);
uint keys= schema_table->table->s->keys;
uint db_options= 0;
DBUG_ENTER("mysql_create_like_schema_frm");
bzero((char*) &local_create_info, sizeof(local_create_info));
local_create_info.db_type= schema_table->table->s->db_type();
local_create_info.row_type= schema_table->table->s->row_type;
local_create_info.default_table_charset=default_charset_info;
alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
schema_table->table->use_all_columns();
if (mysql_prepare_alter_table(thd, schema_table->table,
&local_create_info, &alter_info))
DBUG_RETURN(1);
if (mysql_prepare_create_table(thd, &local_create_info, &alter_info,
tmp_table, &db_options,
schema_table->table->file,
&schema_table->table->s->key_info, &keys, 0))
DBUG_RETURN(1);
local_create_info.max_rows= 0;
if (mysql_create_frm(thd, dst_path, NullS, NullS,
&local_create_info, alter_info.create_list,
keys, schema_table->table->s->key_info,
schema_table->table->file))
DBUG_RETURN(1);
DBUG_RETURN(0);
}
/* /*
Create a table identical to the specified table Create a table identical to the specified table
@@ -5225,12 +5172,8 @@ bool mysql_create_like_schema_frm(THD* thd, TABLE_LIST* schema_table,
bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
HA_CREATE_INFO *create_info) HA_CREATE_INFO *create_info)
{ {
char src_path[FN_REFLEN + 1], dst_path[FN_REFLEN + 1]; HA_CREATE_INFO local_create_info;
uint dst_path_length; Alter_info local_alter_info;
bool has_mdl_lock= FALSE;
char *db= table->db;
char *table_name= table->table_name;
int err;
bool res= TRUE; bool res= TRUE;
uint not_used; uint not_used;
#ifdef WITH_PARTITION_STORAGE_ENGINE #ifdef WITH_PARTITION_STORAGE_ENGINE
@@ -5242,161 +5185,63 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
/* /*
By opening source table and thus acquiring shared metadata lock on it We the open source table to get its description in HA_CREATE_INFO
we guarantee that it exists and no concurrent DDL operation will mess and Alter_info objects. This also acquires a shared metadata lock
with it. Later we also take an exclusive metadata lock on target table on this table which ensures that no concurrent DDL operation will
name, which makes copying of .frm file, call to ha_create_table() and mess with it.
binlogging atomic against concurrent DML and DDL operations on target Also in case when we create non-temporary table open_tables()
table. Thus by holding both these "locks" we ensure that our statement call obtains an exclusive metadata lock on target table ensuring
is properly isolated from all concurrent operations which matter. that we can safely perform table creation.
Thus by holding both these locks we ensure that our statement is
properly isolated from all concurrent operations which matter.
*/ */
if (open_tables(thd, &src_table, &not_used, 0)) if (open_tables(thd, &thd->lex->query_tables, &not_used, 0))
DBUG_RETURN(TRUE);
/*
For bug#25875, Newly created table through CREATE TABLE .. LIKE
has no ndb_dd attributes;
Add something to get possible tablespace info from src table,
it can get valid tablespace name only for disk-base ndb table
*/
if ((src_table->table->file->get_tablespace_name(thd, ts_name, FN_LEN)))
{
create_info->tablespace= ts_name;
create_info->storage_media= HA_SM_DISK;
}
strxmov(src_path, src_table->table->s->path.str, reg_ext, NullS);
DBUG_EXECUTE_IF("sleep_create_like_before_check_if_exists", my_sleep(6000000););
/*
Check that destination tables does not exist. Note that its name
was already checked when it was added to the table list.
*/
if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
{
if (find_temporary_table(thd, db, table_name))
goto table_exists;
dst_path_length= build_tmptable_filename(thd, dst_path, sizeof(dst_path));
create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE;
}
else
{
table->mdl_request.init(MDL_key::TABLE, db, table_name, MDL_EXCLUSIVE);
if (thd->mdl_context.try_acquire_exclusive_lock(&table->mdl_request))
DBUG_RETURN(TRUE);
if (table->mdl_request.ticket == NULL)
goto table_exists;
DEBUG_SYNC(thd, "locked_table_name");
has_mdl_lock= TRUE;
dst_path_length= build_table_filename(dst_path, sizeof(dst_path) - 1,
db, table_name, reg_ext, 0);
if (!access(dst_path, F_OK))
goto table_exists;
}
DBUG_EXECUTE_IF("sleep_create_like_before_copy", my_sleep(6000000););
if (opt_sync_frm && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
flags|= MY_SYNC;
/*
Create a new table by copying from source table
and sync the new table if the flag MY_SYNC is set
TODO: Obtaining LOCK_open mutex here is actually a legacy from the
times when some operations (e.g. I_S implementation) ignored
exclusive metadata lock on target table. Also some engines
(e.g. NDB cluster) require that LOCK_open should be held
during the call to ha_create_table() (See bug #28614 for more
info). So we should double check and probably fix this code
to not acquire this mutex.
*/
pthread_mutex_lock(&LOCK_open);
if (src_table->schema_table)
{
if (mysql_create_like_schema_frm(thd, src_table, dst_path, create_info))
{
pthread_mutex_unlock(&LOCK_open);
goto err;
}
}
else if (my_copy(src_path, dst_path, flags))
{
if (my_errno == ENOENT)
my_error(ER_BAD_DB_ERROR,MYF(0),db);
else
my_error(ER_CANT_CREATE_FILE,MYF(0),dst_path,my_errno);
pthread_mutex_unlock(&LOCK_open);
goto err; goto err;
} src_table->table->use_all_columns();
/* /* Fill HA_CREATE_INFO and Alter_info with description of source table. */
As mysql_truncate don't work on a new table at this stage of bzero((char*) &local_create_info, sizeof(local_create_info));
creation, instead create the table directly (for both normal local_create_info.db_type= src_table->table->s->db_type();
and temporary tables). local_create_info.row_type= src_table->table->s->row_type;
*/ if (mysql_prepare_alter_table(thd, src_table->table, &local_create_info,
&local_alter_info))
goto err;
#ifdef WITH_PARTITION_STORAGE_ENGINE #ifdef WITH_PARTITION_STORAGE_ENGINE
/* /* Partition info is not handled by mysql_prepare_alter_table() call. */
For partitioned tables we need to copy the .par file as well since if (src_table->table->part_info)
it is used in open_table_def to even be able to create a new handler. thd->work_part_info= src_table->table->part_info->get_clone();
There is no way to find out here if the original table is a
partitioned table so we copy the file and ignore any errors.
*/
fn_format(tmp_path, dst_path, reg_ext, ".par", MYF(MY_REPLACE_EXT));
strmov(dst_path, tmp_path);
fn_format(tmp_path, src_path, reg_ext, ".par", MYF(MY_REPLACE_EXT));
strmov(src_path, tmp_path);
my_copy(src_path, dst_path, MYF(MY_DONT_OVERWRITE_FILE));
#endif #endif
DBUG_EXECUTE_IF("sleep_create_like_before_ha_create", my_sleep(6000000);); /*
Adjust description of source table before using it for creation of
target table.
dst_path[dst_path_length - reg_ext_length]= '\0'; // Remove .frm Similarly to SHOW CREATE TABLE we ignore MAX_ROWS attribute of
if (thd->variables.keep_files_on_create) temporary table which represents I_S table.
create_info->options|= HA_CREATE_KEEP_FILES; */
err= ha_create_table(thd, dst_path, db, table_name, create_info, 1); if (src_table->schema_table)
pthread_mutex_unlock(&LOCK_open); local_create_info.max_rows= 0;
/* Set IF NOT EXISTS option as in the CREATE TABLE LIKE statement. */
local_create_info.options|= create_info->options&HA_LEX_CREATE_IF_NOT_EXISTS;
/* Replace type of source table with one specified in the statement. */
local_create_info.options&= ~HA_LEX_CREATE_TMP_TABLE;
local_create_info.options|= create_info->options & HA_LEX_CREATE_TMP_TABLE;
/* Reset auto-increment counter for the new table. */
local_create_info.auto_increment_value= 0;
if (create_info->options & HA_LEX_CREATE_TMP_TABLE) if ((res= mysql_create_table_no_lock(thd, table->db, table->table_name,
{ &local_create_info, &local_alter_info,
if (err || !open_temporary_table(thd, dst_path, db, table_name, 1)) FALSE, 0)) ||
{ local_create_info.table_existed)
(void) rm_temporary_table(create_info->db_type,
dst_path); /* purecov: inspected */
goto err; /* purecov: inspected */
}
thd->thread_specific_used= TRUE;
}
else if (err)
{
(void) quick_rm_table(create_info->db_type, db,
table_name, 0); /* purecov: inspected */
goto err; /* purecov: inspected */
}
goto binlog;
table_exists:
if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
{
char warn_buff[MYSQL_ERRMSG_SIZE];
my_snprintf(warn_buff, sizeof(warn_buff),
ER(ER_TABLE_EXISTS_ERROR), table_name);
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_TABLE_EXISTS_ERROR,warn_buff);
}
else
{
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
goto err; goto err;
}
binlog: /*
DBUG_EXECUTE_IF("sleep_create_like_before_binlogging", my_sleep(6000000);); Ensure that we have an exclusive lock on target table if we are creating
non-temporary table.
*/
DBUG_ASSERT((create_info->options & HA_LEX_CREATE_TMP_TABLE) ||
thd->mdl_context.is_exclusive_lock_owner(MDL_key::TABLE, table->db,
table->table_name));
/* /*
We have to write the query before we unlock the tables. We have to write the query before we unlock the tables.
@@ -5463,12 +5308,7 @@ binlog:
else else
write_bin_log(thd, TRUE, thd->query(), thd->query_length()); write_bin_log(thd, TRUE, thd->query(), thd->query_length());
res= FALSE;
err: err:
if (has_mdl_lock)
thd->mdl_context.release_lock(table->mdl_request.ticket);
DBUG_RETURN(res); DBUG_RETURN(res);
} }

View File

@@ -445,7 +445,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
Also prevent DROP TRIGGER from opening temporary table which might Also prevent DROP TRIGGER from opening temporary table which might
shadow base table on which trigger to be dropped is defined. shadow base table on which trigger to be dropped is defined.
*/ */
tables->skip_temporary= TRUE; tables->open_type= OT_BASE_ONLY;
/* Keep consistent with respect to other DDL statements */ /* Keep consistent with respect to other DDL statements */
mysql_ha_rm_tables(thd, tables); mysql_ha_rm_tables(thd, tables);

View File

@@ -1259,7 +1259,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
tbl; tbl;
tbl= (view_tables_tail= tbl)->next_global) tbl= (view_tables_tail= tbl)->next_global)
{ {
tbl->skip_temporary= 1; tbl->open_type= OT_BASE_ONLY;
tbl->belong_to_view= top_view; tbl->belong_to_view= top_view;
tbl->referencing_view= table; tbl->referencing_view= table;
tbl->prelocking_placeholder= table->prelocking_placeholder; tbl->prelocking_placeholder= table->prelocking_placeholder;

View File

@@ -1781,6 +1781,7 @@ create:
ha_resolve_storage_engine_name(lex->create_info.db_type), ha_resolve_storage_engine_name(lex->create_info.db_type),
$5->table.str); $5->table.str);
} }
create_table_set_open_action_and_adjust_tables(lex);
} }
| CREATE opt_unique_or_fulltext INDEX_SYM ident key_alg ON | CREATE opt_unique_or_fulltext INDEX_SYM ident key_alg ON
table_ident table_ident
@@ -3921,7 +3922,7 @@ size_number:
create2: create2:
'(' create2a {} '(' create2a {}
| opt_create_table_options | opt_create_table_options
opt_partitioning opt_create_partitioning
create3 {} create3 {}
| LIKE table_ident | LIKE table_ident
{ {
@@ -3955,9 +3956,9 @@ create2:
create2a: create2a:
create_field_list ')' opt_create_table_options create_field_list ')' opt_create_table_options
opt_partitioning opt_create_partitioning
create3 {} create3 {}
| opt_partitioning | opt_create_partitioning
create_select ')' create_select ')'
{ Select->set_braces(1);} { Select->set_braces(1);}
union_opt {} union_opt {}
@@ -3973,6 +3974,19 @@ create3:
union_opt {} union_opt {}
; ;
opt_create_partitioning:
opt_partitioning
{
/*
Remove all tables used in PARTITION clause from the global table
list. Partitioning with subqueries is not allowed anyway.
*/
TABLE_LIST *last_non_sel_table= Lex->create_last_non_select_table;
last_non_sel_table->next_global= 0;
Lex->query_tables_last= &last_non_sel_table->next_global;
}
;
/* /*
This part of the parser is about handling of the partition information. This part of the parser is about handling of the partition information.

View File

@@ -1069,6 +1069,16 @@ public:
}; };
/**
Type of table which can be open for an element of table list.
*/
enum enum_open_type
{
OT_TEMPORARY_OR_BASE= 0, OT_TEMPORARY_ONLY, OT_BASE_ONLY
};
/* /*
Table reference in the FROM clause. Table reference in the FROM clause.
@@ -1330,7 +1340,11 @@ struct TABLE_LIST
bool cacheable_table; /* stop PS caching */ bool cacheable_table; /* stop PS caching */
/* used in multi-upd/views privilege check */ /* used in multi-upd/views privilege check */
bool table_in_first_from_clause; bool table_in_first_from_clause;
bool skip_temporary; /* this table shouldn't be temporary */ /**
Specifies which kind of table should be open for this element
of table list.
*/
enum enum_open_type open_type;
/* TRUE if this merged view contain auto_increment field */ /* TRUE if this merged view contain auto_increment field */
bool contain_auto_increment; bool contain_auto_increment;
bool multitable_view; /* TRUE iff this is multitable view */ bool multitable_view; /* TRUE iff this is multitable view */