Commit b56ad494 authored by Sergei Golubchik's avatar Sergei Golubchik

MDEV-8287 DROP TABLE suppresses all engine errors

in ha_delete_table()
* only convert ENOENT and HA_ERR_NO_SUCH_TABLE to warnings
* only return real error codes (that is, not ENOENT and
  not HA_ERR_NO_SUCH_TABLE)
* intercept HA_ERR_ROW_IS_REFERENCED to generate backward
  compatible ER_ROW_IS_REFERENCED

in mysql_rm_table_no_locks()
* no special code to handle HA_ERR_ROW_IS_REFERENCED
* no special code to handle ENOENT and HA_ERR_NO_SUCH_TABLE
* return multi-table error ER_BAD_TABLE_ERROR <table list> only
  when there were many errors, not when there were many
  tables to drop (but only one table generated an error)
parent 66fd45af
...@@ -90,7 +90,7 @@ ERROR HY000: Failed to read from the .par file ...@@ -90,7 +90,7 @@ ERROR HY000: Failed to read from the .par file
# Note that it is currently impossible to drop a partitioned table # Note that it is currently impossible to drop a partitioned table
# without the .par file # without the .par file
DROP TABLE t1; DROP TABLE t1;
ERROR 42S02: Unknown table 'test.t1' ERROR HY000: Got error 1 "Operation not permitted" from storage engine partition
# #
# Bug#50392: insert_id is not reset for partitioned tables # Bug#50392: insert_id is not reset for partitioned tables
# auto_increment on duplicate entry # auto_increment on duplicate entry
......
...@@ -11,6 +11,6 @@ t1 ...@@ -11,6 +11,6 @@ t1
SHOW CREATE TABLE t1; SHOW CREATE TABLE t1;
ERROR HY000: Incorrect information in file: './test/t1.frm' ERROR HY000: Incorrect information in file: './test/t1.frm'
DROP TABLE t1; DROP TABLE t1;
ERROR 42S02: Unknown table 'test.t1' ERROR HY000: Got error 1 "Operation not permitted" from storage engine partition
t1.frm t1.frm
t1.par t1.par
...@@ -45,7 +45,7 @@ BEGIN; ...@@ -45,7 +45,7 @@ BEGIN;
CREATE TABLE t2 (a INT) ENGINE=InnoDB; CREATE TABLE t2 (a INT) ENGINE=InnoDB;
ERROR HY000: Can't create table `test`.`t2` (errno: 131 "Command not supported by database") ERROR HY000: Can't create table `test`.`t2` (errno: 131 "Command not supported by database")
DROP TABLE t1; DROP TABLE t1;
ERROR 42S02: Unknown table 'test.t1' ERROR HY000: Storage engine InnoDB of the table `test`.`t1` doesn't have this option
TRUNCATE TABLE t1; TRUNCATE TABLE t1;
ERROR HY000: Got error 131 "Command not supported by database" during COMMIT ERROR HY000: Got error 131 "Command not supported by database" during COMMIT
ALTER TABLE t1 ENGINE=MyISAM; ALTER TABLE t1 ENGINE=MyISAM;
......
...@@ -38,7 +38,7 @@ SET innodb_fake_changes=1; ...@@ -38,7 +38,7 @@ SET innodb_fake_changes=1;
BEGIN; BEGIN;
--error 1005 --error 1005
CREATE TABLE t2 (a INT) ENGINE=InnoDB; CREATE TABLE t2 (a INT) ENGINE=InnoDB;
--error 1051 --error 1031
DROP TABLE t1; DROP TABLE t1;
--error 1180 --error 1180
TRUNCATE TABLE t1; TRUNCATE TABLE t1;
......
...@@ -123,7 +123,7 @@ CHECK TABLE t1; ...@@ -123,7 +123,7 @@ CHECK TABLE t1;
SELECT * FROM t1; SELECT * FROM t1;
--echo # Note that it is currently impossible to drop a partitioned table --echo # Note that it is currently impossible to drop a partitioned table
--echo # without the .par file --echo # without the .par file
--error ER_BAD_TABLE_ERROR --error ER_GET_ERRNO
DROP TABLE t1; DROP TABLE t1;
--remove_file $MYSQLD_DATADIR/test/t1.frm --remove_file $MYSQLD_DATADIR/test/t1.frm
--remove_file $MYSQLD_DATADIR/test/t1#P#p0.MYI --remove_file $MYSQLD_DATADIR/test/t1#P#p0.MYI
......
...@@ -19,7 +19,7 @@ SHOW TABLES; ...@@ -19,7 +19,7 @@ SHOW TABLES;
--replace_result $MYSQLD_DATADIR ./ --replace_result $MYSQLD_DATADIR ./
--error ER_NOT_FORM_FILE --error ER_NOT_FORM_FILE
SHOW CREATE TABLE t1; SHOW CREATE TABLE t1;
--error ER_BAD_TABLE_ERROR --error ER_GET_ERRNO
DROP TABLE t1; DROP TABLE t1;
--list_files $MYSQLD_DATADIR/test t1* --list_files $MYSQLD_DATADIR/test t1*
--remove_file $MYSQLD_DATADIR/test/t1.frm --remove_file $MYSQLD_DATADIR/test/t1.frm
......
...@@ -2298,9 +2298,11 @@ handle_condition(THD *, ...@@ -2298,9 +2298,11 @@ handle_condition(THD *,
} }
/** @brief /** delete a table in the engine
This should return ENOENT if the file doesn't exists.
The .frm file will be deleted only if we return 0 or ENOENT @note
ENOENT and HA_ERR_NO_SUCH_TABLE are not considered errors.
The .frm file will be deleted only if we return 0.
*/ */
int ha_delete_table(THD *thd, handlerton *table_type, const char *path, int ha_delete_table(THD *thd, handlerton *table_type, const char *path,
const char *db, const char *alias, bool generate_warning) const char *db, const char *alias, bool generate_warning)
...@@ -2315,47 +2317,66 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path, ...@@ -2315,47 +2317,66 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path,
/* table_type is NULL in ALTER TABLE when renaming only .frm files */ /* table_type is NULL in ALTER TABLE when renaming only .frm files */
if (table_type == NULL || table_type == view_pseudo_hton || if (table_type == NULL || table_type == view_pseudo_hton ||
! (file=get_new_handler((TABLE_SHARE*)0, thd->mem_root, table_type))) ! (file=get_new_handler((TABLE_SHARE*)0, thd->mem_root, table_type)))
DBUG_RETURN(HA_ERR_NO_SUCH_TABLE); DBUG_RETURN(0);
bzero((char*) &dummy_table, sizeof(dummy_table)); bzero((char*) &dummy_table, sizeof(dummy_table));
bzero((char*) &dummy_share, sizeof(dummy_share)); bzero((char*) &dummy_share, sizeof(dummy_share));
dummy_table.s= &dummy_share; dummy_table.s= &dummy_share;
path= get_canonical_filename(file, path, tmp_path); path= get_canonical_filename(file, path, tmp_path);
if ((error= file->ha_delete_table(path)) && generate_warning) if ((error= file->ha_delete_table(path)))
{ {
/* /*
Because file->print_error() use my_error() to generate the error message it's not an error if the table doesn't exist in the engine.
we use an internal error handler to intercept it and store the text warn the user, but still report DROP being a success
in a temporary buffer. Later the message will be presented to user
as a warning.
*/ */
Ha_delete_table_error_handler ha_delete_table_error_handler; bool intercept= error == ENOENT || error == HA_ERR_NO_SUCH_TABLE;
/* Fill up strucutures that print_error may need */
dummy_share.path.str= (char*) path;
dummy_share.path.length= strlen(path);
dummy_share.normalized_path= dummy_share.path;
dummy_share.db.str= (char*) db;
dummy_share.db.length= strlen(db);
dummy_share.table_name.str= (char*) alias;
dummy_share.table_name.length= strlen(alias);
dummy_table.alias.set(alias, dummy_share.table_name.length,
table_alias_charset);
file->change_table_ptr(&dummy_table, &dummy_share); if (!intercept || generate_warning)
{
thd->push_internal_handler(&ha_delete_table_error_handler); /*
file->print_error(error, 0); Because file->print_error() use my_error() to generate the error message
we use an internal error handler to intercept it and store the text
in a temporary buffer. Later the message will be presented to user
as a warning.
*/
Ha_delete_table_error_handler ha_delete_table_error_handler;
/* Fill up strucutures that print_error may need */
dummy_share.path.str= (char*) path;
dummy_share.path.length= strlen(path);
dummy_share.normalized_path= dummy_share.path;
dummy_share.db.str= (char*) db;
dummy_share.db.length= strlen(db);
dummy_share.table_name.str= (char*) alias;
dummy_share.table_name.length= strlen(alias);
dummy_table.alias.set(alias, dummy_share.table_name.length,
table_alias_charset);
file->change_table_ptr(&dummy_table, &dummy_share);
#if MYSQL_VERSION_ID > 100105
// XXX as an ugly 10.0-only hack we intercept HA_ERR_ROW_IS_REFERENCED,
// to report it under the old historical error number.
#error remove HA_ERR_ROW_IS_REFERENCED, use ME_JUST_WARNING instead of a handler
#endif
if (intercept || error == HA_ERR_ROW_IS_REFERENCED)
thd->push_internal_handler(&ha_delete_table_error_handler);
thd->pop_internal_handler(); file->print_error(error, 0);
/* if (intercept || error == HA_ERR_ROW_IS_REFERENCED)
XXX: should we convert *all* errors to warnings here? {
What if the error is fatal? thd->pop_internal_handler();
*/ if (error == HA_ERR_ROW_IS_REFERENCED)
push_warning(thd, Sql_condition::WARN_LEVEL_WARN, error, my_message(ER_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED), MYF(0));
ha_delete_table_error_handler.buff); else
push_warning(thd, Sql_condition::WARN_LEVEL_WARN, error,
ha_delete_table_error_handler.buff);
}
}
if (intercept)
error= 0;
} }
delete file; delete file;
......
...@@ -2201,15 +2201,13 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, ...@@ -2201,15 +2201,13 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
char path[FN_REFLEN + 1], wrong_tables_buff[160], *alias= NULL; char path[FN_REFLEN + 1], wrong_tables_buff[160], *alias= NULL;
String wrong_tables(wrong_tables_buff, sizeof(wrong_tables_buff)-1, String wrong_tables(wrong_tables_buff, sizeof(wrong_tables_buff)-1,
system_charset_info); system_charset_info);
uint path_length= 0; uint path_length= 0, errors= 0;
int error= 0; int error= 0;
int non_temp_tables_count= 0; int non_temp_tables_count= 0;
bool foreign_key_error=0;
bool non_tmp_error= 0; bool non_tmp_error= 0;
bool trans_tmp_table_deleted= 0, non_trans_tmp_table_deleted= 0; bool trans_tmp_table_deleted= 0, non_trans_tmp_table_deleted= 0;
bool non_tmp_table_deleted= 0; bool non_tmp_table_deleted= 0;
bool is_drop_tmp_if_exists_added= 0; bool is_drop_tmp_if_exists_added= 0;
bool one_table= tables->next_local == 0;
bool was_view= 0; bool was_view= 0;
String built_query; String built_query;
String built_trans_tmp_query, built_non_trans_tmp_query; String built_trans_tmp_query, built_non_trans_tmp_query;
...@@ -2495,12 +2493,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, ...@@ -2495,12 +2493,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
error= ha_delete_table(thd, table_type, path, db, table->table_name, error= ha_delete_table(thd, table_type, path, db, table->table_name,
!dont_log_query); !dont_log_query);
if (error == HA_ERR_ROW_IS_REFERENCED) if (!error)
{
/* the table is referenced by a foreign key constraint */
foreign_key_error= 1;
}
if (!error || error == ENOENT || error == HA_ERR_NO_SUCH_TABLE)
{ {
int frm_delete_error, trigger_drop_error= 0; int frm_delete_error, trigger_drop_error= 0;
/* Delete the table definition file */ /* Delete the table definition file */
...@@ -2518,11 +2511,8 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, ...@@ -2518,11 +2511,8 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
if (trigger_drop_error || if (trigger_drop_error ||
(frm_delete_error && frm_delete_error != ENOENT)) (frm_delete_error && frm_delete_error != ENOENT))
error= 1; error= 1;
else if (!frm_delete_error || !error || if_exists) else if (frm_delete_error && if_exists)
{
error= 0;
thd->clear_error(); thd->clear_error();
}
} }
non_tmp_error= error ? TRUE : non_tmp_error; non_tmp_error= error ? TRUE : non_tmp_error;
} }
...@@ -2533,6 +2523,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, ...@@ -2533,6 +2523,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
wrong_tables.append(db); wrong_tables.append(db);
wrong_tables.append('.'); wrong_tables.append('.');
wrong_tables.append(table->table_name); wrong_tables.append(table->table_name);
errors++;
} }
else else
{ {
...@@ -2556,14 +2547,13 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, ...@@ -2556,14 +2547,13 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
err: err:
if (wrong_tables.length()) if (wrong_tables.length())
{ {
if (one_table && was_view) DBUG_ASSERT(errors);
if (errors == 1 && was_view)
my_printf_error(ER_IT_IS_A_VIEW, ER(ER_IT_IS_A_VIEW), MYF(0), my_printf_error(ER_IT_IS_A_VIEW, ER(ER_IT_IS_A_VIEW), MYF(0),
wrong_tables.c_ptr_safe()); wrong_tables.c_ptr_safe());
else if (!foreign_key_error) else if (errors > 1 || !thd->is_error())
my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0), my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0),
wrong_tables.c_ptr_safe()); wrong_tables.c_ptr_safe());
else
my_message(ER_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED), MYF(0));
error= 1; error= 1;
} }
...@@ -2614,8 +2604,8 @@ err: ...@@ -2614,8 +2604,8 @@ err:
/* Chop of the last comma */ /* Chop of the last comma */
built_query.chop(); built_query.chop();
built_query.append(" /* generated by server */"); built_query.append(" /* generated by server */");
int error_code = (non_tmp_error ? int error_code = non_tmp_error ? thd->get_stmt_da()->sql_errno()
(foreign_key_error ? ER_ROW_IS_REFERENCED : ER_BAD_TABLE_ERROR) : 0); : 0;
error |= thd->binlog_query(THD::STMT_QUERY_TYPE, error |= thd->binlog_query(THD::STMT_QUERY_TYPE,
built_query.ptr(), built_query.ptr(),
built_query.length(), built_query.length(),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment