1 /* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
2 Copyright (c) 2020, MariaDB Corporation.
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-1335 USA */
16
17 /* Functions to handle keys */
18
19 #include "myisamdef.h"
20 #include "m_ctype.h"
21 #include "sp_defs.h"
22 #ifdef HAVE_IEEEFP_H
23 #include <ieeefp.h>
24 #endif
25
26 #define CHECK_KEYS /* Enable safety checks */
27
28 #define FIX_LENGTH(cs, pos, length, char_length) \
29 do { \
30 if (length > char_length) \
31 char_length= my_ci_charpos(cs, (const char *) pos, \
32 (const char *) pos+length, \
33 char_length); \
34 set_if_smaller(char_length,length); \
35 } while(0)
36
37 static int _mi_put_key_in_record(MI_INFO *info,uint keynr,
38 my_bool unpack_blobs, uchar *record);
39
40 /*
41 Make a intern key from a record
42
43 SYNOPSIS
44 _mi_make_key()
45 info MyiSAM handler
46 keynr key number
47 key Store created key here
48 record Record
49 filepos Position to record in the data file
50
51 RETURN
52 Length of key
53 */
54
_mi_make_key(register MI_INFO * info,uint keynr,uchar * key,const uchar * record,my_off_t filepos)55 uint _mi_make_key(register MI_INFO *info, uint keynr, uchar *key,
56 const uchar *record, my_off_t filepos)
57 {
58 uchar *pos;
59 uchar *start;
60 reg1 HA_KEYSEG *keyseg;
61 my_bool is_ft= info->s->keyinfo[keynr].flag & HA_FULLTEXT;
62 DBUG_ENTER("_mi_make_key");
63
64 if (info->s->keyinfo[keynr].flag & HA_SPATIAL)
65 {
66 /*
67 TODO: nulls processing
68 */
69 #ifdef HAVE_SPATIAL
70 DBUG_RETURN(sp_make_key(info,keynr,key,record,filepos));
71 #else
72 DBUG_ASSERT(0); /* mi_open should check that this never happens*/
73 #endif
74 }
75
76 start=key;
77 for (keyseg=info->s->keyinfo[keynr].seg ; keyseg->type ;keyseg++)
78 {
79 enum ha_base_keytype type=(enum ha_base_keytype) keyseg->type;
80 size_t length=keyseg->length;
81 size_t char_length;
82 CHARSET_INFO *cs=keyseg->charset;
83
84 if (keyseg->null_bit)
85 {
86 if (record[keyseg->null_pos] & keyseg->null_bit)
87 {
88 *key++= 0; /* NULL in key */
89 continue;
90 }
91 *key++=1; /* Not NULL */
92 }
93
94 char_length= ((!is_ft && cs && cs->mbmaxlen > 1) ? length/cs->mbmaxlen :
95 length);
96
97 pos= (uchar*) record+keyseg->start;
98 if (type == HA_KEYTYPE_BIT)
99 {
100 if (keyseg->bit_length)
101 {
102 uchar bits= get_rec_bits((uchar*) record + keyseg->bit_pos,
103 keyseg->bit_start, keyseg->bit_length);
104 *key++= bits;
105 length--;
106 }
107 memcpy((uchar*) key, pos, length);
108 key+= length;
109 continue;
110 }
111 if (keyseg->flag & HA_SPACE_PACK)
112 {
113 if (type != HA_KEYTYPE_NUM)
114 {
115 length= my_ci_lengthsp(cs, (char*) pos, length);
116 }
117 else
118 {
119 uchar *end= pos + length;
120 while (pos < end && pos[0] == ' ')
121 pos++;
122 length=(size_t) (end-pos);
123 }
124 FIX_LENGTH(cs, pos, length, char_length);
125 store_key_length_inc(key,char_length);
126 memcpy(key, pos,char_length);
127 key+=char_length;
128 continue;
129 }
130 if (keyseg->flag & HA_VAR_LENGTH_PART)
131 {
132 uint pack_length= (keyseg->bit_start == 1 ? 1 : 2);
133 uint tmp_length= (pack_length == 1 ? (uint) *(uchar*) pos :
134 uint2korr(pos));
135 pos+= pack_length; /* Skip VARCHAR length */
136 set_if_smaller(length,tmp_length);
137 FIX_LENGTH(cs, pos, length, char_length);
138 store_key_length_inc(key,char_length);
139 memcpy(key, pos, char_length);
140 key+= char_length;
141 continue;
142 }
143 else if (keyseg->flag & HA_BLOB_PART)
144 {
145 uint tmp_length=_mi_calc_blob_length(keyseg->bit_start,pos);
146 memcpy(&pos,pos+keyseg->bit_start,sizeof(char*));
147 set_if_smaller(length,tmp_length);
148 FIX_LENGTH(cs, pos, length, char_length);
149 store_key_length_inc(key,char_length);
150 if (char_length)
151 {
152 memcpy(key, pos, char_length);
153 key+= char_length;
154 }
155 continue;
156 }
157 else if (keyseg->flag & HA_SWAP_KEY)
158 { /* Numerical column */
159 if (type == HA_KEYTYPE_FLOAT)
160 {
161 float nr;
162 float4get(nr,pos);
163 if (isnan(nr))
164 {
165 /* Replace NAN with zero */
166 bzero(key,length);
167 key+=length;
168 continue;
169 }
170 }
171 else if (type == HA_KEYTYPE_DOUBLE)
172 {
173 double nr;
174 float8get(nr,pos);
175 if (isnan(nr))
176 {
177 bzero(key,length);
178 key+=length;
179 continue;
180 }
181 }
182 pos+=length;
183 while (length--)
184 {
185 *key++ = *--pos;
186 }
187 continue;
188 }
189 FIX_LENGTH(cs, pos, length, char_length);
190 memcpy((uchar*) key, pos, char_length);
191 if (length > char_length)
192 my_ci_fill(cs, (char*) key+char_length, length-char_length, ' ');
193 key+= length;
194 }
195 _mi_dpointer(info,key,filepos);
196 DBUG_PRINT("exit",("keynr: %d",keynr));
197 DBUG_DUMP("key",(uchar*) start,(uint) (key-start)+keyseg->length);
198 DBUG_EXECUTE("key",
199 _mi_print_key(DBUG_FILE,info->s->keyinfo[keynr].seg,start,
200 (uint) (key-start)););
201 DBUG_RETURN((uint) (key-start)); /* Return keylength */
202 } /* _mi_make_key */
203
204
205 /*
206 Pack a key to intern format from given format (c_rkey)
207
208 SYNOPSIS
209 _mi_pack_key()
210 info MyISAM handler
211 uint keynr key number
212 key Store packed key here
213 old Not packed key
214 keypart_map bitmap of used keyparts
215 last_used_keyseg out parameter. May be NULL
216
217 RETURN
218 length of packed key
219
220 last_use_keyseg Store pointer to the keyseg after the last used one
221 */
222
_mi_pack_key(register MI_INFO * info,uint keynr,uchar * key,uchar * old,key_part_map keypart_map,HA_KEYSEG ** last_used_keyseg)223 uint _mi_pack_key(register MI_INFO *info, uint keynr, uchar *key, uchar *old,
224 key_part_map keypart_map, HA_KEYSEG **last_used_keyseg)
225 {
226 uchar *start_key=key;
227 HA_KEYSEG *keyseg;
228 my_bool is_ft= info->s->keyinfo[keynr].flag & HA_FULLTEXT;
229 DBUG_ENTER("_mi_pack_key");
230
231 /* "one part" rtree key is 2*SPDIMS part key in MyISAM */
232 if (info->s->keyinfo[keynr].key_alg == HA_KEY_ALG_RTREE)
233 keypart_map= (((key_part_map)1) << (2*SPDIMS)) - 1;
234
235 /* only key prefixes are supported */
236 DBUG_ASSERT(((keypart_map+1) & keypart_map) == 0);
237
238 for (keyseg= info->s->keyinfo[keynr].seg ; keyseg->type && keypart_map;
239 old+= keyseg->length, keyseg++)
240 {
241 enum ha_base_keytype type= (enum ha_base_keytype) keyseg->type;
242 size_t length= keyseg->length;
243 size_t char_length;
244 uchar *pos;
245 CHARSET_INFO *cs=keyseg->charset;
246
247 keypart_map>>= 1;
248 if (keyseg->null_bit)
249 {
250 if (!(*key++= (char) 1-*old++)) /* Copy null marker */
251 {
252 if (keyseg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART))
253 old+= 2;
254 continue; /* Found NULL */
255 }
256 }
257 char_length= (!is_ft && cs && cs->mbmaxlen > 1) ? length/cs->mbmaxlen : length;
258 pos=old;
259 if (keyseg->flag & HA_SPACE_PACK)
260 {
261 if (type == HA_KEYTYPE_NUM)
262 {
263 uchar *end= pos + length;
264 while (pos < end && pos[0] == ' ')
265 pos++;
266 length= (size_t)(end - pos);
267 }
268 else if (type != HA_KEYTYPE_BINARY)
269 {
270 length= my_ci_lengthsp(cs, (char*) pos, length);
271 }
272 FIX_LENGTH(cs, pos, length, char_length);
273 store_key_length_inc(key,char_length);
274 memcpy(key,pos,char_length);
275 key+= char_length;
276 continue;
277 }
278 else if (keyseg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART))
279 {
280 /* Length of key-part used with mi_rkey() always 2 */
281 uint tmp_length=uint2korr(pos);
282 pos+=2;
283 set_if_smaller(length,tmp_length); /* Safety */
284 FIX_LENGTH(cs, pos, length, char_length);
285 store_key_length_inc(key,char_length);
286 old+=2; /* Skip length */
287 memcpy(key, pos, char_length);
288 key+= char_length;
289 continue;
290 }
291 else if (keyseg->flag & HA_SWAP_KEY)
292 { /* Numerical column */
293 pos+=length;
294 while (length--)
295 *key++ = *--pos;
296 continue;
297 }
298 FIX_LENGTH(cs, pos, length, char_length);
299 memcpy((uchar*) key, pos, char_length);
300 if (length > char_length)
301 my_ci_fill(cs, (char*) key+char_length, length-char_length, ' ');
302 key+= length;
303 }
304 if (last_used_keyseg)
305 *last_used_keyseg= keyseg;
306
307 DBUG_RETURN((uint) (key-start_key));
308 } /* _mi_pack_key */
309
310
311
312 /*
313 Store found key in record
314
315 SYNOPSIS
316 _mi_put_key_in_record()
317 info MyISAM handler
318 keynr Key number that was used
319 unpack_blobs TRUE <=> Unpack blob columns
320 FALSE <=> Skip them. This is used by index condition
321 pushdown check function
322 record Store key here
323
324 Last read key is in info->lastkey
325
326 NOTES
327 Used when only-keyread is wanted
328
329 RETURN
330 0 ok
331 1 error
332 */
333
_mi_put_key_in_record(register MI_INFO * info,uint keynr,my_bool unpack_blobs,uchar * record)334 static int _mi_put_key_in_record(register MI_INFO *info, uint keynr,
335 my_bool unpack_blobs, uchar *record)
336 {
337 reg2 uchar *key;
338 uchar *pos,*key_end;
339 reg1 HA_KEYSEG *keyseg;
340 uchar *blob_ptr;
341 DBUG_ENTER("_mi_put_key_in_record");
342
343 blob_ptr= (uchar*) info->lastkey2; /* Place to put blob parts */
344 key=(uchar*) info->lastkey; /* KEy that was read */
345 key_end=key+info->lastkey_length;
346 for (keyseg=info->s->keyinfo[keynr].seg ; keyseg->type ;keyseg++)
347 {
348 if (keyseg->null_bit)
349 {
350 if (!*key++)
351 {
352 record[keyseg->null_pos]|= keyseg->null_bit;
353 continue;
354 }
355 record[keyseg->null_pos]&= ~keyseg->null_bit;
356 }
357 if (keyseg->type == HA_KEYTYPE_BIT)
358 {
359 uint length= keyseg->length;
360
361 if (keyseg->bit_length)
362 {
363 uchar bits= *key++;
364 set_rec_bits(bits, record + keyseg->bit_pos, keyseg->bit_start,
365 keyseg->bit_length);
366 length--;
367 }
368 else
369 {
370 clr_rec_bits(record + keyseg->bit_pos, keyseg->bit_start,
371 keyseg->bit_length);
372 }
373 memcpy(record + keyseg->start, (uchar*) key, length);
374 key+= length;
375 continue;
376 }
377 if (keyseg->flag & HA_SPACE_PACK)
378 {
379 uint length;
380 get_key_length(length,key);
381 #ifdef CHECK_KEYS
382 if (length > keyseg->length || key+length > key_end)
383 goto err;
384 #endif
385 pos= record+keyseg->start;
386 if (keyseg->type != (int) HA_KEYTYPE_NUM)
387 {
388 memcpy(pos,key,(size_t) length);
389 my_ci_fill(keyseg->charset, (char*) pos + length,
390 keyseg->length - length,
391 ' ');
392 }
393 else
394 {
395 bfill(pos,keyseg->length-length,' ');
396 memcpy(pos+keyseg->length-length,key,(size_t) length);
397 }
398 key+=length;
399 continue;
400 }
401
402 if (keyseg->flag & HA_VAR_LENGTH_PART)
403 {
404 uint length;
405 get_key_length(length,key);
406 #ifdef CHECK_KEYS
407 if (length > keyseg->length || key+length > key_end)
408 goto err;
409 #endif
410 /* Store key length */
411 if (keyseg->bit_start == 1)
412 *(uchar*) (record+keyseg->start)= (uchar) length;
413 else
414 int2store(record+keyseg->start, length);
415 /* And key data */
416 memcpy(record+keyseg->start + keyseg->bit_start, (uchar*) key, length);
417 key+= length;
418 }
419 else if (keyseg->flag & HA_BLOB_PART)
420 {
421 uint length;
422 get_key_length(length,key);
423 #ifdef CHECK_KEYS
424 if (length > keyseg->length || key+length > key_end)
425 goto err;
426 #endif
427 if (unpack_blobs)
428 {
429 memcpy(record+keyseg->start+keyseg->bit_start,
430 &blob_ptr, sizeof(char *));
431 memcpy(blob_ptr,key,length);
432 blob_ptr+=length;
433
434 /* The above changed info->lastkey2. Inform mi_rnext_same(). */
435 info->update&= ~HA_STATE_RNEXT_SAME;
436
437 _mi_store_blob_length(record+keyseg->start,
438 (uint) keyseg->bit_start,length);
439 }
440 key+=length;
441 }
442 else if (keyseg->flag & HA_SWAP_KEY)
443 {
444 uchar *to= record+keyseg->start+keyseg->length;
445 uchar *end= key+keyseg->length;
446 #ifdef CHECK_KEYS
447 if (end > key_end)
448 goto err;
449 #endif
450 do
451 {
452 *--to= *key++;
453 } while (key != end);
454 continue;
455 }
456 else
457 {
458 #ifdef CHECK_KEYS
459 if (key+keyseg->length > key_end)
460 goto err;
461 #endif
462 memcpy(record+keyseg->start,(uchar*) key,
463 (size_t) keyseg->length);
464 key+= keyseg->length;
465 }
466 }
467 DBUG_RETURN(0);
468
469 err:
470 DBUG_RETURN(1); /* Crashed row */
471 } /* _mi_put_key_in_record */
472
473
474 /* Here when key reads are used */
475
_mi_read_key_record(MI_INFO * info,my_off_t filepos,uchar * buf)476 int _mi_read_key_record(MI_INFO *info, my_off_t filepos, uchar *buf)
477 {
478 fast_mi_writeinfo(info);
479 if (filepos != HA_OFFSET_ERROR)
480 {
481 if (info->lastinx >= 0)
482 { /* Read only key */
483 if (_mi_put_key_in_record(info,(uint) info->lastinx, TRUE, buf))
484 {
485 mi_print_error(info->s, HA_ERR_CRASHED);
486 my_errno=HA_ERR_CRASHED;
487 return -1;
488 }
489 info->update|= HA_STATE_AKTIV; /* We should find a record */
490 return 0;
491 }
492 my_errno=HA_ERR_WRONG_INDEX;
493 }
494 return(-1); /* Wrong data to read */
495 }
496
497
498 static
mi_unpack_index_tuple(MI_INFO * info,uint keynr,uchar * record)499 int mi_unpack_index_tuple(MI_INFO *info, uint keynr, uchar *record)
500 {
501 if (_mi_put_key_in_record(info, keynr, FALSE, record))
502 {
503 /* Impossible case; Can only happen if bug in code */
504 mi_print_error(info->s, HA_ERR_CRASHED);
505 info->lastpos= HA_OFFSET_ERROR; /* No active record */
506 my_errno= HA_ERR_CRASHED;
507 return 1;
508 }
509 return 0;
510 }
511
512
mi_check_rowid_filter_is_active(MI_INFO * info)513 static int mi_check_rowid_filter_is_active(MI_INFO *info)
514 {
515 if (info->rowid_filter_is_active_func == NULL)
516 return 0;
517 return info->rowid_filter_is_active_func(info->rowid_filter_func_arg);
518 }
519
520
521 /*
522 Check the current index tuple: Check ICP condition and/or Rowid Filter
523
524 SYNOPSIS
525 mi_check_index_tuple()
526 info MyISAM handler
527 keynr Index we're running a scan on
528 record Record buffer to use (it is assumed that index check function
529 will look for column values there)
530
531 RETURN
532 Check result according to check_result_t definition
533 */
534
mi_check_index_tuple(MI_INFO * info,uint keynr,uchar * record)535 check_result_t mi_check_index_tuple(MI_INFO *info, uint keynr, uchar *record)
536 {
537 int need_unpack= TRUE;
538 check_result_t res= CHECK_POS;
539
540 if (info->index_cond_func)
541 {
542 if (mi_unpack_index_tuple(info, keynr, record))
543 res= CHECK_ERROR;
544 else if ((res= info->index_cond_func(info->index_cond_func_arg)) ==
545 CHECK_OUT_OF_RANGE)
546 {
547 /* We got beyond the end of scanned range */
548 info->lastpos= HA_OFFSET_ERROR; /* No active record */
549 my_errno= HA_ERR_END_OF_FILE;
550 }
551
552 /*
553 If we got an error, out-of-range condition, or ICP condition computed to
554 FALSE - we don't need to check the Rowid Filter.
555 */
556 if (res != CHECK_POS)
557 return res;
558
559 need_unpack= FALSE;
560 }
561
562 /* Check the Rowid Filter, if present */
563 if (mi_check_rowid_filter_is_active(info))
564 {
565 /* Unpack the index tuple if we haven't done it already */
566 if (need_unpack && mi_unpack_index_tuple(info, keynr, record))
567 res= CHECK_ERROR;
568 else
569 {
570 if ((res= info->rowid_filter_func(info->rowid_filter_func_arg)) ==
571 CHECK_OUT_OF_RANGE)
572 {
573 /* We got beyond the end of scanned range */
574 info->lastpos= HA_OFFSET_ERROR; /* No active record */
575 my_errno= HA_ERR_END_OF_FILE;
576 }
577 }
578 }
579 return res;
580 }
581
582
583 /*
584 Retrieve auto_increment info
585
586 SYNOPSIS
587 retrieve_auto_increment()
588 info MyISAM handler
589 record Row to update
590
591 IMPLEMENTATION
592 For signed columns we don't retrieve the auto increment value if it's
593 less than zero.
594 */
595
retrieve_auto_increment(MI_INFO * info,const uchar * record)596 ulonglong retrieve_auto_increment(MI_INFO *info,const uchar *record)
597 {
598 ulonglong value= 0; /* Store unsigned values here */
599 longlong s_value= 0; /* Store signed values here */
600 HA_KEYSEG *keyseg= info->s->keyinfo[info->s->base.auto_key-1].seg;
601 const uchar *key= (uchar*) record + keyseg->start;
602
603 switch (keyseg->type) {
604 case HA_KEYTYPE_INT8:
605 s_value= (longlong) *(const signed char*) key;
606 break;
607 case HA_KEYTYPE_BINARY:
608 value=(ulonglong) *(uchar*) key;
609 break;
610 case HA_KEYTYPE_SHORT_INT:
611 s_value= (longlong) sint2korr(key);
612 break;
613 case HA_KEYTYPE_USHORT_INT:
614 value=(ulonglong) uint2korr(key);
615 break;
616 case HA_KEYTYPE_LONG_INT:
617 s_value= (longlong) sint4korr(key);
618 break;
619 case HA_KEYTYPE_ULONG_INT:
620 value=(ulonglong) uint4korr(key);
621 break;
622 case HA_KEYTYPE_INT24:
623 s_value= (longlong) sint3korr(key);
624 break;
625 case HA_KEYTYPE_UINT24:
626 value=(ulonglong) uint3korr(key);
627 break;
628 case HA_KEYTYPE_FLOAT: /* This shouldn't be used */
629 {
630 float f_1;
631 float4get(f_1,key);
632 /* Ignore negative values */
633 value = (f_1 < (float) 0.0) ? 0 : (ulonglong) f_1;
634 break;
635 }
636 case HA_KEYTYPE_DOUBLE: /* This shouldn't be used */
637 {
638 double f_1;
639 float8get(f_1,key);
640 /* Ignore negative values */
641 value = (f_1 < 0.0) ? 0 : (ulonglong) f_1;
642 break;
643 }
644 case HA_KEYTYPE_LONGLONG:
645 s_value= sint8korr(key);
646 break;
647 case HA_KEYTYPE_ULONGLONG:
648 value= uint8korr(key);
649 break;
650 default:
651 DBUG_ASSERT(0);
652 value=0; /* Error */
653 break;
654 }
655
656 /*
657 The following code works because if s_value < 0 then value is 0
658 and if s_value == 0 then value will contain either s_value or the
659 correct value.
660 */
661 return (s_value > 0) ? (ulonglong) s_value : value;
662 }
663