diff --git a/mysql-test/r/grant2.result b/mysql-test/r/grant2.result index ff9b7bc6f1f..93098e68070 100644 --- a/mysql-test/r/grant2.result +++ b/mysql-test/r/grant2.result @@ -380,3 +380,27 @@ drop function f2; drop table t2; REVOKE ALL PRIVILEGES, GRANT OPTION FROM `a@`@localhost; drop user `a@`@localhost; +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; +End of 5.0 tests diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index bdcb51c4db8..7a2f812cde4 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -1030,7 +1030,7 @@ select bug12329(); bug12329() 101 execute stmt1; -ERROR HY000: Table 't2' was not locked with LOCK TABLES +ERROR 42S02: Table 'test.t2' doesn't exist deallocate prepare stmt1; drop function bug12329; drop table t1, t2; @@ -1152,12 +1152,12 @@ create trigger t1_ai after insert on t1 for each row insert into t2 values (new. create view v1 as select * from t1; drop table t2; insert into v1 values (1); -ERROR HY000: Table 't2' was not locked with LOCK TABLES +ERROR 42S02: Table 'test.t2' doesn't exist drop trigger t1_ai; create function bug11555_1() returns int return (select max(i) from t2); create trigger t1_ai after insert on t1 for each row set @a:=bug11555_1(); insert into v1 values (2); -ERROR HY000: Table 't2' was not locked with LOCK TABLES +ERROR 42S02: Table 'test.t2' doesn't exist drop function bug11555_1; drop table t1; drop view v1; diff --git a/mysql-test/r/sp-prelocking.result b/mysql-test/r/sp-prelocking.result index 7d8dd862748..5eac54803f0 100644 --- a/mysql-test/r/sp-prelocking.result +++ b/mysql-test/r/sp-prelocking.result @@ -254,4 +254,17 @@ execute stmt; deallocate prepare stmt; drop function bug19634; drop table t1, t2, t3; +drop table if exists bug_27907_logs; +drop table if exists bug_27907_t1; +create table bug_27907_logs (a int); +create table bug_27907_t1 (a int); +create trigger bug_27907_t1_ai after insert on bug_27907_t1 +for each row +begin +insert into bug_27907_logs (a) values (1); +end| +drop table bug_27907_logs; +insert into bug_27907_t1(a) values (1); +ERROR 42S02: Table 'test.bug_27907_logs' doesn't exist +drop table bug_27907_t1; End of 5.0 tests diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index 3e6a901dc00..196a990c673 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -820,9 +820,9 @@ call p1(); drop trigger t1_bi; create trigger t1_bi after insert on t1 for each row insert into t3 values (new.id); execute stmt1; -ERROR HY000: Table 't3' was not locked with LOCK TABLES +ERROR 42S02: Table 'test.t3' doesn't exist call p1(); -ERROR HY000: Table 't3' was not locked with LOCK TABLES +ERROR 42S02: Table 'test.t3' doesn't exist deallocate prepare stmt1; drop procedure p1; drop table t1, t2, t3; diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index 882ff669d8c..9d6a9b57e9a 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -13,3 +13,6 @@ user_limits : Bug#23921 random failure of user_limits.test im_life_cycle : Bug#27851: Instance manager test im_life_cycle fails randomly im_daemon_life_cycle : Bug#20294: Instance manager tests fail randomly +im_options_set : Bug#20294: Instance manager tests fail randomly +im_options_unset : Bug#20294: Instance manager tests fail randomly +im_utils : Bug#20294: Instance manager tests fail randomly diff --git a/mysql-test/t/grant2.test b/mysql-test/t/grant2.test index d08a9e3f83d..4a3324b1833 100644 --- a/mysql-test/t/grant2.test +++ b/mysql-test/t/grant2.test @@ -513,3 +513,47 @@ disconnect bug13310; connection default; REVOKE ALL PRIVILEGES, GRANT OPTION FROM `a@`@localhost; drop user `a@`@localhost; + + +# +# 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; + +--echo End of 5.0 tests + diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index 9e5c795d586..ec91be13ba0 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -1454,7 +1454,7 @@ select bug12329(); # Until we implement proper mechanism for invalidation of PS/SP when table # or SP's are changed the following statement will fail with 'Table ... was # not locked' error (this mechanism should be based on the new TDC). ---error 1100 +--error ER_NO_SUCH_TABLE execute stmt1; deallocate prepare stmt1; drop function bug12329; @@ -1639,13 +1639,13 @@ create trigger t1_ai after insert on t1 for each row insert into t2 values (new. create view v1 as select * from t1; drop table t2; # Limitation, the desired error is ER_VIEW_INVALID ---error ER_TABLE_NOT_LOCKED +--error ER_NO_SUCH_TABLE insert into v1 values (1); drop trigger t1_ai; create function bug11555_1() returns int return (select max(i) from t2); create trigger t1_ai after insert on t1 for each row set @a:=bug11555_1(); # Limitation, the desired error is ER_VIEW_INVALID ---error ER_TABLE_NOT_LOCKED +--error ER_NO_SUCH_TABLE insert into v1 values (2); drop function bug11555_1; drop table t1; diff --git a/mysql-test/t/sp-prelocking.test b/mysql-test/t/sp-prelocking.test index cc3e3b93e06..ec5b7fbad7c 100644 --- a/mysql-test/t/sp-prelocking.test +++ b/mysql-test/t/sp-prelocking.test @@ -301,5 +301,36 @@ deallocate prepare stmt; drop function bug19634; drop table t1, t2, t3; +# +# Bug #27907 Misleading error message when opening/locking tables +# + +--disable_warnings +drop table if exists bug_27907_logs; +drop table if exists bug_27907_t1; +--enable_warnings + +create table bug_27907_logs (a int); +create table bug_27907_t1 (a int); + +delimiter |; + +create trigger bug_27907_t1_ai after insert on bug_27907_t1 +for each row +begin + insert into bug_27907_logs (a) values (1); +end| + +delimiter ;| + +drop table bug_27907_logs; + +# +# was failing before with error ER_NOT_LOCKED +# +--error ER_NO_SUCH_TABLE +insert into bug_27907_t1(a) values (1); + +drop table bug_27907_t1; --echo End of 5.0 tests diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index 82de4dac111..43a582439fb 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -1000,9 +1000,9 @@ create trigger t1_bi after insert on t1 for each row insert into t3 values (new. # Until we implement proper mechanism for invalidation of PS/SP when table # or SP's are changed these two statements will fail with 'Table ... was # not locked' error (this mechanism should be based on the new TDC). ---error 1100 #ER_TABLE_NOT_LOCKED +--error ER_NO_SUCH_TABLE execute stmt1; ---error 1100 #ER_TABLE_NOT_LOCKED +--error ER_NO_SUCH_TABLE call p1(); deallocate prepare stmt1; drop procedure p1; diff --git a/sql/handler.h b/sql/handler.h index 9863d541b5f..a59b7a09740 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -162,6 +162,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 ea472691760..7a307c3be85 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -816,9 +816,8 @@ bool mysql_alter_table(THD *thd, char *new_db, char *new_name, Alter_info *alter_info, uint order_num, ORDER *order, bool ignore); bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list); -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(enum db_type base, const char *old_db, const char * old_name, diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 051bad5b310..761cb4b50e2 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -346,7 +346,15 @@ bool opt_endinfo, using_udf_functions; my_bool locked_in_memory; bool opt_using_transactions, using_update_log; bool volatile abort_loop; -bool volatile shutdown_in_progress, grant_option; +bool volatile shutdown_in_progress; +/** + @brief 'grant_option' is used to indicate if privileges needs + to be checked, in which case the lock, LOCK_grant, is used + to protect access to the grant table. + @note This flag is dropped in 5.1 + @see grant_init() + */ +bool volatile grant_option; my_bool opt_skip_slave_start = 0; // If set, slave is not autostarted my_bool opt_reckless_slave = 0; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 835d8bf038f..bb742198381 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -539,7 +539,7 @@ sp_head::init_strings(THD *thd, LEX *lex) Trim "garbage" at the end. This is sometimes needed with the "/ * ! VERSION... * /" wrapper in dump files. */ - endp= skip_rear_comments((char*) m_body_begin, (char*) endp); + endp= skip_rear_comments(thd->charset(), (char*) m_body_begin, (char*) endp); m_body.length= endp - m_body_begin; m_body.str= strmake_root(root, m_body_begin, m_body.length); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index ebf9385d177..ba6d03d6063 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2995,7 +2995,7 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list, } } } - grant_option=TRUE; + thd->mem_root= old_root; pthread_mutex_unlock(&acl_cache->lock); @@ -3162,7 +3162,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, continue; } } - grant_option=TRUE; + thd->mem_root= old_root; pthread_mutex_unlock(&acl_cache->lock); if (!result && !no_error) @@ -3338,6 +3338,8 @@ my_bool grant_init() delete thd; /* Remember that we don't have a THD */ my_pthread_setspecific_ptr(THR_THD, 0); + /* Set the grant option flag so we will check grants */ + grant_option= TRUE; DBUG_RETURN(return_val); } @@ -3367,7 +3369,6 @@ static my_bool grant_load(TABLE_LIST *tables) THR_MALLOC); DBUG_ENTER("grant_load"); - grant_option = FALSE; (void) hash_init(&column_priv_hash,system_charset_info, 0,0,0, (hash_get_key) get_grant_table, (hash_free_key) free_grant_table,0); @@ -3478,7 +3479,6 @@ static my_bool grant_load(TABLE_LIST *tables) } while (!p_table->file->index_next(p_table->record[0])); } - grant_option= TRUE; return_val=0; // Return ok end_unlock: @@ -3511,7 +3511,6 @@ my_bool grant_reload(THD *thd) { TABLE_LIST tables[3]; HASH old_column_priv_hash, old_proc_priv_hash, old_func_priv_hash; - bool old_grant_option; MEM_ROOT old_mem; my_bool return_val= 1; DBUG_ENTER("grant_reload"); @@ -3541,7 +3540,6 @@ my_bool grant_reload(THD *thd) old_column_priv_hash= column_priv_hash; old_proc_priv_hash= proc_priv_hash; old_func_priv_hash= func_priv_hash; - old_grant_option= grant_option; old_mem= memex; if ((return_val= grant_load(tables))) @@ -3551,7 +3549,6 @@ my_bool grant_reload(THD *thd) column_priv_hash= old_column_priv_hash; /* purecov: deadcode */ proc_priv_hash= old_proc_priv_hash; func_priv_hash= old_func_priv_hash; - grant_option= old_grant_option; /* purecov: deadcode */ memex= old_mem; /* purecov: deadcode */ } else diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 16a88346bb6..98e1f2dacd5 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1669,10 +1669,17 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, VOID(pthread_mutex_unlock(&LOCK_open)); } } - if ((thd->locked_tables) && (thd->locked_tables->lock_count > 0)) - my_error(ER_TABLE_NOT_LOCKED, MYF(0), alias); - else + /* + No table in the locked tables list. In case of explicit LOCK TABLES + this can happen if a user did not include the able into the list. + In case of pre-locked mode locked tables list is generated automatically, + so we may only end up here if the table did not exist when + locked tables list was created. + */ + if (thd->prelocked_mode == PRELOCKED) my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias); + else + my_error(ER_TABLE_NOT_LOCKED, MYF(0), alias); DBUG_RETURN(0); } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 7bcdc499011..cbfba3d4d80 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1098,6 +1098,7 @@ Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root) SYNOPSIS skip_rear_comments() + cs character set begin pointer to the beginning of statement end pointer to the end of statement @@ -1108,10 +1109,11 @@ Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root) Pointer to the last non-comment symbol of the statement. */ -char *skip_rear_comments(char *begin, char *end) +char *skip_rear_comments(CHARSET_INFO *cs, char *begin, char *end) { - while (begin < end && (end[-1] <= ' ' || end[-1] == '*' || - end[-1] == '/' || end[-1] == ';')) + while (begin < end && (end[-1] == '*' || + end[-1] == '/' || end[-1] == ';' || + my_isspace(cs, end[-1]))) end-= 1; return end; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 6c9283126c4..f8405ef14ca 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1271,4 +1271,4 @@ extern void lex_free(void); extern void lex_start(THD *thd); extern void lex_end(LEX *lex); extern int MYSQLlex(void *arg, void *yythd); -extern char *skip_rear_comments(char *begin, char *end); +extern char *skip_rear_comments(CHARSET_INFO *cs, char *begin, char *end); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 7e58b36a939..6277a6c0c10 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -75,6 +75,7 @@ static bool check_db_used(THD *thd,TABLE_LIST *tables); static void remove_escape(char *name); static bool append_file_to_dir(THD *thd, const char **filename_ptr, const char *table_name); +static bool check_show_create_table_access(THD *thd, TABLE_LIST *table); const char *any_db="*any*"; // Special symbol for check_access @@ -3080,9 +3081,9 @@ mysql_execute_command(THD *thd) else { /* regular create */ - if (lex->name) - res= mysql_create_like_table(thd, create_table, &create_info, - (Table_ident *)lex->name); + if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE) + res= mysql_create_like_table(thd, create_table, select_tables, + &create_info); else { res= mysql_create_table(thd, create_table->db, @@ -3319,11 +3320,7 @@ end_with_restore_list: first_table->skip_temporary= 1; if (check_db_used(thd, all_tables) || - 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)) + check_show_create_table_access(thd, first_table)) goto error; res= mysqld_show_create(thd, first_table); break; @@ -7519,6 +7516,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 @@ -7583,6 +7599,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_table.cc b/sql/sql_table.cc index 079cc0d6456..4a9f6074b59 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2718,7 +2718,8 @@ 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 @@ -2727,61 +2728,52 @@ bool mysql_preload_keys(THD* thd, TABLE_LIST* tables) TRUE error */ -bool mysql_create_like_table(THD* thd, TABLE_LIST* table, - HA_CREATE_INFO *create_info, - Table_ident *table_ident) +bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST *src_table, + HA_CREATE_INFO *create_info) { TABLE **tmp_table; char src_path[FN_REFLEN], dst_path[FN_REFLEN]; 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; db_type not_used; - - TABLE_LIST src_tables_list; DBUG_ENTER("mysql_create_like_table"); - DBUG_ASSERT(table_ident->db.str); /* Must be set in the parser */ - src_db= table_ident->db.str; /* - Validate the source table + By taking name-lock on the source table and holding LOCK_open mutex we + ensure that no concurrent DDL operation will mess with this table. Note + that holding only name-lock is not enough for this, because it won't block + other DDL statements that only take name-locks on the table and don't + open it (simple name-locks are not exclusive between each other). + + Unfortunately, simply opening this table is not enough for our purproses, + since in 5.0 ALTER TABLE may change .FRM files on disk even if there are + connections that still have old version of table open. This 'optimization' + was removed in 5.1 so there we open the source table instead of taking + name-lock on it. + + We also have to acquire LOCK_open to make copying of .frm file, call to + ha_create_table() and binlogging atomic against concurrent DML and DDL + operations on the target 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(src_db)) - { - my_error(ER_WRONG_DB_NAME, MYF(0), src_db ? src_db : "NULL"); - DBUG_RETURN(-1); - } - - bzero((gptr)&src_tables_list, sizeof(src_tables_list)); - src_tables_list.db= src_db; - src_tables_list.table_name= src_table; - - if (lock_and_wait_for_table_name(thd, &src_tables_list)) + if (lock_and_wait_for_table_name(thd, src_table)) goto err; - if ((tmp_table= find_temporary_table(thd, src_db, src_table))) + pthread_mutex_lock(&LOCK_open); + + if ((tmp_table= find_temporary_table(thd, src_table->db, + src_table->table_name))) strxmov(src_path, (*tmp_table)->s->path, reg_ext, NullS); else { - strxmov(src_path, mysql_data_home, "/", src_db, "/", src_table, - reg_ext, NullS); + strxmov(src_path, mysql_data_home, "/", src_table->db, "/", + src_table->table_name, reg_ext, NullS); /* Resolve symlinks (for windows) */ fn_format(src_path, src_path, "", "", MYF(MY_UNPACK_FILENAME)); - 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); + my_error(ER_BAD_TABLE_ERROR, MYF(0), src_table->table_name); goto err; } } @@ -2791,10 +2783,13 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, */ if (mysql_frm_type(thd, src_path, ¬_used) != FRMTYPE_TABLE) { - my_error(ER_WRONG_OBJECT, MYF(0), src_db, src_table, "BASE TABLE"); + my_error(ER_WRONG_OBJECT, MYF(0), src_table->db, src_table->table_name, + "BASE TABLE"); goto err; } + DBUG_EXECUTE_IF("sleep_create_like_before_check_if_exists", my_sleep(6000000);); + /* Validate the destination table @@ -2810,27 +2805,22 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, } else { - bool exists; strxmov(dst_path, mysql_data_home, "/", db, "/", table_name, reg_ext, NullS); fn_format(dst_path, dst_path, "", "", MYF(MY_UNPACK_FILENAME)); /* - Note that this critical section should actually cover most - of mysql_create_like_table() function. See bugs #18950 and - #23667 for more information. - Also note that starting from 5.1 we obtain name-lock on - target table instead of inspecting table cache for presence + Note that starting from 5.1 we obtain name-lock on target + table instead of inspecting table cache for presence of open placeholders (see comment in mysql_create_table()). */ - pthread_mutex_lock(&LOCK_open); - exists= (table_cache_has_open_placeholder(thd, db, table_name) || - !access(dst_path, F_OK)); - pthread_mutex_unlock(&LOCK_open); - if (exists) + if (table_cache_has_open_placeholder(thd, db, table_name) || + !access(dst_path, F_OK)) goto table_exists; } + DBUG_EXECUTE_IF("sleep_create_like_before_copy", my_sleep(6000000);); + /* Create a new table by copying from source table */ @@ -2843,6 +2833,8 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, goto err; } + DBUG_EXECUTE_IF("sleep_create_like_before_ha_create", my_sleep(6000000);); + /* As mysql_truncate don't work on a new table at this stage of creation, instead create the table directly (for both normal @@ -2867,6 +2859,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);); + // Must be written before unlock if (mysql_bin_log.is_open()) { @@ -2891,8 +2885,7 @@ table_exists: my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name); err: - pthread_mutex_lock(&LOCK_open); - unlock_table_name(thd, &src_tables_list); + unlock_table_name(thd, src_table); pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(res); } diff --git a/sql/sql_view.cc b/sql/sql_view.cc index ba367040b36..bfa799ff289 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -772,7 +772,8 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, view->query.str= (char*)str.ptr(); view->query.length= str.length()-1; // we do not need last \0 view->source.str= thd->query + thd->lex->create_view_select_start; - view->source.length= (char *)skip_rear_comments((char *)view->source.str, + view->source.length= (char *)skip_rear_comments(thd->charset(), + (char *)view->source.str, (char *)thd->query + thd->query_length) - view->source.str; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index c1da870960b..b970bcaedd6 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1483,7 +1483,6 @@ create: lex->create_info.options=$2 | $4; lex->create_info.db_type= (enum db_type) lex->thd->variables.table_type; lex->create_info.default_table_charset= NULL; - lex->name=0; } create2 { Lex->current_select= &Lex->select_lex; } @@ -2763,27 +2762,15 @@ create2: | opt_create_table_options create3 {} | LIKE table_ident { - LEX *lex=Lex; - THD *thd= lex->thd; - if (!(lex->name= (char *)$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 ')' { - LEX *lex=Lex; - THD *thd= lex->thd; - if (!(lex->name= (char *)$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; - } } ; diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 38346ad0dbc..e56dd693287 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -15560,6 +15560,69 @@ static void test_bug24179() } +/* + Bug#27876 (SF with cyrillic variable name fails during execution (regression)) +*/ +static void test_bug27876() +{ + int rc; + MYSQL_RES *result; + + char utf8_func[] = + { + 0xd1, 0x84, 0xd1, 0x83, 0xd0, 0xbd, 0xd0, 0xba, + 0xd1, 0x86, 0xd0, 0xb8, 0xd0, 0xb9, 0xd0, 0xba, + 0xd0, 0xb0, + 0x00 + }; + + char utf8_param[] = + { + 0xd0, 0xbf, 0xd0, 0xb0, 0xd1, 0x80, 0xd0, 0xb0, + 0xd0, 0xbc, 0xd0, 0xb5, 0xd1, 0x82, 0xd1, 0x8a, + 0xd1, 0x80, 0x5f, 0xd0, 0xb2, 0xd0, 0xb5, 0xd1, + 0x80, 0xd1, 0x81, 0xd0, 0xb8, 0xd1, 0x8f, + 0x00 + }; + + char query[500]; + + DBUG_ENTER("test_bug27876"); + myheader("test_bug27876"); + + rc= mysql_query(mysql, "set names utf8"); + myquery(rc); + + rc= mysql_query(mysql, "select version()"); + myquery(rc); + result= mysql_store_result(mysql); + mytest(result); + + sprintf(query, "DROP FUNCTION IF EXISTS %s", utf8_func); + rc= mysql_query(mysql, query); + myquery(rc); + + sprintf(query, + "CREATE FUNCTION %s( %s VARCHAR(25))" + " RETURNS VARCHAR(25) DETERMINISTIC RETURN %s", + utf8_func, utf8_param, utf8_param); + rc= mysql_query(mysql, query); + myquery(rc); + sprintf(query, "SELECT %s(VERSION())", utf8_func); + rc= mysql_query(mysql, query); + myquery(rc); + result= mysql_store_result(mysql); + mytest(result); + + sprintf(query, "DROP FUNCTION %s", utf8_func); + rc= mysql_query(mysql, query); + myquery(rc); + + rc= mysql_query(mysql, "set names default"); + myquery(rc); +} + + /* Read and parse arguments and MySQL options from my.cnf */ @@ -15840,6 +15903,7 @@ static struct my_tests_st my_tests[]= { { "test_bug23383", test_bug23383 }, { "test_bug21635", test_bug21635 }, { "test_bug24179", test_bug24179 }, + { "test_bug27876", test_bug27876 }, { 0, 0 } };