From 13987057f64b97776e9491447186bebc54ee1439 Mon Sep 17 00:00:00 2001
From: "pem@mysql.comhem.se" <>
Date: Fri, 19 Mar 2004 19:01:54 +0100
Subject: [PATCH] WL#1366: Use the schema (db) associated with an SP. Phase 3:
 Made qualified names work for functions as well.

---
 mysql-test/r/sp-security.result |  25 ++++++
 mysql-test/t/sp-security.test   |  20 ++++-
 sql/item_func.cc                |  15 +++-
 sql/item_func.h                 |   6 ++
 sql/mysql_priv.h                |   3 +-
 sql/sp.cc                       | 106 ++++++++++++++++++++---
 sql/sp_head.cc                  |  12 ++-
 sql/sql_db.cc                   |  95 +++++++++------------
 sql/sql_yacc.yy                 | 147 +++++++++++++++++---------------
 9 files changed, 286 insertions(+), 143 deletions(-)

diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result
index 51439e0878..25bceb0f54 100644
--- a/mysql-test/r/sp-security.result
+++ b/mysql-test/r/sp-security.result
@@ -10,14 +10,27 @@ insert into db1_secret.t1 values (user(), i);
 show procedure status like 'stamp';
 Db	Name	Type	Definer	Modified	Created	Security_type	Comment
 db1_secret	stamp	PROCEDURE	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	DEFINER	
+create function db() returns varchar(64) return database();
+show function status like 'db';
+Db	Name	Type	Definer	Modified	Created	Security_type	Comment
+db1_secret	db	FUNCTION	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	DEFINER	
 call stamp(1);
 select * from t1;
 u	i
 root@localhost	1
+select db();
+db()
+db1_secret
 call db1_secret.stamp(2);
+select db1_secret.db();
+db1_secret.db()
+db1_secret
 select * from db1_secret.t1;
 ERROR 42000: Access denied for user: 'user1'@'localhost' to database 'db1_secret'
 call db1_secret.stamp(3);
+select db1_secret.db();
+db1_secret.db()
+db1_secret
 select * from db1_secret.t1;
 ERROR 42000: Access denied for user: ''@'localhost' to database 'db1_secret'
 select * from t1;
@@ -29,6 +42,10 @@ alter procedure stamp sql security invoker;
 show procedure status like 'stamp';
 Db	Name	Type	Definer	Modified	Created	Security_type	Comment
 db1_secret	stamp	PROCEDURE	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	INVOKER	
+alter function db sql security invoker;
+show function status like 'db';
+Db	Name	Type	Definer	Modified	Created	Security_type	Comment
+db1_secret	db	FUNCTION	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	INVOKER	
 call stamp(4);
 select * from t1;
 u	i
@@ -36,10 +53,17 @@ root@localhost	1
 user1@localhost	2
 anon@localhost	3
 root@localhost	4
+select db();
+db()
+db1_secret
 call db1_secret.stamp(5);
 ERROR 42000: Access denied for user: 'user1'@'localhost' to database 'db1_secret'
+select db1_secret.db();
+ERROR 42000: Access denied for user: 'user1'@'localhost' to database 'db1_secret'
 call db1_secret.stamp(6);
 ERROR 42000: Access denied for user: ''@'localhost' to database 'db1_secret'
+select db1_secret.db();
+ERROR 42000: Access denied for user: ''@'localhost' to database 'db1_secret'
 drop database if exists db2;
 create database db2;
 use db2;
@@ -74,6 +98,7 @@ s1
 2
 2
 drop procedure db1_secret.stamp;
+drop function db1_secret.db;
 drop procedure db2.p;
 drop procedure db2.q;
 use test;
diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test
index 2d089e72d0..ae97768412 100644
--- a/mysql-test/t/sp-security.test
+++ b/mysql-test/t/sp-security.test
@@ -21,15 +21,20 @@ use db1_secret;
 
 create table t1 ( u varchar(64), i int );
 
-# Our test procedure
+# A test procedure and function
 create procedure stamp(i int)
   insert into db1_secret.t1 values (user(), i);
 --replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
 show procedure status like 'stamp';
 
