diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result
index 4833b152f07a621909cdc65764fbd34f618e9b3d..1ff6d1552f4d36f25aae43dfa06dd6d36717b6d4 100644
--- a/mysql-test/r/sp.result
+++ b/mysql-test/r/sp.result
@@ -20,6 +20,7 @@ delete from t1;
 drop procedure foo42;
 create procedure u()
 use sptmp;
+drop database if exists sptmp;
 create database sptmp;
 use test;
 call u();
diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test
index 95b3e4732a9d0e57ada4136ed5df4bf43ed39e55..a12e06f24b1694a4a12687ceb1cbcf6ec181b521 100644
--- a/mysql-test/t/sp.test
+++ b/mysql-test/t/sp.test
@@ -35,6 +35,9 @@ drop procedure foo42;
 create procedure u()
   use sptmp;
 
+--disable_warnings
+drop database if exists sptmp;
+--enable_warnings
 create database sptmp;
 use test;
 call u();
diff --git a/sql/sp.cc b/sql/sp.cc
index 5eb12c9fae5f5e2656182fb9aabe65379fa8e253..c8dc328ced147373ca5eb1a253ef5097713d5580 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -19,8 +19,8 @@
 #include "sp.h"
 #include "sp_head.h"
 
-  static sp_head *
-    sp_find_cached_function(THD *thd, char *name, uint namelen);
+static sp_head *
+sp_find_cached_function(THD *thd, char *name, uint namelen);
 
 /*
  *
@@ -373,6 +373,13 @@ sp_cache_functions(THD *thd, LEX *lex)
 void
 sp_clear_function_cache(THD *thd)
 {
+  //QQ This doesn't work for some completely mysterious reason, but since this
+  //QQ is tempoarary code anyway, we just ignore it for now.
+  //QQ List_iterator_fast<sp_head> li(thd->spfuns);
+  //QQ  sp_head *sp;
+
+  //QQ  while ((sp= li++))
+  //QQ    sp->destroy();
   thd->spfuns.empty();
 }
 
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 61cf322876030f90761cd4b4649634fa778e4536..036207796c36dc650a4de8fcaacb65e722399554 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -92,7 +92,7 @@ eval_func_item(THD *thd, Item *it, enum enum_field_types type)
 }
 
 sp_head::sp_head(LEX_STRING *name, LEX *lex)
-  : m_simple_case(FALSE)
+  : Sql_alloc(), m_simple_case(FALSE)
 {
   const char *dstr = (const char*)lex->buf;
 
@@ -126,19 +126,34 @@ sp_head::create(THD *thd)
   DBUG_RETURN(ret);
 }
 
+void
+sp_head::destroy()
+{
+  delete_dynamic(&m_instr);
+  m_pcont->destroy();
+}
 
 int
 sp_head::execute(THD *thd)
 {
   DBUG_ENTER("sp_head::execute");
-  char *olddbname;
+  char olddbname[128];
   char *olddbptr= thd->db;
   int ret= 0;
   uint ip= 0;
 
-  LINT_INIT(olddbname);
   if (olddbptr)
-    olddbname= my_strdup(olddbptr, MYF(MY_WME));
+  {
+    uint i= 0;
+    char *p= olddbptr;
+
+    /* Fast inline strncpy without padding... */
+    while (*p && i < sizeof(olddbname))
+      olddbname[i++]= *p++;
+    if (i == sizeof(olddbname))
+      i-= 1;			// QQ Error or warning for truncate?
+    olddbname[i]= '\0';
+  }
 
   do
   {
@@ -156,13 +171,12 @@ sp_head::execute(THD *thd)
     ret= -1;
   /* If the DB has changed, the pointer has changed too, but the
      original thd->db will then have been freed */
-  if (olddbptr && olddbptr != thd->db && olddbname)
+  if (olddbptr && olddbptr != thd->db)
   {
     /* QQ Maybe we should issue some special error message or warning here,
        if this fails?? */
     if (! thd->killed)
       ret= mysql_change_db(thd, olddbname);
-    my_free(olddbname, MYF(0));
   }
   DBUG_RETURN(ret);
 }
