diff --git a/sql/event_parse_data.cc b/sql/event_parse_data.cc
index 6c123c8..2cd8f5c 100644
--- a/sql/event_parse_data.cc
+++ b/sql/event_parse_data.cc
@@ -88,9 +88,6 @@ Event_parse_data::init_name(THD *thd, sp_name *spn)
   name.length= spn->m_name.length;
   name.str= thd->strmake(spn->m_name.str, spn->m_name.length);
 
-  if (spn->m_qname.length == 0)
-    spn->init_qname(thd);
-
   DBUG_VOID_RETURN;
 }
 
diff --git a/sql/item_create.cc b/sql/item_create.cc
index 4730e18..455da1f 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -3444,7 +3444,6 @@ Create_sp_func::create_with_db(THD *thd, LEX_STRING db, LEX_STRING name,
     arg_count= item_list->elements;
 
   qname= new (thd->mem_root) sp_name(db, name, use_explicit_name);
-  qname->init_qname(thd);
   sp_add_used_routine(lex, thd, qname, TYPE_ENUM_FUNCTION);
 
   if (arg_count > 0)
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 3a9f61e..35bba56 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -6338,7 +6338,6 @@ Item_func_sp::Item_func_sp(THD *thd, Name_resolution_context *context_arg,
   Item_func(thd), context(context_arg), m_name(name), m_sp(NULL), sp_result_field(NULL)
 {
   maybe_null= 1;
-  m_name->init_qname(thd);
   dummy_table= (TABLE*) thd->calloc(sizeof(TABLE)+ sizeof(TABLE_SHARE));
   dummy_table->s= (TABLE_SHARE*) (dummy_table+1);
 }
@@ -6350,7 +6349,6 @@ Item_func_sp::Item_func_sp(THD *thd, Name_resolution_context *context_arg,
   sp_result_field(NULL)
 {
   maybe_null= 1;
-  m_name->init_qname(thd);
   dummy_table= (TABLE*) thd->calloc(sizeof(TABLE)+ sizeof(TABLE_SHARE));
   dummy_table->s= (TABLE_SHARE*) (dummy_table+1);
 }
@@ -6435,7 +6433,7 @@ Item_func_sp::init_result_field(THD *thd)
   if (!(m_sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, m_name,
                                &thd->sp_func_cache, TRUE)))
   {
-    my_missing_function_error (m_name->m_name, m_name->m_qname.str);
+    my_missing_function_error (m_name->m_name, ErrConvDQName(m_name).ptr());
     context->process_error(thd);
     DBUG_RETURN(TRUE);
   }
diff --git a/sql/sp.cc b/sql/sp.cc
index a1662ce..90a9ed3 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -1848,7 +1848,6 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool is_proc)
     lex_db.str= thd->strmake(routine->db, lex_db.length);
     lex_name.str= thd->strmake(routine->table_name, lex_name.length);
     name= new sp_name(lex_db, lex_name, true);
-    name->init_qname(thd);
     sp_object_found= is_proc ? sp_find_routine(thd, TYPE_ENUM_PROCEDURE,
                                                name, &thd->sp_proc_cache,
                                                FALSE) != NULL :
@@ -2175,20 +2174,8 @@ int sp_cache_routine(THD *thd, enum stored_procedure_type type, sp_name *name,
       */
       if (! thd->is_error())
       {
-        /*
-          SP allows full NAME_LEN chars thus he have to allocate enough
-          size in bytes. Otherwise there is stack overrun could happen
-          if multibyte sequence is `name`. `db` is still safe because the
-          rest of the server checks agains NAME_LEN bytes and not chars.
-          Hence, the overrun happens only if the name is in length > 32 and
-          uses multibyte (cyrillic, greek, etc.)
-        */
-        char n[NAME_LEN*2+2];
-
-        /* m_qname.str is not always \0 terminated */
-        memcpy(n, name->m_qname.str, name->m_qname.length);
-        n[name->m_qname.length]= '\0';
-        my_error(ER_SP_PROC_TABLE_CORRUPT, MYF(0), n, ret);
+        my_error(ER_SP_PROC_TABLE_CORRUPT, MYF(0),
+                 ErrConvDQName(name).ptr(), ret);
       }
       break;
   }
@@ -2322,7 +2309,6 @@ sp_load_for_information_schema(THD *thd, TABLE *proc_table, String *db,
   sp_name_str.str= name->c_ptr();
   sp_name_str.length= name->length();
   sp_name sp_name_obj(sp_db_str, sp_name_str, true);
-  sp_name_obj.init_qname(thd);
   *free_sp_head= 0;
   if ((sp= sp_cache_lookup(spc, &sp_name_obj)))
   {
diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc
index bafd0f3..b80cde3 100644
--- a/sql/sp_cache.cc
+++ b/sql/sp_cache.cc
@@ -167,8 +167,7 @@ void sp_cache_insert(sp_cache **cp, sp_head *sp)
   }
   /* Reading a ulong variable with no lock. */
   sp->set_sp_cache_version(Cversion);
-  DBUG_PRINT("info",("sp_cache: inserting: %.*s", (int) sp->m_qname.length,
-                     sp->m_qname.str));
+  DBUG_PRINT("info",("sp_cache: inserting: %s", ErrConvDQName(sp).ptr()));
   c->insert(sp);
   *cp= c;                                       // Update *cp if it was NULL
 }
@@ -192,10 +191,11 @@ void sp_cache_insert(sp_cache **cp, sp_head *sp)
 
 sp_head *sp_cache_lookup(sp_cache **cp, sp_name *name)
 {
+  char buf[NAME_LEN * 2 + 2];
   sp_cache *c= *cp;
   if (! c)
     return NULL;
-  return c->lookup(name->m_qname.str, name->m_qname.length);
+  return c->lookup(buf, name->make_qname(buf, sizeof(buf)));
 }
 
 
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 757ee68..3126b3c 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -401,42 +401,14 @@ sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr)
 */
 
 sp_name::sp_name(const MDL_key *key, char *qname_buff)
+ :Database_qualified_name((char*)key->db_name(), key->db_name_length(),
+                          (char*)key->name(),  key->name_length()),
+  m_explicit_name(false)
 {
-  m_db.str= (char*)key->db_name();
-  m_db.length= key->db_name_length();
-  m_name.str= (char*)key->name();
-  m_name.length= key->name_length();
-  m_qname.str= qname_buff;
   if (m_db.length)
-  {
     strxmov(qname_buff, m_db.str, ".", m_name.str, NullS);
-    m_qname.length= m_db.length + 1 + m_name.length;
-  }
   else
-  {
     strmov(qname_buff, m_name.str);
-    m_qname.length= m_name.length;
-  }
-  m_explicit_name= false;
-}
-
-
-/**
-  Init the qualified name from the db and name.
-*/
-void
-sp_name::init_qname(THD *thd)
-{
-  const uint dot= !!m_db.length;
-  /* m_qname format: [database + dot] + name + '\0' */
-  m_qname.length= m_db.length + dot + m_name.length;
-  if (!(m_qname.str= (char*) thd->alloc(m_qname.length + 1)))
-    return;
-  sprintf(m_qname.str, "%.*s%.*s%.*s",
-          (int) m_db.length, (m_db.length ? m_db.str : ""),
-          dot, ".",
-          (int) m_name.length, m_name.str);
-  DBUG_ASSERT(ok_for_lower_case_names(m_db.str));
 }
 
 
@@ -516,6 +488,7 @@ sp_head::operator delete(void *ptr, size_t size) throw()
 
 sp_head::sp_head()
   :Query_arena(&main_mem_root, STMT_INITIALIZED_FOR_SP),
+   Database_qualified_name(null_lex_str, null_lex_str),
    m_flags(0),
    m_sp_cache_version(0),
    m_creation_ctx(0),
@@ -534,7 +507,7 @@ sp_head::sp_head()
     be rewritten soon. Remove the else part and replace 'if' with
     an assert when this is done.
   */
-  m_db= m_name= m_qname= null_lex_str;
+  m_qname= null_lex_str;
 
   DBUG_ENTER("sp_head::sp_head");
 
@@ -620,13 +593,7 @@ sp_head::init_sp_name(THD *thd, sp_name *spname)
 
   m_explicit_name= spname->m_explicit_name;
 
-  if (spname->m_qname.length == 0)
-    spname->init_qname(thd);
-
-  m_qname.length= spname->m_qname.length;
-  m_qname.str= (char*) memdup_root(thd->mem_root,
-                                   spname->m_qname.str,
-                                   spname->m_qname.length + 1);
+  spname->make_qname(thd, &m_qname);
 
   DBUG_VOID_RETURN;
 }
@@ -1623,7 +1590,8 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
       invoking query properly.
     */
     my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0),
