Commit 5838b527 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-21511 Wrong estimate of affected BLOB columns in update

During update, rollback, or MVCC read, we may miscalculate
the number of off-page columns, and thus the size of the
clustered index record. The function btr_push_update_extern_fields()
is mostly redundant, because the off-page columns would also be
moved by row_upd_index_replace_new_col_val(), which is invoked
via row_upd_index_replace_new_col_vals().

btr_push_update_extern_fields(): Remove.

This is based on
mysql/mysql-server@1fa475b85d24de4b9ce2958c0eed738c221fc82c
which refines a fix for a recovery bug fix
mysql/mysql-server@ce0a1e85e24e48b8171f767b44330da635a6ea0a
in MySQL 5.7.5.

No test case was provided by Oracle.
Some of the changed code is being covered by the existing test
innodb.blob-crash.
parent 3e38d155
/*****************************************************************************
Copyright (c) 1994, 2018, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1994, 2019, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2008, Google Inc.
Copyright (c) 2012, Facebook Inc.
Copyright (c) 2015, 2019, MariaDB Corporation.
Copyright (c) 2015, 2020, MariaDB Corporation.
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
......@@ -4374,7 +4374,9 @@ btr_cur_pessimistic_update(
ut_ad(!page_is_comp(page) || !rec_get_node_ptr_flag(rec));
ut_ad(rec_offs_validate(rec, index, *offsets));
n_ext += btr_push_update_extern_fields(new_entry, update, entry_heap);
/* Get number of externally stored columns in updated record */
n_ext = dtuple_get_n_ext(new_entry);
if ((flags & BTR_NO_UNDO_LOG_FLAG)
&& rec_offs_any_extern(*offsets)) {
......@@ -6593,84 +6595,6 @@ btr_cur_unmark_extern_fields(
}
}
/*******************************************************************//**
Flags the data tuple fields that are marked as extern storage in the
update vector. We use this function to remember which fields we must
mark as extern storage in a record inserted for an update.
@return number of flagged external columns */
ulint
btr_push_update_extern_fields(
/*==========================*/
dtuple_t* tuple, /*!< in/out: data tuple */
const upd_t* update, /*!< in: update vector */
mem_heap_t* heap) /*!< in: memory heap */
{
ulint n_pushed = 0;
ulint n;
const upd_field_t* uf;
uf = update->fields;
n = upd_get_n_fields(update);
for (; n--; uf++) {
if (dfield_is_ext(&uf->new_val)) {
dfield_t* field
= dtuple_get_nth_field(tuple, uf->field_no);
if (!dfield_is_ext(field)) {
dfield_set_ext(field);
n_pushed++;
}
switch (uf->orig_len) {
byte* data;
ulint len;
byte* buf;
case 0:
break;
case BTR_EXTERN_FIELD_REF_SIZE:
/* Restore the original locally stored
part of the column. In the undo log,
InnoDB writes a longer prefix of externally
stored columns, so that column prefixes
in secondary indexes can be reconstructed. */
dfield_set_data(field,
(byte*) dfield_get_data(field)
+ dfield_get_len(field)
- BTR_EXTERN_FIELD_REF_SIZE,
BTR_EXTERN_FIELD_REF_SIZE);
dfield_set_ext(field);
break;
default:
/* Reconstruct the original locally
stored part of the column. The data
will have to be copied. */
ut_a(uf->orig_len > BTR_EXTERN_FIELD_REF_SIZE);
data = (byte*) dfield_get_data(field);
len = dfield_get_len(field);
buf = (byte*) mem_heap_alloc(heap,
uf->orig_len);
/* Copy the locally stored prefix. */
memcpy(buf, data,
uf->orig_len
- BTR_EXTERN_FIELD_REF_SIZE);
/* Copy the BLOB pointer. */
memcpy(buf + uf->orig_len
- BTR_EXTERN_FIELD_REF_SIZE,
data + len - BTR_EXTERN_FIELD_REF_SIZE,
BTR_EXTERN_FIELD_REF_SIZE);
dfield_set_data(field, buf, uf->orig_len);
dfield_set_ext(field);
}
}
}
return(n_pushed);
}
/*******************************************************************//**
Returns the length of a BLOB part stored on the header page.
@return part length */
......
/*****************************************************************************
Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2019, MariaDB Corporation.
Copyright (c) 1994, 2019, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
......@@ -755,18 +755,6 @@ btr_rec_copy_externally_stored_field(
ulint* len,
mem_heap_t* heap);
/*******************************************************************//**
Flags the data tuple fields that are marked as extern storage in the
update vector. We use this function to remember which fields we must
mark as extern storage in a record inserted for an update.
@return number of flagged external columns */
ulint
btr_push_update_extern_fields(
/*==========================*/
dtuple_t* tuple, /*!< in/out: data tuple */
const upd_t* update, /*!< in: update vector */
mem_heap_t* heap) /*!< in: memory heap */
MY_ATTRIBUTE((nonnull));
/***********************************************************//**
Sets a secondary index record's delete mark to the given value. This
function is only used by the insert buffer merge mechanism. */
......
/*****************************************************************************
Copyright (c) 1994, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2019, MariaDB Corporation.
Copyright (c) 1994, 2019, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
......@@ -1626,6 +1626,7 @@ rec_get_converted_size(
data_size = dtuple_get_data_size(dtuple, 0);
ut_ad(n_ext == dtuple_get_n_ext(dtuple));
extra_size = rec_get_converted_extra_size(
data_size, dtuple_get_n_fields(dtuple), n_ext);
......
/*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2019, MariaDB Corporation.
Copyright (c) 1996, 2019, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
......@@ -2448,12 +2448,14 @@ trx_undo_prev_version_build(
entry = row_rec_to_index_entry(
rec, index, offsets, &n_ext, heap);
n_ext += btr_push_update_extern_fields(entry, update, heap);
/* The page containing the clustered index record
corresponding to entry is latched in mtr. Thus the
following call is safe. */
row_upd_index_replace_new_col_vals(entry, index, update, heap);
/* Get number of externally stored columns in updated record */
n_ext = dtuple_get_n_ext(entry);
buf = static_cast<byte*>(mem_heap_alloc(
heap, rec_get_converted_size(index, entry, n_ext)));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment