List:Commits« Previous MessageNext Message »
From:marc.alff Date:October 17 2007 4:47am
Subject:bk commit into 5.1 tree (malff:1.2583)
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of malff. When malff does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2007-10-16 20:47:08-06:00, malff@stripped. +8 -0
  Manual merge of 5.0-runtime to 5.1-runtime

  mysql-test/r/sp-error.result@stripped, 2007-10-16 20:47:02-06:00,
malff@stripped. +14 -1
    Manual merge

  mysql-test/r/sp.result@stripped, 2007-10-16 20:47:03-06:00,
malff@stripped. +16 -0
    Manual merge

  mysql-test/r/udf.result@stripped, 2007-10-16 20:47:03-06:00,
malff@stripped. +10 -3
    Manual merge

  mysql-test/t/sp.test@stripped, 2007-10-16 20:47:03-06:00, malff@stripped.
+31 -0
    Manual merge

  mysql-test/t/udf.test@stripped, 2007-10-16 20:47:03-06:00, malff@stripped.
+2 -1
    Manual merge

  sql/item_create.cc@stripped, 2007-10-16 20:47:03-06:00, malff@stripped.
+19 -0
    Manual merge

  sql/sp_head.cc@stripped, 2007-10-16 20:47:04-06:00, malff@stripped. +30
-4
    Manual merge

  sql/sql_yacc.yy@stripped, 2007-10-16 20:47:04-06:00, malff@stripped. +259
-224
    Manual merge

diff -Nrup a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result
--- a/mysql-test/r/sp-error.result	2007-10-16 15:42:10 -06:00
+++ b/mysql-test/r/sp-error.result	2007-10-16 20:47:02 -06:00
@@ -1226,7 +1226,7 @@ ERROR 42S02: Unknown table 'c' in field 
 drop procedure bug15091;
 drop function if exists bug16896;
 create aggregate function bug16896() returns int return 1;
-ERROR 42000: AGGREGATE is not supported for stored functions
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near '() returns int return 1' at
line 1
 DROP PROCEDURE IF EXISTS bug14702;
 CREATE IF NOT EXISTS PROCEDURE bug14702()
 BEGIN
@@ -1510,3 +1510,16 @@ FETCH cur1 INTO c;
 select c;
 CLOSE cur1;
 END' at line 4
+DROP DATABASE IF EXISTS mysqltest;
+CREATE DATABASE mysqltest;
+USE mysqltest;
+DROP DATABASE mysqltest;
+SELECT inexistent(), 1 + ,;
+ERROR 42000: FUNCTION inexistent does not exist
+SELECT inexistent();
+ERROR 42000: FUNCTION inexistent does not exist
+SELECT .inexistent();
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near '()' at line 1
+SELECT ..inexistent();
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near '.inexistent()' at line 1
+USE test;
diff -Nrup a/mysql-test/r/sp.result b/mysql-test/r/sp.result
--- a/mysql-test/r/sp.result	2007-10-16 15:42:18 -06:00
+++ b/mysql-test/r/sp.result	2007-10-16 20:47:03 -06:00
@@ -6796,6 +6796,22 @@ f1()
 DROP TABLE t1;
 DROP FUNCTION f1;
 
+DROP PROCEDURE IF EXISTS db28318_a.t1;
+DROP PROCEDURE IF EXISTS db28318_b.t2;
+DROP DATABASE IF EXISTS db28318_a;
+DROP DATABASE IF EXISTS db28318_b;
+CREATE DATABASE db28318_a;
+CREATE DATABASE db28318_b;
+CREATE PROCEDURE db28318_a.t1() SELECT "db28318_a.t1";
+CREATE PROCEDURE db28318_b.t2() CALL t1();
+use db28318_a;
+CALL db28318_b.t2();
+ERROR 42000: PROCEDURE db28318_b.t1 does not exist
+DROP PROCEDURE db28318_a.t1;
+DROP PROCEDURE db28318_b.t2;
+DROP DATABASE db28318_a;
+DROP DATABASE db28318_b;
+use test;
 End of 5.0 tests
 
 #
diff -Nrup a/mysql-test/r/udf.result b/mysql-test/r/udf.result
--- a/mysql-test/r/udf.result	2007-10-16 15:42:27 -06:00
+++ b/mysql-test/r/udf.result	2007-10-16 20:47:03 -06:00
@@ -95,10 +95,10 @@ FR
 DROP TABLE bug19904;
 CREATE DEFINER=CURRENT_USER() FUNCTION should_not_parse
 RETURNS STRING SONAME "should_not_parse.so";