+create function db() returns varchar(64) return database();
+--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
+show function status like 'db';
+
 # root can, of course
 call stamp(1);
 select * from t1;
+select db();
 
 connect (con2user1,localhost,user1,,);
 connect (con3anon,localhost,anon,,);
@@ -41,6 +46,7 @@ connection con2user1;
 
 # This should work...
 call db1_secret.stamp(2);
+select db1_secret.db();
 
 # ...but not this
 --error 1044
@@ -53,6 +59,7 @@ connection con3anon;
 
 # This should work...
 call db1_secret.stamp(3);
+select db1_secret.db();
 
 # ...but not this
 --error 1044
@@ -71,9 +78,14 @@ alter procedure stamp sql security invoker;
 --replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
 show procedure status like 'stamp';
 
+alter function db sql security invoker;
+--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
+show function status like 'db';
+
 # root still can
 call stamp(4);
 select * from t1;
+select db();
 
 #
 # User1 cannot
@@ -83,6 +95,8 @@ connection con2user1;
 # This should not work
 --error 1044
 call db1_secret.stamp(5);
+--error 1044
+select db1_secret.db();
 
 #
 # Anonymous cannot
@@ -92,7 +106,8 @@ connection con3anon;
 # This should not work
 --error 1044
 call db1_secret.stamp(6);
-
+--error 1044
+select db1_secret.db();
 
 #
 # BUG#2777
@@ -149,6 +164,7 @@ select * from t2;
 # Clean up
 connection con1root;
 drop procedure db1_secret.stamp;
+drop function db1_secret.db;
 drop procedure db2.p;
 drop procedure db2.q;
 use test;
diff --git a/sql/item_func.cc b/sql/item_func.cc
index fdb0a5e524..2a74f2801c 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -3120,7 +3120,11 @@ Item_func_sp::execute(Item **itp)
   if (! m_sp)
     m_sp= sp_find_function(thd, m_name);
   if (! m_sp)
+  {
+    my_printf_error(ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), MYF(0),
+		    "FUNCTION", m_name->m_qname);
     DBUG_RETURN(-1);
+  }
 
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
   sp_change_security_context(thd, m_sp, &save_ctx);
@@ -3147,6 +3151,8 @@ Item_func_sp::field_type() const
     DBUG_PRINT("info", ("m_returns = %d", m_sp->m_returns));
     DBUG_RETURN(m_sp->m_returns);
   }
+  my_printf_error(ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), MYF(0),
+		  "FUNCTION", m_name->m_qname);
   DBUG_RETURN(MYSQL_TYPE_STRING);
 }
 
@@ -3162,6 +3168,8 @@ Item_func_sp::result_type() const
   {
     DBUG_RETURN(m_sp->result());
   }
+  my_printf_error(ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), MYF(0),
+		  "FUNCTION", m_name->m_qname);
   DBUG_RETURN(STRING_RESULT);
 }
 
@@ -3172,7 +3180,12 @@ Item_func_sp::fix_length_and_dec()
 
   if (! m_sp)
     m_sp= sp_find_function(current_thd, m_name);
-  if (m_sp)
+  if (! m_sp)
+  {
+    my_printf_error(ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), MYF(0),
+		    "FUNCTION", m_name->m_qname);
+  }
+  else
   {
     switch (m_sp->result()) {
     case STRING_RESULT:
diff --git a/sql/item_func.h b/sql/item_func.h
index e68826ca56..6d313a8ea6 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -1107,7 +1107,10 @@ public:
     Item *it;
 
     if (execute(&it))
+    {
+      null_value= 1;
       return 0.0;
+    }
     return it->val();
   }
 
@@ -1116,7 +1119,10 @@ public:
     Item *it;
 
     if (execute(&it))
+    {
+      null_value= 1;
       return NULL;
+    }
     return it->val_str(str);
   }
 
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 21822e02d2..e17847ebe2 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -445,8 +445,7 @@ int mysql_rm_table_part2_with_lock(THD *thd, TABLE_LIST *tables,
 int quick_rm_table(enum db_type base,const char *db,
 		   const char *table_name);
 bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list);
