Commit 5698a6a8 authored by unknown's avatar unknown

5.1 version of fix for:

  Bug #23667 "CREATE TABLE LIKE is not isolated from alteration
              by other connections"
  Bug #18950 "CREATE TABLE LIKE does not obtain LOCK_open"
As well as:
  Bug #25578 "CREATE TABLE LIKE does not require any privileges
              on source table".

The first and the second bugs resulted in various errors and wrong
binary log order when one tried to execute concurrently CREATE TABLE LIKE
statement and DDL statements on source table or DML/DDL statements on its
target table.

The problem was caused by incomplete protection/table-locking against
concurrent statements implemented in mysql_create_like_table() routine.
We solve it by simply implementing such protection in proper way.
Most of actual work for 5.1 was already done by fix for bug 20662 and
preliminary patch changing locking in ALTER TABLE.

The third bug allowed user who didn't have any privileges on table create
its copy and therefore circumvent privilege check for SHOW CREATE TABLE.

This patch solves this problem by adding privilege check, which was missing.

Finally it also removes some duplicated code from mysql_create_like_table()
and thus fixes bug #26869 "TABLE_LIST::table_name_length inconsistent with
TABLE_LIST::table_name".


mysql-test/r/create-big.result:
  Added test coverage for concurrency-related issues with CREATE TABLE LIKE.
mysql-test/r/create.result:
  Adjusted error-code in the test case after refactoring code that
  implements CREATE TABLE ... LIKE.
mysql-test/r/grant2.result:
  Added test for bug#25578 "CREATE TABLE LIKE does not require any privileges
  on source table".
mysql-test/t/create-big.test:
  Added test coverage for concurrency-related issues with CREATE TABLE LIKE.
mysql-test/t/create.test:
  Adjusted error-code in the test case after refactoring code that
  implements CREATE TABLE ... LIKE.
mysql-test/t/disabled.def:
  Recent code changes ensured that CREATE TABLE LIKE statement is properly
  isolated against other statements, so synchronization.test should no
  longer fail (see fix for bug 20662 and preliminary patch for bug 23667
  changing ALTER TABLE locking).
mysql-test/t/grant2.test:
  Added test for bug#25578 "CREATE TABLE LIKE does not require any privileges
  on source table".
sql/handler.h:
  Introduced new flag for HA_CREATE_INFO::options in order to be able to
  distinguish CREATE TABLE ... LIKE from other types of CREATE TABLE.
sql/mysql_priv.h:
  mysql_create_like_table() now takes source table name not as a
  Table_ident object but as regular table list element.
sql/sql_lex.h:
  Removed LEX::like_name member. Now we use special flag in
  LEX::create_info::options for distinguishing CREATE TABLE ... LIKE
  from other types of CREATE TABLE and store name of source table as
  regular element in statement's table list.
sql/sql_parse.cc:
  CREATE TABLE ... LIKE implementation now uses statement's table list
  for storing information about the source table. We also use flag
  in LEX::create_info.options for distinguishing it from other types
  of CREATE TABLE.
  Finally CREATE TABLE ... LIKE now requires the same privileges on
  the source tables as SHOW CREATE TABLE. Moved this privilege check
  to check_show_create_table_access() function.
sql/sql_partition.cc:
  Now we use special flag in LEX::create_info::options for distinguishing
  CREATE TABLE ... LIKE from other types of CREATE TABLE and store name
  of source table as regular element in statement's table list.
