diff options
-rw-r--r-- | mysql-test/r/create-big.result (renamed from mysql-test/r/create_select-big.result) | 83 | ||||
-rw-r--r-- | mysql-test/r/create.result | 2 | ||||
-rw-r--r-- | mysql-test/r/grant2.result | 23 | ||||
-rw-r--r-- | mysql-test/t/create-big.test (renamed from mysql-test/t/create_select-big.test) | 132 | ||||
-rw-r--r-- | mysql-test/t/create.test | 2 | ||||
-rw-r--r-- | mysql-test/t/disabled.def | 1 | ||||
-rw-r--r-- | mysql-test/t/grant2.test | 41 | ||||
-rw-r--r-- | sql/handler.h | 1 | ||||
-rw-r--r-- | sql/mysql_priv.h | 5 | ||||
-rw-r--r-- | sql/sql_lex.h | 1 | ||||
-rw-r--r-- | sql/sql_parse.cc | 38 | ||||
-rw-r--r-- | sql/sql_partition.cc | 17 | ||||
-rw-r--r-- | sql/sql_table.cc | 137 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 22 |
14 files changed, 364 insertions, 141 deletions
diff --git a/mysql-test/r/create_select-big.result b/mysql-test/r/create-big.result index 1c393bd2224..eb57bf59084 100644 --- a/mysql-test/r/create_select-big.result +++ b/mysql-test/r/create-big.result @@ -162,3 +162,86 @@ t1 CREATE TABLE `t1` ( `j` int(11) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1, t2, t3; +drop table if exists t1,t2; +create table t1 (i int); +set session debug="+d,sleep_create_like_before_check_if_exists"; +reset master; +create table t2 like t1;; +insert into t1 values (1); +drop table t1; +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `i` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +drop table t2; +show binlog events in 'master-bin.000001' from 106; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query 1 # use `test`; insert into t1 values (1) +master-bin.000001 # Query 1 # use `test`; create table t2 like t1 +master-bin.000001 # Query 1 # use `test`; drop table t1 +master-bin.000001 # Query 1 # use `test`; drop table t2 +create table t1 (i int); +set session debug="-d,sleep_create_like_before_check_if_exists:+d,sleep_create_like_before_copy"; +create table t2 like t1;; +create table if not exists t2 (j int); +Warnings: +Note 1050 Table 't2' already exists +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `i` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +drop table t2; +reset master; +create table t2 like t1;; +drop table t1; +drop table t2; +show binlog events in 'master-bin.000001' from 106; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query 1 # use `test`; create table t2 like t1 +master-bin.000001 # Query 1 # use `test`; drop table t1 +master-bin.000001 # Query 1 # use `test`; drop table t2 +create table t1 (i int); +set session debug="-d,sleep_create_like_before_copy:+d,sleep_create_like_before_ha_create"; +reset master; +create table t2 like t1;; +insert into t2 values (1); +drop table t2; +create table t2 like t1;; +drop table t2; +create table t2 like t1;; +drop table t1; +drop table t2; +show binlog events in 'master-bin.000001' from 106; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query 1 # use `test`; create table t2 like t1 +master-bin.000001 # Query 1 # use `test`; insert into t2 values (1) +master-bin.000001 # Query 1 # use `test`; drop table t2 +master-bin.000001 # Query 1 # use `test`; create table t2 like t1 +master-bin.000001 # Query 1 # use `test`; drop table t2 +master-bin.000001 # Query 1 # use `test`; create table t2 like t1 +master-bin.000001 # Query 1 # use `test`; drop table t1 +master-bin.000001 # Query 1 # use `test`; drop table t2 +create table t1 (i int); +set session debug="-d,sleep_create_like_before_ha_create:+d,sleep_create_like_before_binlogging"; +reset master; +create table t2 like t1;; +insert into t2 values (1); +drop table t2; +create table t2 like t1;; +drop table t2; +create table t2 like t1;; +drop table t1; +drop table t2; +show binlog events in 'master-bin.000001' from 106; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query 1 # use `test`; create table t2 like t1 +master-bin.000001 # Query 1 # use `test`; insert into t2 values (1) +master-bin.000001 # Query 1 # use `test`; drop table t2 +master-bin.000001 # Query 1 # use `test`; create table t2 like t1 +master-bin.000001 # Query 1 # use `test`; drop table t2 +master-bin.000001 # Query 1 # use `test`; create table t2 like t1 +master-bin.000001 # Query 1 # use `test`; drop table t1 +master-bin.000001 # Query 1 # use `test`; drop table t2 +set session debug="-d,sleep_create_like_before_binlogging"; diff --git a/mysql-test/r/create.result b/mysql-test/r/create.result index ef22b21e9fb..f570f6eb75c 100644 --- a/mysql-test/r/create.result +++ b/mysql-test/r/create.result @@ -371,7 +371,7 @@ ERROR 42S01: Table 't3' already exists create table non_existing_database.t1 like t1; ERROR 42000: Unknown database 'non_existing_database' create table t3 like non_existing_table; -ERROR 42S02: Unknown table 'non_existing_table' +ERROR 42S02: Table 'test.non_existing_table' doesn't exist create temporary table t3 like t1; ERROR 42S01: Table 't3' already exists drop table t1, t2, t3; diff --git a/mysql-test/r/grant2.result b/mysql-test/r/grant2.result index 03019bd5c1f..f2722ee052e 100644 --- a/mysql-test/r/grant2.result +++ b/mysql-test/r/grant2.result @@ -381,3 +381,26 @@ drop table t2; REVOKE ALL PRIVILEGES, GRANT OPTION FROM `a@`@localhost; drop user `a@`@localhost; SET GLOBAL log_bin_trust_function_creators = 0; +drop database if exists mysqltest_1; +drop database if exists mysqltest_2; +drop user mysqltest_u1@localhost; +create database mysqltest_1; +create database mysqltest_2; +grant all on mysqltest_1.* to mysqltest_u1@localhost; +use mysqltest_2; +create table t1 (i int); +show create table mysqltest_2.t1; +ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't1' +create table t1 like mysqltest_2.t1; +ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't1' +grant select on mysqltest_2.t1 to mysqltest_u1@localhost; +show create table mysqltest_2.t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `i` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +create table t1 like mysqltest_2.t1; +use test; +drop database mysqltest_1; +drop database mysqltest_2; +drop user mysqltest_u1@localhost; diff --git a/mysql-test/t/create_select-big.test b/mysql-test/t/create-big.test index 3fa655c5501..6cd6326cdb8 100644 --- a/mysql-test/t/create_select-big.test +++ b/mysql-test/t/create-big.test @@ -1,12 +1,17 @@ -# Tests for various aspects of CREATE TABLE ... SELECT implementation +# Tests for various concurrency-related aspects of CREATE TABLE ... SELECT +# and CREATE TABLE like implementation. # -# Note that we don't test general CREATE TABLE ... SELECT functionality here as -# it is already covered by create.test. We are more interested in extreme cases. +# Note that we don't test general CREATE TABLE ... SELECT/LIKE functionality +# here as it is already covered by create.test. We are more interested in +# extreme cases. # # This test takes rather long time so let us run it only in --big-test mode --source include/big_test.inc # We are using some debug-only features in this test --source include/have_debug.inc +# Some of tests below also use binlog to check that statements are +# executed and logged in correct order +--source include/have_binlog_format_mixed_or_statement.inc # Create auxilliary connections connect (addconroot1, localhost, root,,); @@ -20,7 +25,7 @@ drop table if exists t1,t2,t3,t4,t5; # -# Tests for concurrency problems. +# Tests for concurrency problems in CREATE TABLE ... SELECT # # We introduce delays between various stages of table creation # and check that other statements dealing with this table cannot @@ -266,3 +271,122 @@ connection default; select * from t1; show create table t1; drop table t1, t2, t3; + + +# Tests for possible concurrency issues with CREATE TABLE ... LIKE +# +# Bug #18950 "create table like does not obtain LOCK_open" +# Bug #23667 "CREATE TABLE LIKE is not isolated from alteration by other +# connections" +# +# Again the idea of this test is that we introduce artificial delays on +# various stages of table creation and check that concurrent statements +# for tables from CREATE TABLE ... LIKE are not interfering. + +--disable_warnings +drop table if exists t1,t2; +--enable_warnings + +# What happens if some statements sneak in right after we have +# opened source table ? +create table t1 (i int); +set session debug="+d,sleep_create_like_before_check_if_exists"; +# Reset binlog to have clear start +reset master; +--send create table t2 like t1; +connection addconroot1; +--sleep 2 +# DML on source table should be allowed to run concurrently +insert into t1 values (1); +# And DDL should wait +drop table t1; +connection default; +--reap +show create table t2; +drop table t2; +# Let us check that statements were executed/binlogged in correct order +--replace_column 2 # 5 # +show binlog events in 'master-bin.000001' from 106; + +# Now let us check the gap between check for target table +# existance and copying of .frm file. +create table t1 (i int); +set session debug="-d,sleep_create_like_before_check_if_exists:+d,sleep_create_like_before_copy"; +# It should be impossible to create target table concurrently +--send create table t2 like t1; +connection addconroot1; +--sleep 2 +create table if not exists t2 (j int); +connection default; +--reap +show create table t2; +drop table t2; +# And concurrent DDL on the source table should be still disallowed +reset master; +--send create table t2 like t1; +connection addconroot1; +--sleep 2 +drop table t1; +connection default; +--reap +drop table t2; +--replace_column 2 # 5 # +show binlog events in 'master-bin.000001' from 106; + +# And now he gap between copying of .frm file and ha_create_table() call. +create table t1 (i int); +set session debug="-d,sleep_create_like_before_copy:+d,sleep_create_like_before_ha_create"; +# Both DML and DDL on target table should wait till operation completes +reset master; +--send create table t2 like t1; +connection addconroot1; +--sleep 2 +insert into t2 values (1); +connection default; +--reap +drop table t2; +--send create table t2 like t1; +connection addconroot1; +--sleep 2 +drop table t2; +connection default; +--reap +# Concurrent DDL on the source table still waits +--send create table t2 like t1; +connection addconroot1; +--sleep 2 +drop table t1; +connection default; +--reap +drop table t2; +--replace_column 2 # 5 # +show binlog events in 'master-bin.000001' from 106; + +# Finally we check the gap between ha_create_table() and binlogging +create table t1 (i int); +set session debug="-d,sleep_create_like_before_ha_create:+d,sleep_create_like_before_binlogging"; +reset master; +--send create table t2 like t1; +connection addconroot1; +--sleep 2 +insert into t2 values (1); +connection default; +--reap +drop table t2; +--send create table t2 like t1; +connection addconroot1; +--sleep 2 +drop table t2; +connection default; +--reap +--send create table t2 like t1; +connection addconroot1; +--sleep 2 +drop table t1; +connection default; +--reap +drop table t2; +--replace_column 2 # 5 # +show binlog events in 'master-bin.000001' from 106; + +set session debug="-d,sleep_create_like_before_binlogging"; diff --git a/mysql-test/t/create.test b/mysql-test/t/create.test index 243cdea009e..fb589a5d11e 100644 --- a/mysql-test/t/create.test +++ b/mysql-test/t/create.test @@ -306,7 +306,7 @@ create table t3 like t1; create table t3 like mysqltest.t3; --error 1049 create table non_existing_database.t1 like t1; ---error 1051 +--error ER_NO_SUCH_TABLE create table t3 like non_existing_table; --error 1050 create temporary table t3 like t1; diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index e3d72f1f7e0..12c5f38c680 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -30,7 +30,6 @@ rpl_ddl : BUG#26418 2007-03-01 mleich Slave out of sync after C rpl_ndb_innodb2ndb : Bug #19710 Cluster replication to partition table fails on DELETE FROM statement rpl_ndb_myisam2ndb : Bug #19710 Cluster replication to partition table fails on DELETE FROM statement rpl_row_blob_innodb : BUG#18980 2006-04-10 kent Test fails randomly -synchronization : Bug#24529 Test 'synchronization' fails on Mac pushbuild; Also on Linux 64 bit. # the below testcase have been reworked to avoid the bug, test contains comment, keep bug open #ndb_binlog_ddl_multi : BUG#18976 2006-04-10 kent CRBR: multiple binlog, second binlog may miss schema log events diff --git a/mysql-test/t/grant2.test b/mysql-test/t/grant2.test index 866c5a8a4b3..9b83cd5aab7 100644 --- a/mysql-test/t/grant2.test +++ b/mysql-test/t/grant2.test @@ -513,3 +513,44 @@ REVOKE ALL PRIVILEGES, GRANT OPTION FROM `a@`@localhost; drop user `a@`@localhost; SET GLOBAL log_bin_trust_function_creators = 0; + + +# +# Bug#25578 "CREATE TABLE LIKE does not require any privileges on source table" +# +--disable_warnings +drop database if exists mysqltest_1; +drop database if exists mysqltest_2; +--enable_warnings +--error 0,ER_CANNOT_USER +drop user mysqltest_u1@localhost; + +create database mysqltest_1; +create database mysqltest_2; +grant all on mysqltest_1.* to mysqltest_u1@localhost; +use mysqltest_2; +create table t1 (i int); + +# Connect as user with all rights on mysqltest_1 but with no rights on mysqltest_2. +connect (user1,localhost,mysqltest_u1,,mysqltest_1); +connection user1; +# As expected error is emitted +--error ER_TABLEACCESS_DENIED_ERROR +show create table mysqltest_2.t1; +# This should emit error as well +--error ER_TABLEACCESS_DENIED_ERROR +create table t1 like mysqltest_2.t1; + +# Now let us check that SELECT privilege on the source is enough +connection default; +grant select on mysqltest_2.t1 to mysqltest_u1@localhost; +connection user1; +show create table mysqltest_2.t1; +create table t1 like mysqltest_2.t1; + +# Clean-up +connection default; +use test; +drop database mysqltest_1; +drop database mysqltest_2; +drop user mysqltest_u1@localhost; diff --git a/sql/handler.h b/sql/handler.h index 052c245b801..216620a6882 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -223,6 +223,7 @@ #define HA_LEX_CREATE_TMP_TABLE 1 #define HA_LEX_CREATE_IF_NOT_EXISTS 2 +#define HA_LEX_CREATE_TABLE_LIKE 4 #define HA_OPTION_NO_CHECKSUM (1L << 17) #define HA_OPTION_NO_DELAY_KEY_WRITE (1L << 18) #define HA_MAX_REC_LENGTH 65535 diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index e5e70f81bff..0b843d5610d 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -978,9 +978,8 @@ bool mysql_alter_table(THD *thd, char *new_db, char *new_name, uint order_num, ORDER *order, bool ignore, ALTER_INFO *alter_info, bool do_send_ok); bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool do_send_ok); -bool mysql_create_like_table(THD *thd, TABLE_LIST *table, - HA_CREATE_INFO *create_info, - Table_ident *src_table); +bool mysql_create_like_table(THD *thd, TABLE_LIST *table, TABLE_LIST *src_table, + HA_CREATE_INFO *create_info); bool mysql_rename_table(handlerton *base, const char *old_db, const char * old_name, const char *new_db, const char * new_name, uint flags); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 3dca1d15b88..ddd7c752e11 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1066,7 +1066,6 @@ typedef struct st_lex : public Query_tables_list char *length,*dec,*change; LEX_STRING name; - Table_ident *like_name; char *help_arg; char *backup_dir; /* For RESTORE/BACKUP */ char* to_log; /* For PURGE MASTER LOGS TO */ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 06419010a24..ed4fefe1e19 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -39,6 +39,7 @@ "FUNCTION" : "PROCEDURE") static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables); +static bool check_show_create_table_access(THD *thd, TABLE_LIST *table); const char *any_db="*any*"; // Special symbol for check_access @@ -2240,9 +2241,9 @@ mysql_execute_command(THD *thd) if (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) thd->options|= OPTION_KEEP_LOG; /* regular create */ - if (lex->like_name) - res= mysql_create_like_table(thd, create_table, &lex->create_info, - lex->like_name); + if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE) + res= mysql_create_like_table(thd, create_table, select_tables, + &lex->create_info); else { res= mysql_create_table(thd, create_table->db, @@ -2432,12 +2433,7 @@ end_with_restore_list: /* Ignore temporary tables if this is "SHOW CREATE VIEW" */ if (lex->only_view) first_table->skip_temporary= 1; - - if (check_access(thd, SELECT_ACL | EXTRA_ACL, first_table->db, - &first_table->grant.privilege, 0, 0, - test(first_table->schema_table))) - goto error; - if (grant_option && check_grant(thd, SELECT_ACL, all_tables, 2, UINT_MAX, 0)) + if (check_show_create_table_access(thd, first_table)) goto error; res= mysqld_show_create(thd, first_table); break; @@ -6854,6 +6850,25 @@ bool insert_precheck(THD *thd, TABLE_LIST *tables) } +/** + @brief Check privileges for SHOW CREATE TABLE statement. + + @param thd Thread context + @param table Target table + + @retval TRUE Failure + @retval FALSE Success +*/ + +static bool check_show_create_table_access(THD *thd, TABLE_LIST *table) +{ + return check_access(thd, SELECT_ACL | EXTRA_ACL, table->db, + &table->grant.privilege, 0, 0, + test(table->schema_table)) || + grant_option && check_grant(thd, SELECT_ACL, table, 2, UINT_MAX, 0); +} + + /* CREATE TABLE query pre-check @@ -6919,6 +6934,11 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, if (tables && check_table_access(thd, SELECT_ACL, tables,0)) goto err; } + else if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE) + { + if (check_show_create_table_access(thd, tables)) + goto err; + } error= FALSE; err: diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index e49c2642924..9b929900143 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -3776,20 +3776,15 @@ bool mysql_unpack_partition(THD *thd, ha_legacy_type(default_db_type))); if (is_create_table_ind && old_lex->sql_command == SQLCOM_CREATE_TABLE) { - if (old_lex->like_name) + if (old_lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE) { /* - This code is executed when we do a CREATE TABLE t1 LIKE t2 - old_lex->like_name contains the t2 and the table we are opening has - name t1. + This code is executed when we create table in CREATE TABLE t1 LIKE t2. + old_lex->query_tables contains table list element for t2 and the table + we are opening has name t1. */ - Table_ident *table_ident= old_lex->like_name; - char *src_db= table_ident->db.str ? table_ident->db.str : thd->db; - char *src_table= table_ident->table.str; - char buf[FN_REFLEN]; - build_table_filename(buf, sizeof(buf), src_db, src_table, "", 0); - if (partition_default_handling(table, part_info, - FALSE, buf)) + if (partition_default_handling(table, part_info, FALSE, + old_lex->query_tables->table->s->path.str)) { result= TRUE; goto end; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 149c746a1de..2727b014db0 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4665,114 +4665,51 @@ bool mysql_preload_keys(THD* thd, TABLE_LIST* tables) SYNOPSIS mysql_create_like_table() thd Thread object - table Table list (one table only) + table Table list element for target table + src_table Table list element for source table create_info Create info - table_ident Src table_ident RETURN VALUES FALSE OK TRUE error */ -bool mysql_create_like_table(THD* thd, TABLE_LIST* table, - HA_CREATE_INFO *lex_create_info, - Table_ident *table_ident) +bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, + HA_CREATE_INFO *lex_create_info) { - TABLE *tmp_table, *name_lock= 0; + TABLE *name_lock= 0; char src_path[FN_REFLEN], dst_path[FN_REFLEN]; - char src_table_name_buff[FN_REFLEN], src_db_name_buff[FN_REFLEN]; uint dst_path_length; char *db= table->db; char *table_name= table->table_name; - char *src_db; - char *src_table= table_ident->table.str; int err; bool res= TRUE; - enum legacy_db_type not_used; + uint not_used; HA_CREATE_INFO *create_info; #ifdef WITH_PARTITION_STORAGE_ENGINE char tmp_path[FN_REFLEN]; #endif char ts_name[FN_LEN]; - TABLE_LIST src_tables_list; DBUG_ENTER("mysql_create_like_table"); if (!(create_info= copy_create_info(lex_create_info))) { DBUG_RETURN(TRUE); } - DBUG_ASSERT(table_ident->db.str); /* Must be set in the parser */ - src_db= table_ident->db.str; + + /* CREATE TABLE ... LIKE is not allowed for views. */ + src_table->required_type= FRMTYPE_TABLE; /* - Validate the source table + By opening source table we guarantee that it exists and no concurrent + DDL operation will mess with it. Later we also take an exclusive + name-lock on target table name, which makes copying of .frm file, + call to ha_create_table() and binlogging atomic against concurrent DML + and DDL operations on target table. Thus by holding both these "locks" + we ensure that our statement is properly isolated from all concurrent + operations which matter. */ - if (check_string_char_length(&table_ident->table, "", NAME_CHAR_LEN, - system_charset_info, 1) || - (table_ident->table.length && - check_table_name(src_table,table_ident->table.length))) - { - my_error(ER_WRONG_TABLE_NAME, MYF(0), src_table); - DBUG_RETURN(TRUE); - } - if (!src_db || check_db_name(&table_ident->db)) - { - my_error(ER_WRONG_DB_NAME, MYF(0), src_db ? src_db : "NULL"); - DBUG_RETURN(-1); - } - - if ((tmp_table= find_temporary_table(thd, src_db, src_table))) - strxmov(src_path, tmp_table->s->path.str, reg_ext, NullS); - else - { - build_table_filename(src_path, sizeof(src_path), - src_db, src_table, reg_ext, 0); - /* Resolve symlinks (for windows) */ - unpack_filename(src_path, src_path); - if (lower_case_table_names) - my_casedn_str(files_charset_info, src_path); - if (access(src_path, F_OK)) - { - my_error(ER_BAD_TABLE_ERROR, MYF(0), src_table); - goto err; - } - } - - /* - create like should be not allowed for Views, Triggers, ... - */ - if (mysql_frm_type(thd, src_path, ¬_used) != FRMTYPE_TABLE) - { - my_error(ER_WRONG_OBJECT, MYF(0), src_db, src_table, "BASE TABLE"); - goto err; - } - - if (lower_case_table_names) - { - if (src_db) - { - strmake(src_db_name_buff, src_db, - min(sizeof(src_db_name_buff) - 1, table_ident->db.length)); - my_casedn_str(files_charset_info, src_db_name_buff); - src_db= src_db_name_buff; - } - if (src_table) - { - strmake(src_table_name_buff, src_table, - min(sizeof(src_table_name_buff) - 1, table_ident->table.length)); - my_casedn_str(files_charset_info, src_table_name_buff); - src_table= src_table_name_buff; - } - } - - bzero((gptr)&src_tables_list, sizeof(src_tables_list)); - src_tables_list.db= src_db; - src_tables_list.db_length= table_ident->db.length; - src_tables_list.lock_type= TL_READ; - src_tables_list.table_name= src_table; - src_tables_list.alias= src_table; - - if (simple_open_n_lock_tables(thd, &src_tables_list)) + if (open_tables(thd, &src_table, ¬_used, 0)) DBUG_RETURN(TRUE); /* @@ -4781,17 +4718,19 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, Add something to get possible tablespace info from src table, it can get valid tablespace name only for disk-base ndb table */ - if ((src_tables_list.table->file->get_tablespace_name(thd, ts_name, FN_LEN))) + 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; } - /* - Validate the destination table + strxmov(src_path, src_table->table->s->path.str, reg_ext, NullS); - skip the destination table name checking as this is already - validated. + 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) { @@ -4812,15 +4751,29 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, goto table_exists; } + DBUG_EXECUTE_IF("sleep_create_like_before_copy", my_sleep(6000000);); + /* Create a new table by copying from source table + + Altough exclusive name-lock on target table protects us from concurrent + DML and DDL operations on it we still want to wrap .FRM creation and call + to ha_create_table() in critical section protected by LOCK_open in order + to provide minimal atomicity against operations which disregard name-locks, + like I_S implementation, for example. This is a temporary and should not + be copied. Instead we should fix our code to always honor name-locks. + + 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. */ + VOID(pthread_mutex_lock(&LOCK_open)); if (my_copy(src_path, dst_path, MYF(MY_DONT_OVERWRITE_FILE))) { 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); + VOID(pthread_mutex_unlock(&LOCK_open)); goto err; } @@ -4842,10 +4795,12 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, strmov(src_path, tmp_path); my_copy(src_path, dst_path, MYF(MY_DONT_OVERWRITE_FILE)); #endif + + DBUG_EXECUTE_IF("sleep_create_like_before_ha_create", my_sleep(6000000);); + dst_path[dst_path_length - reg_ext_length]= '\0'; // Remove .frm - pthread_mutex_lock(&LOCK_open); err= ha_create_table(thd, dst_path, db, table_name, create_info, 1); - pthread_mutex_unlock(&LOCK_open); + VOID(pthread_mutex_unlock(&LOCK_open)); if (create_info->options & HA_LEX_CREATE_TMP_TABLE) { if (err || !open_temporary_table(thd, dst_path, db, table_name, 1)) @@ -4862,6 +4817,8 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, goto err; /* purecov: inspected */ } + DBUG_EXECUTE_IF("sleep_create_like_before_binlogging", my_sleep(6000000);); + /* We have to write the query before we unlock the tables. */ @@ -4881,14 +4838,10 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, 3 temporary normal Nothing 4 temporary temporary Nothing ==== ========= ========= ============================== - - The variable 'tmp_table' below is used to see if the source - table is a temporary table: if it is set, then the source table - was a temporary table and we can take apropriate actions. */ if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE)) { - if (tmp_table) // Case 2 + if (src_table->table->s->tmp_table) // Case 2 { char buf[2048]; String query(buf, sizeof(buf), system_charset_info); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 38d9663fa5c..93df7db0605 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1579,7 +1579,6 @@ create: lex->create_info.default_table_charset= NULL; lex->name.str= 0; lex->name.length= 0; - lex->like_name= 0; } create2 { @@ -3603,27 +3602,15 @@ create2: create3 {} | LIKE table_ident { - THD *thd= YYTHD; - LEX *lex= thd->lex; - if (!(lex->like_name= $2)) + Lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE; + if (!Lex->select_lex.add_table_to_list(YYTHD, $2, NULL, 0, TL_READ)) MYSQL_YYABORT; - if ($2->db.str == NULL && - thd->copy_db_to(&($2->db.str), &($2->db.length))) - { - MYSQL_YYABORT; - } } | '(' LIKE table_ident ')' { - THD *thd= YYTHD; - LEX *lex= thd->lex; - if (!(lex->like_name= $3)) + Lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE; + if (!Lex->select_lex.add_table_to_list(YYTHD, $3, NULL, 0, TL_READ)) MYSQL_YYABORT; - if ($3->db.str == NULL && - thd->copy_db_to(&($3->db.str), &($3->db.length))) - { - MYSQL_YYABORT; - } } ; @@ -5112,7 +5099,6 @@ alter: lex->key_list.empty(); lex->col_list.empty(); lex->select_lex.init_order(); - lex->like_name= 0; lex->select_lex.db= ((TABLE_LIST*) lex->select_lex.table_list.first)->db; bzero((char*) &lex->create_info,sizeof(lex->create_info)); |