MDEV-41026 ER_DUP_KEY on '(temporary)' converting a subquery cache - #5637
Open
arcivanov wants to merge 1 commit into
Open
MDEV-41026 ER_DUP_KEY on '(temporary)' converting a subquery cache#5637arcivanov wants to merge 1 commit into
arcivanov wants to merge 1 commit into
Conversation
A subquery expression cache whose parameter is NULL for many rows
fills its in-memory table with rows that all carry a NULL key.
`TABLE::add_tmp_key()` builds that key without `HA_NULL_ARE_EQUAL`,
`ha_heap::create()` propagates the absence, and `hp_write_key()` then
skips the duplicate check for any key value containing NULL, so such
rows are legal there. They accumulate rather than being exceptional:
`ref.null_rejecting` is set, so every NULL parameter is a miss and
adds one more NULL-keyed row.
The on-disk creators disagreed. Both the key and the unique
constraint forced `HA_NULL_ARE_EQUAL` on, making the destination of a
conversion stricter about NULLs than the table being converted from.
`create_internal_tmp_table_from_heap()` re-inserts every stored row
and treats a duplicate among them as fatal, so the second NULL-keyed
row was rejected. The user saw either
Can't write; duplicate key in table '(temporary)'
or, when the key is too wide for the on-disk engine to index and is
built as a unique constraint over a row hash instead,
Can't write, because of unique constraint, to table '(temporary)'
Make `KEY::flags & HA_NULL_ARE_EQUAL` the single source of truth and
read it at all four creator sites rather than deciding it there. For
the flag to be truthful the GROUP BY key has to carry it when it is
built as a unique constraint as well; the assignment sat inside the
branch that only the real-key case takes, which also lays the group
buffer out around the NULL flag.
The MyISAM halves of both creators compile only with
`-DUSE_ARIA_FOR_TMP_TABLES=OFF`, which is not the default. They are
changed to match and verified to compile warning-free in that
configuration, but **no test run has executed them**.
The test covers both on-disk shapes: a narrow key that stays a key,
and a key too wide for Aria that becomes a unique constraint. Each
block reports whether the conversion happened at all, so the test
cannot pass by quietly ceasing to overflow. The count itself is not
asserted: `--ps-protocol` executes the statement twice and so
converts twice. Neither cached query aborts on error, so a
regression in either shape is reported rather than hidden behind the
other one.
arcivanov
force-pushed
the
MDEV-41026-11.8
branch
from
September 4, 2026 21:53
a285e79 to
fb22178
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
https://jira.mariadb.org/browse/MDEV-41026
This is the same change as #5636, targeted at 11.8 instead of
main. Fix Version/s on the ticket starts at 11.8, and a fix committed tomainnever reaches 11.8 or 12.3, so this is the one that should land; #5636 becomes redundant once this merges up.The defect
A subquery expression cache whose parameter is NULL for many rows fills its in-memory table with rows that all carry a NULL key.
TABLE::add_tmp_key()builds that key withoutHA_NULL_ARE_EQUAL,ha_heap::create()propagates the absence, andhp_write_key()then skips the duplicate check for any key value containing NULL, so such rows are legal there. They accumulate rather than being exceptional:ref.null_rejectingis set, so every NULL parameter is a cache miss and adds one more NULL-keyed row.The on-disk creators disagreed. Both the key and the unique constraint forced
HA_NULL_ARE_EQUALon, making the destination of a conversion stricter about NULLs than the table being converted from.create_internal_tmp_table_from_heap()re-inserts every stored row and treats a duplicate among them as fatal, so the second NULL-keyed row was rejected.The reported symptom is
ER_DUP_KEY. The same defect surfaces asER_DUP_ENTRYonce the cache table is already on disk, and asER_DUP_UNIQUEwhen the key is too wide for the on-disk engine to index and is built as a unique constraint over a row hash instead.The fix
Make
KEY::flags & HA_NULL_ARE_EQUALthe single source of truth and read it at all four creator sites -keydefanduniquedef, in each of the Aria and the MyISAM creator - rather than deciding it there.For that flag to be truthful, the GROUP BY key has to carry it when it is built as a unique constraint as well. The assignment sat inside the branch that only the real-key case takes, which also lays the group buffer out around the NULL flag, so it is now set in the unique-constraint case too. This is behaviour-neutral on Aria, whose creator tests
HA_UNIQUE_HASHexplicitly and therefore always took theuniquedefbranch for such a key, wherenull_are_equalwas hardcoded to 1.choose_engine()routes everym_using_unique_constrainttable to the on-disk engine, so that change cannot reachha_heap::create(), the only other place aKEY'sHA_NULL_ARE_EQUALis read.Testing
mysql-test/main/subquery_cache_null_convertcovers both on-disk shapes. Each block printsCreated_tmp_disk_tables, so the test cannot pass by quietly ceasing to overflow, and neither cached query aborts on error, so a regression in either shape is reported rather than hidden behind the other one. Reverting the fix and running the test unchanged gives, in one run on this branch:ERROR 23000: Can't write; duplicate key in table '(temporary)'(1022)ERROR 23000: Can't write, because of unique constraint, to table '(temporary)'(1169)main,heapandmariasuites on 11.8: 1393 of 1393 pass.Not covered
The MyISAM halves of both creators compile only with
-DUSE_ARIA_FOR_TMP_TABLES=OFF, which is not the default. They are changed to match and verified to compile warning-free in that configuration, but no test run has executed them.