mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 20:42:30 +01:00
7d2dcab3a2
innobase/include/rem0rec.ic: Eliminate int16_t from an assertion, as it is not defined in the same system headers on all platforms. (Bug #8244)
1531 lines
41 KiB
Text
1531 lines
41 KiB
Text
/************************************************************************
|
|
Record manager
|
|
|
|
(c) 1994-1996 Innobase Oy
|
|
|
|
Created 5/30/1994 Heikki Tuuri
|
|
*************************************************************************/
|
|
|
|
#include "mach0data.h"
|
|
#include "ut0byte.h"
|
|
#include "dict0dict.h"
|
|
|
|
/* Compact flag ORed to the extra size returned by rec_get_offsets() */
|
|
#define REC_OFFS_COMPACT ((ulint) 1 << 31)
|
|
/* SQL NULL flag in offsets returned by rec_get_offsets() */
|
|
#define REC_OFFS_SQL_NULL ((ulint) 1 << 31)
|
|
/* External flag in offsets returned by rec_get_offsets() */
|
|
#define REC_OFFS_EXTERNAL ((ulint) 1 << 30)
|
|
/* Mask for offsets returned by rec_get_offsets() */
|
|
#define REC_OFFS_MASK (REC_OFFS_EXTERNAL - 1)
|
|
|
|
/* 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 the offset of the next
|
|
record with the formula:
|
|
relative_offset + offset_of_this_record
|
|
mod UNIV_PAGE_SIZE
|
|
3 3 bits status:
|
|
000=conventional record
|
|
001=node pointer record (inside B-tree)
|
|
010=infimum record
|
|
011=supremum record
|
|
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_NEW_STATUS 3 /* This is single byte bit-field */
|
|
#define REC_NEW_STATUS_MASK 0x7UL
|
|
#define REC_NEW_STATUS_SHIFT 0
|
|
|
|
#define REC_OLD_HEAP_NO 5
|
|
#define REC_NEW_HEAP_NO 4
|
|
#define REC_HEAP_NO_MASK 0xFFF8UL
|
|
#define REC_HEAP_NO_SHIFT 3
|
|
|
|
#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
|
|
|
|
/* The deleted flag in info bits */
|
|
#define REC_INFO_DELETED_FLAG 0x20UL /* when bit is set to 1, it means the
|
|
record has been delete marked */
|
|
/* The following masks are used to filter the SQL null bit from
|
|
one-byte and two-byte offsets */
|
|
|
|
#define REC_1BYTE_SQL_NULL_MASK 0x80UL
|
|
#define REC_2BYTE_SQL_NULL_MASK 0x8000UL
|
|
|
|
/* In a 2-byte offset the second most significant bit denotes
|
|
a field stored to another page: */
|
|
|
|
#define REC_2BYTE_EXTERN_MASK 0x4000UL
|
|
|
|
#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
|
|
|
|
/***************************************************************
|
|
Sets the value of the ith field SQL null bit of an old-style record. */
|
|
|
|
void
|
|
rec_set_nth_field_null_bit(
|
|
/*=======================*/
|
|
rec_t* rec, /* in: record */
|
|
ulint i, /* in: ith field */
|
|
ibool val); /* in: value to set */
|
|
/***************************************************************
|
|
Sets an old-style record field to SQL null.
|
|
The physical size of the field is not changed. */
|
|
|
|
void
|
|
rec_set_nth_field_sql_null(
|
|
/*=======================*/
|
|
rec_t* rec, /* in: record */
|
|
ulint n); /* in: index of the field */
|
|
|
|
/***************************************************************
|
|
Sets the value of the ith field extern storage bit of an old-style record. */
|
|
|
|
void
|
|
rec_set_nth_field_extern_bit_old(
|
|
/*=============================*/
|
|
rec_t* rec, /* in: old-style record */
|
|
ulint i, /* in: ith field */
|
|
ibool val, /* in: value to set */
|
|
mtr_t* mtr); /* in: mtr holding an X-latch to the page where
|
|
rec is, or NULL; in the NULL case we do not
|
|
write to log about the change */
|
|
/***************************************************************
|
|
Sets the value of the ith field extern storage bit of a new-style record. */
|
|
|
|
void
|
|
rec_set_nth_field_extern_bit_new(
|
|
/*=============================*/
|
|
rec_t* rec, /* in: record */
|
|
dict_index_t* index, /* in: record descriptor */
|
|
ulint ith, /* in: ith field */
|
|
ibool val, /* in: value to set */
|
|
mtr_t* mtr); /* in: mtr holding an X-latch to the page
|
|
where rec is, or NULL; in the NULL case
|
|
we do not write to log about the change */
|
|
|
|
/**********************************************************
|
|
Gets a bit field from within 1 byte. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_get_bit_field_1(
|
|
/*================*/
|
|
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_1(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(
|
|
/*================*/
|
|
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. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_get_next_offs(
|
|
/*==============*/
|
|
/* out: the page offset of the next chained record, or
|
|
0 if none */
|
|
rec_t* rec, /* in: physical record */
|
|
ibool comp) /* in: TRUE=compact page format */
|
|
{
|
|
ulint field_value;
|
|
|
|
ut_ad(REC_NEXT_MASK == 0xFFFFUL);
|
|
ut_ad(REC_NEXT_SHIFT == 0);
|
|
|
|
field_value = mach_read_from_2(rec - REC_NEXT);
|
|
|
|
if (comp) {
|
|
#if UNIV_PAGE_SIZE <= 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(...) < UNIV_PAGE_SIZE
|
|
*/
|
|
ut_ad((field_value >= 32768
|
|
? field_value - 65536
|
|
: field_value)
|
|
+ ut_align_offset(rec, UNIV_PAGE_SIZE)
|
|
< UNIV_PAGE_SIZE);
|
|
#endif
|
|
if (field_value == 0) {
|
|
|
|
return(0);
|
|
}
|
|
|
|
return(ut_align_offset(rec + field_value, UNIV_PAGE_SIZE));
|
|
} else {
|
|
ut_ad(field_value < UNIV_PAGE_SIZE);
|
|
|
|
return(field_value);
|
|
}
|
|
}
|
|
|
|
/**********************************************************
|
|
The following function is used to set the next record offset field of the
|
|
record. */
|
|
UNIV_INLINE
|
|
void
|
|
rec_set_next_offs(
|
|
/*==============*/
|
|
rec_t* rec, /* in: physical record */
|
|
ibool comp, /* in: TRUE=compact page format */
|
|
ulint next) /* in: offset of the next record, or 0 if none */
|
|
{
|
|
ut_ad(rec);
|
|
ut_ad(UNIV_PAGE_SIZE > next);
|
|
ut_ad(REC_NEXT_MASK == 0xFFFFUL);
|
|
ut_ad(REC_NEXT_SHIFT == 0);
|
|
|
|
if (comp) {
|
|
ulint field_value;
|
|
|
|
if (next) {
|
|
/* 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, UNIV_PAGE_SIZE));
|
|
field_value &= REC_NEXT_MASK;
|
|
} else {
|
|
field_value = 0;
|
|
}
|
|
|
|
mach_write_to_2(rec - REC_NEXT, field_value);
|
|
} else {
|
|
mach_write_to_2(rec - REC_NEXT, next);
|
|
}
|
|
}
|
|
|
|
/**********************************************************
|
|
The following function is used to get the number of fields
|
|
in an old-style record. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_get_n_fields_old(
|
|
/*=================*/
|
|
/* out: number of data fields */
|
|
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 retrieves the status bits of a new-style record. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_get_status(
|
|
/*===========*/
|
|
/* out: status bits */
|
|
rec_t* rec) /* in: physical record */
|
|
{
|
|
ulint ret;
|
|
|
|
ut_ad(rec);
|
|
|
|
ret = rec_get_bit_field_1(rec, REC_NEW_STATUS,
|
|
REC_NEW_STATUS_MASK, REC_NEW_STATUS_SHIFT);
|
|
ut_ad((ret & ~REC_NEW_STATUS_MASK) == 0);
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/**********************************************************
|
|
The following function is used to get the number of fields
|
|
in a record. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_get_n_fields(
|
|
/*=============*/
|
|
/* out: number of data fields */
|
|
rec_t* rec, /* in: physical record */
|
|
dict_index_t* index) /* in: record descriptor */
|
|
{
|
|
ut_ad(rec);
|
|
ut_ad(index);
|
|
if (!index->table->comp) {
|
|
return(rec_get_n_fields_old(rec));
|
|
}
|
|
switch (rec_get_status(rec)) {
|
|
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);
|
|
default:
|
|
ut_error;
|
|
return(ULINT_UNDEFINED);
|
|
}
|
|
}
|
|
|
|
/**********************************************************
|
|
The following function is used to get the number of records owned by the
|
|
previous directory record. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_get_n_owned(
|
|
/*============*/
|
|
/* out: number of owned records */
|
|
rec_t* rec, /* in: physical record */
|
|
ibool comp) /* in: TRUE=compact page format */
|
|
{
|
|
ulint ret;
|
|
|
|
ut_ad(rec);
|
|
|
|
ret = rec_get_bit_field_1(rec,
|
|
comp ? REC_NEW_N_OWNED : REC_OLD_N_OWNED,
|
|
REC_N_OWNED_MASK, REC_N_OWNED_SHIFT);
|
|
ut_ad(ret <= REC_MAX_N_OWNED);
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/**********************************************************
|
|
The following function is used to set the number of owned records. */
|
|
UNIV_INLINE
|
|
void
|
|
rec_set_n_owned(
|
|
/*============*/
|
|
rec_t* rec, /* in: physical record */
|
|
ibool comp, /* in: TRUE=compact page format */
|
|
ulint n_owned) /* in: the number of owned */
|
|
{
|
|
ut_ad(rec);
|
|
ut_ad(n_owned <= REC_MAX_N_OWNED);
|
|
|
|
rec_set_bit_field_1(rec, n_owned,
|
|
comp ? REC_NEW_N_OWNED : REC_OLD_N_OWNED,
|
|
REC_N_OWNED_MASK, REC_N_OWNED_SHIFT);
|
|
}
|
|
|
|
/**********************************************************
|
|
The following function is used to retrieve the info bits of a record. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_get_info_bits(
|
|
/*==============*/
|
|
/* out: info bits */
|
|
rec_t* rec, /* in: physical record */
|
|
ibool comp) /* in: TRUE=compact page format */
|
|
{
|
|
ulint ret;
|
|
|
|
ut_ad(rec);
|
|
|
|
ret = rec_get_bit_field_1(rec,
|
|
comp ? REC_NEW_INFO_BITS : REC_OLD_INFO_BITS,
|
|
REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT);
|
|
ut_ad((ret & ~REC_INFO_BITS_MASK) == 0);
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/**********************************************************
|
|
The following function is used to set the info bits of a record. */
|
|
UNIV_INLINE
|
|
void
|
|
rec_set_info_bits(
|
|
/*==============*/
|
|
rec_t* rec, /* in: physical record */
|
|
ibool comp, /* in: TRUE=compact page format */
|
|
ulint bits) /* in: info bits */
|
|
{
|
|
ut_ad(rec);
|
|
ut_ad((bits & ~REC_INFO_BITS_MASK) == 0);
|
|
|
|
rec_set_bit_field_1(rec, bits,
|
|
comp ? REC_NEW_INFO_BITS : REC_OLD_INFO_BITS,
|
|
REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT);
|
|
}
|
|
|
|
/**********************************************************
|
|
The following function is used to set the status bits of a new-style record. */
|
|
UNIV_INLINE
|
|
void
|
|
rec_set_status(
|
|
/*===========*/
|
|
rec_t* rec, /* in: physical record */
|
|
ulint bits) /* in: info bits */
|
|
{
|
|
ut_ad(rec);
|
|
ut_ad((bits & ~REC_NEW_STATUS_MASK) == 0);
|
|
|
|
rec_set_bit_field_1(rec, bits, REC_NEW_STATUS,
|
|
REC_NEW_STATUS_MASK, REC_NEW_STATUS_SHIFT);
|
|
}
|
|
|
|
/**********************************************************
|
|
The following function is used to retrieve the info and status
|
|
bits of a record. (Only compact records have status bits.) */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_get_info_and_status_bits(
|
|
/*=========================*/
|
|
/* out: info bits */
|
|
rec_t* rec, /* in: physical record */
|
|
ibool comp) /* in: TRUE=compact page format */
|
|
{
|
|
ulint bits;
|
|
#if (REC_NEW_STATUS_MASK >> REC_NEW_STATUS_SHIFT) \
|
|
& (REC_INFO_BITS_MASK >> REC_INFO_BITS_SHIFT)
|
|
# error "REC_NEW_STATUS_MASK and REC_INFO_BITS_MASK overlap"
|
|
#endif
|
|
if (comp) {
|
|
bits = rec_get_info_bits(rec, TRUE) | rec_get_status(rec);
|
|
} else {
|
|
bits = rec_get_info_bits(rec, FALSE);
|
|
ut_ad(!(bits & ~(REC_INFO_BITS_MASK >> REC_INFO_BITS_SHIFT)));
|
|
}
|
|
return(bits);
|
|
}
|
|
/**********************************************************
|
|
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: physical record */
|
|
ibool comp, /* in: TRUE=compact page format */
|
|
ulint bits) /* in: info bits */
|
|
{
|
|
#if (REC_NEW_STATUS_MASK >> REC_NEW_STATUS_SHIFT) \
|
|
& (REC_INFO_BITS_MASK >> REC_INFO_BITS_SHIFT)
|
|
# error "REC_NEW_STATUS_MASK and REC_INFO_BITS_MASK overlap"
|
|
#endif
|
|
if (comp) {
|
|
rec_set_status(rec, bits & REC_NEW_STATUS_MASK);
|
|
} else {
|
|
ut_ad(!(bits & ~(REC_INFO_BITS_MASK >> REC_INFO_BITS_SHIFT)));
|
|
}
|
|
rec_set_info_bits(rec, comp, bits & ~REC_NEW_STATUS_MASK);
|
|
}
|
|
|
|
/**********************************************************
|
|
The following function tells if record is delete marked. */
|
|
UNIV_INLINE
|
|
ibool
|
|
rec_get_deleted_flag(
|
|
/*=================*/
|
|
/* out: TRUE if delete marked */
|
|
rec_t* rec, /* in: physical record */
|
|
ibool comp) /* in: TRUE=compact page format */
|
|
{
|
|
if (REC_INFO_DELETED_FLAG & rec_get_info_bits(rec, comp)) {
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
/**********************************************************
|
|
The following function is used to set the deleted bit. */
|
|
UNIV_INLINE
|
|
void
|
|
rec_set_deleted_flag(
|
|
/*=================*/
|
|
rec_t* rec, /* in: physical record */
|
|
ibool comp, /* in: TRUE=compact page format */
|
|
ibool flag) /* in: TRUE if delete marked */
|
|
{
|
|
ulint old_val;
|
|
ulint new_val;
|
|
|
|
ut_ad(TRUE == 1);
|
|
ut_ad(flag <= TRUE);
|
|
|
|
old_val = rec_get_info_bits(rec, comp);
|
|
|
|
if (flag) {
|
|
new_val = REC_INFO_DELETED_FLAG | old_val;
|
|
} else {
|
|
new_val = ~REC_INFO_DELETED_FLAG & old_val;
|
|
}
|
|
|
|
rec_set_info_bits(rec, comp, new_val);
|
|
}
|
|
|
|
/**********************************************************
|
|
The following function tells if a new-style record is a node pointer. */
|
|
UNIV_INLINE
|
|
ibool
|
|
rec_get_node_ptr_flag(
|
|
/*=================*/
|
|
/* out: TRUE if node pointer */
|
|
rec_t* rec) /* in: physical record */
|
|
{
|
|
return(REC_STATUS_NODE_PTR == rec_get_status(rec));
|
|
}
|
|
|
|
/**********************************************************
|
|
The following function is used to flag a record as a node pointer. */
|
|
UNIV_INLINE
|
|
void
|
|
rec_set_node_ptr_flag(
|
|
/*=================*/
|
|
rec_t* rec, /* in: physical record */
|
|
ibool flag) /* in: TRUE if the record is a node pointer */
|
|
{
|
|
ulint status;
|
|
ut_ad(flag <= TRUE);
|
|
ut_ad(REC_STATUS_NODE_PTR >= rec_get_status(rec));
|
|
if (flag) {
|
|
status = REC_STATUS_NODE_PTR;
|
|
} else {
|
|
status = REC_STATUS_ORDINARY;
|
|
}
|
|
rec_set_status(rec, status);
|
|
}
|
|
|
|
/**********************************************************
|
|
The following function is used to get the order number of the record in the
|
|
heap of the index page. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_get_heap_no(
|
|
/*=============*/
|
|
/* out: heap order number */
|
|
rec_t* rec, /* in: physical record */
|
|
ibool comp) /* in: TRUE=compact page format */
|
|
{
|
|
ulint ret;
|
|
|
|
ut_ad(rec);
|
|
|
|
ret = rec_get_bit_field_2(rec,
|
|
comp ? REC_NEW_HEAP_NO : REC_OLD_HEAP_NO,
|
|
REC_HEAP_NO_MASK, REC_HEAP_NO_SHIFT);
|
|
ut_ad(ret <= REC_MAX_HEAP_NO);
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/**********************************************************
|
|
The following function is used to set the heap number field in the record. */
|
|
UNIV_INLINE
|
|
void
|
|
rec_set_heap_no(
|
|
/*=============*/
|
|
rec_t* rec, /* in: physical record */
|
|
ibool comp, /* in: TRUE=compact page format */
|
|
ulint heap_no)/* in: the heap number */
|
|
{
|
|
ut_ad(heap_no <= REC_MAX_HEAP_NO);
|
|
|
|
rec_set_bit_field_2(rec, heap_no,
|
|
comp ? REC_NEW_HEAP_NO : REC_OLD_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. */
|
|
UNIV_INLINE
|
|
ibool
|
|
rec_get_1byte_offs_flag(
|
|
/*====================*/
|
|
/* out: TRUE if 1-byte form */
|
|
rec_t* rec) /* in: physical record */
|
|
{
|
|
#if TRUE != 1
|
|
#error "TRUE != 1"
|
|
#endif
|
|
|
|
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 */
|
|
{
|
|
#if TRUE != 1
|
|
#error "TRUE != 1"
|
|
#endif
|
|
ut_ad(flag <= TRUE);
|
|
|
|
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. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_1_get_field_end_info(
|
|
/*=====================*/
|
|
/* out: offset of the start of the field, SQL null
|
|
flag ORed */
|
|
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. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_2_get_field_end_info(
|
|
/*=====================*/
|
|
/* out: offset of the start of the field, SQL null
|
|
flag and extern storage flag ORed */
|
|
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)));
|
|
}
|
|
|
|
#ifdef UNIV_DEBUG
|
|
/* Length of the rec_get_offsets() header */
|
|
# define REC_OFFS_HEADER_SIZE 4
|
|
#else /* UNIV_DEBUG */
|
|
/* Length of the rec_get_offsets() header */
|
|
# define REC_OFFS_HEADER_SIZE 2
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
/* Get the base address of offsets. The extra_size is stored at
|
|
this position, and following positions hold the end offsets of
|
|
the fields. */
|
|
#define rec_offs_base(offsets) (offsets + REC_OFFS_HEADER_SIZE)
|
|
|
|
/**************************************************************
|
|
The following function returns the number of allocated elements
|
|
for an array of offsets. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_offs_get_n_alloc(
|
|
/*=================*/
|
|
/* out: number of elements */
|
|
const ulint* offsets)/* in: array for rec_get_offsets() */
|
|
{
|
|
ulint n_alloc;
|
|
ut_ad(offsets);
|
|
n_alloc = offsets[0];
|
|
ut_ad(n_alloc > 0);
|
|
return(n_alloc);
|
|
}
|
|
|
|
/**************************************************************
|
|
The following function sets the number of allocated elements
|
|
for an array of offsets. */
|
|
UNIV_INLINE
|
|
void
|
|
rec_offs_set_n_alloc(
|
|
/*=================*/
|
|
ulint* offsets, /* in: array for rec_get_offsets() */
|
|
ulint n_alloc) /* in: number of elements */
|
|
{
|
|
ut_ad(offsets);
|
|
ut_ad(n_alloc > 0);
|
|
offsets[0] = n_alloc;
|
|
}
|
|
|
|
/**************************************************************
|
|
The following function returns the number of fields in a record. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_offs_n_fields(
|
|
/*===============*/
|
|
/* out: number of fields */
|
|
const ulint* offsets)/* in: array returned by rec_get_offsets() */
|
|
{
|
|
ulint n_fields;
|
|
ut_ad(offsets);
|
|
n_fields = offsets[1];
|
|
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));
|
|
return(n_fields);
|
|
}
|
|
|
|
/****************************************************************
|
|
Validates offsets returned by rec_get_offsets(). */
|
|
UNIV_INLINE
|
|
ibool
|
|
rec_offs_validate(
|
|
/*==============*/
|
|
/* out: TRUE if valid */
|
|
rec_t* rec, /* in: record or NULL */
|
|
dict_index_t* index, /* in: record descriptor or NULL */
|
|
const ulint* offsets)/* in: array returned by rec_get_offsets() */
|
|
{
|
|
ulint i = rec_offs_n_fields(offsets);
|
|
ulint last = ULINT_MAX;
|
|
ibool comp = (*rec_offs_base(offsets) & REC_OFFS_COMPACT) != 0;
|
|
|
|
if (rec) {
|
|
ut_ad((ulint) rec == offsets[2]);
|
|
if (!comp) {
|
|
ut_a(rec_get_n_fields_old(rec) >= i);
|
|
}
|
|
}
|
|
if (index) {
|
|
ulint max_n_fields;
|
|
ut_ad((ulint) index == offsets[3]);
|
|
max_n_fields = ut_max(
|
|
dict_index_get_n_fields(index),
|
|
dict_index_get_n_unique_in_tree(index) + 1);
|
|
if (comp && rec) {
|
|
switch (rec_get_status(rec)) {
|
|
case REC_STATUS_ORDINARY:
|
|
break;
|
|
case REC_STATUS_NODE_PTR:
|
|
max_n_fields =
|
|
dict_index_get_n_unique_in_tree(index) + 1;
|
|
break;
|
|
case REC_STATUS_INFIMUM:
|
|
case REC_STATUS_SUPREMUM:
|
|
max_n_fields = 1;
|
|
break;
|
|
default:
|
|
ut_error;
|
|
}
|
|
}
|
|
/* index->n_def == 0 for dummy indexes if !comp */
|
|
ut_a(!comp || index->n_def);
|
|
ut_a(!index->n_def || i <= max_n_fields);
|
|
}
|
|
while (i--) {
|
|
ulint curr = rec_offs_base(offsets)[1 + i] & REC_OFFS_MASK;
|
|
ut_a(curr <= last);
|
|
last = curr;
|
|
}
|
|
return(TRUE);
|
|
}
|
|
/****************************************************************
|
|
Updates debug data in offsets, in order to avoid bogus
|
|
rec_offs_validate() failures. */
|
|
UNIV_INLINE
|
|
void
|
|
rec_offs_make_valid(
|
|
/*================*/
|
|
rec_t* rec __attribute__((unused)),
|
|
/* in: record */
|
|
dict_index_t* index __attribute__((unused)),
|
|
/* in: record descriptor */
|
|
ulint* offsets __attribute__((unused)))
|
|
/* in: array returned by rec_get_offsets() */
|
|
{
|
|
#ifdef UNIV_DEBUG
|
|
ut_ad(rec_get_n_fields(rec, index) >= rec_offs_n_fields(offsets));
|
|
offsets[2] = (ulint) rec;
|
|
offsets[3] = (ulint) index;
|
|
#endif /* UNIV_DEBUG */
|
|
}
|
|
|
|
/****************************************************************
|
|
The following function is used to get a pointer to the nth
|
|
data field in an old-style record. */
|
|
UNIV_INLINE
|
|
byte*
|
|
rec_get_nth_field(
|
|
/*==============*/
|
|
/* out: pointer to the field */
|
|
rec_t* rec, /* in: record */
|
|
const ulint* 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 */
|
|
{
|
|
byte* field;
|
|
ulint length;
|
|
ut_ad(rec);
|
|
ut_ad(rec_offs_validate(rec, NULL, offsets));
|
|
ut_ad(n < rec_offs_n_fields(offsets));
|
|
ut_ad(len);
|
|
|
|
if (n == 0) {
|
|
field = rec;
|
|
} else {
|
|
field = rec + (rec_offs_base(offsets)[n] & REC_OFFS_MASK);
|
|
}
|
|
|
|
length = rec_offs_base(offsets)[1 + n];
|
|
|
|
if (length & REC_OFFS_SQL_NULL) {
|
|
length = UNIV_SQL_NULL;
|
|
} else {
|
|
length &= REC_OFFS_MASK;
|
|
length -= field - rec;
|
|
}
|
|
|
|
*len = length;
|
|
return(field);
|
|
}
|
|
|
|
/**********************************************************
|
|
Determine if the offsets are for a record in the new
|
|
compact format. */
|
|
UNIV_INLINE
|
|
ibool
|
|
rec_offs_comp(
|
|
/*==========*/
|
|
/* out: TRUE if compact format */
|
|
const ulint* offsets)/* in: array returned by rec_get_offsets() */
|
|
{
|
|
ut_ad(rec_offs_validate(NULL, NULL, offsets));
|
|
return((*rec_offs_base(offsets) & REC_OFFS_COMPACT) != 0);
|
|
}
|
|
|
|
/**********************************************************
|
|
Returns TRUE if the nth field of rec is SQL NULL. */
|
|
UNIV_INLINE
|
|
ibool
|
|
rec_offs_nth_null(
|
|
/*==============*/
|
|
/* out: TRUE if SQL NULL */
|
|
const ulint* 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));
|
|
return((rec_offs_base(offsets)[1 + n] & REC_OFFS_SQL_NULL) != 0);
|
|
}
|
|
/**********************************************************
|
|
Returns TRUE if the extern bit is set in nth field of rec. */
|
|
UNIV_INLINE
|
|
ibool
|
|
rec_offs_nth_extern(
|
|
/*================*/
|
|
/* out: TRUE if externally stored */
|
|
const ulint* 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));
|
|
return((rec_offs_base(offsets)[1 + n] & REC_OFFS_EXTERNAL) != 0);
|
|
}
|
|
|
|
/**********************************************************
|
|
Gets the physical size of a field. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_offs_nth_size(
|
|
/*==============*/
|
|
/* out: length of field */
|
|
const ulint* 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));
|
|
return((rec_offs_base(offsets)[1 + n] - rec_offs_base(offsets)[n])
|
|
& REC_OFFS_MASK);
|
|
}
|
|
|
|
/**********************************************************
|
|
Returns TRUE if the extern bit is set in any of the fields
|
|
of an old-style record. */
|
|
UNIV_INLINE
|
|
ibool
|
|
rec_offs_any_extern(
|
|
/*================*/
|
|
/* out: TRUE if a field is stored externally */
|
|
const ulint* offsets)/* in: array returned by rec_get_offsets() */
|
|
{
|
|
ulint i;
|
|
for (i = rec_offs_n_fields(offsets); i--; ) {
|
|
if (rec_offs_nth_extern(offsets, i)) {
|
|
return(TRUE);
|
|
}
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
/***************************************************************
|
|
Sets the value of the ith field extern storage bit. */
|
|
UNIV_INLINE
|
|
void
|
|
rec_set_nth_field_extern_bit(
|
|
/*=========================*/
|
|
rec_t* rec, /* in: record */
|
|
dict_index_t* index, /* in: record descriptor */
|
|
ulint i, /* in: ith field */
|
|
ibool val, /* in: value to set */
|
|
mtr_t* mtr) /* in: mtr holding an X-latch to the page
|
|
where rec is, or NULL; in the NULL case
|
|
we do not write to log about the change */
|
|
{
|
|
if (index->table->comp) {
|
|
rec_set_nth_field_extern_bit_new(rec, index, i, val, mtr);
|
|
} else {
|
|
rec_set_nth_field_extern_bit_old(rec, i, val, mtr);
|
|
}
|
|
}
|
|
|
|
/**********************************************************
|
|
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-compilerwas not able to sum negative and positive constant offsets, and
|
|
warned of constant arithmetic overflow within the compiler. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_1_get_prev_field_end_info(
|
|
/*==========================*/
|
|
/* out: offset of the start of the PREVIOUS field, SQL
|
|
null flag ORed */
|
|
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. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_2_get_prev_field_end_info(
|
|
/*==========================*/
|
|
/* out: offset of the start of the PREVIOUS field, SQL
|
|
null flag ORed */
|
|
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. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_1_get_field_start_offs(
|
|
/*=======================*/
|
|
/* out: offset of the start of the field */
|
|
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. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_2_get_field_start_offs(
|
|
/*=======================*/
|
|
/* out: offset of the start of the field */
|
|
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. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_get_field_start_offs(
|
|
/*=====================*/
|
|
/* out: offset of the start of the field */
|
|
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. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_get_nth_field_size(
|
|
/*===================*/
|
|
/* out: field size in bytes */
|
|
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 < UNIV_PAGE_SIZE);
|
|
|
|
return(next_os - os);
|
|
}
|
|
|
|
/***************************************************************
|
|
This is used to modify the value of an already existing field in a record.
|
|
The previous value must have exactly the same size as the new value. If len
|
|
is UNIV_SQL_NULL then the field is treated as an SQL null for old-style
|
|
records. For new-style records, len must not be UNIV_SQL_NULL. */
|
|
UNIV_INLINE
|
|
void
|
|
rec_set_nth_field(
|
|
/*==============*/
|
|
rec_t* rec, /* in: record */
|
|
const ulint* offsets,/* in: array returned by rec_get_offsets() */
|
|
ulint n, /* in: index number of the field */
|
|
const void* data, /* in: pointer to the data
|
|
if not SQL null */
|
|
ulint len) /* in: length of the data or UNIV_SQL_NULL.
|
|
If not SQL null, must have the same
|
|
length as the previous value.
|
|
If SQL null, previous value must be
|
|
SQL null. */
|
|
{
|
|
byte* data2;
|
|
ulint len2;
|
|
|
|
ut_ad(rec);
|
|
ut_ad(rec_offs_validate(rec, NULL, offsets));
|
|
|
|
if (len == UNIV_SQL_NULL) {
|
|
ut_ad(!rec_offs_comp(offsets));
|
|
rec_set_nth_field_sql_null(rec, n);
|
|
|
|
return;
|
|
}
|
|
|
|
data2 = rec_get_nth_field(rec, offsets, n, &len2);
|
|
if (len2 == UNIV_SQL_NULL) {
|
|
ut_ad(!rec_offs_comp(offsets));
|
|
rec_set_nth_field_null_bit(rec, n, FALSE);
|
|
ut_ad(len == rec_get_nth_field_size(rec, n));
|
|
} else {
|
|
ut_ad(len2 == len);
|
|
}
|
|
|
|
ut_memcpy(data2, data, len);
|
|
}
|
|
|
|
/**************************************************************
|
|
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. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_get_data_size_old(
|
|
/*==================*/
|
|
/* out: size */
|
|
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(
|
|
/*==================*/
|
|
ulint* offsets, /* in: 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] = 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. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_offs_data_size(
|
|
/*===============*/
|
|
/* out: size */
|
|
const ulint* 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_n_fields(offsets)]
|
|
& REC_OFFS_MASK;
|
|
ut_ad(size < UNIV_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. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_offs_extra_size(
|
|
/*================*/
|
|
/* out: size */
|
|
const ulint* 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_COMPACT;
|
|
ut_ad(size < UNIV_PAGE_SIZE);
|
|
return(size);
|
|
}
|
|
|
|
/**************************************************************
|
|
Returns the total size of a physical record. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_offs_size(
|
|
/*==========*/
|
|
/* out: size */
|
|
const ulint* offsets)/* in: array returned by rec_get_offsets() */
|
|
{
|
|
return(rec_offs_data_size(offsets) + rec_offs_extra_size(offsets));
|
|
}
|
|
|
|
/**************************************************************
|
|
Returns a pointer to the end of the record. */
|
|
UNIV_INLINE
|
|
byte*
|
|
rec_get_end(
|
|
/*========*/
|
|
/* out: pointer to end */
|
|
rec_t* rec, /* in: pointer to record */
|
|
const ulint* offsets)/* in: array returned by rec_get_offsets() */
|
|
{
|
|
return(rec + rec_offs_data_size(offsets));
|
|
}
|
|
|
|
/**************************************************************
|
|
Returns a pointer to the start of the record. */
|
|
UNIV_INLINE
|
|
byte*
|
|
rec_get_start(
|
|
/*==========*/
|
|
/* out: pointer to start */
|
|
rec_t* rec, /* in: pointer to record */
|
|
const ulint* offsets)/* in: array returned by rec_get_offsets() */
|
|
{
|
|
return(rec - rec_offs_extra_size(offsets));
|
|
}
|
|
|
|
/*******************************************************************
|
|
Copies a physical record to a buffer. */
|
|
UNIV_INLINE
|
|
rec_t*
|
|
rec_copy(
|
|
/*=====*/
|
|
/* out: pointer to the origin of the copy */
|
|
void* buf, /* in: buffer */
|
|
const rec_t* rec, /* in: physical record */
|
|
const ulint* offsets)/* in: array returned by rec_get_offsets() */
|
|
{
|
|
ulint extra_len;
|
|
ulint data_len;
|
|
|
|
ut_ad(rec && buf);
|
|
ut_ad(rec_offs_validate((rec_t*) rec, NULL, offsets));
|
|
ut_ad(rec_validate((rec_t*) rec, offsets));
|
|
|
|
extra_len = rec_offs_extra_size(offsets);
|
|
data_len = rec_offs_data_size(offsets);
|
|
|
|
ut_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. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_get_converted_extra_size(
|
|
/*=========================*/
|
|
/* out: extra size */
|
|
ulint data_size, /* in: data size */
|
|
ulint n_fields) /* in: number of fields */
|
|
{
|
|
if (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 new-style physical record. */
|
|
|
|
ulint
|
|
rec_get_converted_size_new(
|
|
/*=======================*/
|
|
/* out: size */
|
|
dict_index_t* index, /* in: record descriptor */
|
|
dtuple_t* dtuple);/* in: data tuple */
|
|
/**************************************************************
|
|
The following function returns the size of a data tuple when converted to
|
|
a physical record. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_get_converted_size(
|
|
/*===================*/
|
|
/* out: size */
|
|
dict_index_t* index, /* in: record descriptor */
|
|
dtuple_t* dtuple) /* in: data tuple */
|
|
{
|
|
ulint data_size;
|
|
ulint extra_size;
|
|
|
|
ut_ad(index);
|
|
ut_ad(dtuple);
|
|
ut_ad(dtuple_check_typed(dtuple));
|
|
|
|
ut_ad(index->type & DICT_UNIVERSAL
|
|
|| dtuple_get_n_fields(dtuple) ==
|
|
(((dtuple_get_info_bits(dtuple) & REC_NEW_STATUS_MASK)
|
|
== REC_STATUS_NODE_PTR)
|
|
? dict_index_get_n_unique_in_tree(index) + 1
|
|
: dict_index_get_n_fields(index)));
|
|
|
|
if (index->table->comp) {
|
|
return(rec_get_converted_size_new(index, dtuple));
|
|
}
|
|
|
|
data_size = dtuple_get_data_size(dtuple);
|
|
|
|
extra_size = rec_get_converted_extra_size(
|
|
data_size, dtuple_get_n_fields(dtuple));
|
|
|
|
return(data_size + extra_size);
|
|
}
|
|
|
|
/****************************************************************
|
|
Folds a prefix of a physical record to a ulint. Folds only existing fields,
|
|
that is, checks that we do not run out of the record. */
|
|
UNIV_INLINE
|
|
ulint
|
|
rec_fold(
|
|
/*=====*/
|
|
/* out: the folded value */
|
|
rec_t* rec, /* in: the physical record */
|
|
const ulint* offsets, /* in: array returned by
|
|
rec_get_offsets() */
|
|
ulint n_fields, /* in: number of complete
|
|
fields to fold */
|
|
ulint n_bytes, /* in: number of bytes to fold
|
|
in an incomplete last field */
|
|
dulint tree_id) /* in: index tree id */
|
|
{
|
|
ulint i;
|
|
byte* data;
|
|
ulint len;
|
|
ulint fold;
|
|
ulint n_fields_rec;
|
|
|
|
ut_ad(rec_offs_validate(rec, NULL, offsets));
|
|
ut_ad(rec_validate((rec_t*) rec, offsets));
|
|
ut_ad(n_fields + n_bytes > 0);
|
|
|
|
n_fields_rec = rec_offs_n_fields(offsets);
|
|
ut_ad(n_fields <= n_fields_rec);
|
|
ut_ad(n_fields < n_fields_rec || n_bytes == 0);
|
|
|
|
if (n_fields > n_fields_rec) {
|
|
n_fields = n_fields_rec;
|
|
}
|
|
|
|
if (n_fields == n_fields_rec) {
|
|
n_bytes = 0;
|
|
}
|
|
|
|
fold = ut_fold_dulint(tree_id);
|
|
|
|
for (i = 0; i < n_fields; i++) {
|
|
data = rec_get_nth_field(rec, offsets, i, &len);
|
|
|
|
if (len != UNIV_SQL_NULL) {
|
|
fold = ut_fold_ulint_pair(fold,
|
|
ut_fold_binary(data, len));
|
|
}
|
|
}
|
|
|
|
if (n_bytes > 0) {
|
|
data = rec_get_nth_field(rec, offsets, i, &len);
|
|
|
|
if (len != UNIV_SQL_NULL) {
|
|
if (len > n_bytes) {
|
|
len = n_bytes;
|
|
}
|
|
|
|
fold = ut_fold_ulint_pair(fold,
|
|
ut_fold_binary(data, len));
|
|
}
|
|
}
|
|
|
|
return(fold);
|
|
}
|