-ERROR HY000: Incorrect usage of SONAME and DEFINER
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'RETURNS STRING SONAME
"should_not_parse.so"' at line 2
 CREATE DEFINER=someone@somewhere FUNCTION should_not_parse
 RETURNS STRING SONAME "should_not_parse.so";
-ERROR HY000: Incorrect usage of SONAME and DEFINER
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'RETURNS STRING SONAME
"should_not_parse.so"' at line 2
 create table t1(f1 int);
 insert into t1 values(1),(2);
 explain select myfunc_int(f1) from t1 order by 1;
@@ -214,7 +214,7 @@ DROP FUNCTION IF EXISTS metaphon;
 CREATE FUNCTION metaphon(a int) RETURNS int
 return 0;
 CREATE FUNCTION metaphon RETURNS STRING SONAME "UDF_EXAMPLE_LIB";
-ERROR HY000: Function 'metaphon' already exists
+DROP FUNCTION metaphon;
 DROP FUNCTION metaphon;
 CREATE FUNCTION metaphon RETURNS STRING SONAME "UDF_EXAMPLE_LIB";
 CREATE FUNCTION metaphon(a int) RETURNS int
@@ -334,6 +334,13 @@ Qcache_queries_in_cache	0
 drop table t1;
 drop function metaphon;
 set GLOBAL query_cache_size=default;
