From 9b10e99311f116aa6e2d0b183e614473574380c6 Mon Sep 17 00:00:00 2001 From: Manu Zhang Date: Sun, 7 Jun 2026 23:07:35 +0800 Subject: [PATCH 1/2] test: cover nested complex casts Add CometCastSuite coverage for array-of-struct casts, struct fields containing arrays, and deeply nested struct casts to both struct and string targets. Co-authored-by: Codex --- .../org/apache/comet/CometCastSuite.scala | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/spark/src/test/scala/org/apache/comet/CometCastSuite.scala b/spark/src/test/scala/org/apache/comet/CometCastSuite.scala index c527858507..29c907461c 100644 --- a/spark/src/test/scala/org/apache/comet/CometCastSuite.scala +++ b/spark/src/test/scala/org/apache/comet/CometCastSuite.scala @@ -1493,6 +1493,57 @@ class CometCastSuite extends CometTestBase with AdaptiveSparkPlanHelper { } } + test("cast StructType with ArrayType field to StructType") { + testSingleLineQuery( + """ + |SELECT named_struct( + | 'items', array(1, 2, cast(null as int)), + | 'label', 'first') AS s + |UNION ALL + |SELECT named_struct( + | 'items', cast(array() as array), + | 'label', cast(null as string)) AS s + |UNION ALL + |SELECT cast(null as struct,label:string>) AS s + |""".stripMargin, + "SELECT CAST(s AS struct,label:string>) FROM tbl", + testName = "cast_struct_array_field_to_struct") + } + + test("cast deeply nested StructType to StructType and StringType") { + val input = + """ + |SELECT named_struct( + | 'outer', + | named_struct( + | 'middle', named_struct('value', '1', 'flag', true), + | 'numbers', array('2', '3')), + | 'note', 'alpha') AS s + |UNION ALL + |SELECT named_struct( + | 'outer', + | named_struct( + | 'middle', named_struct('value', cast(null as string), 'flag', false), + | 'numbers', array(cast(null as string))), + | 'note', cast(null as string)) AS s + |UNION ALL + |SELECT cast(null as + | struct,numbers:array>, + | note:string>) AS s + |""".stripMargin + + testSingleLineQuery( + input, + "SELECT CAST(s AS " + + "struct,numbers:array>," + + "note:string>) FROM tbl", + testName = "cast_deep_struct_to_struct") + testSingleLineQuery( + input, + "SELECT CAST(s AS string) FROM tbl", + testName = "cast_deep_struct_to_string") + } + test("cast between decimals with different precision and scale") { val rowData = Seq( Row(BigDecimal("12345.6789")), @@ -1607,6 +1658,23 @@ class CometCastSuite extends CometTestBase with AdaptiveSparkPlanHelper { StringType) } + test("cast ArrayType(StructType) to StringType") { + testSingleLineQuery( + """ + |SELECT array( + | named_struct('id', 1, 'score', '10'), + | named_struct('id', 2, 'score', cast(null as string))) AS a + |UNION ALL + |SELECT array(named_struct('id', cast(null as int), 'score', '30')) AS a + |UNION ALL + |SELECT cast(array() as array>) AS a + |UNION ALL + |SELECT cast(null as array>) AS a + |""".stripMargin, + "SELECT CAST(a AS string) FROM tbl", + testName = "cast_array_struct_to_string") + } + test("cast ArrayType to ArrayType") { val types = Seq( BooleanType, @@ -1632,6 +1700,23 @@ class CometCastSuite extends CometTestBase with AdaptiveSparkPlanHelper { testArrayCastMatrix(types, ArrayType(_), generateArrays(100, _)) } + test("cast ArrayType(StructType) to ArrayType(StructType)") { + testSingleLineQuery( + """ + |SELECT array( + | named_struct('id', 1, 'score', '10'), + | named_struct('id', 2, 'score', cast(null as string))) AS a + |UNION ALL + |SELECT array(named_struct('id', cast(null as int), 'score', '30')) AS a + |UNION ALL + |SELECT cast(array() as array>) AS a + |UNION ALL + |SELECT cast(null as array>) AS a + |""".stripMargin, + "SELECT CAST(a AS array>) FROM tbl", + testName = "cast_array_struct_to_array_struct") + } + test("cast ArrayType(DateType) to unsupported ArrayType falls back") { val fromType = ArrayType(DateType) val unsupportedElementTypes = From 9d89bfbb08eb0041f01fb6c634455e179a4c8819 Mon Sep 17 00:00:00 2001 From: Manu Zhang Date: Wed, 10 Jun 2026 15:07:22 +0800 Subject: [PATCH 2/2] test: add complex cast SQL coverage Add a SQL test resource that exercises nested struct, array, and array-of-struct casts. Co-authored-by: Codex --- .../expressions/cast/cast_complex.sql | 151 ++++++++++++++++++ .../org/apache/comet/CometCastSuite.scala | 85 ---------- 2 files changed, 151 insertions(+), 85 deletions(-) create mode 100644 spark/src/test/resources/sql-tests/expressions/cast/cast_complex.sql diff --git a/spark/src/test/resources/sql-tests/expressions/cast/cast_complex.sql b/spark/src/test/resources/sql-tests/expressions/cast/cast_complex.sql new file mode 100644 index 0000000000..3898b2ac8d --- /dev/null +++ b/spark/src/test/resources/sql-tests/expressions/cast/cast_complex.sql @@ -0,0 +1,151 @@ +-- 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. + +statement +CREATE TABLE test_cast_complex( + id int, + struct_arr struct,label:string>, + deep struct,numbers:array>,note:string>, + arr_struct array> +) USING parquet + +statement +INSERT INTO test_cast_complex VALUES + ( + 1, + named_struct( + 'items', array(1, 2, cast(null as int)), + 'label', 'first'), + named_struct( + 'outer', + named_struct( + 'middle', named_struct('value', '1', 'flag', true), + 'numbers', array('2', '3')), + 'note', 'alpha'), + array( + named_struct('id', 1, 'score', '10'), + named_struct('id', 2, 'score', cast(null as string))) + ), + ( + 2, + named_struct( + 'items', cast(array() as array), + 'label', cast(null as string)), + named_struct( + 'outer', + named_struct( + 'middle', named_struct('value', cast(null as string), 'flag', false), + 'numbers', array(cast(null as string))), + 'note', cast(null as string)), + array(named_struct('id', cast(null as int), 'score', '30')) + ), + ( + 3, + cast(null as struct,label:string>), + cast(null as + struct,numbers:array>, + note:string>), + cast(array() as array>) + ), + ( + 4, + named_struct( + 'items', array(-1, 0, 2147483647), + 'label', 'edge'), + named_struct( + 'outer', + named_struct( + 'middle', named_struct('value', '-4', 'flag', true), + 'numbers', array('-5', '0')), + 'note', 'omega'), + cast(null as array>) + ) + +-- struct field containing an array +query +SELECT cast(struct_arr as struct,label:string>), id +FROM test_cast_complex +ORDER BY id + +-- struct fields can be renamed by the target type +query +SELECT cast(struct_arr as struct,name:string>), id +FROM test_cast_complex +ORDER BY id + +-- struct target field names are applied positionally +query +SELECT cast(struct_arr as struct,items:string>), id +FROM test_cast_complex +ORDER BY id + +-- missing struct fields are rejected +query expect_error(DATATYPE_MISMATCH) +SELECT cast(struct_arr as struct>) +FROM test_cast_complex + +-- extra struct fields are rejected +query expect_error(DATATYPE_MISMATCH) +SELECT cast(struct_arr as struct,label:string,extra:int>) +FROM test_cast_complex + +-- deeply nested struct to struct +query +SELECT cast(deep as + struct,numbers:array>,note:string>), id +FROM test_cast_complex +ORDER BY id + +-- deeply nested struct to string +query +SELECT cast(deep as string), id +FROM test_cast_complex +ORDER BY id + +-- array of structs to array of structs +query +SELECT cast(arr_struct as array>), id +FROM test_cast_complex +ORDER BY id + +-- array of structs with renamed fields +query +SELECT cast(arr_struct as array>), id +FROM test_cast_complex +ORDER BY id + +-- array of structs with target field names applied positionally +query +SELECT cast(arr_struct as array>), id +FROM test_cast_complex +ORDER BY id + +-- array of structs with missing nested fields is rejected +query expect_error(DATATYPE_MISMATCH) +SELECT cast(arr_struct as array>) +FROM test_cast_complex + +-- array of structs with extra nested fields is rejected +query expect_error(DATATYPE_MISMATCH) +SELECT cast(arr_struct as array>) +FROM test_cast_complex + +-- array of structs to string +query +SELECT cast(arr_struct as string), id +FROM test_cast_complex +ORDER BY id diff --git a/spark/src/test/scala/org/apache/comet/CometCastSuite.scala b/spark/src/test/scala/org/apache/comet/CometCastSuite.scala index 29c907461c..c527858507 100644 --- a/spark/src/test/scala/org/apache/comet/CometCastSuite.scala +++ b/spark/src/test/scala/org/apache/comet/CometCastSuite.scala @@ -1493,57 +1493,6 @@ class CometCastSuite extends CometTestBase with AdaptiveSparkPlanHelper { } } - test("cast StructType with ArrayType field to StructType") { - testSingleLineQuery( - """ - |SELECT named_struct( - | 'items', array(1, 2, cast(null as int)), - | 'label', 'first') AS s - |UNION ALL - |SELECT named_struct( - | 'items', cast(array() as array), - | 'label', cast(null as string)) AS s - |UNION ALL - |SELECT cast(null as struct,label:string>) AS s - |""".stripMargin, - "SELECT CAST(s AS struct,label:string>) FROM tbl", - testName = "cast_struct_array_field_to_struct") - } - - test("cast deeply nested StructType to StructType and StringType") { - val input = - """ - |SELECT named_struct( - | 'outer', - | named_struct( - | 'middle', named_struct('value', '1', 'flag', true), - | 'numbers', array('2', '3')), - | 'note', 'alpha') AS s - |UNION ALL - |SELECT named_struct( - | 'outer', - | named_struct( - | 'middle', named_struct('value', cast(null as string), 'flag', false), - | 'numbers', array(cast(null as string))), - | 'note', cast(null as string)) AS s - |UNION ALL - |SELECT cast(null as - | struct,numbers:array>, - | note:string>) AS s - |""".stripMargin - - testSingleLineQuery( - input, - "SELECT CAST(s AS " + - "struct,numbers:array>," + - "note:string>) FROM tbl", - testName = "cast_deep_struct_to_struct") - testSingleLineQuery( - input, - "SELECT CAST(s AS string) FROM tbl", - testName = "cast_deep_struct_to_string") - } - test("cast between decimals with different precision and scale") { val rowData = Seq( Row(BigDecimal("12345.6789")), @@ -1658,23 +1607,6 @@ class CometCastSuite extends CometTestBase with AdaptiveSparkPlanHelper { StringType) } - test("cast ArrayType(StructType) to StringType") { - testSingleLineQuery( - """ - |SELECT array( - | named_struct('id', 1, 'score', '10'), - | named_struct('id', 2, 'score', cast(null as string))) AS a - |UNION ALL - |SELECT array(named_struct('id', cast(null as int), 'score', '30')) AS a - |UNION ALL - |SELECT cast(array() as array>) AS a - |UNION ALL - |SELECT cast(null as array>) AS a - |""".stripMargin, - "SELECT CAST(a AS string) FROM tbl", - testName = "cast_array_struct_to_string") - } - test("cast ArrayType to ArrayType") { val types = Seq( BooleanType, @@ -1700,23 +1632,6 @@ class CometCastSuite extends CometTestBase with AdaptiveSparkPlanHelper { testArrayCastMatrix(types, ArrayType(_), generateArrays(100, _)) } - test("cast ArrayType(StructType) to ArrayType(StructType)") { - testSingleLineQuery( - """ - |SELECT array( - | named_struct('id', 1, 'score', '10'), - | named_struct('id', 2, 'score', cast(null as string))) AS a - |UNION ALL - |SELECT array(named_struct('id', cast(null as int), 'score', '30')) AS a - |UNION ALL - |SELECT cast(array() as array>) AS a - |UNION ALL - |SELECT cast(null as array>) AS a - |""".stripMargin, - "SELECT CAST(a AS array>) FROM tbl", - testName = "cast_array_struct_to_array_struct") - } - test("cast ArrayType(DateType) to unsupported ArrayType falls back") { val fromType = ArrayType(DateType) val unsupportedElementTypes =