MySQL 8.4.13
Source Code Documentation
rec.h
Go to the documentation of this file.
1/*****************************************************************************
2
3Copyright (c) 1994, 2026, Oracle and/or its affiliates.
4
5This program is free software; you can redistribute it and/or modify it under
6the terms of the GNU General Public License, version 2.0, as published by the
7Free Software Foundation.
8
9This program is designed to work with certain software (including
10but not limited to OpenSSL) that is licensed under separate terms,
11as designated in a particular file or component or in included license
12documentation. The authors of MySQL hereby grant you an additional
13permission to link the program and your derivative works with the
14separately licensed software that they have either included with
15the program or referenced in the documentation.
16
17This program is distributed in the hope that it will be useful, but WITHOUT
18ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
20for more details.
21
22You should have received a copy of the GNU General Public License along with
23this program; if not, write to the Free Software Foundation, Inc.,
2451 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25
26*****************************************************************************/
27
28/** @file rem/rec.h
29 Record manager
30
31 Created 5/30/1994 Heikki Tuuri
32 *************************************************************************/
33
34/** NOTE: The functions in this file should only use functions from
35other files in library. The code in this file is used to make a library for
36external tools. */
37
38/**
39Extra Bytes in Redudant Row Format
40
41The extra bytes of the redundant row format (old row format) is explained here.
42It contains a total of 6 bytes that occurs before the record origin. These
43bits and bytes are accessed with reference to the record origin. So when we
44say 3rd byte, it means that it is 3rd byte from the record origin.
45
46 byte 6 byte 5 byte 4 byte 3 byte 2 byte 1
47[iiiioooo][hhhhhhhh][hhhhhfff][fffffffs][pppppppp][pppppppp]+
48
491. The + is the record origin.
502. The next record pointer is given by the bits marked as 'p'. This takes
51 2 bytes - 1st and 2nd byte.
523. One bit is used to indicate whether the field offsets array uses 1 byte or
53 2 bytes each. This is given by the bit 's' in 3rd byte.
544. The total number of fields is given by the bits marked as 'f'. It spans
55 the 4th and 3rd bytes. It uses a total of 10 bits.
565. The heap number of the record is given by the bits marked as 'h'. It spans
57 the 5th and 4th bytes. It uses a total of 13 bits.
586. The record owned (by dir slot) information is given by bits marked as 'o'.
59 It uses a total of 4 bits. It is available in the 6th byte.
607. The info bits are given by the bits marked as 'i'. It uses a total of 4
61 bits. It is available in the 6th byte.
62*/
63
64#ifndef rem_rec_h
65#define rem_rec_h
66
67#include "dict0boot.h"
68#include "dict0dict.h"
69
70#include <optional>
71
72/* Compact flag ORed to the extra size returned by rec_get_offsets() */
73constexpr uint32_t REC_OFFS_COMPACT = 1U << 31;
74/* SQL NULL flag in offsets returned by rec_get_offsets() */
75constexpr uint32_t REC_OFFS_SQL_NULL = 1U << 31;
76/* External flag in offsets returned by rec_get_offsets() */
77constexpr uint32_t REC_OFFS_EXTERNAL = 1 << 30;
78/* Default value flag in offsets returned by rec_get_offsets() */
79constexpr uint32_t REC_OFFS_DEFAULT = 1 << 29;
80/* An INSTANT DROP flag in offsets returned by rec_get_offsets() */
81constexpr uint32_t REC_OFFS_DROP = 1 << 28;
82/* Mask for offsets returned by rec_get_offsets() */
83constexpr uint32_t REC_OFFS_MASK = REC_OFFS_DROP - 1;
84
85/* The offset of heap_no in a compact record */
86constexpr uint32_t REC_NEW_HEAP_NO = 4;
87/* The shift of heap_no in a compact record.
88The status is stored in the low-order bits. */
89constexpr uint32_t REC_HEAP_NO_SHIFT = 3;
90
91/* We list the byte offsets from the origin of the record, the mask,
92and the shift needed to obtain each bit-field of the record. */
93
94constexpr uint32_t REC_NEXT = 2;
95constexpr uint32_t REC_NEXT_MASK = 0xFFFFUL;
96constexpr uint32_t REC_NEXT_SHIFT = 0;
97
98constexpr uint32_t REC_OLD_SHORT = 3; /* This is single byte bit-field */
99constexpr uint32_t REC_OLD_SHORT_MASK = 0x1UL;
100constexpr uint32_t REC_OLD_SHORT_SHIFT = 0;
101
102constexpr uint32_t REC_OLD_N_FIELDS = 4;
103constexpr uint32_t REC_OLD_N_FIELDS_MASK = 0x7FEUL;
104constexpr uint32_t REC_OLD_N_FIELDS_SHIFT = 1;
105
106constexpr uint32_t REC_NEW_STATUS = 3; /* This is single byte bit-field */
107constexpr uint32_t REC_NEW_STATUS_MASK = 0x7UL;
108constexpr uint32_t REC_NEW_STATUS_SHIFT = 0;
109
110constexpr uint32_t REC_OLD_HEAP_NO = 5;
111constexpr uint32_t REC_HEAP_NO_MASK = 0xFFF8UL;
112#if 0 /* defined in rem0rec.h for use of page0zip.cc */
113#define REC_NEW_HEAP_NO 4
114#define REC_HEAP_NO_SHIFT 3
115#endif
116
117constexpr uint32_t REC_OLD_N_OWNED = 6; /* This is single byte bit-field */
118constexpr uint32_t REC_NEW_N_OWNED = 5; /* This is single byte bit-field */
119constexpr uint32_t REC_N_OWNED_MASK = 0xFUL;
120constexpr uint32_t REC_N_OWNED_SHIFT = 0;
121
122constexpr uint32_t REC_OLD_INFO_BITS = 6; /* This is single byte bit-field */
123constexpr uint32_t REC_NEW_INFO_BITS = 5; /* This is single byte bit-field */
124constexpr uint32_t REC_TMP_INFO_BITS = 1; /* This is single byte bit-field */
125constexpr uint32_t REC_INFO_BITS_MASK = 0xF0UL;
126constexpr uint32_t REC_INFO_BITS_SHIFT = 0;
127
128static_assert((REC_OLD_SHORT_MASK << (8 * (REC_OLD_SHORT - 3)) ^
130 REC_HEAP_NO_MASK << (8 * (REC_OLD_HEAP_NO - 4)) ^
131 REC_N_OWNED_MASK << (8 * (REC_OLD_N_OWNED - 3)) ^
133 0xFFFFFFFFUL) == 0,
134 "sum of old-style masks != 0xFFFFFFFFUL");
135static_assert((REC_NEW_STATUS_MASK << (8 * (REC_NEW_STATUS - 3)) ^
136 REC_HEAP_NO_MASK << (8 * (REC_NEW_HEAP_NO - 4)) ^
137 REC_N_OWNED_MASK << (8 * (REC_NEW_N_OWNED - 3)) ^
139 0xFFFFFFUL) == 0,
140 "sum of new-style masks != 0xFFFFFFUL");
141
142/* Info bit denoting the predefined minimum record: this bit is set
143if and only if the record is the first user record on a non-leaf
144B-tree page that is the leftmost page on its level
145(PAGE_LEVEL is nonzero and FIL_PAGE_PREV is FIL_NULL). */
146constexpr uint32_t REC_INFO_MIN_REC_FLAG = 0x10UL;
147/** The deleted flag in info bits; when bit is set to 1, it means the record has
148 been delete marked */
149constexpr uint32_t REC_INFO_DELETED_FLAG = 0x20UL;
150/* Use this bit to indicate record has version */
151constexpr uint32_t REC_INFO_VERSION_FLAG = 0x40UL;
152/** The instant ADD COLUMN flag. When it is set to 1, it means this record
153was inserted/updated after an instant ADD COLUMN. */
154constexpr uint32_t REC_INFO_INSTANT_FLAG = 0x80UL;
155
156/* Number of extra bytes in an old-style record,
157in addition to the data and the offsets */
158constexpr uint32_t REC_N_OLD_EXTRA_BYTES = 6;
159/* Number of extra bytes in a new-style record,
160in addition to the data and the offsets */
161constexpr int32_t REC_N_NEW_EXTRA_BYTES = 5;
162/* Number of extra bytes in a new-style temporary record,
163in addition to the data and the offsets.
164This is used only after instant ADD COLUMN. */
165constexpr uint32_t REC_N_TMP_EXTRA_BYTES = 1;
166
167#if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG
168/** Maximum number of records in a page */
170
171/* We use decltype(A.load()) not decltype(A)::value_type, as some compilers
172don't have it implemented, even as they should have this with the C++17
173implementation. Maybe this will be available on all compilers with C++20. */
174static_assert(MAX_REC_PER_PAGE <=
175 std::numeric_limits<
176 decltype(buf_block_t::ahi_t::n_pointers.load())>::max());
177
178#endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */
179
180/* Record status values */
181constexpr uint32_t REC_STATUS_ORDINARY = 0;
182constexpr uint32_t REC_STATUS_NODE_PTR = 1;
183constexpr uint32_t REC_STATUS_INFIMUM = 2;
184constexpr uint32_t REC_STATUS_SUPREMUM = 3;
185
186/* The following four constants are needed in page0zip.cc in order to
187efficiently compress and decompress pages. */
188
189/* Length of a B-tree node pointer, in bytes */
190constexpr uint32_t REC_NODE_PTR_SIZE = 4;
191
192/** SQL null flag in a 1-byte offset of ROW_FORMAT=REDUNDANT records */
193constexpr uint32_t REC_1BYTE_SQL_NULL_MASK = 0x80UL;
194/** SQL null flag in a 2-byte offset of ROW_FORMAT=REDUNDANT records */
195constexpr uint32_t REC_2BYTE_SQL_NULL_MASK = 0x8000UL;
196
197/** In a 2-byte offset of ROW_FORMAT=REDUNDANT records, the second most
198significant bit denotes that the tail of a field is stored off-page. */
199constexpr uint32_t REC_2BYTE_EXTERN_MASK = 0x4000UL;
200
201#ifdef UNIV_DEBUG
202/* Length of the rec_get_offsets() header */
203constexpr uint32_t REC_OFFS_HEADER_SIZE = 4;
204#else /* UNIV_DEBUG */
205/* Length of the rec_get_offsets() header */
206constexpr uint32_t REC_OFFS_HEADER_SIZE = 2;
207#endif /* UNIV_DEBUG */
208
209/* Number of elements that should be initially allocated for the
210offsets[] array, first passed to rec_get_offsets() */
211constexpr uint32_t REC_OFFS_NORMAL_SIZE = 100;
212constexpr uint32_t REC_OFFS_SMALL_SIZE = 10;
213
214/** Instant state of a record. Determined by instant and version bit in info
215 * bits. */
217 /* Record is neither instant nor versioned: Instant: 0, Version: 0 */
219 /* Record is versioned but not instant: Instant: 0, Version: 1 */
221 /* Record is instant but not versioned: Instant: 1, Version: 0 */
223};
224
225/* Get the base address of offsets. The extra_size is stored at
226this position, and following positions hold the end offsets of
227the fields. */
228
229static inline const ulint *rec_offs_base(const ulint *offsets) {
230 return offsets + REC_OFFS_HEADER_SIZE;
231}
232
233static inline ulint *rec_offs_base(ulint *offsets) {
234 return offsets + REC_OFFS_HEADER_SIZE;
235}
236
237/** Number of fields flag which means it occupies two bytes */
238static const uint8_t REC_N_FIELDS_TWO_BYTES_FLAG = 0x80;
239
240/** Max number of fields which can be stored in one byte */
241static const uint8_t REC_N_FIELDS_ONE_BYTE_MAX = 0x7F;
242
243/** The following function determines the offsets to each field
244 in the record. It can reuse a previously allocated array.
245 Note that after instant ADD COLUMN, if this is a record
246 from clustered index, fields in the record may be less than
247 the fields defined in the clustered index. So the offsets
248 size is allocated according to the clustered index fields.
249 @param[in] rec physical record
250 @param[in] index record descriptor
251 @param[in,out] offsets array consisting of offsets[0] allocated elements, or an
252 array from rec_get_offsets(), or NULL
253 @param[in] n_fields maximum number of initialized fields (ULINT_UNDEFINED is
254 all fields)
255 @param[in] location location where called
256 @param[in,out] heap memory heap
257 @return the new offsets */
258[[nodiscard]] ulint *rec_get_offsets(const rec_t *rec,
259 const dict_index_t *index, ulint *offsets,
260 ulint n_fields, ut::Location location,
261 mem_heap_t **heap);
262
263/** The following function determines the offsets to each field
264 in the record. It can reuse a previously allocated array. */
266 const byte *extra, /*!< in: the extra bytes of a
267 compact record in reverse order,
268 excluding the fixed-size
269 REC_N_NEW_EXTRA_BYTES */
270 const dict_index_t *index, /*!< in: record descriptor */
271 ulint node_ptr, /*!< in: nonzero=node pointer,
272 0=leaf node */
273 ulint *offsets); /*!< in/out: array consisting of
274 offsets[0] allocated elements */
275
276/** Gets a bit field from within 1 byte. */
278 const rec_t *rec, /*!< in: pointer to record origin */
279 ulint offs, /*!< in: offset from the origin down */
280 ulint mask, /*!< in: mask used to filter bits */
281 ulint shift) /*!< in: shift right applied after masking */
282{
283 ut_ad(rec);
284
285 return ((mach_read_from_1(rec - offs) & mask) >> shift);
286}
287
288/** Gets a bit field from within 2 bytes. */
289static inline uint16_t rec_get_bit_field_2(
290 const rec_t *rec, /*!< in: pointer to record origin */
291 ulint offs, /*!< in: offset from the origin down */
292 ulint mask, /*!< in: mask used to filter bits */
293 ulint shift) /*!< in: shift right applied after masking */
294{
295 ut_ad(rec);
296
297 return ((mach_read_from_2(rec - offs) & mask) >> shift);
298}
299
300/** The following function retrieves the status bits of a new-style record.
301 @return status bits */
302[[nodiscard]] static inline ulint rec_get_status(
303 const rec_t *rec) /*!< in: physical record */
304{
305 ulint ret;
306
307 ut_ad(rec);
308
311 ut_ad((ret & ~REC_NEW_STATUS_MASK) == 0);
312
313 return (ret);
314}
315
316#ifdef UNIV_DEBUG
317/** Check if the info bits are valid.
318@param[in] bits info bits to check
319@return true if valid */
320inline bool rec_info_bits_valid(ulint bits) {
321 return (0 == (bits & ~(REC_INFO_DELETED_FLAG | REC_INFO_MIN_REC_FLAG |
323}
324#endif /* UNIV_DEBUG */
325
326/** The following function is used to retrieve the info bits of a record.
327@param[in] rec physical record
328@param[in] comp nonzero=compact page format
329@return info bits */
330static inline ulint rec_get_info_bits(const rec_t *rec, ulint comp) {
331 const ulint val =
335 return (val);
336}
337
338/** The following function is used to retrieve the info bits of a temporary
339record.
340@param[in] rec physical record
341@return info bits */
342static inline ulint rec_get_info_bits_temp(const rec_t *rec) {
343 const ulint val = rec_get_bit_field_1(
346 return (val);
347}
348
349/** The following function is used to get the number of fields
350 in an old-style record, which is stored in the rec
351 @return number of data fields */
352[[nodiscard]] static inline uint16_t rec_get_n_fields_old_raw(
353 const rec_t *rec) /*!< in: physical record */
354{
355 uint16_t n_fields;
356
357 ut_ad(rec);
358
361
362 ut_ad(n_fields <= REC_MAX_N_FIELDS);
363 ut_ad(n_fields > 0);
364
365 return (n_fields);
366}
367
368/** The following function is used to get the number of fields
369in an old-style record. Have to consider the case that after
370instant ADD COLUMN, this record may have less fields than
371current index.
372@param[in] rec physical record
373@param[in] index index where the record resides
374@return number of data fields */
375[[nodiscard]] static inline uint16_t rec_get_n_fields_old(
376 const rec_t *rec, const dict_index_t *index) {
377 uint16_t n = rec_get_n_fields_old_raw(rec);
378
380 if (strcmp(index->name, RECOVERY_INDEX_TABLE_NAME) == 0) {
381 /* In recovery, index is not completely built. Skip validation. */
382 if (n > 1) /* For infimum/supremum, n is 1 */ {
383 n = static_cast<uint16_t>(dict_index_get_n_fields(index));
384 }
385 return n;
386 }
387
388 auto n_uniq = dict_index_get_n_unique_in_tree_nonleaf(index);
389
390 ut_ad(index->is_clustered());
392 ut_ad(n_uniq > 0);
393 /* Only when it's infimum or supremum, n is 1.
394 If n is exact n_uniq, this should be a record copied with prefix during
395 search.
396 And if it's node pointer, n is n_uniq + 1, which should be always less
397 than the number of fields in any leaf page, even if the record in
398 leaf page is before instant ADD COLUMN. This is because any record in
399 leaf page must have at least n_uniq + 2 (system columns) fields */
400 ut_ad(n == 1 || n >= n_uniq);
401 ut_ad(static_cast<uint16_t>(dict_index_get_n_fields(index)) > n_uniq + 1);
402 if (n > n_uniq + 1) {
403 /* This is leaf node. If table has INSTANT columns, then it is possible
404 that record might not have all the fields in index. So get it now from
405 index. */
406#ifdef UNIV_DEBUG
407 if (index->has_instant_cols() && !index->has_row_versions()) {
409 ulint rec_diff = dict_index_get_n_fields(index) - n;
410 ulint col_diff = index->table->n_cols - index->table->n_instant_cols;
411 ut_ad(rec_diff <= col_diff);
412 }
413
414 if (n != dict_index_get_n_fields(index)) {
416 }
417#endif /* UNIV_DEBUG */
418 n = static_cast<uint16_t>(dict_index_get_n_fields(index));
419 }
420 }
421
422 return (n);
423}
424
425/** The following function is used to get the number of fields
426 in a record. If it's REDUNDANT record, the returned number
427 would be a logic one which considers the fact that after
428 instant ADD COLUMN, some records may have less fields than
429 index.
430 @return number of data fields */
432 const rec_t *rec, /*!< in: physical record */
433 const dict_index_t *index) /*!< in: record descriptor */
434{
435 ut_ad(rec);
436 ut_ad(index);
437
438 if (!dict_table_is_comp(index->table)) {
439 return (rec_get_n_fields_old(rec, index));
440 }
441
442 switch (rec_get_status(rec)) {
444 return (dict_index_get_n_fields(index));
446 return (dict_index_get_n_unique_in_tree(index) + 1);
449 return (1);
450 default:
451 ut_error;
452 }
453}
454
455/** Confirms the n_fields of the entry is sane with comparing the other
456record in the same page specified
457@param[in] index index
458@param[in] rec record of the same page
459@param[in] entry index entry
460@return true if n_fields is sane */
461static inline bool rec_n_fields_is_sane(dict_index_t *index, const rec_t *rec,
462 const dtuple_t *entry) {
463 return (rec_get_n_fields(rec, index) == dtuple_get_n_fields(entry)
464 /* a record for older SYS_INDEXES table
465 (missing merge_threshold column) is acceptable. */
466 || (index->table->id == DICT_INDEXES_ID &&
467 rec_get_n_fields(rec, index) == dtuple_get_n_fields(entry) - 1));
468}
469
470/** The following function returns the number of allocated elements
471 for an array of offsets.
472 @return number of elements */
473[[nodiscard]] static inline ulint rec_offs_get_n_alloc(
474 const ulint *offsets) /*!< in: array for rec_get_offsets() */
475{
476 ulint n_alloc;
477 ut_ad(offsets);
478 n_alloc = offsets[0];
479 ut_ad(n_alloc > REC_OFFS_HEADER_SIZE);
480 UNIV_MEM_ASSERT_W(offsets, n_alloc * sizeof *offsets);
481 return (n_alloc);
482}
483
484/** The following function sets the number of allocated elements
485 for an array of offsets. */
486static inline void rec_offs_set_n_alloc(
487 ulint *offsets, /*!< out: array for rec_get_offsets(),
488 must be allocated */
489 ulint n_alloc) /*!< in: number of elements */
490{
491 ut_ad(offsets);
492 ut_ad(n_alloc > REC_OFFS_HEADER_SIZE);
493 UNIV_MEM_ASSERT_AND_ALLOC(offsets, n_alloc * sizeof *offsets);
494 offsets[0] = n_alloc;
495}
496
497/** The following function sets the number of fields in offsets. */
498static inline void rec_offs_set_n_fields(
499 ulint *offsets, /*!< in/out: array returned by
500 rec_get_offsets() */
501 ulint n_fields) /*!< in: number of fields */
502{
503 ut_ad(offsets);
504 ut_ad(n_fields > 0);
505 ut_ad(n_fields <= REC_MAX_N_FIELDS);
506 ut_ad(n_fields + REC_OFFS_HEADER_SIZE <= rec_offs_get_n_alloc(offsets));
507 offsets[1] = n_fields;
508}
509
510/** The following function returns the number of fields in a record.
511 @return number of fields */
512[[nodiscard]] static inline ulint rec_offs_n_fields(
513 const ulint *offsets) /*!< in: array returned by rec_get_offsets() */
514{
515 ulint n_fields;
516 ut_ad(offsets);
517 n_fields = offsets[1];
518 ut_ad(n_fields > 0);
519 ut_ad(n_fields <= REC_MAX_N_FIELDS);
520 ut_ad(n_fields + REC_OFFS_HEADER_SIZE <= rec_offs_get_n_alloc(offsets));
521 return (n_fields);
522}
523
524/** Determine the offset of a specified field in the record, when this
525field is a field added after an instant ADD COLUMN
526@param[in] index Clustered index where the record resides
527@param[in] n Nth field to get offset
528@param[in] offs Last offset before current field
529@return The offset of the specified field */
530static inline uint64_t rec_get_instant_offset(const dict_index_t *index,
531 ulint n, uint64_t offs) {
533
535 index->get_nth_default(n, &length);
536
537 if (length == UNIV_SQL_NULL) {
538 return (offs | REC_OFFS_DEFAULT | REC_OFFS_SQL_NULL);
539 } else {
540 return (offs | REC_OFFS_DEFAULT);
541 }
542}
543
544/** The following function determines the offsets to each field in the record.
545The offsets are written to a previously allocated array of ulint, where
546rec_offs_n_fields(offsets) has been initialized to the number of fields in the
547record. The rest of the array will be initialized by this function.
548- rec_offs_base(offsets)[0] will be set to the extra size
549 (if REC_OFFS_COMPACT is set, the record is in the new format;
550 if REC_OFFS_EXTERNAL is set, the record contains externally stored columns).
551- rec_offs_base(offsets)[1..n_fields] will be set to offsets past the end of
552 fields 0..n_fields, or to the beginning of fields 1..n_fields+1.
553- If the high-order bit of the offset at [i+1] is set (REC_OFFS_SQL_NULL),
554 the field i is NULL.
555- If the second high-order bit of the offset at [i+1] is set
556(REC_OFFS_EXTERNAL), the field i is being stored externally.
557@param[in] rec physical record
558@param[in] index record descriptor
559@param[in,out] offsets array of offsets */
560void rec_init_offsets(const rec_t *rec, const dict_index_t *index,
561 ulint *offsets);
562
563#ifdef UNIV_DEBUG
564/** Validates offsets returned by rec_get_offsets().
565@param[in] rec record whose offsets are being validated or nullptr.
566@param[in] index index to which record belongs or nullptr.
567@param[in] offsets the record offsets array returned by rec_get_offsets()
568@param[in] check_status if true, check status bits of the record.
569@return true if valid */
570[[nodiscard]] static inline bool rec_offs_validate(
571 const rec_t *rec, const dict_index_t *index, const ulint *offsets,
572 const bool check_status = true) {
573 ulint i = rec_offs_n_fields(offsets);
574 ulint last = ULINT_MAX;
575 ulint comp = *rec_offs_base(offsets) & REC_OFFS_COMPACT;
576
577 if (rec) {
578 /* The offsets array might be:
579 - specific to the `rec`, in which case its address is stored in offsets[2],
580 - cached and shared by many records, in which case we've passed rec=nullptr
581 when preparing the offsets array.
582 We use caching only for the ROW_FORMAT=COMPACT format. */
583 ut_ad((ulint)rec == offsets[2] || ((ulint) nullptr == offsets[2] &&
584 offsets == index->rec_cache.offsets));
585 if (!comp && index != nullptr) {
586 ut_a(rec_get_n_fields_old(rec, index) >= i);
587 }
588 }
589
590 if (index) {
591 ulint max_n_fields;
592 ut_ad((ulint)index == offsets[3]);
593 ulint n_fields = dict_index_get_n_fields(index);
594 ulint n_unique_in_tree = dict_index_get_n_unique_in_tree(index) + 1;
595 max_n_fields = std::max(n_fields, n_unique_in_tree);
596 if (!comp && rec != nullptr && rec_get_n_fields_old_raw(rec) < i) {
598 }
599
600 /* In the case of mrec_t the status will not be there. */
601 if (check_status) {
602 if (comp && rec) {
603 switch (rec_get_status(rec)) {
605 break;
607 max_n_fields = dict_index_get_n_unique_in_tree(index) + 1;
608 break;
611 max_n_fields = 1;
612 break;
613 default:
614 ut_error;
615 }
616 }
617 }
618
619 /* index->n_def == 0 for dummy indexes if !comp */
620 ut_a(!comp || index->n_def);
621 ut_a(!index->n_def || i <= max_n_fields);
622 }
623 while (i--) {
624 ulint curr = rec_offs_base(offsets)[1 + i] & REC_OFFS_MASK;
625 ut_a(curr <= last);
626 last = curr;
627 }
628 return true;
629}
630
631/** Updates debug data in offsets, in order to avoid bogus
632 rec_offs_validate() failures. */
633static inline void rec_offs_make_valid(
634 const rec_t *rec, /*!< in: record */
635 const dict_index_t *index, /*!< in: record descriptor */
636 ulint *offsets) /*!< in: array returned by
637 rec_get_offsets() */
638{
639 /* offsets are either intended for this particular rec, or to be cached */
640 ut_ad(rec || offsets == index->rec_cache.offsets);
641 ut_ad(index);
642 ut_ad(offsets);
643 ut_ad(rec == nullptr ||
644 rec_get_n_fields(rec, index) >= rec_offs_n_fields(offsets));
645 offsets[2] = (ulint)rec;
646 offsets[3] = (ulint)index;
647}
648
649/** Check if the given two record offsets are identical.
650@param[in] offsets1 field offsets of a record
651@param[in] offsets2 field offsets of a record
652@return true if they are identical, false otherwise. */
653bool rec_offs_cmp(ulint *offsets1, ulint *offsets2);
654
655/** Print the record offsets.
656@param[in] out the output stream to which offsets are printed.
657@param[in] offsets the field offsets of the record.
658@return the output stream. */
659std::ostream &rec_offs_print(std::ostream &out, const ulint *offsets);
660#else
661#define rec_offs_make_valid(rec, index, offsets) ((void)0)
662#endif /* UNIV_DEBUG */
663
664/** The following function tells if a new-style record is instant record.
665@param[in] rec new-style record
666@return true if it is instant affected */
667static inline bool rec_get_instant_flag_new(const rec_t *rec) {
668 ulint info = rec_get_info_bits(rec, true);
669 return ((info & REC_INFO_INSTANT_FLAG) != 0);
670}
671
672/** The following function tells if a new-style temp record is instant record.
673@param[in] rec new-style temp record
674@return true if it is instant affected */
675static inline bool rec_get_instant_flag_new_temp(const rec_t *rec) {
676 ulint info = rec_get_info_bits_temp(rec);
677 return ((info & REC_INFO_INSTANT_FLAG) != 0);
678}
679
680/** A simplified variant rec_init_offsets(rec, index, offsets) for the case in
681which the rec contains only fixed-length columns and non-NULL values in them,
682thus we can compute the offsets without looking at the rec. Such offsets can be
683cached and reused for many recs which don't contain NULLs.
684@see rec_init_offsets for more details
685@param[in] index The index for which we want to cache the fixed offsets
686@param[in,out] offsets The already allocated array to store the offsets.
687 It should already have been initialized with
688 rec_offs_set_n_alloc() and rec_offs_set_n_fields() before
689 the call.
690 This function will fill all the other elements. */
691inline void rec_init_fixed_offsets(const dict_index_t *index, ulint *offsets) {
693
694 rec_offs_make_valid(nullptr, index, offsets);
695 rec_offs_base(offsets)[0] =
698 const auto n_fields = rec_offs_n_fields(offsets);
699 auto field_end = rec_offs_base(offsets) + 1;
700 for (size_t i = 0; i < n_fields; i++) {
701 field_end[i] = (i ? field_end[i - 1] : 0) + index->get_field(i)->fixed_len;
702 }
703}
704
705/** The following function tells if a new-style record is versioned.
706@param[in] rec new-style (COMPACT/DYNAMIC) record
707@return true if it is versioned */
708static inline bool rec_new_is_versioned(const rec_t *rec) {
709 ulint info = rec_get_info_bits(rec, true);
710 return ((info & REC_INFO_VERSION_FLAG) != 0);
711}
712
713/** The following function tells if an old-style record is versioned.
714@param[in] rec old-style (REDUNDANT) record
715@return true if it's versioned */
716static inline bool rec_old_is_versioned(const rec_t *rec) {
717 ulint info = rec_get_info_bits(rec, false);
718 return ((info & REC_INFO_VERSION_FLAG) != 0);
719}
720
721/** The following function tells if a temporary record is versioned.
722@param[in] rec new-style temporary record
723@return true if it's instant affected */
724static inline bool rec_new_temp_is_versioned(const rec_t *rec) {
725 ulint info = rec_get_info_bits_temp(rec);
726 return ((info & REC_INFO_VERSION_FLAG) != 0);
727}
728
729#if defined(UNIV_DEBUG) && !defined(UNIV_LIBRARY) && !defined(UNIV_HOTBACKUP)
730#define UNIV_ROW_IMPORT_DBG
731/* Declarations of methods from row0import that are less efficient but check
732against page corruptions. */
733
735 const page_t *page, const byte *ptr, uint16_t *n_fields, uint16_t *length,
736 bool force_page_boundary_check);
737
738[[nodiscard]] bool row_import_rec_check_old_metadata(const dict_index_t *index,
739 const rec_t *rec);
740
741[[nodiscard]] bool row_import_rec_check_comp_metadata(const dict_index_t *index,
742 const rec_t *rec);
743
745 const dict_index_t *index);
746
748 const page_t *page, const dict_index_t *index);
749#endif
750
752#ifdef UNIV_ROW_IMPORT_DBG
753 public:
756
758 delete;
760 const Row_import_metadata_check_guard &) = delete;
761#endif
762};
763
764/** Get the number of fields for one new style leaf page record.
765This is only needed for table after instant ADD COLUMN.
766@param[in] rec leaf page record
767@param[in] extra_bytes extra bytes of this record
768@param[in,out] length length of number of fields
769@return number of fields */
770static inline uint32_t rec_get_n_fields_instant(const rec_t *rec,
771 const ulint extra_bytes,
772 uint16_t *length) {
773 uint16_t n_fields;
774 const byte *ptr;
775
776 ptr = rec - (extra_bytes + 1);
777
778 ut_d(std::optional<uint32_t> checked_result);
779 ut_d(uint16_t checked_length{});
780#ifdef UNIV_ROW_IMPORT_DBG
781 /* This helper is also used for compact temporary records. Only normal
782 compact page records use REC_N_NEW_EXTRA_BYTES and have the record layout
783 validated by row import. */
784 if (extra_bytes == REC_N_NEW_EXTRA_BYTES) {
785 uint16_t checked_fields;
787 (const page_t *)ut_align_down(rec, UNIV_PAGE_SIZE), ptr,
788 &checked_fields, &checked_length, false);
789 ut_a(res);
790 checked_result = checked_fields;
791 }
792#endif
793
794 if ((*ptr & REC_N_FIELDS_TWO_BYTES_FLAG) == 0) {
795 *length = 1;
796 ut_ad(!checked_result || (checked_result == *ptr && checked_length == 1));
797 return (*ptr);
798 }
799
800 *length = 2;
801 n_fields = ((*ptr-- & REC_N_FIELDS_ONE_BYTE_MAX) << 8);
802 n_fields |= *ptr;
803 ut_ad(n_fields < REC_MAX_N_FIELDS);
804 ut_ad(n_fields != 0);
805
806 ut_ad(!checked_result ||
807 (checked_result == n_fields && checked_length == *length));
808 return (n_fields);
809}
810
811/* For INSTANT ADD/DROP, we may have following 5 types of rec for table :
812 +----------------------------------------------------------------------------+
813 | SCENARIO | STATE |
814 |----------------------------------+------------+---------+------------------|
815 | Row property | V <= 8.0.28| Bit set | Stored on row |
816 +----------------------------------+------------+---------+------------------+
817 | INSERTED before 1st INSTANT ADD | Y | NONE | N/A |
818 |----------------------------------+------------+---------+------------------|
819 | INSERTED after 1st INSTANT ADD | Y | INSTANT | # of fields |
820 |----------------------------------+------------+---------+------------------|
821 | INSERTED before INSTANT ADD/DROP | Y | VERSION | Version = 0 |
822 |----------------------------------+------------+---------+------------------|
823 | INSERTED before INSTANT ADD/DROP | N | NONE | N/A |
824 |----------------------------------+------------+---------+------------------|
825 | INSERTED after INSTANT ADD/DROP | Y/N | VERSION | row version |
826 +----------------------------------------------------------------------------+
827*/
829 /* Record was inserted before first instant add done in the earlier
830 implementation. */
832 /* Record was inserted after first instant add done in the earlier
833 implementation. */
835 /* Record was inserted after upgrade but before first instant add done in the
836 new implementation. */
838 /* Record was inserted before first instant add/drop done in the new
839 implementation. */
841 /* Record was inserted after first instant add/drop done in the new
842 implementation. */
844 /* Record belongs to table with no verison no instant */
846 NONE
848
850 const dict_index_t *index, const rec_t *rec, bool temp) {
851 ut_ad(dict_table_is_comp(index->table) || temp);
852
853 if (!index->has_instant_cols_or_row_versions()) {
855 }
856
857 /* Position just before info-bits where version will be there if any */
858 const byte *v_ptr =
859 (byte *)rec -
861 const bool is_versioned =
863 const uint8_t version = (is_versioned) ? (uint8_t)(*v_ptr) : UINT8_UNDEFINED;
864
865 const bool is_instant = (temp) ? rec_get_instant_flag_new_temp(rec)
867
868 /* Only one of the two bits could be set */
869 DBUG_EXECUTE_IF("innodb_rec_instant_version_bit_set", {
870 ib::error() << "Record has both instant and version bit set in Table '"
871 << index->table_name << "', Index '" << index->name()
872 << "'. This indicates that the table may be corrupt. Please "
873 "run CHECK TABLE before proceeding.";
874 });
875
876 if (is_versioned && is_instant) {
877 ib::error() << "Record has both instant and version bit set in Table '"
878 << index->table_name << "', Index '" << index->name()
879 << "'. This indicates that the table may be corrupt. Please "
880 "run CHECK TABLE before proceeding.";
881 }
882 ut_ad(!is_versioned || !is_instant);
883
884 enum REC_INSERT_STATE rec_insert_state = REC_INSERT_STATE::NONE;
885 if (is_versioned) {
887 if (version == 0) {
888 ut_ad(index->has_instant_cols());
889 rec_insert_state =
891 } else {
892 ut_ad(index->has_row_versions());
894 }
895 } else if (is_instant) {
896 ut_ad(index->table->has_instant_cols());
898 } else if (index->table->has_instant_cols()) {
900 } else {
902 }
903
904 ut_ad(rec_insert_state != REC_INSERT_STATE::NONE);
905 return rec_insert_state;
906}
907
908/* Following function is to set NULLS and LENS pointers correctly for a temp
909record generated for a record from REDUNDANT FORAMT
910@param[in] index record descriptor
911@param[in,out] rec temp record
912@param[out] n_null number of nullable columns in record
913@param[out] nulls pointer to nullbit map in temp record
914@param[out] lens pointer to lens in temp record
915@returns the row version stored in record or nondefault fields in record */
917 const dict_index_t *index, const rec_t *rec, uint16_t *n_null,
918 const byte **nulls, const byte **lens, uint16_t &non_default_fields,
919 uint8_t &row_version) {
921
922 non_default_fields = static_cast<uint16_t>(dict_index_get_n_fields(index));
923
924 row_version = UINT8_UNDEFINED;
925
926 /* Set nulls just before the record */
927 *nulls = rec - 1;
928
929 enum REC_INSERT_STATE rec_insert_state =
930 get_rec_insert_state(index, rec, true);
931
932 if (rec_insert_state == INSERTED_INTO_TABLE_WITH_NO_INSTANT_NO_VERSION) {
933 *n_null = index->n_nullable;
934 *lens = *nulls - UT_BITS_IN_BYTES(*n_null);
935 return rec_insert_state;
936 }
937
938 /* info-bits must be present. Shift nulls before that. */
939 *nulls -= REC_N_TMP_EXTRA_BYTES;
940
941 switch (rec_insert_state) {
945 *n_null = index->get_nullable_in_version(0);
946 *lens = *nulls - UT_BITS_IN_BYTES(*n_null);
947 } break;
948
951 /* Read the version information */
952 row_version = (uint8_t)(**nulls);
953 ut_a(is_valid_row_version(row_version));
954
955 /* Shift nulls before the record version */
956 *nulls -= 1;
957 *n_null = index->get_nullable_in_version(row_version);
958 } break;
959
961 default:
962 ut_a(0);
963 }
964
965 *lens = *nulls - UT_BITS_IN_BYTES(*n_null);
966 return rec_insert_state;
967}
968
969/** Determines the information about null bytes and variable length bytes
970for a new-style temporary record
971@param[in] rec physical record
972@param[in] index index where the record resides
973@param[out] nulls the start of null bytes
974@param[out] lens the start of variable length bytes
975@param[out] n_null number of null fields
976@param[out] non_default_fields non default fields from record
977@param[out] row_version row version of the record
978@return the row inserted state */
980 const rec_t *rec, const dict_index_t *index, const byte **nulls,
981 const byte **lens, uint16_t *n_null, uint16_t &non_default_fields,
982 uint8_t &row_version) {
983 /* Following is the format for TEMP record.
984 +----+----+-------------------+--------------------+
985 | OP | ES |<-- Extra info --> | F1 | F2 | ... | Fn|
986 +----+----+-------------------+--------------------+
987 |
988 v
989 +--------------------+-------+---------+-----------+
990 | LN | ... | L2 | L1 | nulls | version | info-bits |
991 +--------------------+-------+---------+-----------+
992 <------ LENS ------>
993
994 where
995 info-bits => Present iff table has INSTANT/VERSION
996 version => version number iff version bit is set.
997 */
998
999 /* Redundant format */
1000 if (!dict_table_is_comp(index->table)) {
1001 return init_nulls_lens_for_temp_redundant(index, rec, n_null, nulls, lens,
1002 non_default_fields, row_version);
1003 }
1004
1005 non_default_fields = static_cast<uint16_t>(dict_index_get_n_fields(index));
1006
1007 row_version = UINT8_UNDEFINED;
1008
1009 /* Set nulls just before the record */
1010 *nulls = rec - 1;
1011
1012 const enum REC_INSERT_STATE rec_insert_state =
1013 get_rec_insert_state(index, rec, true);
1014
1015 if (rec_insert_state == INSERTED_INTO_TABLE_WITH_NO_INSTANT_NO_VERSION) {
1016 *n_null = index->n_nullable;
1017 *lens = *nulls - UT_BITS_IN_BYTES(*n_null);
1018 return rec_insert_state;
1019 }
1020
1021 /* info-bits must be present. Shift nulls before that. */
1022 *nulls -= REC_N_TMP_EXTRA_BYTES;
1023
1024 switch (rec_insert_state) {
1027 /* Read the version information */
1028 row_version = (uint8_t)(**nulls);
1029 ut_a(is_valid_row_version(row_version));
1030
1031 /* Shift nulls before the record version */
1032 *nulls -= 1;
1033
1034 *n_null = index->get_nullable_in_version(row_version);
1035 } break;
1036
1038 /* Row inserted after first instant ADD COLUMN V1 */
1039 ut_ad(index->table->has_instant_cols());
1040 uint16_t length;
1041 non_default_fields =
1043 ut_ad(length == 1 || length == 2);
1044
1045 /* Shift nulls before "number of fields" */
1046 *nulls -= length;
1047 *n_null = index->get_n_nullable_before(non_default_fields);
1048 } break;
1049
1051 *n_null = index->get_nullable_before_instant_add_drop();
1052 non_default_fields = index->get_instant_fields();
1053 } break;
1054
1056 /* Row inserted before first INSTANT ADD/DROP in V2 */
1057 *n_null = index->get_nullable_before_instant_add_drop();
1058 } break;
1059
1061 default:
1062 ut_a(0);
1063 }
1064
1065 /* Position lens */
1066 *lens = *nulls - UT_BITS_IN_BYTES(*n_null);
1067
1068 return (rec_insert_state);
1069}
1070
1071/** Determines the information about null bytes and variable length bytes
1072for a new style record
1073@param[in] rec physical record
1074@param[in] index index where the record resides
1075@param[out] nulls the start of null bytes
1076@param[out] lens the start of variable length bytes
1077@param[out] n_null number of null fields
1078@param[out] non_default_fields non default fields from record
1079@param[out] row_version row version of the record
1080@return number of fields with no default or the row version of record */
1082 const rec_t *rec, const dict_index_t *index, const byte **nulls,
1083 const byte **lens, uint16_t *n_null, uint16_t &non_default_fields,
1084 uint8_t &row_version) {
1085 non_default_fields = static_cast<uint16_t>(dict_index_get_n_fields(index));
1086
1087 row_version = UINT8_UNDEFINED;
1088
1089 /* Position nulls */
1090 *nulls = rec - (REC_N_NEW_EXTRA_BYTES + 1);
1091
1092 const enum REC_INSERT_STATE rec_insert_state =
1093 get_rec_insert_state(index, rec, false);
1094
1095 switch (rec_insert_state) {
1099
1100 *n_null = index->n_nullable;
1101 } break;
1102
1105 /* Read the row version from record */
1106 row_version = (uint8_t)(**nulls);
1107 ut_ad(is_valid_row_version(row_version));
1108
1109#ifdef UNIV_DEBUG
1110 if (strcmp(index->name, RECOVERY_INDEX_TABLE_NAME) != 0) {
1111 if (row_version > 0) {
1112 /* Row inserted after first instant ADD/DROP COLUMN V2 */
1113 ut_ad(index->table->has_row_versions());
1114 } else {
1115 /* Upgraded table. Row inserted before V2 INSTANT ADD/DROP */
1116 ut_ad(index->table->is_upgraded_instant());
1117 }
1118 }
1119#endif
1120
1121 /* Reposition nulls to skip version */
1122 *nulls -= 1;
1123 *n_null = index->get_nullable_in_version(row_version);
1124 } break;
1125
1127 /* Row inserted after first instant ADD COLUMN V1 */
1128 uint16_t length;
1129 non_default_fields =
1131 ut_ad(length == 1 || length == 2);
1132
1133 /* Reposition nulls to skip number of fields */
1134 *nulls -= length;
1135 *n_null = index->calculate_n_instant_nullable(non_default_fields);
1136 } break;
1137
1139 *n_null = index->get_nullable_before_instant_add_drop();
1140 non_default_fields = index->get_instant_fields();
1141 } break;
1142
1144 /* Row inserted before first INSTANT ADD/DROP in V2 */
1145 *n_null = index->get_nullable_before_instant_add_drop();
1146 } break;
1147
1148 default:
1149 ut_ad(false);
1150 }
1151
1152 /* Position lens */
1153 *lens = *nulls - UT_BITS_IN_BYTES(*n_null);
1154
1155 /* Return record version or non_default_field stored */
1156 return (rec_insert_state);
1157}
1158
1159/** Determine the offset to each field in a leaf-page record in
1160ROW_FORMAT=COMPACT. This is a special case of rec_init_offsets() and
1161rec_get_offsets().
1162@param[in] rec physical record in ROW_FORMAT=COMPACT
1163@param[in] temp whether to use the format for temporary files in index
1164 creation
1165@param[in] index record descriptor
1166@param[in,out] offsets array of offsets
1167@note This long method is made inline because it is on performance sensitive hot
1168path. One must run performance tests if they intend to improve this method. */
1169inline void rec_init_offsets_comp_ordinary(const rec_t *rec, bool temp,
1170 const dict_index_t *index,
1171 ulint *offsets) {
1172#ifdef UNIV_DEBUG
1173 /* We cannot invoke rec_offs_make_valid() here if temp=true.
1174 Similarly, rec_offs_validate() will fail in that case, because
1175 it invokes rec_get_status(). */
1176 offsets[2] = (ulint)rec;
1177 offsets[3] = (ulint)index;
1178#endif /* UNIV_DEBUG */
1179
1180 const byte *nulls = nullptr;
1181 const byte *lens = nullptr;
1182 uint16_t n_null = 0;
1183 enum REC_INSERT_STATE rec_insert_state = REC_INSERT_STATE::NONE;
1184 uint8_t row_version = UINT8_UNDEFINED;
1185 uint16_t non_default_fields = 0;
1186
1187 if (temp) {
1188 rec_insert_state = rec_init_null_and_len_temp(
1189 rec, index, &nulls, &lens, &n_null, non_default_fields, row_version);
1190 } else {
1191 rec_insert_state = rec_init_null_and_len_comp(
1192 rec, index, &nulls, &lens, &n_null, non_default_fields, row_version);
1193 }
1194
1195 ut_ad(temp || dict_table_is_comp(index->table));
1196
1197 if (temp) {
1198 if (dict_table_is_comp(index->table)) {
1199 /* No need to do adjust fixed_len=0. We only need to
1200 adjust it for ROW_FORMAT=REDUNDANT. */
1201 temp = false;
1202 } else {
1203 /* Redundant temp row. Old instant record is logged as version 0. */
1204 if (rec_insert_state == INSERTED_BEFORE_INSTANT_ADD_OLD_IMPLEMENTATION ||
1207 ut_ad(row_version == UINT8_UNDEFINED);
1208 }
1209 }
1210 }
1211
1212 /* read the lengths of fields 0..n */
1213 ulint offs = 0;
1214 ulint any_ext = 0;
1215 ulint null_mask = 1;
1216 uint16_t i = 0;
1217 do {
1218 /* Fields are stored on disk in version they are added in and are
1219 maintained in fields_array in the same order. Get the right field. */
1220 const dict_field_t *field = index->get_physical_field(i);
1221 const dict_col_t *col = field->col;
1222 uint64_t len;
1223
1224 switch (rec_insert_state) {
1227 break;
1228
1230 ut_ad(row_version == UINT8_UNDEFINED || row_version == 0);
1231 ut_ad(index->has_row_versions() || temp);
1232 /* Record has to be interpreted in v0. */
1233 row_version = 0;
1234 }
1235 [[fallthrough]];
1238 ut_ad(is_valid_row_version(row_version));
1239 /* A record may have version=0 if it's from upgrade table */
1240 ut_ad(index->has_row_versions() ||
1241 (index->table->is_upgraded_instant() && row_version == 0));
1242
1243 /* Based on the record version and column information, see if this
1244 column is there in this record or not. */
1245 if (col->is_dropped_in_or_before(row_version)) {
1246 /* This columns is dropped before or on this row version so its data
1247 won't be there on row. So no need to store the length. Instead, store
1248 offs ORed with REC_OFFS_DROP to indicate the same. */
1249 len = offs | REC_OFFS_DROP;
1250 goto resolved;
1251
1252 /* NOTE : Existing rows, which have data for this column, would still
1253 need to process this column, so don't skip and store the correct
1254 length there. Though it will be skipped while fetching row. */
1255 } else if (col->is_added_after(row_version)) {
1256 /* This columns is added after this row version. In this case no need
1257 to store the length. Instead store only if it is NULL or DEFAULT
1258 value. */
1259 len = rec_get_instant_offset(index, i, offs);
1260
1261 goto resolved;
1262 }
1263 } break;
1264
1267 ut_ad(non_default_fields > 0);
1268 ut_ad(index->has_instant_cols());
1269 ut_ad(!is_valid_row_version(row_version));
1270
1271 if (i >= non_default_fields) {
1272 /* This would be the case when column doesn't exists in the row. In
1273 this case we need not to store the length. Instead we store only if
1274 the column is NULL or DEFAULT value. */
1275 len = rec_get_instant_offset(index, i, offs);
1276
1277 goto resolved;
1278 }
1279
1280 /* Note : Even if the column has been dropped, this row in V1 would
1281 definitely have the value of this column. */
1282 } break;
1283
1284 default:
1285 ut_ad(false);
1286 }
1287
1288 if (!(col->prtype & DATA_NOT_NULL)) {
1289 /* nullable field => read the null flag */
1290 ut_ad(n_null--);
1291
1292 if (UNIV_UNLIKELY(!(byte)null_mask)) {
1293 nulls--;
1294 null_mask = 1;
1295 }
1296
1297 if (*nulls & null_mask) {
1298 null_mask <<= 1;
1299 /* No length is stored for NULL fields.
1300 We do not advance offs, and we set
1301 the length to zero and enable the
1302 SQL NULL flag in offsets[]. */
1303 len = offs | REC_OFFS_SQL_NULL;
1304 goto resolved;
1305 }
1306 null_mask <<= 1;
1307 }
1308
1309 if (!field->fixed_len || (temp && !col->get_fixed_size(temp))) {
1310 ut_ad(col->mtype != DATA_POINT);
1311 /* Variable-length field: read the length */
1312 len = *lens--;
1313 /* If the maximum length of the field is up
1314 to 255 bytes, the actual length is always
1315 stored in one byte. If the maximum length is
1316 more than 255 bytes, the actual length is
1317 stored in one byte for 0..127. The length
1318 will be encoded in two bytes when it is 128 or
1319 more, or when the field is stored externally. */
1320 if (DATA_BIG_COL(col)) {
1321 if (len & 0x80) {
1322 /* 1exxxxxxx xxxxxxxx */
1323 len <<= 8;
1324 len |= *lens--;
1325
1326 offs += len & 0x3fff;
1327 if (UNIV_UNLIKELY(len & 0x4000)) {
1328 ut_ad(index->is_clustered());
1329 any_ext = REC_OFFS_EXTERNAL;
1330 len = offs | REC_OFFS_EXTERNAL;
1331 } else {
1332 len = offs;
1333 }
1334
1335 goto resolved;
1336 }
1337 }
1338
1339 len = offs += len;
1340 } else {
1341 len = offs += field->fixed_len;
1342 }
1343 resolved:
1344 rec_offs_base(offsets)[i + 1] = len;
1345 } while (++i < rec_offs_n_fields(offsets));
1346
1347 *rec_offs_base(offsets) = (rec - (lens + 1)) | REC_OFFS_COMPACT | any_ext;
1348}
1349
1350/** The following function is used to test whether the data offsets in the
1351 record are stored in one-byte or two-byte format.
1352 @return true if 1-byte form */
1353[[nodiscard]] static inline bool rec_get_1byte_offs_flag(
1354 const rec_t *rec) /*!< in: physical record */
1355{
1358}
1359
1360#endif
Definition: rec.h:751
Row_import_metadata_check_guard(const Row_import_metadata_check_guard &)=delete
Row_import_metadata_check_guard & operator=(const Row_import_metadata_check_guard &)=delete
The class error is used to emit error messages.
Definition: ut0log.h:231
int page
Definition: ctype-mb.cc:1234
static ulint dtuple_get_n_fields(const dtuple_t *tuple)
Gets number of fields in a data tuple.
constexpr uint32_t DATA_POINT
geometry datatype of fixed length POINT
Definition: data0type.h:107
constexpr uint32_t DATA_NOT_NULL
this is ORed to the precise type when the column is declared as NOT NULL
Definition: data0type.h:207
#define DATA_BIG_COL(col)
Definition: data0type.h:286
Data dictionary creation and booting.
constexpr uint32_t DICT_INDEXES_ID
Definition: dict0boot.h:97
Data dictionary system.
uint16_t dict_index_get_n_fields(const dict_index_t *index)
Gets the number of fields in the internal representation of an index, including fields added by the d...
Definition: dict0dict.ic:667
static bool dict_table_is_comp(const dict_table_t *table)
Check whether the table uses the compact page format.
uint16_t dict_index_get_n_unique_in_tree_nonleaf(const dict_index_t *index)
Gets the number of fields on nonleaf page level in the internal representation of an index which uniq...
Definition: dict0dict.ic:724
uint16_t dict_index_get_n_unique_in_tree(const dict_index_t *index)
Gets the number of fields in the internal representation of an index which uniquely determine the pos...
Definition: dict0dict.ic:702
static bool is_valid_row_version(const uint8_t version)
Definition: dict0mem.h:427
constexpr char RECOVERY_INDEX_TABLE_NAME[]
index/table name used while applying REDO logs during recovery
Definition: dict0mem.h:89
static bool is_instant(const Alter_inplace_info *ha_alter_info)
Determine if this is an instant ALTER TABLE.
Definition: handler0alter.cc:926
static uint16_t mach_read_from_2(const byte *b)
The following function is used to fetch data from 2 consecutive bytes.
static uint8_t mach_read_from_1(const byte *b)
The following function is used to fetch data from one byte.
static mi_bit_type mask[]
Definition: mi_packrec.cc:141
#define DBUG_EXECUTE_IF(keyword, a1)
Definition: my_dbug.h:171
bool length(const dd::Spatial_reference_system *srs, const Geometry *g1, double *length, bool *null) noexcept
Computes the length of linestrings and multilinestrings.
Definition: length.cc:76
byte page_t
Type of the index page.
Definition: page0types.h:152
bool rec_info_bits_valid(ulint bits)
Check if the info bits are valid.
Definition: rec.h:320
constexpr uint32_t REC_INFO_INSTANT_FLAG
The instant ADD COLUMN flag.
Definition: rec.h:154
void row_import_assert_index_page_records_links_and_types_valid(const page_t *page, const dict_index_t *index)
bool row_import_rec_check_comp_metadata(const dict_index_t *index, const rec_t *rec)
Validate compact record metadata on imported page, not assuming page correctness.
Definition: row0import.cc:2700
static const uint8_t REC_N_FIELDS_TWO_BYTES_FLAG
Number of fields flag which means it occupies two bytes.
Definition: rec.h:238
static uint16_t rec_get_bit_field_2(const rec_t *rec, ulint offs, ulint mask, ulint shift)
Gets a bit field from within 2 bytes.
Definition: rec.h:289
constexpr uint32_t REC_N_OWNED_MASK
Definition: rec.h:119
static enum REC_INSERT_STATE rec_init_null_and_len_temp(const rec_t *rec, const dict_index_t *index, const byte **nulls, const byte **lens, uint16_t *n_null, uint16_t &non_default_fields, uint8_t &row_version)
Determines the information about null bytes and variable length bytes for a new-style temporary recor...
Definition: rec.h:979
constexpr uint32_t REC_OFFS_EXTERNAL
Definition: rec.h:77
constexpr uint32_t REC_OFFS_HEADER_SIZE
Definition: rec.h:203
static enum REC_INSERT_STATE init_nulls_lens_for_temp_redundant(const dict_index_t *index, const rec_t *rec, uint16_t *n_null, const byte **nulls, const byte **lens, uint16_t &non_default_fields, uint8_t &row_version)
Definition: rec.h:916
constexpr uint32_t REC_OFFS_DEFAULT
Definition: rec.h:79
constexpr uint32_t REC_NEW_HEAP_NO
Definition: rec.h:86
void rec_init_offsets_comp_ordinary(const rec_t *rec, bool temp, const dict_index_t *index, ulint *offsets)
Determine the offset to each field in a leaf-page record in ROW_FORMAT=COMPACT.
Definition: rec.h:1169
void rec_init_fixed_offsets(const dict_index_t *index, ulint *offsets)
A simplified variant rec_init_offsets(rec, index, offsets) for the case in which the rec contains onl...
Definition: rec.h:691
static enum REC_INSERT_STATE rec_init_null_and_len_comp(const rec_t *rec, const dict_index_t *index, const byte **nulls, const byte **lens, uint16_t *n_null, uint16_t &non_default_fields, uint8_t &row_version)
Determines the information about null bytes and variable length bytes for a new style record.
Definition: rec.h:1081
static bool rec_get_instant_flag_new_temp(const rec_t *rec)
The following function tells if a new-style temp record is instant record.
Definition: rec.h:675
constexpr uint32_t REC_NEXT
Definition: rec.h:94
constexpr uint32_t REC_N_OLD_EXTRA_BYTES
Definition: rec.h:158
constexpr uint32_t REC_STATUS_SUPREMUM
Definition: rec.h:184
constexpr uint32_t REC_NODE_PTR_SIZE
Definition: rec.h:190
constexpr uint32_t REC_INFO_BITS_SHIFT
Definition: rec.h:126
constexpr uint32_t REC_OFFS_DROP
Definition: rec.h:81
constexpr uint32_t REC_INFO_MIN_REC_FLAG
Definition: rec.h:146
static ulint rec_get_info_bits(const rec_t *rec, ulint comp)
The following function is used to retrieve the info bits of a record.
Definition: rec.h:330
void rec_init_offsets(const rec_t *rec, const dict_index_t *index, ulint *offsets)
The following function determines the offsets to each field in the record.
Definition: rec.cc:348
constexpr uint32_t REC_NEW_INFO_BITS
Definition: rec.h:123
constexpr uint32_t REC_OLD_INFO_BITS
Definition: rec.h:122
constexpr uint32_t REC_OFFS_MASK
Definition: rec.h:83
constexpr uint32_t REC_OLD_SHORT_MASK
Definition: rec.h:99
constexpr uint32_t REC_OLD_SHORT_SHIFT
Definition: rec.h:100
void row_import_assert_rec_metadata_valid(const rec_t *rec, const dict_index_t *index)
Rec_instant_state
Instant state of a record.
Definition: rec.h:216
constexpr uint32_t REC_INFO_BITS_MASK
Definition: rec.h:125
bool row_import_rec_check_old_metadata(const dict_index_t *index, const rec_t *rec)
Validate REDUNDANT record metadata on imported page, not assuming page correctness.
Definition: row0import.cc:2634
static ulint rec_get_info_bits_temp(const rec_t *rec)
The following function is used to retrieve the info bits of a temporary record.
Definition: rec.h:342
static uint64_t rec_get_instant_offset(const dict_index_t *index, ulint n, uint64_t offs)
Determine the offset of a specified field in the record, when this field is a field added after an in...
Definition: rec.h:530
static const uint8_t REC_N_FIELDS_ONE_BYTE_MAX
Max number of fields which can be stored in one byte.
Definition: rec.h:241
constexpr uint32_t REC_HEAP_NO_MASK
Definition: rec.h:111
REC_INSERT_STATE
Definition: rec.h:828
@ INSERTED_AFTER_UPGRADE_BEFORE_INSTANT_ADD_NEW_IMPLEMENTATION
Definition: rec.h:837
@ INSERTED_AFTER_INSTANT_ADD_OLD_IMPLEMENTATION
Definition: rec.h:834
@ INSERTED_AFTER_INSTANT_ADD_NEW_IMPLEMENTATION
Definition: rec.h:843
@ INSERTED_BEFORE_INSTANT_ADD_NEW_IMPLEMENTATION
Definition: rec.h:840
@ INSERTED_INTO_TABLE_WITH_NO_INSTANT_NO_VERSION
Definition: rec.h:845
@ NONE
Definition: rec.h:846
@ INSERTED_BEFORE_INSTANT_ADD_OLD_IMPLEMENTATION
Definition: rec.h:831
constexpr uint32_t REC_TMP_INFO_BITS
Definition: rec.h:124
constexpr uint32_t REC_HEAP_NO_SHIFT
Definition: rec.h:89
constexpr uint32_t REC_STATUS_ORDINARY
Definition: rec.h:181
static enum REC_INSERT_STATE get_rec_insert_state(const dict_index_t *index, const rec_t *rec, bool temp)
Definition: rec.h:849
static ulint rec_offs_get_n_alloc(const ulint *offsets)
The following function returns the number of allocated elements for an array of offsets.
Definition: rec.h:473
constexpr uint32_t REC_STATUS_INFIMUM
Definition: rec.h:183
constexpr uint32_t REC_NEW_STATUS
Definition: rec.h:106
static void rec_offs_make_valid(const rec_t *rec, const dict_index_t *index, ulint *offsets)
Updates debug data in offsets, in order to avoid bogus rec_offs_validate() failures.
Definition: rec.h:633
static bool rec_new_is_versioned(const rec_t *rec)
The following function tells if a new-style record is versioned.
Definition: rec.h:708
constexpr uint32_t REC_INFO_DELETED_FLAG
The deleted flag in info bits; when bit is set to 1, it means the record has been delete marked.
Definition: rec.h:149
static uint16_t rec_get_n_fields_old(const rec_t *rec, const dict_index_t *index)
The following function is used to get the number of fields in an old-style record.
Definition: rec.h:375
constexpr uint32_t REC_1BYTE_SQL_NULL_MASK
SQL null flag in a 1-byte offset of ROW_FORMAT=REDUNDANT records.
Definition: rec.h:193
constexpr uint32_t REC_OLD_N_FIELDS
Definition: rec.h:102
constexpr auto MAX_REC_PER_PAGE
Maximum number of records in a page.
Definition: rec.h:169
constexpr uint32_t REC_OFFS_COMPACT
NOTE: The functions in this file should only use functions from other files in library.
Definition: rec.h:73
static void rec_offs_set_n_alloc(ulint *offsets, ulint n_alloc)
The following function sets the number of allocated elements for an array of offsets.
Definition: rec.h:486
static bool rec_offs_validate(const rec_t *rec, const dict_index_t *index, const ulint *offsets, const bool check_status=true)
Validates offsets returned by rec_get_offsets().
Definition: rec.h:570
static bool rec_new_temp_is_versioned(const rec_t *rec)
The following function tells if a temporary record is versioned.
Definition: rec.h:724
bool row_import_rec_get_n_fields_instant_checked(const page_t *page, const byte *ptr, uint16_t *n_fields, uint16_t *length, bool force_page_boundary_check)
Decode the number of fields for one new-style leaf-page record without asserting on malformed metadat...
Definition: row0import.cc:2544
constexpr uint32_t REC_NEW_STATUS_MASK
Definition: rec.h:107
constexpr uint32_t REC_OFFS_SQL_NULL
Definition: rec.h:75
constexpr uint32_t REC_N_TMP_EXTRA_BYTES
Definition: rec.h:165
constexpr uint32_t REC_OLD_N_OWNED
Definition: rec.h:117
static ulint rec_offs_n_fields(const ulint *offsets)
The following function returns the number of fields in a record.
Definition: rec.h:512
static const ulint * rec_offs_base(const ulint *offsets)
Definition: rec.h:229
constexpr int32_t REC_N_NEW_EXTRA_BYTES
Definition: rec.h:161
static uint16_t rec_get_n_fields_old_raw(const rec_t *rec)
The following function is used to get the number of fields in an old-style record,...
Definition: rec.h:352
constexpr uint32_t REC_NEXT_MASK
Definition: rec.h:95
constexpr uint32_t REC_2BYTE_SQL_NULL_MASK
SQL null flag in a 2-byte offset of ROW_FORMAT=REDUNDANT records.
Definition: rec.h:195
ulint * rec_get_offsets(const rec_t *rec, const dict_index_t *index, ulint *offsets, ulint n_fields, ut::Location location, mem_heap_t **heap)
The following function determines the offsets to each field in the record.
Definition: rec.cc:359
constexpr uint32_t REC_INFO_VERSION_FLAG
Definition: rec.h:151
constexpr uint32_t REC_NEW_N_OWNED
Definition: rec.h:118
constexpr uint32_t REC_OLD_N_FIELDS_SHIFT
Definition: rec.h:104
constexpr uint32_t REC_OLD_HEAP_NO
Definition: rec.h:110
static bool rec_old_is_versioned(const rec_t *rec)
The following function tells if an old-style record is versioned.
Definition: rec.h:716
static bool rec_get_1byte_offs_flag(const rec_t *rec)
The following function is used to test whether the data offsets in the record are stored in one-byte ...
Definition: rec.h:1353
void rec_get_offsets_reverse(const byte *extra, const dict_index_t *index, ulint node_ptr, ulint *offsets)
The following function determines the offsets to each field in the record.
Definition: rec.cc:419
constexpr uint32_t REC_N_OWNED_SHIFT
Definition: rec.h:120
static uint32_t rec_get_n_fields_instant(const rec_t *rec, const ulint extra_bytes, uint16_t *length)
Get the number of fields for one new style leaf page record.
Definition: rec.h:770
constexpr uint32_t REC_STATUS_NODE_PTR
Definition: rec.h:182
static bool rec_n_fields_is_sane(dict_index_t *index, const rec_t *rec, const dtuple_t *entry)
Confirms the n_fields of the entry is sane with comparing the other record in the same page specified...
Definition: rec.h:461
constexpr uint32_t REC_OLD_SHORT
Definition: rec.h:98
constexpr uint32_t REC_NEXT_SHIFT
Definition: rec.h:96
static void rec_offs_set_n_fields(ulint *offsets, ulint n_fields)
The following function sets the number of fields in offsets.
Definition: rec.h:498
constexpr uint32_t REC_OFFS_NORMAL_SIZE
Definition: rec.h:211
static ulint rec_get_bit_field_1(const rec_t *rec, ulint offs, ulint mask, ulint shift)
Gets a bit field from within 1 byte.
Definition: rec.h:277
constexpr uint32_t REC_2BYTE_EXTERN_MASK
In a 2-byte offset of ROW_FORMAT=REDUNDANT records, the second most significant bit denotes that the ...
Definition: rec.h:199
constexpr uint32_t REC_NEW_STATUS_SHIFT
Definition: rec.h:108
bool rec_offs_cmp(ulint *offsets1, ulint *offsets2)
Check if the given two record offsets are identical.
Definition: rec.cc:538
static ulint rec_get_n_fields(const rec_t *rec, const dict_index_t *index)
The following function is used to get the number of fields in a record.
Definition: rec.h:431
std::ostream & rec_offs_print(std::ostream &out, const ulint *offsets)
Print the record offsets.
Definition: rec.cc:570
static ulint rec_get_status(const rec_t *rec)
The following function retrieves the status bits of a new-style record.
Definition: rec.h:302
static bool rec_get_instant_flag_new(const rec_t *rec)
The following function tells if a new-style record is instant record.
Definition: rec.h:667
constexpr uint32_t REC_OLD_N_FIELDS_MASK
Definition: rec.h:103
constexpr uint32_t REC_OFFS_SMALL_SIZE
Definition: rec.h:212
byte rec_t
Definition: rem0types.h:41
constexpr uint32_t REC_MAX_N_FIELDS
Definition: rem0types.h:44
required uint64 version
Definition: replication_group_member_actions.proto:41
Definition: completion_hash.h:35
std::atomic< uint16_t > n_pointers
Used in debugging.
Definition: buf0buf.h:1840
Data structure for a column in a table.
Definition: dict0mem.h:489
bool is_added_after(uint8_t version) const
Check if column is added after the current version.
Definition: dict0mem.h:753
unsigned mtype
main data type
Definition: dict0mem.h:506
ulint get_fixed_size(ulint comp) const
Returns the size of a fixed size column, 0 if not a fixed size column.
Definition: dict0mem.h:698
bool is_dropped_in_or_before(uint8_t version) const
Check if column is dropped before the given version.
Definition: dict0mem.h:740
unsigned prtype
precise type; MySQL data type, charset code, flags to indicate nullability, signedness,...
Definition: dict0mem.h:499
Data structure for a field in an index.
Definition: dict0mem.h:895
dict_col_t * col
pointer to the table column
Definition: dict0mem.h:898
unsigned fixed_len
0 or the fixed length of the column if smaller than DICT_ANTELOPE_MAX_INDEX_COL_LEN
Definition: dict0mem.h:908
Data structure for an index.
Definition: dict0mem.h:1046
uint32_t get_n_nullable_before(uint32_t nth) const
Returns the number of nullable fields before specified nth field.
Definition: dict0mem.h:1368
const char * table_name
table name
Definition: dict0mem.h:1057
id_name_t name
index name
Definition: dict0mem.h:1054
const byte * get_nth_default(ulint nth, ulint *length) const
Get the default value of nth field and its length if exists.
Definition: dict0mem.h:1575
bool has_instant_cols_or_row_versions() const
check if either instant or versioned.
Definition: dict0mem.h:1352
dict_field_t * get_physical_field(size_t pos) const
Gets the nth physical pos field.
Definition: dict0mem.h:1488
uint32_t get_instant_fields() const
Returns the number of fields before first instant ADD COLUMN.
Definition: dict0mem.ic:92
dict_field_t * get_field(ulint pos) const
Gets the nth field of an index.
Definition: dict0mem.h:1502
uint16_t get_nullable_before_instant_add_drop() const
Get the nullable fields before any INSTANT ADD/DROP.
Definition: dict0mem.h:1256
size_t calculate_n_instant_nullable(size_t _n_fields) const
Definition: dict0mem.h:1395
unsigned n_def
number of fields defined so far
Definition: dict0mem.h:1103
unsigned n_nullable
number of nullable fields
Definition: dict0mem.h:1112
rec_cache_t rec_cache
cache the field that needs to be re-computed on each insert.
Definition: dict0mem.h:1225
bool has_row_versions() const
Check whether index belongs to a table having row versions.
Definition: dict0mem.h:1348
bool is_clustered() const
Definition: dict0mem.h:1311
bool has_instant_cols() const
Check whether index has any instantly added columns.
Definition: dict0mem.h:1344
dict_table_t * table
back pointer to table
Definition: dict0mem.h:1060
uint32_t get_nullable_in_version(uint8_t version) const
Return nullable in a specific row version.
Definition: dict0mem.h:1436
bool has_instant_cols() const
Definition: dict0mem.h:2503
bool has_row_versions() const
Definition: dict0mem.h:2488
unsigned n_cols
Number of non-virtual columns.
Definition: dict0mem.h:2060
unsigned n_instant_cols
Number of non-virtual columns before first instant ADD COLUMN, including the system columns like n_co...
Definition: dict0mem.h:2065
table_id_t id
Id of the table.
Definition: dict0mem.h:1979
bool is_upgraded_instant() const
Checks if table is upgraded table with INSTANT ADD columns in V1.
Definition: dict0mem.h:2560
Structure for an SQL data tuple of fields (logical record)
Definition: data0data.h:694
The info structure stored at the beginning of a heap block.
Definition: mem0mem.h:302
Part of traditional "extra" column or related hierarchical property.
Definition: opt_explain_format.h:267
const ulint * offsets
Holds reference to cached offsets for record.
Definition: dict0mem.h:982
Definition: ut0core.h:36
#define UNIV_MEM_ASSERT_W(addr, size)
Definition: univ.i:618
#define UNIV_UNLIKELY(cond)
Definition: univ.i:521
constexpr uint8_t UINT8_UNDEFINED
The 'undefined' value for a 8-bit unsigned integer.
Definition: univ.i:434
#define UNIV_PAGE_SIZE
The universal page size of the database.
Definition: univ.i:294
constexpr uint32_t UNIV_SQL_NULL
The following number as the length of a logical field means that the field has the SQL NULL as its va...
Definition: univ.i:463
#define UNIV_MEM_ASSERT_AND_ALLOC(addr, size)
Definition: univ.i:630
constexpr ulint ULINT_MAX
Maximum value for a ulint.
Definition: univ.i:440
unsigned long int ulint
Definition: univ.i:406
constexpr size_t UNIV_PAGE_SIZE_MAX
Maximum page size InnoDB currently supports.
Definition: univ.i:323
static void * ut_align_down(const void *ptr, ulint align_no)
The following function rounds down a pointer to the nearest aligned address.
#define ut_error
Abort execution.
Definition: ut0dbg.h:101
#define ut_ad(EXPR)
Debug assertion.
Definition: ut0dbg.h:105
#define ut_d(EXPR)
Debug statement.
Definition: ut0dbg.h:107
#define ut_a(EXPR)
Abort execution if EXPR does not evaluate to nonzero.
Definition: ut0dbg.h:93
#define UT_BITS_IN_BYTES(b)
Determine how many bytes (groups of 8 bits) are needed to store the given number of bits.
Definition: ut0ut.h:198
int n
Definition: xcom_base.cc:509