sql/sql_table.cc:
  mysql_create_like_table():  
   - Commented and cleaned-up a bit code which is responsible for achieving
     isolation from concurrent statements. Most of actual work was done by
     fix for bug 20662 and preliminary patch changing locking locking in
     ALTER TABLE, so here we do minor things like relaxing locking on
     source table (we don't need lock on it, to have it open is enough) and
     adjusting code to make it more friendly against code implementing I_S.
   - Get rid of duplicated code related to source database/table name
     handling. All these operations are already done in
     st_select_lex::add_table_to_list(), so we achieve the same effect
     by including source table into the statement's table list.
sql/sql_yacc.yy:
  Now we use special flag in LEX::create_info::options for distinguishing
  CREATE TABLE ... LIKE from other types of CREATE TABLE and store name
  of source table as regular element in statement's table list.
parent 72ce840f
...@@ -162,3 +162,86 @@ t1 CREATE TABLE `t1` ( ...@@ -162,3 +162,86 @@ t1 CREATE TABLE `t1` (
`j` int(11) DEFAULT NULL `j` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1, t2, t3; 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";
...@@ -371,7 +371,7 @@ ERROR 42S01: Table 't3' already exists ...@@ -371,7 +371,7 @@ ERROR 42S01: Table 't3' already exists
create table non_existing_database.t1 like t1; create table non_existing_database.t1 like t1;
ERROR 42000: Unknown database 'non_existing_database' ERROR 42000: Unknown database 'non_existing_database'
create table t3 like non_existing_table; 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; create temporary table t3 like t1;
ERROR 42S01: Table 't3' already exists ERROR 42S01: Table 't3' already exists
drop table t1, t2, t3; drop table t1, t2, t3;
......
...@@ -381,3 +381,26 @@ drop table t2; ...@@ -381,3 +381,26 @@ drop table t2;
REVOKE ALL PRIVILEGES, GRANT OPTION FROM `a@`@localhost; REVOKE ALL PRIVILEGES, GRANT OPTION FROM `a@`@localhost;
drop user `a@`@localhost; drop user `a@`@localhost;
SET GLOBAL log_bin_trust_function_creators = 0; 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;
# 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 # Note that we don't test general CREATE TABLE ... SELECT/LIKE functionality
# it is already covered by create.test. We are more interested in extreme cases. # 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 # This test takes rather long time so let us run it only in --big-test mode
--source include/big_test.inc --source include/big_test.inc
# We are using some debug-only features in this test # We are using some debug-only features in this test
--source include/have_debug.inc --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 # Create auxilliary connections
connect (addconroot1, localhost, root,,); connect (addconroot1, localhost, root,,);
...@@ -20,7 +25,7 @@ drop table if exists t1,t2,t3,t4,t5; ...@@ -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 # We introduce delays between various stages of table creation
# and check that other statements dealing with this table cannot # and check that other statements dealing with this table cannot
...@@ -266,3 +271,122 @@ connection default; ...@@ -266,3 +271,122 @@ connection default;
select * from t1; select * from t1;
show create table t1; show create table t1;
drop table t1, t2, t3; 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";
...@@ -306,7 +306,7 @@ create table t3 like t1; ...@@ -306,7 +306,7 @@ create table t3 like t1;
create table t3 like mysqltest.t3; create table t3 like mysqltest.t3;
--error 1049 --error 1049
create table non_existing_database.t1 like t1; create table non_existing_database.t1 like t1;
--error 1051 --error ER_NO_SUCH_TABLE
create table t3 like non_existing_table; create table t3 like non_existing_table;
--error 1050 --error 1050
create temporary table t3 like t1; create temporary table t3 like t1;
......
...@@ -30,7 +30,6 @@ rpl_ddl : BUG#26418 2007-03-01 mleich Slave out of sync after C ...@@ -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_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_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 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 # 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 #ndb_binlog_ddl_multi : BUG#18976 2006-04-10 kent CRBR: multiple binlog, second binlog may miss schema log events
......
...@@ -513,3 +513,44 @@ REVOKE ALL PRIVILEGES, GRANT OPTION FROM `a@`@localhost; ...@@ -513,3 +513,44 @@ REVOKE ALL PRIVILEGES, GRANT OPTION FROM `a@`@localhost;
drop user `a@`@localhost; drop user `a@`@localhost;
SET GLOBAL log_bin_trust_function_creators = 0; 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;
...@@ -223,6 +223,7 @@ ...@@ -223,6 +223,7 @@
#define HA_LEX_CREATE_TMP_TABLE 1 #define HA_LEX_CREATE_TMP_TABLE 1
#define HA_LEX_CREATE_IF_NOT_EXISTS 2 #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_CHECKSUM (1L << 17)
#define HA_OPTION_NO_DELAY_KEY_WRITE (1L << 18) #define HA_OPTION_NO_DELAY_KEY_WRITE (1L << 18)
#define HA_MAX_REC_LENGTH 65535 #define HA_MAX_REC_LENGTH 65535
......
...@@ -978,9 +978,8 @@ bool mysql_alter_table(THD *thd, char *new_db, char *new_name, ...@@ -978,9 +978,8 @@ bool mysql_alter_table(THD *thd, char *new_db, char *new_name,
uint order_num, ORDER *order, bool ignore, uint order_num, ORDER *order, bool ignore,
ALTER_INFO *alter_info, bool do_send_ok); ALTER_INFO *alter_info, bool do_send_ok);
bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, 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, bool mysql_create_like_table(THD *thd, TABLE_LIST *table, TABLE_LIST *src_table,
HA_CREATE_INFO *create_info, HA_CREATE_INFO *create_info);
Table_ident *src_table);
bool mysql_rename_table(handlerton *base, const char *old_db, bool mysql_rename_table(handlerton *base, const char *old_db,
const char * old_name, const char *new_db, const char * old_name, const char *new_db,
const char * new_name, uint flags); const char * new_name, uint flags);
......
...@@ -1066,7 +1066,6 @@ typedef struct st_lex : public Query_tables_list ...@@ -1066,7 +1066,6 @@ typedef struct st_lex : public Query_tables_list
char *length,*dec,*change; char *length,*dec,*change;
LEX_STRING name; LEX_STRING name;
Table_ident *like_name;
char *help_arg; char *help_arg;
char *backup_dir; /* For RESTORE/BACKUP */ char *backup_dir; /* For RESTORE/BACKUP */
char* to_log; /* For PURGE MASTER LOGS TO */ char* to_log; /* For PURGE MASTER LOGS TO */
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
"FUNCTION" : "PROCEDURE") "FUNCTION" : "PROCEDURE")
static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables); 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 const char *any_db="*any*"; // Special symbol for check_access
...@@ -2240,9 +2241,9 @@ mysql_execute_command(THD *thd) ...@@ -2240,9 +2241,9 @@ mysql_execute_command(THD *thd)
if (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) if (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)
thd->options|= OPTION_KEEP_LOG; thd->options|= OPTION_KEEP_LOG;
/* regular create */ /* regular create */
if (lex->like_name) if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE)
res= mysql_create_like_table(thd, create_table, &lex->create_info, res= mysql_create_like_table(thd, create_table, select_tables,
lex->like_name); &lex->create_info);
else else
{ {
res= mysql_create_table(thd, create_table->db, res= mysql_create_table(thd, create_table->db,
...@@ -2432,12 +2433,7 @@ mysql_execute_command(THD *thd) ...@@ -2432,12 +2433,7 @@ mysql_execute_command(THD *thd)
/* Ignore temporary tables if this is "SHOW CREATE VIEW" */ /* Ignore temporary tables if this is "SHOW CREATE VIEW" */
if (lex->only_view) if (lex->only_view)
first_table->skip_temporary= 1; first_table->skip_temporary= 1;
if (check_show_create_table_access(thd, first_table))
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))
goto error; goto error;
res= mysqld_show_create(thd, first_table); res= mysqld_show_create(thd, first_table);
break; break;
...@@ -6854,6 +6850,25 @@ bool insert_precheck(THD *thd, TABLE_LIST *tables) ...@@ -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 CREATE TABLE query pre-check
...@@ -6919,6 +6934,11 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, ...@@ -6919,6 +6934,11 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
if (tables && check_table_access(thd, SELECT_ACL, tables,0)) if (tables && check_table_access(thd, SELECT_ACL, tables,0))
goto err; 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; error= FALSE;
err: err:
......
...@@ -3776,20 +3776,15 @@ bool mysql_unpack_partition(THD *thd, ...@@ -3776,20 +3776,15 @@ bool mysql_unpack_partition(THD *thd,
ha_legacy_type(default_db_type))); ha_legacy_type(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->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 This code is executed when we create table in CREATE TABLE t1 LIKE t2.
old_lex->like_name contains the t2 and the table we are opening has old_lex->query_tables contains table list element for t2 and the table
name t1. we are opening has name t1.
*/ */
Table_ident *table_ident= old_lex->like_name; if (partition_default_handling(table, part_info, FALSE,
char *src_db= table_ident->db.str ? table_ident->db.str : thd->db; old_lex->query_tables->table->s->path.str))
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))
{ {
result= TRUE; result= TRUE;
goto end; goto end;
......
...@@ -4665,114 +4665,51 @@ bool mysql_preload_keys(THD* thd, TABLE_LIST* tables) ...@@ -4665,114 +4665,51 @@ bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
SYNOPSIS SYNOPSIS
mysql_create_like_table() mysql_create_like_table()
thd Thread object 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 create_info Create info
table_ident Src table_ident
RETURN VALUES RETURN VALUES
FALSE OK FALSE OK
TRUE error TRUE error
*/ */
bool mysql_create_like_table(THD* thd, TABLE_LIST* table, bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
HA_CREATE_INFO *lex_create_info, HA_CREATE_INFO *lex_create_info)
Table_ident *table_ident)
{ {
TABLE *tmp_table, *name_lock= 0; TABLE *name_lock= 0;
char src_path[FN_REFLEN], dst_path[FN_REFLEN]; 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; uint dst_path_length;
char *db= table->db; char *db= table->db;
char *table_name= table->table_name; char *table_name= table->table_name;
char *src_db;
char *src_table= table_ident->table.str;
int err; int err;
bool res= TRUE; bool res= TRUE;
enum legacy_db_type not_used; uint not_used;
HA_CREATE_INFO *create_info; HA_CREATE_INFO *create_info;
#ifdef WITH_PARTITION_STORAGE_ENGINE #ifdef WITH_PARTITION_STORAGE_ENGINE
char tmp_path[FN_REFLEN]; char tmp_path[FN_REFLEN];
#endif #endif
char ts_name[FN_LEN]; char ts_name[FN_LEN];
TABLE_LIST src_tables_list;
DBUG_ENTER("mysql_create_like_table"); DBUG_ENTER("mysql_create_like_table");
if (!(create_info= copy_create_info(lex_create_info))) if (!(create_info= copy_create_info(lex_create_info)))
{ {
DBUG_RETURN(TRUE); 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, if (open_tables(thd, &src_table, &not_used, 0))
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, &not_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))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
/* /*
...@@ -4781,17 +4718,19 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, ...@@ -4781,17 +4718,19 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
Add something to get possible tablespace info from src table, Add something to get possible tablespace info from src table,
it can get valid tablespace name only for disk-base ndb 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->tablespace= ts_name;
create_info->storage_media= HA_SM_DISK; create_info->storage_media= HA_SM_DISK;
} }
/* strxmov(src_path, src_table->table->s->path.str, reg_ext, NullS);
Validate the destination table
skip the destination table name checking as this is already DBUG_EXECUTE_IF("sleep_create_like_before_check_if_exists", my_sleep(6000000););
validated.
/*
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 (create_info->options & HA_LEX_CREATE_TMP_TABLE)
{ {
...@@ -4812,15 +4751,29 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, ...@@ -4812,15 +4751,29 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
goto table_exists; goto table_exists;
} }
DBUG_EXECUTE_IF("sleep_create_like_before_copy", my_sleep(6000000););
/* /*
Create a new table by copying from source table 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_copy(src_path, dst_path, MYF(MY_DONT_OVERWRITE_FILE)))
{ {
if (my_errno == ENOENT) if (my_errno == ENOENT)
my_error(ER_BAD_DB_ERROR,MYF(0),db); my_error(ER_BAD_DB_ERROR,MYF(0),db);
else else
my_error(ER_CANT_CREATE_FILE,MYF(0),dst_path,my_errno); my_error(ER_CANT_CREATE_FILE,MYF(0),dst_path,my_errno);
VOID(pthread_mutex_unlock(&LOCK_open));
goto err; goto err;
} }
...@@ -4842,10 +4795,12 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, ...@@ -4842,10 +4795,12 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
strmov(src_path, tmp_path); strmov(src_path, tmp_path);
my_copy(src_path, dst_path, MYF(MY_DONT_OVERWRITE_FILE)); 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););
dst_path[dst_path_length - reg_ext_length]= '\0'; // Remove .frm 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); 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 (create_info->options & HA_LEX_CREATE_TMP_TABLE)
{ {
if (err || !open_temporary_table(thd, dst_path, db, table_name, 1)) 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, ...@@ -4862,6 +4817,8 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
goto err; /* purecov: inspected */ 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. 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, ...@@ -4881,14 +4838,10 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
3 temporary normal Nothing 3 temporary normal Nothing
4 temporary temporary 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 (!(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]; char buf[2048];
String query(buf, sizeof(buf), system_charset_info); String query(buf, sizeof(buf), system_charset_info);
......
...@@ -1579,7 +1579,6 @@ create: ...@@ -1579,7 +1579,6 @@ create:
lex->create_info.default_table_charset= NULL; lex->create_info.default_table_charset= NULL;
lex->name.str= 0; lex->name.str= 0;
lex->name.length= 0; lex->name.length= 0;
lex->like_name= 0;
} }
create2 create2
{ {
...@@ -3603,27 +3602,15 @@ create2: ...@@ -3603,27 +3602,15 @@ create2:
create3 {} create3 {}
| LIKE table_ident | LIKE table_ident
{ {
THD *thd= YYTHD; Lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE;
LEX *lex= thd->lex; if (!Lex->select_lex.add_table_to_list(YYTHD, $2, NULL, 0, TL_READ))
if (!(lex->like_name= $2))
MYSQL_YYABORT; MYSQL_YYABORT;
if ($2->db.str == NULL &&
thd->copy_db_to(&($2->db.str), &($2->db.length)))
{
MYSQL_YYABORT;
}
} }
| '(' LIKE table_ident ')' | '(' LIKE table_ident ')'
{ {
THD *thd= YYTHD; Lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE;
LEX *lex= thd->lex; if (!Lex->select_lex.add_table_to_list(YYTHD, $3, NULL, 0, TL_READ))
if (!(lex->like_name= $3))
MYSQL_YYABORT; MYSQL_YYABORT;
if ($3->db.str == NULL &&
thd->copy_db_to(&($3->db.str), &($3->db.length)))
{
MYSQL_YYABORT;
}
} }
; ;
...@@ -5112,7 +5099,6 @@ alter: ...@@ -5112,7 +5099,6 @@ alter:
lex->key_list.empty(); lex->key_list.empty();
lex->col_list.empty(); lex->col_list.empty();
lex->select_lex.init_order(); lex->select_lex.init_order();
lex->like_name= 0;
lex->select_lex.db= lex->select_lex.db=
((TABLE_LIST*) lex->select_lex.table_list.first)->db; ((TABLE_LIST*) lex->select_lex.table_list.first)->db;
bzero((char*) &lex->create_info,sizeof(lex->create_info)); bzero((char*) &lex->create_info,sizeof(lex->create_info));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment