~jlukas79/+junk/mysql-server

« back to all changes in this revision

Viewing changes to storage/maria/ma_ft_parser.c

manual merge 6.0-main --> 6.0-bka-review

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
 
2
 
 
3
   This program is free software; you can redistribute it and/or modify
 
4
   it under the terms of the GNU General Public License as published by
 
5
   the Free Software Foundation; version 2 of the License.
 
6
 
 
7
   This program is distributed in the hope that it will be useful,
 
8
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
9
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
10
   GNU General Public License for more details.
 
11
 
 
12
   You should have received a copy of the GNU General Public License
 
13
   along with this program; if not, write to the Free Software
 
14
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
15
 
 
16
/* Written by Sergei A. Golubchik, who has a shared copyright to this code */
 
17
 
 
18
#include "ma_ftdefs.h"
 
19
 
 
20
typedef struct st_maria_ft_docstat {
 
21
  FT_WORD *list;
 
22
  uint uniq;
 
23
  double sum;
 
24
} FT_DOCSTAT;
 
25
 
 
26
 
 
27
typedef struct st_my_maria_ft_parser_param
 
28
{
 
29
  TREE *wtree;
 
30
  MEM_ROOT *mem_root;
 
31
} MY_FT_PARSER_PARAM;
 
32
 
 
33
 
 
34
static int FT_WORD_cmp(CHARSET_INFO* cs, FT_WORD *w1, FT_WORD *w2)
 
35
{
 
36
  return ha_compare_text(cs, (uchar*) w1->pos, w1->len,
 
37
                         (uchar*) w2->pos, w2->len, 0, 0);
 
38
}
 
39
 
 
40
static int walk_and_copy(FT_WORD *word,uint32 count,FT_DOCSTAT *docstat)
 
41
{
 
42
    word->weight=LWS_IN_USE;
 
43
    docstat->sum+=word->weight;
 
44
    memcpy_fixed((docstat->list)++,word,sizeof(FT_WORD));
 
45
    return 0;
 
46
}
 
47
 
 
48
/* transforms tree of words into the array, applying normalization */
 
49
 
 
50
FT_WORD * maria_ft_linearize(TREE *wtree, MEM_ROOT *mem_root)
 
51
{
 
52
  FT_WORD *wlist,*p;
 
53
  FT_DOCSTAT docstat;
 
54
  DBUG_ENTER("maria_ft_linearize");
 
55
 
 
56
  if ((wlist=(FT_WORD *) alloc_root(mem_root, sizeof(FT_WORD)*
 
57
                                    (1+wtree->elements_in_tree))))
 
58
  {
 
59
    docstat.list=wlist;
 
60
    docstat.uniq=wtree->elements_in_tree;
 
61
    docstat.sum=0;
 
62
    tree_walk(wtree,(tree_walk_action)&walk_and_copy,&docstat,left_root_right);
 
63
  }
 
64
  delete_tree(wtree);
 
65
  if (!wlist)
 
66
    DBUG_RETURN(NULL);
 
67
 
 
68
  docstat.list->pos=NULL;
 
69
 
 
70
  for (p=wlist;p->pos;p++)
 
71
  {
 
72
    p->weight=PRENORM_IN_USE;
 
73
  }
 
74
 
 
75
  for (p=wlist;p->pos;p++)
 
76
  {
 
77
    p->weight/=NORM_IN_USE;
 
78
  }
 
79
 
 
80
  DBUG_RETURN(wlist);
 
81
}
 
82
 
 
83
my_bool maria_ft_boolean_check_syntax_string(const uchar *str)
 
84
{
 
85
  uint i, j;
 
86
 
 
87
  if (!str ||
 
88
      (strlen((const char *) str) + 1 != sizeof(ft_boolean_syntax)) ||
 
89
      (str[0] != ' ' && str[1] != ' '))
 
90
    return 1;
 
91
  for (i=0; i<sizeof(ft_boolean_syntax); i++)
 
92
  {
 
93
    /* limiting to 7-bit ascii only */
 
94
    if ((unsigned char)(str[i]) > 127 ||
 
95
        my_isalnum(default_charset_info, str[i]))
 
96
      return 1;
 
97
    for (j=0; j<i; j++)
 
98
      if (str[i] == str[j] && (i != 11 || j != 10))
 
99
        return 1;
 
100
  }
 
101
  return 0;
 
102
}
 
