diff options
Diffstat (limited to '')
-rw-r--r-- | storage/innobase/include/rem0rec.inl | 1134 |
1 files changed, 1134 insertions, 0 deletions
diff --git a/storage/innobase/include/rem0rec.inl b/storage/innobase/include/rem0rec.inl new file mode 100644 index 00000000..46c209cb --- /dev/null +++ b/storage/innobase/include/rem0rec.inl @@ -0,0 +1,1134 @@ +/***************************************************************************** + +Copyright (c) 1994, 2019, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, 2022, MariaDB Corporation. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA + +*****************************************************************************/ + +/********************************************************************//** +@file include/rem0rec.ic +Record manager + +Created 5/30/1994 Heikki Tuuri +*************************************************************************/ + +#include "mach0data.h" +#include "ut0byte.h" +#include "dict0boot.h" +#include "btr0types.h" + +/* Offsets of the bit-fields in an old-style record. NOTE! In the table the +most significant bytes and bits are written below less significant. + + (1) byte offset (2) bit usage within byte + downward from + origin -> 1 8 bits pointer to next record + 2 8 bits pointer to next record + 3 1 bit short flag + 7 bits number of fields + 4 3 bits number of fields + 5 bits heap number + 5 8 bits heap number + 6 4 bits n_owned + 4 bits info bits +*/ + +/* Offsets of the bit-fields in a new-style record. NOTE! In the table the +most significant bytes and bits are written below less significant. + + (1) byte offset (2) bit usage within byte + downward from + origin -> 1 8 bits relative offset of next record + 2 8 bits relative offset of next record + the relative offset is an unsigned 16-bit + integer: + (offset_of_next_record + - offset_of_this_record) mod 64Ki, + where mod is the modulo as a non-negative + number; + we can calculate the offset of the next + record with the formula: + relative_offset + offset_of_this_record + mod srv_page_size + 3 3 bits status: + 000=REC_STATUS_ORDINARY + 001=REC_STATUS_NODE_PTR + 010=REC_STATUS_INFIMUM + 011=REC_STATUS_SUPREMUM + 100=REC_STATUS_INSTANT + 1xx=reserved + 5 bits heap number + 4 8 bits heap number + 5 4 bits n_owned + 4 bits info bits +*/ + +/* We list the byte offsets from the origin of the record, the mask, +and the shift needed to obtain each bit-field of the record. */ + +#define REC_NEXT 2 +#define REC_NEXT_MASK 0xFFFFUL +#define REC_NEXT_SHIFT 0 + +#define REC_OLD_SHORT 3 /* This is single byte bit-field */ +#define REC_OLD_SHORT_MASK 0x1UL +#define REC_OLD_SHORT_SHIFT 0 + +#define REC_OLD_N_FIELDS 4 +#define REC_OLD_N_FIELDS_MASK 0x7FEUL +#define REC_OLD_N_FIELDS_SHIFT 1 + +#define REC_OLD_HEAP_NO 5 +#define REC_HEAP_NO_MASK 0xFFF8UL +#if 0 /* defined in rem0rec.h for use of page0zip.cc */ +#define REC_NEW_HEAP_NO 4 +#define REC_HEAP_NO_SHIFT 3 +#endif + +#define REC_OLD_N_OWNED 6 /* This is single byte bit-field */ +#define REC_NEW_N_OWNED 5 /* This is single byte bit-field */ +#define REC_N_OWNED_MASK 0xFUL +#define REC_N_OWNED_SHIFT 0 + +#define REC_OLD_INFO_BITS 6 /* This is single byte bit-field */ +#define REC_NEW_INFO_BITS 5 /* This is single byte bit-field */ +#define REC_INFO_BITS_MASK 0xF0UL +#define REC_INFO_BITS_SHIFT 0 + +#if REC_OLD_SHORT_MASK << (8 * (REC_OLD_SHORT - 3)) \ + ^ REC_OLD_N_FIELDS_MASK << (8 * (REC_OLD_N_FIELDS - 4)) \ + ^ REC_HEAP_NO_MASK << (8 * (REC_OLD_HEAP_NO - 4)) \ + ^ REC_N_OWNED_MASK << (8 * (REC_OLD_N_OWNED - 3)) \ + ^ REC_INFO_BITS_MASK << (8 * (REC_OLD_INFO_BITS - 3)) \ + ^ 0xFFFFFFFFUL +# error "sum of old-style masks != 0xFFFFFFFFUL" +#endif +#if REC_NEW_STATUS_MASK << (8 * (REC_NEW_STATUS - 3)) \ + ^ REC_HEAP_NO_MASK << (8 * (REC_NEW_HEAP_NO - 4)) \ + ^ REC_N_OWNED_MASK << (8 * (REC_NEW_N_OWNED - 3)) \ + ^ REC_INFO_BITS_MASK << (8 * (REC_NEW_INFO_BITS - 3)) \ + ^ 0xFFFFFFUL +# error "sum of new-style masks != 0xFFFFFFUL" +#endif + +/******************************************************//** +Gets a bit field from within 1 byte. */ +UNIV_INLINE +byte +rec_get_bit_field_1( +/*================*/ + const rec_t* rec, /*!< in: pointer to record origin */ + ulint offs, /*!< in: offset from the origin down */ + ulint mask, /*!< in: mask used to filter bits */ + ulint shift) /*!< in: shift right applied after masking */ +{ + return static_cast<byte>((*(rec - offs) & mask) >> shift); +} + +/******************************************************//** +Sets a bit field within 1 byte. */ +UNIV_INLINE +void +rec_set_bit_field_1( +/*================*/ + rec_t* rec, /*!< in: pointer to record origin */ + ulint val, /*!< in: value to set */ + ulint offs, /*!< in: offset from the origin down */ + ulint mask, /*!< in: mask used to filter bits */ + ulint shift) /*!< in: shift right applied after masking */ +{ + ut_ad(rec); + ut_ad(offs <= REC_N_OLD_EXTRA_BYTES); + ut_ad(mask); + ut_ad(mask <= 0xFFUL); + ut_ad(((mask >> shift) << shift) == mask); + ut_ad(((val << shift) & mask) == (val << shift)); + + mach_write_to_1(rec - offs, + (mach_read_from_1(rec - offs) & ~mask) + | (val << shift)); +} + +/******************************************************//** +Gets a bit field from within 2 bytes. */ +UNIV_INLINE +ulint +rec_get_bit_field_2( +/*================*/ + const rec_t* rec, /*!< in: pointer to record origin */ + ulint offs, /*!< in: offset from the origin down */ + ulint mask, /*!< in: mask used to filter bits */ + ulint shift) /*!< in: shift right applied after masking */ +{ + ut_ad(rec); + + return((mach_read_from_2(rec - offs) & mask) >> shift); +} + +/******************************************************//** +Sets a bit field within 2 bytes. */ +UNIV_INLINE +void +rec_set_bit_field_2( +/*================*/ + rec_t* rec, /*!< in: pointer to record origin */ + ulint val, /*!< in: value to set */ + ulint offs, /*!< in: offset from the origin down */ + ulint mask, /*!< in: mask used to filter bits */ + ulint shift) /*!< in: shift right applied after masking */ +{ + ut_ad(rec); + ut_ad(offs <= REC_N_OLD_EXTRA_BYTES); + ut_ad(mask > 0xFFUL); + ut_ad(mask <= 0xFFFFUL); + ut_ad((mask >> shift) & 1); + ut_ad(0 == ((mask >> shift) & ((mask >> shift) + 1))); + ut_ad(((mask >> shift) << shift) == mask); + ut_ad(((val << shift) & mask) == (val << shift)); + + mach_write_to_2(rec - offs, + (mach_read_from_2(rec - offs) & ~mask) + | (val << shift)); +} + +/******************************************************//** +The following function is used to get the offset of the next chained record +on the same page. +@return the page offset of the next chained record, or 0 if none */ +UNIV_INLINE +ulint +rec_get_next_offs( +/*==============*/ + const rec_t* rec, /*!< in: physical record */ + ulint comp) /*!< in: nonzero=compact page format */ +{ + ulint field_value; + compile_time_assert(REC_NEXT_MASK == 0xFFFFUL); + compile_time_assert(REC_NEXT_SHIFT == 0); + + field_value = mach_read_from_2(rec - REC_NEXT); + + if (comp) { +#if UNIV_PAGE_SIZE_MAX <= 32768 + /* Note that for 64 KiB pages, field_value can 'wrap around' + and the debug assertion is not valid */ + + /* In the following assertion, field_value is interpreted + as signed 16-bit integer in 2's complement arithmetics. + If all platforms defined int16_t in the standard headers, + the expression could be written simpler as + (int16_t) field_value + ut_align_offset(...) < srv_page_size + */ + ut_ad((field_value >= 32768 + ? field_value - 65536 + : field_value) + + ut_align_offset(rec, srv_page_size) + < srv_page_size); +#endif + if (field_value == 0) { + + return(0); + } + + /* There must be at least REC_N_NEW_EXTRA_BYTES + 1 + between each record. */ + ut_ad((field_value > REC_N_NEW_EXTRA_BYTES + && field_value < 32768) + || field_value < (uint16) -REC_N_NEW_EXTRA_BYTES); + + return(ut_align_offset(rec + field_value, srv_page_size)); + } else { + ut_ad(field_value < srv_page_size); + + return(field_value); + } +} + +/******************************************************//** +The following function is used to set the next record offset field +of an old-style record. */ +UNIV_INLINE +void +rec_set_next_offs_old( +/*==================*/ + rec_t* rec, /*!< in: old-style physical record */ + ulint next) /*!< in: offset of the next record */ +{ + ut_ad(srv_page_size > next); + compile_time_assert(REC_NEXT_MASK == 0xFFFFUL); + compile_time_assert(REC_NEXT_SHIFT == 0); + mach_write_to_2(rec - REC_NEXT, next); +} + +/******************************************************//** +The following function is used to set the next record offset field +of a new-style record. */ +UNIV_INLINE +void +rec_set_next_offs_new( +/*==================*/ + rec_t* rec, /*!< in/out: new-style physical record */ + ulint next) /*!< in: offset of the next record */ +{ + ulint field_value; + + ut_ad(srv_page_size > next); + + if (!next) { + field_value = 0; + } else { + /* The following two statements calculate + next - offset_of_rec mod 64Ki, where mod is the modulo + as a non-negative number */ + + field_value = (ulint) + ((lint) next + - (lint) ut_align_offset(rec, srv_page_size)); + field_value &= REC_NEXT_MASK; + } + + mach_write_to_2(rec - REC_NEXT, field_value); +} + +/******************************************************//** +The following function is used to get the number of fields +in an old-style record. +@return number of data fields */ +UNIV_INLINE +ulint +rec_get_n_fields_old( +/*=================*/ + const rec_t* rec) /*!< in: physical record */ +{ + ulint ret; + + ut_ad(rec); + + ret = rec_get_bit_field_2(rec, REC_OLD_N_FIELDS, + REC_OLD_N_FIELDS_MASK, + REC_OLD_N_FIELDS_SHIFT); + ut_ad(ret <= REC_MAX_N_FIELDS); + ut_ad(ret > 0); + + return(ret); +} + +/******************************************************//** +The following function is used to set the number of fields +in an old-style record. */ +UNIV_INLINE +void +rec_set_n_fields_old( +/*=================*/ + rec_t* rec, /*!< in: physical record */ + ulint n_fields) /*!< in: the number of fields */ +{ + ut_ad(rec); + ut_ad(n_fields <= REC_MAX_N_FIELDS); + ut_ad(n_fields > 0); + + rec_set_bit_field_2(rec, n_fields, REC_OLD_N_FIELDS, + REC_OLD_N_FIELDS_MASK, REC_OLD_N_FIELDS_SHIFT); +} + +/******************************************************//** +The following function is used to get the number of fields +in a record. +@return number of data fields */ +UNIV_INLINE +ulint +rec_get_n_fields( +/*=============*/ + const rec_t* rec, /*!< in: physical record */ + const dict_index_t* index) /*!< in: record descriptor */ +{ + ut_ad(rec); + ut_ad(index); + + if (!dict_table_is_comp(index->table)) { + return(rec_get_n_fields_old(rec)); + } + + switch (rec_get_status(rec)) { + case REC_STATUS_INSTANT: + case REC_STATUS_ORDINARY: + return(dict_index_get_n_fields(index)); + case REC_STATUS_NODE_PTR: + return(dict_index_get_n_unique_in_tree(index) + 1); + case REC_STATUS_INFIMUM: + case REC_STATUS_SUPREMUM: + return(1); + } + + ut_error; + return(ULINT_UNDEFINED); +} + +/** Confirms the n_fields of the entry is sane with comparing the other +record in the same page specified +@param[in] index index +@param[in] rec record of the same page +@param[in] entry index entry +@return true if n_fields is sane */ +UNIV_INLINE +bool +rec_n_fields_is_sane( + dict_index_t* index, + const rec_t* rec, + const dtuple_t* entry) +{ + const ulint n_fields = rec_get_n_fields(rec, index); + + return(n_fields == dtuple_get_n_fields(entry) + || (index->is_instant() + && n_fields >= index->n_core_fields) + /* a record for older SYS_INDEXES table + (missing merge_threshold column) is acceptable. */ + || (index->table->id == DICT_INDEXES_ID + && n_fields == dtuple_get_n_fields(entry) - 1)); +} + +/******************************************************//** +The following function is used to get the number of records owned by the +previous directory record. +@return number of owned records */ +UNIV_INLINE +ulint +rec_get_n_owned_old( +/*================*/ + const rec_t* rec) /*!< in: old-style physical record */ +{ + return(rec_get_bit_field_1(rec, REC_OLD_N_OWNED, + REC_N_OWNED_MASK, REC_N_OWNED_SHIFT)); +} + +/******************************************************//** +The following function is used to get the number of records owned by the +previous directory record. +@return number of owned records */ +UNIV_INLINE +ulint +rec_get_n_owned_new( +/*================*/ + const rec_t* rec) /*!< in: new-style physical record */ +{ + return(rec_get_bit_field_1(rec, REC_NEW_N_OWNED, + REC_N_OWNED_MASK, REC_N_OWNED_SHIFT)); +} + +/******************************************************//** +The following function is used to retrieve the info bits of a record. +@return info bits */ +UNIV_INLINE +byte +rec_get_info_bits( +/*==============*/ + const rec_t* rec, /*!< in: physical record */ + ulint comp) /*!< in: nonzero=compact page format */ +{ + return rec_get_bit_field_1( + rec, comp ? REC_NEW_INFO_BITS : REC_OLD_INFO_BITS, + REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT); +} + +/******************************************************//** +The following function is used to retrieve the info and status +bits of a record. (Only compact records have status bits.) +@return info and status bits */ +UNIV_INLINE +byte +rec_get_info_and_status_bits( +/*=========================*/ + const rec_t* rec, /*!< in: physical record */ + ulint comp) /*!< in: nonzero=compact page format */ +{ + compile_time_assert(!((REC_NEW_STATUS_MASK >> REC_NEW_STATUS_SHIFT) + & (REC_INFO_BITS_MASK >> REC_INFO_BITS_SHIFT))); + if (comp) + return static_cast<byte>(rec_get_info_bits(rec, TRUE) | + rec_get_status(rec)); + else + return rec_get_info_bits(rec, FALSE); +} +/******************************************************//** +The following function is used to set the info and status +bits of a record. (Only compact records have status bits.) */ +UNIV_INLINE +void +rec_set_info_and_status_bits( +/*=========================*/ + rec_t* rec, /*!< in/out: physical record */ + ulint bits) /*!< in: info bits */ +{ + compile_time_assert(!((REC_NEW_STATUS_MASK >> REC_NEW_STATUS_SHIFT) + & (REC_INFO_BITS_MASK >> REC_INFO_BITS_SHIFT))); + rec_set_status(rec, bits & REC_NEW_STATUS_MASK); + rec_set_bit_field_1(rec, bits & ~REC_NEW_STATUS_MASK, + REC_NEW_INFO_BITS, + REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT); +} + +/******************************************************//** +The following function tells if record is delete marked. +@return nonzero if delete marked */ +UNIV_INLINE +ulint +rec_get_deleted_flag( +/*=================*/ + const rec_t* rec, /*!< in: physical record */ + ulint comp) /*!< in: nonzero=compact page format */ +{ + if (comp) { + return(rec_get_bit_field_1(rec, REC_NEW_INFO_BITS, + REC_INFO_DELETED_FLAG, + REC_INFO_BITS_SHIFT)); + } else { + return(rec_get_bit_field_1(rec, REC_OLD_INFO_BITS, + REC_INFO_DELETED_FLAG, + REC_INFO_BITS_SHIFT)); + } +} + +/******************************************************//** +The following function tells if a new-style record is a node pointer. +@return TRUE if node pointer */ +UNIV_INLINE +bool +rec_get_node_ptr_flag( +/*==================*/ + const rec_t* rec) /*!< in: physical record */ +{ + return(REC_STATUS_NODE_PTR == rec_get_status(rec)); +} + +/******************************************************//** +The following function is used to get the order number +of an old-style record in the heap of the index page. +@return heap order number */ +UNIV_INLINE +ulint +rec_get_heap_no_old( +/*================*/ + const rec_t* rec) /*!< in: physical record */ +{ + return(rec_get_bit_field_2(rec, REC_OLD_HEAP_NO, + REC_HEAP_NO_MASK, REC_HEAP_NO_SHIFT)); +} + +/******************************************************//** +The following function is used to get the order number +of a new-style record in the heap of the index page. +@return heap order number */ +UNIV_INLINE +ulint +rec_get_heap_no_new( +/*================*/ + const rec_t* rec) /*!< in: physical record */ +{ + return(rec_get_bit_field_2(rec, REC_NEW_HEAP_NO, + REC_HEAP_NO_MASK, REC_HEAP_NO_SHIFT)); +} + +/******************************************************//** +The following function is used to test whether the data offsets in the record +are stored in one-byte or two-byte format. +@return TRUE if 1-byte form */ +UNIV_INLINE +ibool +rec_get_1byte_offs_flag( +/*====================*/ + const rec_t* rec) /*!< in: physical record */ +{ + return(rec_get_bit_field_1(rec, REC_OLD_SHORT, REC_OLD_SHORT_MASK, + REC_OLD_SHORT_SHIFT)); +} + +/******************************************************//** +The following function is used to set the 1-byte offsets flag. */ +UNIV_INLINE +void +rec_set_1byte_offs_flag( +/*====================*/ + rec_t* rec, /*!< in: physical record */ + ibool flag) /*!< in: TRUE if 1byte form */ +{ + ut_ad(flag <= 1); + + rec_set_bit_field_1(rec, flag, REC_OLD_SHORT, REC_OLD_SHORT_MASK, + REC_OLD_SHORT_SHIFT); +} + +/******************************************************//** +Returns the offset of nth field end if the record is stored in the 1-byte +offsets form. If the field is SQL null, the flag is ORed in the returned +value. +@return offset of the start of the field, SQL null flag ORed */ +UNIV_INLINE +uint8_t +rec_1_get_field_end_info( +/*=====================*/ + const rec_t* rec, /*!< in: record */ + ulint n) /*!< in: field index */ +{ + ut_ad(rec_get_1byte_offs_flag(rec)); + ut_ad(n < rec_get_n_fields_old(rec)); + + return(mach_read_from_1(rec - (REC_N_OLD_EXTRA_BYTES + n + 1))); +} + +/******************************************************//** +Returns the offset of nth field end if the record is stored in the 2-byte +offsets form. If the field is SQL null, the flag is ORed in the returned +value. +@return offset of the start of the field, SQL null flag and extern +storage flag ORed */ +UNIV_INLINE +uint16_t +rec_2_get_field_end_info( +/*=====================*/ + const rec_t* rec, /*!< in: record */ + ulint n) /*!< in: field index */ +{ + ut_ad(!rec_get_1byte_offs_flag(rec)); + ut_ad(n < rec_get_n_fields_old(rec)); + + return(mach_read_from_2(rec - (REC_N_OLD_EXTRA_BYTES + 2 * n + 2))); +} + +/******************************************************//** +Returns nonzero if the field is stored off-page. +@retval 0 if the field is stored in-page +@retval REC_2BYTE_EXTERN_MASK if the field is stored externally */ +UNIV_INLINE +ulint +rec_2_is_field_extern( +/*==================*/ + const rec_t* rec, /*!< in: record */ + ulint n) /*!< in: field index */ +{ + return(rec_2_get_field_end_info(rec, n) & REC_2BYTE_EXTERN_MASK); +} + +/**********************************************************//** +The following function sets the number of allocated elements +for an array of offsets. */ +UNIV_INLINE +void +rec_offs_set_n_alloc( +/*=================*/ + rec_offs*offsets, /*!< out: array for rec_get_offsets(), + must be allocated */ + ulint n_alloc) /*!< in: number of elements */ +{ + ut_ad(n_alloc > REC_OFFS_HEADER_SIZE); + MEM_UNDEFINED(offsets, n_alloc * sizeof *offsets); + offsets[0] = static_cast<rec_offs>(n_alloc); +} + +/************************************************************//** +The following function is used to get an offset to the nth +data field in a record. +@return offset from the origin of rec */ +UNIV_INLINE +rec_offs +rec_get_nth_field_offs( +/*===================*/ + const rec_offs* offsets,/*!< in: array returned by rec_get_offsets() */ + ulint n, /*!< in: index of the field */ + ulint* len) /*!< out: length of the field; UNIV_SQL_NULL + if SQL null; UNIV_SQL_DEFAULT is default value */ +{ + ut_ad(n < rec_offs_n_fields(offsets)); + + rec_offs offs = n == 0 ? 0 : get_value(rec_offs_base(offsets)[n]); + rec_offs next_offs = rec_offs_base(offsets)[1 + n]; + + if (get_type(next_offs) == SQL_NULL) { + *len = UNIV_SQL_NULL; + } else if (get_type(next_offs) == DEFAULT) { + *len = UNIV_SQL_DEFAULT; + } else { + *len = get_value(next_offs) - offs; + } + + return(offs); +} + +/******************************************************//** +Determine if the offsets are for a record containing null BLOB pointers. +@return first field containing a null BLOB pointer, or NULL if none found */ +UNIV_INLINE +const byte* +rec_offs_any_null_extern( +/*=====================*/ + const rec_t* rec, /*!< in: record */ + const rec_offs* offsets) /*!< in: rec_get_offsets(rec) */ +{ + ulint i; + ut_ad(rec_offs_validate(rec, NULL, offsets)); + + if (!rec_offs_any_extern(offsets)) { + return(NULL); + } + + for (i = 0; i < rec_offs_n_fields(offsets); i++) { + if (rec_offs_nth_extern(offsets, i)) { + ulint len; + const byte* field + = rec_get_nth_field(rec, offsets, i, &len); + + ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE); + if (!memcmp(field + len + - BTR_EXTERN_FIELD_REF_SIZE, + field_ref_zero, + BTR_EXTERN_FIELD_REF_SIZE)) { + return(field); + } + } + } + + return(NULL); +} + +/******************************************************//** +Gets the physical size of a field. +@return length of field */ +UNIV_INLINE +ulint +rec_offs_nth_size( +/*==============*/ + const rec_offs* offsets,/*!< in: array returned by rec_get_offsets() */ + ulint n) /*!< in: nth field */ +{ + ut_ad(rec_offs_validate(NULL, NULL, offsets)); + ut_ad(n < rec_offs_n_fields(offsets)); + if (!n) { + return get_value(rec_offs_base(offsets)[1 + n]); + } + return get_value((rec_offs_base(offsets)[1 + n])) + - get_value(rec_offs_base(offsets)[n]); +} + +/******************************************************//** +Returns the number of extern bits set in a record. +@return number of externally stored fields */ +UNIV_INLINE +ulint +rec_offs_n_extern( +/*==============*/ + const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */ +{ + ulint n = 0; + + if (rec_offs_any_extern(offsets)) { + ulint i; + + for (i = rec_offs_n_fields(offsets); i--; ) { + if (rec_offs_nth_extern(offsets, i)) { + n++; + } + } + } + + return(n); +} + +/******************************************************//** +Returns the offset of n - 1th field end if the record is stored in the 1-byte +offsets form. If the field is SQL null, the flag is ORed in the returned +value. This function and the 2-byte counterpart are defined here because the +C-compiler was not able to sum negative and positive constant offsets, and +warned of constant arithmetic overflow within the compiler. +@return offset of the start of the PREVIOUS field, SQL null flag ORed */ +UNIV_INLINE +ulint +rec_1_get_prev_field_end_info( +/*==========================*/ + const rec_t* rec, /*!< in: record */ + ulint n) /*!< in: field index */ +{ + ut_ad(rec_get_1byte_offs_flag(rec)); + ut_ad(n <= rec_get_n_fields_old(rec)); + + return(mach_read_from_1(rec - (REC_N_OLD_EXTRA_BYTES + n))); +} + +/******************************************************//** +Returns the offset of n - 1th field end if the record is stored in the 2-byte +offsets form. If the field is SQL null, the flag is ORed in the returned +value. +@return offset of the start of the PREVIOUS field, SQL null flag ORed */ +UNIV_INLINE +ulint +rec_2_get_prev_field_end_info( +/*==========================*/ + const rec_t* rec, /*!< in: record */ + ulint n) /*!< in: field index */ +{ + ut_ad(!rec_get_1byte_offs_flag(rec)); + ut_ad(n <= rec_get_n_fields_old(rec)); + + return(mach_read_from_2(rec - (REC_N_OLD_EXTRA_BYTES + 2 * n))); +} + +/******************************************************//** +Sets the field end info for the nth field if the record is stored in the +1-byte format. */ +UNIV_INLINE +void +rec_1_set_field_end_info( +/*=====================*/ + rec_t* rec, /*!< in: record */ + ulint n, /*!< in: field index */ + ulint info) /*!< in: value to set */ +{ + ut_ad(rec_get_1byte_offs_flag(rec)); + ut_ad(n < rec_get_n_fields_old(rec)); + + mach_write_to_1(rec - (REC_N_OLD_EXTRA_BYTES + n + 1), info); +} + +/******************************************************//** +Sets the field end info for the nth field if the record is stored in the +2-byte format. */ +UNIV_INLINE +void +rec_2_set_field_end_info( +/*=====================*/ + rec_t* rec, /*!< in: record */ + ulint n, /*!< in: field index */ + ulint info) /*!< in: value to set */ +{ + ut_ad(!rec_get_1byte_offs_flag(rec)); + ut_ad(n < rec_get_n_fields_old(rec)); + + mach_write_to_2(rec - (REC_N_OLD_EXTRA_BYTES + 2 * n + 2), info); +} + +/******************************************************//** +Returns the offset of nth field start if the record is stored in the 1-byte +offsets form. +@return offset of the start of the field */ +UNIV_INLINE +ulint +rec_1_get_field_start_offs( +/*=======================*/ + const rec_t* rec, /*!< in: record */ + ulint n) /*!< in: field index */ +{ + ut_ad(rec_get_1byte_offs_flag(rec)); + ut_ad(n <= rec_get_n_fields_old(rec)); + + if (n == 0) { + + return(0); + } + + return(rec_1_get_prev_field_end_info(rec, n) + & ~REC_1BYTE_SQL_NULL_MASK); +} + +/******************************************************//** +Returns the offset of nth field start if the record is stored in the 2-byte +offsets form. +@return offset of the start of the field */ +UNIV_INLINE +ulint +rec_2_get_field_start_offs( +/*=======================*/ + const rec_t* rec, /*!< in: record */ + ulint n) /*!< in: field index */ +{ + ut_ad(!rec_get_1byte_offs_flag(rec)); + ut_ad(n <= rec_get_n_fields_old(rec)); + + if (n == 0) { + + return(0); + } + + return(rec_2_get_prev_field_end_info(rec, n) + & ~(REC_2BYTE_SQL_NULL_MASK | REC_2BYTE_EXTERN_MASK)); +} + +/******************************************************//** +The following function is used to read the offset of the start of a data field +in the record. The start of an SQL null field is the end offset of the +previous non-null field, or 0, if none exists. If n is the number of the last +field + 1, then the end offset of the last field is returned. +@return offset of the start of the field */ +UNIV_INLINE +ulint +rec_get_field_start_offs( +/*=====================*/ + const rec_t* rec, /*!< in: record */ + ulint n) /*!< in: field index */ +{ + ut_ad(rec); + ut_ad(n <= rec_get_n_fields_old(rec)); + + if (n == 0) { + + return(0); + } + + if (rec_get_1byte_offs_flag(rec)) { + + return(rec_1_get_field_start_offs(rec, n)); + } + + return(rec_2_get_field_start_offs(rec, n)); +} + +/************************************************************//** +Gets the physical size of an old-style field. +Also an SQL null may have a field of size > 0, +if the data type is of a fixed size. +@return field size in bytes */ +UNIV_INLINE +ulint +rec_get_nth_field_size( +/*===================*/ + const rec_t* rec, /*!< in: record */ + ulint n) /*!< in: index of the field */ +{ + ulint os; + ulint next_os; + + os = rec_get_field_start_offs(rec, n); + next_os = rec_get_field_start_offs(rec, n + 1); + + ut_ad(next_os - os < srv_page_size); + + return(next_os - os); +} + +/**********************************************************//** +The following function returns the data size of an old-style physical +record, that is the sum of field lengths. SQL null fields +are counted as length 0 fields. The value returned by the function +is the distance from record origin to record end in bytes. +@return size */ +UNIV_INLINE +ulint +rec_get_data_size_old( +/*==================*/ + const rec_t* rec) /*!< in: physical record */ +{ + ut_ad(rec); + + return(rec_get_field_start_offs(rec, rec_get_n_fields_old(rec))); +} + +/**********************************************************//** +The following function sets the number of fields in offsets. */ +UNIV_INLINE +void +rec_offs_set_n_fields( +/*==================*/ + rec_offs* offsets, /*!< in/out: array returned by + rec_get_offsets() */ + ulint n_fields) /*!< in: number of fields */ +{ + ut_ad(offsets); + ut_ad(n_fields > 0); + ut_ad(n_fields <= REC_MAX_N_FIELDS); + ut_ad(n_fields + REC_OFFS_HEADER_SIZE + <= rec_offs_get_n_alloc(offsets)); + offsets[1] = static_cast<rec_offs>(n_fields); +} + +/**********************************************************//** +The following function returns the data size of a physical +record, that is the sum of field lengths. SQL null fields +are counted as length 0 fields. The value returned by the function +is the distance from record origin to record end in bytes. +@return size */ +UNIV_INLINE +ulint +rec_offs_data_size( +/*===============*/ + const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */ +{ + ulint size; + + ut_ad(rec_offs_validate(NULL, NULL, offsets)); + size = get_value(rec_offs_base(offsets)[rec_offs_n_fields(offsets)]); + ut_ad(size < srv_page_size); + return(size); +} + +/**********************************************************//** +Returns the total size of record minus data size of record. The value +returned by the function is the distance from record start to record origin +in bytes. +@return size */ +UNIV_INLINE +ulint +rec_offs_extra_size( +/*================*/ + const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */ +{ + ulint size; + ut_ad(rec_offs_validate(NULL, NULL, offsets)); + size = *rec_offs_base(offsets) & REC_OFFS_MASK; + ut_ad(size < srv_page_size); + return(size); +} + +/**********************************************************//** +Returns the total size of a physical record. +@return size */ +UNIV_INLINE +ulint +rec_offs_size( +/*==========*/ + const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */ +{ + return(rec_offs_data_size(offsets) + rec_offs_extra_size(offsets)); +} + +#ifdef UNIV_DEBUG +/**********************************************************//** +Returns a pointer to the end of the record. +@return pointer to end */ +UNIV_INLINE +byte* +rec_get_end( +/*========*/ + const rec_t* rec, /*!< in: pointer to record */ + const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */ +{ + ut_ad(rec_offs_validate(rec, NULL, offsets)); + return(const_cast<rec_t*>(rec + rec_offs_data_size(offsets))); +} + +/**********************************************************//** +Returns a pointer to the start of the record. +@return pointer to start */ +UNIV_INLINE +byte* +rec_get_start( +/*==========*/ + const rec_t* rec, /*!< in: pointer to record */ + const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */ +{ + ut_ad(rec_offs_validate(rec, NULL, offsets)); + return(const_cast<rec_t*>(rec - rec_offs_extra_size(offsets))); +} +#endif /* UNIV_DEBUG */ + +/** Copy a physical record to a buffer. +@param[in] buf buffer +@param[in] rec physical record +@param[in] offsets array returned by rec_get_offsets() +@return pointer to the origin of the copy */ +UNIV_INLINE +rec_t* +rec_copy( + void* buf, + const rec_t* rec, + const rec_offs* offsets) +{ + ulint extra_len; + ulint data_len; + + ut_ad(rec != NULL); + ut_ad(buf != NULL); + ut_ad(rec_offs_validate(rec, NULL, offsets)); + ut_ad(rec_validate(rec, offsets)); + + extra_len = rec_offs_extra_size(offsets); + data_len = rec_offs_data_size(offsets); + + memcpy(buf, rec - extra_len, extra_len + data_len); + + return((byte*) buf + extra_len); +} + +/**********************************************************//** +Returns the extra size of an old-style physical record if we know its +data size and number of fields. +@return extra size */ +UNIV_INLINE +ulint +rec_get_converted_extra_size( +/*=========================*/ + ulint data_size, /*!< in: data size */ + ulint n_fields, /*!< in: number of fields */ + ulint n_ext) /*!< in: number of externally stored columns */ +{ + if (!n_ext && data_size <= REC_1BYTE_OFFS_LIMIT) { + + return(REC_N_OLD_EXTRA_BYTES + n_fields); + } + + return(REC_N_OLD_EXTRA_BYTES + 2 * n_fields); +} + +/**********************************************************//** +The following function returns the size of a data tuple when converted to +a physical record. +@return size */ +UNIV_INLINE +ulint +rec_get_converted_size( +/*===================*/ + dict_index_t* index, /*!< in: record descriptor */ + const dtuple_t* dtuple, /*!< in: data tuple */ + ulint n_ext) /*!< in: number of externally stored columns */ +{ + ulint data_size; + ulint extra_size; + + ut_ad(dtuple_check_typed(dtuple)); +#ifdef UNIV_DEBUG + if (dict_index_is_ibuf(index)) { + ut_ad(dtuple->n_fields > 1); + } else if ((dtuple_get_info_bits(dtuple) & REC_NEW_STATUS_MASK) + == REC_STATUS_NODE_PTR) { + ut_ad(dtuple->n_fields - 1 + == dict_index_get_n_unique_in_tree_nonleaf(index)); + } else if (index->table->id == DICT_INDEXES_ID) { + /* The column SYS_INDEXES.MERGE_THRESHOLD was + instantly added in MariaDB 10.2.2 (MySQL 5.7). */ + ut_ad(!index->table->is_temporary()); + ut_ad(index->n_fields == DICT_NUM_FIELDS__SYS_INDEXES); + ut_ad(dtuple->n_fields == DICT_NUM_FIELDS__SYS_INDEXES + || dtuple->n_fields + == DICT_FLD__SYS_INDEXES__MERGE_THRESHOLD); + } else { + ut_ad(dtuple->n_fields >= index->n_core_fields); + ut_ad(dtuple->n_fields <= index->n_fields + || dtuple->is_alter_metadata()); + } +#endif + + if (dict_table_is_comp(index->table)) { + return rec_get_converted_size_comp(index, dtuple, NULL); + } + + data_size = dtuple_get_data_size(dtuple, 0); + + /* If primary key is being updated then the new record inherits + externally stored fields from the delete-marked old record. + In that case, n_ext may be less value than + dtuple_get_n_ext(tuple). */ + ut_ad(n_ext <= dtuple_get_n_ext(dtuple)); + extra_size = rec_get_converted_extra_size( + data_size, dtuple_get_n_fields(dtuple), n_ext); + + return(data_size + extra_size); +} |