@@ -399,7 +413,7 @@ sp_head::restore_lex(THD *thd)
 void
 sp_head::push_backpatch(sp_instr *i, sp_label_t *lab)
 {
-  bp_t *bp= (bp_t *)my_malloc(sizeof(bp_t), MYF(MY_WME));
+  bp_t *bp= (bp_t *)sql_alloc(sizeof(bp_t));
 
   if (bp)
   {
diff --git a/sql/sp_head.h b/sql/sp_head.h
index 52cbdc560932c5e8d0617e30f0aef7c0b9d74ef1..840276681a561a0fca3f351b9d76088d3f786a5f 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -52,21 +52,15 @@ class sp_head : public Sql_alloc
   List<char *> m_tables;	// Used tables.
 #endif
 
-  static void *operator new(size_t size)
-  {
-    return (void*) sql_alloc((uint) size);
-  }
-
-  static void operator delete(void *ptr, size_t size)
-  {
-    /* Empty */
-  }
-
   sp_head(LEX_STRING *name, LEX *lex);
 
   int
   create(THD *thd);
   
+  // Free memory
+  void
+  destroy();
+
   int
   execute_function(THD *thd, Item **args, uint argcount, Item **resp);
 
@@ -134,11 +128,13 @@ class sp_head : public Sql_alloc
   inline sp_instr *
   get_instr(uint i)
   {
-    sp_instr *in= NULL;
+    sp_instr *ip;
 
     if (i < m_instr.elements)
-      get_dynamic(&m_instr, (gptr)&in, i);
-    return in;
+      get_dynamic(&m_instr, (gptr)&ip, i);
+    else
+      ip= NULL;
+    return ip;
   }
 
   int
diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc
index d59db9b449b4b488f4ca018f92207d00dfe84f65..4d3cf80cd34104c5d3ed7dd79df2a8800cd0a41c 100644
--- a/sql/sp_pcontext.cc
+++ b/sql/sp_pcontext.cc
@@ -27,30 +27,20 @@
 #include "sp_head.h"
 
 sp_pcontext::sp_pcontext()
-  : m_params(0), m_framesize(0), m_i(0), m_genlab(0)
+  : Sql_alloc(), m_params(0), m_framesize(0), m_genlab(0)
 {
-  m_pvar_size = 16;
-  m_pvar = (sp_pvar_t *)my_malloc(m_pvar_size * sizeof(sp_pvar_t), MYF(MY_WME));
-  if (m_pvar)
-    memset(m_pvar, 0, m_pvar_size * sizeof(sp_pvar_t));
+  VOID(my_init_dynamic_array(&m_pvar, sizeof(sp_pvar_t *), 16, 8));
   m_label.empty();
 }
 
 void
-sp_pcontext::grow()
+sp_pcontext::destroy()
 {
-  uint sz = m_pvar_size + 8;
-  sp_pvar_t *a = (sp_pvar_t *)my_realloc((char *)m_pvar,
-					 sz * sizeof(sp_pvar_t),
-					 MYF(MY_WME | MY_ALLOW_ZERO_PTR));
-
-  if (a)
-  {
-    m_pvar_size = sz;
-    m_pvar = a;
-  }
+  delete_dynamic(&m_pvar);
+  m_label.empty();
 }
 
+
 /* This does a linear search (from newer to older variables, in case
 ** we have shadowed names).
 ** It's possible to have a more efficient allocation and search method,
@@ -61,19 +51,20 @@ sp_pcontext::grow()
 sp_pvar_t *
 sp_pcontext::find_pvar(LEX_STRING *name)
 {
-  uint i = m_i;
+  uint i = m_pvar.elements;
 
   while (i-- > 0)
   {
-    uint len= (m_pvar[i].name.length > name->length ?
-	       m_pvar[i].name.length : name->length);
+    sp_pvar_t *p= find_pvar(i);
+    uint len= (p->name.length > name->length ?
+	       p->name.length : name->length);
 
     if (my_strncasecmp(system_charset_info,
 		       name->str,
-		       m_pvar[i].name.str,
+		       p->name.str,
 		       len) == 0)
     {
-      return m_pvar + i;
+      return p;
     }
   }
   return NULL;
@@ -83,26 +74,26 @@ void
 sp_pcontext::push(LEX_STRING *name, enum enum_field_types type,
 		  sp_param_mode_t mode)
 {
-  if (m_i >= m_pvar_size)
-    grow();
-  if (m_i < m_pvar_size)
+  sp_pvar_t *p= (sp_pvar_t *)sql_alloc(sizeof(sp_pvar_t));
+
+  if (p)
   {
-    if (m_i == m_framesize)
+    if (m_pvar.elements == m_framesize)
       m_framesize += 1;
-    m_pvar[m_i].name.str= name->str;
-    m_pvar[m_i].name.length= name->length,
-    m_pvar[m_i].type= type;
-    m_pvar[m_i].mode= mode;
-    m_pvar[m_i].offset= m_i;
-    m_pvar[m_i].isset= (mode == sp_param_out ? FALSE : TRUE);
-    m_i += 1;
+    p->name.str= name->str;
+    p->name.length= name->length;
+    p->type= type;
+    p->mode= mode;
+    p->offset= m_pvar.elements;
+    p->isset= (mode == sp_param_out ? FALSE : TRUE);
+    insert_dynamic(&m_pvar, (gptr)&p);
   }
 }
 
 sp_label_t *
 sp_pcontext::push_label(char *name, uint ip)
 {
-  sp_label_t *lab = (sp_label_t *)my_malloc(sizeof(sp_label_t), MYF(MY_WME));
+  sp_label_t *lab = (sp_label_t *)sql_alloc(sizeof(sp_label_t));
 
   if (lab)
   {
diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h
index c5b0f1d410b53e50cfb62347edd07c9d35bccc02..6900e18aa93b01565ae82db4bfd185bf04620fa0 100644
--- a/sql/sp_pcontext.h
+++ b/sql/sp_pcontext.h
@@ -53,6 +53,10 @@ class sp_pcontext : public Sql_alloc
 
   sp_pcontext();
 
+  // Free memory
+  void
+  destroy();
+
   inline uint
   max_framesize()
   {
@@ -62,7 +66,7 @@ class sp_pcontext : public Sql_alloc
   inline uint
   current_framesize()
   {
-    return m_i;
+    return m_pvar.elements;
   }
 
   inline uint
@@ -75,21 +79,25 @@ class sp_pcontext : public Sql_alloc
   inline void
   set_params()
   {
-    m_params= m_i;
+    m_params= m_pvar.elements;
   }
 
   inline void
   set_type(uint i, enum enum_field_types type)
   {
-    if (i < m_i)
-      m_pvar[i].type= type;
+    sp_pvar_t *p= find_pvar(i);
+
+    if (p)
+      p->type= type;
   }
 
   inline void
   set_isset(uint i, my_bool val)
   {
-    if (i < m_i)
-      m_pvar[i].isset= val;
+    sp_pvar_t *p= find_pvar(i);
+
+    if (p)
+      p->isset= val;
   }
 
   void
@@ -99,8 +107,8 @@ class sp_pcontext : public Sql_alloc
   inline void
   pop(uint num = 1)
   {
-    if (num < m_i)
-      m_i -= num;
+    while (num--)
+      pop_dynamic(&m_pvar);
   }
 
   // Find by name
@@ -111,9 +119,13 @@ class sp_pcontext : public Sql_alloc
   sp_pvar_t *
   find_pvar(uint i)
   {
-    if (i >= m_i)
-      return NULL;
-    return m_pvar+i;
+    sp_pvar_t *p;
+
+    if (i < m_pvar.elements)
+      get_dynamic(&m_pvar, (gptr)&p, i);
+    else
+      p= NULL;
+    return p;
   }
 
   sp_label_t *
@@ -138,13 +150,8 @@ class sp_pcontext : public Sql_alloc
 
   uint m_params;		// The number of parameters
   uint m_framesize;		// The maximum framesize
-  uint m_i;			// The current index (during parsing)
-
-  sp_pvar_t *m_pvar;
-  uint m_pvar_size;		// Current size of m_pvar.
 
-  void
-  grow();
+  DYNAMIC_ARRAY m_pvar;
 
   List<sp_label_t> m_label;	// The label list
   uint m_genlab;		// Gen. label counter
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index d5197b6507193ea4aa4948499af5dab7c7c194cb..7247d5bc3d092b3096d8d5b90f4e482598af27d0 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -191,6 +191,9 @@ THD::THD():user_time(0), is_fatal_error(0),
     pthread_mutex_unlock(&LOCK_thread_count);
     randominit(&rand, tmp + (ulong) &rand, tmp + (ulong) ::query_id);
   }
+
+  /* QQ init the temporary function cache */
+  spfuns.empty();
 }
 
 
@@ -282,6 +285,10 @@ void THD::cleanup(void)
     pthread_mutex_unlock(&LOCK_user_locks);
     ull= 0;
   }