103
 
 
104
/*
 
105
  RETURN VALUE
 
106
  0 - eof
 
107
  1 - word found
 
108
  2 - left bracket
 
109
  3 - right bracket
 
110
  4 - stopword found
 
111
*/
 
112
uchar maria_ft_get_word(CHARSET_INFO *cs, uchar **start, uchar *end,
 
113
                        FT_WORD *word, MYSQL_FTPARSER_BOOLEAN_INFO *param)
 
114
{
 
115
  uchar *doc=*start;
 
116
  int ctype;
 
117
  uint mwc, length;
 
118
  int mbl;
 
119
 
 
120
  param->yesno=(FTB_YES==' ') ? 1 : (param->quot != 0);
 
121
  param->weight_adjust= param->wasign= 0;
 
122
  param->type= FT_TOKEN_EOF;
 
123
 
 
124
  while (doc<end)
 
125
  {
 
126
    for (; doc < end; doc+= (mbl > 0 ? mbl : (mbl < 0 ? -mbl : 1)))
 
127
    {
 
128
      mbl= cs->cset->ctype(cs, &ctype, (uchar*)doc, (uchar*)end);
 
129
      if (true_word_char(ctype, *doc))
 
130
        break;
 
131
      if (*doc == FTB_RQUOT && param->quot)
 
132
      {
 
133
        param->quot= (char *) doc;
 
134
        *start=doc+1;
 
135
        param->type= FT_TOKEN_RIGHT_PAREN;
 
136
        goto ret;
 
137
      }
 
138
      if (!param->quot)
 
139
      {
 
140
        if (*doc == FTB_LBR || *doc == FTB_RBR || *doc == FTB_LQUOT)
 
141
        {
 
142
          /* param->prev=' '; */
 
143
          *start=doc+1;
 
144
          if (*doc == FTB_LQUOT)
 
145
            param->quot= (char *) *start;
 
146
          param->type= (*doc == FTB_RBR ? FT_TOKEN_RIGHT_PAREN : FT_TOKEN_LEFT_PAREN);
 
147
          goto ret;
 
148
        }
 
149
        if (param->prev == ' ')
 
150
        {
 
151
          if (*doc == FTB_YES ) { param->yesno=+1;    continue; } else
 
152
          if (*doc == FTB_EGAL) { param->yesno= 0;    continue; } else
 
153
          if (*doc == FTB_NO  ) { param->yesno=-1;    continue; } else
 
154
          if (*doc == FTB_INC ) { param->weight_adjust++; continue; } else
 
155
          if (*doc == FTB_DEC ) { param->weight_adjust--; continue; } else
 
156
          if (*doc == FTB_NEG ) { param->wasign= !param->wasign; continue; }
 
157
        }
 
158
      }
 
159
      param->prev=*doc;
 
160
      param->yesno=(FTB_YES==' ') ? 1 : (param->quot != 0);
 
161
      param->weight_adjust= param->wasign= 0;
 
162
    }
 
163
 
 
164
    mwc=length=0;
 
165
    for (word->pos= doc; doc < end; length++,
 
166
         doc+= (mbl > 0 ? mbl : (mbl < 0 ? -mbl : 1)))
 
167
    {
 
168
      mbl= cs->cset->ctype(cs, &ctype, (uchar*)doc, (uchar*)end);
 
169
      if (true_word_char(ctype, *doc))
 
170
        mwc=0;
 
171
      else if (!misc_word_char(*doc) || mwc)
 
172
        break;
 
173
      else
 
174
        mwc++;
 
175
    }
 
176
    param->prev='A'; /* be sure *prev is true_word_char */
 
177
    word->len= (uint)(doc-word->pos) - mwc;
 
178
    if ((param->trunc=(doc<end && *doc == FTB_TRUNC)))
 
179
      doc++;
 
180
 
 
181
    if (((length >= ft_min_word_len && !is_stopword((char *) word->pos,
 
182
                                                    word->len))
 
183
         || param->trunc) && length < ft_max_word_len)
 
184
    {
 
185
      *start=doc;
 
186
      param->type= FT_TOKEN_WORD;
 
187
      goto ret;
 
188
    }
 
189
    else if (length) /* make sure length > 0 (if start contains spaces only) */
 
190
    {
 
191
      *start= doc;
 
192
      param->type= FT_TOKEN_STOPWORD;
 
193
      goto ret;
 
194
    }
 
195
  }
 
196
  if (param->quot)
 
197
  {
 
198
    param->quot= (char *)(*start= doc);
 
199
    param->type= 3; /* FT_RBR */
 
200
    goto ret;
 
201
  }
 
202
ret:
 
203
  return param->type;
 
204
}
 
