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