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;