+DROP DATABASE IF EXISTS mysqltest;
+CREATE DATABASE mysqltest;
+USE mysqltest;
+DROP DATABASE mysqltest;
+CREATE FUNCTION metaphon RETURNS STRING SONAME "UDF_EXAMPLE_LIB";
+DROP FUNCTION metaphon;
+USE test;
 CREATE TABLE const_len_bug (
 str_const varchar(4000),
 result1 varchar(4000),
diff -Nrup a/mysql-test/t/sp.test b/mysql-test/t/sp.test
--- a/mysql-test/t/sp.test	2007-10-16 15:42:39 -06:00
+++ b/mysql-test/t/sp.test	2007-10-16 20:47:03 -06:00
@@ -7871,6 +7871,37 @@ DROP FUNCTION f1;
 
 ###########################################################################
 
+#
+# Bug#28318 (CREATE FUNCTION (UDF) requires a schema)
+#
+
+--disable_warnings
+DROP PROCEDURE IF EXISTS db28318_a.t1;
+DROP PROCEDURE IF EXISTS db28318_b.t2;
+DROP DATABASE IF EXISTS db28318_a;
+DROP DATABASE IF EXISTS db28318_b;
+--enable_warnings
+
+CREATE DATABASE db28318_a;
+CREATE DATABASE db28318_b;
+
+CREATE PROCEDURE db28318_a.t1() SELECT "db28318_a.t1";
+CREATE PROCEDURE db28318_b.t2() CALL t1();
+
+use db28318_a;
+
+# In db28318_b.t2, t1 refers to db28318_b.t1
+--error ER_SP_DOES_NOT_EXIST
+CALL db28318_b.t2();
+
+DROP PROCEDURE db28318_a.t1;
+DROP PROCEDURE db28318_b.t2;
+DROP DATABASE db28318_a;
+DROP DATABASE db28318_b;
+use test;
+
+###########################################################################
+
 --echo End of 5.0 tests
 
 ###########################################################################
diff -Nrup a/mysql-test/t/udf.test b/mysql-test/t/udf.test
--- a/mysql-test/t/udf.test	2007-10-16 15:41:27 -06:00
+++ b/mysql-test/t/udf.test	2007-10-16 20:47:03 -06:00
@@ -206,10 +206,11 @@ DROP FUNCTION IF EXISTS metaphon;
 CREATE FUNCTION metaphon(a int) RETURNS int
 return 0;
 
+# this currently passes, and eclipse the stored function
 --replace_result $UDF_EXAMPLE_LIB UDF_EXAMPLE_LIB
---error ER_UDF_EXISTS
 eval CREATE FUNCTION metaphon RETURNS STRING SONAME "$UDF_EXAMPLE_LIB";
 
+DROP FUNCTION metaphon;
 DROP FUNCTION metaphon;
 
 --replace_result $UDF_EXAMPLE_LIB UDF_EXAMPLE_LIB
diff -Nrup a/sql/item_create.cc b/sql/item_create.cc
--- a/sql/item_create.cc	2007-08-29 07:32:22 -06:00
+++ b/sql/item_create.cc	2007-10-16 20:47:03 -06:00
@@ -2326,6 +2326,25 @@ Item*
 Create_qfunc::create(THD *thd, LEX_STRING name, List<Item> *item_list)
 {
   LEX_STRING db;
+
+  if (! thd->db && ! thd->lex->sphead)
+  {
+    /*
+      The proper error message should be in the lines of:
+        Can't resolve <name>() to a function call,
+        because this function:
+        - is not a native function,
+        - is not a user defined function,
+        - can not match a qualified (read: stored) function
+          since no database is selected.
+      Reusing ER_SP_DOES_NOT_EXIST have a message consistent with
+      the case when a default database exist, see Create_sp_func::create().
+    */
+    my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
+             "FUNCTION", name.str);
+    return NULL;
+  }
+
   if (thd->lex->copy_db_to(&db.str, &db.length))
     return NULL;
 
diff -Nrup a/sql/sp_head.cc b/sql/sp_head.cc
--- a/sql/sp_head.cc	2007-10-16 15:42:56 -06:00
+++ b/sql/sp_head.cc	2007-10-16 20:47:04 -06:00
@@ -386,17 +386,43 @@ sp_eval_expr(THD *thd, Field *result_fie
  *
  */
 
+sp_name::sp_name(THD *thd, char *key, uint key_len)
+{
+  m_sroutines_key.str= key;
+  m_sroutines_key.length= key_len;
+  m_qname.str= ++key;
+  m_qname.length= key_len - 1;
+  if ((m_name.str= strchr(m_qname.str, '.')))
+  {
+    m_db.length= m_name.str - key;
+    m_db.str= strmake_root(thd->mem_root, key, m_db.length);
+    m_name.str++;
+    m_name.length= m_qname.length - m_db.length - 1;
+  }
+  else
+  {
+    m_name.str= m_qname.str;
+    m_name.length= m_qname.length;
+    m_db.str= 0;
+    m_db.length= 0;
+  }
+  m_explicit_name= false;
+}
+
 void
 sp_name::init_qname(THD *thd)
 {
-  m_sroutines_key.length=  m_db.length + m_name.length + 2;
+  const uint dot= !!m_db.length;
+  /* m_sroutines format: m_type + [database + dot] + name + nul */
+  m_sroutines_key.length= 1 + m_db.length + dot + m_name.length;
   if (!(m_sroutines_key.str= (char*) thd->alloc(m_sroutines_key.length + 1)))
     return;
   m_qname.length= m_sroutines_key.length - 1;
   m_qname.str= m_sroutines_key.str + 1;
-  sprintf(m_qname.str, "%.*s.%.*s",
-	  (int) m_db.length, (m_db.length ? m_db.str : ""),
-	  (int) m_name.length, m_name.str);
+  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);
 }
 
 
diff -Nrup a/sql/sql_yacc.yy b/sql/sql_yacc.yy
--- a/sql/sql_yacc.yy	2007-10-16 15:43:07 -06:00
+++ b/sql/sql_yacc.yy	2007-10-16 20:47:04 -06:00
@@ -1211,8 +1211,6 @@ bool my_yyoverflow(short **a, YYSTYPE **
 
 %type <cast_type> cast_type
 
-%type <udf_type> udf_func_type
-
 %type <symbol> keyword keyword_sp
 
 %type <lex_user> user grant_user
@@ -1254,7 +1252,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
         ref_list opt_on_delete opt_on_delete_list opt_on_delete_item use
         opt_delete_options opt_delete_option varchar nchar nvarchar
         opt_outer table_list table_name table_alias_ref_list table_alias_ref
-	opt_option opt_place
+        opt_option opt_place
         opt_attribute opt_attribute_list attribute column_list column_list_id
         opt_column_list grant_privileges grant_ident grant_list grant_option
         object_privilege object_privilege_list user_list rename_list
@@ -1272,14 +1270,15 @@ bool my_yyoverflow(short **a, YYSTYPE **
         statement sp_suid
         sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa
         load_data opt_field_or_var_spec fields_or_vars opt_load_data_set_spec
-        definer view_replace_or_algorithm view_replace
+        view_replace_or_algorithm view_replace
         view_algorithm view_or_trigger_or_sp_or_event
-        view_or_trigger_or_sp_or_event_tail
+        definer_tail no_definer_tail
         view_suid view_tail view_list_opt view_list view_select
-        view_check_option trigger_tail sp_tail
+        view_check_option trigger_tail sp_tail sf_tail udf_tail event_tail
         install uninstall partition_entry binlog_base64_event
         init_key_options key_options key_opts key_opt key_using_alg
         server_def server_options_list server_option
+        definer_opt no_definer definer
 END_OF_INPUT
 
 %type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt
@@ -2002,181 +2001,6 @@ sp_name:
           }
         ;
 
-create_function_tail:
-          RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys
-          {
-            THD *thd= YYTHD;
-            LEX *lex= thd->lex;
-            if (lex->definer != NULL)
-            {
-              /*
-                DEFINER is a concept meaningful when interpreting SQL code.
-                UDF functions are compiled.
-                Using DEFINER with UDF has therefore no semantic,
-                and is considered a parsing error.
-              */
-              my_error(ER_WRONG_USAGE, MYF(0), "SONAME", "DEFINER");
-              MYSQL_YYABORT;
-            }
-            if (is_native_function(thd, & lex->spname->m_name))
-            {
-              my_error(ER_NATIVE_FCT_NAME_COLLISION, MYF(0),
-                       lex->spname->m_name.str);
-              MYSQL_YYABORT;
-            }
-            lex->sql_command = SQLCOM_CREATE_FUNCTION;
-            lex->udf.name = lex->spname->m_name;
-            lex->udf.returns=(Item_result) $2;
-            lex->udf.dl=$4.str;
-          }
-        | '('
-          {
-            THD *thd= YYTHD;
-            LEX *lex= thd->lex;
-            Lex_input_stream *lip= thd->m_lip;
-            sp_head *sp;
-            const char* tmp_param_begin;
-
-            /* 
-              First check if AGGREGATE was used, in that case it's a
-              syntax error.
-            */
-            if (lex->udf.type == UDFTYPE_AGGREGATE)
-            {
-              my_error(ER_SP_NO_AGGREGATE, MYF(0));
-              MYSQL_YYABORT;
-            }
-
-            if (lex->sphead)
-            {
-              my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "FUNCTION");
-              MYSQL_YYABORT;
-            }
-            /* Order is important here: new - reset - init */
-            sp= new sp_head();
-            sp->reset_thd_mem_root(thd);
-            sp->init(lex);
-            sp->init_sp_name(thd, lex->spname);
-
-            sp->m_type= TYPE_ENUM_FUNCTION;
-            lex->sphead= sp;
-            /*
-              We have to turn off CLIENT_MULTI_QUERIES while parsing a
-              stored procedure, otherwise yylex will chop it into pieces
-              at each ';'.
-            */
-            $<ulong_num>$= thd->client_capabilities & CLIENT_MULTI_QUERIES;
-            thd->client_capabilities &= ~CLIENT_MULTI_QUERIES;
-
-            tmp_param_begin= lip->get_cpp_tok_start();
-            tmp_param_begin++;
-            lex->sphead->m_param_begin= tmp_param_begin;
-          }
-          sp_fdparam_list ')'
-          {
-            THD *thd= YYTHD;
-            LEX *lex= thd->lex;
-            Lex_input_stream *lip= thd->m_lip;
-
-            lex->sphead->m_param_end= lip->get_cpp_tok_start();
-          }
-          RETURNS_SYM
-          {
-            LEX *lex= Lex;
-            lex->charset= NULL;
-            lex->length= lex->dec= NULL;
-            lex->interval_list.empty();
-            lex->type= 0;
-          }
-          type
-          {
-            LEX *lex= Lex;
-            sp_head *sp= lex->sphead;
-            /*
-              This was disabled in 5.1.12. See bug #20701
-              When collation support in SP is implemented, then this test
-              should be removed.
-            */
-            if (($8 == MYSQL_TYPE_STRING || $8 == MYSQL_TYPE_VARCHAR)
-                && (lex->type & BINCMP_FLAG))
-            {
-              my_error(ER_NOT_SUPPORTED_YET, MYF(0), "return value collation");
-              MYSQL_YYABORT;
-            }
-
-            if (sp->fill_field_definition(YYTHD, lex,
-                                          (enum enum_field_types) $8,
-                                          &sp->m_return_field_def))
-              MYSQL_YYABORT;
-
-            bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
-          }
-          sp_c_chistics
-          {
-            THD *thd= YYTHD;
-            LEX *lex= thd->lex;
-            Lex_input_stream *lip= thd->m_lip;
-
-            lex->sphead->m_chistics= &lex->sp_chistics;
-            lex->sphead->set_body_start(thd, lip->get_cpp_tok_start());
-          }
-          sp_proc_stmt
-          {
-            THD *thd= YYTHD;
-            LEX *lex= thd->lex;
-            sp_head *sp= lex->sphead;
-
-            if (sp->is_not_allowed_in_function("function"))
-              MYSQL_YYABORT;
-
-            lex->sql_command= SQLCOM_CREATE_SPFUNCTION;
-            sp->set_stmt_end(thd);
-            if (!(sp->m_flags & sp_head::HAS_RETURN))
-            {
-              my_error(ER_SP_NORETURN, MYF(0), sp->m_qname.str);
-              MYSQL_YYABORT;
-            }
-            if (is_native_function(thd, & sp->m_name))
-            {
-              /*
-                This warning will be printed when
-                [1] A client query is parsed,
-                [2] A stored function is loaded by db_load_routine.
-                Printing the warning for [2] is intentional, to cover the
-                following scenario:
-                - A user define a SF 'foo' using MySQL 5.N
-                - An application uses select foo(), and works.
-                - MySQL 5.{N+1} defines a new native function 'foo', as
-                part of a new feature.
-                - MySQL 5.{N+1} documentation is updated, and should mention
-                that there is a potential incompatible change in case of
-                existing stored function named 'foo'.
-                - The user deploys 5.{N+1}. At this point, 'select foo()'
-                means something different, and the user code is most likely
-                broken (it's only safe if the code is 'select db.foo()').
-                With a warning printed when the SF is loaded (which has to occur
-                before the call), the warning will provide a hint explaining
-                the root cause of a later failure of 'select foo()'.
-                With no warning printed, the user code will fail with no
-                apparent reason.
-                Printing a warning each time db_load_routine is executed for
-                an ambiguous function is annoying, since that can happen a lot,
-                but in practice should not happen unless there *are* name
-                collisions.
-                If a collision exists, it should not be silenced but fixed.
-              */
-              push_warning_printf(thd,
-                                  MYSQL_ERROR::WARN_LEVEL_NOTE,
-                                  ER_NATIVE_FCT_NAME_COLLISION,
-                                  ER(ER_NATIVE_FCT_NAME_COLLISION),
-                                  sp->m_name.str);
-            }
-            /* Restore flag if it was cleared above */
-            thd->client_capabilities |= $<ulong_num>2;
-            sp->restore_thd_mem_root(thd);
-          }
-        ;
-
 sp_a_chistics:
           /* Empty */ {}
         | sp_a_chistics sp_chistic {}
@@ -4489,11 +4313,10 @@ create_table_option:
           }
         | TRANSACTIONAL_SYM opt_equal ulong_num
           {
-	    Lex->create_info.used_fields|= HA_CREATE_USED_TRANSACTIONAL;
+            Lex->create_info.used_fields|= HA_CREATE_USED_TRANSACTIONAL;
             Lex->create_info.transactional= ($3 != 0 ? HA_CHOICE_YES :
-        				     HA_CHOICE_NO);
+              HA_CHOICE_NO);
           }
