polardbxengine/storage/innobase/rem/rec.cc

467 lines
15 KiB
C++

/*****************************************************************************
Copyright (c) 1994, 2018, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License, version 2.0, as published by the
Free Software Foundation.
This program is also distributed with certain software (including but not
limited to OpenSSL) that is licensed under separate terms, as designated in a
particular file or component or in included license documentation. The authors
of MySQL hereby grant you an additional permission to link the program and
your derivative works with the separately licensed software that they have
included with MySQL.
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, version 2.0,
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 St, Fifth Floor, Boston, MA 02110-1301 USA
*****************************************************************************/
/** @file rem/rec.cc
Record manager
Created 5/30/1994 Heikki Tuuri
*************************************************************************/
/** NOTE: The functions in this file should only use functions from
other files in library. The code in this file is used to make a library for
external tools. */
#include <stddef.h>
#include "dict0dict.h"
#include "mem0mem.h"
#include "rem/rec.h"
#include "rem0rec.h"
/** The following function determines the offsets to each field in the
record. The offsets are written to a previously allocated array of
ulint, where rec_offs_n_fields(offsets) has been initialized to the
number of fields in the record. The rest of the array will be
initialized by this function. rec_offs_base(offsets)[0] will be set
to the extra size (if REC_OFFS_COMPACT is set, the record is in the
new format; if REC_OFFS_EXTERNAL is set, the record contains externally
stored columns), and rec_offs_base(offsets)[1..n_fields] will be set to
offsets past the end of fields 0..n_fields, or to the beginning of
fields 1..n_fields+1. When the high-order bit of the offset at [i+1]
is set (REC_OFFS_SQL_NULL), the field i is NULL. When the second
high-order bit of the offset at [i+1] is set (REC_OFFS_EXTERNAL), the
field i is being stored externally. */
void rec_init_offsets(const rec_t *rec, /*!< in: physical record */
const dict_index_t *index, /*!< in: record descriptor */
ulint *offsets) /*!< in/out: array of offsets;
in: n=rec_offs_n_fields(offsets) */
{
ulint i = 0;
ulint offs;
rec_offs_make_valid(rec, index, offsets);
if (dict_table_is_comp(index->table)) {
const byte *nulls;
const byte *lens;
dict_field_t *field;
ulint null_mask;
ulint status = rec_get_status(rec);
ulint n_node_ptr_field = ULINT_UNDEFINED;
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:
n_node_ptr_field = dict_index_get_n_unique_in_tree_nonleaf(index);
break;
case REC_STATUS_ORDINARY:
rec_init_offsets_comp_ordinary(rec, false, index, offsets);
return;
}
ut_ad(!rec_get_instant_flag_new(rec));
nulls = rec - (REC_N_NEW_EXTRA_BYTES + 1);
lens = nulls - UT_BITS_IN_BYTES(index->n_instant_nullable);
offs = 0;
null_mask = 1;
/* read the lengths of fields 0..n */
do {
ulint len;
if (UNIV_UNLIKELY(i == n_node_ptr_field)) {
len = offs += REC_NODE_PTR_SIZE;
goto resolved;
}
field = index->get_field(i);
if (!(field->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.
We do not advance offs, and we set
the length to zero and enable the
SQL NULL flag in offsets[]. */
len = offs | REC_OFFS_SQL_NULL;
goto resolved;
}
null_mask <<= 1;
}
if (UNIV_UNLIKELY(!field->fixed_len)) {
const dict_col_t *col = field->col;
/* DATA_POINT should always be a fixed
length column. */
ut_ad(col->mtype != DATA_POINT);
/* Variable-length field: read the length */
len = *lens--;
/* If the maximum length of the field
is up to 255 bytes, the actual length
is always stored in one byte. If the
maximum length is more than 255 bytes,
the actual length is stored in one
byte for 0..127. The length will be
encoded in two bytes when it is 128 or
more, or when the field is stored
externally. */
if (DATA_BIG_COL(col)) {
if (len & 0x80) {
/* 1exxxxxxx xxxxxxxx */
len <<= 8;
len |= *lens--;
/* B-tree node pointers
must not contain externally
stored columns. 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) = (rec - (lens + 1)) | REC_OFFS_COMPACT;
} else {
/* Old-style record: determine extra size and end offsets */
offs = REC_N_OLD_EXTRA_BYTES;
if (rec_get_1byte_offs_flag(rec)) {
offs += rec_get_n_fields_old_raw(rec);
*rec_offs_base(offsets) = offs;
/* Determine offsets to fields */
do {
if (index->has_instant_cols() && i >= rec_get_n_fields_old_raw(rec)) {
offs &= ~REC_OFFS_SQL_NULL;
offs = rec_get_instant_offset(index, i, offs);
} else {
offs = rec_1_get_field_end_info(rec, i);
}
if (offs & REC_1BYTE_SQL_NULL_MASK) {
offs &= ~REC_1BYTE_SQL_NULL_MASK;
offs |= REC_OFFS_SQL_NULL;
}
ut_ad(i < rec_get_n_fields_old_raw(rec) || (offs & REC_OFFS_SQL_NULL) ||
(offs & REC_OFFS_DEFAULT));
rec_offs_base(offsets)[1 + i] = offs;
} while (++i < rec_offs_n_fields(offsets));
} else {
offs += 2 * rec_get_n_fields_old_raw(rec);
*rec_offs_base(offsets) = offs;
/* Determine offsets to fields */
do {
if (index->has_instant_cols() && i >= rec_get_n_fields_old_raw(rec)) {
offs &= ~(REC_OFFS_SQL_NULL | REC_OFFS_EXTERNAL);
offs = rec_get_instant_offset(index, i, offs);
} else {
offs = rec_2_get_field_end_info(rec, i);
}
if (offs & REC_2BYTE_SQL_NULL_MASK) {
offs &= ~REC_2BYTE_SQL_NULL_MASK;
offs |= REC_OFFS_SQL_NULL;
}
if (offs & REC_2BYTE_EXTERN_MASK) {
offs &= ~REC_2BYTE_EXTERN_MASK;
offs |= REC_OFFS_EXTERNAL;
*rec_offs_base(offsets) |= REC_OFFS_EXTERNAL;
}
ut_ad(i < rec_get_n_fields_old_raw(rec) || (offs & REC_OFFS_SQL_NULL) ||
(offs & REC_OFFS_DEFAULT));
rec_offs_base(offsets)[1 + i] = offs;
} while (++i < rec_offs_n_fields(offsets));
}
}
}
/** The following function determines the offsets to each field
in the record. It can reuse a previously returned array.
Note that after instant ADD COLUMN, if this is a record
from clustered index, fields in the record may be less than
the fields defined in the clustered index. So the offsets
size is allocated according to the clustered index fields.
@return the new offsets */
ulint *rec_get_offsets_func(
const rec_t *rec, /*!< in: physical record */
const dict_index_t *index, /*!< in: record descriptor */
ulint *offsets, /*!< in/out: array consisting of
offsets[0] allocated elements,
or an array from rec_get_offsets(),
or NULL */
ulint n_fields, /*!< in: maximum number of
initialized fields
(ULINT_UNDEFINED if all fields) */
#ifdef UNIV_DEBUG
const char *file, /*!< in: file name where called */
ulint line, /*!< in: line number where called */
#endif /* UNIV_DEBUG */
mem_heap_t **heap) /*!< in/out: memory heap */
{
ulint n;
ulint size;
ut_ad(rec);
ut_ad(index);
ut_ad(heap);
if (dict_table_is_comp(index->table)) {
switch (UNIV_EXPECT(rec_get_status(rec), REC_STATUS_ORDINARY)) {
case REC_STATUS_ORDINARY:
n = dict_index_get_n_fields(index);
break;
case REC_STATUS_NODE_PTR:
/* Node pointer records consist of the
uniquely identifying fields of the record
followed by a child page number field. */
n = dict_index_get_n_unique_in_tree_nonleaf(index) + 1;
break;
case REC_STATUS_INFIMUM:
case REC_STATUS_SUPREMUM:
/* infimum or supremum record */
n = 1;
break;
default:
ut_error;
}
} else {
n = rec_get_n_fields_old(rec, index);
}
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);
if (UNIV_UNLIKELY(!offsets) ||
UNIV_UNLIKELY(rec_offs_get_n_alloc(offsets) < size)) {
if (UNIV_UNLIKELY(!*heap)) {
*heap = mem_heap_create_at(size * sizeof(ulint), file, line);
}
offsets = static_cast<ulint *>(mem_heap_alloc(*heap, size * sizeof(ulint)));
rec_offs_set_n_alloc(offsets, size);
}
rec_offs_set_n_fields(offsets, n);
rec_init_offsets(rec, index, offsets);
return (offsets);
}
/** 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
compact record in reverse order,
excluding the fixed-size
REC_N_NEW_EXTRA_BYTES */
const dict_index_t *index, /*!< in: record descriptor */
ulint node_ptr, /*!< in: nonzero=node pointer,
0=leaf node */
ulint *offsets) /*!< in/out: array consisting of
offsets[0] allocated elements */
{
ulint n;
ulint i;
ulint offs;
ulint any_ext;
const byte *nulls;
const byte *lens;
dict_field_t *field;
ulint null_mask;
ulint n_node_ptr_field;
ut_ad(extra);
ut_ad(index);
ut_ad(offsets);
ut_ad(dict_table_is_comp(index->table));
if (UNIV_UNLIKELY(node_ptr)) {
n_node_ptr_field = dict_index_get_n_unique_in_tree_nonleaf(index);
n = n_node_ptr_field + 1;
} else {
n_node_ptr_field = ULINT_UNDEFINED;
n = dict_index_get_n_fields(index);
}
ut_a(rec_offs_get_n_alloc(offsets) >= n + (1 + REC_OFFS_HEADER_SIZE));
rec_offs_set_n_fields(offsets, n);
nulls = extra;
lens = nulls + UT_BITS_IN_BYTES(index->n_nullable);
i = offs = 0;
null_mask = 1;
any_ext = 0;
/* read the lengths of fields 0..n */
do {
ulint len;
if (UNIV_UNLIKELY(i == n_node_ptr_field)) {
len = offs += REC_NODE_PTR_SIZE;
goto resolved;
}
field = index->get_field(i);
if (!(field->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.
We do not advance offs, and we set
the length to zero and enable the
SQL NULL flag in offsets[]. */
len = offs | REC_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 = field->col;
len = *lens++;
/* If the maximum length of the field is up
to 255 bytes, the actual length is always
stored in one byte. If the maximum length is
more than 255 bytes, the actual length is
stored in one byte for 0..127. The length
will be encoded in two bytes when it is 128 or
more, or when the field is stored externally. */
if (DATA_BIG_COL(col)) {
if (len & 0x80) {
/* 1exxxxxxx xxxxxxxx */
len <<= 8;
len |= *lens++;
offs += len & 0x3fff;
if (UNIV_UNLIKELY(len & 0x4000)) {
any_ext = REC_OFFS_EXTERNAL;
len = offs | REC_OFFS_EXTERNAL;
} else {
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));
ut_ad(lens >= extra);
*rec_offs_base(offsets) =
(lens - extra + REC_N_NEW_EXTRA_BYTES) | REC_OFFS_COMPACT | any_ext;
}
#ifdef UNIV_DEBUG
/** Check if the given two record offsets are identical.
@param[in] offsets1 field offsets of a record
@param[in] offsets2 field offsets of a record
@return true if they are identical, false otherwise. */
bool rec_offs_cmp(ulint *offsets1, ulint *offsets2) {
ulint n1 = rec_offs_n_fields(offsets1);
ulint n2 = rec_offs_n_fields(offsets2);
if (n1 != n2) {
return (false);
}
for (ulint i = 0; i < n1; ++i) {
ulint len_1;
ulint field_offset_1 = rec_get_nth_field_offs(offsets1, i, &len_1);
ulint len_2;
ulint field_offset_2 = rec_get_nth_field_offs(offsets2, i, &len_2);
if (field_offset_1 != field_offset_2) {
return (false);
}
if (len_1 != len_2) {
return (false);
}
}
return (true);
}
/** Print the record offsets.
@param[in] out the output stream to which offsets are printed.
@param[in] offsets the field offsets of the record.
@return the output stream. */
std::ostream &rec_offs_print(std::ostream &out, const ulint *offsets) {
ulint n = rec_offs_n_fields(offsets);
out << "[rec offsets: &offsets[0]=" << (void *)&offsets[0] << ", n=" << n
<< std::endl;
for (ulint i = 0; i < n; ++i) {
ulint len;
ulint field_offset = rec_get_nth_field_offs(offsets, i, &len);
out << "i=" << i << ", offsets[" << i << "]=" << offsets[i]
<< ", field_offset=" << field_offset << ", len=" << len << std::endl;
}
out << "]" << std::endl;
return (out);
}
#endif /* UNIV_DEBUG */