MDEV-39530 Collation mix issue after update#5396
Conversation
A user variable assignment (e.g. @p:=expr) exposed collation derivation DERIVATION_COERCIBLE, the same level as a string literal. MDEV-35041 introduced DERIVATION_USERVAR (weaker than a literal) and applied it when reading a user variable back via Item_func_get_user_var, but not to the inline assignment form handled by Item_func_set_user_var. As a result, an assignment carrying a column collation and a string literal carrying the (possibly different) connection collation ended up at the same derivation level with different collations, raising a spurious "Illegal mix of collations" error, e.g.: SET NAMES utf8mb3 COLLATE utf8mb3_uca1400_ai_ci; SELECT IF((@p:=(SELECT f.Path FROM file f WHERE f.ID=1 AND f.Path REGEXP "^xx"))!="",1,2); Make Item_func_set_user_var use DERIVATION_USERVAR so that assigning a user variable has the same derivation as reading it back, keeping it weaker than a literal. The query above now works again as it did before MDEV-35041. All new code of the whole pull request, including one or several files that are either new files or modified ones, are contributed under the BSD-new license. I am contributing on behalf of my employer Amazon Web Services, Inc.
6944075 to
71717b5
Compare
There was a problem hiding this comment.
Code Review
This pull request addresses MDEV-39530 by updating the collation derivation for user variable assignments in sql/item_func.cc. By changing DERIVATION_COERCIBLE to DERIVATION_USERVAR, it ensures consistent collation handling and prevents "Illegal mix of collations" errors. Corresponding test cases and result files have been updated to reflect these changes. I have one piece of feedback regarding a potentially redundant assignment in fix_length_and_dec.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| else | ||
| { | ||
| collation.set(DERIVATION_COERCIBLE); | ||
| collation.set(DERIVATION_USERVAR); |
There was a problem hiding this comment.
This is indeed a good suggestion. I'd fix the fix_fields's setting and remove these.
gkodinov
left a comment
There was a problem hiding this comment.
This is a preliminary review. Thank you for your contribution!
LGTM, except the cleanup suggestion below.
Please stand by for the final review.
Description
SELECT IF((@p:=(SELECT f.Path FROM file f WHERE f.ID=1 AND f.Path REGEXP "^xx"))!="",1,2);fails withER_CANT_AGGREGATE_2COLLATIONS("Illegal mix of collations") when then connection collation (utf8mb3_uca1400_ai_ci, the post-MDEV-25829 default) differs from the column collation (utf8mb3_unicode_ci). The query worked in 11.4.Root cause
The comparison
(@p := subquery) != ""forces the!=operator to pick a single collation for both operands. Every string expression carries two attributes that drive this decision:is attached to the value. Lower numeric derivation = stronger.
For the failing query the two operands are:
@p := (SELECT f.Path ...)utf8mb3_unicode_ciDERIVATION_COERCIBLE(6)Path""(empty string literal)utf8mb3_uca1400_ai_ciDERIVATION_COERCIBLE(6)collation_connectionHere both operands sit at
DERIVATION_COERCIBLE(6) with different collations, so aggregation fails and the error is raised.Fix
Make
Item_func_set_user_varuseDERIVATION_USERVARso that assigning a user variable has the same derivation as reading it back, keeping it weaker than a literal. With the assignment at level 5 and the literal at level 6, the operands are now at different levels, so aggregation simply coerces the empty string to the variable's collation and returns a result — regardless of the connection collation.Release Notes
N/A
How can this PR be tested?
Execute the
main.user_var_collationtest inmysql-test-run. It sets a connection collation that differs from the column collation and runs the reporter's query.Before this change:
The query raised an error instead of returning a result:
After this change:
The query returns the expected result, matching 11.4 behavior:
The coercibility of a user variable assignment now matches reading it back
(both
DERIVATION_USERVAR), reflected in the updatedmain.user_varresult:main.user_var_collation,main.user_var, and thectype_*/main.variablessuites all pass:mainsuite passed without failure relevant to this change.Basing the PR against the correct MariaDB version
Copyright
All new code of the whole pull request, including one or several files that are either new files or modified ones, are contributed under the BSD-new license. I am contributing on behalf of my employer Amazon Web Services, Inc.