-
         ;
 
 default_charset:
@@ -4575,7 +4398,7 @@ row_types:
         | COMPRESSED_SYM { $$= ROW_TYPE_COMPRESSED; }
         | REDUNDANT_SYM  { $$= ROW_TYPE_REDUNDANT; }
         | COMPACT_SYM    { $$= ROW_TYPE_COMPACT; }
- 	| PAGE_SYM       { $$= ROW_TYPE_PAGE; }
+        | PAGE_SYM       { $$= ROW_TYPE_PAGE; }
         ;
 
 merge_insert_types:
@@ -4589,10 +4412,6 @@ opt_select_from:
         | select_from select_lock_type
         ;
 
-udf_func_type:
-          /* empty */ { $$ = UDFTYPE_FUNCTION; }
-        | AGGREGATE_SYM { $$ = UDFTYPE_AGGREGATE; };
-
 udf_type:
           STRING_SYM {$$ = (int) STRING_RESULT; }
         | REAL {$$ = (int) REAL_RESULT; }
@@ -5437,7 +5256,7 @@ alter:
             lex->sql_command= SQLCOM_ALTER_FUNCTION;
             lex->spname= $3;
           }
-        | ALTER view_algorithm definer
+        | ALTER view_algorithm definer_opt
           {
             LEX *lex= Lex;
 
@@ -5450,7 +5269,7 @@ alter:
           }
           view_tail
           {}