205
 
 
206
uchar maria_ft_simple_get_word(CHARSET_INFO *cs, uchar **start,
 
207
                               const uchar *end, FT_WORD *word,
 
208
                               my_bool skip_stopwords)
 
209
{
 
210
  uchar *doc= *start;
 
211
  uint mwc, length;
 
212
  int ctype, mbl;
 
213
  DBUG_ENTER("maria_ft_simple_get_word");
 
214
 
 
215
  do
 
216
  {
 
217
    for (;; doc+= (mbl > 0 ? mbl : (mbl < 0 ? -mbl : 1)))
 
218
    {
 
219
      if (doc >= end)
 
220
        DBUG_RETURN(0);
 
221
      mbl= cs->cset->ctype(cs, &ctype, doc, end);
 
222
      if (true_word_char(ctype, *doc))
 
223
        break;
 
224
    }
 
225
 
 
226
    mwc= length= 0;
 
227
    for (word->pos= doc; doc < end; length++,
 
228
         doc+= (mbl > 0 ? mbl : (mbl < 0 ? -mbl : 1)))
 
229
    {
 
230
      mbl= cs->cset->ctype(cs, &ctype, doc, end);
 
231
      if (true_word_char(ctype, *doc))
 
232
        mwc= 0;
 
233
      else if (!misc_word_char(*doc) || mwc)
 
234
        break;
 
235
      else
 
236
        mwc++;
 
237
    }
 
238
 
 
239
    word->len= (uint)(doc-word->pos) - mwc;
 
240
 
 
241
    if (skip_stopwords == FALSE ||
 
242
        (length >= ft_min_word_len && length < ft_max_word_len &&
 
243
         !is_stopword((char *) word->pos, word->len)))
 
244
    {
 
245
      *start= doc;
 
246
      DBUG_RETURN(1);
 
247
    }
 
248
  } while (doc < end);
 
249
  DBUG_RETURN(0);
 
250
}
 
251
 
 
252
void maria_ft_parse_init(TREE *wtree, CHARSET_INFO *cs)
 
253
{
 
254
  DBUG_ENTER("maria_ft_parse_init");
 
255
  if (!is_tree_inited(wtree))
 
256
    init_tree(wtree,0,0,sizeof(FT_WORD),(qsort_cmp2)&FT_WORD_cmp,0,NULL, cs);
 
257
  DBUG_VOID_RETURN;
 
258
}
 
259
 
 
260
 
 
261
static int maria_ft_add_word(MYSQL_FTPARSER_PARAM *param,
 
262
                       char *word, int word_len,
 
263
             MYSQL_FTPARSER_BOOLEAN_INFO *boolean_info __attribute__((unused)))
 
