diff --git a/mysql-test/suite/compat/oracle/r/sp-stacktrace.result b/mysql-test/suite/compat/oracle/r/sp-stacktrace.result
new file mode 100644
index 0000000..3d08b9b
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/r/sp-stacktrace.result
@@ -0,0 +1,175 @@
+SET sql_mode=ORACLE;
+set max_sp_recursion_depth=5;
+#
+# MDEV- ???? - add stacktrace as note when an error occured in SP
+#
+# Error in anonymous block
+BEGIN NOT ATOMIC
+SIGNAL SQLSTATE '02000';
+END
+$$
+ERROR 02000: Unhandled user-defined not found condition
+show warnings;
+Level	Code	Message
+Error	1643	Unhandled user-defined not found condition
+Note	4058	At line 2 in anonymous block
+# Warning in anonymous block
+BEGIN NOT ATOMIC
+SIGNAL SQLSTATE '01000';
+END
+$$
+Warnings:
+Warning	1642	Unhandled user-defined warning condition
+show warnings;
+Level	Code	Message
+Warning	1642	Unhandled user-defined warning condition
+# Warning and error in anonymous block
+BEGIN NOT ATOMIC
+SIGNAL SQLSTATE '01000';
+SIGNAL SQLSTATE '02000';
+END
+$$
+ERROR 02000: Unhandled user-defined not found condition
+show warnings;
+Level	Code	Message
+Error	1643	Unhandled user-defined not found condition
+Note	4058	At line 3 in anonymous block
+create or replace table t1(c1 integer not null);
+create trigger trg1 before insert on t1
+for each row
+begin
+if :new.c1 = 1 then
+:new.c1:=null;
+end if;
+if :new.c1 = 2 then
+call f2(0);
+end if;
+end;
+$$
+CREATE or replace procedure P1(lim INT)
+AS
+a INT;
+BEGIN
+P2(lim);
+return ;
+END;
+$$
+CREATE or replace procedure P2(lim INT)
+AS
+a INT;
+b INT;
+BEGIN
+a:=10;
+-- comment
+if (lim = 2) then
+-- error on insert values
+insert into t1 values (null);
+end if;
+if (lim = 4) then
+-- error in trigger
+insert into t1 values (1);
+end if;
+if (lim = 5) then
+-- error sp called by a trigger
+insert into t1 values (2);
+end if;
+/*
+** Multi line comment
+*/
+if (lim = 3) then
+a:=11;
+SIGNAL SQLSTATE '02000';
+return ;
+end if;
+if (lim = 1) then
+declare
+c int;
+begin
+-- unknow procedure
+call P3();
+end;
+end if;
+-- max recursion depth
+P2(lim);
+return ;
+END;
+$$
+CREATE or replace function F1(val INT) return integer
+AS
+a INT;
+BEGIN
+if (val = 3) then
+SIGNAL SQLSTATE '02000';
+end if;
+return  val;
+END;
+$$
+bad insert values
+CALL P1(2);
+ERROR 23000: Column 'c1' cannot be null
+show warnings;
+Level	Code	Message
+Error	1048	Column 'c1' cannot be null
+Note	4058	At line 10 in test.P2
+Note	4058	At line 5 in test.P1
+call unknow function
+CALL P1(1);
+ERROR 42000: PROCEDURE test.P3 does not exist
+show warnings;
+Level	Code	Message
+Error	1305	PROCEDURE test.P3 does not exist
+Note	4058	At line 33 in test.P2
+Note	4058	At line 5 in test.P1
+max recursion
+CALL P1(0);
+ERROR HY000: Recursive limit 5 (as set by the max_sp_recursion_depth variable) was exceeded for routine P2
+show warnings;
+Level	Code	Message
+Error	1456	Recursive limit 5 (as set by the max_sp_recursion_depth variable) was exceeded for routine P2
+Note	4058	At line 37 in test.P2
+Note	4058	At line 37 in test.P2
+Note	4058	At line 37 in test.P2
+Note	4058	At line 37 in test.P2
+Note	4058	At line 37 in test.P2
+Note	4058	At line 37 in test.P2
+Note	4058	At line 5 in test.P1
+signal error
+CALL P2(3);
+ERROR 02000: Unhandled user-defined not found condition
+show warnings;
+Level	Code	Message
+Error	1643	Unhandled user-defined not found condition
+Note	4058	At line 25 in test.P2
+bad insert values cause by trigger
+CALL P2(4);
+ERROR 23000: Column 'c1' cannot be null
+show warnings;
+Level	Code	Message
+Error	1048	Column 'c1' cannot be null
+Note	4058	At line 14 in test.P2
+error in sp called by a trigger
+CALL P2(5);
+ERROR 42000: PROCEDURE test.f2 does not exist
+show warnings;
+Level	Code	Message
+Error	1305	PROCEDURE test.f2 does not exist
+Note	4058	At line 8 in test.trg1
+Note	4058	At line 18 in test.P2
+error in sp called by a trigger
+insert into t1 select 3 union select 2;
+ERROR 42000: PROCEDURE test.f2 does not exist
+show warnings;
+Level	Code	Message
+Error	1305	PROCEDURE test.f2 does not exist
+Note	4058	At line 8 in test.trg1
+error in function
+select F1(t.col1) from (select 3 "col1" union select 4) as t;
+ERROR 02000: Unhandled user-defined not found condition
+show warnings;
+Level	Code	Message
+Error	1643	Unhandled user-defined not found condition
+Note	4058	At line 6 in test.F1
+drop function F1;
+drop procedure P1;
+drop procedure P2;
+drop table t1;
diff --git a/mysql-test/suite/compat/oracle/t/sp-stacktrace.test b/mysql-test/suite/compat/oracle/t/sp-stacktrace.test
new file mode 100644
index 0000000..29de688
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-stacktrace.test
@@ -0,0 +1,144 @@
+
+SET sql_mode=ORACLE;
+set max_sp_recursion_depth=5;
+
+--echo #
+--echo # MDEV- ???? - add stacktrace as note when an error occured in SP
+--echo #
+
+DELIMITER $$;
+--echo # Error in anonymous block
+--error ER_SIGNAL_NOT_FOUND
+BEGIN NOT ATOMIC
+  SIGNAL SQLSTATE '02000';
+END
+$$
+DELIMITER ;$$
+show warnings;
+DELIMITER $$;
+--echo # Warning in anonymous block
+BEGIN NOT ATOMIC
+  SIGNAL SQLSTATE '01000';
+END
+$$
+DELIMITER ;$$
+show warnings;
+DELIMITER $$;
+--echo # Warning and error in anonymous block
+--error ER_SIGNAL_NOT_FOUND
+BEGIN NOT ATOMIC
+  SIGNAL SQLSTATE '01000';
+  SIGNAL SQLSTATE '02000';
+END
+$$
+DELIMITER ;$$
+show warnings;
+create or replace table t1(c1 integer not null);
+DELIMITER $$;
+create trigger trg1 before insert on t1
+for each row
+begin
+  if :new.c1 = 1 then
+    :new.c1:=null;
+  end if;
+  if :new.c1 = 2 then
+    call f2(0);
+  end if;
+end;
+$$
+CREATE or replace procedure P1(lim INT)
+AS
+  a INT;
+BEGIN
+  P2(lim);
+  return ;
+END;
+$$
+CREATE or replace procedure P2(lim INT)
+AS
+  a INT;
+  b INT;
+BEGIN
+  a:=10;
+  -- comment
+  if (lim = 2) then
+    -- error on insert values
+    insert into t1 values (null);
+  end if;
+  if (lim = 4) then
+    -- error in trigger
+    insert into t1 values (1);
+  end if;
+  if (lim = 5) then
+    -- error sp called by a trigger
+    insert into t1 values (2);
+  end if;
+  /*
+  ** Multi line comment
+  */
+  if (lim = 3) then
+    a:=11;
+    SIGNAL SQLSTATE '02000';
+    return ;
+  end if;
+  if (lim = 1) then
+    declare
+       c int;
+    begin
+      -- unknow procedure
+      call P3();
+     end;
+  end if;
+  -- max recursion depth
+  P2(lim);
+  return ;
+END;
+$$
+CREATE or replace function F1(val INT) return integer
+AS
+  a INT;
+BEGIN
+  if (val = 3) then
+    SIGNAL SQLSTATE '02000';
+  end if;
+  return  val;
+END;
+$$
+DELIMITER ;$$
+--echo  bad insert values
+--error ER_BAD_NULL_ERROR
+CALL P1(2);
+show warnings;
+--echo call unknow function
+--error ER_SP_DOES_NOT_EXIST
+CALL P1(1);
+show warnings;
+--echo max recursion
+--error ER_SP_RECURSION_LIMIT
+CALL P1(0);
+show warnings;
+--echo signal error
+--error ER_SIGNAL_NOT_FOUND
+CALL P2(3);
+show warnings;
+--echo bad insert values cause by trigger
+--error ER_BAD_NULL_ERROR
+CALL P2(4);
+show warnings;
+--echo error in sp called by a trigger
+--error ER_SP_DOES_NOT_EXIST
+CALL P2(5);
+show warnings;
+--echo error in sp called by a trigger
+--error ER_SP_DOES_NOT_EXIST
+insert into t1 select 3 union select 2;
+show warnings;
+--echo error in function
+--error ER_SIGNAL_NOT_FOUND
+select F1(t.col1) from (select 3 "col1" union select 4) as t;
+show warnings;
+drop function F1;
+drop procedure P1;
+drop procedure P2;
+drop table t1;
+
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index 38d75d9..deeb35a 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -7461,3 +7461,5 @@ ER_END_IDENTIFIER_DOES_NOT_MATCH
         eng "END identifier '%-.192s' does not match '%-.192s'"
 ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD
         eng "Row variable '%-.192s' does not have a field '%-.192s'"