-        | ALTER definer
+        | ALTER definer_opt
           /*
             We have two separate rules for ALTER VIEW rather that
             optional view_algorithm above, to resolve the ambiguity
@@ -5469,7 +5288,7 @@ alter:
           }
           view_tail
           {}
-        | ALTER definer EVENT_SYM sp_name
+        | ALTER definer_opt EVENT_SYM sp_name
           /*
             BE CAREFUL when you add a new rule to update the block where
             YYTHD->client_capabilities is set back to original value
@@ -5505,7 +5324,7 @@ alter:
           {
             /*
               $1 - ALTER
-              $2 - definer
+              $2 - definer_opt
               $3 - EVENT_SYM
               $4 - sp_name
               $5 - the block above
@@ -8559,9 +8378,11 @@ drop:
             lex->drop_if_exists=$3;
             lex->name= $4;
           }
-        | DROP FUNCTION_SYM if_exists sp_name
+        | DROP FUNCTION_SYM if_exists ident '.' ident
           {
-            LEX *lex=Lex;
+            THD *thd= YYTHD;
+            LEX *lex= thd->lex;
+            sp_name *spname;
             if (lex->sphead)
             {
               my_error(ER_SP_NO_DROP_SP, MYF(0), "FUNCTION");
@@ -8569,7 +8390,28 @@ drop:
             }
             lex->sql_command = SQLCOM_DROP_FUNCTION;
             lex->drop_if_exists= $3;
-            lex->spname= $4;
+            spname= new sp_name($4, $6, true);
+            spname->init_qname(thd);
+            lex->spname= spname;
+          }
+        | DROP FUNCTION_SYM if_exists ident
+          {
+            THD *thd= YYTHD;
+            LEX *lex= thd->lex;
+            LEX_STRING db= {0, 0};
+            sp_name *spname;
+            if (lex->sphead)
+            {
+              my_error(ER_SP_NO_DROP_SP, MYF(0), "FUNCTION");
+              MYSQL_YYABORT;
+            }
+            if (thd->db && lex->copy_db_to(&db.str, &db.length))
+              MYSQL_YYABORT;
+            lex->sql_command = SQLCOM_DROP_FUNCTION;
+            lex->drop_if_exists= $3;
+            spname= new sp_name(db, $4, false);
+            spname->init_qname(thd);
+            lex->spname= spname;
           }
         | DROP PROCEDURE if_exists sp_name
           {
@@ -8639,18 +8481,19 @@ table_name:
         ;
 
 table_alias_ref_list:
-        table_alias_ref
-        | table_alias_ref_list ',' table_alias_ref;
+          table_alias_ref
+        | table_alias_ref_list ',' table_alias_ref
+        ;
 
 table_alias_ref:
-	table_ident
-	{
-	  if (!Select->add_table_to_list(YYTHD, $1, NULL,
-                                         TL_OPTION_UPDATING | TL_OPTION_ALIAS,
-                                         Lex->lock_option ))
-	    MYSQL_YYABORT;
-	}
-	;
+          table_ident
+          {
+            if (!Select->add_table_to_list(YYTHD, $1, NULL,
+                                           TL_OPTION_UPDATING | TL_OPTION_ALIAS,
+                                           Lex->lock_option ))
+              MYSQL_YYABORT;
+          }
+        ;
 
 if_exists:
           /* empty */ { $$= 0; }
