~drizzle-trunk/drizzle/development

1 by brian
clean slate
1
/* Copyright (C) 2000-2004, 2006 MySQL 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
/* functions to work with full-text indices */
19
20
#include "ftdefs.h"
21
#include <math.h>
22
23
void _mi_ft_segiterator_init(MI_INFO *info, uint keynr, const uchar *record,
24
			     FT_SEG_ITERATOR *ftsi)
25
{
26
  DBUG_ENTER("_mi_ft_segiterator_init");
27
28
  ftsi->num=info->s->keyinfo[keynr].keysegs;
29
  ftsi->seg=info->s->keyinfo[keynr].seg;
30
  ftsi->rec=record;
31
  DBUG_VOID_RETURN;
32
}
33
34
void _mi_ft_segiterator_dummy_init(const uchar *record, uint len,
35
				   FT_SEG_ITERATOR *ftsi)
36
{
37
  DBUG_ENTER("_mi_ft_segiterator_dummy_init");
38
39
  ftsi->num=1;
40
  ftsi->seg=0;
41
  ftsi->pos=record;
42
  ftsi->len=len;
43
  DBUG_VOID_RETURN;
44
}
45
46
/*
47
  This function breaks convention "return 0 in success"
48
  but it's easier to use like this
49
50
     while(_mi_ft_segiterator())
51
52
  so "1" means "OK", "0" means "EOF"
53
*/
54
55
uint _mi_ft_segiterator(register FT_SEG_ITERATOR *ftsi)
56
{
57
  DBUG_ENTER("_mi_ft_segiterator");
58
59
  if (!ftsi->num)
60
    DBUG_RETURN(0);
61
62
  ftsi->num--;
63
  if (!ftsi->seg)
64
    DBUG_RETURN(1);
65
66
  ftsi->seg--;
67
68
  if (ftsi->seg->null_bit &&
69
      (ftsi->rec[ftsi->seg->null_pos] & ftsi->seg->null_bit))
70
  {
71
    ftsi->pos=0;
72
    DBUG_RETURN(1);
73
  }
74
  ftsi->pos= ftsi->rec+ftsi->seg->start;
75
  if (ftsi->seg->flag & HA_VAR_LENGTH_PART)
76
  {
77
    uint pack_length= (ftsi->seg->bit_start);
78
    ftsi->len= (pack_length == 1 ? (uint) *(uchar*) ftsi->pos :
79
                uint2korr(ftsi->pos));
80
    ftsi->pos+= pack_length;			 /* Skip VARCHAR length */
81
    DBUG_RETURN(1);
82
  }
83
  if (ftsi->seg->flag & HA_BLOB_PART)
84
  {
85
    ftsi->len=_mi_calc_blob_length(ftsi->seg->bit_start,ftsi->pos);
86
    memcpy_fixed((char*) &ftsi->pos, ftsi->pos+ftsi->seg->bit_start,
87
		 sizeof(char*));
88
    DBUG_RETURN(1);
89
  }
90
  ftsi->len=ftsi->seg->length;
91
  DBUG_RETURN(1);
92
}
93
94
95
/* parses a document i.e. calls ft_parse for every keyseg */
96
97
uint _mi_ft_parse(TREE *parsed, MI_INFO *info, uint keynr, const uchar *record,
98
                  MYSQL_FTPARSER_PARAM *param, MEM_ROOT *mem_root)
99
{
100
  FT_SEG_ITERATOR ftsi;
101
  struct st_mysql_ftparser *parser;
102
  DBUG_ENTER("_mi_ft_parse");
103
104
  _mi_ft_segiterator_init(info, keynr, record, &ftsi);
105
106
  ft_parse_init(parsed, info->s->keyinfo[keynr].seg->charset);
107
  parser= info->s->keyinfo[keynr].parser;
108
  while (_mi_ft_segiterator(&ftsi))
109
  {
110
    if (ftsi.pos)
111
      if (ft_parse(parsed, (uchar *)ftsi.pos, ftsi.len, parser, param, mem_root))
112
        DBUG_RETURN(1);
113
  }
114
  DBUG_RETURN(0);
115
}
116
117
FT_WORD *_mi_ft_parserecord(MI_INFO *info, uint keynr, const uchar *record,
118
                             MEM_ROOT *mem_root)
119
{
120
  TREE ptree;
121
  MYSQL_FTPARSER_PARAM *param;
122
  DBUG_ENTER("_mi_ft_parserecord");
123
  if (! (param= ftparser_call_initializer(info, keynr, 0)))
124
    DBUG_RETURN(NULL);
125
  bzero((char*) &ptree, sizeof(ptree));
126
  param->flags= 0;
127
  if (_mi_ft_parse(&ptree, info, keynr, record, param, mem_root))
128
    DBUG_RETURN(NULL);
129
130
  DBUG_RETURN(ft_linearize(&ptree, mem_root));
131
}
132
133
static int _mi_ft_store(MI_INFO *info, uint keynr, uchar *keybuf,
134
			FT_WORD *wlist, my_off_t filepos)
