diff --git a/include/maria.h b/include/maria.h
index c5bd8641c38675fab32d0e7d5a0af09c4e9be64b..d14ca10e9b567f6936f4fb8477aecea22740f53b 100644
--- a/include/maria.h
+++ b/include/maria.h
@@ -191,7 +191,7 @@ typedef struct st_maria_keydef          /* Key definition with open & info */
   uint16 maxlength;                     /* max length of (packed) key (auto) */
   uint32 write_comp_flag;		/* compare flag for write key (auto) */
   uint32 version;                       /* For concurrent read/write */
-  uint32 ftparser_nr;                   /* distinct ftparser number */
+  uint32 ftkey_nr;                      /* full-text index number */
 
   HA_KEYSEG *seg, *end;
   struct st_mysql_ftparser *parser;     /* Fulltext [pre]parser */
diff --git a/storage/maria/ma_check.c b/storage/maria/ma_check.c
index 8f5e07e27f28427a85dab81a1f38ad9be34e63d5..047ac0617069b764856a9bc0f08dbee4237af868 100644
--- a/storage/maria/ma_check.c
+++ b/storage/maria/ma_check.c
@@ -3675,7 +3675,7 @@ int maria_repair_by_sort(HA_CHECK *param, register MARIA_HA *info,
 
         Note, built-in parser is always nr. 0 - see ftparser_call_initializer()
       */
-      if (sort_param.keyinfo->ftparser_nr == 0)
+      if (sort_param.keyinfo->ftkey_nr == 0)
       {
         /*
           for built-in parser the number of generated index entries
@@ -4222,6 +4222,9 @@ int maria_repair_parallel(HA_CHECK *param, register MARIA_HA *info,
   sort_param[0].fix_datafile= ! rep_quick;
   sort_param[0].calc_checksum= test(param->testflag & T_CALC_CHECKSUM);
 
+  if (!maria_ftparser_alloc_param(info))
+    goto err;
+
   sort_info.got_error=0;
   pthread_mutex_lock(&sort_info.mutex);
 
diff --git a/storage/maria/ma_ft_boolean_search.c b/storage/maria/ma_ft_boolean_search.c
index 3bcbd9edfbf61f0baa287400d02d30a712bd8d2c..148fc8c783c86c664ee882a4535ea85e9f058283 100644
--- a/storage/maria/ma_ft_boolean_search.c
+++ b/storage/maria/ma_ft_boolean_search.c
@@ -161,11 +161,11 @@ static int FTB_WORD_cmp(my_off_t *v, FTB_WORD *a, FTB_WORD *b)
 
 static int FTB_WORD_cmp_list(CHARSET_INFO *cs, FTB_WORD **a, FTB_WORD **b)
 {
-  /* ORDER BY word DESC, ndepth DESC */
-  int i= ha_compare_text(cs, (uchar*) (*b)->word+1,(*b)->len-1,
-                             (uchar*) (*a)->word+1,(*a)->len-1,0,0);
+  /* ORDER BY word, ndepth */
+  int i= ha_compare_text(cs, (uchar*) (*a)->word + 1,(*a)->len - 1,
+                             (uchar*) (*b)->word + 1,(*b)->len - 1, 0, 0);
   if (!i)
-    i=CMP_NUM((*b)->ndepth,(*a)->ndepth);
+    i=CMP_NUM((*a)->ndepth, (*b)->ndepth);
   return i;
 }
 
@@ -879,23 +879,49 @@ static int ftb_find_relevance_add_word(MYSQL_FTPARSER_PARAM *param,
   FT_INFO *ftb= ftb_param->ftb;
   FTB_WORD *ftbw;
   int a, b, c;
+  /*
+    Find right-most element in the array of query words matching this
+    word from a document.
+  */
   for (a= 0, b= ftb->queue.elements, c= (a+b)/2; b-a>1; c= (a+b)/2)
   {
     ftbw= ftb->list[c];
     if (ha_compare_text(ftb->charset, (uchar*)word, len,
                         (uchar*)ftbw->word+1, ftbw->len-1,
-                        (my_bool)(ftbw->flags&FTB_FLAG_TRUNC), 0) > 0)
+                        (my_bool)(ftbw->flags&FTB_FLAG_TRUNC), 0) < 0)
       b= c;
     else
       a= c;
   }
