diff --git a/mysql-test/r/sp-threads.result b/mysql-test/r/sp-threads.result
index a081e52049628ec6762071b9706442135087b4ad..a9d50e6e697a5e17eb6bbf7ed0e693fea86d36df 100644
--- a/mysql-test/r/sp-threads.result
+++ b/mysql-test/r/sp-threads.result
@@ -40,3 +40,18 @@ Id	User	Host	db	Command	Time	State	Info
 unlock tables;
 drop procedure bug9486;
 drop table t1, t2;
+drop procedure if exists bug11158;
+create procedure bug11158() delete t1 from t1, t2 where t1.id = t2.id;
+create table t1 (id int, j int);
+insert into t1 values (1, 1), (2, 2);
+create table t2 (id int);
+insert into t2 values (1);
+call bug11158();
+select * from t1;
+id	j
+2	2
+lock tables t2 read;
+call bug11158();
+unlock tables;
+drop procedure bug11158;
+drop table t1, t2;
diff --git a/mysql-test/t/sp-threads.test b/mysql-test/t/sp-threads.test
index 608ac3e2ee74364ac2100b1165764669ab2ca894..8fec5d14bc118e1de2ea0721e938f9fe4d53245d 100644
--- a/mysql-test/t/sp-threads.test
+++ b/mysql-test/t/sp-threads.test
@@ -84,6 +84,32 @@ reap;
 drop procedure bug9486;
 drop table t1, t2;
 
+#
+# BUG#11158: Can't perform multi-delete in stored procedure
+#
+--disable_warnings
+drop procedure if exists bug11158;
+--enable_warnings
+create procedure bug11158() delete t1 from t1, t2 where t1.id = t2.id;
+create table t1 (id int, j int);
+insert into t1 values (1, 1), (2, 2);
+create table t2 (id int);
+insert into t2 values (1);
+# Procedure should work and cause proper effect (delete only first row)
+call bug11158();
+select * from t1;
+# Also let us test that we obtain only read (and thus non exclusive) lock
+# for table from which we are not going to delete rows.
+connection con2root;
+lock tables t2 read;
+connection con1root;
+call bug11158();
+connection con2root;
+unlock tables;
+connection con1root;
+# Clean-up
+drop procedure bug11158;
+drop table t1, t2;
 
 #
 # BUG#NNNN: New bug synopsis
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 338e45fa0581bc85b18d12fa12adda82a98eeea6..4dca5e32c89013ce64fc2077bca647f21148f31b 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -482,7 +482,7 @@ bool check_merge_table_access(THD *thd, char *db,
 			      TABLE_LIST *table_list);
 bool check_some_routine_access(THD *thd, const char *db, const char *name, bool is_proc);
 bool multi_update_precheck(THD *thd, TABLE_LIST *tables);
-bool multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count);
+bool multi_delete_precheck(THD *thd, TABLE_LIST *tables);
 bool mysql_multi_update_prepare(THD *thd);
 bool mysql_multi_delete_prepare(THD *thd);
 bool mysql_insert_select_prepare(THD *thd);
@@ -577,6 +577,7 @@ void mysql_init_query(THD *thd, uchar *buf, uint length);
 bool mysql_new_select(LEX *lex, bool move_down);
 void create_select_for_variable(const char *var_name);
 void mysql_init_multi_delete(LEX *lex);
+bool multi_delete_set_locks_and_link_aux_tables(LEX *lex);
 void init_max_user_conn(void);
 void init_update_queries(void);
 void free_max_user_conn(void);
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index b5df0be10733fa4bb60d13203055c4368502e7d6..a1887996d00e0c923e92ebe77a4be23b61eca85c 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -42,7 +42,6 @@ static my_bool open_new_frm(const char *path, const char *alias,
 			    uint db_stat, uint prgflag,
 			    uint ha_open_flags, TABLE *outparam,
 			    TABLE_LIST *table_desc, MEM_ROOT *mem_root);
