diff --git a/mysql-test/r/heap_btree.result b/mysql-test/r/heap_btree.result
index ab4b892170a59173689efb40efda246dcc1967a9..bdf19f130bdbf3dc861dfdb70db0bb0bd7243fd1 100644
--- a/mysql-test/r/heap_btree.result
+++ b/mysql-test/r/heap_btree.result
@@ -307,6 +307,13 @@ UNIQUE USING BTREE(c1)
 ) ENGINE= MEMORY DEFAULT CHARSET= utf8;
 INSERT INTO t1 VALUES('1'), ('2');
 DROP TABLE t1;
+CREATE TABLE t1 (a INT, KEY USING BTREE(a)) ENGINE=MEMORY;
+INSERT INTO t1 VALUES(1),(2),(2);
+DELETE FROM t1 WHERE a=2;
+SELECT * FROM t1;
+a
+1
+DROP TABLE t1;
 End of 4.1 tests
 CREATE TABLE t1(val INT, KEY USING BTREE(val)) ENGINE=memory;
 INSERT INTO t1 VALUES(0);
diff --git a/mysql-test/t/heap_btree.test b/mysql-test/t/heap_btree.test
index 0e8cc71dab54b376cd5b395d219c2b0d5af34c39..cbab2b0472e16c4b6e38f7005205a92ec3aaa354 100644
--- a/mysql-test/t/heap_btree.test
+++ b/mysql-test/t/heap_btree.test
@@ -213,6 +213,15 @@ CREATE TABLE t1 (
 INSERT INTO t1 VALUES('1'), ('2');
 DROP TABLE t1;
 
+#
+# BUG#30590 - delete from memory table with composite btree primary key
+#
+CREATE TABLE t1 (a INT, KEY USING BTREE(a)) ENGINE=MEMORY;
+INSERT INTO t1 VALUES(1),(2),(2);
+DELETE FROM t1 WHERE a=2;
+SELECT * FROM t1;
+DROP TABLE t1;
+
 --echo End of 4.1 tests
 
 #
diff --git a/sql/table.cc b/sql/table.cc
index c1d8e3abe94ab2d73f404009eec6c989899dc226..ccddbf8134b0d2c45a52449f6b36e47aa5cb7ab6 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -969,6 +969,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
         }
         parser_name.str= (char*) next_chunk;
         parser_name.length= strlen((char*) next_chunk);
+        next_chunk+= parser_name.length + 1;
         keyinfo->parser= my_plugin_lock_by_name(NULL, &parser_name,
                                                 MYSQL_FTPARSER_PLUGIN);
         if (! keyinfo->parser)
diff --git a/storage/heap/hp_delete.c b/storage/heap/hp_delete.c
index 1dd79a42e0bc88d435b8a61b7c0b107645defa1a..9e9e28da335b2b7bcf1584338e7fa003b554c85d 100644
--- a/storage/heap/hp_delete.c
+++ b/storage/heap/hp_delete.c
@@ -72,10 +72,7 @@ int hp_rb_delete_key(HP_INFO *info, register HP_KEYDEF *keyinfo,
   int res;
 
   if (flag) 
-  {
     info->last_pos= NULL; /* For heap_rnext/heap_rprev */
-    info->lastkey_len= 0;
-  }
 
   custom_arg.keyseg= keyinfo->seg;
   custom_arg.key_length= hp_rb_make_key(keyinfo, info->recbuf, record, recpos);
diff --git a/storage/heap/hp_rfirst.c b/storage/heap/hp_rfirst.c
index 48c1e625bd8a41cbb7af6257dfc0df2455440d47..d0d2ec9b506530fc777ce37a79881dc85fd34c8e 100644
--- a/storage/heap/hp_rfirst.c
+++ b/storage/heap/hp_rfirst.c
@@ -35,6 +35,17 @@ int heap_rfirst(HP_INFO *info, uchar *record, int inx)
 	     sizeof(uchar*));
       info->current_ptr = pos;
       memcpy(record, pos, (size_t)share->reclength);
+      /*
+        If we're performing index_first on a table that was taken from
+        table cache, info->lastkey_len is initialized to previous query.
+        Thus we set info->lastkey_len to proper value for subsequent
+        heap_rnext() calls.
+        This is needed for DELETE queries only, otherwise this variable is
+        not used.
+        Note that the same workaround may be needed for heap_rlast(), but
+        for now heap_rlast() is never used for DELETE queries.
+      */
+      info->lastkey_len= 0;
       info->update = HA_STATE_AKTIV;
     }
     else
diff --git a/storage/heap/hp_rnext.c b/storage/heap/hp_rnext.c
index 262754e9e642de1fd0a9fe07f2ddfe4748c1281c..3d715f4e6d338378d775bd4d5b9916e52ef1bf0d 100644
--- a/storage/heap/hp_rnext.c
+++ b/storage/heap/hp_rnext.c
@@ -33,11 +33,40 @@ int heap_rnext(HP_INFO *info, uchar *record)
     heap_rb_param custom_arg;
 
     if (info->last_pos)
+    {
+      /*
+        We enter this branch for non-DELETE queries after heap_rkey()
+        or heap_rfirst(). As last key position (info->last_pos) is available,
+        we only need to climb the tree using tree_search_next().
+      */
       pos = tree_search_next(&keyinfo->rb_tree, &info->last_pos,
                              offsetof(TREE_ELEMENT, left),
                              offsetof(TREE_ELEMENT, right));
+    }
+    else if (!info->lastkey_len)
+    {
+      /*
+        We enter this branch only for DELETE queries after heap_rfirst(). E.g.
+        DELETE FROM t1 WHERE a<10. As last key position is not available
+        (last key is removed by heap_delete()), we must restart search as it
+        is done in heap_rfirst().
+
+        It should be safe to handle this situation without this branch. That is
+        branch below should find smallest element in a tree as lastkey_len is
+        zero. tree_search_edge() is a kind of optimisation here as it should be
+        faster than tree_search_key().
+      */
+      pos= tree_search_edge(&keyinfo->rb_tree, info->parents,
+                            &info->last_pos, offsetof(TREE_ELEMENT, left));
+    }
     else
     {
+      /*
+        We enter this branch only for DELETE queries after heap_rkey(). E.g.
+        DELETE FROM t1 WHERE a=10. As last key position is not available
+        (last key is removed by heap_delete()), we must restart search as it
+        is done in heap_rkey().
+      */
       custom_arg.keyseg = keyinfo->seg;
       custom_arg.key_length = info->lastkey_len;
       custom_arg.search_flag = SEARCH_SAME | SEARCH_FIND;