diff options
author | Aleksey Midenkov <midenok@gmail.com> | 2023-01-18 16:52:13 +0300 |
---|---|---|
committer | Aleksey Midenkov <midenok@gmail.com> | 2023-01-26 17:15:21 +0300 |
commit | 39ea12cda76d4430629b247db6b8452fc6b1ebee (patch) | |
tree | ead1e4ffd88db9bd04517db5ba59a99511bb3efa | |
parent | 870e7d182e443cbeb8814b65f3fe21eea25ff010 (diff) | |
download | mariadb-git-39ea12cda76d4430629b247db6b8452fc6b1ebee.tar.gz |
MDEV-29709 Unexpected ER_NO_SUCH_TABLE on CREATE OR REPLACE
with further assertion failures
CREATE .. LIKE generates CREATE TABLE statement (without LIKE). See
comment in mysql_create_like_table() for details:
The logging for CREATE .. LIKE is a bit different from normal
create as we want in statement-based logging use the original
statement.
To generate that statement it opens the table and runs
show_create_table_ex(). For atomic C-O-R it is a bit difficult because
the table is temporary. The original code used a hack to open the
temporary table like a normal table via open_table(). This hack
doesn't work properly as the normal open does name mangling: in
alloc_table_share() we don't pass any flags to
build_table_filename(). That leads to frm not found during the
discovery. The original code also didn't release explicit MDL lock
(the part of the hack) in case of errors.
The current patch replaces that hack with the proper call of
create_and_open_tmp_table(). For that we must know frm and path which
we pass back from mysql_create_table_no_lock(). That required a little
modification of its interface.
-rw-r--r-- | mysql-test/main/mysqlbinlog_row_minimal.result | 8 | ||||
-rw-r--r-- | mysql-test/main/mysqlbinlog_row_minimal.test | 8 | ||||
-rw-r--r-- | sql/sql_table.cc | 117 | ||||
-rw-r--r-- | sql/sql_table.h | 2 |
4 files changed, 86 insertions, 49 deletions
diff --git a/mysql-test/main/mysqlbinlog_row_minimal.result b/mysql-test/main/mysqlbinlog_row_minimal.result index d7bbb3d45fe..6720b9b9716 100644 --- a/mysql-test/main/mysqlbinlog_row_minimal.result +++ b/mysql-test/main/mysqlbinlog_row_minimal.result @@ -412,3 +412,11 @@ ROLLBACK /* added by mysqlbinlog */; /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/; DROP TABLE t1,t2; +# +# MDEV-29709 Unexpected ER_NO_SUCH_TABLE on CREATE OR REPLACE +# with further assertion failures +# +create temporary table tmp (a int); +create or replace table t_apparently_it_has_to_be_at_least_this_long like tmp; +drop table t_apparently_it_has_to_be_at_least_this_long; +drop temporary table tmp; diff --git a/mysql-test/main/mysqlbinlog_row_minimal.test b/mysql-test/main/mysqlbinlog_row_minimal.test index 67fa7b94e6d..e7eba9413a2 100644 --- a/mysql-test/main/mysqlbinlog_row_minimal.test +++ b/mysql-test/main/mysqlbinlog_row_minimal.test @@ -100,3 +100,11 @@ FLUSH BINARY LOGS; DROP TABLE t1,t2; +--echo # +--echo # MDEV-29709 Unexpected ER_NO_SUCH_TABLE on CREATE OR REPLACE +--echo # with further assertion failures +--echo # +create temporary table tmp (a int); +create or replace table t_apparently_it_has_to_be_at_least_this_long like tmp; +drop table t_apparently_it_has_to_be_at_least_this_long; +drop temporary table tmp; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 4502ed3d20b..58e4ced0970 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -5051,7 +5051,16 @@ warn: or any positive number (for C_CREATE_SELECT). If set to C_ALTER_TABLE_FRM_ONY then no frm or table is created, only the frm image in memory. - @param[out] frm The frm image. + @param[out] frm The FRM image stored into allocated buffer. This + buffer must be freed by the caller. If pointer is + NULL nothing is returned. The FRM file created by + the function is binary equal to this image. + @param[in,out] path_out Path to FRM file created by the function without + .frm extension. If NULL nothing is returned. + Otherwise on input LEX_CSTRING must contain a + pointer to the valid buffer and its length. On + output the path is written to this buffer and the + length is updated. @result 1 unspecifed error @@ -5059,7 +5068,9 @@ warn: 0 ok -1 Table was used with IF NOT EXISTS and table existed (warning, not error) - TODO: orig_db, orig_table_name, db, table_name should be moved to create_info + TODO: input data: orig_db, orig_table_name, db, table_name + and the output data: frm, path_out should be passed via Alter_ctx. + That is already done as part of MDEV-20865 for 10.11. */ int mysql_create_table_no_lock(THD *thd, const LEX_CSTRING *orig_db, @@ -5069,13 +5080,14 @@ int mysql_create_table_no_lock(THD *thd, const LEX_CSTRING *orig_db, Table_specification_st *create_info, Alter_info *alter_info, bool *is_trans, int create_table_mode, TABLE_LIST *table_list, - LEX_CUSTRING *frm) + LEX_CUSTRING *frm, LEX_CSTRING *path_out) { KEY *not_used_1; uint not_used_2; int res; uint path_length; - char path[FN_REFLEN + 1]; + char path_local[FN_REFLEN + 1]; + char *path; LEX_CSTRING cpath; LEX_CUSTRING frm_local; @@ -5086,25 +5098,30 @@ int mysql_create_table_no_lock(THD *thd, const LEX_CSTRING *orig_db, frm= &frm_local; } + path= path_out ? const_cast<char *>(path_out->str) : path_local; + constexpr size_t max_path= sizeof(path_local); + DBUG_ASSERT(!path_out || path_out->length >= max_path); DBUG_ASSERT(create_info->default_table_charset); if (create_info->tmp_table()) - path_length= build_tmptable_filename(thd, path, sizeof(path)); + path_length= build_tmptable_filename(thd, path, max_path); else { const LEX_CSTRING *alias= table_case_name(create_info, table_name); uint flags= (create_info->options & HA_CREATE_TMP_ALTER) ? FN_IS_TMP : 0; - path_length= build_table_filename(path, sizeof(path) - 1, db->str, + path_length= build_table_filename(path, max_path - 1, db->str, alias->str, "", flags); // Check if we hit FN_REFLEN bytes along with file extension. if (path_length+reg_ext_length > FN_REFLEN) { - my_error(ER_IDENT_CAUSES_TOO_LONG_PATH, MYF(0), (int) sizeof(path)-1, + my_error(ER_IDENT_CAUSES_TOO_LONG_PATH, MYF(0), (int) max_path - 1, path); return true; } } lex_string_set3(&cpath, path, path_length); + if (path_out) + path_out->length= path_length; res= create_table_impl(thd, *orig_db, *orig_table_name, *db, *table_name, cpath, *create_info, create_info, alter_info, @@ -5652,6 +5669,9 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST *orig_table= table; const bool atomic_replace= create_info->is_atomic_replace(); int create_table_mode= C_ORDINARY_CREATE; + LEX_CUSTRING frm= { NULL, 0 }; + char path_buf[FN_REFLEN + 1]; + LEX_CSTRING path= { path_buf, sizeof(path_buf) }; DBUG_ENTER("mysql_create_like_table"); bzero(&ddl_log_state_create, sizeof(ddl_log_state_create)); @@ -5782,7 +5802,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, &table->db, &table->table_name, &local_create_info, &local_alter_info, &is_trans, create_table_mode, - table)) > 0); + table, &frm, &path)) > 0); /* Remember to log if we deleted something */ do_logging= thd->log_current_statement(); if (res) @@ -5875,44 +5895,39 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, } if (!table->table) { - TABLE_LIST::enum_open_strategy save_open_strategy; - int open_res; /* Force the newly created table to be opened */ - save_open_strategy= table->open_strategy; - table->open_strategy= TABLE_LIST::OPEN_NORMAL; if (atomic_replace) { - /* - NOTE: We acquire explicit lock for temporary table just to make - close_thread_table() happy. We open it like a normal table - because it's too complex to open it like tmp_table here. - */ - table->mdl_request.duration= MDL_EXPLICIT; - if (thd->mdl_context.acquire_lock(&table->mdl_request, - thd->variables.lock_wait_timeout)) + table->table= thd->create_and_open_tmp_table(&frm, path.str, table->db.str, + table->table_name.str, false); + if (!table->table) { res= 1; goto err; } table->table->pos_in_table_list= table; } - - /* - In order for show_create_table() to work we need to open - destination table if it is not already open (i.e. if it - has not existed before). We don't need acquire metadata - lock in order to do this as we already hold exclusive - lock on this table. The table will be closed by - close_thread_table() at the end of this branch. - */ - open_res= open_table(thd, table, &ot_ctx); - /* Restore */ - table->open_strategy= save_open_strategy; - if (open_res) + else { - res= 1; - goto err; + TABLE_LIST::enum_open_strategy save_open_strategy= table->open_strategy; + table->open_strategy= TABLE_LIST::OPEN_NORMAL; + /* + In order for show_create_table() to work we need to open + destination table if it is not already open (i.e. if it + has not existed before). We don't need acquire metadata + lock in order to do this as we already hold exclusive + lock on this table. The table will be closed by + close_thread_table() at the end of this branch. + */ + int open_res= open_table(thd, table, &ot_ctx); + /* Restore */ + table->open_strategy= save_open_strategy; + if (open_res) + { + res= 1; + goto err; + } } opened_new_table= TRUE; } @@ -5969,20 +5984,25 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, if (opened_new_table) { - DBUG_ASSERT(thd->open_tables == table->table); - /* - When opening the table, we ignored the locked tables - (MYSQL_OPEN_GET_NEW_TABLE). Now we can close the table - without risking to close some locked table. - - For atomic_replace we must remove TABLE and TABLE_SHARE - from cache since they are the objects for temporary table. - */ - if (atomic_replace) - table->table->s->tdc->flushed= true; - close_thread_table(thd, &thd->open_tables); if (atomic_replace) - thd->mdl_context.release_lock(table->mdl_request.ticket); + { + DBUG_ASSERT(table->table->s->tmp_table); + if (thd->drop_temporary_table(table->table, NULL, false)) + { + res= 1; + goto err; + } + } + else + { + DBUG_ASSERT(thd->open_tables == table->table); + /* + When opening the table, we ignored the locked tables + (MYSQL_OPEN_GET_NEW_TABLE). Now we can close the table + without risking to close some locked table. + */ + close_thread_table(thd, &thd->open_tables); + } } } } @@ -6090,6 +6110,7 @@ err_no_atomic: create_info->or_replace()) res|= (int) local_create_info.finalize_locked_tables(thd, res); + my_free((void *) frm.str); DBUG_RETURN(res != 0); } diff --git a/sql/sql_table.h b/sql/sql_table.h index 2cead6fa683..b95e6d6bdb0 100644 --- a/sql/sql_table.h +++ b/sql/sql_table.h @@ -104,7 +104,7 @@ int mysql_create_table_no_lock(THD *thd, Table_specification_st *create_info, Alter_info *alter_info, bool *is_trans, int create_table_mode, TABLE_LIST *table, - LEX_CUSTRING *frm= NULL); + LEX_CUSTRING *frm= NULL, LEX_CSTRING *path= NULL); handler *mysql_create_frm_image(THD *thd, const LEX_CSTRING &db, |