+  /*
+    If there were no words with truncation operator, we iterate to the
+    beginning of an array until array element is equal to the word from
+    a document. This is done mainly because the same word may be
+    mentioned twice (or more) in the query.
+
+    In case query has words with truncation operator we must iterate
+    to the beginning of the array. There may be non-matching query words
+    between matching word with truncation operator and the right-most
+    matching element. E.g., if we're looking for 'aaa15' in an array of
+    'aaa1* aaa14 aaa15 aaa16'.
+
+    Worse of that there still may be match even if the binary search
+    above didn't find matching element. E.g., if we're looking for
+    'aaa15' in an array of 'aaa1* aaa14 aaa16'. The binary search will
+    stop at 'aaa16'.
+  */
   for (; c >= 0; c--)
   {
     ftbw= ftb->list[c];
     if (ha_compare_text(ftb->charset, (uchar*)word, len,
                         (uchar*)ftbw->word + 1,ftbw->len - 1,
                         (my_bool)(ftbw->flags & FTB_FLAG_TRUNC), 0))
-      break;
+    {
+      if (ftb->with_scan & FTB_FLAG_TRUNC)
+        continue;
+      else
+        break;
+    }
     if (ftbw->docid[1] == ftb->info->cur_row.lastpos)
       continue;
     ftbw->docid[1]= ftb->info->cur_row.lastpos;
diff --git a/storage/maria/ma_ft_parser.c b/storage/maria/ma_ft_parser.c
index 25704f1af5affafd6f58b1ad22292b5630eb7c6f..8eb40a207898bd08b71ce639a7387bee948f2755 100644
--- a/storage/maria/ma_ft_parser.c
+++ b/storage/maria/ma_ft_parser.c
@@ -326,59 +326,39 @@ int maria_ft_parse(TREE *wtree, uchar *doc, int doclen,
 
 
 #define MAX_PARAM_NR 2
-MYSQL_FTPARSER_PARAM *maria_ftparser_call_initializer(MARIA_HA *info,
-                                                      uint keynr, uint paramnr)
+
+MYSQL_FTPARSER_PARAM* maria_ftparser_alloc_param(MARIA_HA *info)
 {
-  uint32 ftparser_nr;
-  struct st_mysql_ftparser *parser;
-  if (! info->ftparser_param)
+  if (!info->ftparser_param)
   {
-    /* info->ftparser_param can not be zero after the initialization,
-       because it always includes built-in fulltext parser. And built-in
-       parser can be called even if the table has no fulltext indexes and
-       no varchar/text fields. */
-    if (! info->s->ftparsers)
-    {
-      /* It's ok that modification to shared structure is done w/o mutex
-         locks, because all threads would set the same variables to the
-         same values. */
-      uint i, j, keys= info->s->state.header.keys, ftparsers= 1;
-      for (i= 0; i < keys; i++)
-      {
-        MARIA_KEYDEF *keyinfo= &info->s->keyinfo[i];
-        if (keyinfo->flag & HA_FULLTEXT)
-        {
-          for (j= 0;; j++)
-          {
-            if (j == i)
-            {
-              keyinfo->ftparser_nr= ftparsers++;
-              break;
-            }
-            if (info->s->keyinfo[j].flag & HA_FULLTEXT &&
-                keyinfo->parser == info->s->keyinfo[j].parser)
-            {
-              keyinfo->ftparser_nr= info->s->keyinfo[j].ftparser_nr;
-              break;
-            }
-          }
-        }
-      }
-      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 can not be zero after the initialization,
+      because it always includes built-in fulltext parser. And built-in
+      parser can be called even if the table has no fulltext indexes and
+      no varchar/text fields.
+
+      ftb_find_relevance... parser (ftb_find_relevance_parse,
+      ftb_find_relevance_add_word) calls ftb_check_phrase... parser
+      (ftb_check_phrase_internal, ftb_phrase_add_word). Thus MAX_PARAM_NR=2.
     */
     info->ftparser_param= (MYSQL_FTPARSER_PARAM *)
       my_malloc(MAX_PARAM_NR * sizeof(MYSQL_FTPARSER_PARAM) *
-                info->s->ftparsers, MYF(MY_WME|MY_ZEROFILL));
+                info->s->ftkeys, MYF(MY_WME | MY_ZEROFILL));
     init_alloc_root(&info->ft_memroot, FTPARSER_MEMROOT_ALLOC_SIZE, 0);
-    if (! info->ftparser_param)
-      return 0;
   }
+  return info->ftparser_param;
+}
+
+
+MYSQL_FTPARSER_PARAM *maria_ftparser_call_initializer(MARIA_HA *info,
+                                                      uint keynr, uint paramnr)
+{
+  uint32 ftparser_nr;
+  struct st_mysql_ftparser *parser;
+  
+  if (!maria_ftparser_alloc_param(info))
+    return 0;
+
   if (keynr == NO_SUCH_KEY)
   {
     ftparser_nr= 0;
@@ -386,7 +366,7 @@ MYSQL_FTPARSER_PARAM *maria_ftparser_call_initializer(MARIA_HA *info,
   }
   else
   {
-    ftparser_nr= info->s->keyinfo[keynr].ftparser_nr;
+    ftparser_nr= info->s->keyinfo[keynr].ftkey_nr;
     parser= info->s->keyinfo[keynr].parser;
   }
   DBUG_ASSERT(paramnr < MAX_PARAM_NR);
@@ -419,7 +399,7 @@ void maria_ftparser_call_deinitializer(MARIA_HA *info)
     for (j=0; j < MAX_PARAM_NR; j++)
     {
       MYSQL_FTPARSER_PARAM *ftparser_param=
-        &info->ftparser_param[keyinfo->ftparser_nr*MAX_PARAM_NR + j];
+        &info->ftparser_param[keyinfo->ftkey_nr*MAX_PARAM_NR + j];
       if (keyinfo->flag & HA_FULLTEXT && ftparser_param->mysql_add_word)
       {
         if (keyinfo->parser->deinit)
diff --git a/storage/maria/ma_ftdefs.h b/storage/maria/ma_ftdefs.h
index bd5309ea265517c105e9f985d674bb854b375600..7e83d774aed43d6debd81ce676eb0037b5ec7832 100644
--- a/storage/maria/ma_ftdefs.h
+++ b/storage/maria/ma_ftdefs.h
@@ -147,6 +147,7 @@ void maria_ft_boolean_close_search(FT_INFO *);
 float maria_ft_boolean_get_relevance(FT_INFO *);
 my_off_t maria_ft_boolean_get_docid(FT_INFO *);
 void maria_ft_boolean_reinit_search(FT_INFO *);
+MYSQL_FTPARSER_PARAM* maria_ftparser_alloc_param(MARIA_HA *info);
 extern MYSQL_FTPARSER_PARAM *maria_ftparser_call_initializer(MARIA_HA *info,
                                                              uint keynr,
                                                              uint paramnr);
diff --git a/storage/maria/ma_open.c b/storage/maria/ma_open.c
index c6c786910db1d6bf630c6c1a60dc528059d71ace..cc004c23a252a6edbba6eaccb1389151770524dc 100644
--- a/storage/maria/ma_open.c
+++ b/storage/maria/ma_open.c
@@ -537,6 +537,7 @@ MARIA_HA *maria_open(const char *name, int mode, uint open_flags)
     share->block_size= share->base.block_size;   /* Convenience */
     {
       HA_KEYSEG *pos=share->keyparts;
+      uint32 ftkey_nr= 1;
       for (i=0 ; i < keys ; i++)
       {
         share->keyinfo[i].share= share;
@@ -609,6 +610,7 @@ MARIA_HA *maria_open(const char *name, int mode, uint open_flags)
             share->ft2_keyinfo.end=pos;
             setup_key_functions(& share->ft2_keyinfo);
           }
+          share->keyinfo[i].ftkey_nr= ftkey_nr++;
 	}
         setup_key_functions(share->keyinfo+i);
 	share->keyinfo[i].end=pos;
@@ -646,7 +648,7 @@ MARIA_HA *maria_open(const char *name, int mode, uint open_flags)
 	pos->flag=0;
 	pos++;
       }
-      share->ftparsers= 0;
+      share->ftkeys= ftkey_nr;
     }
     share->data_file_type= share->state.header.data_file_type;
     share->base_length= (BASE_ROW_HEADER_SIZE +
@@ -1527,7 +1529,7 @@ uchar *_ma_keydef_read(uchar *ptr, MARIA_KEYDEF *keydef)
    keydef->underflow_block_length=keydef->block_length/3;
    keydef->version	= 0;			/* Not saved */
    keydef->parser       = &ft_default_parser;
-   keydef->ftparser_nr  = 0;
+   keydef->ftkey_nr     = 0;
    return ptr;
 }
 
diff --git a/storage/maria/maria_def.h b/storage/maria/maria_def.h
index 7c2017c46715632ffb202d6fa155b0557fa76add..bbc3246f323a71872e186ceaf3290229406ae698 100644
--- a/storage/maria/maria_def.h
+++ b/storage/maria/maria_def.h
@@ -348,7 +348,7 @@ typedef struct st_maria_share
   ulong state_diff_length;
   uint rec_reflength;			/* rec_reflength in use now */
   uint keypage_header;
-  uint32 ftparsers;			/* Number of distinct ftparsers
+  uint32 ftkeys;			/* Number of distinct full-text keys
 						   + 1 */
   PAGECACHE_FILE kfile;			/* Shared keyfile */
   File data_file;			/* Shared data file */
diff --git a/storage/maria/maria_pack.c b/storage/maria/maria_pack.c
index 234ac61c08dadd48ba85d26addbd7a6af9e21711..706a09353638840a2af27a223309a5f72ff971af 100644
--- a/storage/maria/maria_pack.c
+++ b/storage/maria/maria_pack.c
@@ -302,7 +302,7 @@ static void print_version(void)
 static void usage(void)
 {
   print_version();
-  puts("Copyright (C) 2002 MySQL AB");
+  puts("Copyright 2002-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc.");
   puts("This software comes with ABSOLUTELY NO WARRANTY. This is free software,");
   puts("and you are welcome to modify and redistribute it under the GPL license\n");