-             "FUNCTION", m_qname.str, m_pcont->context_var_count(), argcount);
+             "FUNCTION", ErrConvDQName(this).ptr(),
+             m_pcont->context_var_count(), argcount);
     DBUG_RETURN(TRUE);
   }
   /*
@@ -1847,7 +1815,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
   if (args->elements != params)
   {
     my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0), "PROCEDURE",
-             m_qname.str, params, args->elements);
+             ErrConvDQName(this).ptr(), params, args->elements);
     DBUG_RETURN(TRUE);
   }
 
@@ -1905,7 +1873,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
 
         if (!srp)
         {
-          my_error(ER_SP_NOT_VAR_ARG, MYF(0), i+1, m_qname.str);
+          my_error(ER_SP_NOT_VAR_ARG, MYF(0), i+1, ErrConvDQName(this).ptr());
           err_status= TRUE;
           break;
         }
diff --git a/sql/sp_head.h b/sql/sp_head.h
index a3a3453..e5bc3ca 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -107,30 +107,22 @@ class Stored_program_creation_ctx :public Default_object_creation_ctx
 
 /*************************************************************************/
 
-class sp_name : public Sql_alloc
+class sp_name : public Sql_alloc,
+                public Database_qualified_name
 {
 public:
-
-  LEX_STRING m_db;
-  LEX_STRING m_name;
-  LEX_STRING m_qname;
   bool       m_explicit_name;                   /**< Prepend the db name? */
 
   sp_name(LEX_STRING db, LEX_STRING name, bool use_explicit_name)
-    : m_db(db), m_name(name), m_explicit_name(use_explicit_name)
+    : Database_qualified_name(db, name), m_explicit_name(use_explicit_name)
   {
     if (lower_case_table_names && m_db.str)
       m_db.length= my_casedn_str(files_charset_info, m_db.str);
-    m_qname.str= 0;
-    m_qname.length= 0;
   }
 
   /** Create temporary sp_name object from MDL key. */
   sp_name(const MDL_key *key, char *qname_buff);
 
-  // Init. the qualified name from the db and name.
-  void init_qname(THD *thd);	// thd for memroot allocation
-
   ~sp_name()
   {}
 };
@@ -139,7 +131,8 @@ class sp_name : public Sql_alloc
 bool
 check_routine_name(LEX_STRING *ident);
 
-class sp_head :private Query_arena
+class sp_head :private Query_arena,
+               public Database_qualified_name
 {
   sp_head(const sp_head &);	/**< Prevent use of these */
   void operator=(sp_head &);
@@ -185,8 +178,6 @@ class sp_head :private Query_arena
   sql_mode_t m_sql_mode;		///< For SHOW CREATE and execution
   LEX_STRING m_qname;		///< db.name
   bool m_explicit_name;         ///< Prepend the db name? */
-  LEX_STRING m_db;
-  LEX_STRING m_name;
   LEX_STRING m_params;
   LEX_STRING m_body;
   LEX_STRING m_body_utf8;
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 5b18f99..0572b37 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -5757,6 +5757,68 @@ class Sql_mode_save
   sql_mode_t old_mode; // SQL mode saved at construction time.
 };
 
