Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 0 additions & 34 deletions storage/innobase/include/row0sel.h
Original file line number Diff line number Diff line change
Expand Up @@ -420,40 +420,6 @@ enum row_sel_match_mode {
of a fixed length column) */
};

/** Stores a non-SQL-NULL field in the MySQL format. The counterpart of this
function is row_mysql_store_col_in_innobase_format() in row0mysql.cc.
@param[in,out] dest buffer where to store; NOTE
that BLOBs are not in themselves stored
here: the caller must allocate and copy
the BLOB into buffer before, and pass
the pointer to the BLOB in 'data'
@param[in] templ MySQL column template. Its following fields
are referenced: type, is_unsigned,
mysql_col_len, mbminlen, mbmaxlen
@param[in] index InnoDB index
@param[in] field_no templ->rec_field_no or templ->clust_rec_field_no
or templ->icp_rec_field_no
@param[in] data data to store
@param[in] len length of the data
@param[in] compress_heap memory heap used to compress/decompress
blob column
@param[in] sec_field secondary index field no if the secondary index
record but the prebuilt template is in
clustered index format and used only for end
range comparison. */
void row_sel_field_store_in_mysql_format_func(
byte *dest, const mysql_row_templ_t *templ, const dict_index_t *index,
IF_DEBUG(ulint field_no, ) const byte *data,
ulint len, mem_heap_t **compress_heap IF_DEBUG(, ulint sec_field));

/** Convert a non-SQL-NULL field from Innobase format to MySQL format. */
static inline void row_sel_field_store_in_mysql_format(
byte *dest, const mysql_row_templ_t *templ, const dict_index_t *idx,
ulint field, const byte *src, ulint len, mem_heap_t **compress_heap, ulint sec) {
row_sel_field_store_in_mysql_format_func(
dest, templ, idx, IF_DEBUG(field, ) src, len, compress_heap IF_DEBUG(, sec));
}