264
{
 
265
  TREE *wtree;
 
266
  FT_WORD w;
 
267
  MY_FT_PARSER_PARAM *ft_param=param->mysql_ftparam;
 
268
  DBUG_ENTER("maria_ft_add_word");
 
269
  wtree= ft_param->wtree;
 
270
  if (param->flags & MYSQL_FTFLAGS_NEED_COPY)
 
271
  {
 
272
    uchar *ptr;
 
273
    DBUG_ASSERT(wtree->with_delete == 0);
 
274
    ptr= (uchar *)alloc_root(ft_param->mem_root, word_len);
 
275
    memcpy(ptr, word, word_len);
 
276
    w.pos= ptr;
 
277
  }
 
278
  else
 
279
    w.pos= (uchar *) word;
 
280
  w.len= word_len;
 
281
  if (!tree_insert(wtree, &w, 0, wtree->custom_arg))
 
282
  {
 
283
    delete_tree(wtree);
 
284
    DBUG_RETURN(1);
 
285
  }
 
286
  DBUG_RETURN(0);
 
287
}
 
288
 
 
289
 
 
290
static int maria_ft_parse_internal(MYSQL_FTPARSER_PARAM *param,
 
291
                                   char *doc_arg, int doc_len)
 
292
{
 
293
  uchar *doc= (uchar*) doc_arg;
 
294
  uchar *end= doc + doc_len;
 
295
  MY_FT_PARSER_PARAM *ft_param=param->mysql_ftparam;
 
296
  TREE *wtree= ft_param->wtree;
 
297
  FT_WORD w;
 
298
  DBUG_ENTER("maria_ft_parse_internal");
 
299
 
 
300
  while (maria_ft_simple_get_word(wtree->custom_arg, &doc, end, &w, TRUE))
 
301
    if (param->mysql_add_word(param, (char *) w.pos, w.len, 0))
 
302
      DBUG_RETURN(1);
 
303
  DBUG_RETURN(0);
 
304
}
 
305
 
 
306
 
 
307
int maria_ft_parse(TREE *wtree, uchar *doc, int doclen,
 
308
                    struct st_mysql_ftparser *parser,
 
309
                   MYSQL_FTPARSER_PARAM *param, MEM_ROOT *mem_root)
 
310
{
 
311
  MY_FT_PARSER_PARAM my_param;
 
312
  DBUG_ENTER("maria_ft_parse");
 
313
  DBUG_ASSERT(parser);
 
314
  my_param.wtree= wtree;
 
315
  my_param.mem_root= mem_root;
 
316
 
 
317
  param->mysql_parse= maria_ft_parse_internal;
 
318
  param->mysql_add_word= maria_ft_add_word;
 
319
  param->mysql_ftparam= &my_param;
 
320
  param->cs= wtree->custom_arg;
 
321
  param->doc= (char *) doc;
 
322
  param->length= doclen;
 
323
  param->mode= MYSQL_FTPARSER_SIMPLE_MODE;
 
324
  DBUG_RETURN(parser->parse(param));
 
325
}
 
326
 
 
327
 
 
328
#define MAX_PARAM_NR 2
 
329
MYSQL_FTPARSER_PARAM *maria_ftparser_call_initializer(MARIA_HA *info,
 
330
                                                      uint keynr, uint paramnr)
 
