List:Commits« Previous MessageNext Message »
From:antony Date:May 22 2007 12:05pm
Subject:bk commit into 5.1 tree (antony:1.2524)
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of antony. When antony 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-05-22 03:05:29-07:00, antony@stripped +21 -0
  Proof of Concept: EXTERNAL stored procedures.
  In collaboration with Eric.
  (this has been brewing for quite some time)

  include/mysql/plugin.h@stripped, 2007-05-22 03:05:21-07:00, antony@stripped +65 -1
    define new plugin type: PSM LANGUAGE

  mysql-test/include/have_psmexample_plugin.inc@stripped, 2007-05-22 03:05:23-07:00,
antony@stripped +16 -0
    New BitKeeper file ``mysql-test/include/have_psmexample_plugin.inc''

  mysql-test/include/have_psmexample_plugin.inc@stripped, 2007-05-22 03:05:23-07:00,
antony@stripped +0 -0

  mysql-test/mysql-test-run.pl@stripped, 2007-05-22 03:05:21-07:00, antony@stripped +13 -0
    provide hints so that tests can use the simple example psm language.

  mysql-test/r/have_psmexample_plugin.require@stripped, 2007-05-22 03:05:23-07:00,
antony@stripped +2 -0
    New BitKeeper file ``mysql-test/r/have_psmexample_plugin.require''

  mysql-test/r/have_psmexample_plugin.require@stripped, 2007-05-22 03:05:23-07:00,
antony@stripped +0 -0

  mysql-test/r/plugin_psm.result@stripped, 2007-05-22 03:05:23-07:00, antony@stripped +9 -0
    New BitKeeper file ``mysql-test/r/plugin_psm.result''

  mysql-test/r/plugin_psm.result@stripped, 2007-05-22 03:05:23-07:00, antony@stripped +0 -0

  mysql-test/t/plugin_psm-master.opt@stripped, 2007-05-22 03:05:24-07:00, antony@stripped +1
-0
    New BitKeeper file ``mysql-test/t/plugin_psm-master.opt''

  mysql-test/t/plugin_psm-master.opt@stripped, 2007-05-22 03:05:24-07:00, antony@stripped +0
-0

  mysql-test/t/plugin_psm.test@stripped, 2007-05-22 03:05:24-07:00, antony@stripped +14 -0
    New BitKeeper file ``mysql-test/t/plugin_psm.test''

  mysql-test/t/plugin_psm.test@stripped, 2007-05-22 03:05:24-07:00, antony@stripped +0 -0

  plugin/psmlang_example/Makefile.am@stripped, 2007-05-22 03:05:24-07:00, antony@stripped
+51 -0
    New BitKeeper file ``plugin/psmlang_example/Makefile.am''

  plugin/psmlang_example/Makefile.am@stripped, 2007-05-22 03:05:24-07:00, antony@stripped +0
-0

  plugin/psmlang_example/plug.in@stripped, 2007-05-22 03:05:24-07:00, antony@stripped +4 -0
    New BitKeeper file ``plugin/psmlang_example/plug.in''

  plugin/psmlang_example/plug.in@stripped, 2007-05-22 03:05:24-07:00, antony@stripped +0 -0

  plugin/psmlang_example/psm_example.c@stripped, 2007-05-22 03:05:24-07:00, antony@stripped
+185 -0
    New BitKeeper file ``plugin/psmlang_example/psm_example.c''

  plugin/psmlang_example/psm_example.c@stripped, 2007-05-22 03:05:24-07:00, antony@stripped
+0 -0

  scripts/mysql_system_tables.sql@stripped, 2007-05-22 03:05:21-07:00, antony@stripped +1
-1
    language column is now a char()

  sql/Makefile.am@stripped, 2007-05-22 03:05:21-07:00, antony@stripped +2 -1
    new source files, sp_psm.h sp_psm.cc

  sql/lex.h@stripped, 2007-05-22 03:05:21-07:00, antony@stripped +1 -0
    new SQL reserved word: EXTERNAL

  sql/sp.cc@stripped, 2007-05-22 03:05:22-07:00, antony@stripped +26 -1
    store and retreive the language for the stored procedure.

  sql/sp_head.cc@stripped, 2007-05-22 03:05:22-07:00, antony@stripped +14 -2
    handle body and language for external routines

  sql/sp_psm.cc@stripped, 2007-05-22 03:05:23-07:00, antony@stripped +543 -0
    New BitKeeper file ``sql/sp_psm.cc''

  sql/sp_psm.cc@stripped, 2007-05-22 03:05:23-07:00, antony@stripped +0 -0

  sql/sp_psm.h@stripped, 2007-05-22 03:05:23-07:00, antony@stripped +92 -0
    New BitKeeper file ``sql/sp_psm.h''

  sql/sp_psm.h@stripped, 2007-05-22 03:05:23-07:00, antony@stripped +0 -0

  sql/sp_rcontext.h@stripped, 2007-05-22 03:05:22-07:00, antony@stripped +1 -0
    make sp_instr_external a friend so we can access its innards.

  sql/sql_lex.h@stripped, 2007-05-22 03:05:22-07:00, antony@stripped +1 -0
    new chistics: language

  sql/sql_plugin.cc@stripped, 2007-05-22 03:05:22-07:00, antony@stripped +13 -5
    new plugin type: PSM LANGUAGE

  sql/sql_yacc.yy@stripped, 2007-05-22 03:05:23-07:00, antony@stripped +234 -175
    handle EXTERNAL routine declarations

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	antony
# Host:	ppcg5.local
# Root:	/Users/antony/SkunkWorks/mysql-5.1-eric+antony.1

--- 1.184/sql/Makefile.am	2007-05-22 03:05:40 -07:00
+++ 1.185/sql/Makefile.am	2007-05-22 03:05:40 -07:00
@@ -62,6 +62,7 @@ noinst_HEADERS =	item.h item_func.h item
 			spatial.h gstream.h client_settings.h tzfile.h \
 			tztime.h my_decimal.h\
 			sp_head.h sp_pcontext.h sp_rcontext.h sp.h sp_cache.h \
+			sp_psm.h \
 			parse_file.h sql_view.h	sql_trigger.h \
 			sql_array.h sql_cursor.h events.h scheduler.h \
                         event_db_repository.h event_queue.h \
@@ -106,7 +107,7 @@ mysqld_SOURCES =	sql_lex.cc sql_handler.
 			gstream.cc spatial.cc sql_help.cc sql_cursor.cc \
 			tztime.cc my_time.c my_user.c my_decimal.cc\
 			sp_head.cc sp_pcontext.cc  sp_rcontext.cc sp.cc \
-			sp_cache.cc parse_file.cc sql_trigger.cc \
+			sp_cache.cc sp_psm.cc parse_file.cc sql_trigger.cc \
                         event_scheduler.cc event_data_objects.cc \
                         event_queue.cc event_db_repository.cc events.cc \
 			sql_plugin.cc sql_binlog.cc \

