Theoffsetsofthedatafieldsaregivenasone-byte (iftherearelessthan127bytesofdataintherecord) ortwo-byteunsignedintegers.Themostsignificantbit isnotpartoftheoffset,insteaditindicatestheSQL-null
if the bit is set to 1. */
/* PHYSICAL RECORD (NEW STYLE) ===========================
Theoffsetsofthedatafieldsaregivenasone-byte (iftherearelessthan127bytesofdataintherecord) ortwo-byteunsignedintegers.Themostsignificantbit isnotpartoftheoffset,insteaditindicatestheSQL-null
if the bit is set to 1. */
/* CANONICAL COORDINATES. A record can be seen as a single stringof'characters'inthefollowingway:catenatethebytes ineachfield,intheorderoffields.AnSQL-nullfield istakentobeanemptysequenceofbytes.Thenafter thepositionofeachfieldinsertinthestring the'character'<FIELD-END>,exceptthatafteranSQL-nullfield insert<NULL-FIELD-END>.Nowtheordinalpositionofeach byteinthiscanonicalstringisitscanonicalcoordinate. So,fortherecord("AA",SQL-NULL,"BB",""),thecanonical stringis"AA<FIELD_END><NULL-FIELD-END>BB<FIELD-END><FIELD-END>". Weidentifyprefixes(=initialsegments)ofarecord withprefixesofthecanonicalstring.Thecanonical lengthoftheprefixisthelengthofthecorresponding prefixofthecanonicalstring.Thecanonicallengthof arecordisthelengthofitscanonicalstring.
Acomplete-fieldprefixofarecordisaprefixwhichendsatthe endofsomefield(containingalso<FIELD-END>). Arecordisacomplete-fieldprefixofanotherrecord,if
the corresponding canonical strings have the same property. */
/***************************************************************//**
Validates the consistency of an old-style physical record.
@returnTRUEif ok */ static
ibool
rec_validate_old( /*=============*/ const rec_t* rec); /*!< in: physical record */
/******************************************************//**
Determine how many of the first n columns in a compact
physical record are stored externally.
@return number of externally stored columns */
ulint
rec_get_n_extern_new( /*=================*/ const rec_t* rec, /*!< in: compact physical record */ const dict_index_t* index, /*!< in: record descriptor */
ulint n) /*!< in: number of columns to scan */
{ const byte* nulls; const byte* lens;
ulint null_mask;
ulint n_extern;
ulint i;
/* read the lengths of fields 0..n */ do { const dict_field_t* field
= dict_index_get_nth_field(index, i); const dict_col_t* col
= dict_field_get_col(field);
ulint len;
if (!(col->prtype & DATA_NOT_NULL)) { /* nullable field => read the null flag */
if (UNIV_UNLIKELY(!(byte) null_mask)) {
nulls--;
null_mask = 1;
}
if (*nulls & null_mask) {
null_mask <<= 1; /* No length is stored for NULL fields. */ continue;
}
null_mask <<= 1;
}
if (UNIV_UNLIKELY(!field->fixed_len)) { /* Variable-length field: read the length */
len = *lens--; /* If the maximum length of the field is up to255bytes,theactuallengthisalways storedinonebyte.Ifthemaximumlengthis morethan255bytes,theactuallengthis storedinonebytefor0..127.Thelength willbeencodedintwobyteswhenitis128or
more, or when the field is stored externally. */ if (UNIV_UNLIKELY(len & 0x80) && DATA_BIG_COL(col)) { /* 1exxxxxxx xxxxxxxx */ if (len & 0x40) {
n_extern++;
}
lens--;
}
}
} while (++i < n);
return(n_extern);
}
/** Format of a leaf-page ROW_FORMAT!=REDUNDANT record */ enum rec_leaf_format { /** Temporary file record */
REC_LEAF_TEMP, /** Temporary file record, with added columns (REC_STATUS_INSTANT) */
REC_LEAF_TEMP_INSTANT, /** Normal (REC_STATUS_ORDINARY) */
REC_LEAF_ORDINARY, /** With add or drop columns (REC_STATUS_INSTANT) */
REC_LEAF_INSTANT
};
#ifdefined __GNUC__ && !defined __clang__ # pragma GCC diagnostic push # if __GNUC__ < 12 || defined WITH_UBSAN # pragma GCC diagnostic ignored "-Wconversion" # endif #endif /** Determine the offset to each field in a leaf-page record inROW_FORMAT=COMPACT,DYNAMIC,COMPRESSED. Thisisaspecialcaseofrec_init_offsets()andrec_get_offsets_func(). @tparammblobwhethertherecordincludesametadataBLOB @tparamredundant_tempwhethertherecordbelongstoatemporaryfile ofaROW_FORMAT=REDUNDANTtable @param[in]recleaf-pagerecord @param[in]indextheindexthattherecordbelongsin @param[in]n_corenumberofcorefields(index->n_core_fields) @param[in]def_valdefaultvaluesfornon-corefields,or NULLtorefertoindex->fields[].col->def_val @param[in,out]offsetsoffsets,withvalidrec_offs_n_fields(offsets)
@param[in] format record format */ template<bool mblob = false, bool redundant_temp = false> staticinline void
rec_init_offsets_comp_ordinary( const rec_t* rec, const dict_index_t* index,
rec_offs* offsets,
ulint n_core, const dict_col_t::def_t*def_val,
rec_leaf_format format)
{
rec_offs offs = 0;
rec_offs any = 0; const byte* nulls = rec; const byte* lens = NULL;
ulint n_fields = n_core;
ulint null_mask = 1;
switch (format) { case REC_LEAF_TEMP: if (dict_table_is_comp(index->table)) { /* No need to do adjust fixed_len=0. We only need to
adjust it for ROW_FORMAT=REDUNDANT. */
format = REC_LEAF_ORDINARY;
} goto ordinary; case REC_LEAF_ORDINARY:
nulls -= REC_N_NEW_EXTRA_BYTES;
ordinary:
lens = --nulls - n_core_null_bytes;
start: #ifdef UNIV_DEBUG /* We cannot invoke rec_offs_make_valid() if format==REC_LEAF_TEMP. Similarly,rec_offs_validate()willfailinthatcase,because
it invokes rec_get_status(). */
memcpy(&offsets[RECORD_OFFSET], &rec, sizeof(rec));
memcpy(&offsets[INDEX_OFFSET], &index, sizeof(index)); #endif/* UNIV_DEBUG */
/* read the lengths of fields 0..n_fields */
rec_offs len;
ulint i = 0; const dict_field_t* field = index->fields;
do { if (mblob) { if (i == index->first_user_field()) {
offs += FIELD_REF_SIZE;
len = combine(offs, STORED_OFFPAGE);
any |= REC_OFFS_EXTERNAL;
field--; continue;
} elseif (i >= n_fields) {
len = combine(offs, DEFAULT);
any |= REC_OFFS_DEFAULT; continue;
}
} elseif (i < n_fields) { /* The field is present, and will be covered below. */
} elseif (!mblob && def_val) { const dict_col_t::def_t& d = def_val[i - n_core]; if (!d.data) {
len = combine(offs, SQL_NULL);
ut_ad(d.len == UNIV_SQL_NULL);
} else {
len = combine(offs, DEFAULT);
any |= REC_OFFS_DEFAULT;
}
continue;
} else {
ulint dlen; if (!index->instant_field_value(i, &dlen)) {
len = combine(offs, SQL_NULL);
ut_ad(dlen == UNIV_SQL_NULL);
} else {
len = combine(offs, DEFAULT);
any |= REC_OFFS_DEFAULT;
}
continue;
}
const dict_col_t* col = field->col;
if (col->is_nullable()) { /* nullable field => read the null flag */
ut_ad(n_null--);
if (UNIV_UNLIKELY(!(byte) null_mask)) {
nulls--;
null_mask = 1;
}
if (*nulls & null_mask) {
null_mask <<= 1; /* No length is stored for NULL fields. Wedonotadvanceoffs,andweset thelengthtozeroandenablethe
SQL NULL flag in offsets[]. */
len = combine(offs, SQL_NULL); continue;
}
null_mask <<= 1;
}
if (!field->fixed_len
|| (format <= REC_LEAF_TEMP_INSTANT
&& !dict_col_get_fixed_size(col, true))) { /* Variable-length field: read the length */
len = *lens--; /* If the maximum length of the field is up to255bytes,theactuallengthisalways storedinonebyte.Ifthemaximumlengthis morethan255bytes,theactuallengthis storedinonebytefor0..127.Thelength willbeencodedintwobyteswhenitis128or
more, or when the field is stored externally. */ if (UNIV_UNLIKELY(len & 0x80) && DATA_BIG_COL(col)) { /* 1exxxxxxx xxxxxxxx */
len <<= 8;
len |= *lens--;
static_assert(STORED_OFFPAGE == 0x4000, "");
static_assert(REC_OFFS_EXTERNAL == 0x4000, ""); const rec_offs ext = len & REC_OFFS_EXTERNAL;
offs += get_value(len);
len = offs | ext;
any |= ext;
ut_ad(!ext || index->is_primary()); continue;
}
len = offs += static_cast<rec_offs>(len);
} else {
len = offs += field->fixed_len;
}
} while (field++, rec_offs_base(offsets)[++i] = len,
i < rec_offs_n_fields(offsets));
switch (UNIV_EXPECT(status, REC_STATUS_ORDINARY)) { case REC_STATUS_INFIMUM: case REC_STATUS_SUPREMUM: /* the field is 8 bytes long */
rec_offs_base(offsets)[0]
= REC_N_NEW_EXTRA_BYTES | REC_OFFS_COMPACT;
rec_offs_base(offsets)[1] = 8; return; case REC_STATUS_NODE_PTR:
ut_ad(!n_core);
n_node_ptr_field
= dict_index_get_n_unique_in_tree_nonleaf(
index); break; case REC_STATUS_INSTANT:
ut_ad(index->is_instant());
rec_init_offsets_comp_ordinary(rec, index, offsets,
n_core,
NULL,
REC_LEAF_INSTANT); return; case REC_STATUS_ORDINARY:
rec_init_offsets_comp_ordinary(rec, index, offsets,
n_core,
NULL,
REC_LEAF_ORDINARY); return;
}
/* The n_nullable flags in the clustered index node pointer recordsinROW_FORMAT=COMPACTorROW_FORMAT=DYNAMICmust reflectthenumberof'corecolumns'.Theseflagsare uselessgarbage,andtheyareonlyreservedbecauseof fileformatcompatibility. (Clusteredindexnodepointerrecordsonlycontainthe PRIMARYKEYcolumns,whicharealwaysNOTNULL,
so we should have used n_nullable=0.) */
ut_ad(index->n_core_fields > 0);
/* read the lengths of fields 0..n */ do {
rec_offs len; if (UNIV_UNLIKELY(i == n_node_ptr_field)) {
len = offs += REC_NODE_PTR_SIZE; goto resolved;
}
field = dict_index_get_nth_field(index, i); if (!(dict_field_get_col(field)->prtype
& DATA_NOT_NULL)) { /* nullable field => read the null flag */
if (UNIV_UNLIKELY(!(byte) null_mask)) {
nulls--;
null_mask = 1;
}
if (*nulls & null_mask) {
null_mask <<= 1; /* No length is stored for NULL fields. Wedonotadvanceoffs,andweset thelengthtozeroandenablethe
SQL NULL flag in offsets[]. */
len = combine(offs, SQL_NULL); goto resolved;
}
null_mask <<= 1;
}
if (UNIV_UNLIKELY(!field->fixed_len)) { const dict_col_t* col
= dict_field_get_col(field); /* Variable-length field: read the length */
len = *lens--; /* If the maximum length of the field isupto255bytes,theactuallength isalwaysstoredinonebyte.Ifthe maximumlengthismorethan255bytes, theactuallengthisstoredinone bytefor0..127.Thelengthwillbe encodedintwobyteswhenitis128or more,orwhenthefieldisstored
externally. */ if (UNIV_UNLIKELY(len & 0x80)
&& DATA_BIG_COL(col)) { /* 1exxxxxxx xxxxxxxx */
len <<= 8;
len |= *lens--;
/* B-tree node pointers mustnotcontainexternally storedcolumns.Thus
the "e" flag must be 0. */
ut_a(!(len & 0x4000));
offs += len & 0x3fff;
len = offs; goto resolved;
}
len = offs += len;
} else {
len = offs += field->fixed_len;
}
resolved:
rec_offs_base(offsets)[i + 1] = len;
} while (++i < rec_offs_n_fields(offsets));
*rec_offs_base(offsets)
= static_cast<rec_offs>((rec - (lens + 1))
| REC_OFFS_COMPACT);
} else { /* Old-style record: determine extra size and end offsets */
offs = REC_N_OLD_EXTRA_BYTES; const ulint n_fields = rec_get_n_fields_old(rec); const ulint n = std::min(n_fields, rec_offs_n_fields(offsets));
rec_offs any;
if (rec_get_1byte_offs_flag(rec)) {
offs += static_cast<rec_offs>(n_fields);
any = offs; /* Determine offsets to fields */ do {
offs = rec_1_get_field_end_info(rec, i); if (offs & REC_1BYTE_SQL_NULL_MASK) {
offs ^= REC_1BYTE_SQL_NULL_MASK
| SQL_NULL;
}
rec_offs_base(offsets)[1 + i] = offs;
} while (++i < n);
} else {
offs += static_cast<rec_offs>(2 * n_fields);
any = offs; /* Determine offsets to fields */ do {
offs = rec_2_get_field_end_info(rec, i);
static_assert(REC_2BYTE_SQL_NULL_MASK
== SQL_NULL, "");
static_assert(REC_2BYTE_EXTERN_MASK
== STORED_OFFPAGE, "");
static_assert(REC_OFFS_EXTERNAL
== STORED_OFFPAGE, "");
any |= (offs & REC_OFFS_EXTERNAL);
rec_offs_base(offsets)[1 + i] = offs;
} while (++i < n);
}
if (i < rec_offs_n_fields(offsets)) {
ut_ad(index->is_instant()
|| i + (index->id == DICT_INDEXES_ID)
== rec_offs_n_fields(offsets));
do {
rec_offs_base(offsets)[1 + i] = offs;
} while (++i < rec_offs_n_fields(offsets));
any |= REC_OFFS_DEFAULT;
}
*rec_offs_base(offsets) = any;
}
}
/** Determine the offsets to each field in an index record. @param[in]recphysicalrecord @param[in]indextheindexthattherecordbelongsto @param[in,out]offsetsarraycomprisingoffsets[0]allocatedelements, oranarrayfromrec_get_offsets(),orNULL @param[in]n_core0,orindex->n_core_fieldsforleafpage @param[in]n_fieldsmaximumnumberofoffsetstocompute (ULINT_UNDEFINEDtocomputealloffsets) @param[in,out]heapmemoryheap
@return the new offsets */
rec_offs*
rec_get_offsets_func( const rec_t* rec, const dict_index_t* index,
rec_offs* offsets,
ulint n_core,
ulint n_fields, #ifdef UNIV_DEBUG constchar* file, /*!< in: file name where called */ unsigned line, /*!< in: line number where called */ #endif/* UNIV_DEBUG */
mem_heap_t** heap) /*!< in/out: memory heap */
{
ulint n;
ulint size; bool alter_metadata = false;
ut_ad(index->n_core_fields >= n_core); /* This assertion was relaxed for the btr_cur_t::open_leaf() callinbtr_cur_instant_init_low().Wecannotinvoke index->is_instant(),becausethesameassertionwouldfailthere untilbtr_cur_instant_init_low()hasinvoked
dict_table_t::deserialise_columns(). */
ut_ad(index->n_fields >= index->n_core_fields
|| index->in_instant_init);
if (dict_table_is_comp(index->table)) { switch (UNIV_EXPECT(rec_get_status(rec),
REC_STATUS_ORDINARY)) { case REC_STATUS_INSTANT:
alter_metadata = rec_is_alter_metadata(rec, true); /* fall through */ case REC_STATUS_ORDINARY:
ut_ad(n_core);
n = dict_index_get_n_fields(index) + alter_metadata; break; case REC_STATUS_NODE_PTR: /* Node pointer records consist of the uniquelyidentifyingfieldsoftherecord
followed by a child page number field. */
ut_ad(!n_core);
n = dict_index_get_n_unique_in_tree_nonleaf(index) + 1; break; default:
ut_ad("corrupted record header" == 0); /* fall through */ case REC_STATUS_INFIMUM: case REC_STATUS_SUPREMUM: /* infimum or supremum record */
ut_ad(rec_get_heap_no_new(rec)
== ulint(rec_get_status(rec)
== REC_STATUS_INFIMUM
? PAGE_HEAP_NO_INFIMUM
: PAGE_HEAP_NO_SUPREMUM));
n = 1; break;
}
} else {
n = rec_get_n_fields_old(rec); /* Here, rec can be allocated from the heap (copied fromanindexpagerecord),oritcanbelocatedinan indexpage.Ifrecisnotinanindexpage,then page_rec_is_user_rec(rec)andsimilarpredicates cannotbeevaluated.Wecanstilldistinguishthe
infimum and supremum record based on the heap number. */ constbool is_user_rec = rec_get_heap_no_old(rec)
>= PAGE_HEAP_NO_USER_LOW; /* The infimum and supremum records carry 1 field. */
ut_ad(is_user_rec || n == 1);
ut_ad(!is_user_rec || n_core || index->is_dummy
|| n == n_fields /* dict_stats_analyze_index_level() */
|| n - 1
== dict_index_get_n_unique_in_tree_nonleaf(index));
ut_ad(!is_user_rec || !n_core || index->is_dummy
|| n == n_fields /* btr_pcur_restore_position() */
|| (n + (index->id == DICT_INDEXES_ID) >= n_core));
if (is_user_rec && n_core && n < index->n_fields) {
ut_ad(!index->is_dummy);
n = index->n_fields;
}
}
if (UNIV_UNLIKELY(n_fields < n)) {
n = n_fields;
}
/* The offsets header consists of the allocation size at
offsets[0] and the REC_OFFS_HEADER_SIZE bytes. */
size = n + (1 + REC_OFFS_HEADER_SIZE);
/******************************************************//**
The following function determines the offsets to each field
in the record. It can reuse a previously allocated array. */ void
rec_get_offsets_reverse( /*====================*/ const byte* extra, /*!< in: the extra bytes of a compactrecordinreverseorder, excludingthefixed-size
REC_N_NEW_EXTRA_BYTES */ const dict_index_t* index, /*!< in: record descriptor */
ulint node_ptr,/*!< in: nonzero=node pointer,
0=leaf node */
rec_offs* offsets)/*!< in/out: array consisting of
offsets[0] allocated elements */
{
ulint n;
ulint i;
rec_offs offs;
rec_offs any_ext = 0; const byte* nulls; const byte* lens;
dict_field_t* field;
ulint null_mask;
ulint n_node_ptr_field;
/* read the lengths of fields 0..n */ do {
rec_offs len; if (UNIV_UNLIKELY(i == n_node_ptr_field)) {
len = offs += REC_NODE_PTR_SIZE; goto resolved;
}
field = dict_index_get_nth_field(index, i); if (!(dict_field_get_col(field)->prtype & DATA_NOT_NULL)) { /* nullable field => read the null flag */
if (UNIV_UNLIKELY(!(byte) null_mask)) {
nulls++;
null_mask = 1;
}
if (*nulls & null_mask) {
null_mask <<= 1; /* No length is stored for NULL fields. Wedonotadvanceoffs,andweset thelengthtozeroandenablethe
SQL NULL flag in offsets[]. */
len = combine(offs, SQL_NULL); goto resolved;
}
null_mask <<= 1;
}
if (UNIV_UNLIKELY(!field->fixed_len)) { /* Variable-length field: read the length */ const dict_col_t* col
= dict_field_get_col(field);
len = *lens++; /* If the maximum length of the field is up to255bytes,theactuallengthisalways storedinonebyte.Ifthemaximumlengthis morethan255bytes,theactuallengthis storedinonebytefor0..127.Thelength willbeencodedintwobyteswhenitis128or
more, or when the field is stored externally. */ if (UNIV_UNLIKELY(len & 0x80) && DATA_BIG_COL(col)) { /* 1exxxxxxx xxxxxxxx */
len &= 0x7f;
len <<= 8;
len |= *lens++;
static_assert(STORED_OFFPAGE == 0x4000, "");
static_assert(REC_OFFS_EXTERNAL == 0x4000, "");
rec_offs ext = len & REC_OFFS_EXTERNAL;
offs += get_value(len);
len = offs | ext;
any_ext |= ext; goto resolved;
}
len = offs += len;
} else {
len = offs += field->fixed_len;
}
resolved:
rec_offs_base(offsets)[i + 1] = len;
} while (++i < rec_offs_n_fields(offsets));
/************************************************************//**
The following function is used to get the offset to the nth
data field in an old-style record.
@return offset to the field */
ulint
rec_get_nth_field_offs_old( /*=======================*/ const rec_t* rec, /*!< in: record */
ulint n, /*!< in: index of the field */
ulint* len) /*!< out: length of the field;
UNIV_SQL_NULL if SQL null */
{
ulint os;
ulint next_os;
ut_a(n < rec_get_n_fields_old(rec));
if (rec_get_1byte_offs_flag(rec)) {
os = rec_1_get_field_start_offs(rec, n);
next_os = rec_1_get_field_end_info(rec, n);
if (next_os & REC_1BYTE_SQL_NULL_MASK) {
*len = UNIV_SQL_NULL;
return(os);
}
next_os &= ~REC_1BYTE_SQL_NULL_MASK;
} else {
os = rec_2_get_field_start_offs(rec, n);
next_os = rec_2_get_field_end_info(rec, n);
if (next_os & REC_2BYTE_SQL_NULL_MASK) {
*len = UNIV_SQL_NULL;
if (temp && dict_table_is_comp(index->table)) { /* No need to do adjust fixed_len=0. We only need to
adjust it for ROW_FORMAT=REDUNDANT. */
temp = false;
}
const dfield_t* const end = dfield + n_fields; /* read the lengths of fields 0..n */ for (ulint i = 0; dfield < end; i++, dfield++) { if (mblob && i == index->first_user_field()) {
data_size += FIELD_REF_SIZE; if (++dfield == end) {
ut_ad(i == index->n_fields); break;
}
}
ulint fixed_len = field->fixed_len; if (temp && fixed_len
&& !dict_col_get_fixed_size(field->col, temp)) {
fixed_len = 0;
} /* If the maximum length of a variable-length field isupto255bytes,theactuallengthisalwaysstored inonebyte.Ifthemaximumlengthismorethan255 bytes,theactuallengthisstoredinonebytefor 0..127.Thelengthwillbeencodedintwobyteswhen
it is 128 or more, or when the field is stored externally. */
if (fixed_len) { #ifdef UNIV_DEBUG
ut_ad(len <= fixed_len);
/* dict_index_add_col() should guarantee this */
ut_ad(!field->prefix_len
|| fixed_len == field->prefix_len); #endif/* UNIV_DEBUG */
} elseif (dfield_is_ext(dfield)) {
ut_ad(DATA_BIG_COL(field->col));
extra_size += 2;
} elseif (UNIV_LIKELY(len < 128)
|| !DATA_BIG_COL(field->col)) {
extra_size++;
} else { /* For variable-length columns, we look up the maximumlengthfromthecolumnitself.Ifthis isaprefixindexcolumnshorterthan256bytes,
this will waste one byte. */
extra_size += 2;
}
data_size += len;
}
if (extra) {
*extra = extra_size;
}
return(extra_size + data_size);
}
/**********************************************************//**
Determines the size of a data tuple prefix in ROW_FORMAT=COMPACT.
@return total size */
ulint
rec_get_converted_size_comp_prefix( /*===============================*/ const dict_index_t* index, /*!< in: record descriptor */ const dfield_t* fields, /*!< in: array of data fields */
ulint n_fields,/*!< in: number of data fields */
ulint* extra) /*!< out: extra size */
{
ut_ad(dict_table_is_comp(index->table)); return(rec_get_converted_size_comp_prefix_low(
index, fields, n_fields, extra,
REC_STATUS_ORDINARY, false));
}
/** Determine the size of a record in ROW_FORMAT=COMPACT. @param[in]indexrecorddescriptor.dict_table_is_comp() isassumedtohold,evenifitdoesn't @param[in]tuplelogicalrecord @param[out]extraextrasize
@return total size */
ulint
rec_get_converted_size_comp( const dict_index_t* index, const dtuple_t* tuple,
ulint* extra)
{
ut_ad(tuple->n_fields > 0);
rec_comp_status_t status = rec_comp_status_t(tuple->info_bits
& REC_NEW_STATUS_MASK);
switch (UNIV_EXPECT(status, REC_STATUS_ORDINARY)) { case REC_STATUS_ORDINARY:
ut_ad(!tuple->is_metadata()); if (tuple->n_fields > index->n_core_fields) {
ut_ad(index->is_instant());
status = REC_STATUS_INSTANT;
} /* fall through */ case REC_STATUS_INSTANT:
ut_ad(tuple->n_fields >= index->n_core_fields); if (tuple->is_alter_metadata()) { return rec_get_converted_size_comp_prefix_low<true>(
index, tuple->fields, tuple->n_fields,
extra, status, false);
}
ut_ad(tuple->n_fields <= index->n_fields); return rec_get_converted_size_comp_prefix_low(
index, tuple->fields, tuple->n_fields,
extra, status, false); case REC_STATUS_NODE_PTR:
ut_ad(tuple->n_fields - 1
== dict_index_get_n_unique_in_tree_nonleaf(index));
ut_ad(dfield_get_len(&tuple->fields[tuple->n_fields - 1])
== REC_NODE_PTR_SIZE); return REC_NODE_PTR_SIZE /* child page number */
+ rec_get_converted_size_comp_prefix_low(
index, tuple->fields, tuple->n_fields - 1,
extra, status, false); case REC_STATUS_INFIMUM: case REC_STATUS_SUPREMUM: /* not supported */ break;
}
ut_error; return(ULINT_UNDEFINED);
}
/*********************************************************//**
Builds an old-style physical record out of a data tuple and
stores it beginning from the start of the given buffer.
@return pointer to the origin of physical record */ static
rec_t*
rec_convert_dtuple_to_rec_old( /*==========================*/
byte* buf, /*!< in: start address of the physical record */ const dtuple_t* dtuple, /*!< in: data tuple */
ulint n_ext) /*!< in: number of externally stored columns */
{ const dfield_t* field;
ulint n_fields;
ulint data_size;
rec_t* rec;
ulint end_offset;
ulint ored_offset;
ulint len;
ulint i;
/* Calculate the offset of the origin in the physical record */
rec = buf + rec_get_converted_extra_size(data_size, n_fields, n_ext); /* Store the number of fields */
rec_set_n_fields_old(rec, n_fields);
/* Set the info bits of the record */
rec_set_bit_field_1(rec,
dtuple_get_info_bits(dtuple) & REC_INFO_BITS_MASK,
REC_OLD_INFO_BITS,
REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT);
rec_set_bit_field_2(rec, PAGE_HEAP_NO_USER_LOW, REC_OLD_HEAP_NO,
REC_HEAP_NO_MASK, REC_HEAP_NO_SHIFT);
/* Store the data and the offsets */
end_offset = 0;
if (!n_ext && data_size <= REC_1BYTE_OFFS_LIMIT) {
rec_set_1byte_offs_flag(rec, TRUE);
for (i = 0; i < n_fields; i++) {
field = dtuple_get_nth_field(dtuple, i);
if (dfield_is_null(field)) {
len = dtype_get_sql_null_size(
dfield_get_type(field), 0);
data_write_sql_null(rec + end_offset, len);
end_offset += len;
ored_offset = end_offset
| REC_1BYTE_SQL_NULL_MASK;
} else { /* If the data is not SQL null, store it */
len = dfield_get_len(field);
if (len)
memcpy(rec + end_offset,
dfield_get_data(field), len);
end_offset += len;
ored_offset = end_offset;
}
rec_1_set_field_end_info(rec, i, ored_offset);
}
} else {
rec_set_1byte_offs_flag(rec, FALSE);
for (i = 0; i < n_fields; i++) {
field = dtuple_get_nth_field(dtuple, i);
if (dfield_is_null(field)) {
len = dtype_get_sql_null_size(
dfield_get_type(field), 0);
data_write_sql_null(rec + end_offset, len);
end_offset += len;
ored_offset = end_offset
| REC_2BYTE_SQL_NULL_MASK;
} else { /* If the data is not SQL null, store it */
len = dfield_get_len(field);
if (len)
memcpy(rec + end_offset,
dfield_get_data(field), len);
end_offset += len;
ored_offset = end_offset;
if (dfield_is_ext(field)) {
ored_offset |= REC_2BYTE_EXTERN_MASK;
}
}
rec_2_set_field_end_info(rec, i, ored_offset);
}
}
start:
end = rec; /* clear the SQL-null flags */
memset(lens + 1, 0, ulint(nulls - lens));
const dfield_t* const fend = field + n_fields; /* Store the data and the offsets */ for (ulint i = 0; field < fend; i++, field++) {
ulint len = dfield_get_len(field);
if (mblob) { if (i == index->first_user_field()) {
ut_ad(len == FIELD_REF_SIZE);
ut_ad(dfield_is_ext(field));
memcpy(end, dfield_get_data(field), len);
end += len; if (++field == fend) {
ut_ad(i == index->n_fields); break;
}
len = dfield_get_len(field);
}
} elseif (UNIV_UNLIKELY(i == n_node_ptr_field)) {
ut_ad(field->type.prtype & DATA_NOT_NULL);
ut_ad(len == REC_NODE_PTR_SIZE);
memcpy(end, dfield_get_data(field), len);
end += REC_NODE_PTR_SIZE; break;
}
if (!(field->type.prtype & DATA_NOT_NULL)) { /* nullable field */
ut_ad(n_null--);
if (UNIV_UNLIKELY(!(byte) null_mask)) {
nulls--;
null_mask = 1;
}
ut_ad(*nulls < null_mask);
/* set the null flag if necessary */ if (dfield_is_null(field)) {
*nulls |= static_cast<byte>(null_mask);
null_mask <<= 1; continue;
}
null_mask <<= 1;
} /* only nullable fields can be null */
ut_ad(!dfield_is_null(field));
/* If the maximum length of a variable-length field isupto255bytes,theactuallengthisalwaysstored inonebyte.Ifthemaximumlengthismorethan255 bytes,theactuallengthisstoredinonebytefor 0..127.Thelengthwillbeencodedintwobyteswhen
it is 128 or more, or when the field is stored externally. */ if (fixed_len) {
ut_ad(len <= fixed_len);
ut_ad(!ifield->col->mbmaxlen
|| len >= ifield->col->mbminlen
* fixed_len / ifield->col->mbmaxlen);
ut_ad(!dfield_is_ext(field));
} elseif (dfield_is_ext(field)) {
ut_ad(DATA_BIG_COL(ifield->col));
ut_ad(len <= REC_ANTELOPE_MAX_INDEX_COL_LEN
+ BTR_EXTERN_FIELD_REF_SIZE);
*lens-- = static_cast<byte>(len >> 8 | 0xc0);
*lens-- = static_cast<byte>(len);
} else {
ut_ad(len <= field->type.len
|| DATA_LARGE_MTYPE(field->type.mtype)
|| !strcmp(index->name,
FTS_INDEX_TABLE_IND_NAME)); if (len < 128 || !DATA_BIG_LEN_MTYPE(
field->type.len, field->type.mtype)) {
*lens-- = static_cast<byte>(len);
} else {
ut_ad(len < 16384);
*lens-- = static_cast<byte>(len >> 8 | 0x80);
*lens-- = static_cast<byte>(len);
}
}
if (len) {
memcpy(end, dfield_get_data(field), len);
end += len;
}
}
}
/*********************************************************//**
Builds a new-style physical record out of a data tuple and
stores it beginning from the start of the given buffer.
@return pointer to the origin of physical record */ static
rec_t*
rec_convert_dtuple_to_rec_new( /*==========================*/
byte* buf, /*!< in: start address of
the physical record */ const dict_index_t* index, /*!< in: record descriptor */ const dtuple_t* dtuple) /*!< in: data tuple */
{
ut_ad(!(dtuple->info_bits
& ~(REC_NEW_STATUS_MASK | REC_INFO_DELETED_FLAG
| REC_INFO_MIN_REC_FLAG)));
/*********************************************************//**
Builds a physical record out of a data tuple and
stores it beginning from the start of the given buffer.
@return pointer to the origin of physical record */
rec_t*
rec_convert_dtuple_to_rec( /*======================*/
byte* buf, /*!< in: start address of the
physical record */ const dict_index_t* index, /*!< in: record descriptor */ const dtuple_t* dtuple, /*!< in: data tuple */
ulint n_ext) /*!< in: number of
externally stored columns */
{
rec_t* rec;
/** Determine the offset to each field in temporary file. @param[in]rectemporaryfilerecord @param[in]indexindexofthattherecordbelongsto @param[in,out]offsetsoffsetstothefields;in:rec_offs_n_fields(offsets) @param[in]n_corenumberofcorefields(index->n_core_fields) @param[in]def_valdefaultvaluesfornon-corefields
@param[in] status REC_STATUS_ORDINARY or REC_STATUS_INSTANT */ void
rec_init_offsets_temp( const rec_t* rec, const dict_index_t* index,
rec_offs* offsets,
ulint n_core, const dict_col_t::def_t*def_val,
rec_comp_status_t status)
{
ut_ad(status == REC_STATUS_ORDINARY
|| status == REC_STATUS_INSTANT); /* The table may have been converted to plain format
if it was emptied during an ALTER TABLE operation. */
ut_ad(index->n_core_fields == n_core || !index->is_instant());
ut_ad(index->n_core_fields >= n_core); if (index->table->not_redundant()) {
rec_init_offsets_comp_ordinary(
rec, index, offsets, n_core, def_val,
status == REC_STATUS_INSTANT
? REC_LEAF_TEMP_INSTANT
: REC_LEAF_TEMP);
} else {
rec_init_offsets_comp_ordinary<false, true>(
rec, index, offsets, n_core, def_val,
status == REC_STATUS_INSTANT
? REC_LEAF_TEMP_INSTANT
: REC_LEAF_TEMP);
}
}
/** Determine the offset to each field in temporary file. @param[in]rectemporaryfilerecord @param[in]indexindexofthattherecordbelongsto @param[in,out]offsetsoffsetstothefields;in:rec_offs_n_fields(offsets)
*/ void
rec_init_offsets_temp( const rec_t* rec, const dict_index_t* index,
rec_offs* offsets)
{
ut_ad(!index->is_instant()); if (index->table->not_redundant()) {
rec_init_offsets_comp_ordinary(
rec, index, offsets,
index->n_core_fields, NULL, REC_LEAF_TEMP);
} else {
rec_init_offsets_comp_ordinary<false, true>(
rec, index, offsets,
index->n_core_fields, NULL, REC_LEAF_TEMP);
}
}
/** Copy the first n fields of a (copy of a) physical record to a data tuple. Thefieldsarecopiedintothememoryheap. @param[out]tupledatatuple @param[in]recindexrecord,oracopythereof @param[in]indexindexofrec @param[in]n_coreindex->n_core_fieldsatthetimerecwas copied,or0ifnon-leafpagerecord @param[in]n_fieldsnumberoffieldstocopy
@param[in,out] heap memory heap */ void
rec_copy_prefix_to_dtuple(
dtuple_t* tuple, const rec_t* rec, const dict_index_t* index,
ulint n_core,
ulint n_fields,
mem_heap_t* heap)
{
rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
rec_offs* offsets = offsets_;
rec_offs_init(offsets_);
/**************************************************************//**
Copies the first n fields of an old-style physical record
to a new physical record in a buffer.
@return own: copied record */ static
rec_t*
rec_copy_prefix_to_buf_old( /*=======================*/ const rec_t* rec, /*!< in: physical record */
ulint n_fields, /*!< in: number of fields to copy */
ulint area_end, /*!< in: end of the prefix data */
byte** buf, /*!< in/out: memory buffer for
the copied prefix, or NULL */
ulint* buf_size) /*!< in/out: buffer size */
{
rec_t* copy_rec;
ulint area_start;
ulint prefix_len;
/**************************************************************//**
Copies the first n fields of a physical record to a new physical record in
a buffer.
@return own: copied record */
rec_t*
rec_copy_prefix_to_buf( /*===================*/ const rec_t* rec, /*!< in: physical record */ const dict_index_t* index, /*!< in: record descriptor */
ulint n_fields, /*!< in: number of fields
to copy */
byte** buf, /*!< in/out: memory buffer forthecopiedprefix,
or NULL */
ulint* buf_size) /*!< in/out: buffer size */
{
ut_ad(n_fields <= index->n_fields);
ut_ad(index->n_core_null_bytes <= UT_BITS_IN_BYTES(index->n_nullable));
UNIV_PREFETCH_RW(*buf);
switch (rec_get_status(rec)) { default: /* infimum or supremum record: no sense to copy anything */
ut_error; return(NULL); case REC_STATUS_ORDINARY:
ut_ad(n_fields <= index->n_core_fields); break; case REC_STATUS_NODE_PTR: /* For R-tree, we need to copy the child page number field. */
compile_time_assert(DICT_INDEX_SPATIAL_NODEPTR_SIZE == 1); if (dict_index_is_spatial(index)) {
ut_ad(index->n_core_null_bytes == 0);
ut_ad(n_fields == DICT_INDEX_SPATIAL_NODEPTR_SIZE + 1);
ut_ad(index->fields[0].col->prtype & DATA_NOT_NULL);
ut_ad(DATA_BIG_COL(index->fields[0].col)); /* This is a deficiency of the format introduced inMySQL5.7.ThelengthintheR-treeindexshould
always be DATA_MBR_LEN. */
ut_ad(!index->fields[0].fixed_len);
ut_ad(*lens == DATA_MBR_LEN);
lens--;
prefix_len = DATA_MBR_LEN + REC_NODE_PTR_SIZE;
n_fields = 0; /* skip the "for" loop below */ break;
} /* it doesn't make sense to copy the child page number field */
ut_ad(n_fields
<= dict_index_get_n_unique_in_tree_nonleaf(index)); break; case REC_STATUS_INSTANT: /* We would have !index->is_instant() when rolling back
an instant ADD COLUMN operation. */
ut_ad(index->is_instant() || page_rec_is_metadata(rec));
ut_ad(n_fields <= index->first_user_field());
nulls++; const ulint n_rec = ulint(index->n_core_fields) + 1
+ rec_get_n_add_field(nulls)
- rec_is_alter_metadata(rec, true);
instant_omit = ulint(&rec[-REC_N_NEW_EXTRA_BYTES] - nulls);
ut_ad(instant_omit == 1 || instant_omit == 2);
nullf = nulls; const uint nb = UT_BITS_IN_BYTES(index->get_n_nullable(n_rec));
instant_omit += nb - index->n_core_null_bytes;
lens = --nulls - nb;
}
/* read the lengths of fields 0..n */ for (ulint i = 0, null_mask = 1; i < n_fields; i++) { const dict_field_t* field; const dict_col_t* col;
field = dict_index_get_nth_field(index, i);
col = dict_field_get_col(field);
if (!(col->prtype & DATA_NOT_NULL)) { /* nullable field => read the null flag */ if (UNIV_UNLIKELY(!(byte) null_mask)) {
nulls--;
null_mask = 1;
}
if (*nulls & null_mask) {
null_mask <<= 1; continue;
}
null_mask <<= 1;
}
if (field->fixed_len) {
prefix_len += field->fixed_len;
} else {
ulint len = *lens--; /* If the maximum length of the column is up to255bytes,theactuallengthisalways storedinonebyte.Ifthemaximumlengthis morethan255bytes,theactuallengthis storedinonebytefor0..127.Thelength willbeencodedintwobyteswhenitis128or
more, or when the column is stored externally. */ if (UNIV_UNLIKELY(len & 0x80) && DATA_BIG_COL(col)) { /* 1exxxxxx */
len &= 0x3f;
len <<= 8;
len |= *lens--;
UNIV_PREFETCH_R(lens);
}
prefix_len += len;
}
}
if (len_sum != rec_get_data_size_old(rec)) {
ib::error() << "Record len should be " << len_sum << ", len "
<< rec_get_data_size_old(rec); return(FALSE);
}
return(TRUE);
}
/***************************************************************//**
Validates the consistency of a physical record.
@returnTRUEif ok */
ibool
rec_validate( /*=========*/ const rec_t* rec, /*!< in: physical record */ const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */
{
ulint len;
ulint n_fields;
ulint len_sum = 0;
ulint i;
for (i = 0; i < n_fields; i++) {
rec_get_nth_field_offs(offsets, i, &len);
switch (len) { default: if (len >= srv_page_size) {
ib::error() << "Record field " << i
<< " len " << len; return(FALSE);
}
len_sum += len; break; case UNIV_SQL_DEFAULT: break; case UNIV_SQL_NULL: if (!rec_offs_comp(offsets)) {
len_sum += rec_get_nth_field_size(rec, i);
}
}
}
if (len_sum != rec_offs_data_size(offsets)) {
ib::error() << "Record len should be " << len_sum << ", len "
<< rec_offs_data_size(offsets); return(FALSE);
}
if (!rec_offs_comp(offsets)) {
ut_a(rec_validate_old(rec));
}
return(TRUE);
}
/***************************************************************//**
Prints an old-style physical record. */ void
rec_print_old( /*==========*/
FILE* file, /*!< in: file where to print */ const rec_t* rec) /*!< in: physical record */
{ const byte* data;
ulint len;
ulint n;
ulint i;
/***************************************************************//**
Prints a physical record in ROW_FORMAT=COMPACT. Ignores the
record header. */ static void
rec_print_comp( /*===========*/
FILE* file, /*!< in: file where to print */ const rec_t* rec, /*!< in: physical record */ const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */
{
ulint i;
for (i = 0; i < rec_offs_n_fields(offsets); i++) { const byte* UNINIT_VAR(data);
ulint len;
if (rec_offs_nth_default(offsets, i)) {
len = UNIV_SQL_DEFAULT;
} else {
data = rec_get_nth_field(rec, offsets, i, &len);
}
/** Mark the nth field as externally stored. @param[in]offsetsarrayreturnedbyrec_get_offsets()
@param[in] n nth field */ void
rec_offs_make_nth_extern(
rec_offs* offsets, const ulint n)
{
ut_ad(!rec_offs_nth_sql_null(offsets, n));
set_type(rec_offs_base(offsets)[1 + n], STORED_OFFPAGE);
} #ifdef WITH_WSREP # include "ha_prototypes.h"
break;
} case DATA_VARCHAR: case DATA_VARMYSQL: case DATA_CHAR: case DATA_MYSQL:
len = wsrep_normalize_string(
(int)(col_f->prtype & DATA_MYSQL_TYPE_MASK),
dtype_get_charset_coll(col_f->prtype),
data, buf, len, *buf_len); break; case DATA_BLOB: case DATA_BINARY: case DATA_FIXBINARY: case DATA_GEOMETRY:
memcpy(buf, data, len); break;
case DATA_FLOAT:
{ float f = mach_float_read(data);
memcpy(buf, &f, sizeof(float));
} break; case DATA_DOUBLE:
{ double d = mach_double_read(data);
memcpy(buf, &d, sizeof(double));
} break; default: break;
}
key_len += len;
buf += len;
}
}
rec_validate(rec, offsets);
if (UNIV_LIKELY_NULL(heap)) {
mem_heap_free(heap);
}
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.88Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-08)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.