@@ -10625,7 +10468,7 @@ keyword_sp:
         | TEXT_SYM                 {}
         | THAN_SYM                 {}
         | TRANSACTION_SYM          {}
- 	| TRANSACTIONAL_SYM        {}
+        | TRANSACTIONAL_SYM        {}
         | TRIGGERS_SYM             {}
         | TIMESTAMP                {}
         | TIMESTAMP_ADD            {}
@@ -11855,21 +11698,29 @@ subselect_end:
 **************************************************************************/
 
 view_or_trigger_or_sp_or_event:
-          definer view_or_trigger_or_sp_or_event_tail
+          definer definer_tail
           {}
-        | view_replace_or_algorithm definer view_tail
+        | no_definer no_definer_tail
+          {}
+        | view_replace_or_algorithm definer_opt view_tail
           {}
         ;
 
-view_or_trigger_or_sp_or_event_tail:
+definer_tail:
           view_tail
-          {}
         | trigger_tail
-          {}
         | sp_tail
-          {}
+        | sf_tail
+        | event_tail
+        ;
+
+no_definer_tail:
+          view_tail
+        | trigger_tail
+        | sp_tail
+        | sf_tail
+        | udf_tail
         | event_tail
-          {}
         ;
 
 /**************************************************************************
@@ -11878,7 +11729,12 @@ view_or_trigger_or_sp_or_event_tail:
 
 **************************************************************************/
 