-bool mysql_change_db(THD *thd,const char *name,
-		     bool empty_is_ok=0, bool no_access_check=0);
+bool mysql_change_db(THD *thd,const char *name);
 void mysql_parse(THD *thd,char *inBuf,uint length);
 bool is_update_query(enum enum_sql_command command);
 void free_items(Item *item);
diff --git a/sql/sp.cc b/sql/sp.cc
index 1b58c709e4..389c627f5f 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -16,6 +16,7 @@
 
 
 #include "mysql_priv.h"
+#include "sql_acl.h"
 #include "sp.h"
 #include "sp_head.h"
 #include "sp_cache.h"
@@ -960,19 +961,104 @@ sp_use_new_db(THD *thd, char *newdb, char *olddb, uint olddblen,
   }
 }
 
+/*
+  Change database.
+
+  SYNOPSIS
+    sp_change_db()
+    thd		    Thread handler
+    name	    Database name
+    empty_is_ok     True= it's ok with "" as name
+    no_access_check True= don't do access check
+
+  DESCRIPTION
+    This is the same as mysql_change_db(), but with some extra
+    arguments for Stored Procedure usage; doing implicit "use" 
+    when executing an SP in a different database.
+    We also use different error routines, since this might be
+    invoked from a function when executing a query or statement.
+    Note: We would have prefered to reuse mysql_change_db(), but
+      the error handling in particular made that too awkward, so
+      we (reluctantly) have a "copy" here.
+
+  RETURN VALUES
+    0	ok
+    1	error
+*/
+
 int
-sp_change_db(THD *thd, char *db, bool no_access_check)
+sp_change_db(THD *thd, char *name, bool no_access_check)
 {
-  int ret;
-  ulong dbaccess= thd->db_access; /* mysql_change_db() changes this */ 
-  my_bool nsok= thd->net.no_send_ok; /* mysql_change_db() does send_ok() */
-  thd->net.no_send_ok= TRUE;
+  int length, db_length;
+  char *dbname=my_strdup((char*) name,MYF(MY_WME));
+  char	path[FN_REFLEN];
+  ulong db_access;
+  HA_CREATE_INFO create;
   DBUG_ENTER("sp_change_db");
-  DBUG_PRINT("enter", ("db: %s, no_access_check: %d", db, no_access_check));
+  DBUG_PRINT("enter", ("db: %s, no_access_check: %d", name, no_access_check));
 
-  ret= mysql_change_db(thd, db, 1, no_access_check);
+  db_length= (!dbname ? 0 : strip_sp(dbname));
+  if (dbname && db_length)
+  {
+    if ((db_length > NAME_LEN) || check_db_name(dbname))
+    {
+      my_printf_error(ER_WRONG_DB_NAME, ER(ER_WRONG_DB_NAME), MYF(0), dbname);
+      x_free(dbname);
+      DBUG_RETURN(1);
+    }
+  }
 
-  thd->net.no_send_ok= nsok;
-  thd->db_access= dbaccess;
-  DBUG_RETURN(ret);
+  if (dbname && db_length)
+  {
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+    if (! no_access_check)
+    {
+      if (test_all_bits(thd->master_access,DB_ACLS))
+	db_access=DB_ACLS;
+      else
+	db_access= (acl_get(thd->host,thd->ip, thd->priv_user,dbname,0) |
+		    thd->master_access);  
+      if (!(db_access & DB_ACLS) &&
+	  (!grant_option || check_grant_db(thd,dbname)))
+      {
+	my_printf_error(ER_DBACCESS_DENIED_ERROR, ER(ER_DBACCESS_DENIED_ERROR),
+			MYF(0),
+			thd->priv_user,
+			thd->priv_host,
+			dbname);
+	mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
+			thd->priv_user,
+			thd->priv_host,
+			dbname);
+	my_free(dbname,MYF(0));
+	DBUG_RETURN(1);
+      }
+    }
+#endif
+    (void) sprintf(path,"%s/%s",mysql_data_home,dbname);
+    length=unpack_dirname(path,path);		// Convert if not unix
+    if (length && path[length-1] == FN_LIBCHAR)
+      path[length-1]=0;				// remove ending '\'
+    if (access(path,F_OK))
+    {
+      my_printf_error(ER_BAD_DB_ERROR, ER(ER_BAD_DB_ERROR), MYF(0), dbname);
+      my_free(dbname,MYF(0));
+      DBUG_RETURN(1);
+    }
+  }
+
+  x_free(thd->db);
+  thd->db=dbname;				// THD::~THD will free this
+  thd->db_length=db_length;
+
+  if (dbname && db_length)
+  {
+    strmov(path+unpack_dirname(path,path), MY_DB_OPT_FILE);
+    load_db_opt(thd, path, &create);
+    thd->db_charset= create.default_table_charset ?
+      create.default_table_charset :
+      thd->variables.collation_server;
+    thd->variables.collation_database= thd->db_charset;
+  }
+  DBUG_RETURN(0);
 }
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index ebf74e25bb..c8be113e2e 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -389,12 +389,13 @@ sp_head::execute(THD *thd)
 	continue;
       }
     }
