Below is the list of changes that have just been committed into a local
5.1 repository of dlenev. When dlenev does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html
ChangeSet@stripped, 2007-02-01 00:05:29+03:00, dlenev@stripped +14 -0
Tentative fix for bugs #18950 "create table like does not obtain
LOCK_open", #23667 "CREATE TABLE LIKE is not isolated from alteration
by other connections" and bug #25578 "CREATE TABLE LIKE does not
require any privileges on source table".
Concurrent execution of CREATE TABLE LIKE statement and DDL statements
on source table or DML/DDL statements on its target table could result
in various errors in these statements and wrong order of statements in
binlog.
This problems was caused by incomplete protection/table-locking against
concurrent statements implemented in mysql_create_like_table() routine.
This fix simply implements such protection in proper way:
We keep source table open during whole operation and place copying
of .frm file, call to ha_create_table() and writing to binlog into
one critical section protected by LOCK_open mutex. While former
gives us protection against concurrent DDL on source table; the latter
protects from DDL and DML on target table.
It also removes some duplicated code from mysql_create_like_table().
Questions for reviewers are marked by QQ.
mysql-test/r/create.result@stripped, 2007-02-01 00:05:26+03:00, dlenev@stripped +1
-1
Adjusted error-code in the test case after refactoring code that
implements CREATE TABLE ... LIKE.
mysql-test/r/create_like-big.result@stripped, 2007-02-01 00:05:27+03:00,
dlenev@stripped +83 -0
New BitKeeper file ``mysql-test/r/create_like-big.result''
mysql-test/r/create_like-big.result@stripped, 2007-02-01 00:05:27+03:00,
dlenev@stripped +0 -0
mysql-test/r/grant2.result@stripped, 2007-02-01 00:05:26+03:00, dlenev@stripped +23
-0
Added test for bug#25578 "CREATE TABLE LIKE does not require any privileges
on source table"
mysql-test/t/create.test@stripped, 2007-02-01 00:05:26+03:00, dlenev@stripped +1 -1
Adjusted error-code in the test case after refactoring code that
implements CREATE TABLE ... LIKE.
mysql-test/t/create_like-big.test@stripped, 2007-02-01 00:05:27+03:00,
dlenev@stripped +126 -0
New BitKeeper file ``mysql-test/t/create_like-big.test''
mysql-test/t/create_like-big.test@stripped, 2007-02-01 00:05:27+03:00,
dlenev@stripped +0 -0
mysql-test/t/grant2.test@stripped, 2007-02-01 00:05:26+03:00, dlenev@stripped +41 -0
Added test for bug#25578 "CREATE TABLE LIKE does not require any privileges
on source table"
sql/handler.h@stripped, 2007-02-01 00:05:26+03:00, dlenev@stripped +1 -0
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@stripped, 2007-02-01 00:05:26+03:00, dlenev@stripped +3 -3
mysql_create_like_table() now takes source table name not
as Table_ident object but as regular table list element.
Added open_table_under_lock() function to be used in cases
when we need to open table while holding LOCK_open mutex.
sql/sql_base.cc@stripped, 2007-02-01 00:05:26+03:00, dlenev@stripped +102 -35
Added open_table_under_lock() routine to be used in cases when
need to open table while holding LOCK_open mutex.
Common code from open_table(), reopen_name_locked_table() and
open_table_under_lock() responsible for resetting state of
TABLE object was moved to auxiliary routine.
sql/sql_lex.h@stripped, 2007-02-01 00:05:26+03:00, dlenev@stripped +0 -1
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@stripped, 2007-02-01 00:05:26+03:00, dlenev@stripped +34 -9
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@stripped, 2007-02-01 00:05:27+03:00, dlenev@stripped +6 -11
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@stripped, 2007-02-01 00:05:27+03:00, dlenev@stripped +42 -107
Changed mysql_create_like_table() in order to:
- Provide proper protection from concurrent statements.
This is achieved by keeping source table open during whole operation
(this is supposed to give protection against concurrent DDL on it) and
by placing copying of .frm file, call to ha_create_table() and writing
to binlog in one critical section protected by LOCK_open mutex (this
gives protection against any operations on target table).
- 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@stripped, 2007-02-01 00:05:27+03:00, dlenev@stripped +6 -18
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.
# This is a BitKeeper patch. What follows are the unified diffs for the
# set of deltas contained in the patch. The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User: dlenev
# Host: mockturtle.local
# Root: /home/dlenev/src/mysql-5.1-bg23667
--- 1.252/sql/handler.h 2007-02-01 00:05:38 +03:00
+++ 1.253/sql/handler.h 2007-02-01 00:05:38 +03:00
@@ -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
--- 1.466/sql/mysql_priv.h 2007-02-01 00:05:38 +03:00
+++ 1.467/sql/mysql_priv.h 2007-02-01 00:05:38 +03:00
@@ -934,9 +934,8 @@ bool mysql_alter_table(THD *thd, char *n
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);
@@ -981,6 +980,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST
TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT* mem,
bool *refresh, uint flags);
bool reopen_name_locked_table(THD* thd, TABLE_LIST* table);
+TABLE *open_table_under_lock(THD* thd, TABLE_LIST* table_list);
TABLE *find_locked_table(THD *thd, const char *db,const char *table_name);
bool reopen_tables(THD *thd,bool get_locks,bool in_refresh);
bool close_data_tables(THD *thd,const char *db, const char *table_name);
--- 1.370/sql/sql_base.cc 2007-02-01 00:05:38 +03:00
+++ 1.371/sql/sql_base.cc 2007-02-01 00:05:38 +03:00
@@ -1732,6 +1732,51 @@ void wait_for_condition(THD *thd, pthrea
/*
+ Auxiliary routine which resets state of TABLE object during opening of table.
+
+ SYNOPSIS
+ reset_table_for_open()
+ thd Thread object
+ table_list Table list element for table being opened
+ table TABLE object for table being opened
+*/
+
+static void reset_table_for_open(THD *thd, TABLE_LIST *table_list, TABLE *table)
+{
+ char *alias= table_list->alias;
+
+ if (thd->lex->need_correct_ident())
+ table->alias_name_used= my_strcasecmp(table_alias_charset,
+ table->s->table_name.str, alias);
+ /* Fix alias if table name changes */
+ if (strcmp(table->alias, alias))
+ {
+ uint length=(uint) strlen(alias)+1;
+ table->alias= (char*) my_realloc((char*) table->alias, length,
+ MYF(MY_WME));
+ memcpy((char*) table->alias, alias, length);
+ }
+ /* These variables are also set in reopen_table() */
+ table->tablenr= thd->current_tablenr++;
+ table->used_fields= 0;
+ table->const_table= 0;
+ table->null_row= table->maybe_null= table->force_index= 0;
+ table->status= STATUS_NO_RECORD;
+ table->keys_in_use_for_query= table->s->keys_in_use;
+ table->insert_values= 0;
+ table->used_keys= table->s->keys_for_keyread;
+ table->fulltext_searched= 0;
+ table->file->ft_handler= 0;
+ if (table->timestamp_field)
+ table->timestamp_field_type= table->timestamp_field->get_auto_set_type();
+ table->pos_in_table_list= table_list;
+ table_list->updatable= 1; // It is not derived table nor non-updatable VIEW
+ table->clear_column_bitmaps();
+ DBUG_ASSERT(table->key_read == 0);
+}
+
+
+/*
Open table which is already name-locked by this thread.
SYNOPSIS
@@ -1786,18 +1831,67 @@ bool reopen_name_locked_table(THD* thd,
check_unused();
table->next = thd->open_tables;
thd->open_tables = table;
- table->tablenr=thd->current_tablenr++;
- table->used_fields=0;
- table->const_table=0;
- table->null_row= table->maybe_null= table->force_index= 0;
- table->status=STATUS_NO_RECORD;
- table->keys_in_use_for_query= share->keys_in_use;
- table->used_keys= share->keys_for_keyread;
+ reset_table_for_open(thd, table_list, table);
DBUG_RETURN(FALSE);
}
/*
+ Open table while holding LOCK_open mutex.
+
+ SYNOPSIS
+ open_table_under_lock()
+ thd Thread handle
+ table_list TABLE_LIST object for table to be open
+
+ NOTE
+ This function ignores name-locks on the table being opened,
+ so should be used only in circumstances when it is guaranteed
+ that there are no such locks. For example right after creation
+ of table.
+
+ RETURN VALUE
+ non-0 Pointer to TABLE object for table opened
+ 0 Error
+*/
+
+TABLE *open_table_under_lock(THD* thd, TABLE_LIST* table_list)
+{
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length;
+ TABLE *table;
+ TABLE_SHARE *share;
+ char *table_name= table_list->table_name;
+ DBUG_ENTER("open_table_under_lock");
+
+ safe_mutex_assert_owner(&LOCK_open);
+
+ key_length= create_table_def_key(thd, key, table_list, 0);
+
+ if (!(table=(TABLE*) my_malloc(sizeof(*table),MYF(MY_WME))))
+ DBUG_RETURN(NULL);
+
+ if (open_unireg_entry(thd, table, table_list, table_name,
+ key, key_length, thd->mem_root, 0))
+ {
+ my_free((gptr)table, MYF(0));
+ DBUG_RETURN(NULL);
+ }
+
+ share= table->s;
+ share->version=0;
+ share->flush_version=0;
+ table->in_use= thd;
+ check_unused();
+ table->next= thd->open_tables;
+ thd->open_tables= table;
+ VOID(my_hash_insert(&open_cache, (byte*)table));
+ reset_table_for_open(thd, table_list, table);
+ DBUG_RETURN(table);
+}
+
+
+/*
Open a table.
SYNOPSIS
@@ -2097,34 +2191,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *
reset:
DBUG_ASSERT(table->s->ref_count > 0 || table->s->tmp_table !=
NO_TMP_TABLE);
- if (thd->lex->need_correct_ident())
- table->alias_name_used= my_strcasecmp(table_alias_charset,
- table->s->table_name.str, alias);
- /* Fix alias if table name changes */
- if (strcmp(table->alias, alias))
- {
- uint length=(uint) strlen(alias)+1;
- table->alias= (char*) my_realloc((char*) table->alias, length,
- MYF(MY_WME));
- memcpy((char*) table->alias, alias, length);
- }
- /* These variables are also set in reopen_table() */
- table->tablenr=thd->current_tablenr++;
- table->used_fields=0;
- table->const_table=0;
- table->null_row= table->maybe_null= table->force_index= 0;
- table->status=STATUS_NO_RECORD;
- table->keys_in_use_for_query= table->s->keys_in_use;
- table->insert_values= 0;
- table->used_keys= table->s->keys_for_keyread;
- table->fulltext_searched= 0;
- table->file->ft_handler= 0;
- if (table->timestamp_field)
- table->timestamp_field_type= table->timestamp_field->get_auto_set_type();
- table->pos_in_table_list= table_list;
- table_list->updatable= 1; // It is not derived table nor non-updatable VIEW
- table->clear_column_bitmaps();
- DBUG_ASSERT(table->key_read == 0);
+ reset_table_for_open(thd, table_list, table);
DBUG_RETURN(table);
}
--- 1.256/sql/sql_lex.h 2007-02-01 00:05:38 +03:00
+++ 1.257/sql/sql_lex.h 2007-02-01 00:05:38 +03:00
@@ -919,7 +919,6 @@ typedef struct st_lex : public Query_tab
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 */
--- 1.617/sql/sql_parse.cc 2007-02-01 00:05:38 +03:00
+++ 1.618/sql/sql_parse.cc 2007-02-01 00:05:38 +03:00
@@ -63,6 +63,7 @@ static void decrease_user_connections(US
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
static bool check_multi_update_lock(THD *thd);
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
@@ -3073,9 +3074,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,
@@ -3265,12 +3266,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;
@@ -7655,6 +7651,30 @@ bool insert_precheck(THD *thd, TABLE_LIS
/*
+ Check privileges for SHOW CREATE TABLE statement.
+
+ SYNOPSIS
+ check_show_create_table_access()
+ thd Thread context
+ table Target table
+
+ QQ: May be we should merge this routine with check_single_table_access()?
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error
+*/
+
+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
SYNOPSIS
@@ -7717,6 +7737,11 @@ bool create_table_precheck(THD *thd, TAB
}
#endif
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;
--- 1.379/sql/sql_table.cc 2007-02-01 00:05:38 +03:00
+++ 1.380/sql/sql_table.cc 2007-02-01 00:05:38 +03:00
@@ -4587,117 +4587,66 @@ bool mysql_preload_keys(THD* thd, TABLE_
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;
char src_path[FN_REFLEN], dst_path[FN_REFLEN], tmp_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, unlock_dst_table= FALSE;
- enum legacy_db_type not_used;
+ uint not_used;
+ bool res= TRUE;
HA_CREATE_INFO *create_info;
- TABLE_LIST src_tables_list, dst_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
- */
- if (table_ident->table.length > NAME_LEN ||
- (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);
- }
+ By opening source table we guarantee that it exists and no concurrent
+ DDL operation will mess with it.
- 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;
- }
- }
+ QQ: Actually this is not quite TRUE thanks to MyISAM-specific
+ hack in ALTER TABLE... And it seems that it is the reason
+ behind bug#24529. Should we do something about it ?
- /*
- create like should be not allowed for Views, Triggers, ...
+ QQ: May be it is better to use the same logic as was proposed recently for
+ CREATE ... SELECT (i.e. take name-lock on the target table as well)?
+ In this way we won't need open_table_under_lock().
*/
- 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;
- }
- }
+ if (open_tables(thd, &src_table, ¬_used, 0))
+ DBUG_RETURN(TRUE);
- 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;
+ strxmov(src_path, src_table->table->s->path.str, reg_ext, NullS);
- if (simple_open_n_lock_tables(thd, &src_tables_list))
- DBUG_RETURN(TRUE);
+ DBUG_EXECUTE_IF("sleep_create_like_before_check_if_exists", my_sleep(6000000););
/*
- Validate the destination table
+ Copying of .frm file, call to ha_create_table() and binlogging should be
+ atomic against concurrent DML and DDL operations on target table, so we
+ have to put them in the same critical section protected by LOCK_open.
+ */
+ pthread_mutex_lock(&LOCK_open);
- skip the destination table name checking as this is already
- 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)
{
@@ -4716,6 +4665,8 @@ bool mysql_create_like_table(THD* thd, T
goto table_exists;
}
+ DBUG_EXECUTE_IF("sleep_create_like_before_copy", my_sleep(6000000););
+
/*
Create a new table by copying from source table
*/
@@ -4746,10 +4697,11 @@ bool mysql_create_like_table(THD* thd, T
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);
if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
{
if (err || !open_temporary_table(thd, dst_path, db, table_name, 1))
@@ -4766,6 +4718,8 @@ bool mysql_create_like_table(THD* thd, T
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.
*/
@@ -4785,14 +4739,10 @@ bool mysql_create_like_table(THD* thd, T
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);
@@ -4804,21 +4754,11 @@ bool mysql_create_like_table(THD* thd, T
store_create_info() to work. The table will be closed
by close_thread_tables() at the end of the statement.
*/
- if (open_tables(thd, &table, &counter, 0))
- goto err;
-
- bzero((gptr)&dst_tables_list, sizeof(dst_tables_list));
- dst_tables_list.db= table->db;
- dst_tables_list.table_name= table->table_name;
-
- /*
- lock destination table name, to make sure that nobody
- can drop/alter the table while we execute store_create_info()
- */
- if (lock_and_wait_for_table_name(thd, &dst_tables_list))
+ if (!(table->table= open_table_under_lock(thd, table)))
+ {
+ (void) quick_rm_table(create_info->db_type, db, table_name, 0);
goto err;
- else
- unlock_dst_table= TRUE;
+ }
int result= store_create_info(thd, table, &query, create_info);
@@ -4852,12 +4792,7 @@ table_exists:
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
err:
- if (unlock_dst_table)
- {
- pthread_mutex_lock(&LOCK_open);
- unlock_table_name(thd, &dst_tables_list);
- pthread_mutex_unlock(&LOCK_open);
- }
+ pthread_mutex_unlock(&LOCK_open);
DBUG_RETURN(res);
}
--- 1.529/sql/sql_yacc.yy 2007-02-01 00:05:38 +03:00
+++ 1.530/sql/sql_yacc.yy 2007-02-01 00:05:38 +03:00
@@ -1431,7 +1431,6 @@ create:
lex->create_info.default_table_charset= NULL;
lex->name.str= 0;
lex->name.length= 0;
- lex->like_name= 0;
}
create2
{ Lex->current_select= &Lex->select_lex; }
@@ -3438,27 +3437,17 @@ create2:
create3 {}
| LIKE table_ident
{
- LEX *lex=Lex;
- THD *thd= lex->thd;
- if (!(lex->like_name= $2))
- YYABORT;
- if ($2->db.str == NULL &&
- thd->copy_db_to(&($2->db.str), &($2->db.length)))
- {
+ Lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE;
+ if (!Lex->select_lex.add_table_to_list(YYTHD, $2, NULL, 0,
+ TL_READ))
YYABORT;
- }
}
| '(' LIKE table_ident ')'
{
- LEX *lex=Lex;
- THD *thd= lex->thd;
- if (!(lex->like_name= $3))
- YYABORT;
- if ($3->db.str == NULL &&
- thd->copy_db_to(&($3->db.str), &($3->db.length)))
- {
+ Lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE;
+ if (!Lex->select_lex.add_table_to_list(YYTHD, $3, NULL, 0,
+ TL_READ))
YYABORT;
- }
}
;
@@ -4920,7 +4909,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));
--- New file ---
+++ mysql-test/r/create_like-big.result 07/02/01 00:05:27
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 102;
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 102;
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 102;
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 102;
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";
--- New file ---
+++ mysql-test/t/create_like-big.test 07/02/01 00:05:27
# 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"
#
# 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.
#
# QQ: Should we merge this test with test for bug#24738 and co ?
#
--source include/big_test.inc
--source include/have_debug.inc
--source include/have_binlog_format_mixed_or_statement.inc
connect (addconroot1, localhost, root,,);
connection default;
--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 102;
# 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 102;
# 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 102;
# 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 102;
set session debug="-d,sleep_create_like_before_binlogging";
--- 1.129/mysql-test/r/create.result 2007-02-01 00:05:38 +03:00
+++ 1.130/mysql-test/r/create.result 2007-02-01 00:05:38 +03:00
@@ -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;
--- 1.87/mysql-test/t/create.test 2007-02-01 00:05:38 +03:00
+++ 1.88/mysql-test/t/create.test 2007-02-01 00:05:38 +03:00
@@ -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;
--- 1.97/sql/sql_partition.cc 2007-02-01 00:05:38 +03:00
+++ 1.98/sql/sql_partition.cc 2007-02-01 00:05:38 +03:00
@@ -3770,20 +3770,15 @@ bool mysql_unpack_partition(THD *thd, co
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;
--- 1.34/mysql-test/r/grant2.result 2007-02-01 00:05:38 +03:00
+++ 1.35/mysql-test/r/grant2.result 2007-02-01 00:05:38 +03:00
@@ -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;
--- 1.40/mysql-test/t/grant2.test 2007-02-01 00:05:38 +03:00
+++ 1.41/mysql-test/t/grant2.test 2007-02-01 00:05:38 +03:00
@@ -509,3 +509,44 @@ REVOKE ALL PRIVILEGES, GRANT OPTION FROM
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;
| Thread |
|---|
| • bk commit into 5.1 tree (dlenev:1.2379) BUG#25578 | dlenev | 31 Jan |