331
{
 
332
  uint32 ftparser_nr;
 
333
  struct st_mysql_ftparser *parser;
 
334
  if (! info->ftparser_param)
 
335
  {
 
336
    /* info->ftparser_param can not be zero after the initialization,
 
337
       because it always includes built-in fulltext parser. And built-in
 
338
       parser can be called even if the table has no fulltext indexes and
 
339
       no varchar/text fields. */
 
340
    if (! info->s->ftparsers)
 
341
    {
 
342
      /* It's ok that modification to shared structure is done w/o mutex
 
343
         locks, because all threads would set the same variables to the
 
344
         same values. */
 
345
      uint i, j, keys= info->s->state.header.keys, ftparsers= 1;
 
346
      for (i= 0; i < keys; i++)
 
347
      {
 
348
        MARIA_KEYDEF *keyinfo= &info->s->keyinfo[i];
 
349
        if (keyinfo->flag & HA_FULLTEXT)
 
350
        {
 
351
          for (j= 0;; j++)
 
352
          {
 
353
            if (j == i)
 
354
            {
 
355
              keyinfo->ftparser_nr= ftparsers++;
 
356
              break;
 
357
            }
 
358
            if (info->s->keyinfo[j].flag & HA_FULLTEXT &&
 
359
                keyinfo->parser == info->s->keyinfo[j].parser)
 
360
            {
 
361
              keyinfo->ftparser_nr= info->s->keyinfo[j].ftparser_nr;
 
362
              break;
 
363
            }
 
364
          }
 
365
        }
 
366
      }
 
367
      info->s->ftparsers= ftparsers;
 
368
    }
 
369
    /*
 
370
      We have to allocate two MYSQL_FTPARSER_PARAM structures per plugin
 
371
      because in a boolean search a parser is called recursively
 
372
      ftb_find_relevance* calls ftb_check_phrase*
 
373
      (MAX_PARAM_NR=2)
 
374
    */
 
375
    info->ftparser_param= (MYSQL_FTPARSER_PARAM *)
 
376
      my_malloc(MAX_PARAM_NR * sizeof(MYSQL_FTPARSER_PARAM) *
 
377
                info->s->ftparsers, MYF(MY_WME|MY_ZEROFILL));
 
378
    init_alloc_root(&info->ft_memroot, FTPARSER_MEMROOT_ALLOC_SIZE, 0);
 
379
    if (! info->ftparser_param)
 
380
      return 0;
 
381
  }
 
382
  if (keynr == NO_SUCH_KEY)
 
383
  {
 
384
    ftparser_nr= 0;
 
385
    parser= &ft_default_parser;
 
386
  }
 
387
  else
 
388
  {
 
389
    ftparser_nr= info->s->keyinfo[keynr].ftparser_nr;
 
390
    parser= info->s->keyinfo[keynr].parser;
 
391
  }
 
392
  DBUG_ASSERT(paramnr < MAX_PARAM_NR);
 
393
  ftparser_nr= ftparser_nr*MAX_PARAM_NR + paramnr;
 
394
  if (! info->ftparser_param[ftparser_nr].mysql_add_word)
 
395
  {
 
396
    /* Note, that mysql_add_word is used here as a flag:
 
397
       mysql_add_word == 0 - parser is not initialized
 
398
       mysql_add_word != 0 - parser is initialized, or no
 
399
                             initialization needed. */
 
400
    info->ftparser_param[ftparser_nr].mysql_add_word=
 
401
      (int (*)(struct st_mysql_ftparser_param *, char *, int,
 
402
               MYSQL_FTPARSER_BOOLEAN_INFO *)) 1;
 
403
    if (parser->init && parser->init(&info->ftparser_param[ftparser_nr]))
 
404
      return 0;
 
405
  }
 
406
  return &info->ftparser_param[ftparser_nr];
 
407
}
 
408
 
 
409
 
 
410
void maria_ftparser_call_deinitializer(MARIA_HA *info)
 
411
{
 
412
  uint i, j, keys= info->s->state.header.keys;
 
413
  free_root(&info->ft_memroot, MYF(0));
 
414
  if (! info->ftparser_param)
 
415
    return;
 
416
  for (i= 0; i < keys; i++)
 
417
  {
 
418
    MARIA_KEYDEF *keyinfo= &info->s->keyinfo[i];
 
419
    for (j=0; j < MAX_PARAM_NR; j++)
 
420
    {
 
421
      MYSQL_FTPARSER_PARAM *ftparser_param=
 
422
        &info->ftparser_param[keyinfo->ftparser_nr*MAX_PARAM_NR + j];
 
423
      if (keyinfo->flag & HA_FULLTEXT && ftparser_param->mysql_add_word)
 
424
      {
 
425
        if (keyinfo->parser->deinit)
 
426
          keyinfo->parser->deinit(ftparser_param);
 
427
        ftparser_param->mysql_add_word= 0;
 
428
      }
 
429
      else
 
430
        break;
 
431
    }
 
432
  }
 
433
}