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