-static void relink_tables_for_multidelete(THD *thd);
 
 extern "C" byte *table_cache_key(const byte *record,uint *length,
 				 my_bool not_used __attribute__((unused)))
@@ -2089,7 +2088,6 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables)
       (thd->fill_derived_tables() &&
        mysql_handle_derived(thd->lex, &mysql_derived_filling)))
     DBUG_RETURN(TRUE); /* purecov: inspected */
-  relink_tables_for_multidelete(thd);
   DBUG_RETURN(0);
 }
 
@@ -2119,36 +2117,10 @@ bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables)
   if (open_tables(thd, &tables, &counter) ||
       mysql_handle_derived(thd->lex, &mysql_derived_prepare))
     DBUG_RETURN(TRUE); /* purecov: inspected */
-  relink_tables_for_multidelete(thd);           // Not really needed, but
   DBUG_RETURN(0);
 }
 
 
-/*
-  Let us propagate pointers to open tables from global table list
-  to table lists for multi-delete
-*/
-
-static void relink_tables_for_multidelete(THD *thd)
-{
-  if (thd->lex->all_selects_list->next_select_in_list())
-  {
-    for (SELECT_LEX *sl= thd->lex->all_selects_list;
-	 sl;
-	 sl= sl->next_select_in_list())
-    {
-      for (TABLE_LIST *cursor= (TABLE_LIST *) sl->table_list.first;
-           cursor;
-           cursor=cursor->next_local)
-      {
-        if (cursor->correspondent_table)
-          cursor->table= cursor->correspondent_table->table;
-      }
-    }
-  }
-}
-
-
 /*
   Mark all real tables in the list as free for reuse.
 
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index a6f729d76778d338cf1874afb4217e2dc8fc2f0d..8af416f0ce873dc3328ffe101338ea5b23d82fa1 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -751,7 +751,12 @@ typedef struct st_lex
   uint grant, grant_tot_col, which_columns;
   uint fk_delete_opt, fk_update_opt, fk_match_option;
   uint slave_thd_opt, start_transaction_opt;
-  uint table_count; /* used when usual update transformed in multiupdate */
+  /*
+    In LEX representing update which were transformed to multi-update
+    stores total number of tables. For LEX representing multi-delete
+    holds number of tables from which we will delete records.
+  */
+  uint table_count;
   uint8 describe;
   uint8 derived_tables;
   uint8 create_view_algorithm;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index f48bc3713e6592051d87b48bd69c3979d2c98bc2..be7ba7d571d3ec942d36d3148e4f00031ebbd208 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -3291,10 +3291,9 @@ end_with_restore_list:
     DBUG_ASSERT(first_table == all_tables && first_table != 0);
     TABLE_LIST *aux_tables=
       (TABLE_LIST *)thd->lex->auxilliary_table_list.first;
-    uint table_count;
     multi_delete *result;
 
-    if ((res= multi_delete_precheck(thd, all_tables, &table_count)))
+    if ((res= multi_delete_precheck(thd, all_tables)))
       break;
 
     /* condition will be TRUE on SP re-excuting */
@@ -3311,7 +3310,7 @@ end_with_restore_list:
       goto error;
 
     if (!thd->is_fatal_error && (result= new multi_delete(thd,aux_tables,
-							  table_count)))
+							  lex->table_count)))
     {
       res= mysql_select(thd, &select_lex->ref_pointer_array,
 			select_lex->get_table_list(),
@@ -6801,23 +6800,19 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
     multi_delete_precheck()
     thd			Thread handler
     tables		Global/local table list
-    table_count		Pointer to table counter
 
   RETURN VALUE
     FALSE OK
     TRUE  error
 */
 
-bool multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count)
+bool multi_delete_precheck(THD *thd, TABLE_LIST *tables)
 {
   SELECT_LEX *select_lex= &thd->lex->select_lex;
   TABLE_LIST *aux_tables=
     (TABLE_LIST *)thd->lex->auxilliary_table_list.first;
-  TABLE_LIST *target_tbl;
   DBUG_ENTER("multi_delete_precheck");
 
-  *table_count= 0;
-
   /* sql_yacc guarantees that tables and aux_tables are not zero */
   DBUG_ASSERT(aux_tables != 0);
   if (check_db_used(thd, tables) || check_db_used(thd,aux_tables) ||
@@ -6830,9 +6825,35 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count)
                ER(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE), MYF(0));
     DBUG_RETURN(TRUE);
   }