--- 1.174/sql/lex.h	2007-05-22 03:05:40 -07:00
+++ 1.175/sql/lex.h	2007-05-22 03:05:40 -07:00
@@ -196,6 +196,7 @@ static SYMBOL symbols[] = {
   { "EXPLAIN",		SYM(DESCRIBE)},
   { "EXTENDED",		SYM(EXTENDED_SYM)},
   { "EXTENT_SIZE",	SYM(EXTENT_SIZE_SYM)},
+  { "EXTERNAL",		SYM(EXTERNAL_SYM)},
   { "FALSE",		SYM(FALSE_SYM)},
   { "FAST",		SYM(FAST_SYM)},
   { "FETCH",            SYM(FETCH_SYM)},

--- 1.274/sql/sql_lex.h	2007-05-22 03:05:40 -07:00
+++ 1.275/sql/sql_lex.h	2007-05-22 03:05:40 -07:00
@@ -853,6 +853,7 @@ typedef struct st_alter_info
 
 struct st_sp_chistics
 {
+  LEX_STRING language;
   LEX_STRING comment;
   enum enum_sp_suid_behaviour suid;
   bool detistic;

--- 1.566/sql/sql_yacc.yy	2007-05-22 03:05:40 -07:00
+++ 1.567/sql/sql_yacc.yy	2007-05-22 03:05:40 -07:00
@@ -34,6 +34,7 @@
 #include "lex_symbol.h"
 #include "item_create.h"
 #include "sp_head.h"
+#include "sp_psm.h"
 #include "sp_pcontext.h"
 #include "sp_rcontext.h"
 #include "sp.h"
@@ -665,6 +666,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %token  EXPANSION_SYM
 %token  EXTENDED_SYM
 %token  EXTENT_SIZE_SYM
+%token  EXTERNAL_SYM                  /* SQL-2003-R */
 %token  EXTRACT_SYM                   /* SQL-2003-N */
 %token  FALSE_SYM                     /* SQL-2003-R */
 %token  FAST_SYM
@@ -1966,171 +1968,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;
-	  }
-	| '('
-	  {
-	    LEX *lex= Lex;
-	    sp_head *sp;
-
-            /* 
-              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(YYTHD);
-	    sp->init(lex);
-            sp->init_sp_name(YYTHD, 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>$= YYTHD->client_capabilities &
CLIENT_MULTI_QUERIES;
-	    YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES;
-	    lex->sphead->m_param_begin= lex->tok_start+1;
-	  }
-          sp_fdparam_list ')'
-	  {
-	    LEX *lex= Lex;
-
-	    lex->sphead->m_param_end= lex->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
-	  {
-	    LEX *lex= Lex;
-
-	    lex->sphead->m_chistics= &lex->sp_chistics;
-	    lex->sphead->m_body_begin= lex->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->init_strings(thd, lex);
-            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 {}
@@ -2145,8 +1982,6 @@ sp_c_chistics:
 sp_chistic:
 	  COMMENT_SYM TEXT_STRING_sys
 	  { Lex->sp_chistics.comment= $2; }
-	| LANGUAGE_SYM SQL_SYM
-	  { /* Just parse it, we only have one language for now. */ }
 	| NO_SYM SQL_SYM
 	  { Lex->sp_chistics.daccess= SP_NO_SQL; }
 	| CONTAINS_SYM SQL_SYM
@@ -2166,6 +2001,12 @@ sp_c_chistic:
 	| not DETERMINISTIC_SYM { Lex->sp_chistics.detistic= FALSE; }
 	;
 
+opt_language_sql:
+        /* empty */
+	| LANGUAGE_SYM SQL_SYM
+	/* Just parse it, we only have one language for now. */
+        ;
+
 sp_suid:
 	  SQL_SYM SECURITY_SYM DEFINER_SYM
 	  {
@@ -11548,14 +11389,7 @@ sp_tail:
 	  lex->sphead->m_param_end= lex->tok_start;
 	  bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
 	}
-	sp_c_chistics
-	{
-	  LEX *lex= Lex;
-
-	  lex->sphead->m_chistics= &lex->sp_chistics;
-	  lex->sphead->m_body_begin= lex->tok_start;
-	}
-	sp_proc_stmt
+	create_procedure_tail
 	{
 	  LEX *lex= Lex;
 	  sp_head *sp= lex->sphead;
@@ -11570,7 +11404,232 @@ sp_tail:
           YYTHD->client_capabilities |= $<ulong_num>4;
 	  sp->restore_thd_mem_root(YYTHD);
 	}
+        ;
+
+create_procedure_tail:
+	opt_language_sql
+	sp_c_chistics
+	{
+	  LEX *lex= Lex;
+	  lex->sphead->m_chistics= &lex->sp_chistics;
+	  lex->sphead->m_body_begin= lex->tok_start;
+	}
+	opt_as
+	sp_proc_stmt {}
+	| create_external_routine {}
+	;
+
+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;
+	  }
+	| '('
+	  {
+	    LEX *lex= Lex;
+	    sp_head *sp;
+
+            /* 
+              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(YYTHD);
+	    sp->init(lex);
+            sp->init_sp_name(YYTHD, 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>$= YYTHD->client_capabilities &
CLIENT_MULTI_QUERIES;
+	    YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES;
+	    lex->sphead->m_param_begin= lex->tok_start+1;
+	  }
+          sp_fdparam_list ')'
+	  {
+	    LEX *lex= Lex;
+
+	    lex->sphead->m_param_end= lex->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));
+	  }
+	  create_function_tail2
+	  {
+            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->init_strings(thd, lex);
+            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);
+	  }
+	;
+
+create_function_tail2:
+        opt_language_sql
+	sp_c_chistics
+	{
+	  LEX *lex= Lex;
+
+	  lex->sphead->m_chistics= &lex->sp_chistics;
+	  lex->sphead->m_body_begin= lex->tok_start;
+	}
+	sp_proc_stmt {}
+	| create_external_routine
+	{ Lex->sphead->m_flags|= sp_head::HAS_RETURN; }
+	;
+
+create_external_routine:
+	EXTERNAL_SYM NAME_SYM TEXT_STRING_sys        
+	LANGUAGE_SYM ident_or_text
+	{
+          if (!plugin_is_ready(&$5, MYSQL_PSMLANGUAGE_PLUGIN))
+          {
+            my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PSM LANGUAGE", $5.str);
+            MYSQL_YYABORT;
+          }
+	  Lex->sp_chistics.language= $5;
+	}
+	sp_c_chistics
+	{
+	  LEX *lex= Lex;
+	  sp_head *sp= lex->sphead;
+          sp_instr_external *i;
+          LEX_STRING name, lang;
+
+	  sp->m_chistics= &lex->sp_chistics;
+	  sp->m_body_begin= lex->tok_start;
+        
+          name.str= strmake_root(YYTHD->mem_root, 
+                                 $3.str, name.length= $3.length);
+          lang.str= strmake_root(YYTHD->mem_root,
+                                 $5.str, lang.length= $5.length);
+
+          sp->reset_lex(YYTHD);
+          sp->m_tmp_query= lex->tok_start;
+          
+          i= new sp_instr_external(sp->instructions(), Lex->spcont, 
+                                   name, lang, Lex);
+	  sp->add_instr(i);
+          sp->restore_lex(YYTHD);
+          
+	  sp->m_flags|= sp_head::CONTAINS_DYNAMIC_SQL;
+          sp->m_body= name;
+	}
 	;
+
 
 /*************************************************************************/
 
--- New file ---
+++ mysql-test/include/have_psmexample_plugin.inc	07/05/22 03:05:23
#
# Check if server has support for loading udf's
# i.e it will support dlopen
#
--require r/have_dynamic_loading.require
disable_query_log;
show variables like "have_dynamic_loading";
enable_query_log;

#
# Check if the variable PSMEXAMPLE_PLUGIN is set
#
--require r/have_psmexample_plugin.require
disable_query_log;
eval select LENGTH("$PSMEXAMPLE_PLUGIN") > 0 as "have_psmexample_plugin";
enable_query_log;

--- New file ---
+++ mysql-test/r/have_psmexample_plugin.require	07/05/22 03:05:23
have_psmexample_plugin
1

--- New file ---
+++ mysql-test/r/plugin_psm.result	07/05/22 03:05:23
INSTALL PLUGIN Deep_Thought SONAME 'psm_example.so';
CREATE PROCEDURE test.UltimateAnswer() EXTERNAL NAME 'compute' LANGUAGE Deep_Thought;
CALL test.UltimateAnswer();
Answer to Life, the Universe and Everything
Forty-Two
DROP PROCEDURE test.UltimateAnswer;
UNINSTALL PLUGIN Deep_Thought;
UNINSTALL PLUGIN Deep_Thought;
ERROR 42000: PLUGIN Deep_Thought does not exist

--- New file ---
+++ mysql-test/t/plugin_psm-master.opt	07/05/22 03:05:24
$PSMEXAMPLE_PLUGIN_OPT

--- New file ---
+++ mysql-test/t/plugin_psm.test	07/05/22 03:05:24
--source include/have_psmexample_plugin.inc

INSTALL PLUGIN Deep_Thought SONAME 'psm_example.so';

CREATE PROCEDURE test.UltimateAnswer() EXTERNAL NAME 'compute' LANGUAGE Deep_Thought;

# Let's do some advanced ops with the example engine :)
CALL test.UltimateAnswer();

DROP PROCEDURE test.UltimateAnswer;

UNINSTALL PLUGIN Deep_Thought;
--error 1305
UNINSTALL PLUGIN Deep_Thought;

--- New file ---
+++ plugin/psmlang_example/Makefile.am	07/05/22 03:05:24
# Copyright (C) 2007 MySQL AB
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

#called from the top level Makefile

MYSQLDATAdir =          $(localstatedir)
MYSQLSHAREdir =         $(pkgdatadir)
MYSQLBASEdir=           $(prefix)
MYSQLLIBdir=            $(pkglibdir)
INCLUDES =              -I$(top_srcdir)/include -I$(top_builddir)/include \
			-I$(top_srcdir)/regex \
			-I$(top_srcdir)/sql \
                        -I$(srcdir)
WRAPLIBS=

LDADD =

DEFS =                  @DEFS@

noinst_HEADERS =	

EXTRA_LTLIBRARIES =	psm_example.la
pkglib_LTLIBRARIES =	@plugin_psmlang_example_shared_target@
psm_example_la_LDFLAGS =-module -rpath $(MYSQLLIBdir)
psm_example_la_CXXFLAGS=$(AM_CFLAGS) -DMYSQL_DYNAMIC_PLUGIN
psm_example_la_CFLAGS =	$(AM_CFLAGS) -DMYSQL_DYNAMIC_PLUGIN
psm_example_la_SOURCES =psm_example.c


EXTRA_LIBRARIES =	libpsmexample.a
noinst_LIBRARIES =	@plugin_psmlang_example_static_target@
libpsmexample_a_CXXFLAGS =$(AM_CFLAGS)
libpsmexample_a_CFLAGS =$(AM_CFLAGS)
libpsmexample_a_SOURCES=psm_example.c


EXTRA_DIST =		CMakeLists.txt plug.in
# Don't update the files from bitkeeper
%::SCCS/s.%

--- New file ---
+++ plugin/psmlang_example/plug.in	07/05/22 03:05:24
MYSQL_PLUGIN(psmlang_example, [Simple PSM Language],
        [Simple PSM Language Demonstration plugin])
MYSQL_PLUGIN_DYNAMIC(psmlang_example, [psm_example.la])
MYSQL_PLUGIN_STATIC(psmlang_example, [libpsmexample.la])

--- New file ---
+++ plugin/psmlang_example/psm_example.c	07/05/22 03:05:24
/* Copyright (C) 2007 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */

#include <stdlib.h>
#include <ctype.h>
#include <strings.h>
#include <mysql.h>
#include <mysql/plugin.h>

#if !defined(__attribute__) && (defined(__cplusplus) || !defined(__GNUC__)  ||
__GNUC__ == 2 && __GNUC_MINOR__ < 8)
#define __attribute__(A)
#endif

