srielau commented on code in PR #58033:
URL: https://github.com/apache/spark/pull/58033#discussion_r3801133080


##########
sql/core/src/test/resources/sql-tests/inputs/charvarchar-standard-semantics.sql:
##########
@@ -0,0 +1,90 @@
+--SET spark.sql.charVarchar.standardSemantics.enabled=true
+
+-- R3: CAST introduces CHAR/VARCHAR
+SELECT typeof(CAST('ab' AS CHAR(5)));
+SELECT typeof(CAST('hello' AS VARCHAR(5)));
+SELECT 'X' || CAST('5' AS CHAR(5)) || 'X';
+
+-- CAST length enforcement: trailing spaces are trimmed, real overflow errors
+SELECT CAST('ab   ' AS CHAR(2));
+SELECT CAST('abcdef' AS CHAR(2));
+SELECT CAST('abcdef' AS VARCHAR(2));
+SELECT try_cast('abcdef' AS CHAR(2));
+SELECT try_cast('abcdef' AS VARCHAR(2));
+
+-- R2: least common type (COALESCE / CASE)
+SELECT typeof(coalesce(cast('hello' AS VARCHAR(5)), cast('world' AS 
VARCHAR(10))));
+SELECT typeof(coalesce(cast('hello' AS VARCHAR(5)), cast('world!' AS 
CHAR(6))));
+SELECT typeof(coalesce(cast('hello' AS CHAR(5)), cast('world!' AS CHAR(6))));
+SELECT typeof(coalesce(cast('hello' AS VARCHAR(5)), 'world'));
+SELECT typeof(coalesce(cast('hello' AS CHAR(5)), NULL));
+SELECT typeof(
+  CASE WHEN true THEN cast('a' AS CHAR(2)) ELSE cast('bb' AS CHAR(4)) END);
+
+-- R2: least common type for IN lists
+SELECT cast('a' AS CHAR(2)) IN (cast('a ' AS CHAR(2)), cast('bbb' AS 
VARCHAR(3)));
+SELECT typeof(c) FROM (SELECT cast('a' AS CHAR(2)) AS c) t WHERE c IN ('a ', 
'b');
+
+-- R1: transforming functions return STRING
+SELECT typeof(upper(cast('ab' AS CHAR(2))));
+SELECT typeof(lower(cast('AB' AS VARCHAR(2))));
+SELECT typeof(cast('a' AS CHAR(1)) || cast('b' AS VARCHAR(1)));
+SELECT typeof(substr(cast('hello' AS VARCHAR(5)), 1, 2));
+SELECT typeof(upper(coalesce(cast('a' AS CHAR(2)), cast('b' AS CHAR(4)))));
+SELECT typeof(concat(cast('a' AS CHAR(2)), cast('b' AS CHAR(3))));
+SELECT typeof(trim(cast('ab  ' AS CHAR(4))));
+SELECT typeof(lpad(cast('ab' AS CHAR(2)), 5, 'x'));
+
+-- R1: regexp / mask / split family
+SELECT typeof(regexp_replace(cast('ab' AS CHAR(2)), 'a', 'x'));
+SELECT typeof(regexp_extract(cast('ab' AS VARCHAR(2)), '(a)', 1));
+SELECT typeof(regexp_extract_all(cast('aab' AS VARCHAR(3)), '(a)', 1));
+SELECT typeof(split(cast('a,b' AS CHAR(3)), ','));
+SELECT typeof(mask(cast('ab' AS CHAR(2))));
+
+-- R1: CHAR/VARCHAR promote to STRING where a plain string is expected, so 
expressions that
+-- require all their string inputs to share one type accept them alongside a 
STRING argument.
+-- Values are wrapped in sentinels because the golden format trims trailing 
blanks, which would
+-- otherwise hide the CHAR padding these expressions operate on.
+SELECT typeof(overlay(cast('ab' AS CHAR(5)) PLACING 'x' FROM 1));
+SELECT concat('<', overlay(cast('ab' AS CHAR(5)) PLACING 'x' FROM 1), '>');
+SELECT typeof(elt(1, cast('ab' AS CHAR(5)), 'x'));
+SELECT typeof(right(cast('ab' AS CHAR(5)), 2));
+SELECT concat('<', right(cast('ab' AS CHAR(5)), 2), '>');
+SELECT typeof(left(cast('ab' AS CHAR(5)), 2));
+
+-- R1: transforms whose result length differs from the input must not inherit 
the constraint.
+SELECT typeof(reverse(cast('ab' AS CHAR(5))));
+SELECT typeof(hex(cast('ab' AS CHAR(5))));
+SELECT hex(cast('ab' AS CHAR(5)));
+SELECT typeof(array_join(array(cast('ab' AS CHAR(5)), cast('cd' AS CHAR(5))), 
'-'));
+SELECT concat('<', array_join(array(cast('ab' AS CHAR(5)), cast('cd' AS 
CHAR(5))), '-'), '>');
+-- reverse() on a non-string input is unaffected.
+SELECT typeof(reverse(array(1, 2)));
+
+-- R2 with collation.
+-- The recorded "string collate null" is a pre-existing gap, not a 
standardSemantics behavior:

Review Comment:
   You are right, and my note on it was wrong in a way worth recording: I had 
written it off as a constant-folding artifact, but it reproduces with plain 
column references too, and it reproduces identically under 
`spark.sql.preserveCharVarcharTypeInfo`, so it is neither folding-specific nor 
introduced here.
   
   The cause is `CollationTypeCoercion.getWinningStringType`: at equal 
collation strength it compares the operands with `sameType`, so `CHAR(2) 
COLLATE UTF8_LCASE` and `CHAR(4) COLLATE UTF8_LCASE` are read as a collation 
mismatch purely because the lengths differ, and it yields 
`IndeterminateStringType`. It is not a one-line fix, because that rule then 
casts all string children to the single winner, so simply making the lengths 
compare equal would truncate to the shorter one; the length has to stay with 
the LCT. That belongs in its own change.
   
   So I dropped the query rather than golden the wrong type, and replaced it 
with the collated cases that do hold (a collated CAST, and an LCT over equally 
constrained operands, which yields `char(2) collate UTF8_LCASE`). The comment 
in the input file now states the gap and why it is not covered here.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to