-definer:
+definer_opt:
+          no_definer
+        | definer
+        ;
+
+no_definer:
           /* empty */
           {
             /*
@@ -11890,7 +11746,10 @@ definer:
             */
             YYTHD->lex->definer= 0;
           }
-        | DEFINER_SYM EQ user
+        ;
+
+definer:
+          DEFINER_SYM EQ user
           {
             YYTHD->lex->definer= get_current_user(YYTHD, $3);
           }
@@ -12127,17 +11986,193 @@ trigger_tail:
 
 **************************************************************************/
 
-sp_tail:
-          udf_func_type remember_name FUNCTION_SYM sp_name
+udf_tail:
+          AGGREGATE_SYM remember_name FUNCTION_SYM ident
+          RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys
           {
-            LEX *lex=Lex;
-            lex->udf.type= $1;
+            THD *thd= YYTHD;
+            LEX *lex= thd->lex;
+            if (is_native_function(thd, & $4))
+            {
+              my_error(ER_NATIVE_FCT_NAME_COLLISION, MYF(0),
+                       $4.str);
+              MYSQL_YYABORT;
+            }
+            lex->sql_command = SQLCOM_CREATE_FUNCTION;
+            lex->udf.type= UDFTYPE_AGGREGATE;
             lex->stmt_definition_begin= $2;
-            lex->spname= $4;
+            lex->udf.name = $4;
+            lex->udf.returns=(Item_result) $6;
+            lex->udf.dl=$8.str;
           }
