andygrove commented on code in PR #6827:
URL: https://github.com/apache/datafusion-comet/pull/6827#discussion_r4237878377
##########
native/spark-expr/src/conditional_funcs/case_when.rs:
##########
@@ -60,8 +59,17 @@ pub fn create_case_when(
.transpose()?
.unwrap_or(DataType::Null);
- let Some(coerce_type) = get_coerce_type_for_case_expression(&then_types,
Some(&else_type))
- else {
+ // Spark merges THEN types from left to right, with ELSE last, retaining
the first THEN's
Review Comment:
`CometCoalesce` lowers `coalesce` and `nvl` to this same `CaseWhen` proto,
with the last argument as the ELSE, so this fold also decides the field names
of their results. On `main` the query below makes Comet return `{"X":102}`
where Spark returns `{"x":102}`. It passes with this change. Nothing in the PR
pins it, so a later change to `CometCoalesce` could regress it quietly.
Could you add it to a small `coalesce_field_names.sql` that reuses the setup
from `case_when_field_names.sql`? The last argument needs a different case from
the first, or the query also passes on `main`.
```sql
query expect_native(coalesce)
SELECT to_json(coalesce(
CASE WHEN p THEN named_struct('x', i) END,
CASE WHEN q THEN named_struct('X', i + 100) END,
named_struct('X', 7)))
FROM test_case_field_names
```
##########
spark/src/test/resources/sql-tests/expressions/conditional/case_when_field_names.sql:
##########
@@ -0,0 +1,52 @@
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+
+-- Spark preserves the first THEN's struct field names when branches differ
only in case.
+-- Native to_json exposes Arrow field names that the row comparison would
otherwise ignore.
+-- https://github.com/apache/datafusion-comet/issues/6482
+-- Config: spark.sql.caseSensitive=false
+-- Config: spark.comet.expression.StructsToJson.allowIncompatible=true
+
+statement
+CREATE TABLE test_case_field_names(p boolean, q boolean, i int, d double)
USING parquet
+
+statement
+INSERT INTO test_case_field_names VALUES
+ (true, false, 1, 1.5), (false, true, 2, 2.5),
+ (NULL, false, 3, 3.5), (false, NULL, NULL, NULL)
+
+-- The result keeps the THEN name for both selected branches, in either name
order.
+query expect_native(casewhen)
+SELECT
+ to_json(CASE WHEN p THEN named_struct('x', i) ELSE named_struct('X', i) END),
+ to_json(CASE WHEN p THEN named_struct('X', i) ELSE named_struct('x', i) END)
+FROM test_case_field_names
+
+-- Later THEN branches and an omitted ELSE must also preserve the first THEN's
name.
+query expect_native(casewhen)
+SELECT
+ to_json(CASE WHEN p THEN named_struct('x', i)
+ WHEN q THEN named_struct('X', i + 10) ELSE named_struct('X', i + 20) END),
+ to_json(CASE WHEN p THEN named_struct('x', i)
+ WHEN q THEN named_struct('X', i + 10) END)
+FROM test_case_field_names
+
+-- Fields align by position. Matching these names would incorrectly combine
INT with DOUBLE.
+query expect_native(casewhen)
+SELECT to_json(CASE WHEN p THEN named_struct('x', i, 'X', CAST(5.5 AS DOUBLE))
+ ELSE named_struct('X', 0, 'x', d) END)
+FROM test_case_field_names
Review Comment:
These queries use flat structs, and the first THEN is always a struct. Two
shapes that reach other parts of the new fold fail on `main` and pass here.
Could you add them?
The first puts case-variant names at two levels, which exercises the
recursion in `merge_branch_types`. Please keep both branch orders. The second
starts the fold from a typed NULL.
```sql
query expect_native(casewhen)
SELECT
to_json(CASE WHEN p THEN named_struct('a', named_struct('x', i)) ELSE
named_struct('A', named_struct('X', i)) END),
to_json(CASE WHEN p THEN named_struct('A', named_struct('X', i)) ELSE
named_struct('a', named_struct('x', i)) END)
FROM test_case_field_names
query expect_native(casewhen)
SELECT to_json(CASE WHEN p THEN CAST(NULL AS STRUCT<x: INT>) WHEN q THEN
named_struct('x', i) ELSE named_struct('X', i) END)
FROM test_case_field_names
```
##########
native/spark-expr/src/conditional_funcs/case_when/tests.rs:
##########
@@ -0,0 +1,791 @@
+// Licensed to the Apache Software Foundation (ASF) under one
Review Comment:
Was there a reason to move the tests into this file as part of the fix? The
move makes the diff about 1,650 changed lines and hides what changed in the
tests, which is two extended tests, one timestamp expectation and a helper
rename.
It also turns open PRs that edit the inline `mod tests` into conflicts. I
merged this branch with #6716, #6763 and #6135 locally and all three now
conflict in `case_when.rs`. With the tests left in place, #6716 and #6135 merge
cleanly, and #6763 keeps only the conflict it already has with `main`.
Could the move go in a separate PR after those land?
--
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]