MDEV-11828: innodb_page_size=64k must reject ROW_FORMAT=REDUNDANT records longer than 16383 bytes

In all InnoDB row formats, the pointers or lengths stored in the record
header can be at most 14 bits, that is, count up to 16383.
In ROW_FORMAT=REDUNDANT, this limits the maximum possible record length
to 16383 bytes. In other ROW_FORMAT, it could merely limit the maximum
length of variable-length fields.

When MySQL 5.7 introduced innodb_page_size=32k and 64k, the maximum
record length was limited to 16383 bytes (I hope 16383, not 16384,
to be able to distinguish from a record whose length is 0 bytes).
This change is present in MariaDB Server 10.2.

btr_cur_optimistic_update(): Restrict maximum record size to 16K-1
for REDUNDANT and 64K page size.

dict_index_too_big_for_tree(): The maximum allowed record size
is half a B-tree page or 16K(-1 for REDUNDANT) for 64K page size.

convert_error_code_to_mysql(): Fix error message to print
correct limits.

my_error_innodb(): Fix error message to print correct limits.

page_zip_rec_needs_ext() : record size was already restricted to 16K.
Restrict REDUNDANT to 16K-1.

rem0rec.h: Introduce REDUNDANT_REC_MAX_DATA_SIZE (16K-1)
and COMPRESSED_REC_MAX_DATA_SIZE (16K).
This commit is contained in:
Jan Lindström 2017-07-12 19:34:55 +03:00
commit 9284e8b2c6
24 changed files with 2170 additions and 198 deletions

View file

@ -1969,18 +1969,33 @@ convert_error_code_to_mysql(
case DB_TOO_BIG_RECORD: {
/* If prefix is true then a 768-byte prefix is stored
locally for BLOB fields. Refer to dict_table_get_format() */
locally for BLOB fields. Refer to dict_table_get_format().
Note that in page0zip.ic page_zip_rec_needs_ext() rec_size
is limited to COMPRESSED_REC_MAX_DATA_SIZE (16K) or
REDUNDANT_REC_MAX_DATA_SIZE (16K-1). */
bool prefix = (dict_tf_get_format(flags) == UNIV_FORMAT_A);
bool comp = !!(flags & DICT_TF_COMPACT);
ulint free_space = page_get_free_space_of_empty(comp) / 2;
if (free_space >= (comp ? COMPRESSED_REC_MAX_DATA_SIZE :
REDUNDANT_REC_MAX_DATA_SIZE)) {
free_space = (comp ? COMPRESSED_REC_MAX_DATA_SIZE :
REDUNDANT_REC_MAX_DATA_SIZE) - 1;
}
my_printf_error(ER_TOO_BIG_ROWSIZE,
"Row size too large (> %lu). Changing some columns "
"Row size too large (> " ULINTPF "). Changing some columns "
"to TEXT or BLOB %smay help. In current row "
"format, BLOB prefix of %d bytes is stored inline.",
MYF(0),
page_get_free_space_of_empty(flags &
DICT_TF_COMPACT) / 2,
prefix ? "or using ROW_FORMAT=DYNAMIC "
"or ROW_FORMAT=COMPRESSED ": "",
prefix ? DICT_MAX_FIXED_COL_LEN : 0);
free_space,
prefix
? "or using ROW_FORMAT=DYNAMIC or"
" ROW_FORMAT=COMPRESSED "
: "",
prefix
? DICT_MAX_FIXED_COL_LEN
: 0);
return(HA_ERR_TO_BIG_ROW);
}

View file

@ -153,11 +153,22 @@ my_error_innodb(
case DB_CORRUPTION:
my_error(ER_NOT_KEYFILE, MYF(0), table);
break;
case DB_TOO_BIG_RECORD:
my_error(ER_TOO_BIG_ROWSIZE, MYF(0),
page_get_free_space_of_empty(
flags & DICT_TF_COMPACT) / 2);
case DB_TOO_BIG_RECORD: {
/* Note that in page0zip.ic page_zip_rec_needs_ext() rec_size
is limited to COMPRESSED_REC_MAX_DATA_SIZE (16K) or
REDUNDANT_REC_MAX_DATA_SIZE (16K-1). */
bool comp = !!(flags & DICT_TF_COMPACT);
ulint free_space = page_get_free_space_of_empty(comp) / 2;
if (free_space >= (comp ? COMPRESSED_REC_MAX_DATA_SIZE :
REDUNDANT_REC_MAX_DATA_SIZE)) {
free_space = (comp ? COMPRESSED_REC_MAX_DATA_SIZE :
REDUNDANT_REC_MAX_DATA_SIZE) - 1;
}
my_error(ER_TOO_BIG_ROWSIZE, MYF(0), free_space);
break;
}
case DB_INVALID_NULL:
/* TODO: report the row, as we do for DB_DUPLICATE_KEY */
my_error(ER_INVALID_USE_OF_NULL, MYF(0));