1 /*****************************************************************************
2
3 Copyright (c) 1996, 2018, Oracle and/or its affiliates. All Rights Reserved.
4 Copyright (c) 2018, 2021, 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, Fifth Floor, Boston, MA 02110-1335 USA
17
18 *****************************************************************************/
19
20 /**************************************************//**
21 @file row/row0row.cc
22 General row routines
23
24 Created 4/20/1996 Heikki Tuuri
25 *******************************************************/
26
27 #include "row0row.h"
28 #include "data0type.h"
29 #include "dict0dict.h"
30 #include "dict0boot.h"
31 #include "btr0btr.h"
32 #include "mach0data.h"
33 #include "trx0rseg.h"
34 #include "trx0trx.h"
35 #include "trx0roll.h"
36 #include "trx0undo.h"
37 #include "trx0purge.h"
38 #include "trx0rec.h"
39 #include "que0que.h"
40 #include "row0ext.h"
41 #include "row0upd.h"
42 #include "rem0cmp.h"
43 #include "ut0mem.h"
44 #include "gis0geo.h"
45 #include "row0mysql.h"
46
47 /** Build a spatial index key.
48 @param[in] index spatial index
49 @param[in] ext externally stored column prefixes, or NULL
50 @param[in,out] dfield field of the tuple to be copied
51 @param[in] dfield2 field of the tuple to copy
52 @param[in] flag ROW_BUILD_NORMAL, ROW_BUILD_FOR_PURGE or
53 ROW_BUILD_FOR_UNDO
54 @param[in,out] heap memory heap from which the memory
55 of the field entry is allocated.
56 @retval false if undo log is logged before spatial index creation. */
row_build_spatial_index_key(const dict_index_t * index,const row_ext_t * ext,dfield_t * dfield,const dfield_t * dfield2,ulint flag,mem_heap_t * heap)57 static bool row_build_spatial_index_key(
58 const dict_index_t* index,
59 const row_ext_t* ext,
60 dfield_t* dfield,
61 const dfield_t* dfield2,
62 ulint flag,
63 mem_heap_t* heap)
64 {
65 if (dfield2->type.mtype == DATA_MISSING) {
66 return false;
67 }
68
69 double* mbr;
70
71 dfield_copy(dfield, dfield2);
72 dfield->type.prtype |= DATA_GIS_MBR;
73
74 /* Allocate memory for mbr field */
75 mbr = static_cast<double*>(mem_heap_alloc(heap, DATA_MBR_LEN));
76
77 /* Set mbr field data. */
78 dfield_set_data(dfield, mbr, DATA_MBR_LEN);
79
80 const fil_space_t* space = index->table->space;
81
82 if (UNIV_UNLIKELY(!dfield2->data || !space)) {
83 /* FIXME: dfield contains uninitialized data,
84 but row_build_index_entry_low() will not return NULL.
85 This bug is inherited from MySQL 5.7.5
86 commit b66ad511b61fffe75c58d0a607cdb837c6e6c821. */
87 return true;
88 }
89
90 const byte* dptr = NULL;
91 ulint dlen = 0;
92 ulint flen = 0;
93 double tmp_mbr[SPDIMS * 2];
94 mem_heap_t* temp_heap = NULL;
95
96 if (!dfield_is_ext(dfield2)) {
97 dptr = static_cast<const byte*>(dfield_get_data(dfield2));
98 dlen = dfield_get_len(dfield2);
99 ut_ad(dptr != &data_error);
100 goto write_mbr;
101 }
102
103 if (flag == ROW_BUILD_FOR_PURGE) {
104 const byte* ptr = static_cast<const byte*>(
105 dfield_get_data(dfield2));
106
107 switch (dfield_get_spatial_status(dfield2)) {
108 case SPATIAL_ONLY:
109 ut_ad(dfield_get_len(dfield2) == DATA_MBR_LEN);
110 break;
111
112 case SPATIAL_MIXED:
113 ptr += dfield_get_len(dfield2);
114 break;
115
116 case SPATIAL_UNKNOWN:
117 ut_ad(0);
118 /* fall through */
119 case SPATIAL_NONE:
120 /* Undo record is logged before
121 spatial index is created.*/
122 return false;
123 }
124
125 memcpy(mbr, ptr, DATA_MBR_LEN);
126 return true;
127 }
128
129 if (flag == ROW_BUILD_FOR_UNDO
130 && dict_table_has_atomic_blobs(index->table)) {
131 /* For ROW_FORMAT=DYNAMIC or COMPRESSED, a prefix of
132 off-page records is stored in the undo log record (for
133 any column prefix indexes). For SPATIAL INDEX, we
134 must ignore this prefix. The full column value is
135 stored in the BLOB. For non-spatial index, we would
136 have already fetched a necessary prefix of the BLOB,
137 available in the "ext" parameter.
138
139 Here, for SPATIAL INDEX, we are fetching the full
140 column, which is potentially wasting a lot of I/O,
141 memory, and possibly involving a concurrency problem,
142 similar to ones that existed before the introduction
143 of row_ext_t.
144
145 MDEV-11657 FIXME: write the MBR directly to the undo
146 log record, and avoid recomputing it here! */
147 flen = BTR_EXTERN_FIELD_REF_SIZE;
148 ut_ad(dfield_get_len(dfield2) >= BTR_EXTERN_FIELD_REF_SIZE);
149 dptr = static_cast<const byte*>(dfield_get_data(dfield2))
150 + dfield_get_len(dfield2)
151 - BTR_EXTERN_FIELD_REF_SIZE;
152 } else {
153 flen = dfield_get_len(dfield2);
154 dptr = static_cast<const byte*>(dfield_get_data(dfield2));
155 }
156
157 temp_heap = mem_heap_create(1000);
158
159 dptr = btr_copy_externally_stored_field(
160 &dlen, dptr, ext ? ext->zip_size : space->zip_size(),
161 flen, temp_heap);
162
163 write_mbr:
164 if (dlen <= GEO_DATA_HEADER_SIZE) {
165 for (uint i = 0; i < SPDIMS; i += 2) {
166 tmp_mbr[i] = DBL_MAX;
167 tmp_mbr[i + 1] = -DBL_MAX;
168 }
169 } else {
170 rtree_mbr_from_wkb(dptr + GEO_DATA_HEADER_SIZE,
171 uint(dlen - GEO_DATA_HEADER_SIZE),
172 SPDIMS, tmp_mbr);
173 }
174
175 dfield_write_mbr(dfield, tmp_mbr);
176 if (temp_heap) {
177 mem_heap_free(temp_heap);
178 }
179
180 return true;
181 }
182
183 /*****************************************************************//**
184 When an insert or purge to a table is performed, this function builds
185 the entry to be inserted into or purged from an index on the table.
186 @return index entry which should be inserted or purged
187 @retval NULL if the externally stored columns in the clustered index record
188 are unavailable and ext != NULL, or row is missing some needed columns. */
189 dtuple_t*
row_build_index_entry_low(const dtuple_t * row,const row_ext_t * ext,const dict_index_t * index,mem_heap_t * heap,ulint flag)190 row_build_index_entry_low(
191 /*======================*/
192 const dtuple_t* row, /*!< in: row which should be
193 inserted or purged */
194 const row_ext_t* ext, /*!< in: externally stored column
195 prefixes, or NULL */
196 const dict_index_t* index, /*!< in: index on the table */
197 mem_heap_t* heap, /*!< in,out: memory heap from which
198 the memory for the index entry
199 is allocated */
200 ulint flag) /*!< in: ROW_BUILD_NORMAL,
201 ROW_BUILD_FOR_PURGE
202 or ROW_BUILD_FOR_UNDO */
203 {
204 dtuple_t* entry;
205 ulint entry_len;
206 ulint i = 0;
207 ulint num_v = 0;
208
209 entry_len = dict_index_get_n_fields(index);
210
211 if (flag == ROW_BUILD_FOR_INSERT && dict_index_is_clust(index)) {
212 num_v = dict_table_get_n_v_cols(index->table);
213 entry = dtuple_create_with_vcol(heap, entry_len, num_v);
214 } else {
215 entry = dtuple_create(heap, entry_len);
216 }
217
218 if (dict_index_is_ibuf(index)) {
219 dtuple_set_n_fields_cmp(entry, entry_len);
220 /* There may only be externally stored columns
221 in a clustered index B-tree of a user table. */
222 ut_a(!ext);
223 } else {
224 dtuple_set_n_fields_cmp(
225 entry, dict_index_get_n_unique_in_tree(index));
226 if (dict_index_is_spatial(index)) {
227 /* Set the MBR field */
228 if (!row_build_spatial_index_key(
229 index, ext,
230 dtuple_get_nth_field(entry, 0),
231 dtuple_get_nth_field(
232 row,
233 dict_index_get_nth_field(index, i)
234 ->col->ind), flag, heap)) {
235 return NULL;
236 }
237
238 i = 1;
239 }
240 }
241
242 for (; i < entry_len; i++) {
243 const dict_field_t& f = index->fields[i];
244 dfield_t* dfield = dtuple_get_nth_field(entry, i);
245
246 if (f.col->is_dropped()) {
247 ut_ad(index->is_primary());
248 ut_ad(index->is_instant());
249 ut_ad(!f.col->is_virtual());
250 dict_col_copy_type(f.col, &dfield->type);
251 if (f.col->is_nullable()) {
252 dfield_set_null(dfield);
253 } else {
254 dfield_set_data(dfield, field_ref_zero,
255 f.fixed_len);
256 }
257 continue;
258 }
259
260 const dfield_t* dfield2;
261
262 if (f.col->is_virtual()) {
263 const dict_v_col_t* v_col
264 = reinterpret_cast<const dict_v_col_t*>(f.col);
265
266 ut_ad(v_col->v_pos < dtuple_get_n_v_fields(row));
267 dfield2 = dtuple_get_nth_v_field(row, v_col->v_pos);
268
269 ut_ad(dfield_is_null(dfield2) ||
270 dfield_get_len(dfield2) == 0 || dfield2->data);
271 ut_ad(!dfield_is_ext(dfield2));
272 if (UNIV_UNLIKELY(dfield2->type.mtype
273 == DATA_MISSING)) {
274 ut_ad(flag == ROW_BUILD_FOR_PURGE);
275 return(NULL);
276 }
277 } else {
278 dfield2 = dtuple_get_nth_field(row, f.col->ind);
279 if (UNIV_UNLIKELY(dfield2->type.mtype
280 == DATA_MISSING)) {
281 /* The field has not been initialized in
282 the row. This should be from
283 trx_undo_rec_get_partial_row(). */
284 return(NULL);
285 }
286
287 ut_ad(!(dfield2->type.prtype & DATA_VIRTUAL));
288 }
289
290 compile_time_assert(DATA_MISSING == 0);
291
292 *dfield = *dfield2;
293
294 if (dfield_is_null(dfield)) {
295 continue;
296 }
297
298 ut_ad(!(index->type & DICT_FTS));
299
300 ulint len = dfield_get_len(dfield);
301
302 if (f.prefix_len == 0
303 && (!dfield_is_ext(dfield)
304 || dict_index_is_clust(index))) {
305 /* The *dfield = *dfield2 above suffices for
306 columns that are stored in-page, or for
307 clustered index record columns that are not
308 part of a column prefix in the PRIMARY KEY. */
309 continue;
310 }
311
312 /* If the column is stored externally (off-page) in
313 the clustered index, it must be an ordering field in
314 the secondary index. If !atomic_blobs, the only way
315 we may have a secondary index pointing to a clustered
316 index record with an off-page column is when it is a
317 column prefix index. If atomic_blobs, also fully
318 indexed long columns may be stored off-page. */
319 ut_ad(f.col->ord_part);
320
321 if (ext && !f.col->is_virtual()) {
322 /* See if the column is stored externally. */
323 const byte* buf = row_ext_lookup(ext, f.col->ind,
324 &len);
325 if (UNIV_LIKELY_NULL(buf)) {
326 if (UNIV_UNLIKELY(buf == field_ref_zero)) {
327 return(NULL);
328 }
329 dfield_set_data(dfield, buf, len);
330 }
331
332 if (f.prefix_len == 0) {
333 /* If ROW_FORMAT=DYNAMIC or
334 ROW_FORMAT=COMPRESSED, we can have a
335 secondary index on an entire column
336 that is stored off-page in the
337 clustered index. As this is not a
338 prefix index (prefix_len == 0),
339 include the entire off-page column in
340 the secondary index record. */
341 continue;
342 }
343 } else if (dfield_is_ext(dfield)) {
344 /* This table is either in
345 (ROW_FORMAT=REDUNDANT or ROW_FORMAT=COMPACT)
346 or a purge record where the ordered part of
347 the field is not external.
348 In ROW_FORMAT=REDUNDANT and ROW_FORMAT=COMPACT,
349 the maximum column prefix
350 index length is 767 bytes, and the clustered
351 index record contains a 768-byte prefix of
352 each off-page column. */
353 ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE);
354 len -= BTR_EXTERN_FIELD_REF_SIZE;
355 dfield_set_len(dfield, len);
356 }
357
358 /* If a column prefix index, take only the prefix. */
359 if (f.prefix_len) {
360 len = dtype_get_at_most_n_mbchars(
361 f.col->prtype,
362 f.col->mbminlen, f.col->mbmaxlen,
363 f.prefix_len, len,
364 static_cast<char*>(dfield_get_data(dfield)));
365 dfield_set_len(dfield, len);
366 }
367 }
368
369 for (i = num_v; i--; ) {
370 ut_ad(index->is_primary());
371 ut_ad(flag == ROW_BUILD_FOR_INSERT);
372 dfield_t* dfield = dtuple_get_nth_v_field(entry, i);
373 const dict_v_col_t* v_col = dict_table_get_nth_v_col(
374 index->table, i);
375 ut_ad(!v_col->m_col.is_dropped());
376 ut_ad(v_col->v_pos < dtuple_get_n_v_fields(row));
377 const dfield_t* dfield2 = dtuple_get_nth_v_field(
378 row, v_col->v_pos);
379 ut_ad(dfield_is_null(dfield2) ||
380 dfield_get_len(dfield2) == 0 || dfield2->data);
381 ut_ad(dfield2->type.mtype != DATA_MISSING);
382 *dfield = *dfield2;
383 }
384
385 return entry;
386 }
387
388 /** An inverse function to row_build_index_entry. Builds a row from a
389 record in a clustered index, with possible indexing on ongoing
390 addition of new virtual columns.
391 @param[in] type ROW_COPY_POINTERS or ROW_COPY_DATA;
392 @param[in] index clustered index
393 @param[in] rec record in the clustered index
394 @param[in] offsets rec_get_offsets(rec,index) or NULL
395 @param[in] col_table table, to check which
396 externally stored columns
397 occur in the ordering columns
398 of an index, or NULL if
399 index->table should be
400 consulted instead
401 @param[in] defaults default values of added/changed columns, or NULL
402 @param[in] add_v new virtual columns added
403 along with new indexes
404 @param[in] col_map mapping of old column
405 numbers to new ones, or NULL
406 @param[in] ext cache of externally stored column
407 prefixes, or NULL
408 @param[in] heap memory heap from which
409 the memory needed is allocated
410 @return own: row built; */
411 static inline
412 dtuple_t*
row_build_low(ulint type,const dict_index_t * index,const rec_t * rec,const rec_offs * offsets,const dict_table_t * col_table,const dtuple_t * defaults,const dict_add_v_col_t * add_v,const ulint * col_map,row_ext_t ** ext,mem_heap_t * heap)413 row_build_low(
414 ulint type,
415 const dict_index_t* index,
416 const rec_t* rec,
417 const rec_offs* offsets,
418 const dict_table_t* col_table,
419 const dtuple_t* defaults,
420 const dict_add_v_col_t* add_v,
421 const ulint* col_map,
422 row_ext_t** ext,
423 mem_heap_t* heap)
424 {
425 const byte* copy;
426 dtuple_t* row;
427 ulint n_ext_cols;
428 ulint* ext_cols = NULL; /* remove warning */
429 ulint len;
430 byte* buf;
431 ulint j;
432 mem_heap_t* tmp_heap = NULL;
433 rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
434 rec_offs_init(offsets_);
435
436 ut_ad(index != NULL);
437 ut_ad(rec != NULL);
438 ut_ad(heap != NULL);
439 ut_ad(dict_index_is_clust(index));
440 ut_ad(!col_map || col_table);
441
442 if (!offsets) {
443 offsets = rec_get_offsets(rec, index, offsets_,
444 index->n_core_fields,
445 ULINT_UNDEFINED, &tmp_heap);
446 } else {
447 ut_ad(rec_offs_validate(rec, index, offsets));
448 }
449
450 #if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
451 /* Some blob refs can be NULL during crash recovery before
452 trx_rollback_active() has completed execution, or when a concurrently
453 executing insert or update has committed the B-tree mini-transaction
454 but has not yet managed to restore the cursor position for writing
455 the big_rec. Note that the mini-transaction can be committed multiple
456 times, and the cursor restore can happen multiple times for single
457 insert or update statement. */
458 ut_a(!rec_offs_any_null_extern(rec, offsets)
459 || trx_sys.is_registered(current_trx(),
460 row_get_rec_trx_id(rec, index,
461 offsets)));
462 #endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
463
464 if (type != ROW_COPY_POINTERS) {
465 /* Take a copy of rec to heap */
466 buf = static_cast<byte*>(
467 mem_heap_alloc(heap, rec_offs_size(offsets)));
468
469 copy = rec_copy(buf, rec, offsets);
470 } else {
471 copy = rec;
472 }
473
474 n_ext_cols = rec_offs_n_extern(offsets);
475 if (n_ext_cols) {
476 ext_cols = static_cast<ulint*>(
477 mem_heap_alloc(heap, n_ext_cols * sizeof *ext_cols));
478 }
479
480 /* Avoid a debug assertion in rec_offs_validate(). */
481 rec_offs_make_valid(copy, index, true, const_cast<rec_offs*>(offsets));
482
483 if (!col_table) {
484 ut_ad(!col_map);
485 ut_ad(!defaults);
486 col_table = index->table;
487 }
488
489 if (defaults) {
490 ut_ad(col_map);
491 row = dtuple_copy(defaults, heap);
492 /* dict_table_copy_types() would set the fields to NULL */
493 for (ulint i = 0; i < dict_table_get_n_cols(col_table); i++) {
494 dict_col_copy_type(
495 dict_table_get_nth_col(col_table, i),
496 dfield_get_type(dtuple_get_nth_field(row, i)));
497 }
498 } else if (add_v != NULL) {
499 row = dtuple_create_with_vcol(
500 heap, dict_table_get_n_cols(col_table),
501 dict_table_get_n_v_cols(col_table) + add_v->n_v_col);
502 dict_table_copy_types(row, col_table);
503
504 for (ulint i = 0; i < add_v->n_v_col; i++) {
505 dict_col_copy_type(
506 &add_v->v_col[i].m_col,
507 dfield_get_type(dtuple_get_nth_v_field(
508 row, i + col_table->n_v_def)));
509 }
510 } else {
511 row = dtuple_create_with_vcol(
512 heap, dict_table_get_n_cols(col_table),
513 dict_table_get_n_v_cols(col_table));
514 dict_table_copy_types(row, col_table);
515 }
516
517 dtuple_set_info_bits(row, rec_get_info_bits(
518 copy, rec_offs_comp(offsets)));
519
520 j = 0;
521
522 const dict_field_t* ind_field = index->fields;
523
524 for (ulint i = 0; i < rec_offs_n_fields(offsets); i++) {
525 if (i == index->first_user_field()
526 && rec_is_alter_metadata(rec, *index)) {
527 ut_ad(rec_offs_nth_extern(offsets, i));
528 ut_d(ulint len);
529 ut_d(rec_get_nth_field_offs(offsets, i, &len));
530 ut_ad(len == FIELD_REF_SIZE);
531 continue;
532 }
533
534 ut_ad(ind_field < &index->fields[index->n_fields]);
535
536 const dict_col_t* col = dict_field_get_col(ind_field);
537
538 if ((ind_field++)->prefix_len) {
539 /* Column prefixes can only occur in key
540 fields, which cannot be stored externally. For
541 a column prefix, there should also be the full
542 field in the clustered index tuple. The row
543 tuple comprises full fields, not prefixes. */
544 ut_ad(!rec_offs_nth_extern(offsets, i));
545 continue;
546 }
547
548 if (col->is_dropped()) {
549 continue;
550 }
551
552 ulint col_no = dict_col_get_no(col);
553
554 if (col_map) {
555 col_no = col_map[col_no];
556
557 if (col_no == ULINT_UNDEFINED) {
558 /* dropped column */
559 continue;
560 }
561 }
562
563 dfield_t* dfield = dtuple_get_nth_field(row, col_no);
564
565 const void* field = rec_get_nth_field(
566 copy, offsets, i, &len);
567 if (len == UNIV_SQL_DEFAULT) {
568 field = index->instant_field_value(i, &len);
569 if (field && type != ROW_COPY_POINTERS) {
570 field = mem_heap_dup(heap, field, len);
571 }
572 }
573 dfield_set_data(dfield, field, len);
574
575 if (rec_offs_nth_extern(offsets, i)) {
576 dfield_set_ext(dfield);
577
578 col = dict_table_get_nth_col(col_table, col_no);
579
580 if (col->ord_part) {
581 /* We will have to fetch prefixes of
582 externally stored columns that are
583 referenced by column prefixes. */
584 ext_cols[j++] = col_no;
585 }
586 }
587 }
588
589 rec_offs_make_valid(rec, index, true, const_cast<rec_offs*>(offsets));
590
591 ut_ad(dtuple_check_typed(row));
592
593 if (!ext) {
594 /* REDUNDANT and COMPACT formats store a local
595 768-byte prefix of each externally stored
596 column. No cache is needed.
597
598 During online table rebuild,
599 row_log_table_apply_delete_low()
600 may use a cache that was set up by
601 row_log_table_delete(). */
602
603 } else if (j) {
604 *ext = row_ext_create(j, ext_cols, *index->table, row,
605 heap);
606 } else {
607 *ext = NULL;
608 }
609
610 if (tmp_heap) {
611 mem_heap_free(tmp_heap);
612 }
613
614 return(row);
615 }
616
617
618 /*******************************************************************//**
619 An inverse function to row_build_index_entry. Builds a row from a
620 record in a clustered index.
621 @return own: row built; see the NOTE below! */
622 dtuple_t*
row_build(ulint type,const dict_index_t * index,const rec_t * rec,const rec_offs * offsets,const dict_table_t * col_table,const dtuple_t * defaults,const ulint * col_map,row_ext_t ** ext,mem_heap_t * heap)623 row_build(
624 /*======*/
625 ulint type, /*!< in: ROW_COPY_POINTERS or
626 ROW_COPY_DATA; the latter
627 copies also the data fields to
628 heap while the first only
629 places pointers to data fields
630 on the index page, and thus is
631 more efficient */
632 const dict_index_t* index, /*!< in: clustered index */
633 const rec_t* rec, /*!< in: record in the clustered
634 index; NOTE: in the case
635 ROW_COPY_POINTERS the data
636 fields in the row will point
637 directly into this record,
638 therefore, the buffer page of
639 this record must be at least
640 s-latched and the latch held
641 as long as the row dtuple is used! */
642 const rec_offs* offsets,/*!< in: rec_get_offsets(rec,index)
643 or NULL, in which case this function
644 will invoke rec_get_offsets() */
645 const dict_table_t* col_table,
646 /*!< in: table, to check which
647 externally stored columns
648 occur in the ordering columns
649 of an index, or NULL if
650 index->table should be
651 consulted instead */
652 const dtuple_t* defaults,
653 /*!< in: default values of
654 added and changed columns, or NULL */
655 const ulint* col_map,/*!< in: mapping of old column
656 numbers to new ones, or NULL */
657 row_ext_t** ext, /*!< out, own: cache of
658 externally stored column
659 prefixes, or NULL */
660 mem_heap_t* heap) /*!< in: memory heap from which
661 the memory needed is allocated */
662 {
663 return(row_build_low(type, index, rec, offsets, col_table,
664 defaults, NULL, col_map, ext, heap));
665 }
666
667 /** An inverse function to row_build_index_entry. Builds a row from a
668 record in a clustered index, with possible indexing on ongoing
669 addition of new virtual columns.
670 @param[in] type ROW_COPY_POINTERS or ROW_COPY_DATA;
671 @param[in] index clustered index
672 @param[in] rec record in the clustered index
673 @param[in] offsets rec_get_offsets(rec,index) or NULL
674 @param[in] col_table table, to check which
675 externally stored columns
676 occur in the ordering columns
677 of an index, or NULL if
678 index->table should be
679 consulted instead
680 @param[in] defaults default values of added, changed columns, or NULL
681 @param[in] add_v new virtual columns added
682 along with new indexes
683 @param[in] col_map mapping of old column
684 numbers to new ones, or NULL
685 @param[in] ext cache of externally stored column
686 prefixes, or NULL
687 @param[in] heap memory heap from which
688 the memory needed is allocated
689 @return own: row built; */
690 dtuple_t*
row_build_w_add_vcol(ulint type,const dict_index_t * index,const rec_t * rec,const rec_offs * offsets,const dict_table_t * col_table,const dtuple_t * defaults,const dict_add_v_col_t * add_v,const ulint * col_map,row_ext_t ** ext,mem_heap_t * heap)691 row_build_w_add_vcol(
692 ulint type,
693 const dict_index_t* index,
694 const rec_t* rec,
695 const rec_offs* offsets,
696 const dict_table_t* col_table,
697 const dtuple_t* defaults,
698 const dict_add_v_col_t* add_v,
699 const ulint* col_map,
700 row_ext_t** ext,
701 mem_heap_t* heap)
702 {
703 return(row_build_low(type, index, rec, offsets, col_table,
704 defaults, add_v, col_map, ext, heap));
705 }
706
707 /** Convert an index record to a data tuple.
708 @tparam metadata whether the index->instant_field_value() needs to be accessed
709 @tparam mblob 1 if rec_is_alter_metadata();
710 2 if we want converted metadata corresponding to info_bits
711 @param[in] rec index record
712 @param[in] index index
713 @param[in] offsets rec_get_offsets(rec, index)
714 @param[out] n_ext number of externally stored columns
715 @param[in,out] heap memory heap for allocations
716 @param[in] info_bits (only used if mblob=2)
717 @param[in] pad (only used if mblob=2)
718 @return index entry built; does not set info_bits, and the data fields
719 in the entry will point directly to rec */
720 template<bool metadata, int mblob = 0>
721 static inline
722 dtuple_t*
row_rec_to_index_entry_impl(const rec_t * rec,const dict_index_t * index,const rec_offs * offsets,mem_heap_t * heap,ulint info_bits=0,bool pad=false)723 row_rec_to_index_entry_impl(
724 const rec_t* rec,
725 const dict_index_t* index,
726 const rec_offs* offsets,
727 mem_heap_t* heap,
728 ulint info_bits = 0,
729 bool pad = false)
730 {
731 ut_ad(rec != NULL);
732 ut_ad(heap != NULL);
733 ut_ad(index != NULL);
734 ut_ad(!mblob || index->is_primary());
735 ut_ad(!mblob || !index->table->is_temporary());
736 ut_ad(!mblob || !dict_index_is_spatial(index));
737 compile_time_assert(!mblob || metadata);
738 compile_time_assert(mblob <= 2);
739 /* Because this function may be invoked by row0merge.cc
740 on a record whose header is in different format, the check
741 rec_offs_validate(rec, index, offsets) must be avoided here. */
742
743 const bool got = mblob == 2 && rec_is_alter_metadata(rec, *index);
744 ulint rec_len = rec_offs_n_fields(offsets);
745 if (mblob == 2) {
746 ut_ad(info_bits == REC_INFO_METADATA_ALTER
747 || info_bits == REC_INFO_METADATA_ADD);
748 ut_ad(rec_len <= ulint(index->n_fields + got));
749 if (pad) {
750 rec_len = ulint(index->n_fields)
751 + (info_bits == REC_INFO_METADATA_ALTER);
752 } else if (!got && info_bits == REC_INFO_METADATA_ALTER) {
753 rec_len++;
754 }
755 } else {
756 ut_ad(info_bits == 0);
757 ut_ad(!pad);
758 }
759 dtuple_t* entry = dtuple_create(heap, rec_len);
760 dfield_t* dfield = entry->fields;
761
762 dtuple_set_n_fields_cmp(entry,
763 dict_index_get_n_unique_in_tree(index));
764 ut_ad(mblob == 2
765 || rec_len == dict_index_get_n_fields(index) + uint(mblob == 1)
766 /* a record for older SYS_INDEXES table
767 (missing merge_threshold column) is acceptable. */
768 || (!index->table->is_temporary()
769 && index->table->id == DICT_INDEXES_ID
770 && rec_len + 1 == dict_index_get_n_fields(index)));
771
772 ulint i;
773 for (i = 0; i < (mblob ? index->first_user_field() : rec_len);
774 i++, dfield++) {
775 dict_col_copy_type(dict_index_get_nth_col(index, i),
776 &dfield->type);
777 if (!mblob
778 && dict_index_is_spatial(index)
779 && DATA_GEOMETRY_MTYPE(dfield->type.mtype)) {
780 dfield->type.prtype |= DATA_GIS_MBR;
781 }
782
783 ulint len;
784 const byte* field = metadata
785 ? rec_get_nth_cfield(rec, index, offsets, i, &len)
786 : rec_get_nth_field(rec, offsets, i, &len);
787
788 dfield_set_data(dfield, field, len);
789
790 if (rec_offs_nth_extern(offsets, i)) {
791 dfield_set_ext(dfield);
792 }
793 }
794
795 if (mblob) {
796 ulint len;
797 const byte* field;
798 ulint j = i;
799
800 if (mblob == 2) {
801 const bool want = info_bits == REC_INFO_METADATA_ALTER;
802 if (got == want) {
803 if (got) {
804 goto copy_metadata;
805 }
806 } else {
807 if (want) {
808 /* Allocate a placeholder for
809 adding metadata in an update. */
810 len = FIELD_REF_SIZE;
811 field = static_cast<byte*>(
812 mem_heap_zalloc(heap, len));
813 /* In reality there is one fewer
814 field present in the record. */
815 rec_len--;
816 goto init_metadata;
817 }
818
819 /* Skip the undesired metadata blob
820 (for example, when rolling back an
821 instant ALTER TABLE). */
822 i++;
823 }
824 goto copy_user_fields;
825 }
826 copy_metadata:
827 ut_ad(rec_offs_nth_extern(offsets, i));
828 field = rec_get_nth_field(rec, offsets, i++, &len);
829 init_metadata:
830 dfield->type.metadata_blob_init();
831 ut_ad(len == FIELD_REF_SIZE);
832 dfield_set_data(dfield, field, len);
833 dfield_set_ext(dfield++);
834 copy_user_fields:
835 for (; i < rec_len; i++, dfield++) {
836 dict_col_copy_type(dict_index_get_nth_col(index, j++),
837 &dfield->type);
838 if (mblob == 2 && pad
839 && i >= rec_offs_n_fields(offsets)) {
840 field = index->instant_field_value(j - 1,
841 &len);
842 dfield_set_data(dfield, field, len);
843 continue;
844 }
845
846 field = rec_get_nth_field(rec, offsets, i, &len);
847 dfield_set_data(dfield, field, len);
848
849 if (rec_offs_nth_extern(offsets, i)) {
850 dfield_set_ext(dfield);
851 }
852 }
853 }
854
855 if (mblob == 2) {
856 ulint n_fields = ulint(dfield - entry->fields);
857 ut_ad(entry->n_fields >= n_fields);
858 entry->n_fields = n_fields;
859 }
860 ut_ad(dfield == entry->fields + entry->n_fields);
861 ut_ad(dtuple_check_typed(entry));
862 return entry;
863 }
864
865 /** Convert an index record to a data tuple.
866 @param[in] rec index record
867 @param[in] index index
868 @param[in] offsets rec_get_offsets(rec, index)
869 @param[in,out] heap memory heap for allocations */
870 dtuple_t*
row_rec_to_index_entry_low(const rec_t * rec,const dict_index_t * index,const rec_offs * offsets,mem_heap_t * heap)871 row_rec_to_index_entry_low(
872 const rec_t* rec,
873 const dict_index_t* index,
874 const rec_offs* offsets,
875 mem_heap_t* heap)
876 {
877 return row_rec_to_index_entry_impl<false>(rec, index, offsets, heap);
878 }
879
880 /*******************************************************************//**
881 Converts an index record to a typed data tuple. NOTE that externally
882 stored (often big) fields are NOT copied to heap.
883 @return own: index entry built */
884 dtuple_t*
row_rec_to_index_entry(const rec_t * rec,const dict_index_t * index,const rec_offs * offsets,mem_heap_t * heap)885 row_rec_to_index_entry(
886 /*===================*/
887 const rec_t* rec, /*!< in: record in the index */
888 const dict_index_t* index, /*!< in: index */
889 const rec_offs* offsets,/*!< in: rec_get_offsets(rec) */
890 mem_heap_t* heap) /*!< in: memory heap from which
891 the memory needed is allocated */
892 {
893 ut_ad(rec != NULL);
894 ut_ad(heap != NULL);
895 ut_ad(index != NULL);
896 ut_ad(rec_offs_validate(rec, index, offsets));
897
898 /* Take a copy of rec to heap */
899 const rec_t* copy_rec = rec_copy(
900 static_cast<byte*>(mem_heap_alloc(heap,
901 rec_offs_size(offsets))),
902 rec, offsets);
903
904 rec_offs_make_valid(copy_rec, index, true,
905 const_cast<rec_offs*>(offsets));
906
907 dtuple_t* entry = rec_is_alter_metadata(copy_rec, *index)
908 ? row_rec_to_index_entry_impl<true,1>(
909 copy_rec, index, offsets, heap)
910 : row_rec_to_index_entry_impl<true>(
911 copy_rec, index, offsets, heap);
912
913 rec_offs_make_valid(rec, index, true,
914 const_cast<rec_offs*>(offsets));
915
916 dtuple_set_info_bits(entry,
917 rec_get_info_bits(rec, rec_offs_comp(offsets)));
918
919 return(entry);
920 }
921
922 /** Convert a metadata record to a data tuple.
923 @param[in] rec metadata record
924 @param[in] index clustered index after instant ALTER TABLE
925 @param[in] offsets rec_get_offsets(rec)
926 @param[in,out] heap memory heap for allocations
927 @param[in] info_bits the info_bits after an update
928 @param[in] pad whether to pad to index->n_fields */
929 dtuple_t*
row_metadata_to_tuple(const rec_t * rec,const dict_index_t * index,const rec_offs * offsets,mem_heap_t * heap,ulint info_bits,bool pad)930 row_metadata_to_tuple(
931 const rec_t* rec,
932 const dict_index_t* index,
933 const rec_offs* offsets,
934 mem_heap_t* heap,
935 ulint info_bits,
936 bool pad)
937 {
938 ut_ad(info_bits == REC_INFO_METADATA_ALTER
939 || info_bits == REC_INFO_METADATA_ADD);
940 ut_ad(rec_is_metadata(rec, *index));
941 ut_ad(rec_offs_validate(rec, index, offsets));
942
943 const rec_t* copy_rec = rec_copy(
944 static_cast<byte*>(mem_heap_alloc(heap,
945 rec_offs_size(offsets))),
946 rec, offsets);
947
948 rec_offs_make_valid(copy_rec, index, true,
949 const_cast<rec_offs*>(offsets));
950
951 dtuple_t* entry = info_bits == REC_INFO_METADATA_ALTER
952 || rec_is_alter_metadata(copy_rec, *index)
953 ? row_rec_to_index_entry_impl<true,2>(
954 copy_rec, index, offsets, heap, info_bits, pad)
955 : row_rec_to_index_entry_impl<true>(
956 copy_rec, index, offsets, heap);
957
958 rec_offs_make_valid(rec, index, true,
959 const_cast<rec_offs*>(offsets));
960
961 dtuple_set_info_bits(entry, info_bits);
962 return entry;
963 }
964
965 /*******************************************************************//**
966 Builds from a secondary index record a row reference with which we can
967 search the clustered index record.
968 @return own: row reference built; see the NOTE below! */
969 dtuple_t*
row_build_row_ref(ulint type,dict_index_t * index,const rec_t * rec,mem_heap_t * heap)970 row_build_row_ref(
971 /*==============*/
972 ulint type, /*!< in: ROW_COPY_DATA, or ROW_COPY_POINTERS:
973 the former copies also the data fields to
974 heap, whereas the latter only places pointers
975 to data fields on the index page */
976 dict_index_t* index, /*!< in: secondary index */
977 const rec_t* rec, /*!< in: record in the index;
978 NOTE: in the case ROW_COPY_POINTERS
979 the data fields in the row will point
980 directly into this record, therefore,
981 the buffer page of this record must be
982 at least s-latched and the latch held
983 as long as the row reference is used! */
984 mem_heap_t* heap) /*!< in: memory heap from which the memory
985 needed is allocated */
986 {
987 dict_table_t* table;
988 dict_index_t* clust_index;
989 dfield_t* dfield;
990 dtuple_t* ref;
991 const byte* field;
992 ulint len;
993 ulint ref_len;
994 ulint pos;
995 byte* buf;
996 ulint clust_col_prefix_len;
997 ulint i;
998 mem_heap_t* tmp_heap = NULL;
999 rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
1000 rec_offs* offsets = offsets_;
1001 rec_offs_init(offsets_);
1002
1003 ut_ad(index != NULL);
1004 ut_ad(rec != NULL);
1005 ut_ad(heap != NULL);
1006 ut_ad(!dict_index_is_clust(index));
1007
1008 offsets = rec_get_offsets(rec, index, offsets, index->n_core_fields,
1009 ULINT_UNDEFINED, &tmp_heap);
1010 /* Secondary indexes must not contain externally stored columns. */
1011 ut_ad(!rec_offs_any_extern(offsets));
1012
1013 if (type == ROW_COPY_DATA) {
1014 /* Take a copy of rec to heap */
1015
1016 buf = static_cast<byte*>(
1017 mem_heap_alloc(heap, rec_offs_size(offsets)));
1018
1019 rec = rec_copy(buf, rec, offsets);
1020 rec_offs_make_valid(rec, index, true, offsets);
1021 }
1022
1023 table = index->table;
1024
1025 clust_index = dict_table_get_first_index(table);
1026
1027 ref_len = dict_index_get_n_unique(clust_index);
1028
1029 ref = dtuple_create(heap, ref_len);
1030
1031 dict_index_copy_types(ref, clust_index, ref_len);
1032
1033 for (i = 0; i < ref_len; i++) {
1034 dfield = dtuple_get_nth_field(ref, i);
1035
1036 pos = dict_index_get_nth_field_pos(index, clust_index, i);
1037
1038 ut_a(pos != ULINT_UNDEFINED);
1039
1040 ut_ad(!rec_offs_nth_default(offsets, pos));
1041 field = rec_get_nth_field(rec, offsets, pos, &len);
1042
1043 dfield_set_data(dfield, field, len);
1044
1045 /* If the primary key contains a column prefix, then the
1046 secondary index may contain a longer prefix of the same
1047 column, or the full column, and we must adjust the length
1048 accordingly. */
1049
1050 clust_col_prefix_len = dict_index_get_nth_field(
1051 clust_index, i)->prefix_len;
1052
1053 if (clust_col_prefix_len > 0) {
1054 if (len != UNIV_SQL_NULL) {
1055
1056 const dtype_t* dtype
1057 = dfield_get_type(dfield);
1058
1059 dfield_set_len(dfield,
1060 dtype_get_at_most_n_mbchars(
1061 dtype->prtype,
1062 dtype->mbminlen,
1063 dtype->mbmaxlen,
1064 clust_col_prefix_len,
1065 len, (char*) field));
1066 }
1067 }
1068 }
1069
1070 ut_ad(dtuple_check_typed(ref));
1071 if (tmp_heap) {
1072 mem_heap_free(tmp_heap);
1073 }
1074
1075 return(ref);
1076 }
1077
1078 /*******************************************************************//**
1079 Builds from a secondary index record a row reference with which we can
1080 search the clustered index record. */
1081 void
row_build_row_ref_in_tuple(dtuple_t * ref,const rec_t * rec,const dict_index_t * index,rec_offs * offsets)1082 row_build_row_ref_in_tuple(
1083 /*=======================*/
1084 dtuple_t* ref, /*!< in/out: row reference built;
1085 see the NOTE below! */
1086 const rec_t* rec, /*!< in: record in the index;
1087 NOTE: the data fields in ref
1088 will point directly into this
1089 record, therefore, the buffer
1090 page of this record must be at
1091 least s-latched and the latch
1092 held as long as the row
1093 reference is used! */
1094 const dict_index_t* index, /*!< in: secondary index */
1095 rec_offs* offsets)/*!< in: rec_get_offsets(rec, index)
1096 or NULL */
1097 {
1098 const dict_index_t* clust_index;
1099 dfield_t* dfield;
1100 const byte* field;
1101 ulint len;
1102 ulint ref_len;
1103 ulint pos;
1104 ulint clust_col_prefix_len;
1105 ulint i;
1106 mem_heap_t* heap = NULL;
1107 rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
1108 rec_offs_init(offsets_);
1109
1110 ut_ad(!dict_index_is_clust(index));
1111 ut_a(index->table);
1112
1113 clust_index = dict_table_get_first_index(index->table);
1114 ut_ad(clust_index);
1115
1116 if (!offsets) {
1117 offsets = rec_get_offsets(rec, index, offsets_,
1118 index->n_core_fields,
1119 ULINT_UNDEFINED, &heap);
1120 } else {
1121 ut_ad(rec_offs_validate(rec, index, offsets));
1122 }
1123
1124 /* Secondary indexes must not contain externally stored columns. */
1125 ut_ad(!rec_offs_any_extern(offsets));
1126 ref_len = dict_index_get_n_unique(clust_index);
1127
1128 ut_ad(ref_len == dtuple_get_n_fields(ref));
1129
1130 dict_index_copy_types(ref, clust_index, ref_len);
1131
1132 for (i = 0; i < ref_len; i++) {
1133 dfield = dtuple_get_nth_field(ref, i);
1134
1135 pos = dict_index_get_nth_field_pos(index, clust_index, i);
1136
1137 ut_a(pos != ULINT_UNDEFINED);
1138
1139 ut_ad(!rec_offs_nth_default(offsets, pos));
1140 field = rec_get_nth_field(rec, offsets, pos, &len);
1141
1142 dfield_set_data(dfield, field, len);
1143
1144 /* If the primary key contains a column prefix, then the
1145 secondary index may contain a longer prefix of the same
1146 column, or the full column, and we must adjust the length
1147 accordingly. */
1148
1149 clust_col_prefix_len = dict_index_get_nth_field(
1150 clust_index, i)->prefix_len;
1151
1152 if (clust_col_prefix_len > 0) {
1153 if (len != UNIV_SQL_NULL) {
1154
1155 const dtype_t* dtype
1156 = dfield_get_type(dfield);
1157
1158 dfield_set_len(dfield,
1159 dtype_get_at_most_n_mbchars(
1160 dtype->prtype,
1161 dtype->mbminlen,
1162 dtype->mbmaxlen,
1163 clust_col_prefix_len,
1164 len, (char*) field));
1165 }
1166 }
1167 }
1168
1169 ut_ad(dtuple_check_typed(ref));
1170 if (UNIV_LIKELY_NULL(heap)) {
1171 mem_heap_free(heap);
1172 }
1173 }
1174
1175 /***************************************************************//**
1176 Searches the clustered index record for a row, if we have the row reference.
1177 @return TRUE if found */
1178 ibool
row_search_on_row_ref(btr_pcur_t * pcur,ulint mode,const dict_table_t * table,const dtuple_t * ref,mtr_t * mtr)1179 row_search_on_row_ref(
1180 /*==================*/
1181 btr_pcur_t* pcur, /*!< out: persistent cursor, which must
1182 be closed by the caller */
1183 ulint mode, /*!< in: BTR_MODIFY_LEAF, ... */
1184 const dict_table_t* table, /*!< in: table */
1185 const dtuple_t* ref, /*!< in: row reference */
1186 mtr_t* mtr) /*!< in/out: mtr */
1187 {
1188 ulint low_match;
1189 rec_t* rec;
1190 dict_index_t* index;
1191
1192 ut_ad(dtuple_check_typed(ref));
1193
1194 index = dict_table_get_first_index(table);
1195
1196 if (UNIV_UNLIKELY(ref->info_bits != 0)) {
1197 ut_ad(ref->is_metadata());
1198 ut_ad(ref->n_fields <= index->n_uniq);
1199 if (btr_pcur_open_at_index_side(
1200 true, index, mode, pcur, true, 0, mtr)
1201 != DB_SUCCESS
1202 || !btr_pcur_move_to_next_user_rec(pcur, mtr)) {
1203 return FALSE;
1204 }
1205 /* We do not necessarily have index->is_instant() here,
1206 because we could be executing a rollback of an
1207 instant ADD COLUMN operation. The function
1208 rec_is_metadata() asserts index->is_instant();
1209 we do not want to call it here. */
1210 return rec_get_info_bits(btr_pcur_get_rec(pcur),
1211 dict_table_is_comp(index->table))
1212 & REC_INFO_MIN_REC_FLAG;
1213 } else {
1214 ut_a(ref->n_fields == index->n_uniq);
1215 if (btr_pcur_open(index, ref, PAGE_CUR_LE, mode, pcur, mtr)
1216 != DB_SUCCESS) {
1217 return FALSE;
1218 }
1219 }
1220
1221 low_match = btr_pcur_get_low_match(pcur);
1222
1223 rec = btr_pcur_get_rec(pcur);
1224
1225 if (page_rec_is_infimum(rec)) {
1226
1227 return(FALSE);
1228 }
1229
1230 if (low_match != dtuple_get_n_fields(ref)) {
1231
1232 return(FALSE);
1233 }
1234
1235 return(TRUE);
1236 }
1237
1238 /*********************************************************************//**
1239 Fetches the clustered index record for a secondary index record. The latches
1240 on the secondary index record are preserved.
1241 @return record or NULL, if no record found */
1242 rec_t*
row_get_clust_rec(ulint mode,const rec_t * rec,dict_index_t * index,dict_index_t ** clust_index,mtr_t * mtr)1243 row_get_clust_rec(
1244 /*==============*/
1245 ulint mode, /*!< in: BTR_MODIFY_LEAF, ... */
1246 const rec_t* rec, /*!< in: record in a secondary index */
1247 dict_index_t* index, /*!< in: secondary index */
1248 dict_index_t** clust_index,/*!< out: clustered index */
1249 mtr_t* mtr) /*!< in: mtr */
1250 {
1251 mem_heap_t* heap;
1252 dtuple_t* ref;
1253 dict_table_t* table;
1254 btr_pcur_t pcur;
1255 ibool found;
1256 rec_t* clust_rec;
1257
1258 ut_ad(!dict_index_is_clust(index));
1259
1260 table = index->table;
1261
1262 heap = mem_heap_create(256);
1263
1264 ref = row_build_row_ref(ROW_COPY_POINTERS, index, rec, heap);
1265
1266 found = row_search_on_row_ref(&pcur, mode, table, ref, mtr);
1267
1268 clust_rec = found ? btr_pcur_get_rec(&pcur) : NULL;
1269
1270 mem_heap_free(heap);
1271
1272 btr_pcur_close(&pcur);
1273
1274 *clust_index = dict_table_get_first_index(table);
1275
1276 return(clust_rec);
1277 }
1278
1279 /***************************************************************//**
1280 Searches an index record.
1281 @return whether the record was found or buffered */
1282 enum row_search_result
row_search_index_entry(dict_index_t * index,const dtuple_t * entry,ulint mode,btr_pcur_t * pcur,mtr_t * mtr)1283 row_search_index_entry(
1284 /*===================*/
1285 dict_index_t* index, /*!< in: index */
1286 const dtuple_t* entry, /*!< in: index entry */
1287 ulint mode, /*!< in: BTR_MODIFY_LEAF, ... */
1288 btr_pcur_t* pcur, /*!< in/out: persistent cursor, which must
1289 be closed by the caller */
1290 mtr_t* mtr) /*!< in: mtr */
1291 {
1292 ulint n_fields;
1293 ulint low_match;
1294 rec_t* rec;
1295
1296 ut_ad(dtuple_check_typed(entry));
1297
1298 if (dict_index_is_spatial(index)) {
1299 ut_ad(mode & BTR_MODIFY_LEAF || mode & BTR_MODIFY_TREE);
1300 rtr_pcur_open(index, entry, PAGE_CUR_RTREE_LOCATE,
1301 mode, pcur, mtr);
1302 } else {
1303 btr_pcur_open(index, entry, PAGE_CUR_LE, mode, pcur, mtr);
1304 }
1305
1306 switch (btr_pcur_get_btr_cur(pcur)->flag) {
1307 case BTR_CUR_DELETE_REF:
1308 ut_a(mode & BTR_DELETE && !dict_index_is_spatial(index));
1309 return(ROW_NOT_DELETED_REF);
1310
1311 case BTR_CUR_DEL_MARK_IBUF:
1312 case BTR_CUR_DELETE_IBUF:
1313 case BTR_CUR_INSERT_TO_IBUF:
1314 return(ROW_BUFFERED);
1315
1316 case BTR_CUR_HASH:
1317 case BTR_CUR_HASH_FAIL:
1318 case BTR_CUR_BINARY:
1319 break;
1320 }
1321
1322 low_match = btr_pcur_get_low_match(pcur);
1323
1324 rec = btr_pcur_get_rec(pcur);
1325
1326 n_fields = dtuple_get_n_fields(entry);
1327
1328 if (page_rec_is_infimum(rec)) {
1329
1330 return(ROW_NOT_FOUND);
1331 } else if (low_match != n_fields) {
1332
1333 return(ROW_NOT_FOUND);
1334 }
1335
1336 return(ROW_FOUND);
1337 }
1338
1339 /*******************************************************************//**
1340 Formats the raw data in "data" (in InnoDB on-disk format) that is of
1341 type DATA_INT using "prtype" and writes the result to "buf".
1342 If the data is in unknown format, then nothing is written to "buf",
1343 0 is returned and "format_in_hex" is set to TRUE, otherwise
1344 "format_in_hex" is left untouched.
1345 Not more than "buf_size" bytes are written to "buf".
1346 The result is always '\0'-terminated (provided buf_size > 0) and the
1347 number of bytes that were written to "buf" is returned (including the
1348 terminating '\0').
1349 @return number of bytes that were written */
1350 static
1351 ulint
row_raw_format_int(const char * data,ulint data_len,ulint prtype,char * buf,ulint buf_size,ibool * format_in_hex)1352 row_raw_format_int(
1353 /*===============*/
1354 const char* data, /*!< in: raw data */
1355 ulint data_len, /*!< in: raw data length
1356 in bytes */
1357 ulint prtype, /*!< in: precise type */
1358 char* buf, /*!< out: output buffer */
1359 ulint buf_size, /*!< in: output buffer size
1360 in bytes */
1361 ibool* format_in_hex) /*!< out: should the data be
1362 formated in hex */
1363 {
1364 ulint ret;
1365
1366 if (data_len <= sizeof(ib_uint64_t)) {
1367
1368 ib_uint64_t value;
1369 ibool unsigned_type = prtype & DATA_UNSIGNED;
1370
1371 value = mach_read_int_type(
1372 (const byte*) data, data_len, unsigned_type);
1373
1374 ret = (ulint) snprintf(
1375 buf, buf_size,
1376 unsigned_type ? "%llu" : "%lld", (longlong) value)+1;
1377 } else {
1378
1379 *format_in_hex = TRUE;
1380 ret = 0;
1381 }
1382
1383 return(ut_min(ret, buf_size));
1384 }
1385
1386 /*******************************************************************//**
1387 Formats the raw data in "data" (in InnoDB on-disk format) that is of
1388 type DATA_(CHAR|VARCHAR|MYSQL|VARMYSQL) using "prtype" and writes the
1389 result to "buf".
1390 If the data is in binary format, then nothing is written to "buf",
1391 0 is returned and "format_in_hex" is set to TRUE, otherwise
1392 "format_in_hex" is left untouched.
1393 Not more than "buf_size" bytes are written to "buf".
1394 The result is always '\0'-terminated (provided buf_size > 0) and the
1395 number of bytes that were written to "buf" is returned (including the
1396 terminating '\0').
1397 @return number of bytes that were written */
1398 static
1399 ulint
row_raw_format_str(const char * data,ulint data_len,ulint prtype,char * buf,ulint buf_size,ibool * format_in_hex)1400 row_raw_format_str(
1401 /*===============*/
1402 const char* data, /*!< in: raw data */
1403 ulint data_len, /*!< in: raw data length
1404 in bytes */
1405 ulint prtype, /*!< in: precise type */
1406 char* buf, /*!< out: output buffer */
1407 ulint buf_size, /*!< in: output buffer size
1408 in bytes */
1409 ibool* format_in_hex) /*!< out: should the data be
1410 formated in hex */
1411 {
1412 ulint charset_coll;
1413
1414 if (buf_size == 0) {
1415
1416 return(0);
1417 }
1418
1419 /* we assume system_charset_info is UTF-8 */
1420
1421 charset_coll = dtype_get_charset_coll(prtype);
1422
1423 if (UNIV_LIKELY(dtype_is_utf8(prtype))) {
1424
1425 return(ut_str_sql_format(data, data_len, buf, buf_size));
1426 }
1427 /* else */
1428
1429 if (charset_coll == DATA_MYSQL_BINARY_CHARSET_COLL) {
1430
1431 *format_in_hex = TRUE;
1432 return(0);
1433 }
1434 /* else */
1435
1436 return(innobase_raw_format(data, data_len, charset_coll,
1437 buf, buf_size));
1438 }
1439
1440 /*******************************************************************//**
1441 Formats the raw data in "data" (in InnoDB on-disk format) using
1442 "dict_field" and writes the result to "buf".
1443 Not more than "buf_size" bytes are written to "buf".
1444 The result is always NUL-terminated (provided buf_size is positive) and the
1445 number of bytes that were written to "buf" is returned (including the
1446 terminating NUL).
1447 @return number of bytes that were written */
1448 ulint
row_raw_format(const char * data,ulint data_len,const dict_field_t * dict_field,char * buf,ulint buf_size)1449 row_raw_format(
1450 /*===========*/
1451 const char* data, /*!< in: raw data */
1452 ulint data_len, /*!< in: raw data length
1453 in bytes */
1454 const dict_field_t* dict_field, /*!< in: index field */
1455 char* buf, /*!< out: output buffer */
1456 ulint buf_size) /*!< in: output buffer size
1457 in bytes */
1458 {
1459 ulint mtype;
1460 ulint prtype;
1461 ulint ret;
1462 ibool format_in_hex;
1463
1464 ut_ad(data_len != UNIV_SQL_DEFAULT);
1465
1466 if (buf_size == 0) {
1467
1468 return(0);
1469 }
1470
1471 if (data_len == UNIV_SQL_NULL) {
1472
1473 ret = snprintf((char*) buf, buf_size, "NULL") + 1;
1474
1475 return(ut_min(ret, buf_size));
1476 }
1477
1478 mtype = dict_field->col->mtype;
1479 prtype = dict_field->col->prtype;
1480
1481 format_in_hex = FALSE;
1482
1483 switch (mtype) {
1484 case DATA_INT:
1485
1486 ret = row_raw_format_int(data, data_len, prtype,
1487 buf, buf_size, &format_in_hex);
1488 if (format_in_hex) {
1489
1490 goto format_in_hex;
1491 }
1492 break;
1493 case DATA_CHAR:
1494 case DATA_VARCHAR:
1495 case DATA_MYSQL:
1496 case DATA_VARMYSQL:
1497
1498 ret = row_raw_format_str(data, data_len, prtype,
1499 buf, buf_size, &format_in_hex);
1500 if (format_in_hex) {
1501
1502 goto format_in_hex;
1503 }
1504
1505 break;
1506 /* XXX support more data types */
1507 default:
1508 format_in_hex:
1509
1510 if (UNIV_LIKELY(buf_size > 2)) {
1511
1512 memcpy(buf, "0x", 2);
1513 buf += 2;
1514 buf_size -= 2;
1515 ret = 2 + ut_raw_to_hex(data, data_len,
1516 buf, buf_size);
1517 } else {
1518
1519 buf[0] = '\0';
1520 ret = 1;
1521 }
1522 }
1523
1524 return(ret);
1525 }
1526
1527 #ifdef UNIV_ENABLE_UNIT_TEST_ROW_RAW_FORMAT_INT
1528
1529 #ifdef HAVE_UT_CHRONO_T
1530
1531 void
test_row_raw_format_int()1532 test_row_raw_format_int()
1533 {
1534 ulint ret;
1535 char buf[128];
1536 ibool format_in_hex;
1537 ulint i;
1538
1539 #define CALL_AND_TEST(data, data_len, prtype, buf, buf_size,\
1540 ret_expected, buf_expected, format_in_hex_expected)\
1541 do {\
1542 ibool ok = TRUE;\
1543 ulint i;\
1544 memset(buf, 'x', 10);\
1545 buf[10] = '\0';\
1546 format_in_hex = FALSE;\
1547 fprintf(stderr, "TESTING \"\\x");\
1548 for (i = 0; i < data_len; i++) {\
1549 fprintf(stderr, "%02hhX", data[i]);\
1550 }\
1551 fprintf(stderr, "\", %lu, %lu, %lu\n",\
1552 (ulint) data_len, (ulint) prtype,\
1553 (ulint) buf_size);\
1554 ret = row_raw_format_int(data, data_len, prtype,\
1555 buf, buf_size, &format_in_hex);\
1556 if (ret != ret_expected) {\
1557 fprintf(stderr, "expected ret %lu, got %lu\n",\
1558 (ulint) ret_expected, ret);\
1559 ok = FALSE;\
1560 }\
1561 if (strcmp((char*) buf, buf_expected) != 0) {\
1562 fprintf(stderr, "expected buf \"%s\", got \"%s\"\n",\
1563 buf_expected, buf);\
1564 ok = FALSE;\
1565 }\
1566 if (format_in_hex != format_in_hex_expected) {\
1567 fprintf(stderr, "expected format_in_hex %d, got %d\n",\
1568 (int) format_in_hex_expected,\
1569 (int) format_in_hex);\
1570 ok = FALSE;\
1571 }\
1572 if (ok) {\
1573 fprintf(stderr, "OK: %lu, \"%s\" %d\n\n",\
1574 (ulint) ret, buf, (int) format_in_hex);\
1575 } else {\
1576 return;\
1577 }\
1578 } while (0)
1579
1580 #if 1
1581 /* min values for signed 1-8 byte integers */
1582
1583 CALL_AND_TEST("\x00", 1, 0,
1584 buf, sizeof(buf), 5, "-128", 0);
1585
1586 CALL_AND_TEST("\x00\x00", 2, 0,
1587 buf, sizeof(buf), 7, "-32768", 0);
1588
1589 CALL_AND_TEST("\x00\x00\x00", 3, 0,
1590 buf, sizeof(buf), 9, "-8388608", 0);
1591
1592 CALL_AND_TEST("\x00\x00\x00\x00", 4, 0,
1593 buf, sizeof(buf), 12, "-2147483648", 0);
1594
1595 CALL_AND_TEST("\x00\x00\x00\x00\x00", 5, 0,
1596 buf, sizeof(buf), 14, "-549755813888", 0);
1597
1598 CALL_AND_TEST("\x00\x00\x00\x00\x00\x00", 6, 0,
1599 buf, sizeof(buf), 17, "-140737488355328", 0);
1600
1601 CALL_AND_TEST("\x00\x00\x00\x00\x00\x00\x00", 7, 0,
1602 buf, sizeof(buf), 19, "-36028797018963968", 0);
1603
1604 CALL_AND_TEST("\x00\x00\x00\x00\x00\x00\x00\x00", 8, 0,
1605 buf, sizeof(buf), 21, "-9223372036854775808", 0);
1606
1607 /* min values for unsigned 1-8 byte integers */
1608
1609 CALL_AND_TEST("\x00", 1, DATA_UNSIGNED,
1610 buf, sizeof(buf), 2, "0", 0);
1611
1612 CALL_AND_TEST("\x00\x00", 2, DATA_UNSIGNED,
1613 buf, sizeof(buf), 2, "0", 0);
1614
1615 CALL_AND_TEST("\x00\x00\x00", 3, DATA_UNSIGNED,
1616 buf, sizeof(buf), 2, "0", 0);
1617
1618 CALL_AND_TEST("\x00\x00\x00\x00", 4, DATA_UNSIGNED,
1619 buf, sizeof(buf), 2, "0", 0);
1620
1621 CALL_AND_TEST("\x00\x00\x00\x00\x00", 5, DATA_UNSIGNED,
1622 buf, sizeof(buf), 2, "0", 0);
1623
1624 CALL_AND_TEST("\x00\x00\x00\x00\x00\x00", 6, DATA_UNSIGNED,
1625 buf, sizeof(buf), 2, "0", 0);
1626
1627 CALL_AND_TEST("\x00\x00\x00\x00\x00\x00\x00", 7, DATA_UNSIGNED,
1628 buf, sizeof(buf), 2, "0", 0);
1629
1630 CALL_AND_TEST("\x00\x00\x00\x00\x00\x00\x00\x00", 8, DATA_UNSIGNED,
1631 buf, sizeof(buf), 2, "0", 0);
1632
1633 /* max values for signed 1-8 byte integers */
1634
1635 CALL_AND_TEST("\xFF", 1, 0,
1636 buf, sizeof(buf), 4, "127", 0);
1637
1638 CALL_AND_TEST("\xFF\xFF", 2, 0,
1639 buf, sizeof(buf), 6, "32767", 0);
1640
1641 CALL_AND_TEST("\xFF\xFF\xFF", 3, 0,
1642 buf, sizeof(buf), 8, "8388607", 0);
1643
1644 CALL_AND_TEST("\xFF\xFF\xFF\xFF", 4, 0,
1645 buf, sizeof(buf), 11, "2147483647", 0);
1646
1647 CALL_AND_TEST("\xFF\xFF\xFF\xFF\xFF", 5, 0,
1648 buf, sizeof(buf), 13, "549755813887", 0);
1649
1650 CALL_AND_TEST("\xFF\xFF\xFF\xFF\xFF\xFF", 6, 0,
1651 buf, sizeof(buf), 16, "140737488355327", 0);
1652
1653 CALL_AND_TEST("\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 7, 0,
1654 buf, sizeof(buf), 18, "36028797018963967", 0);
1655
1656 CALL_AND_TEST("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8, 0,
1657 buf, sizeof(buf), 20, "9223372036854775807", 0);
1658
1659 /* max values for unsigned 1-8 byte integers */
1660
1661 CALL_AND_TEST("\xFF", 1, DATA_UNSIGNED,
1662 buf, sizeof(buf), 4, "255", 0);
1663
1664 CALL_AND_TEST("\xFF\xFF", 2, DATA_UNSIGNED,
1665 buf, sizeof(buf), 6, "65535", 0);
1666
1667 CALL_AND_TEST("\xFF\xFF\xFF", 3, DATA_UNSIGNED,
1668 buf, sizeof(buf), 9, "16777215", 0);
1669
1670 CALL_AND_TEST("\xFF\xFF\xFF\xFF", 4, DATA_UNSIGNED,
1671 buf, sizeof(buf), 11, "4294967295", 0);
1672
1673 CALL_AND_TEST("\xFF\xFF\xFF\xFF\xFF", 5, DATA_UNSIGNED,
1674 buf, sizeof(buf), 14, "1099511627775", 0);
1675
1676 CALL_AND_TEST("\xFF\xFF\xFF\xFF\xFF\xFF", 6, DATA_UNSIGNED,
1677 buf, sizeof(buf), 16, "281474976710655", 0);
1678
1679 CALL_AND_TEST("\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 7, DATA_UNSIGNED,
1680 buf, sizeof(buf), 18, "72057594037927935", 0);
1681
1682 CALL_AND_TEST("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8, DATA_UNSIGNED,
1683 buf, sizeof(buf), 21, "18446744073709551615", 0);
1684
1685 /* some random values */
1686
1687 CALL_AND_TEST("\x52", 1, 0,
1688 buf, sizeof(buf), 4, "-46", 0);
1689
1690 CALL_AND_TEST("\x0E", 1, DATA_UNSIGNED,
1691 buf, sizeof(buf), 3, "14", 0);
1692
1693 CALL_AND_TEST("\x62\xCE", 2, 0,
1694 buf, sizeof(buf), 6, "-7474", 0);
1695
1696 CALL_AND_TEST("\x29\xD6", 2, DATA_UNSIGNED,
1697 buf, sizeof(buf), 6, "10710", 0);
1698
1699 CALL_AND_TEST("\x7F\xFF\x90", 3, 0,
1700 buf, sizeof(buf), 5, "-112", 0);
1701
1702 CALL_AND_TEST("\x00\xA1\x16", 3, DATA_UNSIGNED,
1703 buf, sizeof(buf), 6, "41238", 0);
1704
1705 CALL_AND_TEST("\x7F\xFF\xFF\xF7", 4, 0,
1706 buf, sizeof(buf), 3, "-9", 0);
1707
1708 CALL_AND_TEST("\x00\x00\x00\x5C", 4, DATA_UNSIGNED,
1709 buf, sizeof(buf), 3, "92", 0);
1710
1711 CALL_AND_TEST("\x7F\xFF\xFF\xFF\xFF\xFF\xDC\x63", 8, 0,
1712 buf, sizeof(buf), 6, "-9117", 0);
1713
1714 CALL_AND_TEST("\x00\x00\x00\x00\x00\x01\x64\x62", 8, DATA_UNSIGNED,
1715 buf, sizeof(buf), 6, "91234", 0);
1716 #endif
1717
1718 /* speed test */
1719
1720 ut_chrono_t ch(__func__);
1721
1722 for (i = 0; i < 1000000; i++) {
1723 row_raw_format_int("\x23", 1,
1724 0, buf, sizeof(buf),
1725 &format_in_hex);
1726 row_raw_format_int("\x23", 1,
1727 DATA_UNSIGNED, buf, sizeof(buf),
1728 &format_in_hex);
1729
1730 row_raw_format_int("\x00\x00\x00\x00\x00\x01\x64\x62", 8,
1731 0, buf, sizeof(buf),
1732 &format_in_hex);
1733 row_raw_format_int("\x00\x00\x00\x00\x00\x01\x64\x62", 8,
1734 DATA_UNSIGNED, buf, sizeof(buf),
1735 &format_in_hex);
1736 }
1737 }
1738
1739 #endif /* HAVE_UT_CHRONO_T */
1740
1741 #endif /* UNIV_ENABLE_UNIT_TEST_ROW_RAW_FORMAT_INT */
1742