struct st_mysql_psmroutine
{
  char answer[64];
};

static char *ultimate_answer;

static MYSQL_THDVAR_ULONG(call_count,
  PLUGIN_VAR_RQCMDARG,
  "Count of executions",
  NULL, NULL, 0L, 0L, ~0L, 0);

static long number_of_finds= 0; /* for SHOW STATUS, see below */


/*
  Initialize the PSM plugin at server start or plugin installation.

  SYNOPSIS
    simple_psm_plugin_init()

  DESCRIPTION
    Does nothing.

  RETURN VALUE
    0                    success
    1                    failure (cannot happen)
*/

static int simple_psm_plugin_init(void *arg __attribute__((unused)))
{
  return(0);
}


/*
  Terminate the PSM plugin at server shutdown or plugin deinstallation.

  SYNOPSIS
    simple_psm_plugin_deinit()
    Does nothing.

  RETURN VALUE
    0                    success
    1                    failure (cannot happen)

*/

static int simple_psm_plugin_deinit(void *arg __attribute__((unused)))
{
  return(0);
}


static int simple_psm_find(MYSQL_PSM_ROUTINE *handle,
                           const char *name, int name_length)
{
  number_of_finds++;
  if (name_length == 7 && !strncasecmp(name, "compute", 7))
  {
    const char *answer= ultimate_answer;
    if (!answer)
      answer= "Forty-Two";
  
    *handle= (MYSQL_PSM_ROUTINE) malloc(sizeof(struct st_mysql_psmroutine));
    bzero(*handle, sizeof(struct st_mysql_psmroutine));
    strncpy((*handle)->answer, answer, sizeof((*handle)->answer) - 1);
    return 0;
  }
  return -1;
}


static int simple_psm_execute(MYSQL_PSM_ROUTINE handle,
                              MYSQL_PSM_CONTEXT context)
{
  THDVAR(context->thd, call_count)++;

  if (context->cb->row_field(context, 
                             "Answer to Life, the Universe and Everything",
                             MYSQL_TYPE_STRING, 64, 0))
    goto err;

  if (context->cb->row_prepare(context))
    goto err;

  context->cb->store_string(context, -1, handle->answer, 
                            strlen(handle->answer), NULL);
  context->cb->row_send(context);
  context->cb->row_send_eof(context);

  return 0;
err:
  return -1;
}

static int simple_psm_release(MYSQL_PSM_ROUTINE handle)
{
  free(handle);
  return 0;
};


/*
  Plugin type-specific descriptor
*/

static struct st_mysql_psmlanguage simple_psm_descriptor=
{
  MYSQL_PSM_LANGUAGE_INTERFACE_VERSION, /* interface version      */
  simple_psm_find,                  /* function resolution function */
  simple_psm_release,               /* function release function   */
  simple_psm_execute                /* execute function */
};

/*
  Plugin status variables for SHOW STATUS
*/

static struct st_mysql_show_var simple_status[]=
{
  {"find_count", (char *)&number_of_finds,         SHOW_LONG},
  {0,0,0}
};

/*
  Plugin system variables.
*/

static MYSQL_SYSVAR_STR(response, ultimate_answer,
  PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
  "The answer of Deep Thought's calculation",
  NULL, NULL, "Forty-Two");

static struct st_mysql_sys_var* system_variables[]= {
  MYSQL_SYSVAR(call_count),
  NULL
};

/*
  Plugin library descriptor
*/

mysql_declare_plugin(ftexample)
{
  MYSQL_PSMLANGUAGE_PLUGIN,      /* type                            */
  &simple_psm_descriptor,     /* descriptor                      */
  "Deep_Thought",             /* name                            */
  "MySQL AB",                 /* author                          */
  "Simple PSM Language Demo", /* description                     */
  PLUGIN_LICENSE_GPL,
  simple_psm_plugin_init,     /* init function (when loaded)     */
  simple_psm_plugin_deinit,   /* deinit function (when unloaded) */
  0x0001,                     /* version                         */
  simple_status,              /* status variables                */
  system_variables,           /* system variables                */
  NULL
}
mysql_declare_plugin_end;


