1 | /* |
2 | Copyright (c) 2000, 2010, Oracle and/or its affiliates |
3 | |
4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of the GNU General Public License as published by |
6 | the Free Software Foundation; version 2 of the License. |
7 | |
8 | This program is distributed in the hope that it will be useful, |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | GNU General Public License for more details. |
12 | |
13 | You should have received a copy of the GNU General Public License |
14 | along with this program; if not, write to the Free Software |
15 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ |
16 | |
17 | /* The hash functions used for saveing keys */ |
18 | |
19 | #include "heapdef.h" |
20 | #include <m_ctype.h> |
21 | |
22 | /* |
23 | Find out how many rows there is in the given range |
24 | |
25 | SYNOPSIS |
26 | hp_rb_records_in_range() |
27 | info HEAP handler |
28 | inx Index to use |
29 | min_key Min key. Is = 0 if no min range |
30 | max_key Max key. Is = 0 if no max range |
31 | |
32 | NOTES |
33 | min_key.flag can have one of the following values: |
34 | HA_READ_KEY_EXACT Include the key in the range |
35 | HA_READ_AFTER_KEY Don't include key in range |
36 | |
37 | max_key.flag can have one of the following values: |
38 | HA_READ_BEFORE_KEY Don't include key in range |
39 | HA_READ_AFTER_KEY Include all 'end_key' values in the range |
40 | |
41 | RETURN |
42 | HA_POS_ERROR Something is wrong with the index tree. |
43 | 0 There is no matching keys in the given range |
44 | number > 0 There is approximately 'number' matching rows in |
45 | the range. |
46 | */ |
47 | |
48 | ha_rows hp_rb_records_in_range(HP_INFO *info, int inx, key_range *min_key, |
49 | key_range *max_key) |
50 | { |
51 | ha_rows start_pos, end_pos; |
52 | HP_KEYDEF *keyinfo= info->s->keydef + inx; |
53 | TREE *rb_tree = &keyinfo->rb_tree; |
54 | heap_rb_param custom_arg; |
55 | DBUG_ENTER("hp_rb_records_in_range" ); |
56 | |
57 | info->lastinx= inx; |
58 | custom_arg.keyseg= keyinfo->seg; |
59 | custom_arg.search_flag= SEARCH_FIND | SEARCH_SAME; |
60 | if (min_key) |
61 | { |
62 | custom_arg.key_length= hp_rb_pack_key(keyinfo, (uchar*) info->recbuf, |
63 | (uchar*) min_key->key, |
64 | min_key->keypart_map); |
65 | start_pos= tree_record_pos(rb_tree, info->recbuf, min_key->flag, |
66 | &custom_arg); |
67 | } |
68 | else |
69 | { |
70 | start_pos= 0; |
71 | } |
72 | |
73 | if (max_key) |
74 | { |
75 | custom_arg.key_length= hp_rb_pack_key(keyinfo, (uchar*) info->recbuf, |
76 | (uchar*) max_key->key, |
77 | max_key->keypart_map); |
78 | end_pos= tree_record_pos(rb_tree, info->recbuf, max_key->flag, |
79 | &custom_arg); |
80 | } |
81 | else |
82 | { |
83 | end_pos= rb_tree->elements_in_tree + (ha_rows)1; |
84 | } |
85 | |
86 | DBUG_PRINT("info" ,("start_pos: %lu end_pos: %lu" , (ulong) start_pos, |
87 | (ulong) end_pos)); |
88 | if (start_pos == HA_POS_ERROR || end_pos == HA_POS_ERROR) |
89 | DBUG_RETURN(HA_POS_ERROR); |
90 | DBUG_RETURN(end_pos < start_pos ? (ha_rows) 0 : |
91 | (end_pos == start_pos ? (ha_rows) 1 : end_pos - start_pos)); |
92 | } |
93 | |
94 | |
95 | /* Search after a record based on a key */ |
96 | /* Sets info->current_ptr to found record */ |
97 | /* next_flag: Search=0, next=1, prev =2, same =3 */ |
98 | |
99 | uchar *hp_search(HP_INFO *info, HP_KEYDEF *keyinfo, const uchar *key, |
100 | uint nextflag) |
101 | { |
102 | reg1 HASH_INFO *pos,*prev_ptr; |
103 | uint old_nextflag; |
104 | HP_SHARE *share=info->s; |
105 | DBUG_ENTER("hp_search" ); |
106 | old_nextflag=nextflag; |
107 | prev_ptr=0; |
108 | |
109 | if (share->records) |
110 | { |
111 | ulong search_pos= |
112 | hp_mask(hp_hashnr(keyinfo, key), share->blength, share->records); |
113 | pos=hp_find_hash(&keyinfo->block, search_pos); |
114 | if (search_pos != |
115 | hp_mask(pos->hash_of_key, share->blength, share->records)) |
116 | goto not_found; /* Wrong link */ |
117 | do |
118 | { |
119 | if (!hp_key_cmp(keyinfo, pos->ptr_to_rec, key)) |
120 | { |
121 | switch (nextflag) { |
122 | case 0: /* Search after key */ |
123 | DBUG_PRINT("exit" , ("found key at %p" , pos->ptr_to_rec)); |
124 | info->current_hash_ptr=pos; |
125 | DBUG_RETURN(info->current_ptr= pos->ptr_to_rec); |
126 | case 1: /* Search next */ |
127 | if (pos->ptr_to_rec == info->current_ptr) |
128 | nextflag=0; |
129 | break; |
130 | case 2: /* Search previous */ |
131 | if (pos->ptr_to_rec == info->current_ptr) |
132 | { |
133 | my_errno=HA_ERR_KEY_NOT_FOUND; /* If gpos == 0 */ |
134 | info->current_hash_ptr=prev_ptr; |
135 | DBUG_RETURN(info->current_ptr=prev_ptr ? prev_ptr->ptr_to_rec : 0); |
136 | } |
137 | prev_ptr=pos; /* Prev. record found */ |
138 | break; |
139 | case 3: /* Search same */ |
140 | if (pos->ptr_to_rec == info->current_ptr) |
141 | { |
142 | info->current_hash_ptr=pos; |
143 | DBUG_RETURN(info->current_ptr); |
144 | } |
145 | } |
146 | } |
147 | } |
148 | while ((pos=pos->next_key)); |
149 | } |
150 | |
151 | not_found: |
152 | my_errno=HA_ERR_KEY_NOT_FOUND; |
153 | if (nextflag == 2 && ! info->current_ptr) |
154 | { |
155 | /* Do a previous from end */ |
156 | info->current_hash_ptr=prev_ptr; |
157 | DBUG_RETURN(info->current_ptr=prev_ptr ? prev_ptr->ptr_to_rec : 0); |
158 | } |
159 | |
160 | if (old_nextflag && nextflag) |
161 | my_errno=HA_ERR_RECORD_CHANGED; /* Didn't find old record */ |
162 | DBUG_PRINT("exit" ,("Error: %d" ,my_errno)); |
163 | info->current_hash_ptr=0; |
164 | DBUG_RETURN((info->current_ptr= 0)); |
165 | } |
166 | |
167 | |
168 | /* |
169 | Search next after last read; Assumes that the table hasn't changed |
170 | since last read ! |
171 | */ |
172 | |
173 | uchar *hp_search_next(HP_INFO *info, HP_KEYDEF *keyinfo, const uchar *key, |
174 | HASH_INFO *pos) |
175 | { |
176 | DBUG_ENTER("hp_search_next" ); |
177 | |
178 | while ((pos= pos->next_key)) |
179 | { |
180 | if (! hp_key_cmp(keyinfo, pos->ptr_to_rec, key)) |
181 | { |
182 | info->current_hash_ptr=pos; |
183 | DBUG_RETURN (info->current_ptr= pos->ptr_to_rec); |
184 | } |
185 | } |
186 | my_errno=HA_ERR_KEY_NOT_FOUND; |
187 | DBUG_PRINT("exit" ,("Error: %d" ,my_errno)); |
188 | info->current_hash_ptr=0; |
189 | DBUG_RETURN ((info->current_ptr= 0)); |
190 | } |
191 | |
192 | |
193 | /* |
194 | Change |
195 | next_link -> ... -> X -> pos |
196 | to |
197 | next_link -> ... -> X -> newlink |
198 | */ |
199 | |
200 | void hp_movelink(HASH_INFO *pos, HASH_INFO *next_link, HASH_INFO *newlink) |
201 | { |
202 | HASH_INFO *old_link; |
203 | do |
204 | { |
205 | old_link=next_link; |
206 | } |
207 | while ((next_link=next_link->next_key) != pos); |
208 | old_link->next_key=newlink; |
209 | return; |
210 | } |
211 | |
212 | #ifndef NEW_HASH_FUNCTION |
213 | |
214 | /* Calc hashvalue for a key */ |
215 | |
216 | ulong hp_hashnr(register HP_KEYDEF *keydef, register const uchar *key) |
217 | { |
218 | /*register*/ |
219 | ulong nr=1, nr2=4; |
220 | HA_KEYSEG *seg,*endseg; |
221 | |
222 | for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++) |
223 | { |
224 | uchar *pos=(uchar*) key; |
225 | key+=seg->length; |
226 | if (seg->null_bit) |
227 | { |
228 | key++; /* Skip null byte */ |
229 | if (*pos) /* Found null */ |
230 | { |
231 | nr^= (nr << 1) | 1; |
232 | /* Add key pack length (2) to key for VARCHAR segments */ |
233 | if (seg->type == HA_KEYTYPE_VARTEXT1) |
234 | key+= 2; |
235 | continue; |
236 | } |
237 | pos++; |
238 | } |
239 | if (seg->type == HA_KEYTYPE_TEXT) |
240 | { |
241 | CHARSET_INFO *cs= seg->charset; |
242 | size_t length= seg->length; |
243 | if (cs->mbmaxlen > 1) |
244 | { |
245 | size_t char_length; |
246 | char_length= my_charpos(cs, pos, pos + length, length/cs->mbmaxlen); |
247 | set_if_smaller(length, char_length); |
248 | } |
249 | cs->coll->hash_sort(cs, pos, length, &nr, &nr2); |
250 | } |
251 | else if (seg->type == HA_KEYTYPE_VARTEXT1) /* Any VARCHAR segments */ |
252 | { |
253 | CHARSET_INFO *cs= seg->charset; |
254 | size_t pack_length= 2; /* Key packing is constant */ |
255 | size_t length= uint2korr(pos); |
256 | if (cs->mbmaxlen > 1) |
257 | { |
258 | size_t char_length; |
259 | char_length= my_charpos(cs, pos +pack_length, |
260 | pos +pack_length + length, |
261 | seg->length/cs->mbmaxlen); |
262 | set_if_smaller(length, char_length); |
263 | } |
264 | cs->coll->hash_sort(cs, pos+pack_length, length, &nr, &nr2); |
265 | key+= pack_length; |
266 | } |
267 | else |
268 | { |
269 | for (; pos < (uchar*) key ; pos++) |
270 | { |
271 | nr^=(ulong) ((((uint) nr & 63)+nr2)*((uint) *pos)) + (nr << 8); |
272 | nr2+=3; |
273 | } |
274 | } |
275 | } |
276 | #ifdef ONLY_FOR_HASH_DEBUGGING |
277 | DBUG_PRINT("exit" , ("hash: 0x%lx" , nr)); |
278 | #endif |
279 | return((ulong) nr); |
280 | } |
281 | |
282 | /* Calc hashvalue for a key in a record */ |
283 | |
284 | ulong hp_rec_hashnr(register HP_KEYDEF *keydef, register const uchar *rec) |
285 | { |
286 | ulong nr=1, nr2=4; |
287 | HA_KEYSEG *seg,*endseg; |
288 | |
289 | for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++) |
290 | { |
291 | uchar *pos=(uchar*) rec+seg->start,*end=pos+seg->length; |
292 | if (seg->null_bit) |
293 | { |
294 | if (rec[seg->null_pos] & seg->null_bit) |
295 | { |
296 | nr^= (nr << 1) | 1; |
297 | continue; |
298 | } |
299 | } |
300 | if (seg->type == HA_KEYTYPE_TEXT) |
301 | { |
302 | CHARSET_INFO *cs= seg->charset; |
303 | size_t char_length= seg->length; |
304 | if (cs->mbmaxlen > 1) |
305 | { |
306 | char_length= my_charpos(cs, pos, pos + char_length, |
307 | char_length / cs->mbmaxlen); |
308 | set_if_smaller(char_length, seg->length); /* QQ: ok to remove? */ |
309 | } |
310 | cs->coll->hash_sort(cs, pos, char_length, &nr, &nr2); |
311 | } |
312 | else if (seg->type == HA_KEYTYPE_VARTEXT1) /* Any VARCHAR segments */ |
313 | { |
314 | CHARSET_INFO *cs= seg->charset; |
315 | size_t pack_length= seg->bit_start; |
316 | size_t length= (pack_length == 1 ? (size_t) *(uchar*) pos : uint2korr(pos)); |
317 | if (cs->mbmaxlen > 1) |
318 | { |
319 | size_t char_length; |
320 | char_length= my_charpos(cs, pos + pack_length, |
321 | pos + pack_length + length, |
322 | seg->length/cs->mbmaxlen); |
323 | set_if_smaller(length, char_length); |
324 | } |
325 | else |
326 | set_if_smaller(length, seg->length); |
327 | cs->coll->hash_sort(cs, pos+pack_length, length, &nr, &nr2); |
328 | } |
329 | else |
330 | { |
331 | if (seg->type == HA_KEYTYPE_BIT && seg->bit_length) |
332 | { |
333 | uchar bits= get_rec_bits(rec + seg->bit_pos, |
334 | seg->bit_start, seg->bit_length); |
335 | nr^=(ulong) ((((uint) nr & 63)+nr2)*((uint) bits))+ (nr << 8); |
336 | nr2+=3; |
337 | end--; |
338 | } |
339 | |
340 | for (; pos < end ; pos++) |
341 | { |
342 | nr^=(ulong) ((((uint) nr & 63)+nr2)*((uint) *pos))+ (nr << 8); |
343 | nr2+=3; |
344 | } |
345 | } |
346 | } |
347 | #ifdef ONLY_FOR_HASH_DEBUGGING |
348 | DBUG_PRINT("exit" , ("hash: 0x%lx" , nr)); |
349 | #endif |
350 | return(nr); |
351 | } |
352 | |
353 | #else |
354 | |
355 | /* |
356 | * Fowler/Noll/Vo hash |
357 | * |
358 | * The basis of the hash algorithm was taken from an idea sent by email to the |
359 | * IEEE Posix P1003.2 mailing list from Phong Vo (kpv@research.att.com) and |
360 | * Glenn Fowler (gsf@research.att.com). Landon Curt Noll (chongo@toad.com) |
361 | * later improved on their algorithm. |
362 | * |
363 | * The magic is in the interesting relationship between the special prime |
364 | * 16777619 (2^24 + 403) and 2^32 and 2^8. |
365 | * |
366 | * This hash produces the fewest collisions of any function that we've seen so |
367 | * far, and works well on both numbers and strings. |
368 | */ |
369 | |
370 | ulong hp_hashnr(register HP_KEYDEF *keydef, register const uchar *key) |
371 | { |
372 | /* |
373 | Note, if a key consists of a combination of numeric and |
374 | a text columns, it most likely won't work well. |
375 | Making text columns work with NEW_HASH_FUNCTION |
376 | needs also changes in strings/ctype-xxx.c. |
377 | */ |
378 | ulong nr= 1, nr2= 4; |
379 | HA_KEYSEG *seg,*endseg; |
380 | |
381 | for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++) |
382 | { |
383 | uchar *pos=(uchar*) key; |
384 | key+=seg->length; |
385 | if (seg->null_bit) |
386 | { |
387 | key++; |
388 | if (*pos) |
389 | { |
390 | nr^= (nr << 1) | 1; |
391 | /* Add key pack length (2) to key for VARCHAR segments */ |
392 | if (seg->type == HA_KEYTYPE_VARTEXT1) |
393 | key+= 2; |
394 | continue; |
395 | } |
396 | pos++; |
397 | } |
398 | if (seg->type == HA_KEYTYPE_TEXT) |
399 | { |
400 | seg->charset->coll->hash_sort(seg->charset, pos, ((uchar*)key)-pos, |
401 | &nr, &nr2); |
402 | } |
403 | else if (seg->type == HA_KEYTYPE_VARTEXT1) /* Any VARCHAR segments */ |
404 | { |
405 | uint pack_length= 2; /* Key packing is constant */ |
406 | uint length= uint2korr(pos); |
407 | seg->charset->coll->hash_sort(seg->charset, pos+pack_length, length, |
408 | &nr, &nr2); |
409 | key+= pack_length; |
410 | } |
411 | else |
412 | { |
413 | for ( ; pos < (uchar*) key ; pos++) |
414 | { |
415 | nr *=16777619; |
416 | nr ^=(uint) *pos; |
417 | } |
418 | } |
419 | } |
420 | #ifdef ONLY_FOR_HASH_DEBUGGING |
421 | DBUG_PRINT("exit" , ("hash: 0x%lx" , nr)); |
422 | #endif |
423 | return(nr); |
424 | } |
425 | |
426 | /* Calc hashvalue for a key in a record */ |
427 | |
428 | ulong hp_rec_hashnr(register HP_KEYDEF *keydef, register const uchar *rec) |
429 | { |
430 | ulong nr= 1, nr2= 4; |
431 | HA_KEYSEG *seg,*endseg; |
432 | |
433 | for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++) |
434 | { |
435 | uchar *pos=(uchar*) rec+seg->start; |
436 | if (seg->null_bit) |
437 | { |
438 | if (rec[seg->null_pos] & seg->null_bit) |
439 | { |
440 | nr^= (nr << 1) | 1; |
441 | continue; |
442 | } |
443 | } |
444 | if (seg->type == HA_KEYTYPE_TEXT) |
445 | { |
446 | uint char_length= seg->length; /* TODO: fix to use my_charpos() */ |
447 | seg->charset->coll->hash_sort(seg->charset, pos, char_length, |
448 | &nr, &nr2); |
449 | } |
450 | else if (seg->type == HA_KEYTYPE_VARTEXT1) /* Any VARCHAR segments */ |
451 | { |
452 | uint pack_length= seg->bit_start; |
453 | uint length= (pack_length == 1 ? (uint) *(uchar*) pos : uint2korr(pos)); |
454 | seg->charset->coll->hash_sort(seg->charset, pos+pack_length, |
455 | length, &nr, &nr2); |
456 | } |
457 | else |
458 | { |
459 | uchar *end= pos+seg->length; |
460 | if (seg->type == HA_KEYTYPE_BIT && seg->bit_length) |
461 | { |
462 | uchar bits= get_rec_bits(rec + seg->bit_pos, |
463 | seg->bit_start, seg->bit_length); |
464 | nr *=16777619; |
465 | nr ^=(uint) bits; |
466 | end--; |
467 | } |
468 | for ( ; pos < end ; pos++) |
469 | { |
470 | nr *=16777619; |
471 | nr ^=(uint) *pos; |
472 | } |
473 | } |
474 | } |
475 | #ifdef ONLY_FOR_HASH_DEBUGGING |
476 | DBUG_PRINT("exit" , ("hash: 0x%lx" , nr)); |
477 | #endif |
478 | return(nr); |
479 | } |
480 | |
481 | #endif |
482 | |
483 | |
484 | /* |
485 | Compare keys for two records. Returns 0 if they are identical |
486 | |
487 | SYNOPSIS |
488 | hp_rec_key_cmp() |
489 | keydef Key definition |
490 | rec1 Record to compare |
491 | rec2 Other record to compare |
492 | |
493 | NOTES |
494 | diff_if_only_endspace_difference is used to allow us to insert |
495 | 'a' and 'a ' when there is an an unique key. |
496 | |
497 | RETURN |
498 | 0 Key is identical |
499 | <> 0 Key differes |
500 | */ |
501 | |
502 | int hp_rec_key_cmp(HP_KEYDEF *keydef, const uchar *rec1, const uchar *rec2) |
503 | { |
504 | HA_KEYSEG *seg,*endseg; |
505 | |
506 | for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++) |
507 | { |
508 | if (seg->null_bit) |
509 | { |
510 | if ((rec1[seg->null_pos] & seg->null_bit) != |
511 | (rec2[seg->null_pos] & seg->null_bit)) |
512 | return 1; |
513 | if (rec1[seg->null_pos] & seg->null_bit) |
514 | continue; |
515 | } |
516 | if (seg->type == HA_KEYTYPE_TEXT) |
517 | { |
518 | CHARSET_INFO *cs= seg->charset; |
519 | size_t char_length1; |
520 | size_t char_length2; |
521 | uchar *pos1= (uchar*)rec1 + seg->start; |
522 | uchar *pos2= (uchar*)rec2 + seg->start; |
523 | if (cs->mbmaxlen > 1) |
524 | { |
525 | size_t char_length= seg->length / cs->mbmaxlen; |
526 | char_length1= my_charpos(cs, pos1, pos1 + seg->length, char_length); |
527 | set_if_smaller(char_length1, seg->length); |
528 | char_length2= my_charpos(cs, pos2, pos2 + seg->length, char_length); |
529 | set_if_smaller(char_length2, seg->length); |
530 | } |
531 | else |
532 | { |
533 | char_length1= char_length2= seg->length; |
534 | } |
535 | if (seg->charset->coll->strnncollsp(seg->charset, |
536 | pos1,char_length1, |
537 | pos2,char_length2)) |
538 | return 1; |
539 | } |
540 | else if (seg->type == HA_KEYTYPE_VARTEXT1) /* Any VARCHAR segments */ |
541 | { |
542 | uchar *pos1= (uchar*) rec1 + seg->start; |
543 | uchar *pos2= (uchar*) rec2 + seg->start; |
544 | size_t char_length1, char_length2; |
545 | size_t pack_length= seg->bit_start; |
546 | CHARSET_INFO *cs= seg->charset; |
547 | if (pack_length == 1) |
548 | { |
549 | char_length1= (size_t) *(uchar*) pos1++; |
550 | char_length2= (size_t) *(uchar*) pos2++; |
551 | } |
552 | else |
553 | { |
554 | char_length1= uint2korr(pos1); |
555 | char_length2= uint2korr(pos2); |
556 | pos1+= 2; |
557 | pos2+= 2; |
558 | } |
559 | if (cs->mbmaxlen > 1) |
560 | { |
561 | size_t safe_length1= char_length1; |
562 | size_t safe_length2= char_length2; |
563 | size_t char_length= seg->length / cs->mbmaxlen; |
564 | char_length1= my_charpos(cs, pos1, pos1 + char_length1, char_length); |
565 | set_if_smaller(char_length1, safe_length1); |
566 | char_length2= my_charpos(cs, pos2, pos2 + char_length2, char_length); |
567 | set_if_smaller(char_length2, safe_length2); |
568 | } |
569 | else |
570 | { |
571 | set_if_smaller(char_length1, seg->length); |
572 | set_if_smaller(char_length2, seg->length); |
573 | } |
574 | |
575 | if (cs->coll->strnncollsp(seg->charset, |
576 | pos1, char_length1, |
577 | pos2, char_length2)) |
578 | return 1; |
579 | } |
580 | else |
581 | { |
582 | uint dec= 0; |
583 | if (seg->type == HA_KEYTYPE_BIT && seg->bit_length) |
584 | { |
585 | uchar bits1= get_rec_bits(rec1 + seg->bit_pos, |
586 | seg->bit_start, seg->bit_length); |
587 | uchar bits2= get_rec_bits(rec2 + seg->bit_pos, |
588 | seg->bit_start, seg->bit_length); |
589 | if (bits1 != bits2) |
590 | return 1; |
591 | dec= 1; |
592 | } |
593 | if (bcmp(rec1 + seg->start, rec2 + seg->start, seg->length - dec)) |
594 | return 1; |
595 | } |
596 | } |
597 | return 0; |
598 | } |
599 | |
600 | /* Compare a key in a record to a whole key */ |
601 | |
602 | int hp_key_cmp(HP_KEYDEF *keydef, const uchar *rec, const uchar *key) |
603 | { |
604 | HA_KEYSEG *seg,*endseg; |
605 | |
606 | for (seg=keydef->seg,endseg=seg+keydef->keysegs ; |
607 | seg < endseg ; |
608 | key+= (seg++)->length) |
609 | { |
610 | if (seg->null_bit) |
611 | { |
612 | int found_null= MY_TEST(rec[seg->null_pos] & seg->null_bit); |
613 | if (found_null != (int) *key++) |
614 | return 1; |
615 | if (found_null) |
616 | { |
617 | /* Add key pack length (2) to key for VARCHAR segments */ |
618 | if (seg->type == HA_KEYTYPE_VARTEXT1) |
619 | key+= 2; |
620 | continue; |
621 | } |
622 | } |
623 | if (seg->type == HA_KEYTYPE_TEXT) |
624 | { |
625 | CHARSET_INFO *cs= seg->charset; |
626 | size_t char_length_key; |
627 | size_t char_length_rec; |
628 | uchar *pos= (uchar*) rec + seg->start; |
629 | if (cs->mbmaxlen > 1) |
630 | { |
631 | size_t char_length= seg->length / cs->mbmaxlen; |
632 | char_length_key= my_charpos(cs, key, key + seg->length, char_length); |
633 | set_if_smaller(char_length_key, seg->length); |
634 | char_length_rec= my_charpos(cs, pos, pos + seg->length, char_length); |
635 | set_if_smaller(char_length_rec, seg->length); |
636 | } |
637 | else |
638 | { |
639 | char_length_key= seg->length; |
640 | char_length_rec= seg->length; |
641 | } |
642 | |
643 | if (seg->charset->coll->strnncollsp(seg->charset, |
644 | (uchar*) pos, char_length_rec, |
645 | (uchar*) key, char_length_key)) |
646 | return 1; |
647 | } |
648 | else if (seg->type == HA_KEYTYPE_VARTEXT1) /* Any VARCHAR segments */ |
649 | { |
650 | uchar *pos= (uchar*) rec + seg->start; |
651 | CHARSET_INFO *cs= seg->charset; |
652 | size_t pack_length= seg->bit_start; |
653 | size_t char_length_rec= (pack_length == 1 ? (size_t) *(uchar*) pos : |
654 | uint2korr(pos)); |
655 | /* Key segments are always packed with 2 bytes */ |
656 | size_t char_length_key= uint2korr(key); |
657 | pos+= pack_length; |
658 | key+= 2; /* skip key pack length */ |
659 | if (cs->mbmaxlen > 1) |
660 | { |
661 | size_t char_length1, char_length2; |
662 | char_length1= char_length2= seg->length / cs->mbmaxlen; |
663 | char_length1= my_charpos(cs, key, key + char_length_key, char_length1); |
664 | set_if_smaller(char_length_key, char_length1); |
665 | char_length2= my_charpos(cs, pos, pos + char_length_rec, char_length2); |
666 | set_if_smaller(char_length_rec, char_length2); |
667 | } |
668 | else |
669 | set_if_smaller(char_length_rec, seg->length); |
670 | |
671 | if (cs->coll->strnncollsp(seg->charset, |
672 | (uchar*) pos, char_length_rec, |
673 | (uchar*) key, char_length_key)) |
674 | return 1; |
675 | } |
676 | else |
677 | { |
678 | uint dec= 0; |
679 | if (seg->type == HA_KEYTYPE_BIT && seg->bit_length) |
680 | { |
681 | uchar bits= get_rec_bits(rec + seg->bit_pos, |
682 | seg->bit_start, seg->bit_length); |
683 | if (bits != (*key)) |
684 | return 1; |
685 | dec= 1; |
686 | key++; |
687 | } |
688 | |
689 | if (bcmp(rec + seg->start, key, seg->length - dec)) |
690 | return 1; |
691 | } |
692 | } |
693 | return 0; |
694 | } |
695 | |
696 | |
697 | /* Copy a key from a record to a keybuffer */ |
698 | |
699 | void hp_make_key(HP_KEYDEF *keydef, uchar *key, const uchar *rec) |
700 | { |
701 | HA_KEYSEG *seg,*endseg; |
702 | |
703 | for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++) |
704 | { |
705 | CHARSET_INFO *cs= seg->charset; |
706 | size_t char_length= seg->length; |
707 | uchar *pos= (uchar*) rec + seg->start; |
708 | if (seg->null_bit) |
709 | *key++= MY_TEST(rec[seg->null_pos] & seg->null_bit); |
710 | if (cs->mbmaxlen > 1) |
711 | { |
712 | char_length= my_charpos(cs, pos, pos + seg->length, |
713 | char_length / cs->mbmaxlen); |
714 | set_if_smaller(char_length, seg->length); /* QQ: ok to remove? */ |
715 | } |
716 | if (seg->type == HA_KEYTYPE_VARTEXT1) |
717 | char_length+= seg->bit_start; /* Copy also length */ |
718 | else if (seg->type == HA_KEYTYPE_BIT && seg->bit_length) |
719 | { |
720 | *key++= get_rec_bits(rec + seg->bit_pos, |
721 | seg->bit_start, seg->bit_length); |
722 | char_length--; |
723 | } |
724 | memcpy(key,rec+seg->start,(size_t) char_length); |
725 | key+= char_length; |
726 | } |
727 | } |
728 | |
729 | #define FIX_LENGTH(cs, pos, length, char_length) \ |
730 | do { \ |
731 | if (length > char_length) \ |
732 | char_length= my_charpos(cs, pos, pos+length, char_length); \ |
733 | set_if_smaller(char_length,length); \ |
734 | } while(0) |
735 | |
736 | |
737 | uint hp_rb_make_key(HP_KEYDEF *keydef, uchar *key, |
738 | const uchar *rec, uchar *recpos) |
739 | { |
740 | uchar *start_key= key; |
741 | HA_KEYSEG *seg, *endseg; |
742 | |
743 | for (seg= keydef->seg, endseg= seg + keydef->keysegs; seg < endseg; seg++) |
744 | { |
745 | size_t char_length; |
746 | if (seg->null_bit) |
747 | { |
748 | if (!(*key++= 1 - MY_TEST(rec[seg->null_pos] & seg->null_bit))) |
749 | continue; |
750 | } |
751 | if (seg->flag & HA_SWAP_KEY) |
752 | { |
753 | uint length= seg->length; |
754 | uchar *pos= (uchar*) rec + seg->start; |
755 | DBUG_ASSERT(seg->type != HA_KEYTYPE_BIT); |
756 | |
757 | #ifdef HAVE_ISNAN |
758 | if (seg->type == HA_KEYTYPE_FLOAT) |
759 | { |
760 | float nr; |
761 | float4get(nr, pos); |
762 | if (isnan(nr)) |
763 | { |
764 | /* Replace NAN with zero */ |
765 | bzero(key, length); |
766 | key+= length; |
767 | continue; |
768 | } |
769 | } |
770 | else if (seg->type == HA_KEYTYPE_DOUBLE) |
771 | { |
772 | double nr; |
773 | float8get(nr, pos); |
774 | if (isnan(nr)) |
775 | { |
776 | bzero(key, length); |
777 | key+= length; |
778 | continue; |
779 | } |
780 | } |
781 | #endif |
782 | pos+= length; |
783 | while (length--) |
784 | { |
785 | *key++= *--pos; |
786 | } |
787 | continue; |
788 | } |
789 | |
790 | if (seg->flag & HA_VAR_LENGTH_PART) |
791 | { |
792 | uchar *pos= (uchar*) rec + seg->start; |
793 | size_t length= seg->length; |
794 | size_t pack_length= seg->bit_start; |
795 | size_t tmp_length= (pack_length == 1 ? (uint) *(uchar*) pos : |
796 | uint2korr(pos)); |
797 | CHARSET_INFO *cs= seg->charset; |
798 | char_length= length/cs->mbmaxlen; |
799 | |
800 | pos+= pack_length; /* Skip VARCHAR length */ |
801 | set_if_smaller(length,tmp_length); |
802 | FIX_LENGTH(cs, pos, length, char_length); |
803 | store_key_length_inc(key,char_length); |
804 | memcpy((uchar*) key,(uchar*) pos,(size_t) char_length); |
805 | key+= char_length; |
806 | continue; |
807 | } |
808 | |
809 | char_length= seg->length; |
810 | if (seg->charset->mbmaxlen > 1) |
811 | { |
812 | char_length= my_charpos(seg->charset, |
813 | rec + seg->start, rec + seg->start + char_length, |
814 | char_length / seg->charset->mbmaxlen); |
815 | set_if_smaller(char_length, seg->length); /* QQ: ok to remove? */ |
816 | if (char_length < seg->length) |
817 | seg->charset->cset->fill(seg->charset, (char*) key + char_length, |
818 | seg->length - char_length, ' '); |
819 | } |
820 | if (seg->type == HA_KEYTYPE_BIT && seg->bit_length) |
821 | { |
822 | *key++= get_rec_bits(rec + seg->bit_pos, |
823 | seg->bit_start, seg->bit_length); |
824 | char_length--; |
825 | } |
826 | memcpy(key, rec + seg->start, (size_t) char_length); |
827 | key+= seg->length; |
828 | } |
829 | memcpy(key, &recpos, sizeof(uchar*)); |
830 | return (uint) (key - start_key); |
831 | } |
832 | |
833 | |
834 | uint hp_rb_pack_key(HP_KEYDEF *keydef, uchar *key, const uchar *old, |
835 | key_part_map keypart_map) |
836 | { |
837 | HA_KEYSEG *seg, *endseg; |
838 | uchar *start_key= key; |
839 | |
840 | for (seg= keydef->seg, endseg= seg + keydef->keysegs; |
841 | seg < endseg && keypart_map; old+= seg->length, seg++) |
842 | { |
843 | size_t char_length; |
844 | keypart_map>>= 1; |
845 | if (seg->null_bit) |
846 | { |
847 | /* Convert NULL from MySQL representation into HEAP's. */ |
848 | if (!(*key++= (char) 1 - *old++)) |
849 | { |
850 | /* Add key pack length (2) to key for VARCHAR segments */ |
851 | if (seg->type == HA_KEYTYPE_VARTEXT1) |
852 | old+= 2; |
853 | continue; |
854 | } |
855 | } |
856 | if (seg->flag & HA_SWAP_KEY) |
857 | { |
858 | uint length= seg->length; |
859 | uchar *pos= (uchar*) old + length; |
860 | |
861 | while (length--) |
862 | { |
863 | *key++= *--pos; |
864 | } |
865 | continue; |
866 | } |
867 | if (seg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART)) |
868 | { |
869 | /* Length of key-part used with heap_rkey() always 2 */ |
870 | size_t tmp_length=uint2korr(old); |
871 | size_t length= seg->length; |
872 | CHARSET_INFO *cs= seg->charset; |
873 | char_length= length/cs->mbmaxlen; |
874 | |
875 | old+= 2; |
876 | set_if_smaller(length,tmp_length); /* Safety */ |
877 | FIX_LENGTH(cs, old, length, char_length); |
878 | store_key_length_inc(key,char_length); |
879 | memcpy((uchar*) key, old,(size_t) char_length); |
880 | key+= char_length; |
881 | continue; |
882 | } |
883 | char_length= seg->length; |
884 | if (seg->charset->mbmaxlen > 1) |
885 | { |
886 | char_length= my_charpos(seg->charset, old, old+char_length, |
887 | char_length / seg->charset->mbmaxlen); |
888 | set_if_smaller(char_length, seg->length); /* QQ: ok to remove? */ |
889 | if (char_length < seg->length) |
890 | seg->charset->cset->fill(seg->charset, (char*) key + char_length, |
891 | seg->length - char_length, ' '); |
892 | } |
893 | memcpy(key, old, (size_t) char_length); |
894 | key+= seg->length; |
895 | } |
896 | return (uint) (key - start_key); |
897 | } |
898 | |
899 | |
900 | uint hp_rb_key_length(HP_KEYDEF *keydef, |
901 | const uchar *key __attribute__((unused))) |
902 | { |
903 | return keydef->length; |
904 | } |
905 | |
906 | |
907 | uint hp_rb_null_key_length(HP_KEYDEF *keydef, const uchar *key) |
908 | { |
909 | const uchar *start_key= key; |
910 | HA_KEYSEG *seg, *endseg; |
911 | |
912 | for (seg= keydef->seg, endseg= seg + keydef->keysegs; seg < endseg; seg++) |
913 | { |
914 | if (seg->null_bit && !*key++) |
915 | continue; |
916 | key+= seg->length; |
917 | } |
918 | return (uint) (key - start_key); |
919 | } |
920 | |
921 | |
922 | uint hp_rb_var_key_length(HP_KEYDEF *keydef, const uchar *key) |
923 | { |
924 | const uchar *start_key= key; |
925 | HA_KEYSEG *seg, *endseg; |
926 | |
927 | for (seg= keydef->seg, endseg= seg + keydef->keysegs; seg < endseg; seg++) |
928 | { |
929 | uint length= seg->length; |
930 | if (seg->null_bit && !*key++) |
931 | continue; |
932 | if (seg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART)) |
933 | { |
934 | get_key_length(length, key); |
935 | } |
936 | key+= length; |
937 | } |
938 | return (uint) (key - start_key); |
939 | } |
940 | |
941 | |
942 | /* |
943 | Test if any of the key parts are NULL. |
944 | Return: |
945 | 1 if any of the key parts was NULL |
946 | 0 otherwise |
947 | */ |
948 | |
949 | my_bool hp_if_null_in_key(HP_KEYDEF *keydef, const uchar *record) |
950 | { |
951 | HA_KEYSEG *seg,*endseg; |
952 | for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++) |
953 | { |
954 | if (seg->null_bit && (record[seg->null_pos] & seg->null_bit)) |
955 | return 1; |
956 | } |
957 | return 0; |
958 | } |
959 | |
960 | |
961 | /* |
962 | Update auto_increment info |
963 | |
964 | SYNOPSIS |
965 | update_auto_increment() |
966 | info MyISAM handler |
967 | record Row to update |
968 | |
969 | IMPLEMENTATION |
970 | Only replace the auto_increment value if it is higher than the previous |
971 | one. For signed columns we don't update the auto increment value if it's |
972 | less than zero. |
973 | */ |
974 | |
975 | void heap_update_auto_increment(HP_INFO *info, const uchar *record) |
976 | { |
977 | ulonglong value= 0; /* Store unsigned values here */ |
978 | longlong s_value= 0; /* Store signed values here */ |
979 | |
980 | HA_KEYSEG *keyseg= info->s->keydef[info->s->auto_key - 1].seg; |
981 | const uchar *key= (uchar*) record + keyseg->start; |
982 | |
983 | switch (info->s->auto_key_type) { |
984 | case HA_KEYTYPE_INT8: |
985 | s_value= (longlong) *(char*)key; |
986 | break; |
987 | case HA_KEYTYPE_BINARY: |
988 | value=(ulonglong) *(uchar*) key; |
989 | break; |
990 | case HA_KEYTYPE_SHORT_INT: |
991 | s_value= (longlong) sint2korr(key); |
992 | break; |
993 | case HA_KEYTYPE_USHORT_INT: |
994 | value=(ulonglong) uint2korr(key); |
995 | break; |
996 | case HA_KEYTYPE_LONG_INT: |
997 | s_value= (longlong) sint4korr(key); |
998 | break; |
999 | case HA_KEYTYPE_ULONG_INT: |
1000 | value=(ulonglong) uint4korr(key); |
1001 | break; |
1002 | case HA_KEYTYPE_INT24: |
1003 | s_value= (longlong) sint3korr(key); |
1004 | break; |
1005 | case HA_KEYTYPE_UINT24: |
1006 | value=(ulonglong) uint3korr(key); |
1007 | break; |
1008 | case HA_KEYTYPE_FLOAT: /* This shouldn't be used */ |
1009 | { |
1010 | float f_1; |
1011 | float4get(f_1,key); |
1012 | /* Ignore negative values */ |
1013 | value = (f_1 < (float) 0.0) ? 0 : (ulonglong) f_1; |
1014 | break; |
1015 | } |
1016 | case HA_KEYTYPE_DOUBLE: /* This shouldn't be used */ |
1017 | { |
1018 | double f_1; |
1019 | float8get(f_1,key); |
1020 | /* Ignore negative values */ |
1021 | value = (f_1 < 0.0) ? 0 : (ulonglong) f_1; |
1022 | break; |
1023 | } |
1024 | case HA_KEYTYPE_LONGLONG: |
1025 | s_value= sint8korr(key); |
1026 | break; |
1027 | case HA_KEYTYPE_ULONGLONG: |
1028 | value= uint8korr(key); |
1029 | break; |
1030 | default: |
1031 | DBUG_ASSERT(0); |
1032 | value=0; /* Error */ |
1033 | break; |
1034 | } |
1035 | |
1036 | /* |
1037 | The following code works becasue if s_value < 0 then value is 0 |
1038 | and if s_value == 0 then value will contain either s_value or the |
1039 | correct value. |
1040 | */ |
1041 | set_if_bigger(info->s->auto_increment, |
1042 | (s_value > 0) ? (ulonglong) s_value : value); |
1043 | } |
1044 | |