diff --git a/mysql-test/r/create.result b/mysql-test/r/create.result index 0be0d624fca..e1c5bb61da9 100644 --- a/mysql-test/r/create.result +++ b/mysql-test/r/create.result @@ -161,3 +161,63 @@ drop table if exists t1; create table t1 (a int, key(a)); create table t2 (b int, foreign key(b) references t1(a), key(b)); drop table if exists t1,t2; +drop table if exists t1, t2, t3; +create table t1(id int not null, name char(20)); +insert into t1 values(10,'mysql'),(20,'monty- the creator'); +create table t2(id int not null); +insert into t2 values(10),(20); +create table t3 like t1; +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `id` int(11) NOT NULL default '0', + `name` char(20) character set latin1 default NULL +) TYPE=MyISAM CHARSET=latin1 +select * from t3; +id name +create table if not exists t3 like t1; +Warnings: +Warning 1050 Table 't3' already exists +select @@warning_count; +@@warning_count +1 +create temporary table t3 like t2; +show create table t3; +Table Create Table +t3 CREATE TEMPORARY TABLE `t3` ( + `id` int(11) NOT NULL default '0' +) TYPE=MyISAM CHARSET=latin1 +select * from t3; +id +drop table t3; +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `id` int(11) NOT NULL default '0', + `name` char(20) character set latin1 default NULL +) TYPE=MyISAM CHARSET=latin1 +select * from t3; +id name +drop table t3; +create database test_$1; +drop table if exists test_$1.t3; +create table test_$1.t3 like t1; +create temporary table t3 like test_$1.t3; +show create table t3; +Table Create Table +t3 CREATE TEMPORARY TABLE `t3` ( + `id` int(11) NOT NULL default '0', + `name` char(20) character set latin1 default NULL +) TYPE=MyISAM CHARSET=latin1 +create table t3 like t1; +create table t3 like test_$1.t3; +Table 't3' already exists +create table non_existing_database.t1 like t1; +Got one of the listed errors +create table t3 like non_existing_table; +Unknown table 'non_existing_table' +create temporary table t3 like t1; +Table 't3' already exists +drop table t1, t2, t3; +drop table t3; +drop database test_$1; diff --git a/mysql-test/t/create.test b/mysql-test/t/create.test index 3bad053875c..f5f5116b429 100644 --- a/mysql-test/t/create.test +++ b/mysql-test/t/create.test @@ -113,3 +113,40 @@ drop table if exists t1; create table t1 (a int, key(a)); create table t2 (b int, foreign key(b) references t1(a), key(b)); drop table if exists t1,t2; + +# +# Test for CREATE TABLE .. LIKE .. +# + +drop table if exists t1, t2, t3; +create table t1(id int not null, name char(20)); +insert into t1 values(10,'mysql'),(20,'monty- the creator'); +create table t2(id int not null); +insert into t2 values(10),(20); +create table t3 like t1; +show create table t3; +select * from t3; +create table if not exists t3 like t1; +select @@warning_count; +create temporary table t3 like t2; +show create table t3; +select * from t3; +drop table t3; +show create table t3; +select * from t3; +drop table t3; +create database test_$1; +drop table if exists test_$1.t3; +create table test_$1.t3 like t1; +create temporary table t3 like test_$1.t3; +show create table t3; +create table t3 like t1; +!$1050 create table t3 like test_$1.t3; +--error 1044,1 +create table non_existing_database.t1 like t1; +!$1051 create table t3 like non_existing_table; +!$1050 create temporary table t3 like t1; +drop table t1, t2, t3; +drop table t3; +drop database test_$1; + diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index cf0cefd76da..1b0608e5f79 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -422,6 +422,9 @@ int mysql_alter_table(THD *thd, char *new_db, char *new_name, enum enum_duplicates handle_duplicates, enum enum_enable_or_disable keys_onoff=LEAVE_AS_IS, bool simple_alter=0); +int mysql_create_like_table(THD *thd, TABLE_LIST *table, + HA_CREATE_INFO *create_info, + Table_ident *src_table); bool mysql_rename_table(enum db_type base, const char *old_db, const char * old_name, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index f8a3350f051..de976076a33 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1830,10 +1830,14 @@ mysql_execute_command(THD *thd) } else // regular create { - res = mysql_create_table(thd,tables->db ? tables->db : thd->db, - tables->real_name, &lex->create_info, - lex->create_list, - lex->key_list,0,0,0); // do logging + if (lex->name) + res= mysql_create_like_table(thd, tables, &lex->create_info, + (Table_ident *)lex->name); + else + res= mysql_create_table(thd,tables->db ? tables->db : thd->db, + tables->real_name, &lex->create_info, + lex->create_list, + lex->key_list,0,0,0); // do logging if (!res) send_ok(thd); } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 1ab84531e40..7cb1cee7e3c 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1376,6 +1376,125 @@ int mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt) } +/* + Create a table identical to the specified table + + SYNOPSIS + mysql_create_like_table() + thd Thread object + table Table list (one table only) + create_info Create info + table_ident Src table_ident + + RETURN VALUES + 0 ok + -1 error +*/ + +int mysql_create_like_table(THD* thd, TABLE_LIST* table, + HA_CREATE_INFO *create_info, + Table_ident *table_ident) +{ + TABLE **tmp_table; + char src_path[FN_REFLEN], dst_path[FN_REFLEN]; + char *db= table->db; + char *table_name= table->real_name; + char *src_db= thd->db; + char *src_table= table_ident->table.str; + int err; + + DBUG_ENTER("mysql_create_like_table"); + + /* + Validate the source table + */ + if (table_ident->table.length > NAME_LEN || + (table_ident->table.length && + check_table_name(src_table,table_ident->table.length)) || + table_ident->db.str && check_db_name((src_db= table_ident->db.str))) + { + net_printf(thd,ER_WRONG_TABLE_NAME,src_table); + DBUG_RETURN(0); + } + + if ((tmp_table= find_temporary_table(thd, src_db, src_table))) + strxmov(src_path, (*tmp_table)->path, reg_ext, NullS); + else + { + strxmov(src_path, mysql_data_home, "/", src_db, "/", src_table, + reg_ext, NullS); + if (access(src_path, F_OK)) + { + my_error(ER_BAD_TABLE_ERROR, MYF(0), src_table); + DBUG_RETURN(-1); + } + } + + /* + Validate the destination table + + skip the destination table name checking as this is already + validated. + */ + if (create_info->options & HA_LEX_CREATE_TMP_TABLE) + { + if (find_temporary_table(thd, db, table_name)) + goto table_exists; + sprintf(dst_path,"%s%s%lx_%lx_%x%s",mysql_tmpdir,tmp_file_prefix, + current_pid, thd->thread_id, thd->tmp_table++,reg_ext); + create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE; + } + else + { + strxmov(dst_path, mysql_data_home, "/", db, "/", table_name, + reg_ext, NullS); + if (!access(dst_path, F_OK)) + goto table_exists; + } + + /* + Create a new table by copying from source table + */ + if (my_copy(src_path, dst_path, MYF(MY_WME))) + DBUG_RETURN(-1); + + /* + As mysql_truncate don't work on a new table at this stage of + creation, instead create the table directly (for both normal + and temporary tables). + */ + *fn_ext(dst_path)= 0; + err= ha_create_table(dst_path, create_info, 1); + + if (create_info->options & HA_LEX_CREATE_TMP_TABLE) + { + if (err || !open_temporary_table(thd, dst_path, db, table_name, 1)) + { + (void) rm_temporary_table(create_info->db_type, dst_path); + DBUG_RETURN(-1); + } + } + else if (err) + { + (void) quick_rm_table(create_info->db_type, db, table_name); + DBUG_RETURN(-1); + } + DBUG_RETURN(0); + +table_exists: + if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) + { + char warn_buff[MYSQL_ERRMSG_SIZE]; + sprintf(warn_buff,ER(ER_TABLE_EXISTS_ERROR),table_name); + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_TABLE_EXISTS_ERROR,warn_buff); + DBUG_RETURN(0); + } + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name); + DBUG_RETURN(-1); +} + + int mysql_analyze_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt) { #ifdef OS2 diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index e99f2a3b17c..25c9ecb13ee 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -835,6 +835,7 @@ create: lex->create_info.options=$2 | $4; lex->create_info.db_type= (enum db_type) lex->thd->variables.table_type; lex->create_info.table_charset=thd->db_charset?thd->db_charset:default_charset_info; + lex->name=0; } create2 {} @@ -883,7 +884,13 @@ create: create2: '(' field_list ')' opt_create_table_options create3 {} | opt_create_table_options create3 {} - ; + | LIKE table_ident + { + LEX *lex=Lex; + if (!(lex->name= (char *)$2)) + YYABORT; + } + ; create3: /* empty */ {}