diff --git a/mysql-test/t/rpl_ndb_delete_nowhere.test b/mysql-test/t/rpl_ndb_delete_nowhere.test
index 64e0156c3f84da028d5545388921f391b375835a..7c8c16339cb303fb378792832cce339f246e6d8b 100644
--- a/mysql-test/t/rpl_ndb_delete_nowhere.test
+++ b/mysql-test/t/rpl_ndb_delete_nowhere.test
@@ -2,5 +2,6 @@
 # By JBM 2006-02-14 Test wrapping to    #
 # Share test code between engine tests  #
 #########################################
+--source include/have_ndb.inc
 let $engine_type=NDB;
 -- source extra/rpl_tests/rpl_delete_no_where.test
diff --git a/plugin/fulltext/AUTHORS b/plugin/fulltext/AUTHORS
new file mode 100644
index 0000000000000000000000000000000000000000..9c4567884af1ade8f3b857af10eb8730f97384cd
--- /dev/null
+++ b/plugin/fulltext/AUTHORS
@@ -0,0 +1 @@
+AUTHORS file example for a plugin
diff --git a/plugin/fulltext/ChangeLog b/plugin/fulltext/ChangeLog
new file mode 100644
index 0000000000000000000000000000000000000000..1faaaad618ac7e9c46eedc44686d6cc0b54b45e0
--- /dev/null
+++ b/plugin/fulltext/ChangeLog
@@ -0,0 +1 @@
+ChangeLog file example for a plugin
diff --git a/plugin/fulltext/Makefile.am b/plugin/fulltext/Makefile.am
index e5a70a23beb7856183e51b2bab34d670265e5465..4df5a1dc78ab6003573aa2afc81899a668ae5d26 100644
--- a/plugin/fulltext/Makefile.am
+++ b/plugin/fulltext/Makefile.am
@@ -1,4 +1,9 @@
+#Makefile.am example for a plugin
+
+pkglibdir=$(libdir)/mysql
 INCLUDES= -I$(top_builddir)/include -I$(top_srcdir)/include
 noinst_LTLIBRARIES= mypluglib.la
+#pkglib_LTLIBRARIES= mypluglib.la
 mypluglib_la_SOURCES= plugin_example.c
 mypluglib_la_LDFLAGS= -module -rpath $(pkglibdir)
+
diff --git a/plugin/fulltext/NEWS b/plugin/fulltext/NEWS
new file mode 100644
index 0000000000000000000000000000000000000000..e5b6cbd50a288b37bdaed4ddb5a8a404c293a331
--- /dev/null
+++ b/plugin/fulltext/NEWS
@@ -0,0 +1 @@
+NEWS file example for a plugin
diff --git a/plugin/fulltext/README b/plugin/fulltext/README
new file mode 100644
index 0000000000000000000000000000000000000000..e4d01f2afb75f17ebd84e897b8cfffe21225bf13
--- /dev/null
+++ b/plugin/fulltext/README
@@ -0,0 +1 @@
+README file example for a plugin
diff --git a/plugin/fulltext/configure.in b/plugin/fulltext/configure.in
new file mode 100644
index 0000000000000000000000000000000000000000..dc0a2f4630d070ee1231b7d0dfbb0d15483f850e
--- /dev/null
+++ b/plugin/fulltext/configure.in
@@ -0,0 +1,8 @@
+# configure.in example for a plugin
+
+AC_INIT(plugin_example, 0.1)
+AM_INIT_AUTOMAKE
+AC_PROG_LIBTOOL
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
+
diff --git a/sql/lex.h b/sql/lex.h
index 9fd8cae13251d83c7688aec959de866e91e8b377..9f0c6c4f8b74f396bfb797c3afe6f0b3910b020c 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -396,6 +396,7 @@ static SYMBOL symbols[] = {
   { "PASSWORD",		SYM(PASSWORD)},
   { "PHASE",            SYM(PHASE_SYM)},
   { "PLUGIN",           SYM(PLUGIN_SYM)},
+  { "PLUGINS",          SYM(PLUGINS_SYM)},
   { "POINT",		SYM(POINT_SYM)},
   { "POLYGON",		SYM(POLYGON)},
   { "PRECISION",	SYM(PRECISION)},
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index e9379ab2f1899793035d49b0ed024e90e2e4628d..be768d6e755c3cfd8d7c0bed0b3a507052846374 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -501,6 +501,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
 %token  PARAM_MARKER
 %token  PHASE_SYM
 %token  PLUGIN_SYM
+%token  PLUGINS_SYM
 %token  POINTFROMTEXT
 %token  POINT_SYM
 %token  POLYFROMTEXT
@@ -8170,6 +8171,15 @@ show_param:
               YYABORT;
 	  }
         | PLUGIN_SYM
