1 /* Copyright (c) 2000-2007 MySQL AB
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
16 /* Written by Sergei A. Golubchik, who has a shared copyright to this code */
18 /* functions to work with full-text indices */
23 void _mi_ft_segiterator_init(MI_INFO
*info
, uint keynr
, const uchar
*record
,
24 FT_SEG_ITERATOR
*ftsi
)
26 DBUG_ENTER("_mi_ft_segiterator_init");
28 ftsi
->num
=info
->s
->keyinfo
[keynr
].keysegs
;
29 ftsi
->seg
=info
->s
->keyinfo
[keynr
].seg
;
34 void _mi_ft_segiterator_dummy_init(const uchar
*record
, uint len
,
35 FT_SEG_ITERATOR
*ftsi
)
37 DBUG_ENTER("_mi_ft_segiterator_dummy_init");
47 This function breaks convention "return 0 in success"
48 but it's easier to use like this
50 while(_mi_ft_segiterator())
52 so "1" means "OK", "0" means "EOF"
55 uint
_mi_ft_segiterator(register FT_SEG_ITERATOR
*ftsi
)
57 DBUG_ENTER("_mi_ft_segiterator");
68 if (ftsi
->seg
->null_bit
&&
69 (ftsi
->rec
[ftsi
->seg
->null_pos
] & ftsi
->seg
->null_bit
))
74 ftsi
->pos
= ftsi
->rec
+ftsi
->seg
->start
;
75 if (ftsi
->seg
->flag
& HA_VAR_LENGTH_PART
)
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 */
83 if (ftsi
->seg
->flag
& HA_BLOB_PART
)
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
,
90 ftsi
->len
=ftsi
->seg
->length
;
95 /* parses a document i.e. calls ft_parse for every keyseg */
97 uint
_mi_ft_parse(TREE
*parsed
, MI_INFO
*info
, uint keynr
, const uchar
*record
,
98 MYSQL_FTPARSER_PARAM
*param
, MEM_ROOT
*mem_root
)
100 FT_SEG_ITERATOR ftsi
;
101 struct st_mysql_ftparser
*parser
;
102 DBUG_ENTER("_mi_ft_parse");
104 _mi_ft_segiterator_init(info
, keynr
, record
, &ftsi
);
106 ft_parse_init(parsed
, info
->s
->keyinfo
[keynr
].seg
->charset
);
107 parser
= info
->s
->keyinfo
[keynr
].parser
;
108 while (_mi_ft_segiterator(&ftsi
))
111 if (ft_parse(parsed
, (uchar
*)ftsi
.pos
, ftsi
.len
, parser
, param
, mem_root
))
117 FT_WORD
*_mi_ft_parserecord(MI_INFO
*info
, uint keynr
, const uchar
*record
,
121 MYSQL_FTPARSER_PARAM
*param
;
122 DBUG_ENTER("_mi_ft_parserecord");
123 if (! (param
= ftparser_call_initializer(info
, keynr
, 0)))
125 bzero((char*) &ptree
, sizeof(ptree
));
127 if (_mi_ft_parse(&ptree
, info
, keynr
, record
, param
, mem_root
))
130 DBUG_RETURN(ft_linearize(&ptree
, mem_root
));
133 static int _mi_ft_store(MI_INFO
*info
, uint keynr
, uchar
*keybuf
,
134 FT_WORD
*wlist
, my_off_t filepos
)
137 DBUG_ENTER("_mi_ft_store");
139 for (; wlist
->pos
; wlist
++)
141 key_length
=_ft_make_key(info
,keynr
,keybuf
,wlist
,filepos
);
142 if (_mi_ck_write(info
,keynr
,(uchar
*) keybuf
,key_length
))
148 static int _mi_ft_erase(MI_INFO
*info
, uint keynr
, uchar
*keybuf
,
149 FT_WORD
*wlist
, my_off_t filepos
)
151 uint key_length
, err
=0;
152 DBUG_ENTER("_mi_ft_erase");
154 for (; wlist
->pos
; wlist
++)
156 key_length
=_ft_make_key(info
,keynr
,keybuf
,wlist
,filepos
);
157 if (_mi_ck_delete(info
,keynr
,(uchar
*) keybuf
,key_length
))
164 Compares an appropriate parts of two WORD_KEY keys directly out of records
165 returns 1 if they are different
168 #define THOSE_TWO_DAMN_KEYS_ARE_REALLY_DIFFERENT 1
169 #define GEE_THEY_ARE_ABSOLUTELY_IDENTICAL 0
171 int _mi_ft_cmp(MI_INFO
*info
, uint keynr
, const uchar
*rec1
, const uchar
*rec2
)
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
);
179 while (_mi_ft_segiterator(&ftsi1
) && _mi_ft_segiterator(&ftsi2
))
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
);
187 DBUG_RETURN(GEE_THEY_ARE_ABSOLUTELY_IDENTICAL
);
191 /* update a document entry */
193 int _mi_ft_update(MI_INFO
*info
, uint keynr
, uchar
*keybuf
,
194 const uchar
*oldrec
, const uchar
*newrec
, my_off_t pos
)
197 FT_WORD
*oldlist
,*newlist
, *old_word
, *new_word
;
198 CHARSET_INFO
*cs
=info
->s
->keyinfo
[keynr
].seg
->charset
;
201 DBUG_ENTER("_mi_ft_update");
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
,
210 while(old_word
->pos
&& new_word
->pos
)
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);
218 key_length
=_ft_make_key(info
,keynr
,keybuf
,old_word
,pos
);
219 if ((error
=_mi_ck_delete(info
,keynr
,(uchar
*) keybuf
,key_length
)))
224 key_length
=_ft_make_key(info
,keynr
,keybuf
,new_word
,pos
);
225 if ((error
=_mi_ck_write(info
,keynr
,(uchar
*) keybuf
,key_length
)))
228 if (cmp
<=0) old_word
++;
229 if (cmp
>=0) new_word
++;
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
);
237 free_root(&info
->ft_memroot
, MYF(MY_MARK_BLOCKS_FREE
));
242 /* adds a document to the collection */
244 int _mi_ft_add(MI_INFO
*info
, uint keynr
, uchar
*keybuf
, const uchar
*record
,
249 DBUG_ENTER("_mi_ft_add");
250 DBUG_PRINT("enter",("keynr: %d",keynr
));
252 if ((wlist
=_mi_ft_parserecord(info
, keynr
, record
, &info
->ft_memroot
)))
253 error
=_mi_ft_store(info
,keynr
,keybuf
,wlist
,pos
);
255 free_root(&info
->ft_memroot
, MYF(MY_MARK_BLOCKS_FREE
));
256 DBUG_PRINT("exit",("Return: %d",error
));
261 /* removes a document from the collection */
263 int _mi_ft_del(MI_INFO
*info
, uint keynr
, uchar
*keybuf
, const uchar
*record
,
268 DBUG_ENTER("_mi_ft_del");
269 DBUG_PRINT("enter",("keynr: %d",keynr
));
271 if ((wlist
=_mi_ft_parserecord(info
, keynr
, record
, &info
->ft_memroot
)))
272 error
=_mi_ft_erase(info
,keynr
,keybuf
,wlist
,pos
);
274 free_root(&info
->ft_memroot
, MYF(MY_MARK_BLOCKS_FREE
));
275 DBUG_PRINT("exit",("Return: %d",error
));
279 uint
_ft_make_key(MI_INFO
*info
, uint keynr
, uchar
*keybuf
, FT_WORD
*wptr
,
282 uchar buf
[HA_FT_MAXBYTELEN
+16];
283 DBUG_ENTER("_ft_make_key");
285 #if HA_FT_WTYPE == HA_KEYTYPE_FLOAT
287 float weight
=(float) ((filepos
==HA_OFFSET_ERROR
) ? 0 : wptr
->weight
);
288 mi_float4store(buf
,weight
);
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
));
301 convert key value to ft2
304 uint
_mi_ft_convert_to_ft2(MI_INFO
*info
, uint keynr
, uchar
*key
)
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");
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
;
319 get_key_full_length_rdonly(key_length
, key
);
320 while (_mi_ck_delete(info
, keynr
, key
, key_length
) == 0)
324 _mi_ck_delete() will populate info->ft1_to_ft2 with deleted keys
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
))
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
))
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
);
346 DBUG_RETURN(_mi_ck_real_write_btree(info
,
347 info
->s
->keyinfo
+keynr
,
349 &info
->s
->state
.key_root
[keynr
],