summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksey Midenkov <midenok@gmail.com>2023-01-18 16:52:13 +0300
committerAleksey Midenkov <midenok@gmail.com>2023-01-26 17:15:21 +0300
commit39ea12cda76d4430629b247db6b8452fc6b1ebee (patch)
treeead1e4ffd88db9bd04517db5ba59a99511bb3efa
parent870e7d182e443cbeb8814b65f3fe21eea25ff010 (diff)
downloadmariadb-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.result8
-rw-r--r--mysql-test/main/mysqlbinlog_row_minimal.test8
-rw-r--r--sql/sql_table.cc117
-rw-r--r--sql/sql_table.h2
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,