+	  {
+	    LEX *lex= Lex;
+	    WARN_DEPRECATED(yythd, "5.2", "SHOW PLUGIN", "'SHOW PLUGINS'");
+            lex->sql_command= SQLCOM_SELECT;
+            lex->orig_sql_command= SQLCOM_SHOW_PLUGINS;
+            if (prepare_schema_table(YYTHD, lex, 0, SCH_PLUGINS))
+              YYABORT;
+	  }
+        | PLUGINS_SYM
 	  {
 	    LEX *lex= Lex;
             lex->sql_command= SQLCOM_SELECT;
@@ -9358,7 +9368,6 @@ keyword:
 	| OPEN_SYM		{}
         | PARSER_SYM            {}
 	| PARTITION_SYM		{}
-        | PLUGIN_SYM            {}
         | PREPARE_SYM           {}
 	| REMOVE_SYM		{}
 	| REPAIR		{}
@@ -9539,6 +9548,8 @@ keyword_sp:
 	| PARTITIONS_SYM	{}
 	| PASSWORD		{}
         | PHASE_SYM             {}
+        | PLUGIN_SYM            {}
+        | PLUGINS_SYM           {}
 	| POINT_SYM		{}
 	| POLYGON		{}
         | PRESERVE_SYM          {}
@@ -9552,7 +9563,7 @@ keyword_sp:
         | REBUILD_SYM           {}
         | RECOVER_SYM           {}
 	| REDO_BUFFER_SIZE_SYM	{}
-	| REDOFILE_SYM  	{}
+	| REDOFILE_SYM          {}
         | REDUNDANT_SYM         {}
 	| RELAY_LOG_FILE_SYM	{}
 	| RELAY_LOG_POS_SYM	{}
diff --git a/sql/table.cc b/sql/table.cc
index 4807d9d0cf00aaec92afe0f0635ae263b95dc240..41621a1990055506d566e8bd35e19f20c596a936 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -26,8 +26,8 @@
 
 void open_table_error(TABLE_SHARE *share, int error, int db_errno,
                       myf errortype, int errarg);
-static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
-                           File file);
+static int open_binary_frm(THD *thd, TABLE_SHARE *share,
+                           uchar *head, File file);
 static void fix_type_pointers(const char ***array, TYPELIB *point_to_type,
 			      uint types, char **names);
 static uint find_field(Field **fields, uint start, uint length);
@@ -717,8 +717,8 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
         keyinfo->parser= plugin_lock(&parser_name, MYSQL_FTPARSER_PLUGIN);
         if (! keyinfo->parser)
         {
-          my_free(buff, MYF(0));
           my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), parser_name.str);
+          my_free(buff, MYF(0));
           goto err;
         }
       }
diff --git a/storage/myisam/ft_boolean_search.c b/storage/myisam/ft_boolean_search.c
index 4204f211f2ef1d5e5f9d67fb79841d09f6a9f015..8d48f533203d0f4f71f40a76fdfd48069027c1ef 100644
--- a/storage/myisam/ft_boolean_search.c
+++ b/storage/myisam/ft_boolean_search.c
@@ -160,6 +160,7 @@ static int FTB_WORD_cmp_list(CHARSET_INFO *cs, FTB_WORD **a, FTB_WORD **b)
 
 typedef struct st_my_ftb_param
 {
+  MYSQL_FTPARSER_PARAM *up;
   FTB *ftb;
   FTB_EXPR *ftbe;
   byte *up_quot;
@@ -280,7 +281,7 @@ static int ftb_parse_query_internal(void *param, char *query, int len)
   info.prev= ' ';
   info.quot= 0;
   while (ft_get_word(cs, start, end, &w, &info))
-    ftb_query_add_word(param, w.pos, w.len, &info);
+    ftb_param->up->mysql_add_word(param, w.pos, w.len, &info);
   return(0);
 }
 