-  } while (ret == 0 && !thd->killed && !thd->query_error);
+  } while (ret == 0 && !thd->killed && !thd->query_error &&
+	   !thd->net.report_error);
 
  done:
   DBUG_PRINT("info", ("ret=%d killed=%d query_error=%d",
 		      ret, thd->killed, thd->query_error));
-  if (thd->killed || thd->query_error)
+  if (thd->killed || thd->query_error || thd->net.report_error)
     ret= -1;
   /* If the DB has changed, the pointer has changed too, but the
      original thd->db will then have been freed */
@@ -553,7 +554,12 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
   ret= execute(thd);
 
   // Don't copy back OUT values if we got an error
-  if (ret == 0 && csize > 0)
+  if (ret)
+  {
+    if (thd->net.report_error)
+      send_error(thd, 0, NullS);
+  }
+  else if (csize > 0)
   {
     List_iterator_fast<Item> li(*args);
     Item *it;
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 3ea6821ef8..bc6b30040d 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -595,8 +595,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
     1	error
 */
 
-bool mysql_change_db(THD *thd, const char *name,
-		     bool empty_is_ok, bool no_access_check)
+bool mysql_change_db(THD *thd, const char *name)
 {
   int length, db_length;
   char *dbname=my_strdup((char*) name,MYF(MY_WME));
@@ -605,76 +604,62 @@ bool mysql_change_db(THD *thd, const char *name,
   HA_CREATE_INFO create;
   DBUG_ENTER("mysql_change_db");
 
-  if ((!dbname || !(db_length=strip_sp(dbname))) && !empty_is_ok)
+  if (!dbname || !(db_length=strip_sp(dbname)))
   {
     x_free(dbname);				/* purecov: inspected */
     send_error(thd,ER_NO_DB_ERROR);	/* purecov: inspected */
     DBUG_RETURN(1);				/* purecov: inspected */
   }
-  if (!empty_is_ok || (dbname && db_length))
+  if ((db_length > NAME_LEN) || check_db_name(dbname))
   {
-    if ((db_length > NAME_LEN) || check_db_name(dbname))
-    {
-      net_printf(thd, ER_WRONG_DB_NAME, dbname);
-      x_free(dbname);
-      DBUG_RETURN(1);
-    }
+    net_printf(thd, ER_WRONG_DB_NAME, dbname);
+    x_free(dbname);
+    DBUG_RETURN(1);
   }
   DBUG_PRINT("info",("Use database: %s", dbname));
-  if (!empty_is_ok || (dbname && db_length))
-  {
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
-    if (! no_access_check)
-    {
-      if (test_all_bits(thd->master_access,DB_ACLS))
-	db_access=DB_ACLS;
-      else
-	db_access= (acl_get(thd->host,thd->ip, thd->priv_user,dbname,0) |
-		    thd->master_access);  
-      if (!(db_access & DB_ACLS) &&
-	  (!grant_option || check_grant_db(thd,dbname)))
-      {
-	net_printf(thd,ER_DBACCESS_DENIED_ERROR,
-		   thd->priv_user,
-		   thd->priv_host,
-		   dbname);
-	mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
-			thd->priv_user,
-			thd->priv_host,
-			dbname);
-	my_free(dbname,MYF(0));
-	DBUG_RETURN(1);
-      }
-    }
+  if (test_all_bits(thd->master_access,DB_ACLS))
+    db_access=DB_ACLS;
+  else
+    db_access= (acl_get(thd->host,thd->ip, thd->priv_user,dbname,0) |
+		thd->master_access);
+  if (!(db_access & DB_ACLS) && (!grant_option || check_grant_db(thd,dbname)))
+  {
+    net_printf(thd,ER_DBACCESS_DENIED_ERROR,
+	       thd->priv_user,
+	       thd->priv_host,
+	       dbname);
+    mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
+		    thd->priv_user,
+		    thd->priv_host,
+		    dbname);
+    my_free(dbname,MYF(0));
+    DBUG_RETURN(1);
+  }
 #endif
-    (void) sprintf(path,"%s/%s",mysql_data_home,dbname);
-    length=unpack_dirname(path,path);		// Convert if not unix
-    if (length && path[length-1] == FN_LIBCHAR)
-      path[length-1]=0;				// remove ending '\'
-    if (access(path,F_OK))
-    {
-      net_printf(thd,ER_BAD_DB_ERROR,dbname);
-      my_free(dbname,MYF(0));
-      DBUG_RETURN(1);
-    }
+  (void) sprintf(path,"%s/%s",mysql_data_home,dbname);
+  length=unpack_dirname(path,path);		// Convert if not unix
+  if (length && path[length-1] == FN_LIBCHAR)
+    path[length-1]=0;				// remove ending '\'
+  if (access(path,F_OK))
+  {
+    net_printf(thd,ER_BAD_DB_ERROR,dbname);
+    my_free(dbname,MYF(0));
+    DBUG_RETURN(1);
   }
   send_ok(thd);
   x_free(thd->db);
   thd->db=dbname;				// THD::~THD will free this
   thd->db_length=db_length;
-  if (!empty_is_ok || (dbname && db_length))
-  {
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
-    if (! no_access_check)
-      thd->db_access=db_access;
+  thd->db_access=db_access;
 #endif
-    strmov(path+unpack_dirname(path,path), MY_DB_OPT_FILE);
-    load_db_opt(thd, path, &create);
-    thd->db_charset= create.default_table_charset ?
-      create.default_table_charset :
-      thd->variables.collation_server;
-    thd->variables.collation_database= thd->db_charset;
-  }
+  strmov(path+unpack_dirname(path,path), MY_DB_OPT_FILE);
+  load_db_opt(thd, path, &create);
+  thd->db_charset= create.default_table_charset ?
+		   create.default_table_charset :
+		   thd->variables.collation_server;
+  thd->variables.collation_database= thd->db_charset;
   DBUG_RETURN(0);
 }
 
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 50f475eb68..d2f7e73b2b 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -3923,83 +3923,90 @@ simple_expr:
 	  { $$= new Item_func_round($3,$5,1); }
 	| TRUE_SYM
 	  { $$= new Item_int((char*) "TRUE",1,1); }
-	| IDENT_sys '(' udf_expr_list ')'
+	| ident '.' ident '(' udf_expr_list ')'
 	  {
-	    sp_name *name= sp_name_current_db_new(YYTHD, $1);
-
-	    if (sp_function_exists(YYTHD, name))
-	    {
-	      LEX *lex= Lex;
+	    LEX *lex= Lex;
+	    sp_name *name= new sp_name($1, $3);
 
-	      sp_add_fun_to_lex(lex, name);
-	      if ($3)
-	        $$= new Item_func_sp(name, *$3);
-	      else
-	        $$= new Item_func_sp(name);
-	    }
+	    name->init_qname(YYTHD);
+	    sp_add_fun_to_lex(Lex, name);
+	    if ($5)
+	      $$= new Item_func_sp(name, *$5);
 	    else
-	    {
+	      $$= new Item_func_sp(name);
+	  }
+	| IDENT_sys '(' udf_expr_list ')'
+          {
 #ifdef HAVE_DLOPEN
-	      udf_func *udf;
+            udf_func *udf;
 
-	      if (using_udf_functions && (udf=find_udf($1.str, $1.length)))
-	      {
-		switch (udf->returns) {
-		case STRING_RESULT:
-		  if (udf->type == UDFTYPE_FUNCTION)
-		  {
-		    if ($3 != NULL)
-		      $$ = new Item_func_udf_str(udf, *$3);
-		    else
-		      $$ = new Item_func_udf_str(udf);
-		  }
-		  else
-		  {
-		    if ($3 != NULL)
-		      $$ = new Item_sum_udf_str(udf, *$3);
-		    else
-		      $$ = new Item_sum_udf_str(udf);
-		  }
-		  break;
-		case REAL_RESULT:
-		  if (udf->type == UDFTYPE_FUNCTION)
-		  {
-		    if ($3 != NULL)
-		      $$ = new Item_func_udf_float(udf, *$3);
-		    else
-		      $$ = new Item_func_udf_float(udf);
-		  }
-		  else
-		  {
-		    if ($3 != NULL)
-		      $$ = new Item_sum_udf_float(udf, *$3);
-		    else
-		      $$ = new Item_sum_udf_float(udf);
-		  }
-		  break;
-		case INT_RESULT:
-		  if (udf->type == UDFTYPE_FUNCTION)
-		  {
-		    if ($3 != NULL)
-		      $$ = new Item_func_udf_int(udf, *$3);
-		    else
-		      $$ = new Item_func_udf_int(udf);
-		  }
-		  else
-		  {
-		    if ($3 != NULL)
-		      $$ = new Item_sum_udf_int(udf, *$3);
-		    else
-		      $$ = new Item_sum_udf_int(udf);
-		  }
-		  break;
-		default:
-		  YYABORT;
-		}
-	      }
+            if (using_udf_functions && (udf=find_udf($1.str, $1.length)))
+            {
+              switch (udf->returns) {
+              case STRING_RESULT:
+                if (udf->type == UDFTYPE_FUNCTION)
+                {
+                  if ($3 != NULL)
+                    $$ = new Item_func_udf_str(udf, *$3);
+                  else
+                    $$ = new Item_func_udf_str(udf);
+                }
+                else
+                {
+                  if ($3 != NULL)
+                    $$ = new Item_sum_udf_str(udf, *$3);
+                  else
+                    $$ = new Item_sum_udf_str(udf);
+                }
+                break;
+              case REAL_RESULT:
+                if (udf->type == UDFTYPE_FUNCTION)
+                {
+                  if ($3 != NULL)
+                    $$ = new Item_func_udf_float(udf, *$3);
+                  else
+                    $$ = new Item_func_udf_float(udf);
+                }
+                else
+                {
+                  if ($3 != NULL)
+                    $$ = new Item_sum_udf_float(udf, *$3);
+                  else
+                    $$ = new Item_sum_udf_float(udf);
+                }
+                break;
+              case INT_RESULT:
+                if (udf->type == UDFTYPE_FUNCTION)
+                {
+                  if ($3 != NULL)
+                    $$ = new Item_func_udf_int(udf, *$3);
+                  else
+                    $$ = new Item_func_udf_int(udf);
+                }
+                else
+                {
+                  if ($3 != NULL)
+                    $$ = new Item_sum_udf_int(udf, *$3);
+                  else
+                    $$ = new Item_sum_udf_int(udf);
+                }
+                break;
+              default:
+                YYABORT;
+              }
+            }
+            else
 #endif /* HAVE_DLOPEN */
+            {
+              sp_name *name= sp_name_current_db_new(YYTHD, $1);
+
+              sp_add_fun_to_lex(Lex, name);
+              if ($3)
+                $$= new Item_func_sp(name, *$3);
+              else
+                $$= new Item_func_sp(name);
 	    }
-	  }
+          }
 	| UNIQUE_USERS '(' text_literal ',' NUM ',' NUM ',' expr_list ')'
 	  {
             $$= new Item_func_unique_users($3,atoi($5.str),atoi($7.str), * $9);
-- 
2.30.9