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