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
48ha_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
99uchar *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
151not_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
173uchar *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
200void 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
216ulong 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
284ulong 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
370ulong 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
428ulong 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
502int 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
602int 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
699void 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
737uint 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
834uint 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
900uint hp_rb_key_length(HP_KEYDEF *keydef,
901 const uchar *key __attribute__((unused)))
902{
903 return keydef->length;
904}
905
906
907uint 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
922uint 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
949my_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
975void 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