--- New file ---
+++ sql/sp_psm.cc	07/05/22 03:05:23
/* Copyright (C) 2007 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#include "mysql_priv.h"
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation
#endif
#include "sp_head.h"
#include "sp.h"
#include "sp_pcontext.h"
#include "sp_rcontext.h"
#include "sp_psm.h"


struct st_psm_context : public st_mysql_psmcontext
{
  sp_instr_external *i;
};


static const struct st_mysql_psmcallbacks psm_callbacks=
{
  sp_instr_external::psm_store_null,
  sp_instr_external::psm_store_string,
  sp_instr_external::psm_store_double,
  sp_instr_external::psm_store_integer,
  sp_instr_external::psm_store_time,
  sp_instr_external::psm_val_null,
  sp_instr_external::psm_val_string,
  sp_instr_external::psm_val_integer,
  sp_instr_external::psm_val_double,
  sp_instr_external::psm_field_ptr,
  sp_instr_external::psm_row_field,
  sp_instr_external::psm_row_prepare,
  sp_instr_external::psm_row_send,
  sp_instr_external::psm_row_send_eof
};


int sp_initialize_language(st_plugin_int *plugin)
{
  DBUG_ENTER("sp_initialize_language");
  
  if (plugin->plugin->init)
  {
    if (plugin->plugin->init(NULL))
    {
      sql_print_error("Plugin '%s' init funtion returned error.",
                      plugin->name.str);
      goto err;
    }
  }
  
  plugin->data= plugin->plugin->info;

  DBUG_RETURN(0);

err:
  DBUG_RETURN(1);
}


int sp_finalize_language(st_plugin_int *plugin)
{
  DBUG_ENTER("sp_finalize_language");

  if (plugin->plugin->deinit)
  {
    if (plugin->plugin->deinit(NULL))
    {
      DBUG_PRINT("warning", ("Plugin '%s' deinit function returned error.",
                             plugin->name.str));
    }
  }

  DBUG_RETURN(0);
}


/*
  sp_instr_external class functions
*/

int sp_instr_external::execute(THD *thd, uint *nextp)
{
  int res;
  struct st_mysql_psmlanguage *language;
  DBUG_ENTER("sp_instr_external::execute");

  if (!(m_plugin= my_plugin_lock_by_name(NULL, &m_language,
                                         MYSQL_PSMLANGUAGE_PLUGIN)))
  {
    my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PSM LANGUAGE", m_language.str);
    DBUG_RETURN(-1);
  }
  
  language= plugin_data(m_plugin, struct st_mysql_psmlanguage *);
  
  if ((res= language->find(&m_routine, m_name.str, m_name.length)))
    my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "EXTERNAL", m_name.str);
  else
  {
    res= m_lex_keeper.reset_lex_and_exec_core(thd, nextp, FALSE, this);

    if (res && !(thd->net.report_error == 1 || thd->killed))
    {
      my_error(ER_UNKNOWN_ERROR, MYF(0), "Something went wrong");
    }

    language->release(m_routine);
    m_routine= NULL;
  }

  plugin_unlock(NULL, m_plugin);

  DBUG_RETURN(res);
}


int sp_instr_external::exec_core(THD *thd, uint *nextp)
{
  struct st_psm_context context;
  struct st_mysql_psmlanguage *language;
  int res;
  DBUG_ENTER("sp_instr_external::exec_core");
  
  language= plugin_data(m_plugin, struct st_mysql_psmlanguage *);

  context.cb= &psm_callbacks;
  context.psm_phase= 0;
  context.arg_count= m_ctx->max_var_index();
  context.thd= thd;
  context.i= this;

  res= language->execute(m_routine, &context);
  
  *nextp= m_ip+1;
  DBUG_RETURN(res);
}


void sp_instr_external::print(String *str)
{
}


int 
sp_instr_external::psm_field_ptr(MYSQL_PSM_CONTEXT context, int idx,
                                 int *field_type, void **ptr, int *length)
{
  struct st_psm_context *ctx = (struct st_psm_context *) context;
  Field *field;
  DBUG_ENTER("sp_instr_external::psm_field_ptr");
    
  if (idx > 0 && (uint) idx < ctx->i->m_ctx->max_var_index())
    field= ctx->thd->spcont->m_var_table->field[idx];
  else
  if (idx == -1 && ctx->thd->spcont->m_return_value_fld)
    field= ctx->thd->spcont->m_return_value_fld;
  else
    goto err;
  
  *field_type= field->type();
  *ptr= field->ptr;
  *length= field->field_length;
  DBUG_RETURN(0);

err:
  DBUG_RETURN(-1);
}


int 
sp_instr_external::psm_store_null(MYSQL_PSM_CONTEXT context, int idx)
{
  struct st_psm_context *ctx = (struct st_psm_context *) context;
  Field *field;
  DBUG_ENTER("sp_instr_external::psm_store_null");
    
  if (idx > 0 && (uint) idx < ctx->i->m_ctx->max_var_index())
    field= ctx->thd->spcont->m_var_table->field[idx];
  else
  if (idx == -1 && ctx->thd->spcont->m_return_value_fld)
    field= ctx->thd->spcont->m_return_value_fld;
  if (idx == -1)
    return ctx->thd->protocol->store_null() ? -1 : 0;
  else
    goto err;
  
  field->set_null();

  DBUG_RETURN(0);

err:
  DBUG_RETURN(-1);
}

int 
sp_instr_external::psm_store_string(MYSQL_PSM_CONTEXT context, int idx,
                                    const char *str, int length,
                                    struct charset_info_st *cs)
{
  struct st_psm_context *ctx = (struct st_psm_context *) context;
  Field *field;
  DBUG_ENTER("sp_instr_external::psm_store_string");
  
  if (cs == NULL)
    cs= system_charset_info;
    
  if (idx > 0 && (uint) idx < ctx->i->m_ctx->max_var_index())
    field= ctx->thd->spcont->m_var_table->field[idx];
  else
  if (idx == -1 && ctx->thd->spcont->m_return_value_fld)
    field= ctx->thd->spcont->m_return_value_fld;
  if (idx == -1)
    return ctx->thd->protocol->store(str, length, cs) ? -1 : 0;
  else
    goto err;
  
  field->set_notnull();
  field->store(str, length, cs);

  DBUG_RETURN(0);

err:
  DBUG_RETURN(-1);
}

int 
sp_instr_external::psm_store_double(MYSQL_PSM_CONTEXT context, int idx,
                                    double value, int precision)
{
  struct st_psm_context *ctx = (struct st_psm_context *) context;
  Field *field;
  DBUG_ENTER("sp_instr_external::psm_store_double");
    
  if (idx > 0 && (uint) idx < ctx->i->m_ctx->max_var_index())
    field= ctx->thd->spcont->m_var_table->field[idx];
  else
  if (idx == -1 && ctx->thd->spcont->m_return_value_fld)
    field= ctx->thd->spcont->m_return_value_fld;
  if (idx == -1)
    return ctx->thd->protocol->store(value, precision,
                                     &ctx->i->m_str_value) ? -1 : 0;
  else
    goto err;
  
  field->set_notnull();
  field->store(value);

  DBUG_RETURN(0);

err:
  DBUG_RETURN(-1);
}


