Skip to content

MDEV-39792 InnoDB: ALTER TABLE FORCE triggers assertion "s" in buf_pa… - #5724

Open
iMineLink wants to merge 1 commit into
11.4from
11.4-MDEV-39792
Open

iMineLink wants to merge 1 commit into
11.4from
11.4-MDEV-39792

Conversation

@iMineLink

Copy link
Copy Markdown
Contributor

…ge_get_gen()

When rebuilding a table into ROW_FORMAT=REDUNDANT, row_merge_buf_add() fetches the full value of an externally stored (off-page) CHAR/VARCHAR column and pads it to REDUNDANT's fixed local width via row_merge_buf_redundant_convert(). That helper already dereferences the BLOB and calls dfield_set_data(), which clears the field's "externally stored" flag, since the value is now held in full locally.

The "flag externally stored fields" step further down in row_merge_buf_add() did not know this had happened. It still consulted the row_ext_t cache built from the original (pre-conversion) record and, for a column that is not part of the clustered index's unique key, called dfield_set_ext() again on the very field that had just been converted, without restoring its data pointer to a valid 20-byte external reference. row_merge_copy_blobs() would then read the tail of the padded, space-filled buffer as if it were a BTR_EXTERN_FIELD_REF, deriving a garbage tablespace id and crashing buf_page_get_gen()'s fil_space_get() assertion when the alter tried to build the new clustered index.

Track whether the field went through the redundant-format conversion, and skip the stale re-flagging step for it: a converted field is already fully local and must not be treated as external again.

With the field no longer corrupted, InnoDB's existing row-size check correctly rejects the ALTER with ER_TOO_BIG_ROWSIZE instead of crashing, because REDUNDANT's mandatory local BLOB prefixes make the record too large for a leaf page.

…ge_get_gen()

When rebuilding a table into ROW_FORMAT=REDUNDANT, row_merge_buf_add()
fetches the full value of an externally stored (off-page) CHAR/VARCHAR
column and pads it to REDUNDANT's fixed local width via
row_merge_buf_redundant_convert(). That helper already dereferences the
BLOB and calls dfield_set_data(), which clears the field's "externally
stored" flag, since the value is now held in full locally.

The "flag externally stored fields" step further down in
row_merge_buf_add() did not know this had happened. It still consulted
the row_ext_t cache built from the original (pre-conversion) record and,
for a column that is not part of the clustered index's unique key,
called dfield_set_ext() again on the very field that had just been
converted, without restoring its data pointer to a valid 20-byte
external reference. row_merge_copy_blobs() would then read the tail of
the padded, space-filled buffer as if it were a BTR_EXTERN_FIELD_REF,
deriving a garbage tablespace id and crashing buf_page_get_gen()'s
fil_space_get() assertion when the alter tried to build the new
clustered index.

Track whether the field went through the redundant-format conversion,
and skip the stale re-flagging step for it: a converted field is
already fully local and must not be treated as external again.

With the field no longer corrupted, InnoDB's existing row-size check
correctly rejects the ALTER with ER_TOO_BIG_ROWSIZE instead of crashing,
because REDUNDANT's mandatory local BLOB prefixes make the record too
large for a leaf page.
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The change is narrowly scoped to the crash mechanism and includes a targeted regression test to ensure the failure mode is an error rather than an assertion/crash.

Review effort: Lite
Findings: 1 Low severity

Open (1)
What changed in this PR

Fixes an InnoDB crash during ALTER TABLE ... FORCE when rebuilding into ROW_FORMAT=REDUNDANT and encountering an externally stored CHAR/VARCHAR value that gets materialized during redundant-format conversion.

Changes:

  • Track whether a field was materialized from external storage during redundant-format conversion and skip stale re-flagging as “externally stored”.
  • Add an InnoDB MTR regression test that verifies the operation fails with ER_TOO_BIG_ROWSIZE instead of crashing.
File Description
storage/​innobase/​row/​row0merge.cc Prevent re-applying dfield_set_ext() after redundant conversion has already materialized an external value locally.
mysql-test/​suite/​innodb/​t/​alter_force_redundant_ext_char.test New regression test reproducing the scenario and asserting ER_TOO_BIG_ROWSIZE.
mysql-test/​suite/​innodb/​r/​alter_force_redundant_ext_char.result Expected output for the new regression test.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +769 to +774
/* The field was already fetched in
full and converted to a fixed-width
ROW_FORMAT=REDUNDANT value above; it
is no longer externally stored, and
must not be re-flagged as such below. */
converted_from_ext = row_field->ext;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

4 participants