diff --git a/mysql-test/include/mix1.inc b/mysql-test/include/mix1.inc index e7ea8b3d25a..75ba0e43221 100644 --- a/mysql-test/include/mix1.inc +++ b/mysql-test/include/mix1.inc @@ -630,15 +630,9 @@ copy_file $MYSQLD_DATADIR/test/t1.frm $MYSQLD_DATADIR/test/bug29807.frm; --error ER_NO_SUCH_TABLE_IN_ENGINE select * from bug29807; drop table t1; ---error ER_BAD_TABLE_ERROR drop table bug29807; -create table bug29807 (a int); -drop table bug29807; ---disable_query_log call mtr.add_suppression("InnoDB: Error: table .test...bug29807. does not exist in the InnoDB internal"); call mtr.add_suppression("Cannot find or open table test\/bug29807 from"); -call mtr.add_suppression("Table 'test.bug29807' doesn't exist in engine"); ---enable_query_log # diff --git a/mysql-test/r/plugin.result b/mysql-test/r/plugin.result index 62864d0f16d..ac2ba843131 100644 --- a/mysql-test/r/plugin.result +++ b/mysql-test/r/plugin.result @@ -35,7 +35,9 @@ PLUGIN_LICENSE GPL LOAD_OPTION ON PLUGIN_MATURITY Experimental PLUGIN_AUTH_VERSION 3.14.15.926 -CREATE TABLE t1(a int) ENGINE=EXAMPLE; +CREATE TABLE t1 (a int) ENGINE=EXAMPLE; +CREATE TABLE t2 (a int) ENGINE=EXAMPLE; +FLUSH TABLES; SELECT * FROM t1; a set global example_ulong_var=500; @@ -65,6 +67,10 @@ LOAD_OPTION ON PLUGIN_MATURITY Experimental PLUGIN_AUTH_VERSION 0.1 DROP TABLE t1; +select * from information_schema.plugins where plugin_library like 'ha_example%'; +SELECT * FROM t2; +ERROR 42000: Unknown storage engine 'EXAMPLE' +DROP TABLE t2; UNINSTALL PLUGIN EXAMPLE; ERROR 42000: PLUGIN EXAMPLE does not exist UNINSTALL PLUGIN non_exist; diff --git a/mysql-test/suite/innodb/r/innodb_mysql.result b/mysql-test/suite/innodb/r/innodb_mysql.result index d963c4cc89b..7247b26e86b 100644 --- a/mysql-test/suite/innodb/r/innodb_mysql.result +++ b/mysql-test/suite/innodb/r/innodb_mysql.result @@ -633,9 +633,10 @@ select * from bug29807; ERROR 42S02: Table 'test.bug29807' doesn't exist in engine drop table t1; drop table bug29807; -ERROR 42S02: Unknown table 'bug29807' -create table bug29807 (a int); -drop table bug29807; +Warnings: +Warning 155 Table 'test.bug29807' doesn't exist in engine +call mtr.add_suppression("InnoDB: Error: table .test...bug29807. does not exist in the InnoDB internal"); +call mtr.add_suppression("Cannot find or open table test\/bug29807 from"); CREATE TABLE t1 (a INT) ENGINE=InnoDB; CREATE TABLE t2 (a INT) ENGINE=InnoDB; switch to connection c1 diff --git a/mysql-test/t/plugin.test b/mysql-test/t/plugin.test index 4412383f837..71ca860e269 100644 --- a/mysql-test/t/plugin.test +++ b/mysql-test/t/plugin.test @@ -16,12 +16,13 @@ INSTALL SONAME 'ha_example'; --replace_regex /\.dll/.so/ --query_vertical select * from information_schema.plugins where plugin_library like 'ha_example%' -CREATE TABLE t1(a int) ENGINE=EXAMPLE; +CREATE TABLE t1 (a int) ENGINE=EXAMPLE; +CREATE TABLE t2 (a int) ENGINE=EXAMPLE; +FLUSH TABLES; # Let's do some advanced ops with the example engine :) SELECT * FROM t1; - # a couple of tests for variables set global example_ulong_var=500; set global example_enum_var= e1; @@ -29,12 +30,26 @@ show status like 'example%'; show variables like 'example%'; UNINSTALL SONAME 'ha_example'; + +# the engine is NOT uninstalled yet, +# because the table `t1` is open, sitting in the table defintion cache + --replace_column 5 # --replace_regex /\.dll/.so/ --query_vertical select * from information_schema.plugins where plugin_library like 'ha_example%' - DROP TABLE t1; +# now the engine IS unloaded +# and the table `t2` belongs to an unknown engine + +--replace_column 5 # +--replace_regex /\.dll/.so/ +--query_vertical select * from information_schema.plugins where plugin_library like 'ha_example%' +--error ER_UNKNOWN_STORAGE_ENGINE +SELECT * FROM t2; +DROP TABLE t2; + + --error 1305 UNINSTALL PLUGIN EXAMPLE; diff --git a/sql/handler.cc b/sql/handler.cc index a9a2214a91d..c164a2e2b83 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -2202,15 +2202,15 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path, TABLE_SHARE dummy_share; DBUG_ENTER("ha_delete_table"); + /* table_type is NULL in ALTER TABLE when renaming only .frm files */ + if (table_type == NULL || table_type == view_pseudo_hton || + ! (file=get_new_handler((TABLE_SHARE*)0, thd->mem_root, table_type))) + DBUG_RETURN(HA_ERR_NO_SUCH_TABLE); + bzero((char*) &dummy_table, sizeof(dummy_table)); bzero((char*) &dummy_share, sizeof(dummy_share)); dummy_table.s= &dummy_share; - /* DB_TYPE_UNKNOWN is used in ALTER TABLE when renaming only .frm files */ - if (table_type == NULL || - ! (file=get_new_handler((TABLE_SHARE*)0, thd->mem_root, table_type))) - DBUG_RETURN(HA_ERR_NO_SUCH_TABLE); - path= get_canonical_filename(file, path, tmp_path); if ((error= file->ha_delete_table(path)) && generate_warning) { @@ -4319,10 +4319,6 @@ int ha_discover_table(THD *thd, TABLE_SHARE *share) DBUG_RETURN(share->error != OPEN_FRM_OK); } -/** - Check if a given table exists, without doing a full discover, if possible -*/ - static my_bool file_ext_exists(char *path, size_t path_len, const char *ext) { strmake(path + path_len, ext, FN_REFLEN - path_len); @@ -4334,6 +4330,7 @@ struct st_discover_existence_args char *path; size_t path_len; const char *db, *table_name; + handlerton *hton; }; static my_bool discover_existence(THD *thd, plugin_ref plugin, @@ -4344,6 +4341,8 @@ static my_bool discover_existence(THD *thd, plugin_ref plugin, if (ht->state != SHOW_OPTION_YES || !ht->discover_table_existence) return FALSE; + args->hton= ht; + if (ht->discover_table_existence == ext_based_existence) return file_ext_exists(args->path, args->path_len, ht->tablefile_extensions[0]); @@ -4390,25 +4389,53 @@ private: int m_unhandled_errors; }; -bool ha_table_exists(THD *thd, const char *db, const char *table_name) +/** + Check if a given table exists, without doing a full discover, if possible + + If the 'hton' is not NULL, it's set to the handlerton of the storage engine + of this table, or to view_pseudo_hton if the frm belongs to a view. + + + @retval true Table exists (even if the error occurred, like bad frm) + @retval false Table does not exist (one can do CREATE TABLE table_name) +*/ +bool ha_table_exists(THD *thd, const char *db, const char *table_name, + handlerton **hton) { - DBUG_ENTER("ha_discover_table_existence"); + DBUG_ENTER("ha_table_exists"); + + if (hton) + *hton= 0; if (need_full_discover_for_existence) { TABLE_LIST table; + uint flags = GTS_TABLE | GTS_VIEW; + + if (!hton) + flags|= GTS_NOLOCK; Table_exists_error_handler no_such_table_handler; thd->push_internal_handler(&no_such_table_handler); - get_table_share(thd, db, table_name, GTS_TABLE | GTS_VIEW | GTS_NOLOCK); + TABLE_SHARE *share= get_table_share(thd, db, table_name, flags); thd->pop_internal_handler(); + if (hton && share) + { + *hton= share->db_type(); + mysql_mutex_lock(&LOCK_open); + release_table_share(share); + mysql_mutex_unlock(&LOCK_open); + } + // the table doesn't exist if we've caught ER_NO_SUCH_TABLE and nothing else DBUG_RETURN(!no_such_table_handler.safely_trapped_errors()); } mysql_mutex_lock(&LOCK_open); TABLE_SHARE *share= get_cached_table_share(db, table_name); + if (hton && share) + *hton= share->db_type(); mysql_mutex_unlock(&LOCK_open); if (share) @@ -4419,13 +4446,27 @@ bool ha_table_exists(THD *thd, const char *db, const char *table_name) db, table_name, "", 0); if (file_ext_exists(path, path_len, reg_ext)) + { + if (hton) + { + enum legacy_db_type db_type; + if (dd_frm_type(thd, path, &db_type) != FRMTYPE_VIEW) + *hton= ha_resolve_by_legacy_type(thd, db_type); + else + *hton= view_pseudo_hton; + } DBUG_RETURN(TRUE); + } - st_discover_existence_args args= {path, path_len, db, table_name}; + st_discover_existence_args args= {path, path_len, db, table_name, 0}; if (plugin_foreach(thd, discover_existence, MYSQL_STORAGE_ENGINE_PLUGIN, &args)) + { + if (hton) + *hton= args.hton; DBUG_RETURN(TRUE); + } DBUG_RETURN(FALSE); } diff --git a/sql/handler.h b/sql/handler.h index 2093a3d1605..02e273204e2 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -3073,6 +3073,8 @@ static inline bool ha_storage_engine_is_enabled(const handlerton *db_type) (db_type->state == SHOW_OPTION_YES) : FALSE; } +#define view_pseudo_hton ((handlerton *)1) + /* basic stuff */ int ha_init_errors(void); int ha_init(void); @@ -3102,7 +3104,8 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat); int ha_discover_table(THD *thd, TABLE_SHARE *share); int ha_discover_table_names(THD *thd, LEX_STRING *db, MY_DIR *dirp, handlerton::discovered_list *result); -bool ha_table_exists(THD *thd, const char *db, const char *table_name); +bool ha_table_exists(THD *thd, const char *db, const char *table_name, + handlerton **hton= 0); #ifdef MYSQL_SERVER extern volatile int32 engines_with_discover_table_names; diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 0406f8190a8..d6a64b38446 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -906,7 +906,8 @@ static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc) mysql_mutex_assert_owner(&LOCK_plugin); - if (pi->state & (PLUGIN_IS_READY | PLUGIN_IS_UNINITIALIZED)) + if (pi->state & (PLUGIN_IS_READY | PLUGIN_IS_UNINITIALIZED | + PLUGIN_IS_DELETED)) { plugin_ref plugin; #ifdef DBUG_OFF diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 830c57438a3..a59ecbead8d 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -54,7 +54,6 @@ #include "sql_parse.h" #include "sql_show.h" #include "transaction.h" -#include "datadict.h" // dd_frm_type() #ifdef __WIN__ #include <io.h> @@ -2137,8 +2136,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, bool is_trans; char *db=table->db; size_t db_length= table->db_length; - handlerton *table_type; - enum legacy_db_type frm_db_type= DB_TYPE_UNKNOWN; + handlerton *table_type= 0; DBUG_PRINT("table", ("table_l: '%s'.'%s' table: 0x%lx s: 0x%lx", table->db, table->table_name, (long) table->table, @@ -2262,8 +2260,8 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table"); error= 0; if (!table->internal_tmp_table && - (drop_temporary || !ha_table_exists(thd, db, alias) || - (!drop_view && dd_frm_type(thd, path, &frm_db_type) != FRMTYPE_TABLE))) + (drop_temporary || !ha_table_exists(thd, db, alias, &table_type) || + (!drop_view && table_type == view_pseudo_hton))) { /* One of the following cases happened: @@ -2290,6 +2288,19 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, { char *end; + /* + It could happen that table's share in the table_def_cache + is the only thing that keeps the engine plugin loaded + (if it is uninstalled and waits for the ref counter to drop to 0). + + In this case, the tdc_remove_table() below will release and unload + the plugin. And ha_delete_table() will get a dangling pointer. + + Let's lock the plugin till the end of the statement. + */ + if (table_type && table_type != view_pseudo_hton) + plugin_lock(thd, plugin_int_to_ref(hton2plugin[table_type->slot])); + if (thd->locked_tables_mode) { if (wait_while_table_is_used(thd, table->table, HA_EXTRA_NOT_USED, @@ -2298,6 +2309,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, error= -1; goto err; } + /* the following internally does TDC_RT_REMOVE_ALL */ close_all_tables_for_name(thd, table->table->s, HA_EXTRA_PREPARE_FOR_DROP); table->table= 0; @@ -2311,30 +2323,12 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, table->table_name, MDL_EXCLUSIVE)); - /* - Cannot use the db_type from the table, since that might have changed - while waiting for the exclusive name lock. - */ - if (frm_db_type == DB_TYPE_UNKNOWN) - { - dd_frm_type(thd, path, &frm_db_type); - DBUG_PRINT("info", ("frm_db_type %d from %s", frm_db_type, path)); - } - table_type= ha_resolve_by_legacy_type(thd, frm_db_type); // Remove extension for delete *(end= path + path_length - reg_ext_length)= '\0'; - DBUG_PRINT("info", ("deleting table of type %d", - (table_type ? table_type->db_type : 0))); + error= ha_delete_table(thd, table_type, path, db, table->table_name, !dont_log_query); - /* No error if non existent table and 'IF EXIST' clause or view */ - if (error == ENOENT || (error == HA_ERR_NO_SUCH_TABLE && - (if_exists || table_type == NULL))) - { - error= 0; - thd->clear_error(); - } if (error == HA_ERR_ROW_IS_REFERENCED) { /* the table is referenced by a foreign key constraint */ @@ -2342,18 +2336,29 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, } if (!error || error == ENOENT || error == HA_ERR_NO_SUCH_TABLE) { - int new_error; + int frm_delete_error, trigger_drop_error= 0; /* Delete the table definition file */ strmov(end,reg_ext); - if (!(new_error= mysql_file_delete(key_file_frm, path, MYF(MY_WME)))) + frm_delete_error= mysql_file_delete(key_file_frm, path, MYF(MY_WME)); + if (frm_delete_error) + frm_delete_error= my_errno; + else { non_tmp_table_deleted= TRUE; - new_error= Table_triggers_list::drop_all_triggers(thd, db, - table->table_name); + trigger_drop_error= + Table_triggers_list::drop_all_triggers(thd, db, table->table_name); + } + + if (trigger_drop_error || + (frm_delete_error && frm_delete_error != ENOENT)) + error= 1; + else if (!frm_delete_error || !error || if_exists) + { + error= 0; + thd->clear_error(); } - error|= new_error; } - non_tmp_error= error ? TRUE : non_tmp_error; + non_tmp_error= error ? TRUE : non_tmp_error; } if (error) { @@ -2376,7 +2381,6 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, err: if (wrong_tables.length()) { - thd->clear_error(); if (!foreign_key_error) my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0), wrong_tables.c_ptr_safe()); diff --git a/sql/table.h b/sql/table.h index e4e5b839a85..3fe7ef53f4e 100644 --- a/sql/table.h +++ b/sql/table.h @@ -665,8 +665,9 @@ struct TABLE_SHARE plugin_ref db_plugin; /* storage engine plugin */ inline handlerton *db_type() const /* table_type for handler */ { - // DBUG_ASSERT(db_plugin); - return db_plugin ? plugin_data(db_plugin, handlerton*) : NULL; + return is_view ? view_pseudo_hton : + db_plugin ? plugin_data(db_plugin, handlerton*) + : NULL; } enum row_type row_type; /* How rows are stored */ enum tmp_table_type tmp_table;