1 /*****************************************************************************
2
3 Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved.
4 Copyright (c) 2017, 2021, MariaDB Corporation.
5
6 This program is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free Software
8 Foundation; version 2 of the License.
9
10 This program is distributed in the hope that it will be useful, but WITHOUT
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along with
15 this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
17
18 *****************************************************************************/
19
20 /********************************************************************//**
21 @file include/rem0rec.h
22 Record manager
23
24 Created 5/30/1994 Heikki Tuuri
25 *************************************************************************/
26
27 #ifndef rem0rec_h
28 #define rem0rec_h
29
30 #ifndef UNIV_INNOCHECKSUM
31 #include "data0data.h"
32 #include "rem0types.h"
33 #include "mtr0types.h"
34 #include "page0types.h"
35 #include "dict0dict.h"
36 #include "trx0types.h"
37 #endif /*! UNIV_INNOCHECKSUM */
38 #include <ostream>
39 #include <sstream>
40
41 /* Number of extra bytes in an old-style record,
42 in addition to the data and the offsets */
43 #define REC_N_OLD_EXTRA_BYTES 6
44 /* Number of extra bytes in a new-style record,
45 in addition to the data and the offsets */
46 #define REC_N_NEW_EXTRA_BYTES 5
47
48 #define REC_NEW_STATUS 3 /* This is single byte bit-field */
49 #define REC_NEW_STATUS_MASK 0x7UL
50 #define REC_NEW_STATUS_SHIFT 0
51
52 /* The following four constants are needed in page0zip.cc in order to
53 efficiently compress and decompress pages. */
54
55 /* The offset of heap_no in a compact record */
56 #define REC_NEW_HEAP_NO 4
57 /* The shift of heap_no in a compact record.
58 The status is stored in the low-order bits. */
59 #define REC_HEAP_NO_SHIFT 3
60
61 /* Length of a B-tree node pointer, in bytes */
62 #define REC_NODE_PTR_SIZE 4
63
64 #ifndef UNIV_INNOCHECKSUM
65 /** SQL null flag in a 1-byte offset of ROW_FORMAT=REDUNDANT records */
66 static const rec_offs REC_1BYTE_SQL_NULL_MASK= 0x80;
67 /** SQL null flag in a 2-byte offset of ROW_FORMAT=REDUNDANT records */
68 static const rec_offs REC_2BYTE_SQL_NULL_MASK= 0x8000;
69
70 /** In a 2-byte offset of ROW_FORMAT=REDUNDANT records, the second most
71 significant bit denotes that the tail of a field is stored off-page. */
72 static const rec_offs REC_2BYTE_EXTERN_MASK= 0x4000;
73
74 static const size_t RECORD_OFFSET= 2;
75 static const size_t INDEX_OFFSET=
76 RECORD_OFFSET + sizeof(rec_t *) / sizeof(rec_offs);
77 #endif /* UNIV_INNOCHECKSUM */
78
79 /* Length of the rec_get_offsets() header */
80 static const size_t REC_OFFS_HEADER_SIZE=
81 #ifdef UNIV_DEBUG
82 #ifndef UNIV_INNOCHECKSUM
83 sizeof(rec_t *) / sizeof(rec_offs) +
84 sizeof(dict_index_t *) / sizeof(rec_offs) +
85 #endif /* UNIV_INNOCHECKSUM */
86 #endif /* UNIV_DEBUG */
87 2;
88
89 /* Number of elements that should be initially allocated for the
90 offsets[] array, first passed to rec_get_offsets() */
91 static const size_t REC_OFFS_NORMAL_SIZE= 300;
92 static const size_t REC_OFFS_SMALL_SIZE= 18;
93 static const size_t REC_OFFS_SEC_INDEX_SIZE=
94 /* PK max key parts */ 16 + /* sec idx max key parts */ 16 +
95 /* child page number for non-leaf pages */ 1;
96
97 /** Get the base address of offsets. The extra_size is stored at
98 this position, and following positions hold the end offsets of
99 the fields. */
100 #define rec_offs_base(offsets) (offsets + REC_OFFS_HEADER_SIZE)
101
102 #ifndef UNIV_INNOCHECKSUM
103 /* Offset consists of two parts: 2 upper bits is type and all other bits is
104 value */
105
106 /** Only 4 different values is possible! */
107 enum field_type_t
108 {
109 /** normal field */
110 STORED_IN_RECORD= 0 << 14,
111 /** this field is stored off-page */
112 STORED_OFFPAGE= 1 << 14,
113 /** just an SQL NULL */
114 SQL_NULL= 2 << 14,
115 /** instantly added field */
116 DEFAULT= 3 << 14,
117 };
118
119 /** without 2 upper bits */
120 static const rec_offs DATA_MASK= 0x3fff;
121 /** 2 upper bits */
122 static const rec_offs TYPE_MASK= ~DATA_MASK;
get_type(rec_offs n)123 inline field_type_t get_type(rec_offs n)
124 {
125 return static_cast<field_type_t>(n & TYPE_MASK);
126 }
set_type(rec_offs & n,field_type_t type)127 inline void set_type(rec_offs &n, field_type_t type)
128 {
129 n= (n & DATA_MASK) | static_cast<rec_offs>(type);
130 }
get_value(rec_offs n)131 inline rec_offs get_value(rec_offs n) { return n & DATA_MASK; }
combine(rec_offs value,field_type_t type)132 inline rec_offs combine(rec_offs value, field_type_t type)
133 {
134 return get_value(value) | static_cast<rec_offs>(type);
135 }
136
137 /** Compact flag ORed to the extra size returned by rec_get_offsets() */
138 const rec_offs REC_OFFS_COMPACT= ~(rec_offs(~0) >> 1);
139 /** External flag in offsets returned by rec_get_offsets() */
140 const rec_offs REC_OFFS_EXTERNAL= REC_OFFS_COMPACT >> 1;
141 /** Default value flag in offsets returned by rec_get_offsets() */
142 const rec_offs REC_OFFS_DEFAULT= REC_OFFS_COMPACT >> 2;
143 const rec_offs REC_OFFS_MASK= REC_OFFS_DEFAULT - 1;
144 /******************************************************//**
145 The following function is used to get the pointer of the next chained record
146 on the same page.
147 @return pointer to the next chained record, or NULL if none */
148 UNIV_INLINE
149 const rec_t*
150 rec_get_next_ptr_const(
151 /*===================*/
152 const rec_t* rec, /*!< in: physical record */
153 ulint comp) /*!< in: nonzero=compact page format */
154 MY_ATTRIBUTE((warn_unused_result));
155 /******************************************************//**
156 The following function is used to get the pointer of the next chained record
157 on the same page.
158 @return pointer to the next chained record, or NULL if none */
159 UNIV_INLINE
160 rec_t*
161 rec_get_next_ptr(
162 /*=============*/
163 rec_t* rec, /*!< in: physical record */
164 ulint comp) /*!< in: nonzero=compact page format */
165 MY_ATTRIBUTE((warn_unused_result));
166 /******************************************************//**
167 The following function is used to get the offset of the
168 next chained record on the same page.
169 @return the page offset of the next chained record, or 0 if none */
170 UNIV_INLINE
171 ulint
172 rec_get_next_offs(
173 /*==============*/
174 const rec_t* rec, /*!< in: physical record */
175 ulint comp) /*!< in: nonzero=compact page format */
176 MY_ATTRIBUTE((warn_unused_result));
177 /******************************************************//**
178 The following function is used to set the next record offset field
179 of an old-style record. */
180 UNIV_INLINE
181 void
182 rec_set_next_offs_old(
183 /*==================*/
184 rec_t* rec, /*!< in: old-style physical record */
185 ulint next) /*!< in: offset of the next record */
186 MY_ATTRIBUTE((nonnull));
187 /******************************************************//**
188 The following function is used to set the next record offset field
189 of a new-style record. */
190 UNIV_INLINE
191 void
192 rec_set_next_offs_new(
193 /*==================*/
194 rec_t* rec, /*!< in/out: new-style physical record */
195 ulint next) /*!< in: offset of the next record */
196 MY_ATTRIBUTE((nonnull));
197 /******************************************************//**
198 The following function is used to get the number of fields
199 in an old-style record.
200 @return number of data fields */
201 UNIV_INLINE
202 ulint
203 rec_get_n_fields_old(
204 /*=================*/
205 const rec_t* rec) /*!< in: physical record */
206 MY_ATTRIBUTE((warn_unused_result));
207 /******************************************************//**
208 The following function is used to get the number of fields
209 in a record.
210 @return number of data fields */
211 UNIV_INLINE
212 ulint
213 rec_get_n_fields(
214 /*=============*/
215 const rec_t* rec, /*!< in: physical record */
216 const dict_index_t* index) /*!< in: record descriptor */
217 MY_ATTRIBUTE((warn_unused_result));
218
219 /** Confirms the n_fields of the entry is sane with comparing the other
220 record in the same page specified
221 @param[in] index index
222 @param[in] rec record of the same page
223 @param[in] entry index entry
224 @return true if n_fields is sane */
225 UNIV_INLINE
226 bool
227 rec_n_fields_is_sane(
228 dict_index_t* index,
229 const rec_t* rec,
230 const dtuple_t* entry)
231 MY_ATTRIBUTE((warn_unused_result));
232
233 /******************************************************//**
234 The following function is used to get the number of records owned by the
235 previous directory record.
236 @return number of owned records */
237 UNIV_INLINE
238 ulint
239 rec_get_n_owned_old(
240 /*================*/
241 const rec_t* rec) /*!< in: old-style physical record */
242 MY_ATTRIBUTE((warn_unused_result));
243 /******************************************************//**
244 The following function is used to set the number of owned records. */
245 UNIV_INLINE
246 void
247 rec_set_n_owned_old(
248 /*================*/
249 rec_t* rec, /*!< in: old-style physical record */
250 ulint n_owned) /*!< in: the number of owned */
251 MY_ATTRIBUTE((nonnull));
252 /******************************************************//**
253 The following function is used to get the number of records owned by the
254 previous directory record.
255 @return number of owned records */
256 UNIV_INLINE
257 ulint
258 rec_get_n_owned_new(
259 /*================*/
260 const rec_t* rec) /*!< in: new-style physical record */
261 MY_ATTRIBUTE((warn_unused_result));
262 /******************************************************//**
263 The following function is used to set the number of owned records. */
264 UNIV_INLINE
265 void
266 rec_set_n_owned_new(
267 /*================*/
268 rec_t* rec, /*!< in/out: new-style physical record */
269 page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */
270 ulint n_owned)/*!< in: the number of owned */
271 MY_ATTRIBUTE((nonnull(1)));
272 /******************************************************//**
273 The following function is used to retrieve the info bits of
274 a record.
275 @return info bits */
276 UNIV_INLINE
277 ulint
278 rec_get_info_bits(
279 /*==============*/
280 const rec_t* rec, /*!< in: physical record */
281 ulint comp) /*!< in: nonzero=compact page format */
282 MY_ATTRIBUTE((warn_unused_result));
283 /******************************************************//**
284 The following function is used to set the info bits of a record. */
285 UNIV_INLINE
286 void
287 rec_set_info_bits_old(
288 /*==================*/
289 rec_t* rec, /*!< in: old-style physical record */
290 ulint bits) /*!< in: info bits */
291 MY_ATTRIBUTE((nonnull));
292 /******************************************************//**
293 The following function is used to set the info bits of a record. */
294 UNIV_INLINE
295 void
296 rec_set_info_bits_new(
297 /*==================*/
298 rec_t* rec, /*!< in/out: new-style physical record */
299 ulint bits) /*!< in: info bits */
300 MY_ATTRIBUTE((nonnull));
301
302 /** Determine the status bits of a non-REDUNDANT record.
303 @param[in] rec ROW_FORMAT=COMPACT,DYNAMIC,COMPRESSED record
304 @return status bits */
305 inline
306 rec_comp_status_t
rec_get_status(const rec_t * rec)307 rec_get_status(const rec_t* rec)
308 {
309 byte bits = rec[-REC_NEW_STATUS] & REC_NEW_STATUS_MASK;
310 ut_ad(bits <= REC_STATUS_INSTANT);
311 return static_cast<rec_comp_status_t>(bits);
312 }
313
314 /** Set the status bits of a non-REDUNDANT record.
315 @param[in,out] rec ROW_FORMAT=COMPACT,DYNAMIC,COMPRESSED record
316 @param[in] bits status bits */
317 inline
318 void
rec_set_status(rec_t * rec,byte bits)319 rec_set_status(rec_t* rec, byte bits)
320 {
321 ut_ad(bits <= REC_STATUS_INSTANT);
322 rec[-REC_NEW_STATUS] = (rec[-REC_NEW_STATUS] & ~REC_NEW_STATUS_MASK)
323 | bits;
324 }
325
326 /** Get the length of added field count in a REC_STATUS_INSTANT record.
327 @param[in] n_add_field number of added fields, minus one
328 @return storage size of the field count, in bytes */
rec_get_n_add_field_len(ulint n_add_field)329 inline unsigned rec_get_n_add_field_len(ulint n_add_field)
330 {
331 ut_ad(n_add_field < REC_MAX_N_FIELDS);
332 return n_add_field < 0x80 ? 1 : 2;
333 }
334
335 /** Get the added field count in a REC_STATUS_INSTANT record.
336 @param[in,out] header variable header of a REC_STATUS_INSTANT record
337 @return number of added fields */
rec_get_n_add_field(const byte * & header)338 inline unsigned rec_get_n_add_field(const byte*& header)
339 {
340 unsigned n_fields_add = *--header;
341 if (n_fields_add < 0x80) {
342 ut_ad(rec_get_n_add_field_len(n_fields_add) == 1);
343 return n_fields_add;
344 }
345
346 n_fields_add &= 0x7f;
347 n_fields_add |= unsigned(*--header) << 7;
348 ut_ad(n_fields_add < REC_MAX_N_FIELDS);
349 ut_ad(rec_get_n_add_field_len(n_fields_add) == 2);
350 return n_fields_add;
351 }
352
353 /** Set the added field count in a REC_STATUS_INSTANT record.
354 @param[in,out] header variable header of a REC_STATUS_INSTANT record
355 @param[in] n_add number of added fields, minus 1
356 @return record header before the number of added fields */
rec_set_n_add_field(byte * & header,ulint n_add)357 inline void rec_set_n_add_field(byte*& header, ulint n_add)
358 {
359 ut_ad(n_add < REC_MAX_N_FIELDS);
360
361 if (n_add < 0x80) {
362 *header-- = byte(n_add);
363 } else {
364 *header-- = byte(n_add) | 0x80;
365 *header-- = byte(n_add >> 7);
366 }
367 }
368
369 /******************************************************//**
370 The following function is used to retrieve the info and status
371 bits of a record. (Only compact records have status bits.)
372 @return info bits */
373 UNIV_INLINE
374 ulint
375 rec_get_info_and_status_bits(
376 /*=========================*/
377 const rec_t* rec, /*!< in: physical record */
378 ulint comp) /*!< in: nonzero=compact page format */
379 MY_ATTRIBUTE((warn_unused_result));
380 /******************************************************//**
381 The following function is used to set the info and status
382 bits of a record. (Only compact records have status bits.) */
383 UNIV_INLINE
384 void
385 rec_set_info_and_status_bits(
386 /*=========================*/
387 rec_t* rec, /*!< in/out: compact physical record */
388 ulint bits) /*!< in: info bits */
389 MY_ATTRIBUTE((nonnull));
390
391 /******************************************************//**
392 The following function tells if record is delete marked.
393 @return nonzero if delete marked */
394 UNIV_INLINE
395 ulint
396 rec_get_deleted_flag(
397 /*=================*/
398 const rec_t* rec, /*!< in: physical record */
399 ulint comp) /*!< in: nonzero=compact page format */
400 MY_ATTRIBUTE((warn_unused_result));
401 /******************************************************//**
402 The following function is used to set the deleted bit. */
403 UNIV_INLINE
404 void
405 rec_set_deleted_flag_old(
406 /*=====================*/
407 rec_t* rec, /*!< in: old-style physical record */
408 ulint flag) /*!< in: nonzero if delete marked */
409 MY_ATTRIBUTE((nonnull));
410 /******************************************************//**
411 The following function is used to set the deleted bit. */
412 UNIV_INLINE
413 void
414 rec_set_deleted_flag_new(
415 /*=====================*/
416 rec_t* rec, /*!< in/out: new-style physical record */
417 page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */
418 ulint flag) /*!< in: nonzero if delete marked */
419 MY_ATTRIBUTE((nonnull(1)));
420 /******************************************************//**
421 The following function tells if a new-style record is a node pointer.
422 @return TRUE if node pointer */
423 UNIV_INLINE
424 bool
425 rec_get_node_ptr_flag(
426 /*==================*/
427 const rec_t* rec) /*!< in: physical record */
428 MY_ATTRIBUTE((warn_unused_result));
429 /******************************************************//**
430 The following function is used to get the order number
431 of an old-style record in the heap of the index page.
432 @return heap order number */
433 UNIV_INLINE
434 ulint
435 rec_get_heap_no_old(
436 /*================*/
437 const rec_t* rec) /*!< in: physical record */
438 MY_ATTRIBUTE((warn_unused_result));
439 /******************************************************//**
440 The following function is used to set the heap number
441 field in an old-style record. */
442 UNIV_INLINE
443 void
444 rec_set_heap_no_old(
445 /*================*/
446 rec_t* rec, /*!< in: physical record */
447 ulint heap_no)/*!< in: the heap number */
448 MY_ATTRIBUTE((nonnull));
449 /******************************************************//**
450 The following function is used to get the order number
451 of a new-style record in the heap of the index page.
452 @return heap order number */
453 UNIV_INLINE
454 ulint
455 rec_get_heap_no_new(
456 /*================*/
457 const rec_t* rec) /*!< in: physical record */
458 MY_ATTRIBUTE((warn_unused_result));
459 /******************************************************//**
460 The following function is used to set the heap number
461 field in a new-style record. */
462 UNIV_INLINE
463 void
464 rec_set_heap_no_new(
465 /*================*/
466 rec_t* rec, /*!< in/out: physical record */
467 ulint heap_no)/*!< in: the heap number */
468 MY_ATTRIBUTE((nonnull));
469 /******************************************************//**
470 The following function is used to test whether the data offsets
471 in the record are stored in one-byte or two-byte format.
472 @return TRUE if 1-byte form */
473 UNIV_INLINE
474 ibool
475 rec_get_1byte_offs_flag(
476 /*====================*/
477 const rec_t* rec) /*!< in: physical record */
478 MY_ATTRIBUTE((warn_unused_result));
479
480 /******************************************************//**
481 The following function is used to set the 1-byte offsets flag. */
482 UNIV_INLINE
483 void
484 rec_set_1byte_offs_flag(
485 /*====================*/
486 rec_t* rec, /*!< in: physical record */
487 ibool flag) /*!< in: TRUE if 1byte form */
488 MY_ATTRIBUTE((nonnull));
489
490 /******************************************************//**
491 Returns the offset of nth field end if the record is stored in the 1-byte
492 offsets form. If the field is SQL null, the flag is ORed in the returned
493 value.
494 @return offset of the start of the field, SQL null flag ORed */
495 UNIV_INLINE
496 uint8_t
497 rec_1_get_field_end_info(
498 /*=====================*/
499 const rec_t* rec, /*!< in: record */
500 ulint n) /*!< in: field index */
501 MY_ATTRIBUTE((warn_unused_result));
502
503 /******************************************************//**
504 Returns the offset of nth field end if the record is stored in the 2-byte
505 offsets form. If the field is SQL null, the flag is ORed in the returned
506 value.
507 @return offset of the start of the field, SQL null flag and extern
508 storage flag ORed */
509 UNIV_INLINE
510 uint16_t
511 rec_2_get_field_end_info(
512 /*=====================*/
513 const rec_t* rec, /*!< in: record */
514 ulint n) /*!< in: field index */
515 MY_ATTRIBUTE((warn_unused_result));
516
517 /******************************************************//**
518 Returns nonzero if the field is stored off-page.
519 @retval 0 if the field is stored in-page
520 @retval REC_2BYTE_EXTERN_MASK if the field is stored externally */
521 UNIV_INLINE
522 ulint
523 rec_2_is_field_extern(
524 /*==================*/
525 const rec_t* rec, /*!< in: record */
526 ulint n) /*!< in: field index */
527 MY_ATTRIBUTE((warn_unused_result));
528
529 /******************************************************//**
530 Determine how many of the first n columns in a compact
531 physical record are stored externally.
532 @return number of externally stored columns */
533 ulint
534 rec_get_n_extern_new(
535 /*=================*/
536 const rec_t* rec, /*!< in: compact physical record */
537 const dict_index_t* index, /*!< in: record descriptor */
538 ulint n) /*!< in: number of columns to scan */
539 MY_ATTRIBUTE((nonnull, warn_unused_result));
540
541 /** Determine the offsets to each field in an index record.
542 @param[in] rec physical record
543 @param[in] index the index that the record belongs to
544 @param[in,out] offsets array comprising offsets[0] allocated elements,
545 or an array from rec_get_offsets(), or NULL
546 @param[in] n_core 0, or index->n_core_fields for leaf page
547 @param[in] n_fields maximum number of offsets to compute
548 (ULINT_UNDEFINED to compute all offsets)
549 @param[in,out] heap memory heap
550 @return the new offsets */
551 rec_offs*
552 rec_get_offsets_func(
553 const rec_t* rec,
554 const dict_index_t* index,
555 rec_offs* offsets,
556 ulint n_core,
557 ulint n_fields,
558 #ifdef UNIV_DEBUG
559 const char* file, /*!< in: file name where called */
560 unsigned line, /*!< in: line number where called */
561 #endif /* UNIV_DEBUG */
562 mem_heap_t** heap) /*!< in/out: memory heap */
563 #ifdef UNIV_DEBUG
564 MY_ATTRIBUTE((nonnull(1,2,6,8),warn_unused_result));
565 #else /* UNIV_DEBUG */
566 MY_ATTRIBUTE((nonnull(1,2,6),warn_unused_result));
567 #endif /* UNIV_DEBUG */
568
569 #ifdef UNIV_DEBUG
570 # define rec_get_offsets(rec, index, offsets, leaf, n, heap) \
571 rec_get_offsets_func(rec,index,offsets,leaf,n,__FILE__,__LINE__,heap)
572 #else /* UNIV_DEBUG */
573 # define rec_get_offsets(rec, index, offsets, leaf, n, heap) \
574 rec_get_offsets_func(rec, index, offsets, leaf, n, heap)
575 #endif /* UNIV_DEBUG */
576
577 /******************************************************//**
578 The following function determines the offsets to each field
579 in the record. It can reuse a previously allocated array. */
580 void
581 rec_get_offsets_reverse(
582 /*====================*/
583 const byte* extra, /*!< in: the extra bytes of a
584 compact record in reverse order,
585 excluding the fixed-size
586 REC_N_NEW_EXTRA_BYTES */
587 const dict_index_t* index, /*!< in: record descriptor */
588 ulint node_ptr,/*!< in: nonzero=node pointer,
589 0=leaf node */
590 rec_offs* offsets)/*!< in/out: array consisting of
591 offsets[0] allocated elements */
592 MY_ATTRIBUTE((nonnull));
593 #ifdef UNIV_DEBUG
594 /** Validate offsets returned by rec_get_offsets().
595 @param[in] rec record, or NULL
596 @param[in] index the index that the record belongs in, or NULL
597 @param[in,out] offsets the offsets of the record
598 @return true */
599 bool
600 rec_offs_validate(
601 const rec_t* rec,
602 const dict_index_t* index,
603 const rec_offs* offsets)
604 MY_ATTRIBUTE((nonnull(3), warn_unused_result));
605 /** Update debug data in offsets, in order to tame rec_offs_validate().
606 @param[in] rec record
607 @param[in] index the index that the record belongs in
608 @param[in] leaf whether the record resides in a leaf page
609 @param[in,out] offsets offsets from rec_get_offsets() to adjust */
610 void
611 rec_offs_make_valid(
612 const rec_t* rec,
613 const dict_index_t* index,
614 bool leaf,
615 rec_offs* offsets)
616 MY_ATTRIBUTE((nonnull));
617 #else
618 # define rec_offs_make_valid(rec, index, leaf, offsets)
619 #endif /* UNIV_DEBUG */
620
621 /************************************************************//**
622 The following function is used to get the offset to the nth
623 data field in an old-style record.
624 @return offset to the field */
625 ulint
626 rec_get_nth_field_offs_old(
627 /*=======================*/
628 const rec_t* rec, /*!< in: record */
629 ulint n, /*!< in: index of the field */
630 ulint* len) /*!< out: length of the field; UNIV_SQL_NULL
631 if SQL null */
632 MY_ATTRIBUTE((nonnull));
633 #define rec_get_nth_field_old(rec, n, len) \
634 ((rec) + rec_get_nth_field_offs_old(rec, n, len))
635 /************************************************************//**
636 Gets the physical size of an old-style field.
637 Also an SQL null may have a field of size > 0,
638 if the data type is of a fixed size.
639 @return field size in bytes */
640 UNIV_INLINE
641 ulint
642 rec_get_nth_field_size(
643 /*===================*/
644 const rec_t* rec, /*!< in: record */
645 ulint n) /*!< in: index of the field */
646 MY_ATTRIBUTE((warn_unused_result));
647 /************************************************************//**
648 The following function is used to get an offset to the nth
649 data field in a record.
650 @return offset from the origin of rec */
651 UNIV_INLINE
652 rec_offs
653 rec_get_nth_field_offs(
654 /*===================*/
655 const rec_offs* offsets,/*!< in: array returned by rec_get_offsets() */
656 ulint n, /*!< in: index of the field */
657 ulint* len) /*!< out: length of the field; UNIV_SQL_NULL
658 if SQL null */
659 MY_ATTRIBUTE((nonnull));
660 #define rec_get_nth_field(rec, offsets, n, len) \
661 ((rec) + rec_get_nth_field_offs(offsets, n, len))
662 /******************************************************//**
663 Determine if the offsets are for a record containing null BLOB pointers.
664 @return first field containing a null BLOB pointer, or NULL if none found */
665 UNIV_INLINE
666 const byte*
667 rec_offs_any_null_extern(
668 /*=====================*/
669 const rec_t* rec, /*!< in: record */
670 const rec_offs* offsets) /*!< in: rec_get_offsets(rec) */
671 MY_ATTRIBUTE((warn_unused_result));
672
673 /** Mark the nth field as externally stored.
674 @param[in] offsets array returned by rec_get_offsets()
675 @param[in] n nth field */
676 void
677 rec_offs_make_nth_extern(
678 rec_offs* offsets,
679 const ulint n);
680
681 MY_ATTRIBUTE((nonnull))
682 /** Determine the number of allocated elements for an array of offsets.
683 @param[in] offsets offsets after rec_offs_set_n_alloc()
684 @return number of elements */
rec_offs_get_n_alloc(const rec_offs * offsets)685 inline ulint rec_offs_get_n_alloc(const rec_offs *offsets)
686 {
687 ut_ad(offsets);
688 ulint n_alloc= offsets[0];
689 ut_ad(n_alloc > REC_OFFS_HEADER_SIZE);
690 MEM_CHECK_ADDRESSABLE(offsets, n_alloc * sizeof *offsets);
691 return n_alloc;
692 }
693
694 /** Determine the number of fields for which offsets have been initialized.
695 @param[in] offsets rec_get_offsets()
696 @return number of fields */
697 inline
698 ulint
rec_offs_n_fields(const rec_offs * offsets)699 rec_offs_n_fields(const rec_offs* offsets)
700 {
701 ulint n_fields;
702 ut_ad(offsets);
703 n_fields = offsets[1];
704 ut_ad(n_fields > 0);
705 ut_ad(n_fields <= REC_MAX_N_FIELDS);
706 ut_ad(n_fields + REC_OFFS_HEADER_SIZE
707 <= rec_offs_get_n_alloc(offsets));
708 return(n_fields);
709 }
710
711 /** Get a flag of a record field.
712 @param[in] offsets rec_get_offsets()
713 @param[in] n nth field
714 @param[in] flag flag to extract
715 @return type of the record field */
rec_offs_nth_type(const rec_offs * offsets,ulint n)716 inline field_type_t rec_offs_nth_type(const rec_offs *offsets, ulint n)
717 {
718 ut_ad(rec_offs_validate(NULL, NULL, offsets));
719 ut_ad(n < rec_offs_n_fields(offsets));
720 return get_type(rec_offs_base(offsets)[1 + n]);
721 }
722
723 /** Determine if a record field is missing
724 (should be replaced by dict_index_t::instant_field_value()).
725 @param[in] offsets rec_get_offsets()
726 @param[in] n nth field
727 @return nonzero if default bit is set */
rec_offs_nth_default(const rec_offs * offsets,ulint n)728 inline ulint rec_offs_nth_default(const rec_offs *offsets, ulint n)
729 {
730 return rec_offs_nth_type(offsets, n) == DEFAULT;
731 }
732
733 /** Determine if a record field is SQL NULL
734 (should be replaced by dict_index_t::instant_field_value()).
735 @param[in] offsets rec_get_offsets()
736 @param[in] n nth field
737 @return nonzero if SQL NULL set */
rec_offs_nth_sql_null(const rec_offs * offsets,ulint n)738 inline ulint rec_offs_nth_sql_null(const rec_offs *offsets, ulint n)
739 {
740 return rec_offs_nth_type(offsets, n) == SQL_NULL;
741 }
742
743 /** Determine if a record field is stored off-page.
744 @param[in] offsets rec_get_offsets()
745 @param[in] n nth field
746 Returns nonzero if the extern bit is set in nth field of rec.
747 @return nonzero if externally stored */
rec_offs_nth_extern(const rec_offs * offsets,ulint n)748 inline ulint rec_offs_nth_extern(const rec_offs *offsets, ulint n)
749 {
750 return rec_offs_nth_type(offsets, n) == STORED_OFFPAGE;
751 }
752
753 /** Get a global flag of a record.
754 @param[in] offsets rec_get_offsets()
755 @param[in] flag flag to extract
756 @return the flag of the record field */
rec_offs_any_flag(const rec_offs * offsets,ulint flag)757 inline ulint rec_offs_any_flag(const rec_offs *offsets, ulint flag)
758 {
759 ut_ad(rec_offs_validate(NULL, NULL, offsets));
760 return *rec_offs_base(offsets) & flag;
761 }
762
763 /** Determine if the offsets are for a record containing off-page columns.
764 @param[in] offsets rec_get_offsets()
765 @return nonzero if any off-page columns exist */
rec_offs_any_extern(const rec_offs * offsets)766 inline bool rec_offs_any_extern(const rec_offs *offsets)
767 {
768 return rec_offs_any_flag(offsets, REC_OFFS_EXTERNAL);
769 }
770
771 /** Determine if the offsets are for a record that is missing fields.
772 @param[in] offsets rec_get_offsets()
773 @return nonzero if any fields need to be replaced with
774 dict_index_t::instant_field_value() */
rec_offs_any_default(const rec_offs * offsets)775 inline ulint rec_offs_any_default(const rec_offs *offsets)
776 {
777 return rec_offs_any_flag(offsets, REC_OFFS_DEFAULT);
778 }
779
780 /** Determine if the offsets are for other than ROW_FORMAT=REDUNDANT.
781 @param[in] offsets rec_get_offsets()
782 @return nonzero if ROW_FORMAT is COMPACT,DYNAMIC or COMPRESSED
783 @retval 0 if ROW_FORMAT=REDUNDANT */
rec_offs_comp(const rec_offs * offsets)784 inline ulint rec_offs_comp(const rec_offs *offsets)
785 {
786 ut_ad(rec_offs_validate(NULL, NULL, offsets));
787 return (*rec_offs_base(offsets) & REC_OFFS_COMPACT);
788 }
789
790 /** Determine if the record is the metadata pseudo-record
791 in the clustered index for instant ADD COLUMN or ALTER TABLE.
792 @param[in] rec leaf page record
793 @param[in] comp 0 if ROW_FORMAT=REDUNDANT, else nonzero
794 @return whether the record is the metadata pseudo-record */
rec_is_metadata(const rec_t * rec,ulint comp)795 inline bool rec_is_metadata(const rec_t* rec, ulint comp)
796 {
797 bool is = !!(rec_get_info_bits(rec, comp) & REC_INFO_MIN_REC_FLAG);
798 ut_ad(!is || !comp || rec_get_status(rec) == REC_STATUS_INSTANT);
799 return is;
800 }
801
802 /** Determine if the record is the metadata pseudo-record
803 in the clustered index for instant ADD COLUMN or ALTER TABLE.
804 @param[in] rec leaf page record
805 @param[in] index index of the record
806 @return whether the record is the metadata pseudo-record */
rec_is_metadata(const rec_t * rec,const dict_index_t & index)807 inline bool rec_is_metadata(const rec_t* rec, const dict_index_t& index)
808 {
809 bool is = rec_is_metadata(rec, dict_table_is_comp(index.table));
810 ut_ad(!is || index.is_instant());
811 return is;
812 }
813
814 /** Determine if the record is the metadata pseudo-record
815 in the clustered index for instant ADD COLUMN (not other ALTER TABLE).
816 @param[in] rec leaf page record
817 @param[in] comp 0 if ROW_FORMAT=REDUNDANT, else nonzero
818 @return whether the record is the metadata pseudo-record */
rec_is_add_metadata(const rec_t * rec,ulint comp)819 inline bool rec_is_add_metadata(const rec_t* rec, ulint comp)
820 {
821 bool is = rec_get_info_bits(rec, comp) == REC_INFO_MIN_REC_FLAG;
822 ut_ad(!is || !comp || rec_get_status(rec) == REC_STATUS_INSTANT);
823 return is;
824 }
825
826 /** Determine if the record is the metadata pseudo-record
827 in the clustered index for instant ADD COLUMN (not other ALTER TABLE).
828 @param[in] rec leaf page record
829 @param[in] index index of the record
830 @return whether the record is the metadata pseudo-record */
rec_is_add_metadata(const rec_t * rec,const dict_index_t & index)831 inline bool rec_is_add_metadata(const rec_t* rec, const dict_index_t& index)
832 {
833 bool is = rec_is_add_metadata(rec, dict_table_is_comp(index.table));
834 ut_ad(!is || index.is_instant());
835 return is;
836 }
837
838 /** Determine if the record is the metadata pseudo-record
839 in the clustered index for instant ALTER TABLE (not plain ADD COLUMN).
840 @param[in] rec leaf page record
841 @param[in] comp 0 if ROW_FORMAT=REDUNDANT, else nonzero
842 @return whether the record is the ALTER TABLE metadata pseudo-record */
rec_is_alter_metadata(const rec_t * rec,ulint comp)843 inline bool rec_is_alter_metadata(const rec_t* rec, ulint comp)
844 {
845 bool is = !(~rec_get_info_bits(rec, comp)
846 & (REC_INFO_MIN_REC_FLAG | REC_INFO_DELETED_FLAG));
847 ut_ad(!is || rec_is_metadata(rec, comp));
848 return is;
849 }
850
851 /** Determine if the record is the metadata pseudo-record
852 in the clustered index for instant ALTER TABLE (not plain ADD COLUMN).
853 @param[in] rec leaf page record
854 @param[in] index index of the record
855 @return whether the record is the ALTER TABLE metadata pseudo-record */
rec_is_alter_metadata(const rec_t * rec,const dict_index_t & index)856 inline bool rec_is_alter_metadata(const rec_t* rec, const dict_index_t& index)
857 {
858 bool is = rec_is_alter_metadata(rec, dict_table_is_comp(index.table));
859 ut_ad(!is || index.is_dummy || index.is_instant());
860 return is;
861 }
862
863 /** Determine if a record is delete-marked (not a metadata pseudo-record).
864 @param[in] rec record
865 @param[in] comp nonzero if ROW_FORMAT!=REDUNDANT
866 @return whether the record is a delete-marked user record */
rec_is_delete_marked(const rec_t * rec,ulint comp)867 inline bool rec_is_delete_marked(const rec_t* rec, ulint comp)
868 {
869 return (rec_get_info_bits(rec, comp)
870 & (REC_INFO_MIN_REC_FLAG | REC_INFO_DELETED_FLAG))
871 == REC_INFO_DELETED_FLAG;
872 }
873
874 /** Get the nth field from an index.
875 @param[in] rec index record
876 @param[in] index index
877 @param[in] offsets rec_get_offsets(rec, index)
878 @param[in] n field number
879 @param[out] len length of the field in bytes, or UNIV_SQL_NULL
880 @return a read-only copy of the index field */
881 inline
882 const byte*
rec_get_nth_cfield(const rec_t * rec,const dict_index_t * index,const rec_offs * offsets,ulint n,ulint * len)883 rec_get_nth_cfield(
884 const rec_t* rec,
885 const dict_index_t* index,
886 const rec_offs* offsets,
887 ulint n,
888 ulint* len)
889 {
890 /* Because this function may be invoked by innobase_rec_to_mysql()
891 for reporting a duplicate key during ALTER TABLE or
892 CREATE UNIQUE INDEX, and in that case the rec omit the fixed-size
893 header of 5 or 6 bytes, the check
894 rec_offs_validate(rec, index, offsets) must be avoided here. */
895 if (!rec_offs_nth_default(offsets, n)) {
896 return rec_get_nth_field(rec, offsets, n, len);
897 }
898 return index->instant_field_value(n, len);
899 }
900
901 /******************************************************//**
902 Gets the physical size of a field.
903 @return length of field */
904 UNIV_INLINE
905 ulint
906 rec_offs_nth_size(
907 /*==============*/
908 const rec_offs* offsets,/*!< in: array returned by rec_get_offsets() */
909 ulint n) /*!< in: nth field */
910 MY_ATTRIBUTE((warn_unused_result));
911
912 /******************************************************//**
913 Returns the number of extern bits set in a record.
914 @return number of externally stored fields */
915 UNIV_INLINE
916 ulint
917 rec_offs_n_extern(
918 /*==============*/
919 const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */
920 MY_ATTRIBUTE((warn_unused_result));
921 /***********************************************************//**
922 This is used to modify the value of an already existing field in a record.
923 The previous value must have exactly the same size as the new value. If len
924 is UNIV_SQL_NULL then the field is treated as an SQL null.
925 For records in ROW_FORMAT=COMPACT (new-style records), len must not be
926 UNIV_SQL_NULL unless the field already is SQL null. */
927 UNIV_INLINE
928 void
929 rec_set_nth_field(
930 /*==============*/
931 rec_t* rec, /*!< in: record */
932 const rec_offs* offsets,/*!< in: array returned by rec_get_offsets() */
933 ulint n, /*!< in: index number of the field */
934 const void* data, /*!< in: pointer to the data if not SQL null */
935 ulint len) /*!< in: length of the data or UNIV_SQL_NULL.
936 If not SQL null, must have the same
937 length as the previous value.
938 If SQL null, previous value must be
939 SQL null. */
940 MY_ATTRIBUTE((nonnull(1,2)));
941 /**********************************************************//**
942 The following function returns the data size of an old-style physical
943 record, that is the sum of field lengths. SQL null fields
944 are counted as length 0 fields. The value returned by the function
945 is the distance from record origin to record end in bytes.
946 @return size */
947 UNIV_INLINE
948 ulint
949 rec_get_data_size_old(
950 /*==================*/
951 const rec_t* rec) /*!< in: physical record */
952 MY_ATTRIBUTE((warn_unused_result));
953 /**********************************************************//**
954 The following function sets the number of allocated elements
955 for an array of offsets. */
956 UNIV_INLINE
957 void
958 rec_offs_set_n_alloc(
959 /*=================*/
960 rec_offs*offsets, /*!< out: array for rec_get_offsets(),
961 must be allocated */
962 ulint n_alloc) /*!< in: number of elements */
963 MY_ATTRIBUTE((nonnull));
964 #define rec_offs_init(offsets) \
965 rec_offs_set_n_alloc(offsets, (sizeof offsets) / sizeof *offsets)
966 /**********************************************************//**
967 The following function returns the data size of a physical
968 record, that is the sum of field lengths. SQL null fields
969 are counted as length 0 fields. The value returned by the function
970 is the distance from record origin to record end in bytes.
971 @return size */
972 UNIV_INLINE
973 ulint
974 rec_offs_data_size(
975 /*===============*/
976 const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */
977 MY_ATTRIBUTE((warn_unused_result));
978 /**********************************************************//**
979 Returns the total size of record minus data size of record.
980 The value returned by the function is the distance from record
981 start to record origin in bytes.
982 @return size */
983 UNIV_INLINE
984 ulint
985 rec_offs_extra_size(
986 /*================*/
987 const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */
988 MY_ATTRIBUTE((warn_unused_result));
989 /**********************************************************//**
990 Returns the total size of a physical record.
991 @return size */
992 UNIV_INLINE
993 ulint
994 rec_offs_size(
995 /*==========*/
996 const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */
997 MY_ATTRIBUTE((warn_unused_result));
998 #ifdef UNIV_DEBUG
999 /**********************************************************//**
1000 Returns a pointer to the start of the record.
1001 @return pointer to start */
1002 UNIV_INLINE
1003 byte*
1004 rec_get_start(
1005 /*==========*/
1006 const rec_t* rec, /*!< in: pointer to record */
1007 const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */
1008 MY_ATTRIBUTE((warn_unused_result));
1009 /**********************************************************//**
1010 Returns a pointer to the end of the record.
1011 @return pointer to end */
1012 UNIV_INLINE
1013 byte*
1014 rec_get_end(
1015 /*========*/
1016 const rec_t* rec, /*!< in: pointer to record */
1017 const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */
1018 MY_ATTRIBUTE((warn_unused_result));
1019 #else /* UNIV_DEBUG */
1020 # define rec_get_start(rec, offsets) ((rec) - rec_offs_extra_size(offsets))
1021 # define rec_get_end(rec, offsets) ((rec) + rec_offs_data_size(offsets))
1022 #endif /* UNIV_DEBUG */
1023
1024 /** Copy a physical record to a buffer.
1025 @param[in] buf buffer
1026 @param[in] rec physical record
1027 @param[in] offsets array returned by rec_get_offsets()
1028 @return pointer to the origin of the copy */
1029 UNIV_INLINE
1030 rec_t*
1031 rec_copy(
1032 void* buf,
1033 const rec_t* rec,
1034 const rec_offs* offsets);
1035
1036 /** Determine the size of a data tuple prefix in a temporary file.
1037 @tparam redundant_temp whether to use the ROW_FORMAT=REDUNDANT format
1038 @param[in] index clustered or secondary index
1039 @param[in] fields data fields
1040 @param[in] n_fields number of data fields
1041 @param[out] extra record header size
1042 @param[in] status REC_STATUS_ORDINARY or REC_STATUS_INSTANT
1043 @return total size, in bytes */
1044 template<bool redundant_temp>
1045 ulint
1046 rec_get_converted_size_temp(
1047 const dict_index_t* index,
1048 const dfield_t* fields,
1049 ulint n_fields,
1050 ulint* extra,
1051 rec_comp_status_t status = REC_STATUS_ORDINARY)
1052 MY_ATTRIBUTE((warn_unused_result, nonnull));
1053
1054 /** Determine the offset to each field in temporary file.
1055 @param[in] rec temporary file record
1056 @param[in] index index of that the record belongs to
1057 @param[in,out] offsets offsets to the fields; in: rec_offs_n_fields(offsets)
1058 @param[in] n_core number of core fields (index->n_core_fields)
1059 @param[in] def_val default values for non-core fields
1060 @param[in] status REC_STATUS_ORDINARY or REC_STATUS_INSTANT */
1061 void
1062 rec_init_offsets_temp(
1063 const rec_t* rec,
1064 const dict_index_t* index,
1065 rec_offs* offsets,
1066 ulint n_core,
1067 const dict_col_t::def_t*def_val,
1068 rec_comp_status_t status = REC_STATUS_ORDINARY)
1069 MY_ATTRIBUTE((nonnull(1,2,3)));
1070 /** Determine the offset to each field in temporary file.
1071 @param[in] rec temporary file record
1072 @param[in] index index of that the record belongs to
1073 @param[in,out] offsets offsets to the fields; in: rec_offs_n_fields(offsets)
1074 */
1075 void
1076 rec_init_offsets_temp(
1077 const rec_t* rec,
1078 const dict_index_t* index,
1079 rec_offs* offsets)
1080 MY_ATTRIBUTE((nonnull));
1081
1082 /** Convert a data tuple prefix to the temporary file format.
1083 @tparam redundant_temp whether to use the ROW_FORMAT=REDUNDANT format
1084 @param[out] rec record in temporary file format
1085 @param[in] index clustered or secondary index
1086 @param[in] fields data fields
1087 @param[in] n_fields number of data fields
1088 @param[in] status REC_STATUS_ORDINARY or REC_STATUS_INSTANT */
1089 template<bool redundant_temp>
1090 void
1091 rec_convert_dtuple_to_temp(
1092 rec_t* rec,
1093 const dict_index_t* index,
1094 const dfield_t* fields,
1095 ulint n_fields,
1096 rec_comp_status_t status = REC_STATUS_ORDINARY)
1097 MY_ATTRIBUTE((nonnull));
1098
1099 /**************************************************************//**
1100 Copies the first n fields of a physical record to a new physical record in
1101 a buffer.
1102 @return own: copied record */
1103 rec_t*
1104 rec_copy_prefix_to_buf(
1105 /*===================*/
1106 const rec_t* rec, /*!< in: physical record */
1107 const dict_index_t* index, /*!< in: record descriptor */
1108 ulint n_fields, /*!< in: number of fields
1109 to copy */
1110 byte** buf, /*!< in/out: memory buffer
1111 for the copied prefix,
1112 or NULL */
1113 ulint* buf_size) /*!< in/out: buffer size */
1114 MY_ATTRIBUTE((nonnull));
1115 /*********************************************************//**
1116 Builds a physical record out of a data tuple and
1117 stores it into the given buffer.
1118 @return pointer to the origin of physical record */
1119 rec_t*
1120 rec_convert_dtuple_to_rec(
1121 /*======================*/
1122 byte* buf, /*!< in: start address of the
1123 physical record */
1124 const dict_index_t* index, /*!< in: record descriptor */
1125 const dtuple_t* dtuple, /*!< in: data tuple */
1126 ulint n_ext) /*!< in: number of
1127 externally stored columns */
1128 MY_ATTRIBUTE((warn_unused_result));
1129 /**********************************************************//**
1130 Returns the extra size of an old-style physical record if we know its
1131 data size and number of fields.
1132 @return extra size */
1133 UNIV_INLINE
1134 ulint
1135 rec_get_converted_extra_size(
1136 /*=========================*/
1137 ulint data_size, /*!< in: data size */
1138 ulint n_fields, /*!< in: number of fields */
1139 ulint n_ext) /*!< in: number of externally stored columns */
1140 MY_ATTRIBUTE((const));
1141 /**********************************************************//**
1142 Determines the size of a data tuple prefix in ROW_FORMAT=COMPACT.
1143 @return total size */
1144 ulint
1145 rec_get_converted_size_comp_prefix(
1146 /*===============================*/
1147 const dict_index_t* index, /*!< in: record descriptor */
1148 const dfield_t* fields, /*!< in: array of data fields */
1149 ulint n_fields,/*!< in: number of data fields */
1150 ulint* extra) /*!< out: extra size */
1151 MY_ATTRIBUTE((warn_unused_result, nonnull(1,2)));
1152
1153 /** Determine the size of a record in ROW_FORMAT=COMPACT.
1154 @param[in] index record descriptor. dict_table_is_comp()
1155 is assumed to hold, even if it doesn't
1156 @param[in] tuple logical record
1157 @param[out] extra extra size
1158 @return total size */
1159 ulint
1160 rec_get_converted_size_comp(
1161 const dict_index_t* index,
1162 const dtuple_t* tuple,
1163 ulint* extra)
1164 MY_ATTRIBUTE((nonnull(1,2)));
1165
1166 /**********************************************************//**
1167 The following function returns the size of a data tuple when converted to
1168 a physical record.
1169 @return size */
1170 UNIV_INLINE
1171 ulint
1172 rec_get_converted_size(
1173 /*===================*/
1174 dict_index_t* index, /*!< in: record descriptor */
1175 const dtuple_t* dtuple, /*!< in: data tuple */
1176 ulint n_ext) /*!< in: number of externally stored columns */
1177 MY_ATTRIBUTE((warn_unused_result, nonnull));
1178 /** Copy the first n fields of a (copy of a) physical record to a data tuple.
1179 The fields are copied into the memory heap.
1180 @param[out] tuple data tuple
1181 @param[in] rec index record, or a copy thereof
1182 @param[in] index index of rec
1183 @param[in] n_core index->n_core_fields at the time rec was
1184 copied, or 0 if non-leaf page record
1185 @param[in] n_fields number of fields to copy
1186 @param[in,out] heap memory heap */
1187 void
1188 rec_copy_prefix_to_dtuple(
1189 dtuple_t* tuple,
1190 const rec_t* rec,
1191 const dict_index_t* index,
1192 ulint n_core,
1193 ulint n_fields,
1194 mem_heap_t* heap)
1195 MY_ATTRIBUTE((nonnull));
1196 /***************************************************************//**
1197 Validates the consistency of a physical record.
1198 @return TRUE if ok */
1199 ibool
1200 rec_validate(
1201 /*=========*/
1202 const rec_t* rec, /*!< in: physical record */
1203 const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */
1204 MY_ATTRIBUTE((nonnull));
1205 /***************************************************************//**
1206 Prints an old-style physical record. */
1207 void
1208 rec_print_old(
1209 /*==========*/
1210 FILE* file, /*!< in: file where to print */
1211 const rec_t* rec) /*!< in: physical record */
1212 MY_ATTRIBUTE((nonnull));
1213 /***************************************************************//**
1214 Prints a spatial index record. */
1215 void
1216 rec_print_mbr_rec(
1217 /*==========*/
1218 FILE* file, /*!< in: file where to print */
1219 const rec_t* rec, /*!< in: physical record */
1220 const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */
1221 MY_ATTRIBUTE((nonnull));
1222 /***************************************************************//**
1223 Prints a physical record. */
1224 void
1225 rec_print_new(
1226 /*==========*/
1227 FILE* file, /*!< in: file where to print */
1228 const rec_t* rec, /*!< in: physical record */
1229 const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */
1230 MY_ATTRIBUTE((nonnull));
1231 /***************************************************************//**
1232 Prints a physical record. */
1233 void
1234 rec_print(
1235 /*======*/
1236 FILE* file, /*!< in: file where to print */
1237 const rec_t* rec, /*!< in: physical record */
1238 const dict_index_t* index) /*!< in: record descriptor */
1239 MY_ATTRIBUTE((nonnull));
1240
1241 /** Pretty-print a record.
1242 @param[in,out] o output stream
1243 @param[in] rec physical record
1244 @param[in] info rec_get_info_bits(rec)
1245 @param[in] offsets rec_get_offsets(rec) */
1246 void
1247 rec_print(
1248 std::ostream& o,
1249 const rec_t* rec,
1250 ulint info,
1251 const rec_offs* offsets);
1252
1253 /** Wrapper for pretty-printing a record */
1254 struct rec_index_print
1255 {
1256 /** Constructor */
rec_index_printrec_index_print1257 rec_index_print(const rec_t* rec, const dict_index_t* index) :
1258 m_rec(rec), m_index(index)
1259 {}
1260
1261 /** Record */
1262 const rec_t* m_rec;
1263 /** Index */
1264 const dict_index_t* m_index;
1265 };
1266
1267 /** Display a record.
1268 @param[in,out] o output stream
1269 @param[in] r record to display
1270 @return the output stream */
1271 std::ostream&
1272 operator<<(std::ostream& o, const rec_index_print& r);
1273
1274 /** Wrapper for pretty-printing a record */
1275 struct rec_offsets_print
1276 {
1277 /** Constructor */
rec_offsets_printrec_offsets_print1278 rec_offsets_print(const rec_t* rec, const rec_offs* offsets) :
1279 m_rec(rec), m_offsets(offsets)
1280 {}
1281
1282 /** Record */
1283 const rec_t* m_rec;
1284 /** Offsets to each field */
1285 const rec_offs* m_offsets;
1286 };
1287
1288 /** Display a record.
1289 @param[in,out] o output stream
1290 @param[in] r record to display
1291 @return the output stream */
1292 ATTRIBUTE_COLD
1293 std::ostream&
1294 operator<<(std::ostream& o, const rec_offsets_print& r);
1295
1296 /** Pretty-printer of records and tuples */
1297 class rec_printer : public std::ostringstream {
1298 public:
1299 /** Construct a pretty-printed record.
1300 @param rec record with header
1301 @param offsets rec_get_offsets(rec, ...) */
1302 ATTRIBUTE_COLD
rec_printer(const rec_t * rec,const rec_offs * offsets)1303 rec_printer(const rec_t* rec, const rec_offs* offsets)
1304 :
1305 std::ostringstream ()
1306 {
1307 rec_print(*this, rec,
1308 rec_get_info_bits(rec, rec_offs_comp(offsets)),
1309 offsets);
1310 }
1311
1312 /** Construct a pretty-printed record.
1313 @param rec record, possibly lacking header
1314 @param info rec_get_info_bits(rec)
1315 @param offsets rec_get_offsets(rec, ...) */
1316 ATTRIBUTE_COLD
rec_printer(const rec_t * rec,ulint info,const rec_offs * offsets)1317 rec_printer(const rec_t* rec, ulint info, const rec_offs* offsets)
1318 :
1319 std::ostringstream ()
1320 {
1321 rec_print(*this, rec, info, offsets);
1322 }
1323
1324 /** Construct a pretty-printed tuple.
1325 @param tuple data tuple */
1326 ATTRIBUTE_COLD
rec_printer(const dtuple_t * tuple)1327 rec_printer(const dtuple_t* tuple)
1328 :
1329 std::ostringstream ()
1330 {
1331 dtuple_print(*this, tuple);
1332 }
1333
1334 /** Construct a pretty-printed tuple.
1335 @param field array of data tuple fields
1336 @param n number of fields */
1337 ATTRIBUTE_COLD
rec_printer(const dfield_t * field,ulint n)1338 rec_printer(const dfield_t* field, ulint n)
1339 :
1340 std::ostringstream ()
1341 {
1342 dfield_print(*this, field, n);
1343 }
1344
1345 /** Destructor */
~rec_printer()1346 ~rec_printer() override {}
1347
1348 private:
1349 /** Copy constructor */
1350 rec_printer(const rec_printer& other);
1351 /** Assignment operator */
1352 rec_printer& operator=(const rec_printer& other);
1353 };
1354
1355
1356 # ifdef UNIV_DEBUG
1357 /** Read the DB_TRX_ID of a clustered index record.
1358 @param[in] rec clustered index record
1359 @param[in] index clustered index
1360 @return the value of DB_TRX_ID */
1361 trx_id_t
1362 rec_get_trx_id(
1363 const rec_t* rec,
1364 const dict_index_t* index)
1365 MY_ATTRIBUTE((nonnull, warn_unused_result));
1366 # endif /* UNIV_DEBUG */
1367
1368 /* Maximum lengths for the data in a physical record if the offsets
1369 are given in one byte (resp. two byte) format. */
1370 #define REC_1BYTE_OFFS_LIMIT 0x7FUL
1371 #define REC_2BYTE_OFFS_LIMIT 0x7FFFUL
1372
1373 /* The data size of record must not be larger than this on
1374 REDUNDANT row format because we reserve two upmost bits in a
1375 two byte offset for special purposes */
1376 #define REDUNDANT_REC_MAX_DATA_SIZE (16383)
1377
1378 /* The data size of record must be smaller than this on
1379 COMPRESSED row format because we reserve two upmost bits in a
1380 two byte offset for special purposes */
1381 #define COMPRESSED_REC_MAX_DATA_SIZE (16384)
1382
1383 #ifdef WITH_WSREP
1384 int wsrep_rec_get_foreign_key(
1385 byte *buf, /* out: extracted key */
1386 ulint *buf_len, /* in/out: length of buf */
1387 const rec_t* rec, /* in: physical record */
1388 dict_index_t* index_for, /* in: index for foreign table */
1389 dict_index_t* index_ref, /* in: index for referenced table */
1390 ibool new_protocol); /* in: protocol > 1 */
1391 #endif /* WITH_WSREP */
1392
1393 #include "rem0rec.inl"
1394
1395 #endif /* !UNIV_INNOCHECKSUM */
1396 #endif /* rem0rec_h */
1397