-  for (target_tbl= aux_tables; target_tbl; target_tbl= target_tbl->next_local)
+  DBUG_RETURN(FALSE);
+}
+
+
+/*
+  Link tables in auxilary table list of multi-delete with corresponding
+  elements in main table list, and set proper locks for them.
+
+  SYNOPSIS
+    multi_delete_set_locks_and_link_aux_tables()
+      lex - pointer to LEX representing multi-delete
+
+  RETURN VALUE
+    FALSE - success
+    TRUE  - error
+*/
+
+bool multi_delete_set_locks_and_link_aux_tables(LEX *lex)
+{
+  TABLE_LIST *tables= (TABLE_LIST*)lex->select_lex.table_list.first;
+  TABLE_LIST *target_tbl;
+  DBUG_ENTER("multi_delete_set_locks_and_link_aux_tables");
+
+  lex->table_count= 0;
+
+  for (target_tbl= (TABLE_LIST *)lex->auxilliary_table_list.first;
+       target_tbl; target_tbl= target_tbl->next_local)
   {
-    (*table_count)++;
+    lex->table_count++;
     /* All tables in aux_tables must be found in FROM PART */
     TABLE_LIST *walk;
     for (walk= tables; walk; walk= walk->next_local)
@@ -6850,14 +6871,6 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count)
     }
     walk->lock_type= target_tbl->lock_type;
     target_tbl->correspondent_table= walk;	// Remember corresponding table
-    
-    /* in case of subselects, we need to set lock_type in
-     * corresponding table in list of all tables */
-    if (walk->correspondent_table)
-    {
-      target_tbl->correspondent_table= walk->correspondent_table;
-      walk->correspondent_table->lock_type= walk->lock_type;
-    }
   }
   DBUG_RETURN(FALSE);
 }
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index cb96f57a58a93976916fdd0fc03fb9ba335a8326..f1b3c69264c524b327b42ffa37705c5bc74c5633 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -1402,8 +1402,6 @@ static bool mysql_test_multiupdate(Prepared_statement *stmt,
 static bool mysql_test_multidelete(Prepared_statement *stmt,
                                   TABLE_LIST *tables)
 {
-  uint fake_counter;
-
   stmt->thd->lex->current_select= &stmt->thd->lex->select_lex;
   if (add_item_to_list(stmt->thd, new Item_null()))
   {
@@ -1411,7 +1409,7 @@ static bool mysql_test_multidelete(Prepared_statement *stmt,
     goto error;
   }
 
-  if (multi_delete_precheck(stmt->thd, tables, &fake_counter) ||
+  if (multi_delete_precheck(stmt->thd, tables) ||
       select_like_stmt_test_with_open_n_lock(stmt, tables,
                                              &mysql_multi_delete_prepare,
                                              OPTION_SETUP_TABLES_DONE))
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 719b42e890fc3087df6f8331ca3e705db5a21fb4..b2bda8818c519ee0739eb1ce4131075b9e8b601d 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -6132,10 +6132,17 @@ single_multi:
 	| table_wild_list
 	  { mysql_init_multi_delete(Lex); }
           FROM join_table_list where_clause
+          { 
+            if (multi_delete_set_locks_and_link_aux_tables(Lex))
+              YYABORT;
+          }
 	| FROM table_wild_list
 	  { mysql_init_multi_delete(Lex); }
 	  USING join_table_list where_clause
-	  {}
+          { 
+            if (multi_delete_set_locks_and_link_aux_tables(Lex))
+              YYABORT;
+          }
 	;
 
 table_wild_list: