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