135
{
136
  uint key_length;
137
  DBUG_ENTER("_mi_ft_store");
138
139
  for (; wlist->pos; wlist++)
140
  {
141
    key_length=_ft_make_key(info,keynr,keybuf,wlist,filepos);
142
    if (_mi_ck_write(info,keynr,(uchar*) keybuf,key_length))
143
      DBUG_RETURN(1);
144
   }
145
   DBUG_RETURN(0);
146
}
147
148
static int _mi_ft_erase(MI_INFO *info, uint keynr, uchar *keybuf,
149
			FT_WORD *wlist, my_off_t filepos)
150
{
151
  uint key_length, err=0;
152
  DBUG_ENTER("_mi_ft_erase");
153
154
  for (; wlist->pos; wlist++)
155
  {
156
    key_length=_ft_make_key(info,keynr,keybuf,wlist,filepos);
157
    if (_mi_ck_delete(info,keynr,(uchar*) keybuf,key_length))
158
      err=1;
159
   }
160
   DBUG_RETURN(err);
161
}
162
163
/*
164
  Compares an appropriate parts of two WORD_KEY keys directly out of records
165
  returns 1 if they are different
166
*/
167
168
#define THOSE_TWO_DAMN_KEYS_ARE_REALLY_DIFFERENT 1
169
#define GEE_THEY_ARE_ABSOLUTELY_IDENTICAL	 0
170
171
int _mi_ft_cmp(MI_INFO *info, uint keynr, const uchar *rec1, const uchar *rec2)
172
{
173
  FT_SEG_ITERATOR ftsi1, ftsi2;
174
  CHARSET_INFO *cs=info->s->keyinfo[keynr].seg->charset;
175
  DBUG_ENTER("_mi_ft_cmp");
176
  _mi_ft_segiterator_init(info, keynr, rec1, &ftsi1);
177
  _mi_ft_segiterator_init(info, keynr, rec2, &ftsi2);
178
179
  while (_mi_ft_segiterator(&ftsi1) && _mi_ft_segiterator(&ftsi2))
180
  {
181
    if ((ftsi1.pos != ftsi2.pos) &&
182
        (!ftsi1.pos || !ftsi2.pos ||
183
         ha_compare_text(cs, (uchar*) ftsi1.pos,ftsi1.len,
184
                         (uchar*) ftsi2.pos,ftsi2.len,0,0)))
185
      DBUG_RETURN(THOSE_TWO_DAMN_KEYS_ARE_REALLY_DIFFERENT);
186
  }
187
  DBUG_RETURN(GEE_THEY_ARE_ABSOLUTELY_IDENTICAL);
188
}
189
190
191
/* update a document entry */
192
193
int _mi_ft_update(MI_INFO *info, uint keynr, uchar *keybuf,
194
                  const uchar *oldrec, const uchar *newrec, my_off_t pos)
195
{
196
  int error= -1;
197
  FT_WORD *oldlist,*newlist, *old_word, *new_word;
198
  CHARSET_INFO *cs=info->s->keyinfo[keynr].seg->charset;
199
  uint key_length;
200
  int cmp, cmp2;
201
  DBUG_ENTER("_mi_ft_update");
202
203
  if (!(old_word=oldlist=_mi_ft_parserecord(info, keynr, oldrec,
204
                                            &info->ft_memroot)) ||
205
      !(new_word=newlist=_mi_ft_parserecord(info, keynr, newrec,
206
                                            &info->ft_memroot)))
207
    goto err;
208
209
  error=0;
210
  while(old_word->pos && new_word->pos)
211
  {
212
    cmp= ha_compare_text(cs, (uchar*) old_word->pos,old_word->len,
213
                             (uchar*) new_word->pos,new_word->len,0,0);
214
    cmp2= cmp ? 0 : (fabs(old_word->weight - new_word->weight) > 1.e-5);
215
216
    if (cmp < 0 || cmp2)
217
    {
218
      key_length=_ft_make_key(info,keynr,keybuf,old_word,pos);
219
      if ((error=_mi_ck_delete(info,keynr,(uchar*) keybuf,key_length)))
220
        goto err;
221
    }
222
    if (cmp > 0 || cmp2)
223
    {
224
      key_length=_ft_make_key(info,keynr,keybuf,new_word,pos);
225
      if ((error=_mi_ck_write(info,keynr,(uchar*) keybuf,key_length)))
226
        goto err;
227
    }
228
    if (cmp<=0) old_word++;
229
    if (cmp>=0) new_word++;
230
 }
231
 if (old_word->pos)
232
   error=_mi_ft_erase(info,keynr,keybuf,old_word,pos);
233
 else if (new_word->pos)
234
   error=_mi_ft_store(info,keynr,keybuf,new_word,pos);
235
236
err:
237
  free_root(&info->ft_memroot, MYF(MY_MARK_BLOCKS_FREE));
238
  DBUG_RETURN(error);
239
}
240
241
242
/* adds a document to the collection */
243
244
int _mi_ft_add(MI_INFO *info, uint keynr, uchar *keybuf, const uchar *record,
245
	       my_off_t pos)