@@ -295,14 +296,15 @@ static void _ftb_parse_query(FTB *ftb, byte *query, uint len,
 
   if (ftb->state != UNINITIALIZED)
     DBUG_VOID_RETURN;
+  if (! (param= ftparser_call_initializer(ftb->info, ftb->keynr, 0)))
+    DBUG_VOID_RETURN;
 
+  ftb_param.up= param;
   ftb_param.ftb= ftb;
   ftb_param.depth= 0;
   ftb_param.ftbe= ftb->root;
   ftb_param.up_quot= 0;
 
-  if (! (param= ftparser_call_initializer(ftb->info, ftb->keynr)))
-    DBUG_VOID_RETURN;
   param->mysql_parse= ftb_parse_query_internal;
   param->mysql_add_word= ftb_query_add_word;
   param->mysql_ftparam= (void *)&ftb_param;
@@ -313,7 +315,7 @@ static void _ftb_parse_query(FTB *ftb, byte *query, uint len,
   parser->parse(param);
   DBUG_VOID_RETURN;
 }
- 
+
 
 static int _ftb_no_dupes_cmp(void* not_used __attribute__((unused)),
                              const void *a,const void *b)
@@ -569,6 +571,7 @@ FT_INFO * ft_init_boolean_search(MI_INFO *info, uint keynr, byte *query,
 
 typedef struct st_my_ftb_phrase_param
 {
+  MYSQL_FTPARSER_PARAM *up;
   LIST *phrase;
   LIST *document;
   CHARSET_INFO *cs;
@@ -615,7 +618,7 @@ static int ftb_check_phrase_internal(void *param, char *document, int len)
   const char *docend= document + len;
   while (ft_simple_get_word(phrase_param->cs, &document, docend, &word, FALSE))
   {
-    ftb_phrase_add_word(param, word.pos, word.len, 0);
+    phrase_param->up->mysql_add_word(param, word.pos, word.len, 0);
     if (phrase_param->match)
       return 1;
   }
@@ -644,8 +647,11 @@ static int _ftb_check_phrase(FTB *ftb, const byte *document, uint len,
   MYSQL_FTPARSER_PARAM *param;
   DBUG_ENTER("_ftb_check_phrase");
   DBUG_ASSERT(parser);
-  if (! (param= ftparser_call_initializer(ftb->info, ftb->keynr)))
+
+  if (! (param= ftparser_call_initializer(ftb->info, ftb->keynr, 1)))
     DBUG_RETURN(0);
+
+  ftb_param.up= param;
   ftb_param.phrase= ftbe->phrase;
   ftb_param.document= ftbe->document;
   ftb_param.cs= ftb->charset;
@@ -814,6 +820,7 @@ int ft_boolean_read_next(FT_INFO *ftb, char *record)
 
 typedef struct st_my_ftb_find_param
 {
+  MYSQL_FTPARSER_PARAM *up;
   FT_INFO *ftb;
   FT_SEG_ITERATOR *ftsi;
 } MY_FTB_FIND_PARAM;
@@ -854,11 +861,12 @@ static int ftb_find_relevance_add_word(void *param, char *word, int len,
 
 static int ftb_find_relevance_parse(void *param, char *doc, int len)
 {
-  FT_INFO *ftb= ((MY_FTB_FIND_PARAM *)param)->ftb;
+  MY_FTB_FIND_PARAM *ftb_param=(MY_FTB_FIND_PARAM *)param;
+  FT_INFO *ftb= ftb_param->ftb;
   char *end= doc + len;
   FT_WORD w;
   while (ft_simple_get_word(ftb->charset, &doc, end, &w, TRUE))
-    ftb_find_relevance_add_word(param, w.pos, w.len, 0);
+    ftb_param->up->mysql_add_word(param, w.pos, w.len, 0);
   return(0);
 }
 
@@ -878,7 +886,7 @@ float ft_boolean_find_relevance(FT_INFO *ftb, byte *record, uint length)
     return -2.0;
   if (!ftb->queue.elements)
     return 0;
-  if (! (param= ftparser_call_initializer(ftb->info, ftb->keynr)))
+  if (! (param= ftparser_call_initializer(ftb->info, ftb->keynr, 0)))
     return 0;
 
   if (ftb->state != INDEX_SEARCH && docid <= ftb->lastpos)
@@ -902,19 +910,18 @@ float ft_boolean_find_relevance(FT_INFO *ftb, byte *record, uint length)
     _mi_ft_segiterator_init(ftb->info, ftb->keynr, record, &ftsi);
   memcpy(&ftsi2, &ftsi, sizeof(ftsi));
 
+  ftb_param.up= param;
   ftb_param.ftb= ftb;
   ftb_param.ftsi= &ftsi2;
+  param->mysql_parse= ftb_find_relevance_parse;
+  param->mysql_add_word= ftb_find_relevance_add_word;
+  param->mysql_ftparam= (void *)&ftb_param;
+  param->cs= ftb->charset;
+  param->mode= MYSQL_FTPARSER_SIMPLE_MODE;
   while (_mi_ft_segiterator(&ftsi))
   {
     if (!ftsi.pos)
       continue;
-    /* Since subsequent call to _ftb_check_phrase overwrites param elements,
-       it must be reinitialized at each iteration _inside_ the loop. */
-    param->mysql_parse= ftb_find_relevance_parse;
-    param->mysql_add_word= ftb_find_relevance_add_word;
-    param->mysql_ftparam= (void *)&ftb_param;
-    param->cs= ftb->charset;
-    param->mode= MYSQL_FTPARSER_SIMPLE_MODE;
     param->doc= (byte *)ftsi.pos;
     param->length= ftsi.len;
     parser->parse(param);
diff --git a/storage/myisam/ft_nlq_search.c b/storage/myisam/ft_nlq_search.c
index b4468d8bd950f53c4e704c620c84654b3c1613a9..b8207c1c3d07e3c2f5b6646cee20e7b89a9615e5 100644
--- a/storage/myisam/ft_nlq_search.c
+++ b/storage/myisam/ft_nlq_search.c
@@ -226,7 +226,7 @@ FT_INFO *ft_init_nlq_search(MI_INFO *info, uint keynr, byte *query,
   aio.charset=info->s->keyinfo[keynr].seg->charset;
   aio.keybuff=info->lastkey+info->s->base.max_key_length;
   parser= info->s->keyinfo[keynr].parser;
-  if (! (ftparser_param= ftparser_call_initializer(info, keynr)))
+  if (! (ftparser_param= ftparser_call_initializer(info, keynr, 0)))
     goto err;
 
   bzero(&wtree,sizeof(wtree));
diff --git a/storage/myisam/ft_parser.c b/storage/myisam/ft_parser.c
index f333a661ea9b7be3161b7dc2289698738778a041..89ede813a2b4b1181b6c91080a4b27bf543ced34 100644
--- a/storage/myisam/ft_parser.c
+++ b/storage/myisam/ft_parser.c
@@ -27,6 +27,7 @@ typedef struct st_ft_docstat {
 
 typedef struct st_my_ft_parser_param
 {
+  MYSQL_FTPARSER_PARAM *up;
   TREE *wtree;
   my_bool with_alloc;
 } MY_FT_PARSER_PARAM;
@@ -268,16 +269,16 @@ static int ft_add_word(void *param, byte *word, uint word_len,
 }
 
 
-static int ft_parse_internal(void *param, byte *doc, uint doc_len)
+static int ft_parse_internal(void *param, byte *doc, int doc_len)
 {
   byte   *end=doc+doc_len;
+  MY_FT_PARSER_PARAM *ft_param=(MY_FT_PARSER_PARAM *)param;
+  TREE *wtree= ft_param->wtree;
   FT_WORD w;
-  TREE *wtree;
   DBUG_ENTER("ft_parse_internal");
 
-  wtree= ((MY_FT_PARSER_PARAM *)param)->wtree;
   while (ft_simple_get_word(wtree->custom_arg, &doc, end, &w, TRUE))
-    if (ft_add_word(param, w.pos, w.len, 0))
+    if (ft_param->up->mysql_add_word(param, w.pos, w.len, 0))
       DBUG_RETURN(1);
   DBUG_RETURN(0);
 }
@@ -290,6 +291,8 @@ int ft_parse(TREE *wtree, byte *doc, int doclen, my_bool with_alloc,
   MY_FT_PARSER_PARAM my_param;
   DBUG_ENTER("ft_parse");
   DBUG_ASSERT(parser);
+
+  my_param.up= param;
   my_param.wtree= wtree;
   my_param.with_alloc= with_alloc;
 
@@ -300,11 +303,12 @@ int ft_parse(TREE *wtree, byte *doc, int doclen, my_bool with_alloc,
   param->doc= doc;
   param->length= doclen;
   param->mode= MYSQL_FTPARSER_SIMPLE_MODE;
-  DBUG_RETURN(parser->parse(param));  
+  DBUG_RETURN(parser->parse(param));
 }
 
-
-MYSQL_FTPARSER_PARAM *ftparser_call_initializer(MI_INFO *info, uint keynr)
+#define MAX_PARAM_NR 2
+MYSQL_FTPARSER_PARAM *ftparser_call_initializer(MI_INFO *info,
+                                                uint keynr, uint paramnr)
 {
   uint32 ftparser_nr;
   struct st_mysql_ftparser *parser;
@@ -343,8 +347,14 @@ MYSQL_FTPARSER_PARAM *ftparser_call_initializer(MI_INFO *info, uint keynr)
       }
       info->s->ftparsers= ftparsers;
     }
+    /*
+      We have to allocate two MYSQL_FTPARSER_PARAM structures per plugin
+      because in a boolean search a parser is called recursively
+      ftb_find_relevance* calls ftb_check_phrase*
+      (MAX_PARAM_NR=2)
+    */
     info->ftparser_param= (MYSQL_FTPARSER_PARAM *)
-      my_malloc(sizeof(MYSQL_FTPARSER_PARAM) *
+      my_malloc(MAX_PARAM_NR * sizeof(MYSQL_FTPARSER_PARAM) *
                 info->s->ftparsers, MYF(MY_WME|MY_ZEROFILL));
     if (! info->ftparser_param)
       return 0;
@@ -359,6 +369,8 @@ MYSQL_FTPARSER_PARAM *ftparser_call_initializer(MI_INFO *info, uint keynr)
     ftparser_nr= info->s->keyinfo[keynr].ftparser_nr;
     parser= info->s->keyinfo[keynr].parser;
   }
+  DBUG_ASSERT(paramnr < MAX_PARAM_NR);
+  ftparser_nr= ftparser_nr*MAX_PARAM_NR + paramnr;
   if (! info->ftparser_param[ftparser_nr].mysql_add_word)
   {
     /* Note, that mysql_add_word is used here as a flag:
@@ -372,22 +384,27 @@ MYSQL_FTPARSER_PARAM *ftparser_call_initializer(MI_INFO *info, uint keynr)
   return &info->ftparser_param[ftparser_nr];
 }
 
-
 void ftparser_call_deinitializer(MI_INFO *info)
 {
-  uint i, keys= info->s->state.header.keys;
+  uint i, j, keys= info->s->state.header.keys;
   if (! info->ftparser_param)
     return;
   for (i= 0; i < keys; i++)
   {
     MI_KEYDEF *keyinfo= &info->s->keyinfo[i];
-    MYSQL_FTPARSER_PARAM *ftparser_param=
-      &info->ftparser_param[keyinfo->ftparser_nr];
-    if (keyinfo->flag & HA_FULLTEXT && ftparser_param->mysql_add_word)
+    for (j=0; j < MAX_PARAM_NR; j++)
     {
-      if (keyinfo->parser->deinit)
-        keyinfo->parser->deinit(ftparser_param);
-      ftparser_param->mysql_add_word= 0;
+      MYSQL_FTPARSER_PARAM *ftparser_param=
+        &info->ftparser_param[keyinfo->ftparser_nr*MAX_PARAM_NR + j];
+      if (keyinfo->flag & HA_FULLTEXT && ftparser_param->mysql_add_word)
+      {
+        if (keyinfo->parser->deinit)
+          keyinfo->parser->deinit(ftparser_param);
+        ftparser_param->mysql_add_word= 0;
+      }
+      else
+        break;
     }
   }
 }
+
diff --git a/storage/myisam/ft_update.c b/storage/myisam/ft_update.c
index 1ec91b4121853a0030e628437245da0b70f64972..f5501792dcd3c24a27c48f56e2de50e015f06561 100644
--- a/storage/myisam/ft_update.c
+++ b/storage/myisam/ft_update.c
@@ -122,7 +122,7 @@ FT_WORD * _mi_ft_parserecord(MI_INFO *info, uint keynr, const byte *record)
   TREE ptree;
   MYSQL_FTPARSER_PARAM *param;
   DBUG_ENTER("_mi_ft_parserecord");
-  if (! (param= ftparser_call_initializer(info, keynr)))
+  if (! (param= ftparser_call_initializer(info, keynr, 0)))
     DBUG_RETURN(NULL);
   bzero((char*) &ptree, sizeof(ptree));
   if (_mi_ft_parse(&ptree, info, keynr, record, 0, param))
diff --git a/storage/myisam/ftdefs.h b/storage/myisam/ftdefs.h
index 175f34df102a9cd09bcc8194448c051bbc77fadb..2c4cfa1ffd6a2486930a7a81d481a77004711d18 100644
--- a/storage/myisam/ftdefs.h
+++ b/storage/myisam/ftdefs.h
@@ -145,5 +145,6 @@ float ft_boolean_get_relevance(FT_INFO *);
 my_off_t ft_boolean_get_docid(FT_INFO *);
 void ft_boolean_reinit_search(FT_INFO *);
 extern MYSQL_FTPARSER_PARAM *ftparser_call_initializer(MI_INFO *info,
-                                                       uint keynr);
+                                                       uint keynr,
+                                                       uint paramnr);
 extern void ftparser_call_deinitializer(MI_INFO *info);