1 /* Copyright (c) 2000, 2013, 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 as published by
5    the Free Software Foundation; version 2 of the License.
6 
7    This program is distributed in the hope that it will be useful,
8    but WITHOUT ANY WARRANTY; without even the implied warranty of
9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10    GNU General Public License for more details.
11 
12    You should have received a copy of the GNU General Public License
13    along with this program; if not, write to the Free Software
14    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
15 
16 /* Functions to handle keys */
17 
18 #include "myisamdef.h"
19 #include "m_ctype.h"
20 #include "sp_defs.h"
21 #ifdef HAVE_IEEEFP_H
22 #include <ieeefp.h>
23 #endif
24 
25 #define CHECK_KEYS                              /* Enable safety checks */
26 
27 #define FIX_LENGTH(cs, pos, length, char_length)                            \
28             do {                                                            \
29               if (length > char_length)                                     \
30                 char_length= my_charpos(cs, pos, pos+length, char_length);  \
31               set_if_smaller(char_length,length);                           \
32             } while(0)
33 
34 static int _mi_put_key_in_record(MI_INFO *info,uint keynr,uchar *record);
35 
36 /*
37   Make a intern key from a record
38 
39   SYNOPSIS
40     _mi_make_key()
41     info		MyiSAM handler
42     keynr		key number
43     key			Store created key here
44     record		Record
45     filepos		Position to record in the data file
46 
47   RETURN
48     Length of key
49 */
50 
_mi_make_key(register MI_INFO * info,uint keynr,uchar * key,const uchar * record,my_off_t filepos)51 uint _mi_make_key(register MI_INFO *info, uint keynr, uchar *key,
52 		  const uchar *record, my_off_t filepos)
53 {
54   uchar *pos;
55   uchar *start;
56   reg1 HA_KEYSEG *keyseg;
57   my_bool is_ft= info->s->keyinfo[keynr].flag & HA_FULLTEXT;
58   DBUG_ENTER("_mi_make_key");
59 
60   if (info->s->keyinfo[keynr].flag & HA_SPATIAL)
61   {
62     /*
63       TODO: nulls processing
64     */
65 #ifdef HAVE_SPATIAL
66     DBUG_RETURN(sp_make_key(info,keynr,key,record,filepos));
67 #else
68     DBUG_ASSERT(0); /* mi_open should check that this never happens*/
69 #endif
70   }
71 
72   start=key;
73   for (keyseg=info->s->keyinfo[keynr].seg ; keyseg->type ;keyseg++)
74   {
75     enum ha_base_keytype type=(enum ha_base_keytype) keyseg->type;
76     uint length=keyseg->length;
77     uint char_length;
78     CHARSET_INFO *cs=keyseg->charset;
79 
80     if (keyseg->null_bit)
81     {
82       if (record[keyseg->null_pos] & keyseg->null_bit)
83       {
84 	*key++= 0;				/* NULL in key */
85 	continue;
86       }
87       *key++=1;					/* Not NULL */
88     }
89 
90     char_length= ((!is_ft && cs && cs->mbmaxlen > 1) ? length/cs->mbmaxlen :
91                   length);
92 
93     pos= (uchar*) record+keyseg->start;
94     if (type == HA_KEYTYPE_BIT)
95     {
96       if (keyseg->bit_length)
97       {
98         uchar bits= get_rec_bits((uchar*) record + keyseg->bit_pos,
99                                  keyseg->bit_start, keyseg->bit_length);
100         *key++= bits;
101         length--;
102       }
103       memcpy((uchar*) key, pos, length);
104       key+= length;
105       continue;
106     }
107     if (keyseg->flag & HA_SPACE_PACK)
108     {
109       if (type != HA_KEYTYPE_NUM)
110       {
111         length= cs->cset->lengthsp(cs, (char*) pos, length);
112       }
113       else
114       {
115         uchar *end= pos + length;
116 	while (pos < end && pos[0] == ' ')
117 	  pos++;
118 	length=(uint) (end-pos);
119       }
120       FIX_LENGTH(cs, pos, length, char_length);
121       store_key_length_inc(key,char_length);
122       memcpy((uchar*) key,(uchar*) pos,(size_t) char_length);
123       key+=char_length;
124       continue;
125     }
126     if (keyseg->flag & HA_VAR_LENGTH_PART)
127     {
128       uint pack_length= (keyseg->bit_start == 1 ? 1 : 2);
129       uint tmp_length= (pack_length == 1 ? (uint) *(uchar*) pos :
130                         uint2korr(pos));
131       pos+= pack_length;			/* Skip VARCHAR length */
132       set_if_smaller(length,tmp_length);
133       FIX_LENGTH(cs, pos, length, char_length);
134       store_key_length_inc(key,char_length);
135       memcpy((uchar*) key,(uchar*) pos,(size_t) char_length);
136       key+= char_length;
137       continue;
138     }
139     else if (keyseg->flag & HA_BLOB_PART)
140     {
141       uint tmp_length=_mi_calc_blob_length(keyseg->bit_start,pos);
142       memcpy(&pos,pos+keyseg->bit_start,sizeof(char*));
143       set_if_smaller(length,tmp_length);
144       FIX_LENGTH(cs, pos, length, char_length);
145       store_key_length_inc(key,char_length);
146       memcpy((uchar*) key,(uchar*) pos,(size_t) char_length);
147       key+= char_length;
148       continue;
149     }
150     else if (keyseg->flag & HA_SWAP_KEY)
151     {						/* Numerical column */
152 #ifdef HAVE_ISNAN
153       if (type == HA_KEYTYPE_FLOAT)
154       {
155 	float nr;
156 	float4get(nr,pos);
157 	if (isnan(nr))
158 	{
159 	  /* Replace NAN with zero */
160 	  bzero(key,length);
161 	  key+=length;
162 	  continue;
163 	}
164       }
165       else if (type == HA_KEYTYPE_DOUBLE)
166       {
167 	double nr;
168 	float8get(nr,pos);
169 	if (isnan(nr))
170 	{
171 	  bzero(key,length);
172 	  key+=length;
173 	  continue;
174 	}
175       }
176 #endif
177       pos+=length;
178       while (length--)
179       {
180 	*key++ = *--pos;
181       }
182       continue;
183     }
184     FIX_LENGTH(cs, pos, length, char_length);
185     memcpy((uchar*) key, pos, char_length);
186     if (length > char_length)
187       cs->cset->fill(cs, (char*) key+char_length, length-char_length, ' ');
188     key+= length;
189   }
190   _mi_dpointer(info,key,filepos);
191   DBUG_PRINT("exit",("keynr: %d",keynr));
192   DBUG_DUMP("key",(uchar*) start,(uint) (key-start)+keyseg->length);
193   DBUG_EXECUTE("key",
194 	       _mi_print_key(DBUG_FILE,info->s->keyinfo[keynr].seg,start,
195 			     (uint) (key-start)););
196   DBUG_RETURN((uint) (key-start));		/* Return keylength */
197 } /* _mi_make_key */
198 
199 
200 /*
201   Pack a key to intern format from given format (c_rkey)
202 
203   SYNOPSIS
204     _mi_pack_key()
205     info		MyISAM handler
206     uint keynr		key number
207     key			Store packed key here
208     old			Not packed key
209     keypart_map         bitmap of used keyparts
210     last_used_keyseg	out parameter.  May be NULL
211 
212    RETURN
213      length of packed key
214 
215      last_use_keyseg    Store pointer to the keyseg after the last used one
216 */
217 
_mi_pack_key(register MI_INFO * info,uint keynr,uchar * key,uchar * old,key_part_map keypart_map,HA_KEYSEG ** last_used_keyseg)218 uint _mi_pack_key(register MI_INFO *info, uint keynr, uchar *key, uchar *old,
219                   key_part_map keypart_map, HA_KEYSEG **last_used_keyseg)
220 {
221   uchar *start_key=key;
222   HA_KEYSEG *keyseg;
223   my_bool is_ft= info->s->keyinfo[keynr].flag & HA_FULLTEXT;
224   DBUG_ENTER("_mi_pack_key");
225 
226   /* "one part" rtree key is 2*SPDIMS part key in MyISAM */
227   if (info->s->keyinfo[keynr].key_alg == HA_KEY_ALG_RTREE)
228     keypart_map= (((key_part_map)1) << (2*SPDIMS)) - 1;
229 
230   /* only key prefixes are supported */
231   DBUG_ASSERT(((keypart_map+1) & keypart_map) == 0);
232 
233   for (keyseg= info->s->keyinfo[keynr].seg ; keyseg->type && keypart_map;
234        old+= keyseg->length, keyseg++)
235   {
236     enum ha_base_keytype type= (enum ha_base_keytype) keyseg->type;
237     uint length= keyseg->length;
238     uint char_length;
239     uchar *pos;
240     CHARSET_INFO *cs=keyseg->charset;
241 
242     keypart_map>>= 1;
243     if (keyseg->null_bit)
244     {
245       if (!(*key++= (char) 1-*old++))			/* Copy null marker */
246       {
247         if (keyseg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART))
248           old+= 2;
249 	continue;					/* Found NULL */
250       }
251     }
252     char_length= (!is_ft && cs && cs->mbmaxlen > 1) ? length/cs->mbmaxlen : length;
253     pos=old;
254     if (keyseg->flag & HA_SPACE_PACK)
255     {
256       if (type == HA_KEYTYPE_NUM)
257       {
258         uchar *end= pos + length;
259         while (pos < end && pos[0] == ' ')
260           pos++;
261         length= (uint) (end - pos);
262       }
263       else if (type != HA_KEYTYPE_BINARY)
264       {
265         length= cs->cset->lengthsp(cs, (char*) pos, length);
266       }
267       FIX_LENGTH(cs, pos, length, char_length);
268       store_key_length_inc(key,char_length);
269       memcpy((uchar*) key,pos,(size_t) char_length);
270       key+= char_length;
271       continue;
272     }
273     else if (keyseg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART))
274     {
275       /* Length of key-part used with mi_rkey() always 2 */
276       uint tmp_length=uint2korr(pos);
277       pos+=2;
278       set_if_smaller(length,tmp_length);	/* Safety */
279       FIX_LENGTH(cs, pos, length, char_length);
280       store_key_length_inc(key,char_length);
281       old+=2;					/* Skip length */
282       memcpy((uchar*) key, pos,(size_t) char_length);
283       key+= char_length;
284       continue;
285     }
286     else if (keyseg->flag & HA_SWAP_KEY)
287     {						/* Numerical column */
288       pos+=length;
289       while (length--)
290 	*key++ = *--pos;
291       continue;
292     }
293     FIX_LENGTH(cs, pos, length, char_length);
294     memcpy((uchar*) key, pos, char_length);
295     if (length > char_length)
296       cs->cset->fill(cs, (char*) key+char_length, length-char_length, ' ');
297     key+= length;
298   }
299   if (last_used_keyseg)
300     *last_used_keyseg= keyseg;
301 
302   DBUG_RETURN((uint) (key-start_key));
303 } /* _mi_pack_key */
304 
305 
306 
307 /*
308   Store found key in record
309 
310   SYNOPSIS
311     _mi_put_key_in_record()
312     info		MyISAM handler
313     keynr		Key number that was used
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(register MI_INFO * info,uint keynr,uchar * record)326 static int _mi_put_key_in_record(register MI_INFO *info, uint keynr,
327 				 uchar *record)
328 {
329   reg2 uchar *key;
330   uchar *pos,*key_end;
331   reg1 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 #ifdef CHECK_KEYS
374       if (length > keyseg->length || key+length > key_end)
375 	goto err;
376 #endif
377       pos= record+keyseg->start;
378       if (keyseg->type != (int) HA_KEYTYPE_NUM)
379       {
380         memcpy(pos,key,(size_t) length);
381         keyseg->charset->cset->fill(keyseg->charset,
382                                     (char*) pos + length,
383                                     keyseg->length - length,
384                                     ' ');
385       }
386       else
387       {
388 	bfill(pos,keyseg->length-length,' ');
389 	memcpy(pos+keyseg->length-length,key,(size_t) length);
390       }
391       key+=length;
392       continue;
393     }
394 
395     if (keyseg->flag & HA_VAR_LENGTH_PART)
396     {
397       uint length;
398       get_key_length(length,key);
399 #ifdef CHECK_KEYS
400       if (length > keyseg->length || key+length > key_end)
401 	goto err;
402 #endif
403       /* Store key length */
404       if (keyseg->bit_start == 1)
405         *(uchar*) (record+keyseg->start)= (uchar) length;
406       else
407         int2store(record+keyseg->start, length);
408       /* And key data */
409       memcpy(record+keyseg->start + keyseg->bit_start, (uchar*) key, length);
410       key+= length;
411     }
412     else if (keyseg->flag & HA_BLOB_PART)
413     {
414       uint length;
415       get_key_length(length,key);
416 #ifdef CHECK_KEYS
417       if (length > keyseg->length || key+length > key_end)
418 	goto err;
419 #endif
420       memcpy(record+keyseg->start+keyseg->bit_start,
421 	     &blob_ptr,sizeof(char*));
422       memcpy(blob_ptr,key,length);
423       blob_ptr+=length;
424 
425       /* The above changed info->lastkey2. Inform mi_rnext_same(). */
426       info->update&= ~HA_STATE_RNEXT_SAME;
427 
428       _my_store_blob_length(record+keyseg->start,
429 			    (uint) keyseg->bit_start,length);
430       key+=length;
431     }
432     else if (keyseg->flag & HA_SWAP_KEY)
433     {
434       uchar *to=  record+keyseg->start+keyseg->length;
435       uchar *end= key+keyseg->length;
436 #ifdef CHECK_KEYS
437       if (end > key_end)
438 	goto err;
439 #endif
440       do
441       {
442 	 *--to= *key++;
443       } while (key != end);
444       continue;
445     }
446     else
447     {
448 #ifdef CHECK_KEYS
449       if (key+keyseg->length > key_end)
450 	goto err;
451 #endif
452       memcpy(record+keyseg->start,(uchar*) key,
453 	     (size_t) keyseg->length);
454       key+= keyseg->length;
455     }
456   }
457   DBUG_RETURN(0);
458 
459 err:
460   DBUG_RETURN(1);				/* Crashed row */
461 } /* _mi_put_key_in_record */
462 
463 
464 	/* Here when key reads are used */
465 
_mi_read_key_record(MI_INFO * info,my_off_t filepos,uchar * buf)466 int _mi_read_key_record(MI_INFO *info, my_off_t filepos, uchar *buf)
467 {
468   fast_mi_writeinfo(info);
469   if (filepos != HA_OFFSET_ERROR)
470   {
471     if (info->lastinx >= 0)
472     {				/* Read only key */
473       if (_mi_put_key_in_record(info,(uint) info->lastinx,buf))
474       {
475         mi_print_error(info->s, HA_ERR_CRASHED);
476 	my_errno=HA_ERR_CRASHED;
477 	return -1;
478       }
479       info->update|= HA_STATE_AKTIV; /* We should find a record */
480       return 0;
481     }
482     my_errno=HA_ERR_WRONG_INDEX;
483   }
484   return(-1);				/* Wrong data to read */
485 }
486 
487 
488 /*
489   Retrieve auto_increment info
490 
491   SYNOPSIS
492     retrieve_auto_increment()
493     info			MyISAM handler
494     record			Row to update
495 
496   IMPLEMENTATION
497     For signed columns we don't retrieve the auto increment value if it's
498     less than zero.
499 */
500 
retrieve_auto_increment(MI_INFO * info,const uchar * record)501 ulonglong retrieve_auto_increment(MI_INFO *info,const uchar *record)
502 {
503   ulonglong value= 0;			/* Store unsigned values here */
504   longlong s_value= 0;			/* Store signed values here */
505   HA_KEYSEG *keyseg= info->s->keyinfo[info->s->base.auto_key-1].seg;
506   const uchar *key= (uchar*) record + keyseg->start;
507 
508   switch (keyseg->type) {
509   case HA_KEYTYPE_INT8:
510     s_value= (longlong) *(char*)key;
511     break;
512   case HA_KEYTYPE_BINARY:
513     value=(ulonglong)  *(uchar*) key;
514     break;
515   case HA_KEYTYPE_SHORT_INT:
516     s_value= (longlong) sint2korr(key);
517     break;
518   case HA_KEYTYPE_USHORT_INT:
519     value=(ulonglong) uint2korr(key);
520     break;
521   case HA_KEYTYPE_LONG_INT:
522     s_value= (longlong) sint4korr(key);
523     break;
524   case HA_KEYTYPE_ULONG_INT:
525     value=(ulonglong) uint4korr(key);
526     break;
527   case HA_KEYTYPE_INT24:
528     s_value= (longlong) sint3korr(key);
529     break;
530   case HA_KEYTYPE_UINT24:
531     value=(ulonglong) uint3korr(key);
532     break;
533   case HA_KEYTYPE_FLOAT:                        /* This shouldn't be used */
534   {
535     float f_1;
536     float4get(f_1,key);
537     /* Ignore negative values */
538     value = (f_1 < (float) 0.0) ? 0 : (ulonglong) f_1;
539     break;
540   }
541   case HA_KEYTYPE_DOUBLE:                       /* This shouldn't be used */
542   {
543     double f_1;
544     float8get(f_1,key);
545     /* Ignore negative values */
546     value = (f_1 < 0.0) ? 0 : (ulonglong) f_1;
547     break;
548   }
549   case HA_KEYTYPE_LONGLONG:
550     s_value= sint8korr(key);
551     break;
552   case HA_KEYTYPE_ULONGLONG:
553     value= uint8korr(key);
554     break;
555   default:
556     DBUG_ASSERT(0);
557     value=0;                                    /* Error */
558     break;
559   }
560 
561   /*
562     The following code works becasue if s_value < 0 then value is 0
563     and if s_value == 0 then value will contain either s_value or the
564     correct value.
565   */
566   return (s_value > 0) ? (ulonglong) s_value : value;
567 }
568