+ER_SP_STACK_TRACE
+        eng "At line %d in %s"
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 55b7773..5fcae45 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -1208,11 +1208,10 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
   /* Discard the initial part of executing routines. */
   thd->profiling.discard_current_query();
 #endif
+  sp_instr *i;
   DEBUG_SYNC(thd, "sp_head_execute_before_loop");
   do
   {
-    sp_instr *i;
-
 #if defined(ENABLED_PROFILING)
     /*
      Treat each "instr" of a routine as discrete unit that could be profiled.
@@ -1372,6 +1371,17 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
       da->opt_clear_warning_info(thd->query_id);
       da->copy_sql_conditions_from_wi(thd, &sp_wi);
       da->remove_marked_sql_conditions();
+      if (i != NULL)
+        if (m_qname.str != NULL)
+          push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+                              ER_SP_STACK_TRACE,
+                              ER_THD(thd, ER_SP_STACK_TRACE),
+                              i->m_lineno, m_qname.str);
+        else
+          push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+                              ER_SP_STACK_TRACE,
+                              ER_THD(thd, ER_SP_STACK_TRACE),
+                              i->m_lineno, "anonymous block");
     }
   }
 
@@ -2799,6 +2809,7 @@ int sp_head::add_instr(sp_instr *instr)
     entire stored procedure, as their life span is equal.
   */
   instr->mem_root= &main_mem_root;
+  instr->m_lineno= m_thd->m_parser_state->m_lip.yylineno;
   return insert_dynamic(&m_instr, (uchar*)&instr);
 }
 
