sunchao commented on code in PR #7343:
URL: https://github.com/apache/datafusion-comet/pull/7343#discussion_r4427286628


##########
native/spark-expr/src/datetime_funcs/make_date.rs:
##########
@@ -174,10 +175,17 @@ fn cast_to_int32(arr: &Arc<dyn Array>) -> Result<Arc<dyn 
Array>> {
 }
 
 /// Convert year, month, day to days since Unix epoch (1970-01-01).
-/// Returns None if the date is invalid or its epoch day does not fit Date32.
-fn make_date(year: i32, month: i32, day: i32) -> Option<i32> {
-    ymd_to_epoch_day(year.into(), month.into(), day.into())
-        .and_then(|days| i32::try_from(days).ok())
+/// Invalid Java calendar dates return None; valid dates outside Date32 raise 
arithmetic overflow.
+fn make_date(year: i32, month: i32, day: i32) -> Result<Option<i32>> {
+    if !(-999_999_999..=999_999_999).contains(&year) {
+        return Ok(None);
+    }
+    let Some(days) = ymd_to_epoch_day(year.into(), month.into(), day.into()) 
else {
+        return Ok(None);
+    };
+    i32::try_from(days)
+        .map(Some)
+        .map_err(|_| arithmetic_overflow_error("integer").into())

Review Comment:
   **[P2] Keep a throwing `make_date` predicate out of Parquet row filtering 
when another predicate can skip it**
   
   With ANSI off and `spark.comet.parquet.rowFilterPushdown.enabled=true`, I 
reproduced this on Spark 4.1.3. Write the following 1,024 rows to one Parquet 
file and read it as `t`:
   
   ```python
   spark.range(1024, numPartitions=1).selectExpr(
       "cast(if(id=1023,6000000,2024) as int) as y",
       "if(id=1023,'',sha2(cast(id as string),512)) as mask",
   ).write.parquet(path)
   ```
   
   ```sql
   SELECT count(*) FROM t
   WHERE length(mask) > 0 AND unix_date(make_date(y,1,1)) > 0
   ```
   
   Only the last row has an overflowing year, and its mask is empty. Spark and 
merge base `15305af` return `1023`. This head also returns `1023` with 
row-filter pushdown off. With it on, the native Parquet reader evaluates the 
date before the mask and throws `CometNativeException`. Even without the mask, 
when an overflow *should* be raised, that scan path wraps it in 
`CometNativeException` and loses Spark’s structured exception. Please keep the 
date predicate above the scan unless the reader can preserve both evaluation 
order and the typed error.



##########
spark/src/test/scala/org/apache/comet/CometTemporalExpressionSuite.scala:
##########
@@ -117,6 +118,25 @@ class CometTemporalExpressionSuite extends CometTestBase 
with AdaptiveSparkPlanH
                 }
               }
 
+              // Date32 overflow throws even with ANSI off, but only on rows 
Spark consumes.
+              for (ansi <- Seq(false, true)) {
+                withSQLConf(SQLConf.ANSI_ENABLED.key -> ansi.toString) {
+                  val overflow = "SELECT unix_date(make_date(y, 1, 1)) FROM 
make_date_limit"
+                  checkSparkAnswerAndOperator(
+                    sql(overflow + " LIMIT 1"),
+                    includeClasses = Seq(classOf[CometProjectExec], 
classOf[CometLocalLimitExec]))
+                  partialConsumers.foreach { case (name, consume) =>

Review Comment:
   **[P2] Preserve partial consumption through PySpark’s `DataFrame.rdd`**
   
   The Scala `df.rdd.take(1)` here exercises a different path from the public 
PySpark call. PySpark goes through `Dataset.javaToPython` and 
`queryExecution.toRdd`, so the object-producing root that `MakeDateEvaluation` 
guards is absent. The plan keeps a `CometProject` and evaluates the last row 
before emitting the first.
   
   I reproduced this on Spark 4.1.3 with ANSI off and the default operator 
fallback enabled. Write the following to one Parquet file, then read it back:
   
   ```python
   spark.range(4096, numPartitions=1).selectExpr(
       "cast(if(id=4095, 6000000, 2024) as int) as y", "id"
   ).write.parquet(path)
   df = spark.read.parquet(path).selectExpr(
       "unix_date(make_date(y,1,1)) as d",
       "repeat(sha2(cast(id as string),512),512) as padding",
   )
   df.rdd.take(1)[0].d
   ```
   
   Vanilla Spark and merge base `15305af` return `19723`. At `149cf86` this 
raises `ARITHMETIC_OVERFLOW`. The wide payload makes Spark stop reading before 
the last row. Please cover the Python entry point and preserve its ability to 
stop before an overflowing year.



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