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