int 
sp_instr_external::psm_store_integer(MYSQL_PSM_CONTEXT context, int idx,
                                    long long value, char unsigned_flag)
{
  struct st_psm_context *ctx = (struct st_psm_context *) context;
  Field *field;
  DBUG_ENTER("sp_instr_external::psm_store_integer");
    
  if (idx > 0 && (uint) idx < ctx->i->m_ctx->max_var_index())
    field= ctx->thd->spcont->m_var_table->field[idx];
  else
  if (idx == -1 && ctx->thd->spcont->m_return_value_fld)
    field= ctx->thd->spcont->m_return_value_fld;
  if (idx == -1)
    return ctx->thd->protocol->store_longlong((longlong) value, 
                                              unsigned_flag) ? -1 : 0;
  else
    goto err;
  
  field->set_notnull();
  field->store((longlong) value, unsigned_flag);

  DBUG_RETURN(0);

err:
  DBUG_RETURN(-1);
}

int 
sp_instr_external::psm_store_time(MYSQL_PSM_CONTEXT context, int idx,
                                  struct st_mysql_time *ltime)
{
  struct st_psm_context *ctx = (struct st_psm_context *) context;
  Field *field;
  DBUG_ENTER("sp_instr_external::psm_store_time");
    
  if (idx > 0 && (uint) idx < ctx->i->m_ctx->max_var_index())
    field= ctx->thd->spcont->m_var_table->field[idx];
  else
  if (idx == -1 && ctx->thd->spcont->m_return_value_fld)
    field= ctx->thd->spcont->m_return_value_fld;
  if (idx == -1)
    return ctx->thd->protocol->store(ltime) ? -1 : 0;
  else
    goto err;
  
  field->set_notnull();
  field->store_time(ltime, (timestamp_type) 0);

  DBUG_RETURN(0);

err:
  DBUG_RETURN(-1);
}

int 
sp_instr_external::psm_val_null(MYSQL_PSM_CONTEXT context, int idx)
{
  struct st_psm_context *ctx = (struct st_psm_context *) context;
  Field *field;
  DBUG_ENTER("sp_instr_external::psm_val_null");
    
  if (idx > 0 && (uint) idx < ctx->i->m_ctx->max_var_index())
    field= ctx->thd->spcont->m_var_table->field[idx];
  else
  if (idx == -1 && ctx->thd->spcont->m_return_value_fld)
    field= ctx->thd->spcont->m_return_value_fld;
  else
    goto err;
  
  DBUG_RETURN(field->is_null() ? 1 : 0);

err:
  DBUG_RETURN(-1);
}

long long 
sp_instr_external::psm_val_integer(MYSQL_PSM_CONTEXT context, int idx)
{
  struct st_psm_context *ctx = (struct st_psm_context *) context;
  Field *field;
  DBUG_ENTER("sp_instr_external::psm_val_integer");
    
  if (idx > 0 && (uint) idx < ctx->i->m_ctx->max_var_index())
    field= ctx->thd->spcont->m_var_table->field[idx];
  else
  if (idx == -1 && ctx->thd->spcont->m_return_value_fld)
    field= ctx->thd->spcont->m_return_value_fld;
  else
    goto err;
  
  DBUG_RETURN((long long) field->val_int());

err:
  DBUG_RETURN(-1);
}

double
sp_instr_external::psm_val_double(MYSQL_PSM_CONTEXT context, int idx)
{
  struct st_psm_context *ctx = (struct st_psm_context *) context;
  Field *field;
  DBUG_ENTER("sp_instr_external::psm_val_double");
    
  if (idx > 0 && (uint) idx < ctx->i->m_ctx->max_var_index())
    field= ctx->thd->spcont->m_var_table->field[idx];
  else
  if (idx == -1 && ctx->thd->spcont->m_return_value_fld)
    field= ctx->thd->spcont->m_return_value_fld;
  else
    goto err;
  
  DBUG_RETURN(field->val_real());

err:
  DBUG_RETURN(-1);
}

const char* 
sp_instr_external::psm_val_string(MYSQL_PSM_CONTEXT context, int idx,
                                  char *buf, int *length,
                                  struct charset_info_st **cs)
{
  struct st_psm_context *ctx = (struct st_psm_context *) context;
  String *str, buffer(buf, *length, *cs);
  Field *field;
  DBUG_ENTER("sp_instr_external::psm_val_string");
    
  if (idx > 0 && (uint) idx < ctx->i->m_ctx->max_var_index())
    field= ctx->thd->spcont->m_var_table->field[idx];
  else
  if (idx == -1 && ctx->thd->spcont->m_return_value_fld)
    field= ctx->thd->spcont->m_return_value_fld;
  else
    goto err;
  
  str= field->val_str(&buffer, &ctx->i->m_str_value);
  *length= str->length();
  *cs= str->charset();
  
  DBUG_RETURN(str->ptr());

err:
  DBUG_RETURN(NULL);
}

