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