+
+  //  extern void sp_clear_function_cache(THD *);
+  //  sp_clear_function_cache(this);
+
   cleanup_done=1;
   DBUG_VOID_RETURN;
 }
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index cc9c2decf5b8719f5ff9caa98fa7383bd16080e8..4d9cb0729324c1d351e68403b6a9c573ebbef2ad 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -2810,6 +2810,7 @@ mysql_execute_command(THD *thd)
       sp_head *sph= sp_find_function(thd, &lex->udf.name);
       if (sph)
       {
+	sph->destroy();		// QQ Free memory. Remove this when caching!!!
 	net_printf(thd, ER_UDF_EXISTS, lex->udf.name.str);
 	goto error;
       }
@@ -3046,6 +3047,8 @@ mysql_execute_command(THD *thd)
 	thd->net.no_send_ok= nsok;
 #endif
 
+	sp->destroy();		// QQ Free memory. Remove this when caching!!!
+
 	if (res == 0)
 	  send_ok(thd);
 	else
@@ -3071,6 +3074,7 @@ mysql_execute_command(THD *thd)
       {
 	/* QQ This is an no-op right now, since we haven't
 	      put the characteristics in yet. */
+	sp->destroy();		// QQ Free memory. Remove this when caching!!!
 	send_ok(thd);
       }
     }