-          create_function_tail
-          {}
-        | PROCEDURE remember_name sp_name
+        | remember_name FUNCTION_SYM ident
+          RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys
+          {
+            THD *thd= YYTHD;
+            LEX *lex= thd->lex;
+            if (is_native_function(thd, & $3))
+            {
+              my_error(ER_NATIVE_FCT_NAME_COLLISION, MYF(0),
+                       $3.str);
+              MYSQL_YYABORT;
+            }
+            lex->sql_command = SQLCOM_CREATE_FUNCTION;
+            lex->udf.type= UDFTYPE_FUNCTION;
+            lex->stmt_definition_begin= $1;
+            lex->udf.name = $3;
+            lex->udf.returns=(Item_result) $5;
+            lex->udf.dl=$7.str;
+          }
+        ;
+
+sf_tail:
+          remember_name /* $1 */
+          FUNCTION_SYM /* $2 */
+          sp_name /* $3 */
+          '(' /* $4 */
+          { /* $5 */
+            THD *thd= YYTHD;
+            LEX *lex= thd->lex;
+            Lex_input_stream *lip= thd->m_lip;
+            sp_head *sp;
+            const char* tmp_param_begin;
+
+            lex->stmt_definition_begin= $1;
+            lex->spname= $3;
+
+            if (lex->sphead)
+            {
+              my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "FUNCTION");
+              MYSQL_YYABORT;
+            }
+            /* Order is important here: new - reset - init */
+            sp= new sp_head();
+            sp->reset_thd_mem_root(thd);
+            sp->init(lex);
+            sp->init_sp_name(thd, lex->spname);
+
+            sp->m_type= TYPE_ENUM_FUNCTION;
+            lex->sphead= sp;
+            /*
+              We have to turn off CLIENT_MULTI_QUERIES while parsing a
+              stored procedure, otherwise yylex will chop it into pieces
+              at each ';'.
+            */
+            $<ulong_num>$= thd->client_capabilities & CLIENT_MULTI_QUERIES;
+            thd->client_capabilities &= ~CLIENT_MULTI_QUERIES;
+
+            tmp_param_begin= lip->get_cpp_tok_start();
+            tmp_param_begin++;
+            lex->sphead->m_param_begin= tmp_param_begin;
+          }
+          sp_fdparam_list /* $6 */
+          ')' /* $7 */
+          { /* $8 */
+            THD *thd= YYTHD;
+            LEX *lex= thd->lex;
+            Lex_input_stream *lip= thd->m_lip;
+
+            lex->sphead->m_param_end= lip->get_cpp_tok_start();
+          }
+          RETURNS_SYM /* $9 */
+          { /* $10 */
+            LEX *lex= Lex;
+            lex->charset= NULL;
+            lex->length= lex->dec= NULL;
+            lex->interval_list.empty();
+            lex->type= 0;
+          }
+          type /* $11 */
+          { /* $12 */
+            LEX *lex= Lex;
+            sp_head *sp= lex->sphead;
+            /*
+              This was disabled in 5.1.12. See bug #20701
+              When collation support in SP is implemented, then this test
+              should be removed.
+            */
+            if (($11 == MYSQL_TYPE_STRING || $11 == MYSQL_TYPE_VARCHAR)
+                && (lex->type & BINCMP_FLAG))
+            {
+              my_error(ER_NOT_SUPPORTED_YET, MYF(0), "return value collation");
+              MYSQL_YYABORT;
+            }
+
+            if (sp->fill_field_definition(YYTHD, lex,
+                                          (enum enum_field_types) $11,
+                                          &sp->m_return_field_def))
+              MYSQL_YYABORT;
+
+            bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
+          }
+          sp_c_chistics /* $13 */
+          { /* $14 */
+            THD *thd= YYTHD;
+            LEX *lex= thd->lex;
+            Lex_input_stream *lip= thd->m_lip;
+
+            lex->sphead->m_chistics= &lex->sp_chistics;
+            lex->sphead->set_body_start(thd, lip->get_cpp_tok_start());
+          }
+          sp_proc_stmt /* $15 */
+          {
+            THD *thd= YYTHD;
+            LEX *lex= thd->lex;
+            sp_head *sp= lex->sphead;
+
+            if (sp->is_not_allowed_in_function("function"))
+              MYSQL_YYABORT;
+
+            lex->sql_command= SQLCOM_CREATE_SPFUNCTION;
+            sp->set_stmt_end(thd);
+            if (!(sp->m_flags & sp_head::HAS_RETURN))
+            {
+              my_error(ER_SP_NORETURN, MYF(0), sp->m_qname.str);
+              MYSQL_YYABORT;
+            }
+            if (is_native_function(thd, & sp->m_name))
+            {
+              /*
+                This warning will be printed when
+                [1] A client query is parsed,
+                [2] A stored function is loaded by db_load_routine.
+                Printing the warning for [2] is intentional, to cover the
+                following scenario:
+                - A user define a SF 'foo' using MySQL 5.N
+                - An application uses select foo(), and works.
+                - MySQL 5.{N+1} defines a new native function 'foo', as
+                part of a new feature.
+                - MySQL 5.{N+1} documentation is updated, and should mention
+                that there is a potential incompatible change in case of
+                existing stored function named 'foo'.
+                - The user deploys 5.{N+1}. At this point, 'select foo()'
+                means something different, and the user code is most likely
+                broken (it's only safe if the code is 'select db.foo()').
+                With a warning printed when the SF is loaded (which has to occur
+                before the call), the warning will provide a hint explaining
+                the root cause of a later failure of 'select foo()'.
+                With no warning printed, the user code will fail with no
+                apparent reason.
+                Printing a warning each time db_load_routine is executed for
+                an ambiguous function is annoying, since that can happen a lot,
+                but in practice should not happen unless there *are* name
+                collisions.
+                If a collision exists, it should not be silenced but fixed.
+              */
+              push_warning_printf(thd,
+                                  MYSQL_ERROR::WARN_LEVEL_NOTE,
+                                  ER_NATIVE_FCT_NAME_COLLISION,
+                                  ER(ER_NATIVE_FCT_NAME_COLLISION),
+                                  sp->m_name.str);
+            }
+            /* Restore flag if it was cleared above */
+            thd->client_capabilities |= $<ulong_num>5;
+            sp->restore_thd_mem_root(thd);
+          }
+        ;
+
+sp_tail:
+          PROCEDURE remember_name sp_name
           {
             LEX *lex= Lex;
             sp_head *sp;
Thread
bk commit into 5.1 tree (malff:1.2583)marc.alff17 Oct