+
+/**
+  This class resembles the SQL Standard schema qualified object name:
+  <schema qualified name> ::= [ <schema name> <period> ] <qualified identifier>
+*/
+class Database_qualified_name
+{
+public:
+  LEX_STRING m_db;
+  LEX_STRING m_name;
+  Database_qualified_name(const LEX_STRING db, const LEX_STRING name)
+   :m_db(db), m_name(name)
+  { }
+  Database_qualified_name(char *db, size_t db_length,
+                          char *name, size_t name_length)
+  {
+    m_db.str= db;
+    m_db.length= db_length;
+    m_name.str= name;
+    m_name.length= name_length;
+  }
+
+  // Export db and name as a qualified name string: 'db.name'
+  size_t make_qname(char *dst, size_t dstlen) const
+  {
+    return my_snprintf(dst, dstlen, "%.*s.%.*s",
+                       (int) m_db.length, m_db.str,
+                       (int) m_name.length, m_name.str);
+  }
+  // Export db and name as a qualified name string, allocate on mem_root.
+  bool make_qname(THD *thd, LEX_STRING *dst) const
+  {
+    const uint dot= !!m_db.length;
+    /* format: [database + dot] + name + '\0' */
+    dst->length= m_db.length + dot + m_name.length;
+    if (!(dst->str= (char*) thd->alloc(dst->length + 1)))
+      return true;
+    sprintf(dst->str, "%.*s%.*s%.*s",
+            (int) m_db.length, (m_db.length ? m_db.str : ""),
+            dot, ".",
+            (int) m_name.length, m_name.str);
+    DBUG_ASSERT(ok_for_lower_case_names(m_db.str));
+    return false;
+  }
+};
+
+
+class ErrConvDQName: public ErrConv
+{
+  const Database_qualified_name *m_name;
+public:
+  ErrConvDQName(const Database_qualified_name *name)
+   :m_name(name)
+  { }
+  const char *ptr() const
+  {
+    m_name->make_qname(err_buffer, sizeof(err_buffer));
+    return err_buffer;
+  }
+};
+
+
 #endif /* MYSQL_SERVER */
 
 #endif /* SQL_CLASS_INCLUDED */
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 6693947..0fe3b7a 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -2854,7 +2854,7 @@ static bool do_execute_sp(THD *thd, sp_head *sp)
     if (!(thd->client_capabilities & CLIENT_MULTI_RESULTS))
     {
       /* The client does not support multiple result sets being sent back */
-      my_error(ER_SP_BADSELECT, MYF(0), sp->m_qname.str);
+      my_error(ER_SP_BADSELECT, MYF(0), ErrConvDQName(sp).ptr());
       return 1;
     }
     /*
@@ -5750,7 +5750,7 @@ mysql_execute_command(THD *thd)
                                 &thd->sp_proc_cache, TRUE)))
       {
 	my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PROCEDURE",
-                 lex->spname->m_qname.str);
+                 ErrConvDQName(lex->spname).ptr());
 	goto error;
       }
       else
@@ -5810,11 +5810,11 @@ mysql_execute_command(THD *thd)
 	break;
       case SP_KEY_NOT_FOUND:
 	my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
-                 SP_COM_STRING(lex), lex->spname->m_qname.str);
+                 SP_COM_STRING(lex), ErrConvDQName(lex->spname).ptr());
 	goto error;
       default:
 	my_error(ER_SP_CANT_ALTER, MYF(0),
-                 SP_COM_STRING(lex), lex->spname->m_qname.str);
+                 SP_COM_STRING(lex), ErrConvDQName(lex->spname).ptr());
 	goto error;
       }
       break;
@@ -5921,17 +5921,18 @@ mysql_execute_command(THD *thd)
           res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
 	  push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
 			      ER_SP_DOES_NOT_EXIST, ER_THD(thd, ER_SP_DOES_NOT_EXIST),
-                              SP_COM_STRING(lex), lex->spname->m_qname.str);
+                              SP_COM_STRING(lex),
+                              ErrConvDQName(lex->spname).ptr());
           if (!res)
             my_ok(thd);
 	  break;
 	}
 	my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
-                 SP_COM_STRING(lex), lex->spname->m_qname.str);
+                 SP_COM_STRING(lex), ErrConvDQName(lex->spname).ptr());
 	goto error;
       default:
 	my_error(ER_SP_DROP_FAILED, MYF(0),
-                 SP_COM_STRING(lex), lex->spname->m_qname.str);
+                 SP_COM_STRING(lex), ErrConvDQName(lex->spname).ptr());
 	goto error;
       }
       break;
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index e0739ea..b60b581 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -2869,7 +2869,6 @@ sp_name:
             $$= new (thd->mem_root) sp_name($1, $3, true);
             if ($$ == NULL)
               MYSQL_YYABORT;
-            $$->init_qname(thd);
           }
         | ident
           {
@@ -2884,7 +2883,6 @@ sp_name:
             $$= new (thd->mem_root) sp_name(db, $1, false);
             if ($$ == NULL)
               MYSQL_YYABORT;
-            $$->init_qname(thd);
           }
         ;
 
@@ -12155,7 +12153,6 @@ drop:
             spname= new (thd->mem_root) sp_name($4, $6, true);
             if (spname == NULL)
               MYSQL_YYABORT;
-            spname->init_qname(thd);
             lex->spname= spname;
           }
         | DROP FUNCTION_SYM opt_if_exists ident
@@ -12171,7 +12168,6 @@ drop:
             spname= new (thd->mem_root) sp_name(db, $4, false);
             if (spname == NULL)
               MYSQL_YYABORT;
-            spname->init_qname(thd);
             lex->spname= spname;
           }
         | DROP PROCEDURE_SYM opt_if_exists sp_name
@@ -16766,7 +16762,8 @@ sf_tail:
             lex->sql_command= SQLCOM_CREATE_SPFUNCTION;
             sp->set_stmt_end(thd);
             if (!(sp->m_flags & sp_head::HAS_RETURN))
-              my_yyabort_error((ER_SP_NORETURN, MYF(0), sp->m_qname.str));
+              my_yyabort_error((ER_SP_NORETURN, MYF(0),
+                                ErrConvDQName(sp).ptr()));
             if (is_native_function(thd, & sp->m_name))
             {
               /*
