[CALCITE-7749] CONCAT (and other STRING_SAME_SAME-typed operators) gives misleading error message for mixed CHARACTER/BINARY arguments - #5232
Conversation
…ves misleading error message for mixed CHARACTER/BINARY arguments
|
| + "Supported form\\(s\\): " | ||
| + "'<CHARACTER> \\|\\| <CHARACTER>'\\s*" | ||
| + "'<BINARY> \\|\\| <BINARY>'\\s*" | ||
| + "'<EQUIVALENT_TYPE> \\|\\| <EQUIVALENT_TYPE>'.*"); |
There was a problem hiding this comment.
what is EQUIVALENT_TYPE?
There was a problem hiding this comment.
That <EQUIVALENT_TYPE> is the default placeholder from SameOperandTypeChecker#getTypeName(). It shows up because ||'s checker (STRING_SAME_SAME_OR_ARRAY_SAME_SAME) is an OR of two branches — STRING_SAME_SAME (fixed in this PR) and and(SAME_SAME, family(ARRAY, ARRAY)) (untouched). That second branch has the same underlying issue: CompositeOperandTypeChecker#getAllowedSignatures for AND compositions only surfaces the first sub-rule's signature, so it shows SAME_SAME's generic <EQUIVALENT_TYPE> placeholder instead of the more informative .
I scoped this PR to the STRING-family checkers only, but I'm happy to extend it and clean up the array branch as well, so || gets a fully accurate message end to end. Let me know and I'll include it.
mihaibudiu
left a comment
There was a problem hiding this comment.
I think it is generally fine, but simpler comments will make the code more maintainable.
| } | ||
|
|
||
| /** | ||
| * Builds a signature generator for STRING_SAME_SAME-style checkers, which |
There was a problem hiding this comment.
some of these comments can probably be made shorter and clearer.
I would not mention 7749 twice.
Maybe not even once?



CONCAT (and other STRING_SAME_SAME-typed operators) gives misleading error message for mixed CHARACTER/BINARY arguments
Jira Link
CALCITE-7749
Changes Proposed
SqlTypeFamily.STRING is an aggregate family covering both CHARACTER and BINARY. Operators such as CONCAT (SqlLibraryOperators.CONCAT2) use OperandTypes.STRING_SAME_SAME, defined as STRING_STRING.and(SAME_SAME). Each operand independently passes STRING_STRING (since both CHAR and BINARY belong to the STRING family), so the constraint that actually rejects mixed CHARACTER/BINARY calls comes from the second, AND-composed rule SAME_SAME.
CompositeOperandTypeChecker#getAllowedSignatures, however, only includes the first sub-rule's signature for AND compositions and drops any subsequent rule's signature. As a result, the error message for e.g. CONCAT('a', x'0a') was:
Cannot apply 'CONCAT' to arguments of type 'CONCAT(<CHAR(1)>, <BINARY(1)>)'.
Supported form(s): 'CONCAT(, )'
which is misleading, since CONCAT does support BINARY arguments (e.g. CONCAT(x'0a', x'0b')); the real constraint is that both operands must belong to the same concrete sub-family.
This change adds a SqlSingleOperandTypeChecker wrapper in OperandTypes that overrides only getAllowedSignatures, delegating all type-checking behavior to the original checker unchanged, and attaches it to STRING_SAME_SAME, STRING_SAME_SAME_SAME, and STRING_SAME_SAME_INTEGER. The generated message now enumerates both concretely valid forms:
Supported form(s): 'CONCAT(, )'
'CONCAT(, )'
CompositeOperandTypeChecker#withGenerator was not used directly, because it always returns a plain CompositeOperandTypeChecker, which is not a SqlSingleOperandTypeChecker — casting the result throws ClassCastException at runtime for these fields.
STRING_SAME_SAME_OR_ARRAY_SAME_SAME requires no separate change, since it composes STRING_SAME_SAME via OR, and OR-composed signatures already aggregate all sub-rule signatures — it picks up the fix automatically.
Operators sharing STRING_SAME_SAME (CONCAT2, ENDS_WITH/ENDSWITH, STARTS_WITH/STARTSWITH) are all affected by this message change; CONCAT_FUNCTION (the MySQL/BigQuery variadic CONCAT, which uses OperandTypes.repeat(..., STRING) without SAME_SAME) is unrelated and unaffected.
Testing
Updated SqlOperatorTest#checkConcat2Func to assert the new two-form error message for CONCAT('a', x'0a').
Added/updated coverage confirming CONCAT('a', 'b') and CONCAT(x'0a', x'0b') still validate successfully (no regression to the underlying type-checking logic).