1 | /***************************************************************************** |
2 | |
3 | Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved. |
4 | Copyright (c) 2017, 2018, MariaDB Corporation. |
5 | |
6 | This program is free software; you can redistribute it and/or modify it under |
7 | the terms of the GNU General Public License as published by the Free Software |
8 | Foundation; version 2 of the License. |
9 | |
10 | This program is distributed in the hope that it will be useful, but WITHOUT |
11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
12 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU General Public License along with |
15 | this program; if not, write to the Free Software Foundation, Inc., |
16 | 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA |
17 | |
18 | *****************************************************************************/ |
19 | |
20 | /********************************************************************//** |
21 | @file data/data0data.cc |
22 | SQL data field and tuple |
23 | |
24 | Created 5/30/1994 Heikki Tuuri |
25 | *************************************************************************/ |
26 | |
27 | #include "ha_prototypes.h" |
28 | |
29 | #include "data0data.h" |
30 | #include "rem0rec.h" |
31 | #include "rem0cmp.h" |
32 | #include "page0page.h" |
33 | #include "page0zip.h" |
34 | #include "dict0dict.h" |
35 | #include "btr0cur.h" |
36 | #include "row0upd.h" |
37 | |
38 | #ifdef UNIV_DEBUG |
39 | /** Dummy variable to catch access to uninitialized fields. In the |
40 | debug version, dtuple_create() will make all fields of dtuple_t point |
41 | to data_error. */ |
42 | byte data_error; |
43 | #endif /* UNIV_DEBUG */ |
44 | |
45 | /** Trim the tail of an index tuple before insert or update. |
46 | After instant ADD COLUMN, if the last fields of a clustered index tuple |
47 | match the 'default row', there will be no need to store them. |
48 | NOTE: A page latch in the index must be held, so that the index |
49 | may not lose 'instantness' before the trimmed tuple has been |
50 | inserted or updated. |
51 | @param[in] index index possibly with instantly added columns */ |
52 | void dtuple_t::trim(const dict_index_t& index) |
53 | { |
54 | ut_ad(n_fields >= index.n_core_fields); |
55 | ut_ad(n_fields <= index.n_fields); |
56 | ut_ad(index.is_instant()); |
57 | |
58 | ulint i = n_fields; |
59 | for (; i > index.n_core_fields; i--) { |
60 | const dfield_t* dfield = dtuple_get_nth_field(this, i - 1); |
61 | const dict_col_t* col = dict_index_get_nth_col(&index, i - 1); |
62 | ut_ad(col->is_instant()); |
63 | ulint len = dfield_get_len(dfield); |
64 | if (len != col->def_val.len) { |
65 | break; |
66 | } |
67 | |
68 | if (len != 0 && len != UNIV_SQL_NULL |
69 | && dfield->data != col->def_val.data |
70 | && memcmp(dfield->data, col->def_val.data, len)) { |
71 | break; |
72 | } |
73 | } |
74 | |
75 | n_fields = i; |
76 | } |
77 | |
78 | /** Compare two data tuples. |
79 | @param[in] tuple1 first data tuple |
80 | @param[in] tuple2 second data tuple |
81 | @return positive, 0, negative if tuple1 is greater, equal, less, than tuple2, |
82 | respectively */ |
83 | int |
84 | dtuple_coll_cmp( |
85 | const dtuple_t* tuple1, |
86 | const dtuple_t* tuple2) |
87 | { |
88 | ulint n_fields; |
89 | ulint i; |
90 | int cmp; |
91 | |
92 | ut_ad(tuple1 != NULL); |
93 | ut_ad(tuple2 != NULL); |
94 | ut_ad(tuple1->magic_n == DATA_TUPLE_MAGIC_N); |
95 | ut_ad(tuple2->magic_n == DATA_TUPLE_MAGIC_N); |
96 | ut_ad(dtuple_check_typed(tuple1)); |
97 | ut_ad(dtuple_check_typed(tuple2)); |
98 | |
99 | n_fields = dtuple_get_n_fields(tuple1); |
100 | |
101 | cmp = (int) n_fields - (int) dtuple_get_n_fields(tuple2); |
102 | |
103 | for (i = 0; cmp == 0 && i < n_fields; i++) { |
104 | const dfield_t* field1 = dtuple_get_nth_field(tuple1, i); |
105 | const dfield_t* field2 = dtuple_get_nth_field(tuple2, i); |
106 | cmp = cmp_dfield_dfield(field1, field2); |
107 | } |
108 | |
109 | return(cmp); |
110 | } |
111 | |
112 | /*********************************************************************//** |
113 | Sets number of fields used in a tuple. Normally this is set in |
114 | dtuple_create, but if you want later to set it smaller, you can use this. */ |
115 | void |
116 | dtuple_set_n_fields( |
117 | /*================*/ |
118 | dtuple_t* tuple, /*!< in: tuple */ |
119 | ulint n_fields) /*!< in: number of fields */ |
120 | { |
121 | ut_ad(tuple); |
122 | |
123 | tuple->n_fields = n_fields; |
124 | tuple->n_fields_cmp = n_fields; |
125 | } |
126 | |
127 | /**********************************************************//** |
128 | Checks that a data field is typed. |
129 | @return TRUE if ok */ |
130 | static |
131 | ibool |
132 | dfield_check_typed_no_assert( |
133 | /*=========================*/ |
134 | const dfield_t* field) /*!< in: data field */ |
135 | { |
136 | if (dfield_get_type(field)->mtype > DATA_MTYPE_CURRENT_MAX |
137 | || dfield_get_type(field)->mtype < DATA_MTYPE_CURRENT_MIN) { |
138 | |
139 | ib::error() << "Data field type " |
140 | << dfield_get_type(field)->mtype |
141 | << ", len " << dfield_get_len(field); |
142 | |
143 | return(FALSE); |
144 | } |
145 | |
146 | return(TRUE); |
147 | } |
148 | |
149 | /**********************************************************//** |
150 | Checks that a data tuple is typed. |
151 | @return TRUE if ok */ |
152 | static |
153 | ibool |
154 | dtuple_check_typed_no_assert( |
155 | /*=========================*/ |
156 | const dtuple_t* tuple) /*!< in: tuple */ |
157 | { |
158 | const dfield_t* field; |
159 | ulint i; |
160 | |
161 | if (dtuple_get_n_fields(tuple) > REC_MAX_N_FIELDS) { |
162 | ib::error() << "Index entry has " |
163 | << dtuple_get_n_fields(tuple) << " fields" ; |
164 | dump: |
165 | fputs("InnoDB: Tuple contents: " , stderr); |
166 | dtuple_print(stderr, tuple); |
167 | putc('\n', stderr); |
168 | |
169 | return(FALSE); |
170 | } |
171 | |
172 | for (i = 0; i < dtuple_get_n_fields(tuple); i++) { |
173 | |
174 | field = dtuple_get_nth_field(tuple, i); |
175 | |
176 | if (!dfield_check_typed_no_assert(field)) { |
177 | goto dump; |
178 | } |
179 | } |
180 | |
181 | return(TRUE); |
182 | } |
183 | |
184 | #ifdef UNIV_DEBUG |
185 | /**********************************************************//** |
186 | Checks that a data field is typed. Asserts an error if not. |
187 | @return TRUE if ok */ |
188 | ibool |
189 | dfield_check_typed( |
190 | /*===============*/ |
191 | const dfield_t* field) /*!< in: data field */ |
192 | { |
193 | if (dfield_get_type(field)->mtype > DATA_MTYPE_CURRENT_MAX |
194 | || dfield_get_type(field)->mtype < DATA_MTYPE_CURRENT_MIN) { |
195 | |
196 | ib::fatal() << "Data field type " |
197 | << dfield_get_type(field)->mtype |
198 | << ", len " << dfield_get_len(field); |
199 | } |
200 | |
201 | return(TRUE); |
202 | } |
203 | |
204 | /**********************************************************//** |
205 | Checks that a data tuple is typed. Asserts an error if not. |
206 | @return TRUE if ok */ |
207 | ibool |
208 | dtuple_check_typed( |
209 | /*===============*/ |
210 | const dtuple_t* tuple) /*!< in: tuple */ |
211 | { |
212 | const dfield_t* field; |
213 | ulint i; |
214 | |
215 | for (i = 0; i < dtuple_get_n_fields(tuple); i++) { |
216 | |
217 | field = dtuple_get_nth_field(tuple, i); |
218 | |
219 | ut_a(dfield_check_typed(field)); |
220 | } |
221 | |
222 | return(TRUE); |
223 | } |
224 | |
225 | /**********************************************************//** |
226 | Validates the consistency of a tuple which must be complete, i.e, |
227 | all fields must have been set. |
228 | @return TRUE if ok */ |
229 | ibool |
230 | dtuple_validate( |
231 | /*============*/ |
232 | const dtuple_t* tuple) /*!< in: tuple */ |
233 | { |
234 | const dfield_t* field; |
235 | ulint n_fields; |
236 | ulint len; |
237 | ulint i; |
238 | |
239 | ut_ad(tuple->magic_n == DATA_TUPLE_MAGIC_N); |
240 | |
241 | n_fields = dtuple_get_n_fields(tuple); |
242 | |
243 | /* We dereference all the data of each field to test |
244 | for memory traps */ |
245 | |
246 | for (i = 0; i < n_fields; i++) { |
247 | |
248 | field = dtuple_get_nth_field(tuple, i); |
249 | len = dfield_get_len(field); |
250 | |
251 | if (!dfield_is_null(field)) { |
252 | |
253 | const byte* data; |
254 | |
255 | data = static_cast<const byte*>(dfield_get_data(field)); |
256 | #ifndef UNIV_DEBUG_VALGRIND |
257 | ulint j; |
258 | |
259 | for (j = 0; j < len; j++) { |
260 | data++; |
261 | } |
262 | #endif /* !UNIV_DEBUG_VALGRIND */ |
263 | |
264 | UNIV_MEM_ASSERT_RW(data, len); |
265 | } |
266 | } |
267 | |
268 | ut_a(dtuple_check_typed(tuple)); |
269 | |
270 | return(TRUE); |
271 | } |
272 | #endif /* UNIV_DEBUG */ |
273 | |
274 | /*************************************************************//** |
275 | Pretty prints a dfield value according to its data type. */ |
276 | void |
277 | dfield_print( |
278 | /*=========*/ |
279 | const dfield_t* dfield) /*!< in: dfield */ |
280 | { |
281 | const byte* data; |
282 | ulint len; |
283 | ulint i; |
284 | |
285 | len = dfield_get_len(dfield); |
286 | data = static_cast<const byte*>(dfield_get_data(dfield)); |
287 | |
288 | if (dfield_is_null(dfield)) { |
289 | fputs("NULL" , stderr); |
290 | |
291 | return; |
292 | } |
293 | |
294 | switch (dtype_get_mtype(dfield_get_type(dfield))) { |
295 | case DATA_CHAR: |
296 | case DATA_VARCHAR: |
297 | for (i = 0; i < len; i++) { |
298 | int c = *data++; |
299 | putc(isprint(c) ? c : ' ', stderr); |
300 | } |
301 | |
302 | if (dfield_is_ext(dfield)) { |
303 | fputs("(external)" , stderr); |
304 | } |
305 | break; |
306 | case DATA_INT: |
307 | ut_a(len == 4); /* only works for 32-bit integers */ |
308 | fprintf(stderr, "%d" , (int) mach_read_from_4(data)); |
309 | break; |
310 | default: |
311 | ut_error; |
312 | } |
313 | } |
314 | |
315 | /*************************************************************//** |
316 | Pretty prints a dfield value according to its data type. Also the hex string |
317 | is printed if a string contains non-printable characters. */ |
318 | void |
319 | dfield_print_also_hex( |
320 | /*==================*/ |
321 | const dfield_t* dfield) /*!< in: dfield */ |
322 | { |
323 | const byte* data; |
324 | ulint len; |
325 | ulint prtype; |
326 | ulint i; |
327 | ibool print_also_hex; |
328 | |
329 | len = dfield_get_len(dfield); |
330 | data = static_cast<const byte*>(dfield_get_data(dfield)); |
331 | |
332 | if (dfield_is_null(dfield)) { |
333 | fputs("NULL" , stderr); |
334 | |
335 | return; |
336 | } |
337 | |
338 | prtype = dtype_get_prtype(dfield_get_type(dfield)); |
339 | |
340 | switch (dtype_get_mtype(dfield_get_type(dfield))) { |
341 | ib_id_t id; |
342 | case DATA_INT: |
343 | switch (len) { |
344 | ulint val; |
345 | case 1: |
346 | val = mach_read_from_1(data); |
347 | |
348 | if (!(prtype & DATA_UNSIGNED)) { |
349 | val &= ~0x80U; |
350 | fprintf(stderr, "%ld" , (long) val); |
351 | } else { |
352 | fprintf(stderr, "%lu" , (ulong) val); |
353 | } |
354 | break; |
355 | |
356 | case 2: |
357 | val = mach_read_from_2(data); |
358 | |
359 | if (!(prtype & DATA_UNSIGNED)) { |
360 | val &= ~0x8000U; |
361 | fprintf(stderr, "%ld" , (long) val); |
362 | } else { |
363 | fprintf(stderr, "%lu" , (ulong) val); |
364 | } |
365 | break; |
366 | |
367 | case 3: |
368 | val = mach_read_from_3(data); |
369 | |
370 | if (!(prtype & DATA_UNSIGNED)) { |
371 | val &= ~0x800000U; |
372 | fprintf(stderr, "%ld" , (long) val); |
373 | } else { |
374 | fprintf(stderr, "%lu" , (ulong) val); |
375 | } |
376 | break; |
377 | |
378 | case 4: |
379 | val = mach_read_from_4(data); |
380 | |
381 | if (!(prtype & DATA_UNSIGNED)) { |
382 | val &= ~0x80000000; |
383 | fprintf(stderr, "%ld" , (long) val); |
384 | } else { |
385 | fprintf(stderr, "%lu" , (ulong) val); |
386 | } |
387 | break; |
388 | |
389 | case 6: |
390 | id = mach_read_from_6(data); |
391 | fprintf(stderr, IB_ID_FMT, id); |
392 | break; |
393 | |
394 | case 7: |
395 | id = mach_read_from_7(data); |
396 | fprintf(stderr, IB_ID_FMT, id); |
397 | break; |
398 | case 8: |
399 | id = mach_read_from_8(data); |
400 | fprintf(stderr, IB_ID_FMT, id); |
401 | break; |
402 | default: |
403 | goto print_hex; |
404 | } |
405 | break; |
406 | |
407 | case DATA_SYS: |
408 | switch (prtype & DATA_SYS_PRTYPE_MASK) { |
409 | case DATA_TRX_ID: |
410 | id = mach_read_from_6(data); |
411 | |
412 | fprintf(stderr, "trx_id " TRX_ID_FMT, id); |
413 | break; |
414 | |
415 | case DATA_ROLL_PTR: |
416 | id = mach_read_from_7(data); |
417 | |
418 | fprintf(stderr, "roll_ptr " TRX_ID_FMT, id); |
419 | break; |
420 | |
421 | case DATA_ROW_ID: |
422 | id = mach_read_from_6(data); |
423 | |
424 | fprintf(stderr, "row_id " TRX_ID_FMT, id); |
425 | break; |
426 | |
427 | default: |
428 | goto print_hex; |
429 | } |
430 | break; |
431 | |
432 | case DATA_CHAR: |
433 | case DATA_VARCHAR: |
434 | print_also_hex = FALSE; |
435 | |
436 | for (i = 0; i < len; i++) { |
437 | int c = *data++; |
438 | |
439 | if (!isprint(c)) { |
440 | print_also_hex = TRUE; |
441 | |
442 | fprintf(stderr, "\\x%02x" , (unsigned char) c); |
443 | } else { |
444 | putc(c, stderr); |
445 | } |
446 | } |
447 | |
448 | if (dfield_is_ext(dfield)) { |
449 | fputs("(external)" , stderr); |
450 | } |
451 | |
452 | if (!print_also_hex) { |
453 | break; |
454 | } |
455 | |
456 | data = static_cast<byte*>(dfield_get_data(dfield)); |
457 | /* fall through */ |
458 | |
459 | case DATA_BINARY: |
460 | default: |
461 | print_hex: |
462 | fputs(" Hex: " ,stderr); |
463 | |
464 | for (i = 0; i < len; i++) { |
465 | fprintf(stderr, "%02x" , *data++); |
466 | } |
467 | |
468 | if (dfield_is_ext(dfield)) { |
469 | fputs("(external)" , stderr); |
470 | } |
471 | } |
472 | } |
473 | |
474 | /*************************************************************//** |
475 | Print a dfield value using ut_print_buf. */ |
476 | static |
477 | void |
478 | dfield_print_raw( |
479 | /*=============*/ |
480 | FILE* f, /*!< in: output stream */ |
481 | const dfield_t* dfield) /*!< in: dfield */ |
482 | { |
483 | ulint len = dfield_get_len(dfield); |
484 | if (!dfield_is_null(dfield)) { |
485 | ulint print_len = ut_min(len, static_cast<ulint>(1000)); |
486 | ut_print_buf(f, dfield_get_data(dfield), print_len); |
487 | if (len != print_len) { |
488 | fprintf(f, "(total %lu bytes%s)" , |
489 | (ulong) len, |
490 | dfield_is_ext(dfield) ? ", external" : "" ); |
491 | } |
492 | } else { |
493 | fputs(" SQL NULL" , f); |
494 | } |
495 | } |
496 | |
497 | /**********************************************************//** |
498 | The following function prints the contents of a tuple. */ |
499 | void |
500 | dtuple_print( |
501 | /*=========*/ |
502 | FILE* f, /*!< in: output stream */ |
503 | const dtuple_t* tuple) /*!< in: tuple */ |
504 | { |
505 | ulint n_fields; |
506 | ulint i; |
507 | |
508 | n_fields = dtuple_get_n_fields(tuple); |
509 | |
510 | fprintf(f, "DATA TUPLE: %lu fields;\n" , (ulong) n_fields); |
511 | |
512 | for (i = 0; i < n_fields; i++) { |
513 | fprintf(f, " %lu:" , (ulong) i); |
514 | |
515 | dfield_print_raw(f, dtuple_get_nth_field(tuple, i)); |
516 | |
517 | putc(';', f); |
518 | putc('\n', f); |
519 | } |
520 | |
521 | ut_ad(dtuple_validate(tuple)); |
522 | } |
523 | |
524 | /** Print the contents of a tuple. |
525 | @param[out] o output stream |
526 | @param[in] field array of data fields |
527 | @param[in] n number of data fields */ |
528 | void |
529 | dfield_print( |
530 | std::ostream& o, |
531 | const dfield_t* field, |
532 | ulint n) |
533 | { |
534 | for (ulint i = 0; i < n; i++, field++) { |
535 | const void* data = dfield_get_data(field); |
536 | const ulint len = dfield_get_len(field); |
537 | |
538 | if (i) { |
539 | o << ','; |
540 | } |
541 | |
542 | if (dfield_is_null(field)) { |
543 | o << "NULL" ; |
544 | } else if (dfield_is_ext(field)) { |
545 | ulint local_len = len - BTR_EXTERN_FIELD_REF_SIZE; |
546 | ut_ad(len >= BTR_EXTERN_FIELD_REF_SIZE); |
547 | |
548 | o << '[' |
549 | << local_len |
550 | << '+' << BTR_EXTERN_FIELD_REF_SIZE << ']'; |
551 | ut_print_buf(o, data, local_len); |
552 | ut_print_buf_hex(o, static_cast<const byte*>(data) |
553 | + local_len, |
554 | BTR_EXTERN_FIELD_REF_SIZE); |
555 | } else { |
556 | o << '[' << len << ']'; |
557 | ut_print_buf(o, data, len); |
558 | } |
559 | } |
560 | } |
561 | |
562 | /** Print the contents of a tuple. |
563 | @param[out] o output stream |
564 | @param[in] tuple data tuple */ |
565 | void |
566 | dtuple_print( |
567 | std::ostream& o, |
568 | const dtuple_t* tuple) |
569 | { |
570 | const ulint n = dtuple_get_n_fields(tuple); |
571 | |
572 | o << "TUPLE (info_bits=" << dtuple_get_info_bits(tuple) |
573 | << ", " << n << " fields): {" ; |
574 | |
575 | dfield_print(o, tuple->fields, n); |
576 | |
577 | o << "}" ; |
578 | } |
579 | |
580 | /**************************************************************//** |
581 | Moves parts of long fields in entry to the big record vector so that |
582 | the size of tuple drops below the maximum record size allowed in the |
583 | database. Moves data only from those fields which are not necessary |
584 | to determine uniquely the insertion place of the tuple in the index. |
585 | @return own: created big record vector, NULL if we are not able to |
586 | shorten the entry enough, i.e., if there are too many fixed-length or |
587 | short fields in entry or the index is clustered */ |
588 | big_rec_t* |
589 | dtuple_convert_big_rec( |
590 | /*===================*/ |
591 | dict_index_t* index, /*!< in: index */ |
592 | upd_t* upd, /*!< in/out: update vector */ |
593 | dtuple_t* entry, /*!< in/out: index entry */ |
594 | ulint* n_ext) /*!< in/out: number of |
595 | externally stored columns */ |
596 | { |
597 | mem_heap_t* heap; |
598 | big_rec_t* vector; |
599 | dfield_t* dfield; |
600 | dict_field_t* ifield; |
601 | ulint size; |
602 | ulint n_fields; |
603 | ulint local_len; |
604 | ulint local_prefix_len; |
605 | |
606 | if (!dict_index_is_clust(index)) { |
607 | return(NULL); |
608 | } |
609 | |
610 | if (!dict_table_has_atomic_blobs(index->table)) { |
611 | /* up to MySQL 5.1: store a 768-byte prefix locally */ |
612 | local_len = BTR_EXTERN_FIELD_REF_SIZE |
613 | + DICT_ANTELOPE_MAX_INDEX_COL_LEN; |
614 | } else { |
615 | /* new-format table: do not store any BLOB prefix locally */ |
616 | local_len = BTR_EXTERN_FIELD_REF_SIZE; |
617 | } |
618 | |
619 | ut_a(dtuple_check_typed_no_assert(entry)); |
620 | |
621 | size = rec_get_converted_size(index, entry, *n_ext); |
622 | |
623 | if (UNIV_UNLIKELY(size > 1000000000)) { |
624 | ib::warn() << "Tuple size is very big: " << size; |
625 | fputs("InnoDB: Tuple contents: " , stderr); |
626 | dtuple_print(stderr, entry); |
627 | putc('\n', stderr); |
628 | } |
629 | |
630 | heap = mem_heap_create(size + dtuple_get_n_fields(entry) |
631 | * sizeof(big_rec_field_t) + 1000); |
632 | |
633 | vector = big_rec_t::alloc(heap, dtuple_get_n_fields(entry)); |
634 | |
635 | /* Decide which fields to shorten: the algorithm is to look for |
636 | a variable-length field that yields the biggest savings when |
637 | stored externally */ |
638 | |
639 | n_fields = 0; |
640 | |
641 | while (page_zip_rec_needs_ext(rec_get_converted_size(index, entry, |
642 | *n_ext), |
643 | dict_table_is_comp(index->table), |
644 | dict_index_get_n_fields(index), |
645 | dict_table_page_size(index->table))) { |
646 | |
647 | ulint i; |
648 | ulint longest = 0; |
649 | ulint longest_i = ULINT_MAX; |
650 | byte* data; |
651 | |
652 | for (i = dict_index_get_n_unique_in_tree(index); |
653 | i < dtuple_get_n_fields(entry); i++) { |
654 | ulint savings; |
655 | |
656 | dfield = dtuple_get_nth_field(entry, i); |
657 | ifield = dict_index_get_nth_field(index, i); |
658 | |
659 | /* Skip fixed-length, NULL, externally stored, |
660 | or short columns */ |
661 | |
662 | if (ifield->fixed_len |
663 | || dfield_is_null(dfield) |
664 | || dfield_is_ext(dfield) |
665 | || dfield_get_len(dfield) <= local_len |
666 | || dfield_get_len(dfield) |
667 | <= BTR_EXTERN_LOCAL_STORED_MAX_SIZE) { |
668 | goto skip_field; |
669 | } |
670 | |
671 | savings = dfield_get_len(dfield) - local_len; |
672 | |
673 | /* Check that there would be savings */ |
674 | if (longest >= savings) { |
675 | goto skip_field; |
676 | } |
677 | |
678 | /* In DYNAMIC and COMPRESSED format, store |
679 | locally any non-BLOB columns whose maximum |
680 | length does not exceed 256 bytes. This is |
681 | because there is no room for the "external |
682 | storage" flag when the maximum length is 255 |
683 | bytes or less. This restriction trivially |
684 | holds in REDUNDANT and COMPACT format, because |
685 | there we always store locally columns whose |
686 | length is up to local_len == 788 bytes. |
687 | @see rec_init_offsets_comp_ordinary */ |
688 | if (!DATA_BIG_COL(ifield->col)) { |
689 | goto skip_field; |
690 | } |
691 | |
692 | longest_i = i; |
693 | longest = savings; |
694 | |
695 | skip_field: |
696 | continue; |
697 | } |
698 | |
699 | if (!longest) { |
700 | /* Cannot shorten more */ |
701 | |
702 | mem_heap_free(heap); |
703 | |
704 | return(NULL); |
705 | } |
706 | |
707 | /* Move data from field longest_i to big rec vector. |
708 | |
709 | We store the first bytes locally to the record. Then |
710 | we can calculate all ordering fields in all indexes |
711 | from locally stored data. */ |
712 | |
713 | dfield = dtuple_get_nth_field(entry, longest_i); |
714 | ifield = dict_index_get_nth_field(index, longest_i); |
715 | local_prefix_len = local_len - BTR_EXTERN_FIELD_REF_SIZE; |
716 | |
717 | vector->append( |
718 | big_rec_field_t( |
719 | longest_i, |
720 | dfield_get_len(dfield) - local_prefix_len, |
721 | static_cast<char*>(dfield_get_data(dfield)) |
722 | + local_prefix_len)); |
723 | |
724 | /* Allocate the locally stored part of the column. */ |
725 | data = static_cast<byte*>(mem_heap_alloc(heap, local_len)); |
726 | |
727 | /* Copy the local prefix. */ |
728 | memcpy(data, dfield_get_data(dfield), local_prefix_len); |
729 | /* Clear the extern field reference (BLOB pointer). */ |
730 | memset(data + local_prefix_len, 0, BTR_EXTERN_FIELD_REF_SIZE); |
731 | #if 0 |
732 | /* The following would fail the Valgrind checks in |
733 | page_cur_insert_rec_low() and page_cur_insert_rec_zip(). |
734 | The BLOB pointers in the record will be initialized after |
735 | the record and the BLOBs have been written. */ |
736 | UNIV_MEM_ALLOC(data + local_prefix_len, |
737 | BTR_EXTERN_FIELD_REF_SIZE); |
738 | #endif |
739 | |
740 | dfield_set_data(dfield, data, local_len); |
741 | dfield_set_ext(dfield); |
742 | |
743 | n_fields++; |
744 | (*n_ext)++; |
745 | ut_ad(n_fields < dtuple_get_n_fields(entry)); |
746 | |
747 | if (upd && !upd->is_modified(longest_i)) { |
748 | |
749 | DEBUG_SYNC_C("ib_mv_nonupdated_column_offpage" ); |
750 | |
751 | upd_field_t upd_field; |
752 | upd_field.field_no = unsigned(longest_i); |
753 | upd_field.orig_len = 0; |
754 | upd_field.exp = NULL; |
755 | upd_field.old_v_val = NULL; |
756 | dfield_copy(&upd_field.new_val, |
757 | dfield->clone(upd->heap)); |
758 | upd->append(upd_field); |
759 | ut_ad(upd->is_modified(longest_i)); |
760 | |
761 | ut_ad(upd_field.new_val.len |
762 | >= BTR_EXTERN_FIELD_REF_SIZE); |
763 | ut_ad(upd_field.new_val.len == local_len); |
764 | ut_ad(upd_field.new_val.len == dfield_get_len(dfield)); |
765 | } |
766 | } |
767 | |
768 | ut_ad(n_fields == vector->n_fields); |
769 | |
770 | return(vector); |
771 | } |
772 | |
773 | /**************************************************************//** |
774 | Puts back to entry the data stored in vector. Note that to ensure the |
775 | fields in entry can accommodate the data, vector must have been created |
776 | from entry with dtuple_convert_big_rec. */ |
777 | void |
778 | dtuple_convert_back_big_rec( |
779 | /*========================*/ |
780 | dict_index_t* index MY_ATTRIBUTE((unused)), /*!< in: index */ |
781 | dtuple_t* entry, /*!< in: entry whose data was put to vector */ |
782 | big_rec_t* vector) /*!< in, own: big rec vector; it is |
783 | freed in this function */ |
784 | { |
785 | big_rec_field_t* b = vector->fields; |
786 | const big_rec_field_t* const end = b + vector->n_fields; |
787 | |
788 | for (; b < end; b++) { |
789 | dfield_t* dfield; |
790 | ulint local_len; |
791 | |
792 | dfield = dtuple_get_nth_field(entry, b->field_no); |
793 | local_len = dfield_get_len(dfield); |
794 | |
795 | ut_ad(dfield_is_ext(dfield)); |
796 | ut_ad(local_len >= BTR_EXTERN_FIELD_REF_SIZE); |
797 | |
798 | local_len -= BTR_EXTERN_FIELD_REF_SIZE; |
799 | |
800 | /* Only in REDUNDANT and COMPACT format, we store |
801 | up to DICT_ANTELOPE_MAX_INDEX_COL_LEN (768) bytes |
802 | locally */ |
803 | ut_ad(local_len <= DICT_ANTELOPE_MAX_INDEX_COL_LEN); |
804 | |
805 | dfield_set_data(dfield, |
806 | (char*) b->data - local_len, |
807 | b->len + local_len); |
808 | } |
809 | |
810 | mem_heap_free(vector->heap); |
811 | } |
812 | |
813 | /** Allocate a big_rec_t object in the given memory heap, and for storing |
814 | n_fld number of fields. |
815 | @param[in] heap memory heap in which this object is allocated |
816 | @param[in] n_fld maximum number of fields that can be stored in |
817 | this object |
818 | |
819 | @return the allocated object */ |
820 | big_rec_t* |
821 | big_rec_t::alloc( |
822 | mem_heap_t* heap, |
823 | ulint n_fld) |
824 | { |
825 | big_rec_t* rec = static_cast<big_rec_t*>( |
826 | mem_heap_alloc(heap, sizeof(big_rec_t))); |
827 | |
828 | new(rec) big_rec_t(n_fld); |
829 | |
830 | rec->heap = heap; |
831 | rec->fields = static_cast<big_rec_field_t*>( |
832 | mem_heap_alloc(heap, |
833 | n_fld * sizeof(big_rec_field_t))); |
834 | |
835 | rec->n_fields = 0; |
836 | return(rec); |
837 | } |
838 | |
839 | /** Create a deep copy of this object. |
840 | @param[in,out] heap memory heap in which the clone will be created |
841 | @return the cloned object */ |
842 | dfield_t* |
843 | dfield_t::clone(mem_heap_t* heap) const |
844 | { |
845 | const ulint size = len == UNIV_SQL_NULL ? 0 : len; |
846 | dfield_t* obj = static_cast<dfield_t*>( |
847 | mem_heap_alloc(heap, sizeof(dfield_t) + size)); |
848 | |
849 | ut_ad(len != UNIV_SQL_DEFAULT); |
850 | obj->ext = ext; |
851 | obj->len = len; |
852 | obj->type = type; |
853 | obj->spatial_status = spatial_status; |
854 | |
855 | if (len != UNIV_SQL_NULL) { |
856 | obj->data = obj + 1; |
857 | memcpy(obj->data, data, len); |
858 | } else { |
859 | obj->data = 0; |
860 | } |
861 | |
862 | return(obj); |
863 | } |
864 | |