diff --git a/sql/sp_head.h b/sql/sp_head.h
index 2a10fba..e374633 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -835,6 +835,7 @@ class sp_instr :public Query_arena, public Sql_alloc
   uint marked;
   uint m_ip;			///< My index
   sp_pcontext *m_ctx;		///< My parse context
+  uint m_lineno;
 
   /// Should give each a name or type code for debugging purposes?
   sp_instr(uint ip, sp_pcontext *ctx)
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 5e61949..ca4cc5d 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -1548,7 +1548,7 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd)
           below by checking start != lex->ptr.
         */
         for (; state_map[(uchar) c] == MY_LEX_SKIP ; c= lip->yyGet())
-          ;
+           if (c == '\n') lip->yylineno++;
       }
       if (start == lip->get_ptr() && c == '.' &&
           ident_map[(uchar) lip->yyPeek()])
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 3093853..2347968 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -5897,9 +5897,11 @@ mysql_execute_command(THD *thd)
       if (!(sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
                                 &thd->sp_proc_cache, TRUE)))
       {
-	my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PROCEDURE",
-                 lex->spname->m_qname.str);
-	goto error;
+        // Do not send message if it's an ER_SP_RECURSION_LIMIT error
+        if (!sp_cache_lookup(&thd->sp_proc_cache, lex->spname))
+          my_error(ER_SP_DOES_NOT_EXIST, MYF(0),"PROCEDURE",
+                   lex->spname->m_qname.str);
+        goto error;
       }
       else
       {
