andygrove commented on code in PR #5079:
URL: https://github.com/apache/datafusion-comet/pull/5079#discussion_r3668238457


##########
spark/src/main/scala/org/apache/comet/expressions/CometCast.scala:
##########
@@ -159,14 +178,22 @@ object CometCast extends CometExpressionSerde[Cast] with 
CometExprShim {
       timeZoneId: Option[String],
       evalMode: CometEvalMode.Value): SupportLevel = {
 
+    // Spark 4's `VariantType` (SPARK-45827) has no native counterpart in 
Comet: serializing it
+    // into the DataFusion plan would fail in `serializeDataType`, and the 
codegen dispatcher
+    // cannot compile Variant read/write kernels either. Detect it via the 
version-shimmed
+    // `isVariantType` (which returns false on Spark 3.x where the class does 
not exist) and
+    // report `Unsupported` so the enclosing operator falls back to Spark.
+    if (isVariantType(fromType) || isVariantType(toType)) {
+      return unsupported(fromType, toType)
+    }
+
     if (fromType == toType) {
       return Compatible()
     }
 
-    if (toType == DataTypes.StringType && legacyCastComplexTypesToString && 
(fromType
-        .isInstanceOf[ArrayType] || fromType.isInstanceOf[StructType] ||
-        fromType.isInstanceOf[MapType])) {
-      return Unsupported(Some(legacyCastComplexTypesToStringReason))
+    if (toType == DataTypes.StringType && legacyCastComplexTypesToString && 
isComplexType(
+        fromType)) {
+      return Incompatible(Some(legacyCastComplexTypesToStringReason))

Review Comment:
   Since `CodegenDispatchFallback` already routes `Unsupported` through the 
dispatcher, keeping this as `Unsupported` would give the same in-pipeline 
behavior. Switching to `Incompatible` additionally allows the native cast to 
run when `spark.comet.expr.allowIncompatible=true`, and the native cast 
produces the default formatting rather than the legacy formatting the user 
explicitly asked for with the legacy flag. That would be a silent results 
change for those users, where today they always get correct results via 
fallback. Was that intentional? If the goal is just to stay in the Comet 
pipeline, `Unsupported` plus the mixin achieves it without that risk.



##########
spark/src/test/scala/org/apache/comet/CometCastSuite.scala:
##########
@@ -1814,9 +1801,7 @@ class CometCastSuite extends CometTestBase with 
AdaptiveSparkPlanHelper {
           assert(
             CometCast.isSupported(fromType, toType, None, 
CometEvalMode.LEGACY) ==
               Unsupported(Some(expectedMessage)))
-          checkSparkAnswerAndFallbackReason(
-            data.select(col("a").cast(toType).as("converted")),
-            expectedMessage)
+          
checkSparkAnswerAndOperator(data.select(col("a").cast(toType).as("converted")))

Review Comment:
   The test is still named "cast ArrayType(DateType) to unsupported ArrayType 
falls back" but it now asserts the Comet operator is present. Could you rename 
it to reflect the dispatcher routing?



##########
spark/src/main/scala/org/apache/comet/expressions/CometCast.scala:
##########
@@ -159,14 +178,22 @@ object CometCast extends CometExpressionSerde[Cast] with 
CometExprShim {
       timeZoneId: Option[String],
       evalMode: CometEvalMode.Value): SupportLevel = {
 
+    // Spark 4's `VariantType` (SPARK-45827) has no native counterpart in 
Comet: serializing it
+    // into the DataFusion plan would fail in `serializeDataType`, and the 
codegen dispatcher
+    // cannot compile Variant read/write kernels either. Detect it via the 
version-shimmed
+    // `isVariantType` (which returns false on Spark 3.x where the class does 
not exist) and
+    // report `Unsupported` so the enclosing operator falls back to Spark.

Review Comment:
   This comment (and the one in `getSupportLevel` above) says the guard forces 
Spark fallback, but with the `CodegenDispatchFallback` mixin an `Unsupported` 
result first attempts the codegen dispatcher. Variant casts fall back only 
because the dispatcher fails to serialize `VariantType` in the data args or 
return type. The end behavior is the same, but could the comments describe the 
actual routing?
   
   It might also be worth adding a Spark 4.x test that casts to and from 
variant, for example `CAST(parse_json('{"a":1}') AS STRING)` on a column, to 
pin down that this path cleanly falls back and matches Spark. I could not find 
any Variant cast coverage in the repo today.



##########
spark/src/main/scala/org/apache/comet/expressions/CometCast.scala:
##########
@@ -25,30 +25,41 @@ import org.apache.spark.sql.types.{ArrayType, DataType, 
DataTypes, DecimalType,
 
 import org.apache.comet.CometConf
 import org.apache.comet.CometSparkSessionExtensions.{isSpark40Plus, 
withFallbackReason}
-import org.apache.comet.serde.{CometExpressionSerde, Compatible, 
ExprOuterClass, Incompatible, SupportLevel, Unsupported}
+import org.apache.comet.DataTypeSupport.isComplexType
+import org.apache.comet.serde.{CodegenDispatchFallback, CometExpressionSerde, 
Compatible, ExprOuterClass, Incompatible, SupportLevel, Unsupported}
 import org.apache.comet.serde.ExprOuterClass.Expr
 import org.apache.comet.serde.QueryPlanSerde.{evalModeToProto, 
exprToProtoInternal, serializeDataType}
-import org.apache.comet.shims.CometExprShim
+import org.apache.comet.shims.{CometExprShim, CometTypeShim}
 
-object CometCast extends CometExpressionSerde[Cast] with CometExprShim {
+object CometCast
+    extends CometExpressionSerde[Cast]
+    with CometExprShim
+    with CometTypeShim
+    with CodegenDispatchFallback {
 
   // Shared with CometCastSuite so the asserted reason cannot drift from 
production.
   private[comet] val negativeScaleDecimalToStringReason: String =
     "Negative-scale decimal requires 
spark.sql.legacy.allowNegativeScaleOfDecimal=true"
 
-  // When `spark.sql.legacy.castComplexTypesToString.enabled` is true, Spark 
wraps maps and
-  // structs with `[]` (instead of `{}`) when casting to string, and omits 
NULL elements of
-  // structs/maps/arrays (instead of rendering them as the literal "null"). 
Comet only
-  // implements the default formatting, so fall back to Spark for any 
array/map/struct to-string
-  // cast when the flag is enabled. The flag is internal in Spark 4.0 and 
defaults to false.
   private[comet] val legacyCastComplexTypesToStringReason: String =
-    "spark.sql.legacy.castComplexTypesToString.enabled=true is not supported"
+    "spark.sql.legacy.castComplexTypesToString.enabled=true is not supported 
natively"
+
+  private[comet] val nonDefaultTimeParserPolicyReason: String =
+    "spark.sql.legacy.timeParserPolicy is set to a non-CORRECTED value; the 
native " +
+      "string-to-datetime parser only implements CORRECTED semantics"
 
   private def legacyCastComplexTypesToString: Boolean =
     SQLConf.get
       .getConfString("spark.sql.legacy.castComplexTypesToString.enabled", 
"false")
       .toBoolean
 
+  // Non-CORRECTED policies (LEGACY, EXCEPTION) change 
string-to-date/timestamp parsing behavior in
+  // ways the native cast kernel does not replicate.
+  private def isNonDefaultTimeParserPolicy: Boolean =
+    !SQLConf.get
+      .getConfString("spark.sql.legacy.timeParserPolicy", "CORRECTED")
+      .equalsIgnoreCase("CORRECTED")

Review Comment:
   I checked the Spark source to close this out. `Cast.castToDate` and 
`castToTimestamp` for string inputs call `DateTimeUtils.stringToDate` and 
`stringToTimestamp` directly, and there are no references to `timeParserPolicy` 
anywhere in `Cast.scala` on either master or v3.5.8. The policy is only 
consulted by `DateFormatter` and `TimestampFormatter`, which back `to_date`, 
`to_timestamp`, `date_format`, and the CSV/JSON/XML parsers. Cast is unaffected.
   
   Could we remove this branch, the reason string, and 
`cast_string_to_date_time_parser_policy_legacy.sql`? The new test passes with 
or without this change because the results are identical under any policy 
value, so it does not actually verify anything.
   
   If we ever did need to read this conf, note that `getConfString` with a 
hard-coded `"CORRECTED"` default ignores the registered default, which is 
`EXCEPTION` on Spark 3.x, so the typed `SQLConf.get.legacyTimeParserPolicy` 
getter would be the safer read.



-- 
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