/** Search the record present in innodb_table_stats table using
db_name, table_name and fill it in table stats structure.
@param[in] db_name database name
Expand Down
240 changes: 240 additions & 0 deletions storage/innobase/include/row0sel.ic
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,243 @@ static inline dberr_t row_search_for_mysql(byte *buf, page_cur_mode_t mode,
return (row_search_no_mvcc(buf, mode, prebuilt, match_mode, direction));
}
}

/** Stores a non-SQL-NULL field in the MySQL format. The counterpart of this
function is row_mysql_store_col_in_innobase_format() in row0mysql.cc.
@param[in,out] dest buffer where to store; NOTE
that BLOBs are not in themselves stored
here: the caller must allocate and copy
the BLOB into buffer before, and pass
the pointer to the BLOB in 'data'
@param[in] templ MySQL column template. Its following fields
are referenced: type, is_unsigned,
mysql_col_len, mbminlen, mbmaxlen
@param[in] index InnoDB index
@param[in] field_no templ->rec_field_no or templ->clust_rec_field_no
or templ->icp_rec_field_no
@param[in] data data to store
@param[in] len length of the data
@param[in] compress_heap memory heap used to compress/decompress
blob column
@param[in] sec_field secondary index field no if the secondary index
record but the prebuilt template is in
clustered index format and used only for end
range comparison. */
ALWAYS_INLINE void row_sel_field_store_in_mysql_format_func(
byte *dest, const mysql_row_templ_t *templ, const dict_index_t *index,
IF_DEBUG(ulint field_no, ) const byte *data, ulint len,
mem_heap_t **compress_heap IF_DEBUG(, ulint sec_field)) {
byte *ptr;
#ifdef UNIV_DEBUG
const dict_field_t *field =
templ->is_virtual ? nullptr : index->get_field(field_no);

bool clust_templ_for_sec = (sec_field != ULINT_UNDEFINED);
#endif /* UNIV_DEBUG */

if (templ->is_multi_val) {
ib::fatal(UT_LOCATION_HERE, ER_CONVERT_MULTI_VALUE)
<< "Table name: " << index->table->name
<< " Index name: " << index->name;
}

auto const mysql_col_len = templ->mysql_col_len;

ut_ad(rec_field_not_null_not_add_col_def(len));
UNIV_MEM_ASSERT_RW(data, len);
UNIV_MEM_ASSERT_W(dest, mysql_col_len);
UNIV_MEM_INVALID(dest, mysql_col_len);

switch (templ->type) {
const byte *field_end;
byte *pad;
case DATA_INT:
/* Convert integer data from Innobase to a little-endian
format, sign bit restored to normal */

ptr = dest + len;

for (;;) {
ptr--;
*ptr = *data;
if (ptr == dest) {
break;
}
data++;
}

if (!templ->is_unsigned) {
dest[len - 1] = (byte)(dest[len - 1] ^ 128);
}

ut_ad(mysql_col_len == len);

break;

case DATA_VARCHAR:
case DATA_VARMYSQL:
case DATA_BINARY:
field_end = dest + mysql_col_len;

if (templ->mysql_type == DATA_MYSQL_TRUE_VARCHAR) {
/* If this is a compressed column,
decompress it first */
if (templ->compressed)
data = row_decompress_column(
data, &len,
reinterpret_cast<const byte *>(templ->zip_dict_data.str),
templ->zip_dict_data.length, compress_heap);

/* This is a >= 5.0.3 type true VARCHAR. Store the
length of the data to the first byte or the first
two bytes of dest. */

dest =
row_mysql_store_true_var_len(dest, len, templ->mysql_length_bytes);
/* Copy the actual data. Leave the rest of the
buffer uninitialized. */
memcpy(dest, data, len);
break;
}

/* Copy the actual data */
ut_memcpy(dest, data, len);

/* Pad with trailing spaces. */

pad = dest + len;

ut_ad(templ->mbminlen <= templ->mbmaxlen);

/* We treat some Unicode charset strings specially. */
switch (templ->mbminlen) {
case 4:
/* InnoDB should never have stripped partial
UTF-32 characters. */
ut_a(!(len & 3));
break;
case 2:
/* A space char is two bytes,
0x0020 in UCS2 and UTF-16 */

if (UNIV_UNLIKELY(len & 1)) {
/* A 0x20 has been stripped from the column.
Pad it back. */

if (pad < field_end) {
*pad++ = 0x20;
}
}
}

row_mysql_pad_col(templ->mbminlen, pad, field_end - pad);
break;

case DATA_BLOB:
/* Store a pointer to the BLOB buffer to dest: the BLOB was
already copied to the buffer in row_sel_store_mysql_rec */

row_mysql_store_blob_ref(
dest, mysql_col_len, data, len, templ->compressed,
reinterpret_cast<const byte *>(templ->zip_dict_data.str),
templ->zip_dict_data.length, compress_heap);
break;

case DATA_POINT:
case DATA_VAR_POINT:
case DATA_GEOMETRY:
/* We store all geometry data as BLOB data at server layer. */
row_mysql_store_geometry(dest, mysql_col_len, data, len);
break;

case DATA_MYSQL:
memcpy(dest, data, len);

ut_ad(mysql_col_len >= len);
ut_ad(templ->mbmaxlen >= templ->mbminlen);

/* If field_no equals to templ->icp_rec_field_no, we are examining a row
pointed by "icp_rec_field_no". There is possibility that icp_rec_field_no
refers to a field in a secondary index while templ->rec_field_no points
to field in a primary index. The length should still be equal, unless the
field pointed by icp_rec_field_no has a prefix or this is a virtual
column.
For end range condition check of secondary index with cluster index
template (clust_templ_for_sec), the index column data length (len)
could be smaller than the actual column length (mysql_col_len) if index
is on column prefix. This is not a real issue because the end range check
would only need the prefix part. The length check assert is relaxed for
clust_templ_for_sec. */
ut_ad(templ->is_virtual || templ->mbmaxlen > templ->mbminlen ||
mysql_col_len == len || clust_templ_for_sec ||
(field_no == templ->icp_rec_field_no && field->prefix_len > 0));

/* The following assertion would fail for old tables
containing UTF-8 ENUM columns due to Bug #9526. */
ut_ad(!templ->mbmaxlen || !(mysql_col_len % templ->mbmaxlen));
/* Length of the record will be less in case of
clust_templ_for_sec is true or if it is fetched
from prefix virtual column in virtual index. */
ut_ad(templ->is_virtual || clust_templ_for_sec ||
len * templ->mbmaxlen >= mysql_col_len ||
index->has_row_versions() ||
(field_no == templ->icp_rec_field_no && field->prefix_len > 0) ||
templ->rec_field_is_prefix);
ut_ad(templ->is_virtual || !(field->prefix_len % templ->mbmaxlen));

/* Pad with spaces. This undoes the stripping
done in row0mysql.cc, function
row_mysql_store_col_in_innobase_format(). */
if ((templ->mbminlen == 1 && templ->mbmaxlen != 1) ||
(templ->is_virtual && mysql_col_len > len)) {
/* NOTE: This comment is for the second condition:
This probably comes from a prefix virtual index, where no complete
value can be got because the full virtual column can only be
calculated in server layer for now. Since server now assumes the
returned value should always have padding spaces, thus the fixup.
However, a proper and more efficient solution is that server does
not depend on the trailing spaces to check the terminal of the CHAR
string, because at least in this case,server should know it's a prefix
index search and no complete value would be got. */
memset(dest + len, 0x20, mysql_col_len - len);
}
break;

default:
#ifdef UNIV_DEBUG
case DATA_SYS_CHILD:
case DATA_SYS:
/* These column types should never be shipped to MySQL. */
ut_d(ut_error);
[[fallthrough]];

case DATA_CHAR:
case DATA_FIXBINARY:
case DATA_FLOAT:
case DATA_DOUBLE:
case DATA_DECIMAL:
/* Above are the valid column types for MySQL data. */
#endif /* UNIV_DEBUG */

/* If sec_field value is present then mapping of
secondary index records to clustered index template
happens for end range comparison. So length can
vary according to secondary index record length. */
ut_ad((templ->is_virtual && !field) ||
((field && field->prefix_len)
? field->prefix_len == len
: (clust_templ_for_sec || mysql_col_len == len)));

memcpy(dest, data, len);
}
}

/** Convert a non-SQL-NULL field from Innobase format to MySQL format. */
ALWAYS_INLINE void row_sel_field_store_in_mysql_format(
byte *dest, const mysql_row_templ_t *templ, const dict_index_t *idx,
ulint field, const byte *src, ulint len, mem_heap_t **compress_heap,
ulint sec) {
row_sel_field_store_in_mysql_format_func(dest, templ, idx,
IF_DEBUG(field, ) src, len,
compress_heap IF_DEBUG(, sec));
}
5 changes: 3 additions & 2 deletions storage/innobase/rem/rec.cc
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ external tools. */
@param[in] rec physical record
@param[in] index record descriptor
@param[in, out] offsets array of offsets */
static void rec_init_offsets_new(const rec_t *rec, const dict_index_t *index,
ulint *offsets) {
static ALWAYS_INLINE void rec_init_offsets_new(const rec_t *rec,
const dict_index_t *index,
ulint *offsets) {
ulint status = rec_get_status(rec);
ulint n_node_ptr_field = ULINT_UNDEFINED;

Expand Down
Loading