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, |
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 | |
52 | uint _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 | |
219 | uint _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 | |
330 | static 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 | |
466 | err: |
467 | DBUG_RETURN(1); /* Crashed row */ |
468 | } /* _mi_put_key_in_record */ |
469 | |
470 | |
471 | /* Here when key reads are used */ |
472 | |
473 | int _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 | |
512 | ICP_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 | |
547 | ulonglong 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 | |