dongjoon-hyun commented on code in PR #59606:
URL: https://github.com/apache/spark/pull/59606#discussion_r4319357102


##########
sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/parquet/types/ops/ParquetFilterOps.scala:
##########
@@ -62,6 +64,14 @@ private[parquet] trait ParquetFilterOps {
   /** Whether `value` (a non-null external filter value) is pushable for this 
type. */
   def acceptsValue(value: Any): Boolean
 
+  /**
+   * Whether a predicate with a value is pushable for a column of this 
encoding that is read as
+   * `readType`, the Spark type in the read schema. Parquet evaluates a pushed 
down predicate
+   * against the stored values, so this is false when the reader changes them 
for `readType`, e.g.
+   * truncates them to a lower precision. IsNull and IsNotNull are pushed down 
regardless.
+   */

Review Comment:
   Thank you for the review, @MaxGekk . Yes, `TIMESTAMP(NANOS)` has the same 
issue. I am already working on it in a new JIRA issue, including the test 
coverage that you mentioned.



##########
sql/core/src/test/scala/org/apache/spark/sql/execution/datasources/parquet/ParquetFilterSuite.scala:
##########
@@ -2309,6 +2311,91 @@ abstract class ParquetFilterSuite extends ParquetTest 
with SharedSparkSession {
     }
   }
 
+  test("SPARK-60358: filter pushdown - time read with a lower precision than 
the stored value") {
+    // A TIME(6) value 12:00:00.1005 read as TIME(3) is truncated to 
12:00:00.100, while Parquet
+    // evaluates a pushed down predicate against the stored value. For 
example, a pushed down
+    // `t = 12:00:00.100` would skip the row group that holds the matching row.
+    import testImplicits._
+    def time(s: String): LocalTime = LocalTime.parse(s)
+    def time3(s: String): String = s"CAST('$s' AS TIME(3))"
+    def toTime3(dt: DataType): DataType = dt match {
+      case s: StructType => StructType(s.map(f => f.copy(dataType = 
toTime3(f.dataType))))
+      case _: TimeType => TimeType(3)
+      case other => other
+    }
+    val inputDF = Seq(Some(time("12:00:00.1005")), Some(time("12:00:01")), 
None).toDF()
+    withNestedDataFrame(inputDF).foreach { case (df, colName, resultFun) =>
+      withTempPath { dir =>
+        df.coalesce(1).write.parquet(dir.getCanonicalPath)
+        val readDF = 
spark.read.schema(toTime3(df.schema).asInstanceOf[StructType])
+          .parquet(dir.getCanonicalPath)
+        def check(condition: String, expected: Seq[String]): Unit = {
+          checkAnswer(readDF.where(condition), expected.map(s => 
Row(resultFun(time(s)))))
+        }
+        withAllParquetReaders {
+          check(s"$colName = ${time3("12:00:00.1")}", Seq("12:00:00.1"))
+          check(s"$colName <=> ${time3("12:00:00.1")}", Seq("12:00:00.1"))
+          check(s"$colName != ${time3("12:00:00.1")}", Seq("12:00:01"))
+          check(s"$colName < ${time3("12:00:01")}", Seq("12:00:00.1"))
+          check(s"$colName <= ${time3("12:00:00.1")}", Seq("12:00:00.1"))
+          check(s"$colName > ${time3("12:00:00.1")}", Seq("12:00:01"))
+          check(s"$colName >= ${time3("12:00:00.1")}", Seq("12:00:00.1", 
"12:00:01"))
+          Seq(1, 10).foreach { threshold =>
+            withSQLConf(SQLConf.PARQUET_FILTER_PUSHDOWN_INFILTERTHRESHOLD.key 
-> s"$threshold") {
+              check(s"$colName IN (${time3("12:00:00.1")}, 
${time3("12:00:02")})",
+                Seq("12:00:00.1"))
+            }
+          }
+          checkAnswer(readDF.where(s"$colName IS NULL"), Row(resultFun(null)))
+          check(s"$colName IS NOT NULL", Seq("12:00:00.1", "12:00:01"))
+        }
+      }
+    }
+  }
+
+  test("SPARK-60358: TIME(MICROS) predicates with a value are not pushed down 
for TIME(0-5)") {

Review Comment:
   Yes, the new JIRA issue for `TIMESTAMP(NANOS)` will include these test cases.



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