246
{
247
  int error= -1;
248
  FT_WORD *wlist;
249
  DBUG_ENTER("_mi_ft_add");
250
  DBUG_PRINT("enter",("keynr: %d",keynr));
251
252
  if ((wlist=_mi_ft_parserecord(info, keynr, record, &info->ft_memroot)))
253
    error=_mi_ft_store(info,keynr,keybuf,wlist,pos);
254
255
  free_root(&info->ft_memroot, MYF(MY_MARK_BLOCKS_FREE));
256
  DBUG_PRINT("exit",("Return: %d",error));
257
  DBUG_RETURN(error);
258
}
259
260
261
/* removes a document from the collection */
262
263
int _mi_ft_del(MI_INFO *info, uint keynr, uchar *keybuf, const uchar *record,
264
	       my_off_t pos)
265
{
266
  int error= -1;
267
  FT_WORD *wlist;
268
  DBUG_ENTER("_mi_ft_del");
269
  DBUG_PRINT("enter",("keynr: %d",keynr));
270
271
  if ((wlist=_mi_ft_parserecord(info, keynr, record, &info->ft_memroot)))
272
    error=_mi_ft_erase(info,keynr,keybuf,wlist,pos);
273
274
  free_root(&info->ft_memroot, MYF(MY_MARK_BLOCKS_FREE));
275
  DBUG_PRINT("exit",("Return: %d",error));
276
  DBUG_RETURN(error);
277
}
278
279
uint _ft_make_key(MI_INFO *info, uint keynr, uchar *keybuf, FT_WORD *wptr,
280
		  my_off_t filepos)
281
{
282
  uchar buf[HA_FT_MAXBYTELEN+16];
283
  DBUG_ENTER("_ft_make_key");
284
285
#if HA_FT_WTYPE == HA_KEYTYPE_FLOAT
286
  {
287
    float weight=(float) ((filepos==HA_OFFSET_ERROR) ? 0 : wptr->weight);
288
    mi_float4store(buf,weight);
289
  }
290
#else
291
#error
292
#endif
293
294
  int2store(buf+HA_FT_WLEN,wptr->len);
295
  memcpy(buf+HA_FT_WLEN+2,wptr->pos,wptr->len);
296
  DBUG_RETURN(_mi_make_key(info,keynr,(uchar*) keybuf,buf,filepos));
297
}
298
299
300
/*
301
  convert key value to ft2
302
*/
303
304
uint _mi_ft_convert_to_ft2(MI_INFO *info, uint keynr, uchar *key)
305
{
306
  my_off_t root;
307
  DYNAMIC_ARRAY *da=info->ft1_to_ft2;
308
  MI_KEYDEF *keyinfo=&info->s->ft2_keyinfo;
309
  uchar *key_ptr= (uchar*) dynamic_array_ptr(da, 0), *end;
310
  uint length, key_length;
311
  DBUG_ENTER("_mi_ft_convert_to_ft2");
312
313
  /* we'll generate one pageful at once, and insert the rest one-by-one */
314
  /* calculating the length of this page ...*/
315
  length=(keyinfo->block_length-2) / keyinfo->keylength;
316
  set_if_smaller(length, da->elements);
317
  length=length * keyinfo->keylength;
318
319
  get_key_full_length_rdonly(key_length, key);
320
  while (_mi_ck_delete(info, keynr, key, key_length) == 0)
321
  {
322
    /*
323
      nothing to do here.
324
      _mi_ck_delete() will populate info->ft1_to_ft2 with deleted keys
325
     */
326
  }
327
328
  /* creating pageful of keys */
329
  mi_putint(info->buff,length+2,0);
330
  memcpy(info->buff+2, key_ptr, length);
331
  info->buff_used=info->page_changed=1;           /* info->buff is used */
332
  if ((root= _mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR ||
333
      _mi_write_keypage(info,keyinfo,root,DFLT_INIT_HITS,info->buff))
334
    DBUG_RETURN(-1);
335
336
  /* inserting the rest of key values */
337
  end= (uchar*) dynamic_array_ptr(da, da->elements);
338
  for (key_ptr+=length; key_ptr < end; key_ptr+=keyinfo->keylength)
339
    if(_mi_ck_real_write_btree(info, keyinfo, key_ptr, 0, &root, SEARCH_SAME))
340
      DBUG_RETURN(-1);
341
342
  /* now, writing the word key entry */
343
  ft_intXstore(key+key_length, - (int) da->elements);
344
  _mi_dpointer(info, key+key_length+HA_FT_WLEN, root);
345
346
  DBUG_RETURN(_mi_ck_real_write_btree(info,
347
                                     info->s->keyinfo+keynr,
348
                                     key, 0,
349
                                     &info->s->state.key_root[keynr],
350
                                     SEARCH_SAME));
351
}
352