summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/my_handler.h13
-rw-r--r--include/myisam.h9
-rw-r--r--myisam/ft_boolean_search.c199
-rw-r--r--myisam/ft_dump.c21
-rw-r--r--myisam/ft_nlq_search.c85
-rw-r--r--myisam/ft_parser.c32
-rw-r--r--myisam/ft_static.c18
-rw-r--r--myisam/ft_update.c10
-rw-r--r--myisam/ftdefs.h13
-rw-r--r--myisam/fulltext.h11
-rw-r--r--myisam/mi_check.c163
-rw-r--r--myisam/mi_create.c45
-rw-r--r--myisam/mi_delete.c97
-rw-r--r--myisam/mi_open.c64
-rw-r--r--myisam/mi_rnext.c18
-rw-r--r--myisam/mi_update.c2
-rw-r--r--myisam/mi_write.c77
-rw-r--r--myisam/myisamchk.c4
-rw-r--r--myisam/myisamdef.h25
-rw-r--r--myisam/sort.c23
-rw-r--r--mysql-test/r/fulltext.result2
-rw-r--r--mysql-test/r/fulltext2.result871
-rw-r--r--mysql-test/t/fulltext.test1
-rw-r--r--mysql-test/t/fulltext2.test93
-rw-r--r--mysys/mulalloc.c14
25 files changed, 1556 insertions, 354 deletions
diff --git a/include/my_handler.h b/include/my_handler.h
index 629a0974d93..618d1df1a6e 100644
--- a/include/my_handler.h
+++ b/include/my_handler.h
@@ -1,15 +1,15 @@
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
-
+
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
-
+
This library 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
Library General Public License for more details.
-
+
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
@@ -43,6 +43,13 @@ typedef struct st_HA_KEYSEG /* Key-portion */
{ length=mi_uint2korr((key)+1); (key)+=3; } \
}
+#define get_key_length_rdonly(length,key) \
+{ if ((uchar) *(key) != 255) \
+ length= ((uint) (uchar) *((key))); \
+ else \
+ { length=mi_uint2korr((key)+1); } \
+}
+
#define get_key_pack_length(length,length_pack,key) \
{ if ((uchar) *(key) != 255) \
{ length= (uint) (uchar) *((key)++); length_pack=1; }\
diff --git a/include/myisam.h b/include/myisam.h
index 4f8fc149ba1..088fcb9c37f 100644
--- a/include/myisam.h
+++ b/include/myisam.h
@@ -342,6 +342,12 @@ typedef struct st_mi_check_param
char *op_name;
} MI_CHECK;
+typedef struct st_sort_ft_buf
+{
+ uchar *buf, *end;
+ int count;
+ uchar lastkey[MI_MAX_KEY_BUFF];
+} SORT_FT_BUF;
typedef struct st_sort_info
{
@@ -354,7 +360,8 @@ typedef struct st_sort_info
MI_CHECK *param;
char *buff;
SORT_KEY_BLOCKS *key_block,*key_block_end;
- /* sync things*/
+ SORT_FT_BUF *ft_buf;
+ /* sync things */
uint got_error, threads_running;
pthread_mutex_t mutex;
pthread_cond_t cond;
diff --git a/myisam/ft_boolean_search.c b/myisam/ft_boolean_search.c
index 6749de06ee2..190dc5206b1 100644
--- a/myisam/ft_boolean_search.c
+++ b/myisam/ft_boolean_search.c
@@ -21,6 +21,7 @@
#define FT_CORE
#include "ftdefs.h"
#include <queues.h>
+#include <assert.h> /* for DBUG_ASSERT() */
/* search with boolean queries */
@@ -63,42 +64,44 @@ struct st_ftb_expr
{
FTB_EXPR *up;
byte *quot, *qend;
- float weight;
- uint flags;
my_off_t docid[2]; /* for index search and for scan */
+ float weight;
float cur_weight;
- int yesses; /* number of "yes" words matched */
- int nos; /* number of "no" words matched */
- int ythresh; /* number of "yes" words in expr */
- int yweaks; /* number of "yes" words for scan only */
+ uint flags;
+ uint yesses; /* number of "yes" words matched */
+ uint nos; /* number of "no" words matched */
+ uint ythresh; /* number of "yes" words in expr */
+ uint yweaks; /* number of "yes" words for scan only */
};
typedef struct st_ftb_word
{
- FTB_EXPR *up;
- float weight;
- uint flags;
- my_off_t docid[2]; /* for index search and for scan */
- uint ndepth;
- int len;
- /* ... docid cache can be added here. SerG */
- byte word[1];
+ FTB_EXPR *up;
+ MI_KEYDEF *keyinfo;
+ my_off_t docid[2]; /* for index search and for scan */
+ my_off_t key_root;
+ float weight;
+ uint ndepth;
+ uint flags;
+ uint len;
+ uchar off;
+ byte word[1];
} FTB_WORD;
typedef struct st_ft_info
{
struct _ft_vft *please;
MI_INFO *info;
- uint keynr;
CHARSET_INFO *charset;
- enum { UNINITIALIZED, READY, INDEX_SEARCH, INDEX_DONE /*, SCAN*/ } state;
- uint with_scan;
- my_off_t lastpos;
FTB_EXPR *root;
- QUEUE queue;
- TREE no_dupes;
FTB_WORD **list;
MEM_ROOT mem_root;
+ QUEUE queue;
+ TREE no_dupes;
+ my_off_t lastpos;
+ uint keynr;
+ uchar with_scan;
+ enum { UNINITIALIZED, READY, INDEX_SEARCH, INDEX_DONE } state;
} FTB;
static int FTB_WORD_cmp(my_off_t *v, FTB_WORD *a, FTB_WORD *b)
@@ -160,6 +163,7 @@ static void _ftb_parse_query(FTB *ftb, byte **start, byte *end,
ftbw->up=up;
ftbw->docid[0]=ftbw->docid[1]=HA_POS_ERROR;
ftbw->ndepth= (param.yesno<0) + depth;
+ ftbw->key_root=HA_POS_ERROR;
memcpy(ftbw->word+1, w.pos, w.len);
ftbw->word[0]=w.len;
if (param.yesno > 0) up->ythresh++;
@@ -194,22 +198,98 @@ static int _ftb_no_dupes_cmp(void* not_used __attribute__((unused)),
return CMP_NUM((*((my_off_t*)a)), (*((my_off_t*)b)));
}
+/* returns 1 if the search was finished (must-word wasn't found) */
+static int _ft2_search(FTB *ftb, FTB_WORD *ftbw, my_bool init_search)
+{
+ int r;
+ uint off;
+ int subkeys;
+ MI_INFO *info=ftb->info;
+
+ if (init_search)
+ {
+ ftbw->key_root=info->s->state.key_root[ftb->keynr];
+ ftbw->keyinfo=info->s->keyinfo+ftb->keynr;
+ ftbw->off=0;
+
+ r=_mi_search(info, ftbw->keyinfo, (uchar*) ftbw->word, ftbw->len,
+ SEARCH_FIND | SEARCH_BIGGER, ftbw->key_root);
+ }
+ else
+ {
+ r=_mi_search(info, ftbw->keyinfo, (uchar*) ftbw->word+ftbw->off,
+ USE_WHOLE_KEY, SEARCH_BIGGER, ftbw->key_root);
+ }
+ if (!r && !ftbw->off)
+ {
+ r= mi_compare_text(ftb->charset,
+ info->lastkey + (ftbw->flags & FTB_FLAG_TRUNC),
+ ftbw->len - (ftbw->flags & FTB_FLAG_TRUNC),
+ (uchar*) ftbw->word + (ftbw->flags & FTB_FLAG_TRUNC),
+ ftbw->len - (ftbw->flags & FTB_FLAG_TRUNC),
+ 0);
+ }
+
+ if (r) /* not found */
+ {
+ if (!ftbw->off || !(ftbw->flags & FTB_FLAG_TRUNC))
+ {
+ ftbw->docid[0]=HA_POS_ERROR;
+ if ((ftbw->flags & FTB_FLAG_YES) && ftbw->up->up==0)
+ {
+ /*
+ This word MUST BE present in every document returned,
+ so we can stop the search right now
+ */
+ ftb->state=INDEX_DONE;
+ return 1; /* search is done */
+ }
+ else
+ return 0;
+ }
+
+ /* going up to the first-level tree to continue search there */
+ _mi_dpointer(info, ftbw->word+ftbw->off+HA_FT_WLEN, ftbw->key_root);
+ ftbw->key_root=info->s->state.key_root[ftb->keynr];
+ ftbw->keyinfo=info->s->keyinfo+ftb->keynr;
+ ftbw->off=0;
+ return _ft2_search(ftb, ftbw, 0);
+ }
+
+ /* matching key found */
+ memcpy(ftbw->word+ftbw->off, info->lastkey, info->lastkey_length);
+ if (!ftbw->off && (init_search || (ftbw->flags & FTB_FLAG_TRUNC)))
+ {
+ /* going down ? */
+ get_key_full_length_rdonly(off, info->lastkey);
+ subkeys=ft_sintXkorr(info->lastkey+off);
+ if (subkeys<0)
+ {
+ /* yep, going down, to the second-level tree */
+ /* TODO here: subkey-based optimization */
+ ftbw->off=off;
+ ftbw->key_root=info->lastpos;
+ ftbw->keyinfo=& info->s->ft2_keyinfo;
+ r=_mi_search_first(info, ftbw->keyinfo, ftbw->key_root);
+ DBUG_ASSERT(r==0); /* found something */
+ memcpy(ftbw->word+off, info->lastkey, info->lastkey_length);
+ }
+ }
+ ftbw->docid[0]=info->lastpos;
+ return 0;
+}
+
static void _ftb_init_index_search(FT_INFO *ftb)
{
- int i, r;
+ int i;
FTB_WORD *ftbw;
MI_INFO *info=ftb->info;
- MI_KEYDEF *keyinfo;
- my_off_t keyroot;
if ((ftb->state != READY && ftb->state !=INDEX_DONE) ||
ftb->keynr == NO_SUCH_KEY)
return;
ftb->state=INDEX_SEARCH;
- keyinfo=info->s->keyinfo+ftb->keynr;
- keyroot=info->s->state.key_root[ftb->keynr];
-
for (i=ftb->queue.elements; i; i--)
{
ftbw=(FTB_WORD *)(ftb->queue.root[i]);
@@ -248,34 +328,9 @@ static void _ftb_init_index_search(FT_INFO *ftb)
}
}
}
- r=_mi_search(info, keyinfo, (uchar*) ftbw->word, ftbw->len,
- SEARCH_FIND | SEARCH_BIGGER, keyroot);
- if (!r)
- {
- r= mi_compare_text(ftb->charset,
- info->lastkey + (ftbw->flags&FTB_FLAG_TRUNC),
- ftbw->len - (ftbw->flags&FTB_FLAG_TRUNC),
- (uchar*) ftbw->word + (ftbw->flags&FTB_FLAG_TRUNC),
- ftbw->len - (ftbw->flags&FTB_FLAG_TRUNC),
- 0);
- }
- if (r) /* not found */
- {
- if (ftbw->flags&FTB_FLAG_YES && ftbw->up->up==0)
- {
- /*
- This word MUST BE present in every document returned,
- so we can abort the search right now
- */
- ftb->state=INDEX_DONE;
- return;
- }
- }
- else
- {
- memcpy(ftbw->word, info->lastkey, info->lastkey_length);
- ftbw->docid[0]=info->lastpos;
- }
+
+ if (_ft2_search(ftb, ftbw, 1))
+ return;
}
queue_fix(& ftb->queue);
}
@@ -436,10 +491,7 @@ int ft_boolean_read_next(FT_INFO *ftb, char *record)
FTB_EXPR *ftbe;
FTB_WORD *ftbw;
MI_INFO *info=ftb->info;
- MI_KEYDEF *keyinfo=info->s->keyinfo+ftb->keynr;
- my_off_t keyroot=info->s->state.key_root[ftb->keynr];
my_off_t curdoc;
- int r;
if (ftb->state != INDEX_SEARCH && ftb->state != INDEX_DONE)
return -1;
@@ -466,34 +518,7 @@ int ft_boolean_read_next(FT_INFO *ftb, char *record)
_ftb_climb_the_tree(ftb, ftbw, 0);
/* update queue */
- r=_mi_search(info, keyinfo, (uchar*) ftbw->word, USE_WHOLE_KEY,
- SEARCH_BIGGER , keyroot);
- if (!r)
- {
- r= mi_compare_text(ftb->charset,
- info->lastkey + (ftbw->flags&FTB_FLAG_TRUNC),
- ftbw->len - (ftbw->flags&FTB_FLAG_TRUNC),
- (uchar*) ftbw->word + (ftbw->flags&FTB_FLAG_TRUNC),
- ftbw->len - (ftbw->flags&FTB_FLAG_TRUNC),
- 0);
- }
- if (r) /* not found */
- {
- ftbw->docid[0]=HA_POS_ERROR;
- if (ftbw->flags&FTB_FLAG_YES && ftbw->up->up==0)
- {
- /*
- This word MUST BE present in every document returned,
- so we can stop the search right now
- */
- ftb->state=INDEX_DONE;
- }
- }
- else
- {
- memcpy(ftbw->word, info->lastkey, info->lastkey_length);
- ftbw->docid[0]=info->lastpos;
- }
+ _ft2_search(ftb, ftbw, 0);
queue_replaced(& ftb->queue);
}
@@ -503,9 +528,9 @@ int ft_boolean_read_next(FT_INFO *ftb, char *record)
{
/* curdoc matched ! */
if (is_tree_inited(&ftb->no_dupes) &&
- tree_insert(&ftb->no_dupes, &curdoc, 0,
+ tree_insert(&ftb->no_dupes, &curdoc, 0,
ftb->no_dupes.custom_arg)->count >1)
- /* but it managed to get past this line once */
+ /* but it managed already to get past this line once */
continue;
info->lastpos=curdoc;
diff --git a/myisam/ft_dump.c b/myisam/ft_dump.c
index d430b611836..3ecb0652eea 100644
--- a/myisam/ft_dump.c
+++ b/myisam/ft_dump.c
@@ -56,7 +56,7 @@ static struct my_option my_long_options[] =
int main(int argc,char *argv[])
{
- int error=0;
+ int error=0, subkeys;
uint keylen, keylen2=0, inx, doc_cnt=0;
float weight;
double gws, min_gws=0, avg_gws=0;
@@ -125,7 +125,9 @@ int main(int argc,char *argv[])
keylen=*(info->lastkey);
#if HA_FT_WTYPE == HA_KEYTYPE_FLOAT
- mi_float4get(weight,info->lastkey+keylen+1);
+ subkeys=mi_sint4korr(info->lastkey+keylen+1);
+ if (subkeys >= 0)
+ weight=*(float*)&subkeys;
#else
#error
#endif
@@ -164,7 +166,10 @@ int main(int argc,char *argv[])
}
}
if (dump)
- printf("%9qx %20.7f %s\n",info->lastpos,weight,buf);
+ if (subkeys>=0)
+ printf("%9qx %20.7f %s\n",info->lastpos,weight,buf);
+ else
+ printf("%9qx => %17d %s\n",info->lastpos,-subkeys,buf);
if(verbose && (total%HOW_OFTEN_TO_WRITE)==0)
printf("%10ld\r",total);
@@ -216,18 +221,18 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
{
switch(optid) {
case 'd':
- dump=1;
+ dump=1;
complain(count || query);
break;
- case 's':
- stats=1;
+ case 's':
+ stats=1;
complain(query!=0);
break;
- case 'c':
+ case 'c':
count= 1;
complain(dump || query);
break;
- case 'l':
+ case 'l':
lstats=1;
complain(query!=0);
break;
diff --git a/myisam/ft_nlq_search.c b/myisam/ft_nlq_search.c
index f9c276de32f..b1d7d67ae79 100644
--- a/myisam/ft_nlq_search.c
+++ b/myisam/ft_nlq_search.c
@@ -42,8 +42,6 @@ typedef struct st_all_in_one
uint keynr;
CHARSET_INFO *charset;
uchar *keybuff;
- MI_KEYDEF *keyinfo;
- my_off_t key_root;
TREE dtree;
} ALL_IN_ONE;
@@ -66,13 +64,14 @@ static int FT_SUPERDOC_cmp(void* cmp_arg __attribute__((unused)),
static int walk_and_match(FT_WORD *word, uint32 count, ALL_IN_ONE *aio)
{
+ int subkeys;
uint keylen, r, doc_cnt;
-#ifdef EVAL_RUN
- uint cnt;
- double sum, sum2, suml;
-#endif /* EVAL_RUN */
FT_SUPERDOC sdoc, *sptr;
TREE_ELEMENT *selem;
+ MI_INFO *info=aio->info;
+ uchar *keybuff=aio->keybuff;
+ MI_KEYDEF *keyinfo=info->s->keyinfo+aio->keynr;
+ my_off_t key_root=info->s->state.key_root[aio->keynr];
#if HA_FT_WTYPE == HA_KEYTYPE_FLOAT
float tmp_weight;
#else
@@ -83,44 +82,45 @@ static int walk_and_match(FT_WORD *word, uint32 count, ALL_IN_ONE *aio)
word->weight=LWS_FOR_QUERY;
- keylen=_ft_make_key(aio->info,aio->keynr,(char*) aio->keybuff,word,0);
-#ifdef EVAL_RUN
- keylen-=1+HA_FT_WLEN;
-#else /* EVAL_RUN */
+ keylen=_ft_make_key(info,aio->keynr,(char*) keybuff,word,0);
keylen-=HA_FT_WLEN;
-#endif /* EVAL_RUN */
-
-#ifdef EVAL_RUN
- sum=sum2=suml=
-#endif /* EVAL_RUN */
doc_cnt=0;
- r=_mi_search(aio->info, aio->keyinfo, aio->keybuff, keylen,
- SEARCH_FIND | SEARCH_PREFIX, aio->key_root);
+ r=_mi_search(info, keyinfo, keybuff, keylen, SEARCH_FIND, key_root);
+ info->update|= HA_STATE_AKTIV; /* for _mi_test_if_changed() */
while (!r)
{
- if (mi_compare_text(aio->charset,
- aio->info->lastkey,keylen,
- aio->keybuff,keylen,0))
+
+ if (keylen &&
+ mi_compare_text(aio->charset,info->lastkey,keylen, keybuff,keylen,0))
break;
+ subkeys=ft_sintXkorr(info->lastkey+keylen);
+ if (subkeys<0)
+ {
+ if (doc_cnt)
+ DBUG_RETURN(1); /* index is corrupted */
+ /*
+ TODO here: unsafe optimization, should this word
+ be skipped (based on subkeys) ?
+ */
+ keybuff+=keylen;
+ keyinfo=& info->s->ft2_keyinfo;
+ key_root=info->lastpos;
+ keylen=0;
+ r=_mi_search_first(info, keyinfo, key_root);
+ continue;
+ }
#if HA_FT_WTYPE == HA_KEYTYPE_FLOAT
-#ifdef EVAL_RUN
- mi_float4get(tmp_weight,aio->info->lastkey+keylen+1);
-#else /* EVAL_RUN */
- mi_float4get(tmp_weight,aio->info->lastkey+keylen);
-#endif /* EVAL_RUN */
+ tmp_weight=*(float*)&subkeys;
#else
#error
#endif
- if(tmp_weight==0) DBUG_RETURN(doc_cnt); /* stopword, doc_cnt should be 0 */
+ if (tmp_weight==0)
+ DBUG_RETURN(doc_cnt); /* stopword, doc_cnt should be 0 */
-#ifdef EVAL_RUN
- cnt=*(byte *)(aio->info->lastkey+keylen);
-#endif /* EVAL_RUN */
-
- sdoc.doc.dpos=aio->info->lastpos;
+ sdoc.doc.dpos=info->lastpos;
/* saving document matched into dtree */
if (!(selem=tree_insert(&aio->dtree, &sdoc, 0, aio->dtree.custom_arg)))
@@ -137,20 +137,13 @@ static int walk_and_match(FT_WORD *word, uint32 count, ALL_IN_ONE *aio)
sptr->tmp_weight=tmp_weight;
doc_cnt++;
-#ifdef EVAL_RUN
- sum +=cnt;
- sum2+=cnt*cnt;
- suml+=cnt*log(cnt);
-#endif /* EVAL_RUN */
-
- if (_mi_test_if_changed(aio->info) == 0)
- r=_mi_search_next(aio->info, aio->keyinfo, aio->info->lastkey,
- aio->info->lastkey_length, SEARCH_BIGGER,
- aio->key_root);
+
+ if (_mi_test_if_changed(info) == 0)
+ r=_mi_search_next(info, keyinfo, info->lastkey, info->lastkey_length,
+ SEARCH_BIGGER, key_root);
else
- r=_mi_search(aio->info, aio->keyinfo, aio->info->lastkey,
- aio->info->lastkey_length, SEARCH_BIGGER,
- aio->key_root);
+ r=_mi_search(info, keyinfo, info->lastkey, info->lastkey_length,
+ SEARCH_BIGGER, key_root);
}
if (doc_cnt)
{
@@ -200,10 +193,8 @@ FT_INFO *ft_init_nlq_search(MI_INFO *info, uint keynr, byte *query,
aio.info=info;
aio.keynr=keynr;
- aio.keyinfo=info->s->keyinfo+keynr;
- aio.charset=aio.keyinfo->seg->charset;
+ aio.charset=info->s->keyinfo[keynr].seg->charset;
aio.keybuff=info->lastkey+info->s->base.max_key_length;
- aio.key_root=info->s->state.key_root[keynr];
bzero(&allocated_wtree,sizeof(allocated_wtree));
diff --git a/myisam/ft_parser.c b/myisam/ft_parser.c
index 358706a8ffa..14c67333734 100644
--- a/myisam/ft_parser.c
+++ b/myisam/ft_parser.c
@@ -18,21 +18,10 @@
#include "ftdefs.h"
-#ifdef EVAL_RUN
-#ifdef PIVOT_STAT
-ulong collstat=0;
-#endif
-#endif /* EVAL_RUN */
-
typedef struct st_ft_docstat {
FT_WORD *list;
uint uniq;
double sum;
-#ifdef EVAL_RUN
- uint words, totlen;
- double max, nsum, nsum2;
-#endif /* EVAL_RUN */
-
} FT_DOCSTAT;
static int FT_WORD_cmp(CHARSET_INFO* cs, FT_WORD *w1, FT_WORD *w2)
@@ -44,15 +33,7 @@ static int FT_WORD_cmp(CHARSET_INFO* cs, FT_WORD *w1, FT_WORD *w2)
static int walk_and_copy(FT_WORD *word,uint32 count,FT_DOCSTAT *docstat)
{
word->weight=LWS_IN_USE;
-
-#ifdef EVAL_RUN
- word->cnt= (uchar) count;
- if(docstat->max < word->weight) docstat->max=word->weight;
- docstat->words+=count;
- docstat->totlen+=word->len;
-#endif /* EVAL_RUN */
docstat->sum+=word->weight;
-
memcpy_fixed((docstat->list)++,word,sizeof(FT_WORD));
return 0;
}
@@ -70,9 +51,6 @@ FT_WORD * ft_linearize(TREE *wtree)
{
docstat.list=wlist;
docstat.uniq=wtree->elements_in_tree;
-#ifdef EVAL_RUN
- docstat.nsum=docstat.nsum2=docstat.max=docstat.words=docstat.totlen=
-#endif /* EVAL_RUN */
docstat.sum=0;
tree_walk(wtree,(tree_walk_action)&walk_and_copy,&docstat,left_root_right);
}
@@ -85,18 +63,8 @@ FT_WORD * ft_linearize(TREE *wtree)
for (p=wlist;p->pos;p++)
{
p->weight=PRENORM_IN_USE;
-#ifdef EVAL_RUN
- docstat.nsum+=p->weight;
- docstat.nsum2+=p->weight*p->weight;
-#endif /* EVAL_RUN */
}
-#ifdef EVAL_RUN
-#ifdef PIVOT_STAT
- collstat+=PIVOT_STAT;
-#endif
-#endif /* EVAL_RUN */
-
for (p=wlist;p->pos;p++)
{
p->weight/=NORM_IN_USE;
diff --git a/myisam/ft_static.c b/myisam/ft_static.c
index 5c2629e9e9e..b714a7f0c35 100644
--- a/myisam/ft_static.c
+++ b/myisam/ft_static.c
@@ -26,25 +26,21 @@ const char *ft_boolean_syntax="+ -><()~*:\"\"&|";
const HA_KEYSEG ft_keysegs[FT_SEGS]={
{
HA_KEYTYPE_VARTEXT, /* type */
- 7, /* language (will be overwritten) */
+ 63, /* language (will be overwritten) */
0, 0, 0, /* null_bit, bit_start, bit_end */
HA_VAR_LENGTH | HA_PACK_KEY, /* flag */
HA_FT_MAXLEN, /* length */
-#ifdef EVAL_RUN
- HA_FT_WLEN+1, /* start */
-#else /* EVAL_RUN */
HA_FT_WLEN, /* start */
-#endif /* EVAL_RUN */
0, /* null_pos */
NULL /* charset */
},
-#ifdef EVAL_RUN
{
- HA_KEYTYPE_INT8, 7, 0, 0, 0, 0, 1, HA_FT_WLEN, 0, NULL
- },
-#endif /* EVAL_RUN */
- {
- HA_FT_WTYPE, 7, 0, 0, 0, HA_NO_SORT, HA_FT_WLEN, 0, 0, NULL
+/*
+ Note, this (and the last HA_KEYTYPE_END) segment should NOT
+ be packed in any way, otherwise w_search() won't be able to
+ update key entry 'in vivo'
+*/
+ HA_FT_WTYPE, 63, 0, 0, 0, HA_NO_SORT, HA_FT_WLEN, 0, 0, NULL
}
};
diff --git a/myisam/ft_update.c b/myisam/ft_update.c
index 41876028104..4b6d1c4bffc 100644
--- a/myisam/ft_update.c
+++ b/myisam/ft_update.c
@@ -31,7 +31,7 @@
void _mi_ft_segiterator_init(MI_INFO *info, uint keynr, const byte *record,
FT_SEG_ITERATOR *ftsi)
{
- ftsi->num=info->s->keyinfo[keynr].keysegs-FT_SEGS;
+ ftsi->num=info->s->keyinfo[keynr].keysegs;
ftsi->seg=info->s->keyinfo[keynr].seg;
ftsi->rec=record;
}
@@ -113,7 +113,7 @@ FT_WORD * _mi_ft_parserecord(MI_INFO *info, uint keynr,
if (_mi_ft_parse(&ptree, info, keynr, record))
return NULL;
- return ft_linearize(/*info, keynr, keybuf, */ &ptree);
+ return ft_linearize(&ptree);
}
static int _mi_ft_store(MI_INFO *info, uint keynr, byte *keybuf,
@@ -267,13 +267,7 @@ uint _ft_make_key(MI_INFO *info, uint keynr, byte *keybuf, FT_WORD *wptr,
#error
#endif
-#ifdef EVAL_RUN
- *(buf+HA_FT_WLEN)=wptr->cnt;
- int2store(buf+HA_FT_WLEN+1,wptr->len);
- memcpy(buf+HA_FT_WLEN+3,wptr->pos,wptr->len);
-#else /* EVAL_RUN */
int2store(buf+HA_FT_WLEN,wptr->len);
memcpy(buf+HA_FT_WLEN+2,wptr->pos,wptr->len);
-#endif /* EVAL_RUN */
return _mi_make_key(info,keynr,(uchar*) keybuf,buf,filepos);
}
diff --git a/myisam/ftdefs.h b/myisam/ftdefs.h
index 592b60a0133..88d7e79937b 100644
--- a/myisam/ftdefs.h
+++ b/myisam/ftdefs.h
@@ -60,16 +60,6 @@
#define NORM_SUM (docstat.nsum)
#define NORM_COS (sqrt(docstat.nsum2))
-#ifdef EVAL_RUN
-/*
-extern ulong collstat;
-#define PIVOT_STAT (docstat.uniq)
-#define PIVOT_SLOPE (0.69)
-#define PIVOT_PIVOT ((double)collstat/(info->state->records+1))
-#define NORM_PIVOT ((1-PIVOT_SLOPE)*PIVOT_PIVOT+PIVOT_SLOPE*docstat.uniq)
-*/
-#endif /* EVAL_RUN */
-
#define PIVOT_VAL (0.0115)
#define NORM_PIVOT (1+PIVOT_VAL*docstat.uniq)
/*---------------------------------------------------------------*/
@@ -102,9 +92,6 @@ typedef struct st_ft_word {
byte * pos;
uint len;
double weight;
-#ifdef EVAL_RUN
- byte cnt;
-#endif /* EVAL_RUN */
} FT_WORD;
typedef struct st_ftb_param {
diff --git a/myisam/fulltext.h b/myisam/fulltext.h
index 39801c1c87e..ec267eb3e86 100644
--- a/myisam/fulltext.h
+++ b/myisam/fulltext.h
@@ -21,19 +21,16 @@
#include "myisamdef.h"
#include "ft_global.h"
-/* shoudn't be def'ed when linking with mysql */
-#undef EVAL_RUN
-
#define HA_FT_WTYPE HA_KEYTYPE_FLOAT
#define HA_FT_WLEN 4
-#ifdef EVAL_RUN
-#define FT_SEGS 3
-#else /* EVAL_RUN */
#define FT_SEGS 2
-#endif /* EVAL_RUN */
+
+#define ft_sintXkorr(A) mi_sint4korr(A)
+#define ft_intXstore(T,A) mi_int4store(T,A)
extern const HA_KEYSEG ft_keysegs[FT_SEGS];
int _mi_ft_cmp(MI_INFO *, uint, const byte *, const byte *);
int _mi_ft_add(MI_INFO *, uint, byte *, const byte *, my_off_t);
int _mi_ft_del(MI_INFO *, uint, byte *, const byte *, my_off_t);
+
diff --git a/myisam/mi_check.c b/myisam/mi_check.c
index 34222a5703b..1502e719b7d 100644
--- a/myisam/mi_check.c
+++ b/myisam/mi_check.c
@@ -49,10 +49,11 @@ static int sort_key_read(MI_SORT_PARAM *sort_param,void *key);
static int sort_ft_key_read(MI_SORT_PARAM *sort_param,void *key);
static int sort_get_next_record(MI_SORT_PARAM *sort_param);
static int sort_key_cmp(MI_SORT_PARAM *sort_param, const void *a,const void *b);
+static int sort_ft_key_write(MI_SORT_PARAM *sort_param, const void *a);
static int sort_key_write(MI_SORT_PARAM *sort_param, const void *a);
static my_off_t get_record_for_key(MI_INFO *info,MI_KEYDEF *keyinfo,
uchar *key);
-static int sort_insert_key(MI_SORT_PARAM *sort_param,
+static int sort_insert_key(MI_SORT_PARAM *sort_param,
reg1 SORT_KEY_BLOCKS *key_block,
uchar *key, my_off_t prev_block);
static int sort_delete_record(MI_SORT_PARAM *sort_param);
@@ -1890,7 +1891,6 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
((param->testflag & T_CREATE_MISSING_KEYS) ? info->state->records :
(ha_rows) (sort_info.filelength/length+1));
sort_param.key_cmp=sort_key_cmp;
- sort_param.key_write=sort_key_write;
sort_param.lock_in_memory=lock_memory;
sort_param.tmpdir=param->tmpdir;
sort_param.sort_info=&sort_info;
@@ -1943,10 +1943,14 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
(ha_rows) (sort_info.filelength/ft_max_word_len_for_sort+1);
sort_param.key_read=sort_ft_key_read;
+ sort_param.key_write=sort_ft_key_write;
sort_param.key_length+=ft_max_word_len_for_sort-HA_FT_MAXLEN;
}
else
+ {
sort_param.key_read=sort_key_read;
+ sort_param.key_write=sort_key_write;
+ }
if (_create_index_by_sort(&sort_param,
(my_bool) (!(param->testflag & T_VERBOSE)),
@@ -1992,9 +1996,6 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
else
info->state->data_file_length=sort_param.max_pos;
- /*if (flush_pending_blocks(param))
- goto err;*/
-
param->read_cache.file=info->dfile; /* re-init read cache */
reinit_io_cache(&param->read_cache,READ_CACHE,share->pack.header_length,
1,1);
@@ -2093,6 +2094,7 @@ err:
MYF(MY_ALLOW_ZERO_PTR));
my_free(sort_param.record,MYF(MY_ALLOW_ZERO_PTR));
my_free((gptr) sort_info.key_block,MYF(MY_ALLOW_ZERO_PTR));
+ my_free((gptr) sort_info.ft_buf, MYF(MY_ALLOW_ZERO_PTR));
my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR));
VOID(end_io_cache(&param->read_cache));
info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
@@ -2108,7 +2110,7 @@ err:
Threaded repair of table using sorting
SYNOPSIS
- mi_repair_by_sort_r()
+ mi_repair_by_sort_parallel()
param Repair parameters
info MyISAM handler to repair
name Name of table (for warnings)
@@ -2280,10 +2282,17 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info,
}
if ((!(param->testflag & T_SILENT)))
printf ("- Fixing index %d\n",key+1);
- sort_param[i].key_read= ((sort_param[i].keyinfo->flag & HA_FULLTEXT) ?
- sort_ft_key_read : sort_key_read);
+ if (sort_param[i].keyinfo->flag & HA_FULLTEXT)
+ {
+ sort_param[i].key_read=sort_ft_key_read;
+ sort_param[i].key_write=sort_ft_key_write;
+ }
+ else
+ {
+ sort_param[i].key_read=sort_key_read;
+ sort_param[i].key_write=sort_key_write;
+ }
sort_param[i].key_cmp=sort_key_cmp;
- sort_param[i].key_write=sort_key_write;
sort_param[i].lock_in_memory=lock_memory;
sort_param[i].tmpdir=param->tmpdir;
sort_param[i].sort_info=&sort_info;
@@ -2476,6 +2485,7 @@ err:
pthread_cond_destroy (&sort_info.cond);
pthread_mutex_destroy(&sort_info.mutex);
+ my_free((gptr) sort_info.ft_buf, MYF(MY_ALLOW_ZERO_PTR));
my_free((gptr) sort_info.key_block,MYF(MY_ALLOW_ZERO_PTR));
my_free((gptr) sort_param,MYF(MY_ALLOW_ZERO_PTR));
my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR));
@@ -3110,6 +3120,137 @@ static int sort_key_write(MI_SORT_PARAM *sort_param, const void *a)
(uchar*) a, HA_OFFSET_ERROR));
} /* sort_key_write */
+int sort_ft_buf_flush(MI_SORT_PARAM *sort_param)
+{
+ SORT_INFO *sort_info=sort_param->sort_info;
+ SORT_KEY_BLOCKS *key_block=sort_info->key_block;
+ MYISAM_SHARE *share=sort_info->info->s;
+ uint val_off, val_len, error;
+ SORT_FT_BUF *ft_buf=sort_info->ft_buf;
+ uchar *from, *to;
+
+ val_len=share->ft2_keyinfo.keylength;
+ get_key_full_length_rdonly(val_off, ft_buf->lastkey);
+ to=ft_buf->lastkey+val_off;
+
+ if (ft_buf->buf)
+ { /* flushing first-level tree */
+ error=sort_insert_key(sort_param,key_block,ft_buf->lastkey,HA_OFFSET_ERROR);
+ for (from=to+val_len;
+ !error && from < ft_buf->buf;
+ from+= val_len)
+ {
+ memcpy(to, from, val_len);
+ error=sort_insert_key(sort_param,key_block,ft_buf->lastkey,HA_OFFSET_ERROR);
+ }
+ return error;
+ }
+ /* flushing second-level tree keyblocks */
+ error=flush_pending_blocks(sort_param);
+ /* updating lastkey with second-level tree info */
+ ft_intXstore(ft_buf->lastkey+val_off, -ft_buf->count);
+ _mi_dpointer(sort_info->info, ft_buf->lastkey+val_off+HA_FT_WLEN,
+ share->state.key_root[sort_param->key]);
+ /* restoring first level tree data in sort_info/sort_param */
+ sort_info->key_block=sort_info->key_block_end- sort_info->param->sort_key_blocks;
+ sort_param->keyinfo=share->keyinfo+sort_param->key;
+ share->state.key_root[sort_param->key]=HA_OFFSET_ERROR;
+ /* writing lastkey in first-level tree */
+ return error ? error :
+ sort_insert_key(sort_param,sort_info->key_block,
+ ft_buf->lastkey,HA_OFFSET_ERROR);
+}
+
+static int sort_ft_key_write(MI_SORT_PARAM *sort_param, const void *a)
+{
+ uint a_len, val_off, val_len, error;
+ uchar *p;
+ SORT_INFO *sort_info=sort_param->sort_info;
+ SORT_FT_BUF *ft_buf=sort_info->ft_buf;
+ SORT_KEY_BLOCKS *key_block=sort_info->key_block;
+
+ val_len=HA_FT_WLEN+sort_info->info->s->base.rec_reflength;
+ get_key_full_length_rdonly(a_len, (uchar *)a);
+
+ if (!ft_buf)
+ {
+ /*
+ use two-level tree only if key_reflength fits in rec_reflength place
+ and row format is NOT static - for _mi_dpointer not to garble offsets
+ */
+ if ((sort_info->info->s->base.key_reflength <=
+ sort_info->info->s->base.rec_reflength) &&
+ (sort_info->info->s->options &
+ (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)))
+ ft_buf=(SORT_FT_BUF *)my_malloc(sort_param->keyinfo->block_length +
+ sizeof(SORT_FT_BUF), MYF(MY_WME));
+
+ if (!ft_buf)
+ {
+ sort_param->key_write=sort_key_write;
+ return sort_key_write(sort_param, a);
+ }
+ sort_info->ft_buf=ft_buf;
+ goto word_init_ft_buf; /* no need to duplicate the code */
+ }
+ get_key_full_length_rdonly(val_off, ft_buf->lastkey);
+
+ if (val_off == a_len &&
+ mi_compare_text(sort_param->keyinfo->seg->charset,
+ ((uchar *)a)+1,a_len-1,
+ ft_buf->lastkey+1,val_off-1, 0)==0)
+ {
+ if (!ft_buf->buf) /* store in second-level tree */
+ {
+ ft_buf->count++;
+ return sort_insert_key(sort_param,key_block,
+ ((uchar *)a)+val_off, HA_OFFSET_ERROR);
+ }
+
+ /* storing the key in the buffer. */
+ memcpy (ft_buf->buf, a+val_off, val_len);
+ ft_buf->buf+=val_len;
+ if (ft_buf->buf < ft_buf->end)
+ return 0;
+
+ /* converting to two-level tree */
+ p=ft_buf->lastkey+val_off;
+
+ while (key_block->inited)
+ key_block++;
+ sort_info->key_block=key_block;
+ sort_param->keyinfo=& sort_info->info->s->ft2_keyinfo;
+ ft_buf->count=(ft_buf->buf - p)/val_len;
+
+ /* flushing buffer to second-level tree */
+ for (error=0; !error && p < ft_buf->buf; p+= val_len)
+ error=sort_insert_key(sort_param,key_block,p,HA_OFFSET_ERROR);
+ ft_buf->buf=0;
+ return error;
+ }
+ else
+ {
+ /* flushing buffer */
+ if ((error=sort_ft_buf_flush(sort_param)))
+ return error;
+
+word_init_ft_buf:
+ a_len+=val_len;
+ memcpy(ft_buf->lastkey, a, a_len);
+ ft_buf->buf=ft_buf->lastkey+a_len;
+ ft_buf->end=ft_buf->lastkey+ (sort_param->keyinfo->block_length-32);
+ /* 32 is just a safety margin here
+ (at least max(val_len, sizeof(nod_flag)) should be there).
+ May be better performance could be achieved if we'd put
+ (sort_info->keyinfo->block_length-32)/XXX
+ instead.
+ TODO: benchmark the best value for XXX.
+ */
+
+ return 0;
+ }
+ return -1; /* impossible */
+} /* sort_ft_key_write */
/* get pointer to record from a key */
@@ -3269,7 +3410,7 @@ int flush_pending_blocks(MI_SORT_PARAM *sort_param)
my_off_t filepos,key_file_length;
SORT_KEY_BLOCKS *key_block;
SORT_INFO *sort_info= sort_param->sort_info;
- MI_CHECK *param=sort_info->param;
+ myf myf_rw=sort_info->param->myf_rw;
MI_INFO *info=sort_info->info;
MI_KEYDEF *keyinfo=sort_param->keyinfo;
DBUG_ENTER("flush_pending_blocks");
@@ -3294,7 +3435,7 @@ int flush_pending_blocks(MI_SORT_PARAM *sort_param)
DBUG_RETURN(1);
}
else if (my_pwrite(info->s->kfile,(byte*) key_block->buff,
- (uint) keyinfo->block_length,filepos, param->myf_rw))
+ (uint) keyinfo->block_length,filepos, myf_rw))
DBUG_RETURN(1);
DBUG_DUMP("buff",(byte*) key_block->buff,length);
nod_flag=1;
diff --git a/myisam/mi_create.c b/myisam/mi_create.c
index 8087d17904a..c270e83a328 100644
--- a/myisam/mi_create.c
+++ b/myisam/mi_create.c
@@ -44,7 +44,7 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs,
uint fields,length,max_key_length,packed,pointer,
key_length,info_length,key_segs,options,min_key_length_skipp,
base_pos,varchar_count,long_varchar_count,varchar_length,
- max_key_block_length,unique_key_parts,offset;
+ max_key_block_length,unique_key_parts,fulltext_keys,offset;
ulong reclength, real_reclength,min_pack_length;
char filename[FN_REFLEN],linkname[FN_REFLEN], *linkname_ptr;
ulong pack_reclength;
@@ -223,6 +223,7 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs,
reclength+=long_varchar_count; /* We need space for this! */
max_key_length=0; tot_length=0 ; key_segs=0;
+ fulltext_keys=0;
max_key_block_length=0;
share.state.rec_per_key_part=rec_per_key_part;
share.state.key_root=key_root;
@@ -242,14 +243,14 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs,
if (keydef->flag & HA_SPATIAL)
{
/* BAR TODO to support 3D and more dimensions in the future */
- uint sp_segs=SPDIMS*2;
+ uint sp_segs=SPDIMS*2;
keydef->flag=HA_SPATIAL;
if (flags & HA_DONT_TOUCH_DATA)
{
- /*
+ /*
called by myisamchk - i.e. table structure was taken from
- MYI file and SPATIAL key *do has* additional sp_segs keysegs.
+ MYI file and SPATIAL key *does have* additional sp_segs keysegs.
We'd better delete them now
*/
keydef->keysegs-=sp_segs;
@@ -271,20 +272,11 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs,
min_key_length_skipp+=SPLEN*2*SPDIMS;
}
else
- if (keydef->flag & HA_FULLTEXT) /* SerG */
+ if (keydef->flag & HA_FULLTEXT)
{
keydef->flag=HA_FULLTEXT | HA_PACK_KEY | HA_VAR_LENGTH_KEY;
options|=HA_OPTION_PACK_KEYS; /* Using packed keys */
- if (flags & HA_DONT_TOUCH_DATA)
- {
- /* called by myisamchk - i.e. table structure was taken from
- MYI file and FULLTEXT key *do has* additional FT_SEGS keysegs.
- We'd better delete them now
- */
- keydef->keysegs-=FT_SEGS;
- }
-
for (j=0, keyseg=keydef->seg ; (int) j < keydef->keysegs ;
j++, keyseg++)
{
@@ -295,19 +287,11 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs,
goto err;
}
}
- keydef->keysegs+=FT_SEGS;
+ fulltext_keys++;
key_length+= HA_FT_MAXLEN+HA_FT_WLEN;
-#ifdef EVAL_RUN
- key_length++;
-#endif
-
length++; /* At least one length byte */
min_key_length_skipp+=HA_FT_MAXLEN;
-#if HA_FT_MAXLEN >= 255
- min_key_length_skipp+=2; /* prefix may be 3 bytes */
- length+=2;
-#endif
}
else
{
@@ -473,8 +457,9 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs,
mi_get_pointer_length((tot_length + max_key_block_length * keys *
MI_INDEX_BLOCK_MARGIN) / MI_MIN_KEY_BLOCK_LENGTH,
3);
- share.base.keys= share.state.header.keys = keys;
+ share.base.keys= share.state.header.keys= keys;
share.state.header.uniques= uniques;
+ share.state.header.fulltext_keys= fulltext_keys;
mi_int2store(share.state.header.key_parts,key_segs);
mi_int2store(share.state.header.unique_key_parts,unique_key_parts);
@@ -566,7 +551,7 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs,
linkname_ptr=0;
create_flag=MY_DELETE_OLD;
}
- if ((dfile=
+ if ((dfile=
my_create_with_symlink(linkname_ptr, filename,
0,O_RDWR | O_TRUNC,
MYF(MY_WME | create_flag))) < 0)
@@ -590,21 +575,13 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs,
/* Write key and keyseg definitions */
for (i=0 ; i < share.base.keys - uniques; i++)
{
- uint ft_segs=(keydefs[i].flag & HA_FULLTEXT) ? FT_SEGS : 0;
uint sp_segs=(keydefs[i].flag & HA_SPATIAL) ? 2*SPDIMS : 0;
if (mi_keydef_write(file, &keydefs[i]))
goto err;
- for (j=0 ; j < keydefs[i].keysegs-ft_segs-sp_segs ; j++)
+ for (j=0 ; j < keydefs[i].keysegs-sp_segs ; j++)
if (mi_keyseg_write(file, &keydefs[i].seg[j]))
goto err;
- for (j=0 ; j < ft_segs ; j++)
- {
- HA_KEYSEG seg=ft_keysegs[j];
- seg.language= keydefs[i].seg[0].language;
- if (mi_keyseg_write(file, &seg))
- goto err;
- }
for (j=0 ; j < sp_segs ; j++)
{
HA_KEYSEG sseg;
diff --git a/myisam/mi_delete.c b/myisam/mi_delete.c
index ff723303228..2b19139e668 100644
--- a/myisam/mi_delete.c
+++ b/myisam/mi_delete.c
@@ -23,13 +23,13 @@
#include <errno.h>
#endif
-static int d_search(MI_INFO *info,MI_KEYDEF *keyinfo,uchar *key,
- uint key_length, my_off_t page, uchar *anc_buff);
+static int d_search(MI_INFO *info,MI_KEYDEF *keyinfo,uint comp_flag,
+ uchar *key,uint key_length,my_off_t page,uchar *anc_buff);
static int del(MI_INFO *info,MI_KEYDEF *keyinfo,uchar *key,uchar *anc_buff,
my_off_t leaf_page,uchar *leaf_buff,uchar *keypos,
my_off_t next_block,uchar *ret_key);
static int underflow(MI_INFO *info,MI_KEYDEF *keyinfo,uchar *anc_buff,
- my_off_t leaf_page, uchar *leaf_buff,uchar *keypos);
+ my_off_t leaf_page,uchar *leaf_buff,uchar *keypos);
static uint remove_key(MI_KEYDEF *keyinfo,uint nod_flag,uchar *keypos,
uchar *lastkey,uchar *page_end,
my_off_t *next_block);
@@ -73,7 +73,6 @@ int mi_delete(MI_INFO *info,const byte *record)
if (((ulonglong) 1 << i) & info->s->state.key_map)
{
info->s->keyinfo[i].version++;
- /* The following code block is for text searching by SerG */
if (info->s->keyinfo[i].flag & HA_FULLTEXT )
{
if (_mi_ft_del(info,i,(char*) old_key,record,info->lastpos))
@@ -82,7 +81,7 @@ int mi_delete(MI_INFO *info,const byte *record)
else
{
if (info->s->keyinfo[i].ck_delete(info,i,old_key,
- _mi_make_key(info,i,old_key,record,info->lastpos)))
+ _mi_make_key(info,i,old_key,record,info->lastpos)))
goto err;
}
}
@@ -129,18 +128,23 @@ err:
int _mi_ck_delete(register MI_INFO *info, uint keynr, uchar *key,
uint key_length)
{
+ return _mi_ck_real_delete(info, info->s->keyinfo+keynr, key, key_length,
+ &info->s->state.key_root[keynr]);
+} /* _mi_ck_delete */
+
+int _mi_ck_real_delete(register MI_INFO *info, MI_KEYDEF *keyinfo,
+ uchar *key, uint key_length, my_off_t *root)
+{
int error;
uint nod_flag;
my_off_t old_root;
uchar *root_buff;
- MI_KEYDEF *keyinfo;
- DBUG_ENTER("_mi_ck_delete");
+ DBUG_ENTER("_mi_ck_real_delete");
- if ((old_root=info->s->state.key_root[keynr]) == HA_OFFSET_ERROR)
+ if ((old_root=*root) == HA_OFFSET_ERROR)
{
DBUG_RETURN(my_errno=HA_ERR_CRASHED);
}
- keyinfo=info->s->keyinfo+keynr;
if (!(root_buff= (uchar*) my_alloca((uint) keyinfo->block_length+
MI_MAX_KEY_BUFF*2)))
{
@@ -153,12 +157,15 @@ int _mi_ck_delete(register MI_INFO *info, uint keynr, uchar *key,
error= -1;
goto err;
}
- if ((error=d_search(info,keyinfo,key,key_length,old_root,root_buff)) >0)
+ if ((error=d_search(info,keyinfo,
+ (keyinfo->flag & HA_FULLTEXT ? SEARCH_FIND
+ : SEARCH_SAME),
+ key,key_length,old_root,root_buff)) >0)
{
if (error == 2)
{
DBUG_PRINT("test",("Enlarging of root when deleting"));
- error=_mi_enlarge_root(info,keynr,key);
+ error=_mi_enlarge_root(info,keyinfo,key,root);
}
else /* error == 1 */
{
@@ -166,10 +173,9 @@ int _mi_ck_delete(register MI_INFO *info, uint keynr, uchar *key,
{
error=0;
if (nod_flag)
- info->s->state.key_root[keynr]=_mi_kpos(nod_flag,
- root_buff+2+nod_flag);
+ *root=_mi_kpos(nod_flag,root_buff+2+nod_flag);
else
- info->s->state.key_root[keynr]= HA_OFFSET_ERROR;
+ *root=HA_OFFSET_ERROR;
if (_mi_dispose(info,keyinfo,old_root))
error= -1;
}
@@ -180,7 +186,7 @@ int _mi_ck_delete(register MI_INFO *info, uint keynr, uchar *key,
err:
my_afree((gptr) root_buff);
DBUG_RETURN(error);
-} /* _mi_ck_delete */
+} /* _mi_ck_real_delete */
/*
@@ -192,11 +198,11 @@ err:
*/
static int d_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
- uchar *key, uint key_length, my_off_t page,
- uchar *anc_buff)
+ uint comp_flag, uchar *key, uint key_length,
+ my_off_t page, uchar *anc_buff)
{
int flag,ret_value,save_flag;
- uint length,nod_flag;
+ uint length,nod_flag,search_key_length;
my_bool last_key;
uchar *leaf_buff,*keypos;
my_off_t leaf_page,next_block;
@@ -204,9 +210,9 @@ static int d_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
DBUG_ENTER("d_search");
DBUG_DUMP("page",(byte*) anc_buff,mi_getint(anc_buff));
- flag=(*keyinfo->bin_search)(info,keyinfo,anc_buff,key, USE_WHOLE_KEY,
- SEARCH_SAME,
- &keypos, lastkey, &last_key);
+ search_key_length= (comp_flag & SEARCH_FIND) ? key_length : USE_WHOLE_KEY;
+ flag=(*keyinfo->bin_search)(info,keyinfo,anc_buff,key, search_key_length,
+ comp_flag, &keypos, lastkey, &last_key);
if (flag == MI_FOUND_WRONG_KEY)
{
DBUG_PRINT("error",("Found wrong key"));
@@ -214,6 +220,52 @@ static int d_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
}
nod_flag=mi_test_if_nod(anc_buff);
+ if (!flag && keyinfo->flag & HA_FULLTEXT)
+ {
+ uint off;
+ int subkeys;
+
+ get_key_full_length_rdonly(off, lastkey);
+ subkeys=ft_sintXkorr(lastkey+off);
+ comp_flag=SEARCH_SAME;
+ if (subkeys >= 0)
+ {
+ /* normal word, one-level tree structure */
+ flag=(*keyinfo->bin_search)(info,keyinfo,anc_buff,key,USE_WHOLE_KEY,
+ comp_flag, &keypos, lastkey, &last_key);
+ /* fall through to normal delete */
+ }
+ else
+ {
+ /* popular word. two-level tree. going down */
+ uint tmp_key_length;
+ my_off_t root;
+ uchar *kpos=keypos;
+
+ tmp_key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&kpos,lastkey);
+ root=_mi_dpos(info,nod_flag,kpos);
+ if (subkeys == -1)
+ {
+ /* the last entry in sub-tree */
+ _mi_dispose(info, keyinfo, root);
+ /* fall through to normal delete */
+ }
+ else
+ {
+ keyinfo=&info->s->ft2_keyinfo;
+ kpos-=keyinfo->keylength; /* we'll modify key entry 'in vivo' */
+ key+=off;
+ ret_value=_mi_ck_real_delete(info, &info->s->ft2_keyinfo,
+ key, HA_FT_WLEN, &root);
+ _mi_dpointer(info, kpos+HA_FT_WLEN, root);
+ subkeys++;
+ ft_intXstore(kpos, subkeys);
+ if (!ret_value)
+ ret_value=_mi_write_keypage(info,keyinfo,page,anc_buff);
+ DBUG_RETURN(ret_value);
+ }
+ }
+ }
leaf_buff=0;
LINT_INIT(leaf_page);
if (nod_flag)
@@ -239,7 +291,8 @@ static int d_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
goto err;
}
save_flag=0;
- ret_value=d_search(info,keyinfo,key,key_length,leaf_page,leaf_buff);
+ ret_value=d_search(info,keyinfo,comp_flag,key,key_length,
+ leaf_page,leaf_buff);
}
else
{ /* Found key */
diff --git a/myisam/mi_open.c b/myisam/mi_open.c
index 8f0da612c3a..fac53bf77e9 100644
--- a/myisam/mi_open.c
+++ b/myisam/mi_open.c
@@ -69,7 +69,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
{
int lock_error,kfile,open_mode,save_errno,have_rtree=0;
uint i,j,len,errpos,head_length,base_pos,offset,info_length,keys,
- key_parts,unique_key_parts,tmp_length,uniques;
+ key_parts,unique_key_parts,fulltext_keys,uniques;
char name_buff[FN_REFLEN], org_name [FN_REFLEN], index_name[FN_REFLEN],
data_name[FN_REFLEN];
char *disk_cache,*disk_pos;
@@ -126,8 +126,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
HA_OPTION_TEMP_COMPRESS_RECORD | HA_OPTION_CHECKSUM |
HA_OPTION_TMP_TABLE | HA_OPTION_DELAY_KEY_WRITE))
{
- DBUG_PRINT("error",("wrong options: 0x%lx",
- share->options));
+ DBUG_PRINT("error",("wrong options: 0x%lx", share->options));
my_errno=HA_ERR_OLD_FILE;
goto err;
}
@@ -162,11 +161,9 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
len=mi_uint2korr(share->state.header.state_info_length);
keys= (uint) share->state.header.keys;
uniques= (uint) share->state.header.uniques;
+ fulltext_keys= (uint) share->state.header.fulltext_keys;
key_parts= mi_uint2korr(share->state.header.key_parts);
unique_key_parts= mi_uint2korr(share->state.header.unique_key_parts);
- tmp_length=(MI_STATE_INFO_SIZE + keys * MI_STATE_KEY_SIZE +
- key_parts*MI_STATE_KEYSEG_SIZE +
- share->state.header.max_block_size*MI_STATE_KEYBLOCK_SIZE);
if (len != MI_STATE_INFO_SIZE)
{
DBUG_PRINT("warning",
@@ -203,6 +200,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
goto err;
}
+ key_parts+=fulltext_keys*FT_SEGS;
if (share->base.max_key_length > MI_MAX_KEY_BUFF || keys > MI_MAX_KEY ||
key_parts >= MI_MAX_KEY * MI_MAX_KEY_SEG)
{
@@ -211,7 +209,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
goto err;
}
- /* Correct max_file_length based on length of sizeof_t */
+ /* Correct max_file_length based on length of sizeof(off_t) */
max_data_file_length=
(share->options & (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) ?
(((ulonglong) 1 << (share->base.rec_reflength*8))-1) :
@@ -290,12 +288,14 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
for (i=0 ; i < keys ; i++)
{
disk_pos=mi_keydef_read(disk_pos, &share->keyinfo[i]);
+ if (share->keyinfo[i].key_alg == HA_KEY_ALG_RTREE)
+ have_rtree=1;
set_if_smaller(share->blocksize,share->keyinfo[i].block_length);
share->keyinfo[i].seg=pos;
for (j=0 ; j < share->keyinfo[i].keysegs; j++,pos++)
{
disk_pos=mi_keyseg_read(disk_pos, pos);
-
+
if (pos->type == HA_KEYTYPE_TEXT || pos->type == HA_KEYTYPE_VARTEXT)
{
if (!pos->language)
@@ -312,11 +312,41 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
uint sp_segs=SPDIMS*2;
share->keyinfo[i].seg=pos-sp_segs;
share->keyinfo[i].keysegs--;
- } else if (share->keyinfo[i].flag & HA_FULLTEXT)
+ }
+ else if (share->keyinfo[i].flag & HA_FULLTEXT)
{
- share->keyinfo[i].seg=pos-FT_SEGS;
- share->fulltext_index=1;
+ if (!fulltext_keys)
+ { /* 4.0 compatibility code, to be removed in 5.0 */
+ share->keyinfo[i].seg=pos-FT_SEGS;
+ share->keyinfo[i].keysegs-=FT_SEGS;
+ share->state.header.fulltext_keys++;
+ }
+ else
+ {
+ uint j;
+ share->keyinfo[i].seg=pos;
+ for (j=0; j < FT_SEGS; j++)
+ {
+ *pos=ft_keysegs[j];
+ pos[0].language= pos[-1].language;
+ pos[0].charset= pos[-1].charset;
+ pos++;
+ }
+ }
+ if (!share->ft2_keyinfo.seg)
+ {
+ memcpy(& share->ft2_keyinfo, & share->keyinfo[i], sizeof(MI_KEYDEF));
+ share->ft2_keyinfo.keysegs=1;
+ share->ft2_keyinfo.flag=0;
+ share->ft2_keyinfo.keylength=
+ share->ft2_keyinfo.minlength=
+ share->ft2_keyinfo.maxlength=HA_FT_WLEN+share->base.rec_reflength;
+ share->ft2_keyinfo.seg=pos-1;
+ share->ft2_keyinfo.end=pos;
+ setup_key_functions(& share->ft2_keyinfo);
+ }
}
+ setup_key_functions(share->keyinfo+i);
share->keyinfo[i].end=pos;
pos->type=HA_KEYTYPE_END; /* End */
pos->length=share->base.rec_reflength;
@@ -349,12 +379,6 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
pos++;
}
}
- for (i=0 ; i < keys ; i++)
- {
- if (share->keyinfo[i].key_alg == HA_KEY_ALG_RTREE)
- have_rtree=1;
- setup_key_functions(share->keyinfo+i);
- }
for (i=j=offset=0 ; i < share->base.fields ; i++)
{
@@ -712,9 +736,9 @@ static void setup_key_functions(register MI_KEYDEF *keyinfo)
}
-/***************************************************************************
-** Function to save and store the header in the index file (.MSI)
-***************************************************************************/
+/*
+ Function to save and store the header in the index file (.MYI)
+*/
uint mi_state_info_write(File file, MI_STATE_INFO *state, uint pWrite)
{
diff --git a/myisam/mi_rnext.c b/myisam/mi_rnext.c
index daab7c5f085..e1cf916d6d9 100644
--- a/myisam/mi_rnext.c
+++ b/myisam/mi_rnext.c
@@ -51,7 +51,7 @@ int mi_rnext(MI_INFO *info, byte *buf, int inx)
case HA_KEY_ALG_BTREE:
default:
error=_mi_search_first(info,info->s->keyinfo+inx,
- info->s->state.key_root[inx]);
+ info->s->state.key_root[inx]);
break;
}
}
@@ -60,17 +60,17 @@ int mi_rnext(MI_INFO *info, byte *buf, int inx)
switch(info->s->keyinfo[inx].key_alg)
{
case HA_KEY_ALG_RTREE:
- /*
+ /*
Note that rtree doesn't support that the table
may be changed since last call, so we do need
to skip rows inserted by other threads like in btree
*/
error=rtree_get_next(info,inx,info->lastkey_length);
break;
-
+
case HA_KEY_ALG_BTREE:
default:
- if (!changed)
+ if (!changed)
{
error=_mi_search_next(info,info->s->keyinfo+inx,info->lastkey,
info->lastkey_length,flag,
@@ -81,15 +81,15 @@ int mi_rnext(MI_INFO *info, byte *buf, int inx)
error=_mi_search(info,info->s->keyinfo+inx,info->lastkey,
USE_WHOLE_KEY,flag, info->s->state.key_root[inx]);
}
- if (!error)
+ if (!error && info->s->concurrent_insert)
{
while (info->lastpos >= info->state->data_file_length)
{
/* Skip rows that are inserted by other threads since we got a lock */
- if ((error=_mi_search_next(info,info->s->keyinfo+inx,info->lastkey,
- info->lastkey_length,
- SEARCH_BIGGER,
- info->s->state.key_root[inx])))
+ if ((error=_mi_search_next(info,info->s->keyinfo+inx,info->lastkey,
+ info->lastkey_length,
+ SEARCH_BIGGER,
+ info->s->state.key_root[inx])))
break;
}
}
diff --git a/myisam/mi_update.c b/myisam/mi_update.c
index 81142ecabac..53c09a1d35c 100644
--- a/myisam/mi_update.c
+++ b/myisam/mi_update.c
@@ -91,7 +91,6 @@ int mi_update(register MI_INFO *info, const byte *oldrec, byte *newrec)
{
if (((ulonglong) 1 << i) & share->state.key_map)
{
- /* The following code block is for text searching by SerG */
if (share->keyinfo[i].flag & HA_FULLTEXT )
{
if (_mi_ft_cmp(info,i,oldrec, newrec))
@@ -175,7 +174,6 @@ err:
{
if (((ulonglong) 1 << i) & changed)
{
- /* The following code block is for text searching by SerG */
if (share->keyinfo[i].flag & HA_FULLTEXT)
{
if ((flag++ && _mi_ft_del(info,i,(char*) new_key,newrec,pos)) ||
diff --git a/myisam/mi_write.c b/myisam/mi_write.c
index 58f9ebdb735..cd7a4664a22 100644
--- a/myisam/mi_write.c
+++ b/myisam/mi_write.c
@@ -38,9 +38,9 @@ static int _mi_balance_page(MI_INFO *info,MI_KEYDEF *keyinfo,uchar *key,
static uchar *_mi_find_last_pos(MI_KEYDEF *keyinfo, uchar *page,
uchar *key, uint *return_key_length,
uchar **after_key);
-int _mi_ck_write_tree(register MI_INFO *info, uint keynr, uchar *key,
+int _mi_ck_write_tree(register MI_INFO *info, uint keynr,uchar *key,
uint key_length);
-int _mi_ck_write_btree(register MI_INFO *info, uint keynr, uchar *key,
+int _mi_ck_write_btree(register MI_INFO *info, uint keynr,uchar *key,
uint key_length);
/* Write new record to database */
@@ -250,11 +250,12 @@ int _mi_ck_write_btree(register MI_INFO *info, uint keynr, uchar *key,
int error;
uint comp_flag;
MI_KEYDEF *keyinfo=info->s->keyinfo+keynr;
+ my_off_t *root=&info->s->state.key_root[keynr];
DBUG_ENTER("_mi_ck_write_btree");
if (keyinfo->flag & HA_SORT_ALLOWS_SAME)
comp_flag=SEARCH_BIGGER; /* Put after same key */
- else if (keyinfo->flag & HA_NOSAME)
+ else if (keyinfo->flag & (HA_NOSAME|HA_FULLTEXT))
{
comp_flag=SEARCH_FIND | SEARCH_UPDATE; /* No dupplicates */
if (keyinfo->flag & HA_NULL_ARE_EQUAL)
@@ -263,37 +264,34 @@ int _mi_ck_write_btree(register MI_INFO *info, uint keynr, uchar *key,
else
comp_flag=SEARCH_SAME; /* Keys in rec-pos order */
- if (info->s->state.key_root[keynr] == HA_OFFSET_ERROR ||
+ if (*root == HA_OFFSET_ERROR ||
(error=w_search(info, keyinfo, comp_flag, key, key_length,
- info->s->state.key_root[keynr], (uchar *) 0, (uchar*) 0,
+ *root, (uchar *) 0, (uchar*) 0,
(my_off_t) 0, 1)) > 0)
- error=_mi_enlarge_root(info,keynr,key);
+ error=_mi_enlarge_root(info,keyinfo,key,root);
DBUG_RETURN(error);
} /* _mi_ck_write_btree */
/* Make a new root with key as only pointer */
-int _mi_enlarge_root(register MI_INFO *info, uint keynr, uchar *key)
+int _mi_enlarge_root(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
+ my_off_t *root)
{
uint t_length,nod_flag;
- reg2 MI_KEYDEF *keyinfo;
MI_KEY_PARAM s_temp;
MYISAM_SHARE *share=info->s;
DBUG_ENTER("_mi_enlarge_root");
- nod_flag= (share->state.key_root[keynr] != HA_OFFSET_ERROR) ?
- share->base.key_reflength : 0;
- _mi_kpointer(info,info->buff+2,share->state.key_root[keynr]); /* if nod */
- keyinfo=share->keyinfo+keynr;
+ nod_flag= (*root != HA_OFFSET_ERROR) ? share->base.key_reflength : 0;
+ _mi_kpointer(info,info->buff+2,*root); /* if nod */
t_length=(*keyinfo->pack_key)(keyinfo,nod_flag,(uchar*) 0,
(uchar*) 0, (uchar*) 0, key,&s_temp);
mi_putint(info->buff,t_length+2+nod_flag,nod_flag);
(*keyinfo->store_key)(keyinfo,info->buff+2+nod_flag,&s_temp);
info->buff_used=info->page_changed=1; /* info->buff is used */
- if ((share->state.key_root[keynr]= _mi_new(info,keyinfo)) ==
- HA_OFFSET_ERROR ||
- _mi_write_keypage(info,keyinfo,share->state.key_root[keynr],info->buff))
+ if ((*root= _mi_new(info,keyinfo)) == HA_OFFSET_ERROR ||
+ _mi_write_keypage(info,keyinfo,*root,info->buff))
DBUG_RETURN(-1);
DBUG_RETURN(0);
} /* _mi_enlarge_root */
@@ -333,15 +331,54 @@ static int w_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
if (flag == 0)
{
uint tmp_key_length;
- my_errno=HA_ERR_FOUND_DUPP_KEY;
/* get position to record with duplicated key */
tmp_key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&keypos,keybuff);
if (tmp_key_length)
info->dupp_key_pos=_mi_dpos(info,0,keybuff+tmp_key_length);
else
info->dupp_key_pos= HA_OFFSET_ERROR;
- my_afree((byte*) temp_buff);
- DBUG_RETURN(-1);
+ if (keyinfo->flag & HA_FULLTEXT)
+ {
+ uint off;
+ int subkeys;
+
+ get_key_full_length_rdonly(off, keybuff);
+ subkeys=ft_sintXkorr(keybuff+off);
+ comp_flag=SEARCH_SAME;
+ if (subkeys >= 0)
+ {
+ /* normal word, one-level tree structure */
+ flag=(*keyinfo->bin_search)(info, keyinfo, temp_buff, key,
+ USE_WHOLE_KEY, comp_flag,
+ &keypos, keybuff, &was_last_key);
+ }
+ else
+ {
+ /* popular word. two-level tree. going down */
+ my_off_t root=info->dupp_key_pos;
+ keyinfo=&info->s->ft2_keyinfo;
+ key+=off;
+ keypos-=keyinfo->keylength; /* we'll modify key entry 'in vivo' */
+ if ((error=w_search(info, keyinfo, comp_flag, key, HA_FT_WLEN, root,
+ (uchar *) 0, (uchar*) 0, (my_off_t) 0, 1)) > 0)
+ {
+ error=_mi_enlarge_root(info, keyinfo, key, &root);
+ _mi_dpointer(info, keypos+HA_FT_WLEN, root);
+ }
+ subkeys--; /* should there be underflow protection ? */
+ ft_intXstore(keypos, subkeys);
+ if (!error)
+ error=_mi_write_keypage(info,keyinfo,page,temp_buff);
+ my_afree((byte*) temp_buff);
+ DBUG_RETURN(error);
+ }
+ }
+ else /* not HA_FULLTEXT, normal HA_NOSAME key */
+ {
+ my_afree((byte*) temp_buff);
+ my_errno=HA_ERR_FOUND_DUPP_KEY;
+ DBUG_RETURN(-1);
+ }
}
if (flag == MI_FOUND_WRONG_KEY)
DBUG_RETURN(-1);
@@ -394,7 +431,9 @@ int _mi_insert(register MI_INFO *info, register MI_KEYDEF *keyinfo,
#ifndef DBUG_OFF
if (key_pos != anc_buff+2+nod_flag && (keyinfo->flag &
(HA_BINARY_PACK_KEY | HA_PACK_KEY)))
+ {
DBUG_DUMP("prev_key",(byte*) key_buff,_mi_keylength(keyinfo,key_buff));
+ }
if (keyinfo->flag & HA_PACK_KEY)
{
DBUG_PRINT("test",("t_length: %d ref_len: %d",
@@ -753,7 +792,7 @@ int _mi_ck_write_tree(register MI_INFO *info, uint keynr, uchar *key,
DBUG_ENTER("_mi_ck_write_tree");
error= tree_insert(&info->bulk_insert[keynr], key,
- key_length + info->s->rec_reflength,
+ key_length + info->s->rec_reflength,
info->bulk_insert[keynr].custom_arg) ? 0 : HA_ERR_OUT_OF_MEM ;
DBUG_RETURN(error);
diff --git a/myisam/myisamchk.c b/myisam/myisamchk.c
index 281cb90d9bf..3da0c3a2b61 100644
--- a/myisam/myisamchk.c
+++ b/myisam/myisamchk.c
@@ -874,8 +874,8 @@ static int myisamchk(MI_CHECK *param, my_string filename)
}
else
{
- if (share->fulltext_index)
- ft_init_stopwords(ft_precompiled_stopwords); /* SerG */
+ if (share->state.header.fulltext_keys)
+ ft_init_stopwords(ft_precompiled_stopwords);
if (!(param->testflag & T_READONLY))
lock_type = F_WRLCK; /* table is changed */
diff --git a/myisam/myisamdef.h b/myisam/myisamdef.h
index 90aaff46919..5b2a1b7c8e0 100644
--- a/myisam/myisamdef.h
+++ b/myisam/myisamdef.h
@@ -55,7 +55,8 @@ typedef struct st_mi_state_info
uchar uniques; /* number of UNIQUE definitions */
uchar language; /* Language for indexes */
uchar max_block_size; /* max keyblock size */
- uchar not_used[2]; /* To align to 8 */
+ uchar fulltext_keys;
+ uchar not_used; /* To align to 8 */
} header;
MI_STATUS_INFO state;
@@ -90,7 +91,7 @@ typedef struct st_mi_state_info
} MI_STATE_INFO;
#define MI_STATE_INFO_SIZE (24+14*8+7*4+2*2+8)
-#define MI_STATE_KEY_SIZE 8
+#define MI_STATE_KEY_SIZE 8
#define MI_STATE_KEYBLOCK_SIZE 8
#define MI_STATE_KEYSEG_SIZE 4
#define MI_STATE_EXTRA_SIZE ((MI_MAX_KEY+MI_MAX_KEY_BLOCK_SIZE)*MI_STATE_KEY_SIZE + MI_MAX_KEY*MI_MAX_KEY_SEG*MI_STATE_KEYSEG_SIZE)
@@ -154,6 +155,7 @@ typedef struct st_mi_isam_pack {
typedef struct st_mi_isam_share { /* Shared between opens */
MI_STATE_INFO state;
MI_BASE_INFO base;
+ MI_KEYDEF ft2_keyinfo; /* Second-level ft-key definition */
MI_KEYDEF *keyinfo; /* Key definitions */
MI_UNIQUEDEF *uniqueinfo; /* unique definitions */
HA_KEYSEG *keyparts; /* key part info */
@@ -197,8 +199,7 @@ typedef struct st_mi_isam_share { /* Shared between opens */
global_changed, /* If changed since open */
not_flushed,
temporary,delay_key_write,
- concurrent_insert,
- fulltext_index;
+ concurrent_insert;
#ifdef THREAD
THR_LOCK lock;
pthread_mutex_t intern_lock; /* Locking for use with _locking */
@@ -229,6 +230,8 @@ struct st_myisam_info {
byte *rec_buff; /* Tempbuff for recordpack */
uchar *int_keypos, /* Save position for next/previous */
*int_maxpos; /* -""- */
+ uint int_nod_flag; /* -""- */
+ uint32 int_keytree_version; /* -""- */
int (*read_record)(struct st_myisam_info*, my_off_t, byte*);
invalidator_by_filename invalidator; /* query cache invalidator */
ulong this_unique; /* uniq filenumber or thread */
@@ -247,7 +250,6 @@ struct st_myisam_info {
int dfile; /* The datafile */
uint opt_flag; /* Optim. for space/speed */
uint update; /* If file changed since open */
- uint int_nod_flag; /* -""- */
int lastinx; /* Last used index */
uint lastkey_length; /* Length of key in lastkey */
uint last_rkey_length; /* Last length in mi_rkey() */
@@ -259,7 +261,6 @@ struct st_myisam_info {
uint data_changed; /* Somebody has changed data */
uint save_update; /* When using KEY_READ */
int save_lastinx;
- uint32 int_keytree_version; /* -""- */
LIST open_list;
IO_CACHE rec_cache; /* When cacheing records */
myf lock_wait; /* is 0 or MY_DONT_WAIT */
@@ -372,6 +373,13 @@ typedef struct st_mi_sort_param
{ length=mi_uint2korr((key)+1)+3; (key)+=3; } \
}
+#define get_key_full_length_rdonly(length,key) \
+{ if ((uchar) *(key) != 255) \
+ length= ((uint) (uchar) *((key)))+1; \
+ else \
+ { length=mi_uint2korr((key)+1)+3; } \
+}
+
#define get_pack_length(length) ((length) >= 255 ? 3 : 1)
#define MI_MIN_BLOCK_LENGTH 20 /* Because of delete-link */
@@ -395,7 +403,7 @@ typedef struct st_mi_sort_param
#define MI_FOUND_WRONG_KEY 32738 /* Impossible value from ha_key_cmp */
#define MI_MAX_KEY_BLOCK_SIZE (MI_MAX_KEY_BLOCK_LENGTH/MI_MIN_KEY_BLOCK_LENGTH)
-#define MI_BLOCK_SIZE(key_length,data_pointer,key_pointer) ((((key_length+data_pointer+key_pointer)*4+key_pointer+2)/myisam_block_size+1)*myisam_block_size)
+#define MI_BLOCK_SIZE(key_length,data_pointer,key_pointer) (((((key_length)+(data_pointer)+(key_pointer))*4+(key_pointer)+2)/myisam_block_size+1)*myisam_block_size)
#define MI_MAX_KEYPTR_SIZE 5 /* For calculating block lengths */
#define MI_MIN_KEYBLOCK_LENGTH 50 /* When to split delete blocks */
@@ -454,7 +462,7 @@ extern int _mi_delete_static_record(MI_INFO *info);
extern int _mi_cmp_static_record(MI_INFO *info,const byte *record);
extern int _mi_read_rnd_static_record(MI_INFO*, byte *,my_off_t, my_bool);
extern int _mi_ck_write(MI_INFO *info,uint keynr,uchar *key,uint length);
-extern int _mi_enlarge_root(MI_INFO *info,uint keynr,uchar *key);
+extern int _mi_enlarge_root(MI_INFO *info,MI_KEYDEF *keyinfo,uchar *key, my_off_t *root);
extern int _mi_insert(MI_INFO *info,MI_KEYDEF *keyinfo,uchar *key,
uchar *anc_buff,uchar *key_pos,uchar *key_buff,
uchar *father_buff, uchar *father_keypos,
@@ -691,6 +699,7 @@ void mi_check_print_error _VARARGS((MI_CHECK *param, const char *fmt,...));
void mi_check_print_warning _VARARGS((MI_CHECK *param, const char *fmt,...));
void mi_check_print_info _VARARGS((MI_CHECK *param, const char *fmt,...));
int flush_pending_blocks(MI_SORT_PARAM *param);
+int sort_ft_buf_flush(MI_SORT_PARAM *sort_param);
int thr_write_keys(MI_SORT_PARAM *sort_param);
#ifdef THREAD
pthread_handler_decl(thr_find_all_keys,arg);
diff --git a/myisam/sort.c b/myisam/sort.c
index 7ad23df9358..d86aeb6aa3b 100644
--- a/myisam/sort.c
+++ b/myisam/sort.c
@@ -71,7 +71,8 @@ static int NEAR_F merge_buffers(MI_SORT_PARAM *info,uint keys,
BUFFPEK *Fb, BUFFPEK *Tb);
static int NEAR_F merge_index(MI_SORT_PARAM *,uint,uchar **,BUFFPEK *, int,
IO_CACHE *);
-
+static int flush_ft_buf(MI_SORT_PARAM *info);
+
static int NEAR_F write_keys_varlen(MI_SORT_PARAM *info,uchar **sort_keys,
uint count, BUFFPEK *buffpek,
IO_CACHE *tempfile);
@@ -120,7 +121,7 @@ int _create_index_by_sort(MI_SORT_PARAM *info,my_bool no_messages,
info->read_to_buffer=read_to_buffer;
info->write_key=write_merge_key;
}
-
+
my_b_clear(&tempfile);
my_b_clear(&tempfile_for_exceptions);
bzero((char*) &buffpek,sizeof(buffpek));
@@ -207,7 +208,7 @@ int _create_index_by_sort(MI_SORT_PARAM *info,my_bool no_messages,
goto err; /* purecov: inspected */
}
- if (flush_pending_blocks(info))
+ if (flush_ft_buf(info) || flush_pending_blocks(info))
goto err;
if (my_b_inited(&tempfile_for_exceptions))
@@ -478,7 +479,7 @@ int thr_write_keys(MI_SORT_PARAM *sort_param)
fflush(stdout);
}
if (write_index(sinfo, sinfo->sort_keys, sinfo->keys) ||
- flush_pending_blocks(sinfo))
+ flush_ft_buf(sinfo) || flush_pending_blocks(sinfo))
got_error=1;
}
}
@@ -551,6 +552,7 @@ int thr_write_keys(MI_SORT_PARAM *sort_param)
if (merge_index(sinfo, keys, (uchar **)mergebuf,
dynamic_element(&sinfo->buffpek,0,BUFFPEK *),
maxbuffer,&sinfo->tempfile) ||
+ flush_ft_buf(sinfo) ||
flush_pending_blocks(sinfo))
{
got_error=1;
@@ -976,3 +978,16 @@ merge_index(MI_SORT_PARAM *info, uint keys, uchar **sort_keys,
DBUG_RETURN(0);
} /* merge_index */
+static int
+flush_ft_buf(MI_SORT_PARAM *info)
+{
+ int err=0;
+ if (info->sort_info->ft_buf)
+ {
+ err=sort_ft_buf_flush(info);
+ my_free((gptr)info->sort_info->ft_buf, MYF(0));
+ info->sort_info->ft_buf=0;
+ }
+ return err;
+}
+
diff --git a/mysql-test/r/fulltext.result b/mysql-test/r/fulltext.result
index a991981bf21..2f816ba8fc6 100644
--- a/mysql-test/r/fulltext.result
+++ b/mysql-test/r/fulltext.result
@@ -16,6 +16,8 @@ select * from t1 where MATCH(a,b) AGAINST ("indexes collections");
a b
Full-text indexes are called collections
Only MyISAM tables support collections
+select * from t1 where MATCH(a,b) AGAINST ("only");
+a b
select * from t1 where MATCH(a,b) AGAINST ("collections") UNION ALL select * from t1 where MATCH(a,b) AGAINST ("indexes");
a b
Only MyISAM tables support collections
diff --git a/mysql-test/r/fulltext2.result b/mysql-test/r/fulltext2.result
new file mode 100644
index 00000000000..8086faadba4
--- /dev/null
+++ b/mysql-test/r/fulltext2.result
@@ -0,0 +1,871 @@
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (
+i int(10) unsigned not null auto_increment primary key,
+a varchar(255) not null,
+FULLTEXT KEY (a)
+) TYPE=MyISAM;
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaaxxx');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaazzz');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+insert t1 (a) values ('aaayyy');
+repair table t1 quick;
+Table Op Msg_type Msg_text
+test.t1 repair status OK
+select count(*) from t1 where match a against ('aaaxxx');
+count(*)
+260
+select count(*) from t1 where match a against ('aaayyy');
+count(*)
+250
+select count(*) from t1 where match a against ('aaazzz');
+count(*)
+255
+select count(*) from t1 where match a against ('aaaxxx' in boolean mode);
+count(*)
+260
+select count(*) from t1 where match a against ('aaayyy' in boolean mode);
+count(*)
+250
+select count(*) from t1 where match a against ('aaazzz' in boolean mode);
+count(*)
+255
+select count(*) from t1 where match a against ('aaaxxx aaayyy aaazzz');
+count(*)
+765
+select count(*) from t1 where match a against ('aaaxxx aaayyy aaazzz' in boolean mode);
+count(*)
+765
+select count(*) from t1 where match a against ('aaax*' in boolean mode);
+count(*)
+260
+select count(*) from t1 where match a against ('aaay*' in boolean mode);
+count(*)
+250
+select count(*) from t1 where match a against ('aaa*' in boolean mode);
+count(*)
+765
+insert t1 (a) values ('aaaxxx'),('aaayyy');
+insert t1 (a) values ('aaazzz'),('aaazzz'),('aaazzz'),('aaazzz'),('aaazzz');
+select count(*) from t1 where match a against ('aaaxxx');
+count(*)
+261
+select count(*) from t1 where match a against ('aaayyy');
+count(*)
+251
+select count(*) from t1 where match a against ('aaazzz');
+count(*)
+260
+insert t1 (a) values ('aaaxxx 000000');
+select count(*) from t1 where match a against ('000000');
+count(*)
+1
+delete from t1 where match a against ('000000');
+select count(*) from t1 where match a against ('000000');
+count(*)
+0
+select count(*) from t1 where match a against ('aaaxxx');
+count(*)
+261
+delete from t1 where match a against ('aaazzz');
+select count(*) from t1 where match a against ('aaaxxx' in boolean mode);
+count(*)
+261
+select count(*) from t1 where match a against ('aaayyy' in boolean mode);
+count(*)
+251
+select count(*) from t1 where match a against ('aaazzz' in boolean mode);
+count(*)
+0
+select count(*) from t1 where a = 'aaaxxx';
+count(*)
+261
+select count(*) from t1 where a = 'aaayyy';
+count(*)
+251
+select count(*) from t1 where a = 'aaazzz';
+count(*)
+0
+insert t1 (a) values ('aaaxxx 000000');
+select count(*) from t1 where match a against ('000000');
+count(*)
+1
+update t1 set a='aaazzz' where match a against ('000000');
+select count(*) from t1 where match a against ('aaaxxx' in boolean mode);
+count(*)
+261
+select count(*) from t1 where match a against ('aaazzz' in boolean mode);
+count(*)
+1
+update t1 set a='aaazzz' where a = 'aaaxxx';
+update t1 set a='aaaxxx' where a = 'aaayyy';
+select count(*) from t1 where match a against ('aaaxxx' in boolean mode);
+count(*)
+251
+select count(*) from t1 where match a against ('aaayyy' in boolean mode);
+count(*)
+0
+select count(*) from t1 where match a against ('aaazzz' in boolean mode);
+count(*)
+262
+DROP TABLE IF EXISTS t1;
diff --git a/mysql-test/t/fulltext.test b/mysql-test/t/fulltext.test
index 5a64f2614aa..ed628da6a30 100644
--- a/mysql-test/t/fulltext.test
+++ b/mysql-test/t/fulltext.test
@@ -16,6 +16,7 @@ INSERT INTO t1 VALUES('MySQL has now support', 'for full-text search'),
select * from t1 where MATCH(a,b) AGAINST ("collections");
select * from t1 where MATCH(a,b) AGAINST ("indexes");
select * from t1 where MATCH(a,b) AGAINST ("indexes collections");
+select * from t1 where MATCH(a,b) AGAINST ("only");
# UNION of fulltext's
select * from t1 where MATCH(a,b) AGAINST ("collections") UNION ALL select * from t1 where MATCH(a,b) AGAINST ("indexes");
diff --git a/mysql-test/t/fulltext2.test b/mysql-test/t/fulltext2.test
new file mode 100644
index 00000000000..998747018b4
--- /dev/null
+++ b/mysql-test/t/fulltext2.test
@@ -0,0 +1,93 @@
+#
+# test of new fulltext search features
+#
+
+#
+# two-level tree
+#
+
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (
+ i int(10) unsigned not null auto_increment primary key,
+ a varchar(255) not null,
+ FULLTEXT KEY (a)
+) TYPE=MyISAM;
+
+# two-level entry, second-level tree with depth 2
+let $1=260;
+while ($1)
+{
+ eval insert t1 (a) values ('aaaxxx');
+ dec $1;
+}
+
+# two-level entry, second-level tree has only one page
+let $1=255;
+while ($1)
+{
+ eval insert t1 (a) values ('aaazzz');
+ dec $1;
+}
+
+# one-level entry (entries)
+let $1=250;
+while ($1)
+{
+ eval insert t1 (a) values ('aaayyy');
+ dec $1;
+}
+
+# converting to two-level
+repair table t1 quick;
+
+select count(*) from t1 where match a against ('aaaxxx');
+select count(*) from t1 where match a against ('aaayyy');
+select count(*) from t1 where match a against ('aaazzz');
+select count(*) from t1 where match a against ('aaaxxx' in boolean mode);
+select count(*) from t1 where match a against ('aaayyy' in boolean mode);
+select count(*) from t1 where match a against ('aaazzz' in boolean mode);
+select count(*) from t1 where match a against ('aaaxxx aaayyy aaazzz');
+select count(*) from t1 where match a against ('aaaxxx aaayyy aaazzz' in boolean mode);
+
+select count(*) from t1 where match a against ('aaax*' in boolean mode);
+select count(*) from t1 where match a against ('aaay*' in boolean mode);
+select count(*) from t1 where match a against ('aaa*' in boolean mode);
+
+# mi_write:
+
+insert t1 (a) values ('aaaxxx'),('aaayyy');
+# call to enlarge_root() below
+insert t1 (a) values ('aaazzz'),('aaazzz'),('aaazzz'),('aaazzz'),('aaazzz');
+select count(*) from t1 where match a against ('aaaxxx');
+select count(*) from t1 where match a against ('aaayyy');
+select count(*) from t1 where match a against ('aaazzz');
+
+# mi_delete
+insert t1 (a) values ('aaaxxx 000000');
+select count(*) from t1 where match a against ('000000');
+delete from t1 where match a against ('000000');
+select count(*) from t1 where match a against ('000000');
+select count(*) from t1 where match a against ('aaaxxx');
+delete from t1 where match a against ('aaazzz');
+select count(*) from t1 where match a against ('aaaxxx' in boolean mode);
+select count(*) from t1 where match a against ('aaayyy' in boolean mode);
+select count(*) from t1 where match a against ('aaazzz' in boolean mode);
+# double-check without index
+select count(*) from t1 where a = 'aaaxxx';
+select count(*) from t1 where a = 'aaayyy';
+select count(*) from t1 where a = 'aaazzz';
+
+# update
+insert t1 (a) values ('aaaxxx 000000');
+select count(*) from t1 where match a against ('000000');
+update t1 set a='aaazzz' where match a against ('000000');
+select count(*) from t1 where match a against ('aaaxxx' in boolean mode);
+select count(*) from t1 where match a against ('aaazzz' in boolean mode);
+update t1 set a='aaazzz' where a = 'aaaxxx';
+update t1 set a='aaaxxx' where a = 'aaayyy';
+select count(*) from t1 where match a against ('aaaxxx' in boolean mode);
+select count(*) from t1 where match a against ('aaayyy' in boolean mode);
+select count(*) from t1 where match a against ('aaazzz' in boolean mode);
+
+DROP TABLE IF EXISTS t1;
+
diff --git a/mysys/mulalloc.c b/mysys/mulalloc.c
index 6b025bca699..e1eb1c00602 100644
--- a/mysys/mulalloc.c
+++ b/mysys/mulalloc.c
@@ -19,16 +19,18 @@
/*
Malloc many pointers at the same time
+ Only ptr1 can be free'd, and doing this will free all
+ the memory allocated. ptr2, etc all point inside big allocated
+ memory area.
SYNOPSIS
my_multi_malloc()
- myFlags Flags
- ... Multiple arguments terminated by null ptr
-
- ptr, length
- ptr, length
+ myFlags Flags
+ ptr1, length1 Multiple arguments terminated by null ptr
+ ptr2, length2 ...
+ ...
NULL
-*/
+*/
gptr my_multi_malloc(myf myFlags, ...)
{