1 /* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
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, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 /* The hash functions used for saveing keys */
24
25 #include <inttypes.h>
26 #include <sys/types.h>
27
28 #include <algorithm>
29 #include <cmath>
30
31 #include "m_ctype.h"
32 #include "my_byteorder.h"
33 #include "my_compiler.h"
34 #include "my_dbug.h"
35 #include "my_inttypes.h"
36 #include "my_macros.h"
37 #include "storage/heap/heapdef.h"
38
39 /*
40 Find out how many rows there is in the given range
41
42 SYNOPSIS
43 hp_rb_records_in_range()
44 info HEAP handler
45 inx Index to use
46 min_key Min key. Is = 0 if no min range
47 max_key Max key. Is = 0 if no max range
48
49 NOTES
50 min_key.flag can have one of the following values:
51 HA_READ_KEY_EXACT Include the key in the range
52 HA_READ_AFTER_KEY Don't include key in range
53
54 max_key.flag can have one of the following values:
55 HA_READ_BEFORE_KEY Don't include key in range
56 HA_READ_AFTER_KEY Include all 'end_key' values in the range
57
58 RETURN
59 HA_POS_ERROR Something is wrong with the index tree.
60 0 There is no matching keys in the given range
61 number > 0 There is approximately 'number' matching rows in
62 the range.
63 */
64
hp_rb_records_in_range(HP_INFO * info,int inx,key_range * min_key,key_range * max_key)65 ha_rows hp_rb_records_in_range(HP_INFO *info, int inx, key_range *min_key,
66 key_range *max_key) {
67 ha_rows start_pos, end_pos;
68 HP_KEYDEF *keyinfo = info->s->keydef + inx;
69 TREE *rb_tree = &keyinfo->rb_tree;
70 heap_rb_param custom_arg;
71 DBUG_TRACE;
72
73 info->lastinx = inx;
74 custom_arg.keyseg = keyinfo->seg;
75 custom_arg.search_flag = SEARCH_FIND | SEARCH_SAME;
76 if (min_key) {
77 custom_arg.key_length = hp_rb_pack_key(keyinfo, info->recbuf, min_key->key,
78 min_key->keypart_map);
79 start_pos =
80 tree_record_pos(rb_tree, info->recbuf, min_key->flag, &custom_arg);
81 } else {
82 start_pos = 0;
83 }
84
85 if (max_key) {
86 custom_arg.key_length = hp_rb_pack_key(keyinfo, info->recbuf, max_key->key,
87 max_key->keypart_map);
88 end_pos =
89 tree_record_pos(rb_tree, info->recbuf, max_key->flag, &custom_arg);
90 } else {
91 end_pos = rb_tree->elements_in_tree + (ha_rows)1;
92 }
93
94 DBUG_PRINT("info", ("start_pos: %lu end_pos: %lu", (ulong)start_pos,
95 (ulong)end_pos));
96 if (start_pos == HA_POS_ERROR || end_pos == HA_POS_ERROR) return HA_POS_ERROR;
97 return end_pos < start_pos
98 ? (ha_rows)0
99 : (end_pos == start_pos ? (ha_rows)1 : end_pos - start_pos);
100 }
101
102 /* Search after a record based on a key */
103 /* Sets info->current_ptr to found record */
104 /* next_flag: Search=0, next=1, prev =2, same =3 */
105
hp_search(HP_INFO * info,HP_KEYDEF * keyinfo,const uchar * key,uint nextflag)106 uchar *hp_search(HP_INFO *info, HP_KEYDEF *keyinfo, const uchar *key,
107 uint nextflag) {
108 HASH_INFO *pos, *prev_ptr;
109 int flag;
110 uint old_nextflag;
111 HP_SHARE *share = info->s;
112 DBUG_TRACE;
113 old_nextflag = nextflag;
114 flag = 1;
115 prev_ptr = nullptr;
116
117 if (share->records) {
118 pos = hp_find_hash(
119 &keyinfo->block,
120 hp_mask(hp_hashnr(keyinfo, key), share->blength, share->records));
121 do {
122 if (!hp_key_cmp(keyinfo, pos->ptr_to_rec, key)) {
123 switch (nextflag) {
124 case 0: /* Search after key */
125 DBUG_PRINT("exit", ("found key at %p", pos->ptr_to_rec));
126 info->current_hash_ptr = pos;
127 return info->current_ptr = pos->ptr_to_rec;
128 case 1: /* Search next */
129 if (pos->ptr_to_rec == info->current_ptr) nextflag = 0;
130 break;
131 case 2: /* Search previous */
132 if (pos->ptr_to_rec == info->current_ptr) {
133 set_my_errno(HA_ERR_KEY_NOT_FOUND); /* If gpos == 0 */
134 info->current_hash_ptr = prev_ptr;
135 return info->current_ptr =
136 prev_ptr ? prev_ptr->ptr_to_rec : nullptr;
137 }
138 prev_ptr = pos; /* Prev. record found */
139 break;
140 case 3: /* Search same */
141 if (pos->ptr_to_rec == info->current_ptr) {
142 info->current_hash_ptr = pos;
143 return info->current_ptr;
144 }
145 }
146 }
147 if (flag) {
148 flag = 0; /* Reset flag */
149 if (hp_find_hash(&keyinfo->block,
150 hp_mask(hp_rec_hashnr(keyinfo, pos->ptr_to_rec),
151 share->blength, share->records)) != pos)
152 break; /* Wrong link */
153 }
154 } while ((pos = pos->next_key));
155 }
156 set_my_errno(HA_ERR_KEY_NOT_FOUND);
157 if (nextflag == 2 && !info->current_ptr) {
158 /* Do a previous from end */
159 info->current_hash_ptr = prev_ptr;
160 return info->current_ptr = prev_ptr ? prev_ptr->ptr_to_rec : nullptr;
161 }
162
163 if (old_nextflag && nextflag)
164 set_my_errno(HA_ERR_RECORD_CHANGED); /* Didn't find old record */
165 DBUG_PRINT("exit", ("Error: %d", my_errno()));
166 info->current_hash_ptr = nullptr;
167 return (info->current_ptr = nullptr);
168 }
169
170 /*
171 Search next after last read; Assumes that the table hasn't changed
172 since last read !
173 */
174
hp_search_next(HP_INFO * info,HP_KEYDEF * keyinfo,const uchar * key,HASH_INFO * pos)175 uchar *hp_search_next(HP_INFO *info, HP_KEYDEF *keyinfo, const uchar *key,
176 HASH_INFO *pos) {
177 DBUG_TRACE;
178
179 while ((pos = pos->next_key)) {
180 if (!hp_key_cmp(keyinfo, pos->ptr_to_rec, key)) {
181 info->current_hash_ptr = pos;
182 return info->current_ptr = pos->ptr_to_rec;
183 }
184 }
185 set_my_errno(HA_ERR_KEY_NOT_FOUND);
186 DBUG_PRINT("exit", ("Error: %d", my_errno()));
187 info->current_hash_ptr = nullptr;
188 return (info->current_ptr = nullptr);
189 }
190
191 /*
192 Calculate position number for hash value.
193 SYNOPSIS
194 hp_mask()
195 hashnr Hash value
196 buffmax Value such that
197 2^(n-1) < maxlength <= 2^n = buffmax
198 maxlength
199
200 RETURN
201 Array index, in [0..maxlength)
202 */
203
hp_mask(uint64 hashnr,uint64 buffmax,uint64 maxlength)204 uint64 hp_mask(uint64 hashnr, uint64 buffmax, uint64 maxlength) {
205 if ((hashnr & (buffmax - 1)) < maxlength) return (hashnr & (buffmax - 1));
206 return (hashnr & ((buffmax >> 1) - 1));
207 }
208
209 /*
210 Change
211 next_link -> ... -> X -> pos
212 to
213 next_link -> ... -> X -> newlink
214 */
215
hp_movelink(HASH_INFO * pos,HASH_INFO * next_link,HASH_INFO * newlink)216 void hp_movelink(HASH_INFO *pos, HASH_INFO *next_link, HASH_INFO *newlink) {
217 HASH_INFO *old_link;
218 do {
219 old_link = next_link;
220 } while ((next_link = next_link->next_key) != pos);
221 old_link->next_key = newlink;
222 return;
223 }
224
225 /* Calc hashvalue for a key */
226
hp_hashnr(HP_KEYDEF * keydef,const uchar * key)227 uint64 hp_hashnr(HP_KEYDEF *keydef, const uchar *key) {
228 /*register*/
229 uint64 nr = 1, nr2 = 4;
230 HA_KEYSEG *seg, *endseg;
231
232 for (seg = keydef->seg, endseg = seg + keydef->keysegs; seg < endseg; seg++) {
233 const uchar *pos = key;
234 key += seg->length;
235 if (seg->null_bit) {
236 key++; /* Skip null byte */
237 if (*pos) /* Found null */
238 {
239 nr ^= (nr << 1) | 1;
240 /* Add key pack length (2) to key for VARCHAR segments */
241 if (seg->type == HA_KEYTYPE_VARTEXT1) key += 2;
242 continue;
243 }
244 pos++;
245 }
246 if (seg->type == HA_KEYTYPE_TEXT) {
247 const CHARSET_INFO *cs = seg->charset;
248 size_t length = seg->length;
249 if (cs->mbmaxlen > 1 && (seg->flag & HA_PART_KEY_SEG)) {
250 size_t char_length;
251 char_length = my_charpos(cs, pos, pos + length, length / cs->mbmaxlen);
252 length = std::min(length, char_length);
253 }
254 if (cs->pad_attribute == NO_PAD) {
255 /*
256 MySQL specifies that CHAR fields are stripped of
257 trailing spaces before being returned from the database.
258 Normally this is done in Field_string::val_str(),
259 but since we don't involve the Field classes for
260 hashing, we need to do the same thing here
261 for NO PAD collations. (If not, hash_sort will ignore
262 the spaces for us, so we don't need to do it here.)
263 */
264 length = cs->cset->lengthsp(cs, (const char *)pos, length);
265 }
266 cs->coll->hash_sort(cs, pos, length, &nr, &nr2);
267 } else if (seg->type == HA_KEYTYPE_VARTEXT1) /* Any VARCHAR segments */
268 {
269 const CHARSET_INFO *cs = seg->charset;
270 uint pack_length = 2; /* Key packing is constant */
271 size_t length = uint2korr(pos);
272 if (cs->mbmaxlen > 1 && (seg->flag & HA_PART_KEY_SEG)) {
273 size_t char_length;
274 char_length =
275 my_charpos(cs, pos + pack_length, pos + pack_length + length,
276 seg->length / cs->mbmaxlen);
277 length = std::min(length, char_length);
278 }
279 cs->coll->hash_sort(cs, pos + pack_length, length, &nr, &nr2);
280 key += pack_length;
281 } else {
282 for (; pos < key; pos++) {
283 nr ^= (uint64)((((uint)nr & 63) + nr2) * ((uint)*pos)) + (nr << 8);
284 nr2 += 3;
285 }
286 }
287 }
288 DBUG_PRINT("exit", ("hash: 0x%" PRIx64, nr));
289 return nr;
290 }
291
292 /* Calc hashvalue for a key in a record */
293
hp_rec_hashnr(HP_KEYDEF * keydef,const uchar * rec)294 uint64 hp_rec_hashnr(HP_KEYDEF *keydef, const uchar *rec) {
295 uint64 nr = 1, nr2 = 4;
296 HA_KEYSEG *seg, *endseg;
297
298 for (seg = keydef->seg, endseg = seg + keydef->keysegs; seg < endseg; seg++) {
299 const uchar *pos = rec + seg->start, *end = pos + seg->length;
300 if (seg->null_bit) {
301 if (rec[seg->null_pos] & seg->null_bit) {
302 nr ^= (nr << 1) | 1;
303 continue;
304 }
305 }
306 if (seg->type == HA_KEYTYPE_TEXT) {
307 const CHARSET_INFO *cs = seg->charset;
308 size_t char_length = seg->length;
309 if (cs->mbmaxlen > 1 && (seg->flag & HA_PART_KEY_SEG)) {
310 char_length =
311 my_charpos(cs, pos, pos + char_length, char_length / cs->mbmaxlen);
312 char_length =
313 std::min(char_length, size_t(seg->length)); /* QQ: ok to remove? */
314 }
315 if (cs->pad_attribute == NO_PAD) {
316 /*
317 MySQL specifies that CHAR fields are stripped of
318 trailing spaces before being returned from the database.
319 Normally this is done in Field_string::val_str(),
320 but since we don't involve the Field classes for
321 hashing, we need to do the same thing here
322 for NO PAD collations. (If not, hash_sort will ignore
323 the spaces for us, so we don't need to do it here.)
324 */
325 char_length = cs->cset->lengthsp(cs, (const char *)pos, char_length);
326 }
327 cs->coll->hash_sort(cs, pos, char_length, &nr, &nr2);
328 } else if (seg->type == HA_KEYTYPE_VARTEXT1) /* Any VARCHAR segments */
329 {
330 const CHARSET_INFO *cs = seg->charset;
331 uint pack_length = seg->bit_start;
332 size_t length = (pack_length == 1 ? (uint)*pos : uint2korr(pos));
333 if (cs->mbmaxlen > 1 && (seg->flag & HA_PART_KEY_SEG)) {
334 size_t char_length;
335 char_length =
336 my_charpos(cs, pos + pack_length, pos + pack_length + length,
337 seg->length / cs->mbmaxlen);
338 length = std::min(length, char_length);
339 }
340 cs->coll->hash_sort(cs, pos + pack_length, length, &nr, &nr2);
341 } else {
342 for (; pos < end; pos++) {
343 nr ^= (uint64)((((uint)nr & 63) + nr2) * ((uint)*pos)) + (nr << 8);
344 nr2 += 3;
345 }
346 }
347 }
348 DBUG_PRINT("exit", ("hash: 0x%" PRIx64, nr));
349 return (nr);
350 }
351
352 /*
353 Compare keys for two records. Returns 0 if they are identical
354
355 SYNOPSIS
356 hp_rec_key_cmp()
357 keydef Key definition
358 rec1 Record to compare
359 rec2 Other record to compare
360
361 RETURN
362 0 Key is identical
363 <> 0 Key differes
364 */
365
hp_rec_key_cmp(HP_KEYDEF * keydef,const uchar * rec1,const uchar * rec2)366 int hp_rec_key_cmp(HP_KEYDEF *keydef, const uchar *rec1, const uchar *rec2) {
367 HA_KEYSEG *seg, *endseg;
368
369 for (seg = keydef->seg, endseg = seg + keydef->keysegs; seg < endseg; seg++) {
370 if (seg->null_bit) {
371 if ((rec1[seg->null_pos] & seg->null_bit) !=
372 (rec2[seg->null_pos] & seg->null_bit))
373 return 1;
374 if (rec1[seg->null_pos] & seg->null_bit) continue;
375 }
376 if (seg->type == HA_KEYTYPE_TEXT) {
377 const CHARSET_INFO *cs = seg->charset;
378 size_t char_length1;
379 size_t char_length2;
380 const uchar *pos1 = rec1 + seg->start;
381 const uchar *pos2 = rec2 + seg->start;
382 if (cs->mbmaxlen > 1 && (seg->flag & HA_PART_KEY_SEG)) {
383 size_t char_length = seg->length / cs->mbmaxlen;
384 char_length1 = my_charpos(cs, pos1, pos1 + seg->length, char_length);
385 char_length1 = std::min(char_length1, size_t(seg->length));
386 char_length2 = my_charpos(cs, pos2, pos2 + seg->length, char_length);
387 char_length2 = std::min(char_length2, size_t(seg->length));
388 } else {
389 char_length1 = char_length2 = seg->length;
390 }
391 if (cs->pad_attribute == NO_PAD) {
392 /*
393 MySQL specifies that CHAR fields are stripped of
394 trailing spaces before being returned from the database.
395 Normally this is done in Field_string::val_str(),
396 but since we don't involve the Field classes for
397 internal comparisons, we need to do the same thing here
398 for NO PAD collations. (If not, strnncollsp will ignore
399 the spaces for us, so we don't need to do it here.)
400 */
401 char_length1 = cs->cset->lengthsp(cs, (const char *)pos1, char_length1);
402 char_length2 = cs->cset->lengthsp(cs, (const char *)pos2, char_length2);
403 }
404 if (cs->coll->strnncollsp(cs, pos1, char_length1, pos2, char_length2))
405 return 1;
406 } else if (seg->type == HA_KEYTYPE_VARTEXT1) /* Any VARCHAR segments */
407 {
408 const uchar *pos1 = rec1 + seg->start;
409 const uchar *pos2 = rec2 + seg->start;
410 uint char_length1, char_length2;
411 uint pack_length = seg->bit_start;
412 const CHARSET_INFO *cs = seg->charset;
413 if (pack_length == 1) {
414 char_length1 = (uint) * (pos1++);
415 char_length2 = (uint) * (pos2++);
416 } else {
417 char_length1 = uint2korr(pos1);
418 char_length2 = uint2korr(pos2);
419 pos1 += 2;
420 pos2 += 2;
421 }
422 if (cs->mbmaxlen > 1 && (seg->flag & HA_PART_KEY_SEG)) {
423 uint safe_length1 = char_length1;
424 uint safe_length2 = char_length2;
425 uint char_length = seg->length / cs->mbmaxlen;
426 char_length1 = my_charpos(cs, pos1, pos1 + char_length1, char_length);
427 char_length1 = std::min(char_length1, safe_length1);
428 char_length2 = my_charpos(cs, pos2, pos2 + char_length2, char_length);
429 char_length2 = std::min(char_length2, safe_length2);
430 }
431
432 if (cs->coll->strnncollsp(seg->charset, pos1, char_length1, pos2,
433 char_length2))
434 return 1;
435 } else {
436 if (memcmp(rec1 + seg->start, rec2 + seg->start, seg->length)) return 1;
437 }
438 }
439 return 0;
440 }
441
442 /* Compare a key in a record to a whole key */
443
hp_key_cmp(HP_KEYDEF * keydef,const uchar * rec,const uchar * key)444 int hp_key_cmp(HP_KEYDEF *keydef, const uchar *rec, const uchar *key) {
445 HA_KEYSEG *seg, *endseg;
446
447 for (seg = keydef->seg, endseg = seg + keydef->keysegs; seg < endseg;
448 key += (seg++)->length) {
449 if (seg->null_bit) {
450 bool found_null = (rec[seg->null_pos] & seg->null_bit);
451 if (found_null != (int)*key++) return 1;
452 if (found_null) {
453 /* Add key pack length (2) to key for VARCHAR segments */
454 if (seg->type == HA_KEYTYPE_VARTEXT1) key += 2;
455 continue;
456 }
457 }
458 if (seg->type == HA_KEYTYPE_TEXT) {
459 const CHARSET_INFO *cs = seg->charset;
460 uint char_length_key;
461 uint char_length_rec;
462 const uchar *pos = rec + seg->start;
463 if (cs->mbmaxlen > 1 && (seg->flag & HA_PART_KEY_SEG)) {
464 uint char_length = seg->length / cs->mbmaxlen;
465 char_length_key = my_charpos(cs, key, key + seg->length, char_length);
466 char_length_key = std::min(char_length_key, uint(seg->length));
467 char_length_rec = my_charpos(cs, pos, pos + seg->length, char_length);
468 char_length_rec = std::min(char_length_rec, uint(seg->length));
469 } else {
470 char_length_key = seg->length;
471 char_length_rec = seg->length;
472 }
473
474 if (cs->pad_attribute == NO_PAD) {
475 /*
476 MySQL specifies that CHAR fields are stripped of
477 trailing spaces before being returned from the database.
478 Normally this is done in Field_string::val_str(),
479 but since we don't involve the Field classes for
480 internal comparisons, we need to do the same thing here
481 for NO PAD collations. (If not, strnncollsp will ignore
482 the spaces for us, so we don't need to do it here.)
483 */
484 char_length_rec =
485 cs->cset->lengthsp(cs, (const char *)pos, char_length_rec);
486 char_length_key =
487 cs->cset->lengthsp(cs, (const char *)key, char_length_key);
488 }
489
490 if (cs->coll->strnncollsp(cs, pos, char_length_rec, key, char_length_key))
491 return 1;
492 } else if (seg->type == HA_KEYTYPE_VARTEXT1) /* Any VARCHAR segments */
493 {
494 const uchar *pos = rec + seg->start;
495 const CHARSET_INFO *cs = seg->charset;
496 uint pack_length = seg->bit_start;
497 uint char_length_rec = (pack_length == 1 ? (uint)*pos : uint2korr(pos));
498 /* Key segments are always packed with 2 bytes */
499 uint char_length_key = uint2korr(key);
500 pos += pack_length;
501 key += 2; /* skip key pack length */
502 if (cs->mbmaxlen > 1 && (seg->flag & HA_PART_KEY_SEG)) {
503 uint char_length1, char_length2;
504 char_length1 = char_length2 = seg->length / cs->mbmaxlen;
505 char_length1 = my_charpos(cs, key, key + char_length_key, char_length1);
506 char_length_key = std::min(char_length_key, char_length1);
507 char_length2 = my_charpos(cs, pos, pos + char_length_rec, char_length2);
508 char_length_rec = std::min(char_length_rec, char_length2);
509 } else {
510 char_length_rec = std::min(char_length_rec, uint(seg->length));
511 }
512
513 if (cs->coll->strnncollsp(seg->charset, pos, char_length_rec, key,
514 char_length_key))
515 return 1;
516 } else {
517 if (memcmp(rec + seg->start, key, seg->length)) return 1;
518 }
519 }
520 return 0;
521 }
522
523 /* Copy a key from a record to a keybuffer */
524
hp_make_key(HP_KEYDEF * keydef,uchar * key,const uchar * rec)525 void hp_make_key(HP_KEYDEF *keydef, uchar *key, const uchar *rec) {
526 HA_KEYSEG *seg, *endseg;
527
528 for (seg = keydef->seg, endseg = seg + keydef->keysegs; seg < endseg; seg++) {
529 const CHARSET_INFO *cs = seg->charset;
530 uint char_length = seg->length;
531 const uchar *pos = rec + seg->start;
532 if (seg->null_bit) {
533 bool rec_is_null = rec[seg->null_pos] & seg->null_bit;
534 *key++ = (rec_is_null ? 1 : 0);
535 }
536 if (cs->mbmaxlen > 1 && (seg->flag & HA_PART_KEY_SEG)) {
537 char_length =
538 my_charpos(cs, pos, pos + seg->length, char_length / cs->mbmaxlen);
539 char_length =
540 std::min(char_length, uint(seg->length)); /* QQ: ok to remove? */
541 }
542 if (seg->type == HA_KEYTYPE_VARTEXT1)
543 char_length += seg->bit_start; /* Copy also length */
544 memcpy(key, rec + seg->start, (size_t)char_length);
545 key += char_length;
546 }
547 }
548
549 #define FIX_LENGTH(cs, pos, length, char_length) \
550 do { \
551 if (length > char_length) \
552 char_length = my_charpos(cs, pos, pos + length, char_length); \
553 char_length = std::min(char_length, size_t(length)); \
554 } while (0)
555
hp_rb_make_key(HP_KEYDEF * keydef,uchar * key,const uchar * rec,uchar * recpos)556 uint hp_rb_make_key(HP_KEYDEF *keydef, uchar *key, const uchar *rec,
557 uchar *recpos) {
558 uchar *start_key = key;
559 HA_KEYSEG *seg, *endseg;
560
561 for (seg = keydef->seg, endseg = seg + keydef->keysegs; seg < endseg; seg++) {
562 size_t char_length;
563 if (seg->null_bit) {
564 bool rec_is_null = rec[seg->null_pos] & seg->null_bit;
565 if (!(*key++ = 1 - (rec_is_null ? 1 : 0))) continue;
566 }
567 if (seg->flag & HA_SWAP_KEY) {
568 uint length = seg->length;
569 const uchar *pos = rec + seg->start;
570 if (seg->type == HA_KEYTYPE_FLOAT) {
571 float nr = float4get(pos);
572 if (std::isnan(nr)) {
573 /* Replace NAN with zero */
574 memset(key, 0, length);
575 key += length;
576 continue;
577 }
578 } else if (seg->type == HA_KEYTYPE_DOUBLE) {
579 double nr = float8get(pos);
580 if (std::isnan(nr)) {
581 memset(key, 0, length);
582 key += length;
583 continue;
584 }
585 }
586 pos += length;
587 while (length--) {
588 *key++ = *--pos;
589 }
590 continue;
591 }
592
593 if (seg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART)) {
594 const uchar *pos = rec + seg->start;
595 uint length = seg->length;
596 uint pack_length = seg->bit_start;
597 uint tmp_length = (pack_length == 1 ? (uint)*pos : uint2korr(pos));
598 const CHARSET_INFO *cs = seg->charset;
599 char_length = length / cs->mbmaxlen;
600
601 pos += pack_length; /* Skip VARCHAR length */
602 length = std::min(length, tmp_length);
603 FIX_LENGTH(cs, pos, length, char_length);
604 store_key_length_inc(key, char_length);
605 memcpy(key, pos, char_length);
606 key += char_length;
607 continue;
608 }
609
610 char_length = seg->length;
611 if (seg->charset->mbmaxlen > 1) {
612 char_length = my_charpos(seg->charset, rec + seg->start,
613 rec + seg->start + char_length,
614 char_length / seg->charset->mbmaxlen);
615 char_length =
616 std::min(char_length, size_t(seg->length)); /* QQ: ok to remove? */
617 if (char_length < seg->length)
618 seg->charset->cset->fill(seg->charset, (char *)key + char_length,
619 seg->length - char_length, ' ');
620 }
621 memcpy(key, rec + seg->start, (size_t)char_length);
622 key += seg->length;
623 }
624 memcpy(key, &recpos, sizeof(uchar *));
625 return (uint)(key - start_key);
626 }
627
hp_rb_pack_key(const HP_KEYDEF * keydef,uchar * key,const uchar * old,key_part_map keypart_map)628 uint hp_rb_pack_key(const HP_KEYDEF *keydef, uchar *key, const uchar *old,
629 key_part_map keypart_map) {
630 HA_KEYSEG *seg, *endseg;
631 uchar *start_key = key;
632
633 for (seg = keydef->seg, endseg = seg + keydef->keysegs;
634 seg < endseg && keypart_map; old += seg->length, seg++) {
635 size_t char_length;
636 keypart_map >>= 1;
637 if (seg->null_bit) {
638 /* Convert NULL from MySQL representation into HEAP's. */
639 if (!(*key++ = (char)1 - *old++)) {
640 /*
641 Skip length part of a variable length field.
642 Length of key-part used with heap_rkey() always 2.
643 See also hp_hashnr().
644 */
645 if (seg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART)) old += 2;
646 continue;
647 }
648 }
649 if (seg->flag & HA_SWAP_KEY) {
650 uint length = seg->length;
651 const uchar *pos = old + length;
652
653 while (length--) {
654 *key++ = *--pos;
655 }
656 continue;
657 }
658 if (seg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART)) {
659 /* Length of key-part used with heap_rkey() always 2 */
660 uint tmp_length = uint2korr(old);
661 uint length = seg->length;
662 const CHARSET_INFO *cs = seg->charset;
663 char_length = length / cs->mbmaxlen;
664
665 old += 2;
666 length = std::min(length, tmp_length); /* Safety */
667 FIX_LENGTH(cs, old, length, char_length);
668 store_key_length_inc(key, char_length);
669 memcpy((uchar *)key, old, (size_t)char_length);
670 key += char_length;
671 continue;
672 }
673 char_length = seg->length;
674 if (seg->charset->mbmaxlen > 1) {
675 char_length = my_charpos(seg->charset, old, old + char_length,
676 char_length / seg->charset->mbmaxlen);
677 char_length =
678 std::min(char_length, size_t(seg->length)); /* QQ: ok to remove? */
679 if (char_length < seg->length)
680 seg->charset->cset->fill(seg->charset, (char *)key + char_length,
681 seg->length - char_length, ' ');
682 }
683 memcpy(key, old, (size_t)char_length);
684 key += seg->length;
685 }
686 return (uint)(key - start_key);
687 }
688
hp_rb_key_length(HP_KEYDEF * keydef,const uchar * key MY_ATTRIBUTE ((unused)))689 uint hp_rb_key_length(HP_KEYDEF *keydef,
690 const uchar *key MY_ATTRIBUTE((unused))) {
691 return keydef->length;
692 }
693
hp_rb_null_key_length(HP_KEYDEF * keydef,const uchar * key)694 uint hp_rb_null_key_length(HP_KEYDEF *keydef, const uchar *key) {
695 const uchar *start_key = key;
696 HA_KEYSEG *seg, *endseg;
697
698 for (seg = keydef->seg, endseg = seg + keydef->keysegs; seg < endseg; seg++) {
699 if (seg->null_bit && !*key++) continue;
700 key += seg->length;
701 }
702 return (uint)(key - start_key);
703 }
704
hp_rb_var_key_length(HP_KEYDEF * keydef,const uchar * key)705 uint hp_rb_var_key_length(HP_KEYDEF *keydef, const uchar *key) {
706 const uchar *start_key = key;
707 HA_KEYSEG *seg, *endseg;
708
709 for (seg = keydef->seg, endseg = seg + keydef->keysegs; seg < endseg; seg++) {
710 uint length = seg->length;
711 if (seg->null_bit && !*key++) continue;
712 if (seg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART)) {
713 length = get_key_length(&key);
714 }
715 key += length;
716 }
717 return (uint)(key - start_key);
718 }
719
720 /*
721 Test if any of the key parts are NULL.
722 Return:
723 1 if any of the key parts was NULL
724 0 otherwise
725 */
726
hp_if_null_in_key(HP_KEYDEF * keydef,const uchar * record)727 bool hp_if_null_in_key(HP_KEYDEF *keydef, const uchar *record) {
728 HA_KEYSEG *seg, *endseg;
729 for (seg = keydef->seg, endseg = seg + keydef->keysegs; seg < endseg; seg++) {
730 if (seg->null_bit && (record[seg->null_pos] & seg->null_bit)) return true;
731 }
732 return false;
733 }
734
735 /*
736 Update auto_increment info
737
738 SYNOPSIS
739 update_auto_increment()
740 info MyISAM handler
741 record Row to update
742
743 IMPLEMENTATION
744 Only replace the auto_increment value if it is higher than the previous
745 one. For signed columns we don't update the auto increment value if it's
746 less than zero.
747 */
748
heap_update_auto_increment(HP_INFO * info,const uchar * record)749 void heap_update_auto_increment(HP_INFO *info, const uchar *record) {
750 ulonglong value = 0; /* Store unsigned values here */
751 longlong s_value = 0; /* Store signed values here */
752
753 HA_KEYSEG *keyseg = info->s->keydef[info->s->auto_key - 1].seg;
754 const uchar *key = record + keyseg->start;
755
756 switch (info->s->auto_key_type) {
757 case HA_KEYTYPE_INT8:
758 s_value = (longlong) static_cast<char>(*key);
759 break;
760 case HA_KEYTYPE_BINARY:
761 value = (ulonglong)*key;
762 break;
763 case HA_KEYTYPE_SHORT_INT:
764 s_value = (longlong)sint2korr(key);
765 break;
766 case HA_KEYTYPE_USHORT_INT:
767 value = (ulonglong)uint2korr(key);
768 break;
769 case HA_KEYTYPE_LONG_INT:
770 s_value = (longlong)sint4korr(key);
771 break;
772 case HA_KEYTYPE_ULONG_INT:
773 value = (ulonglong)uint4korr(key);
774 break;
775 case HA_KEYTYPE_INT24:
776 s_value = (longlong)sint3korr(key);
777 break;
778 case HA_KEYTYPE_UINT24:
779 value = (ulonglong)uint3korr(key);
780 break;
781 case HA_KEYTYPE_FLOAT: /* This shouldn't be used */
782 {
783 float f_1 = float4get(key);
784 /* Ignore negative values */
785 value = (f_1 < (float)0.0) ? 0 : (ulonglong)f_1;
786 break;
787 }
788 case HA_KEYTYPE_DOUBLE: /* This shouldn't be used */
789 {
790 double f_1 = float8get(key);
791 /* Ignore negative values */
792 value = (f_1 < 0.0) ? 0 : (ulonglong)f_1;
793 break;
794 }
795 case HA_KEYTYPE_LONGLONG:
796 s_value = sint8korr(key);
797 break;
798 case HA_KEYTYPE_ULONGLONG:
799 value = uint8korr(key);
800 break;
801 default:
802 DBUG_ASSERT(0);
803 value = 0; /* Error */
804 break;
805 }
806
807 /*
808 The following code works becasue if s_value < 0 then value is 0
809 and if s_value == 0 then value will contain either s_value or the
810 correct value.
811 */
812 info->s->auto_increment = std::max(
813 info->s->auto_increment, (s_value > 0) ? ulonglong(s_value) : value);
814 }
815