int
sp_instr_external::psm_row_prepare(MYSQL_PSM_CONTEXT context)
{
  struct st_psm_context *ctx = (struct st_psm_context *) context;
  Protocol *protocol= ctx->thd->protocol;
  DBUG_ENTER("sp_instr_external::psm_row_prepare");

  if (ctx->thd->spcont->m_return_value_fld)
    goto err;
  
  if (ctx->i->m_field_list.elements)
  {
    if (protocol->send_fields(&ctx->i->m_field_list,
                              Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
      goto err;
    ctx->i->m_field_list.empty();
  }
    
  protocol->prepare_for_resend();
  DBUG_RETURN(0);

err:
  DBUG_RETURN(-1);
}

int
sp_instr_external::psm_row_send(MYSQL_PSM_CONTEXT context)
{
  struct st_psm_context *ctx = (struct st_psm_context *) context;
  Protocol *protocol= ctx->thd->protocol;
  DBUG_ENTER("sp_instr_external::psm_row_send");
  
  if (ctx->thd->spcont->m_return_value_fld)
    goto err;

  if (protocol->write())
    goto err;
  
  DBUG_RETURN(0);

err:
  DBUG_RETURN(-1);
}

int
sp_instr_external::psm_row_send_eof(MYSQL_PSM_CONTEXT context)
{
  struct st_psm_context *ctx = (struct st_psm_context *) context;
  DBUG_ENTER("sp_instr_external::psm_row_send");

  if (ctx->thd->spcont->m_return_value_fld)
    goto err;
  
  send_eof(ctx->thd);
  
  DBUG_RETURN(0);

err:
  DBUG_RETURN(-1);
}


int
sp_instr_external::psm_row_field(MYSQL_PSM_CONTEXT context, const char *title,
                                 int field_type, int size, int precision)
{
  struct st_psm_context *ctx = (struct st_psm_context *) context;
  Item *item;
  DBUG_ENTER("sp_instr_external::psm_row_field");

  if (ctx->thd->spcont->m_return_value_fld)
    goto err;

  switch (field_type) {
  case MYSQL_TYPE_STRING:
    if (!(item= new Item_empty_string(title, size)))
      goto err;
    break;
  case MYSQL_TYPE_TINY:
  case MYSQL_TYPE_SHORT:
  case MYSQL_TYPE_LONG:
  case MYSQL_TYPE_LONGLONG:
  case MYSQL_TYPE_INT24:
    if (!(item= new Item_return_int(title, size, (enum_field_types)field_type)))
      goto err;
    break;
  case MYSQL_TYPE_DATE:
  case MYSQL_TYPE_TIME:
  case MYSQL_TYPE_TIMESTAMP:
  case MYSQL_TYPE_DATETIME:
    if (!(item= new Item_return_date_time(title, (enum_field_types)field_type)))
      goto err;
    break;
  case MYSQL_TYPE_FLOAT:
  case MYSQL_TYPE_DOUBLE:
    if (!(item= new Item_float(title, 0.0, NOT_FIXED_DEC, size)))
      goto err;
    break;
  case MYSQL_TYPE_DECIMAL:
    if (!(item= new Item_decimal((longlong)0, false)))
      goto err;
    item->decimals= precision;
    item->max_length= size;
    if (item->unsigned_flag == 0)
      item->max_length+= 1;
    if (item->decimals)
      item->max_length+= 1;
    item->set_name(title, strlen(title), system_charset_info);
    break;
  case MYSQL_TYPE_TINY_BLOB:
  case MYSQL_TYPE_MEDIUM_BLOB:
  case MYSQL_TYPE_LONG_BLOB:
  case MYSQL_TYPE_BLOB:
    if (!(item= new Item_blob(title, size)))
      goto err;
    break;
  default:
    goto err;
  }

  ctx->i->m_field_list.push_back(item);

  DBUG_RETURN(0);

err:
  DBUG_RETURN(-1);
}    


--- New file ---
+++ sql/sp_psm.h	07/05/22 03:05:23
/* -*- C++ -*- */
/* Copyright (C) 2007 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#ifndef _SP_PSM_H_
#define _SP_PSM_H_

#ifdef USE_PRAGMA_INTERFACE
#pragma interface			/* gcc class implementation */
#endif


//
// Call out to some prepared SQL statement.
//
class sp_instr_external : public sp_instr
{
public:

  LEX_STRING m_name;
  LEX_STRING m_language;

  sp_instr_external(uint ip, sp_pcontext *ctx, 
                    LEX_STRING &name, LEX_STRING &language,
                    LEX *lex)
    : sp_instr(ip, ctx), m_lex_keeper(lex, TRUE),
      m_name(name), m_language(language)
  {
  }

  virtual ~sp_instr_external()
  {};

  virtual int execute(THD *thd, uint *nextp);

  virtual int exec_core(THD *thd, uint *nextp);

  virtual void print(String *str);

private:

  sp_lex_keeper m_lex_keeper;
  plugin_ref m_plugin;
  MYSQL_PSM_ROUTINE m_routine;
  String m_str_value;
  List<Item> m_field_list;
  
public:
  static int psm_field_ptr(MYSQL_PSM_CONTEXT, int idx, 
                           int *field_type, void **ptr, int *length);
  static int psm_store_null(MYSQL_PSM_CONTEXT, int idx);
  static int psm_store_string(MYSQL_PSM_CONTEXT, int idx, 
                              const char *str, int length, 
                              struct charset_info_st *cs);
  static int psm_store_double(MYSQL_PSM_CONTEXT, int idx, 
                              double nr, int precision);
  static int psm_store_integer(MYSQL_PSM_CONTEXT, int idx, 
                               long long nr, char unsigned_val);
  static int psm_store_time(MYSQL_PSM_CONTEXT, int idx, 
                            struct st_mysql_time *ltime);
  static int psm_val_null(MYSQL_PSM_CONTEXT, int idx);
  static const char *psm_val_string(MYSQL_PSM_CONTEXT, int idx, 
                                    char *buf, int *length, 
                                    struct charset_info_st **cs);
  static long long psm_val_integer(MYSQL_PSM_CONTEXT, int idx);
  static double psm_val_double(MYSQL_PSM_CONTEXT, int idx);
  static int psm_row_field(MYSQL_PSM_CONTEXT, const char *title,
                           int field_type, int size, int precision);
  static int psm_row_prepare(MYSQL_PSM_CONTEXT);
  static int psm_row_send(MYSQL_PSM_CONTEXT);
  static int psm_row_send_eof(MYSQL_PSM_CONTEXT);
}; // class sp_instr_external : public sp_instr




int sp_initialize_language(st_plugin_int *plugin);
int sp_finalize_language(st_plugin_int *plugin);

#endif /* _SP_PSM_H_ */


--- 1.304/mysql-test/mysql-test-run.pl	2007-05-22 03:05:40 -07:00
+++ 1.305/mysql-test/mysql-test-run.pl	2007-05-22 03:05:40 -07:00
@@ -162,6 +162,7 @@ our $exe_my_print_defaults;
 our $exe_perror;
 our $lib_udf_example;
 our $lib_example_plugin;
+our $lib_psmexample_plugin;
 our $exe_libtool;
 
 our $opt_bench= 0;
@@ -1616,6 +1617,10 @@ sub executable_setup () {
       mtr_file_exists(vs_config_dirs('storage/example', 'ha_example.dll'),
                       "$glob_basedir/storage/example/.libs/ha_example.so",);
 
+    # Look for the ha_example library
+    $lib_psmexample_plugin=
+      mtr_file_exists(vs_config_dirs('plugin/psmlang_example', 'psm_example.dll'),
+                      "$glob_basedir/plugin/psmlang_example/.libs/psm_example.so",);
   }
 
   # Look for mysqltest executable
@@ -2054,6 +2059,14 @@ sub environment_setup () {
     ($lib_example_plugin ? basename($lib_example_plugin) : "");
   $ENV{'EXAMPLE_PLUGIN_OPT'}=
     ($lib_example_plugin ? "--plugin_dir=" . dirname($lib_example_plugin) : "");
+
+  # ----------------------------------------------------
+  # Add the path where mysqld will find psm_example.so
+  # ----------------------------------------------------
+  $ENV{'PSMEXAMPLE_PLUGIN'}=
+    ($lib_psmexample_plugin ? basename($lib_psmexample_plugin) : "");
+  $ENV{'PSMEXAMPLE_PLUGIN_OPT'}=
+    ($lib_psmexample_plugin ? "--plugin_dir=" . dirname($lib_psmexample_plugin) : "");
 
   # ----------------------------------------------------
   # We are nice and report a bit about our settings

--- 1.35/include/mysql/plugin.h	2007-05-22 03:05:40 -07:00
+++ 1.36/include/mysql/plugin.h	2007-05-22 03:05:40 -07:00
@@ -39,7 +39,8 @@ class Item;
 #define MYSQL_FTPARSER_PLUGIN        2  /* Full-text parser plugin      */
 #define MYSQL_DAEMON_PLUGIN          3  /* The daemon/raw plugin type */
 #define MYSQL_INFORMATION_SCHEMA_PLUGIN  4  /* The I_S plugin type */
-#define MYSQL_MAX_PLUGIN_TYPE_NUM    5  /* The number of plugin types   */
+#define MYSQL_PSMLANGUAGE_PLUGIN     5  /* Stored Procedure Language    */
+#define MYSQL_MAX_PLUGIN_TYPE_NUM    6  /* The number of plugin types   */
 
 /* We use the following strings to define licenses for plugins */
 #define PLUGIN_LICENSE_PROPRIETARY 0
@@ -612,6 +613,69 @@ struct st_mysql_daemon
 struct st_mysql_information_schema
 {
   int interface_version;
+};
+
+
+/*************************************************************************
+  API for PSM Language plugin. (MYSQL_PSM_LANGUAGE_PLUGIN)
+*/
+
+/* handlertons of different MySQL releases are incompatible */
+#define MYSQL_PSM_LANGUAGE_INTERFACE_VERSION (MYSQL_VERSION_ID << 8)
+
+
+typedef struct st_mysql_psmroutine *MYSQL_PSM_ROUTINE;
+
+typedef struct st_mysql_psmcontext
+{
+  const struct st_mysql_psmcallbacks *cb;
+  MYSQL_THD thd;
+  int arg_count;
+  int psm_phase;
+} *MYSQL_PSM_CONTEXT;
+
+
+struct st_mysql_psmcallbacks
+{
+  /* store with idx == -1 for function results */
+  int (*store_null)(MYSQL_PSM_CONTEXT, int idx);
+  int (*store_string)(MYSQL_PSM_CONTEXT, int idx, 
+                      const char *str, int length, struct charset_info_st *cs);
+  int (*store_double)(MYSQL_PSM_CONTEXT, int idx, double nr, int precision);
+  int (*store_integer)(MYSQL_PSM_CONTEXT, int idx, long long nr, char unsigned_val);
+  int (*store_time)(MYSQL_PSM_CONTEXT, int idx, struct st_mysql_time *ltime); 
+
+  int (*val_null)(MYSQL_PSM_CONTEXT, int idx);
+  const char *(*val_string)(MYSQL_PSM_CONTEXT, int idx, 
+                            char *str, int *length,
+                            struct charset_info_st **cs);
+  long long (*val_integer)(MYSQL_PSM_CONTEXT, int idx);
+  double (*val_double)(MYSQL_PSM_CONTEXT, int idx);
+
+  int (*field_ptr)(MYSQL_PSM_CONTEXT, int idx,
+                   int *field_type, void **ptr, int *length);
+ 
+  int (*row_field)(MYSQL_PSM_CONTEXT, const char *title,
+                   int field_type, int size, int precision);
+  int (*row_prepare)(MYSQL_PSM_CONTEXT);
+  int (*row_send)(MYSQL_PSM_CONTEXT);
+  int (*row_send_eof)(MYSQL_PSM_CONTEXT);
+};
+
+
+
+/*
+  Here we define only the descriptor structure, that is referred from
+  st_mysql_plugin.
+*/
+
+struct st_mysql_psmlanguage
+{
+  int interface_version;
+  int (*find)(MYSQL_PSM_ROUTINE *handle, const char *name, int name_length);
+  int (*release)(MYSQL_PSM_ROUTINE handle);
+  int (*execute)(MYSQL_PSM_ROUTINE handle, MYSQL_PSM_CONTEXT context);
+  
 };
 
 

--- 1.63/sql/sql_plugin.cc	2007-05-22 03:05:40 -07:00
+++ 1.64/sql/sql_plugin.cc	2007-05-22 03:05:40 -07:00
@@ -42,12 +42,16 @@ const LEX_STRING plugin_type_names[MYSQL
   { C_STRING_WITH_LEN("STORAGE ENGINE") },
   { C_STRING_WITH_LEN("FTPARSER") },
   { C_STRING_WITH_LEN("DAEMON") },
-  { C_STRING_WITH_LEN("INFORMATION SCHEMA") }
+  { C_STRING_WITH_LEN("INFORMATION SCHEMA") },
+  { C_STRING_WITH_LEN("PSM LANGUAGE") }
 };
 
 extern int initialize_schema_table(st_plugin_int *plugin);
 extern int finalize_schema_table(st_plugin_int *plugin);
 
+extern int sp_initialize_language(st_plugin_int *plugin);
+extern int sp_finalize_language(st_plugin_int *plugin);
+
 /*
   The number of elements in both plugin_type_initialize and
   plugin_type_deinitialize should equal to the number of plugins
@@ -55,12 +59,14 @@ extern int finalize_schema_table(st_plug
 */
 plugin_type_init plugin_type_initialize[MYSQL_MAX_PLUGIN_TYPE_NUM]=
 {
-  0,ha_initialize_handlerton,0,0,initialize_schema_table
+  0,ha_initialize_handlerton,0,0,initialize_schema_table,
+  sp_initialize_language
 };
 
 plugin_type_init plugin_type_deinitialize[MYSQL_MAX_PLUGIN_TYPE_NUM]=
 {
-  0,ha_finalize_handlerton,0,0,finalize_schema_table
+  0,ha_finalize_handlerton,0,0,finalize_schema_table,
+  sp_finalize_language
 };
 
 #ifdef HAVE_DLOPEN
@@ -81,7 +87,8 @@ static int min_plugin_info_interface_ver
   MYSQL_HANDLERTON_INTERFACE_VERSION,
   MYSQL_FTPARSER_INTERFACE_VERSION,
   MYSQL_DAEMON_INTERFACE_VERSION,
-  MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION
+  MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION,
+  MYSQL_PSM_LANGUAGE_INTERFACE_VERSION
 };
 static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
 {
@@ -89,7 +96,8 @@ static int cur_plugin_info_interface_ver
   MYSQL_HANDLERTON_INTERFACE_VERSION,
   MYSQL_FTPARSER_INTERFACE_VERSION,
   MYSQL_DAEMON_INTERFACE_VERSION,
-  MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION
+  MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION,
+  MYSQL_PSM_LANGUAGE_INTERFACE_VERSION
 };
 
 static bool initialized= 0;

--- 1.10/scripts/mysql_system_tables.sql	2007-05-22 03:05:40 -07:00
+++ 1.11/scripts/mysql_system_tables.sql	2007-05-22 03:05:40 -07:00
@@ -59,7 +59,7 @@ CREATE TABLE IF NOT EXISTS time_zone_tra
 CREATE TABLE IF NOT EXISTS time_zone_leap_second (   Transition_time bigint signed NOT
NULL, Correction int signed NOT NULL, PRIMARY KEY TranTime (Transition_time) )
engine=MyISAM CHARACTER SET utf8   comment='Leap seconds information for time zones';
 
 
-CREATE TABLE IF NOT EXISTS proc ( db char(64) collate utf8_bin DEFAULT '' NOT NULL, name 
char(64) DEFAULT '' NOT NULL, type  enum('FUNCTION','PROCEDURE') NOT NULL, specific_name
char(64) DEFAULT '' NOT NULL, language  enum('SQL') DEFAULT 'SQL' NOT NULL,
sql_data_access enum('CONTAINS_SQL', 'NO_SQL', 'READS_SQL_DATA', 'MODIFIES_SQL_DATA' )
DEFAULT 'CONTAINS_SQL' NOT NULL, is_deterministic  enum('YES','NO') DEFAULT 'NO' NOT
NULL, security_type enum('INVOKER','DEFINER') DEFAULT 'DEFINER' NOT NULL, param_list 
blob NOT NULL, returns char(64) DEFAULT '' NOT NULL, body  longblob NOT NULL, definer
char(77) collate utf8_bin DEFAULT '' NOT NULL, created timestamp, modified timestamp,
sql_mode set( 'REAL_AS_FLOAT', 'PIPES_AS_CONCAT', 'ANSI_QUOTES', 'IGNORE_SPACE',
'NOT_USED', 'ONLY_FULL_GROUP_BY', 'NO_UNSIGNED_SUBTRACTION', 'NO_DIR_IN_CREATE',
'POSTGRESQL', 'ORACLE', 'MSSQL', 'DB2', 'MAXDB', 'NO_KEY_OPTIONS', 'NO_TABLE_OPTIONS',
'NO_FIELD_OPTIONS', 'MYSQL323', 'MYSQL40', 'ANSI', 'NO_
 AUTO_VALUE_ON_ZERO', 'NO_BACKSLASH_ESCAPES', 'STRICT_TRANS_TABLES', 'STRICT_ALL_TABLES',
'NO_ZERO_IN_DATE', 'NO_ZERO_DATE', 'INVALID_DATES', 'ERROR_FOR_DIVISION_BY_ZERO',
'TRADITIONAL', 'NO_AUTO_CREATE_USER', 'HIGH_NOT_PRECEDENCE' ) DEFAULT '' NOT NULL,
comment char(64) collate utf8_bin DEFAULT '' NOT NULL, PRIMARY KEY (db,name,type) )
engine=MyISAM character set utf8 comment='Stored Procedures';
+CREATE TABLE IF NOT EXISTS proc ( db char(64) collate utf8_bin DEFAULT '' NOT NULL, name 
char(64) DEFAULT '' NOT NULL, type  enum('FUNCTION','PROCEDURE') NOT NULL, specific_name
char(64) DEFAULT '' NOT NULL, language char(64) DEFAULT 'SQL' NOT NULL, sql_data_access
enum('CONTAINS_SQL', 'NO_SQL', 'READS_SQL_DATA', 'MODIFIES_SQL_DATA' ) DEFAULT
'CONTAINS_SQL' NOT NULL, is_deterministic  enum('YES','NO') DEFAULT 'NO' NOT NULL,
security_type enum('INVOKER','DEFINER') DEFAULT 'DEFINER' NOT NULL, param_list  blob NOT
NULL, returns char(64) DEFAULT '' NOT NULL, body  longblob NOT NULL, definer char(77)
collate utf8_bin DEFAULT '' NOT NULL, created timestamp, modified timestamp, sql_mode
set( 'REAL_AS_FLOAT', 'PIPES_AS_CONCAT', 'ANSI_QUOTES', 'IGNORE_SPACE', 'NOT_USED',
'ONLY_FULL_GROUP_BY', 'NO_UNSIGNED_SUBTRACTION', 'NO_DIR_IN_CREATE', 'POSTGRESQL',
'ORACLE', 'MSSQL', 'DB2', 'MAXDB', 'NO_KEY_OPTIONS', 'NO_TABLE_OPTIONS',
'NO_FIELD_OPTIONS', 'MYSQL323', 'MYSQL40', 'ANSI', 'NO_AUTO
 _VALUE_ON_ZERO', 'NO_BACKSLASH_ESCAPES', 'STRICT_TRANS_TABLES', 'STRICT_ALL_TABLES',
'NO_ZERO_IN_DATE', 'NO_ZERO_DATE', 'INVALID_DATES', 'ERROR_FOR_DIVISION_BY_ZERO',
'TRADITIONAL', 'NO_AUTO_CREATE_USER', 'HIGH_NOT_PRECEDENCE' ) DEFAULT '' NOT NULL,
comment char(64) collate utf8_bin DEFAULT '' NOT NULL, PRIMARY KEY (db,name,type) )
engine=MyISAM character set utf8 comment='Stored Procedures';
 
 
 CREATE TABLE IF NOT EXISTS procs_priv ( Host char(60) binary DEFAULT '' NOT NULL, Db
char(64) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL,
Routine_name char(64) binary DEFAULT '' NOT NULL, Routine_type
enum('FUNCTION','PROCEDURE') NOT NULL, Grantor char(77) DEFAULT '' NOT NULL, Proc_priv
set('Execute','Alter Routine','Grant') COLLATE utf8_general_ci DEFAULT '' NOT NULL,
Timestamp timestamp(14), PRIMARY KEY (Host,Db,User,Routine_name,Routine_type), KEY
Grantor (Grantor) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin  
comment='Procedure privileges';

--- 1.148/sql/sp.cc	2007-05-22 03:05:40 -07:00
+++ 1.149/sql/sp.cc	2007-05-22 03:05:40 -07:00
@@ -305,6 +305,14 @@ db_find_routine(THD *thd, int type, sp_n
 
   sql_mode= (ulong) table->field[MYSQL_PROC_FIELD_SQL_MODE]->val_int();
 
+  table->field[MYSQL_PROC_FIELD_LANGUAGE]->val_str(&str, &str);
+
+  ptr= 0;
+  if ((length= str.length()))
+    ptr= thd->strmake(str.ptr(), length);
+  chistics.language.str= ptr;
+  chistics.language.length= length;
+
   table->field[MYSQL_PROC_FIELD_COMMENT]->val_str(&str, &str);
 
   ptr= 0;
@@ -516,6 +524,10 @@ db_create_routine(THD *thd, int type, sp
     ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time();
     table->field[MYSQL_PROC_FIELD_SQL_MODE]->
       store((longlong)thd->variables.sql_mode, TRUE);
+    if (sp->m_chistics->language.str)
+      table->field[MYSQL_PROC_FIELD_LANGUAGE]->
+	store(sp->m_chistics->language.str, sp->m_chistics->language.length,
+	      system_charset_info);
     if (sp->m_chistics->comment.str)
       table->field[MYSQL_PROC_FIELD_COMMENT]->
 	store(sp->m_chistics->comment.str, sp->m_chistics->comment.length,
@@ -1748,6 +1760,10 @@ create_string(THD *thd, String *buf,
               const LEX_STRING *definer_user,
               const LEX_STRING *definer_host)
 {
+  bool is_external= chistics->language.length &&
+                    (chistics->language.length != 3 ||
+                     strncasecmp(chistics->language.str, "SQL", 3));
+
   /* Make some room to begin with */
   if (buf->alloc(100 + name->m_qname.length + paramslen + returnslen + bodylen +
 		 chistics->comment.length + 10 /* length of " DEFINER= "*/ +
@@ -1770,6 +1786,14 @@ create_string(THD *thd, String *buf,
     buf->append(returns, returnslen);
   }
   buf->append('\n');
+  if (is_external)
+  {
+    buf->append(STRING_WITH_LEN("    EXTERNAL NAME "));
+    append_unescaped(buf, body, bodylen);
+    buf->append(STRING_WITH_LEN(" LANGUAGE "));
+    append_identifier(thd, buf, chistics->language.str, 
+                      chistics->language.length);
+  }
   switch (chistics->daccess) {
   case SP_NO_SQL:
     buf->append(STRING_WITH_LEN("    NO SQL\n"));
@@ -1795,7 +1819,8 @@ create_string(THD *thd, String *buf,
     append_unescaped(buf, chistics->comment.str, chistics->comment.length);
     buf->append('\n');
   }
-  buf->append(body, bodylen);
+  if (!is_external)
+    buf->append(body, bodylen);
   return TRUE;
 }
 

--- 1.266/sql/sp_head.cc	2007-05-22 03:05:40 -07:00
+++ 1.267/sql/sp_head.cc	2007-05-22 03:05:40 -07:00
@@ -572,8 +572,11 @@ sp_head::init_strings(THD *thd, LEX *lex
   */
   endp= skip_rear_comments(m_body_begin, endp);
 
-  m_body.length= endp - m_body_begin;
-  m_body.str= strmake_root(root, m_body_begin, m_body.length);
+  if (endp > m_body_begin)
+  {
+    m_body.length= endp - m_body_begin;
+    m_body.str= strmake_root(root, m_body_begin, m_body.length);
+  }
   m_defstr.length= endp - lex->buf;
   m_defstr.str= strmake_root(root, lex->buf, m_defstr.length);
   DBUG_VOID_RETURN;
@@ -2025,6 +2028,13 @@ sp_head::set_info(longlong created, long
     m_chistics->comment.str= strmake_root(mem_root,
 					  m_chistics->comment.str,
 					  m_chistics->comment.length);
+
+  if (m_chistics->language.length == 0)
+    m_chistics->language.str= 0;
+  else
+    m_chistics->language.str= strmake_root(mem_root,
+					  m_chistics->language.str,
+					  m_chistics->language.length);
   m_sql_mode= sql_mode;
 }
 
@@ -2540,6 +2550,8 @@ int sp_instr::exec_core(THD *thd, uint *
   DBUG_ASSERT(0);
   return 0;
 }
+
+
 
 /*
   sp_instr_stmt class functions

--- 1.34/sql/sp_rcontext.h	2007-05-22 03:05:40 -07:00
+++ 1.35/sql/sp_rcontext.h	2007-05-22 03:05:40 -07:00
@@ -64,6 +64,7 @@ class sp_rcontext : public Sql_alloc
 {
   sp_rcontext(const sp_rcontext &); /* Prevent use of these */
   void operator=(sp_rcontext &);
+  friend class sp_instr_external;
 
  public:
 
Thread
bk commit into 5.1 tree (antony:1.2524)antony24 May