You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spark.apache.org by do...@apache.org on 2026/10/20 00:32:55 UTC

(spark) branch master updated: [SPARK-60355][SQL] Support TIME precision widening in `UnwrapCastInBinaryComparison`

This is an automated email from the ASF dual-hosted git repository.

dongjoon-hyun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/spark.git


The following commit(s) were added to refs/heads/master by this push:
     new e792c8321f5a [SPARK-60355][SQL] Support TIME precision widening in `UnwrapCastInBinaryComparison`
e792c8321f5a is described below

commit e792c8321f5a3bf9c621e4c12f4f95fdedd2b378
Author: Dongjoon Hyun <do...@apache.org>
AuthorDate: Mon Oct 19 17:32:45 2026 -0700

    [SPARK-60355][SQL] Support TIME precision widening in `UnwrapCastInBinaryComparison`
    
    ### What changes were proposed in this pull request?
    
    This PR aims to support the cast from `TIME(p)` to `TIME(q)` (`p < q`) in `UnwrapCastInBinaryComparison`. Like a decimal cast to a larger scale, this cast keeps the value, so the existing algorithm for numeric types is reused: the literal is truncated to `TIME(p)`, and the comparison is adjusted if the truncation drops digits. For a `TIME(6)` column `t`:
    
    | Before | After |
    |---|---|
    | `CAST(t AS TIME(9)) = TIME '12:00:00.100000000'` | `t = TIME '12:00:00.1'` |
    | `CAST(t AS TIME(9)) = TIME '12:00:00.100000001'` | `isnull(t) AND null` |
    | `CAST(t AS TIME(9)) < TIME '12:00:00.100000001'` | `t <= TIME '12:00:00.1'` |
    
    `In` and `InSet` are supported in the same way, including the DSv2 runtime `IN` filter (SPARK-59301).
    
    ### Why are the changes needed?
    
    When a `TIME(p)` column is compared with a value of a larger precision, type coercion casts the column (SPARK-57585), and the cast blocks the filter pushdown. For example, for a `TIME(6)` Parquet column `t`, `WHERE t = TIME '12:00:00.100000000'` pushes down only `IsNotNull(t)`, while `WHERE t = TIME '12:00:00.1'` pushes down `EqualTo(t,12:00:00.100)`. This was raised in the review of #59571.
    
    ### Does this PR introduce _any_ user-facing change?
    
    No. More filters on `TIME` columns are pushed down, and the query results are the same.
    
    ### How was this patch tested?
    
    Pass the CIs with the newly added test cases in `UnwrapCastInBinaryComparisonSuite`, `ParquetFilterSuite` and `DataSourceV2StrategySuite`.
    
    ### Was this patch authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Opus 5.5
    
    Closes #59602 from dongjoon-hyun/SPARK-60355.
    
    Authored-by: Dongjoon Hyun <do...@apache.org>
    Signed-off-by: Dongjoon Hyun <do...@apache.org>
---
 .../optimizer/UnwrapCastInBinaryComparison.scala   | 31 ++++----
 .../UnwrapCastInBinaryComparisonSuite.scala        | 85 +++++++++++++++++++---
 .../datasources/parquet/ParquetFilterSuite.scala   | 31 ++++++++
 .../datasources/v2/DataSourceV2StrategySuite.scala | 13 +++-
 4 files changed, 135 insertions(+), 25 deletions(-)

diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/UnwrapCastInBinaryComparison.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/UnwrapCastInBinaryComparison.scala
index 3d4877526625..9d8d0a270d30 100644
--- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/UnwrapCastInBinaryComparison.scala
+++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/UnwrapCastInBinaryComparison.scala
@@ -43,7 +43,7 @@ import org.apache.spark.sql.types._
  *   1). `fromType` (of `fromExp`) and `toType` are of numeric types (i.e., short, int, float,
  *     decimal, etc), boolean type or datetime type
  *   2). `fromType` can be safely coerced to `toType` without precision loss (e.g., short to int,
- *     int to long, but not long to int, nor int to boolean)
+ *     int to long, time(3) to time(6), but not long to int, nor int to boolean)
  *
  * If the above conditions are satisfied, the rule checks to see if the literal `value` is within
  * range `(min, max)`, where `min` and `max` are the minimum and maximum value of `fromType`,
@@ -127,9 +127,10 @@ object UnwrapCastInBinaryComparison extends Rule[LogicalPlan] {
       unwrapCast(swap(exp)).map(swap)
 
     // In case both sides have numeric type, optimize the comparison by removing casts or
-    // moving cast to the literal side.
+    // moving cast to the literal side. The same applies to a time cast to a larger precision,
+    // which keeps the value like a decimal cast to a larger scale.
     case be @ BinaryComparison(
-      Cast(fromExp, toType: NumericType, _, _), Literal(value, literalType))
+      Cast(fromExp, toType @ (_: NumericType | _: TimeType), _, _), Literal(value, literalType))
         if canImplicitlyCast(fromExp, toType, literalType) && value != null =>
       Some(simplifyNumericComparison(be, fromExp, toType, value))
 
@@ -165,9 +166,10 @@ object UnwrapCastInBinaryComparison extends Rule[LogicalPlan] {
     // 1. this rule doesn't convert in when `in.list` is empty or `in.list` contains only null
     // values.
     // 2. this rule only handles the case when both `fromExp` and value in `in.list` are of numeric
-    // type.
+    // type, or of time type.
     // 3. this rule doesn't optimize In when `in.list` contains an expression that is not literal.
-    case in @ In(Cast(fromExp, toType: NumericType, tz, mode), list @ Seq(firstLit, _*))
+    case in @ In(Cast(fromExp, toType @ (_: NumericType | _: TimeType), tz, mode),
+        list @ Seq(firstLit, _*))
       if canImplicitlyCast(fromExp, toType, firstLit.dataType) && in.inSetConvertible =>
       val (nullList, canCastList) = castLiterals(fromExp.dataType, toType, list)
       if (nullList.isEmpty && canCastList.isEmpty) {
@@ -199,7 +201,7 @@ object UnwrapCastInBinaryComparison extends Rule[LogicalPlan] {
   private def simplifyNumericComparison(
       exp: BinaryComparison,
       fromExp: Expression,
-      toType: NumericType,
+      toType: DataType,
       value: Any): Expression = {
 
     val fromType = fromExp.dataType
@@ -273,7 +275,7 @@ object UnwrapCastInBinaryComparison extends Rule[LogicalPlan] {
     // decimal type), or that the literal `value` is within range `(min, max)`. For these, we
     // optimize by moving the cast to the literal side.
 
-    val newValue = Cast(Literal(value), fromType, ansiEnabled = false).eval()
+    val newValue = Cast(Literal(value, toType), fromType, ansiEnabled = false).eval()
     if (newValue == null) {
       // This means the cast failed, for instance, due to the value is not representable in the
       // narrower type. In this case we simply return the original expression.
@@ -418,8 +420,8 @@ object UnwrapCastInBinaryComparison extends Rule[LogicalPlan] {
   private[sql] def unwrapCastInSet(inSet: InSet): Option[(Expression, Set[Any])] = inSet match {
     // The same with `In` expression, the analyzer makes sure that the hset of InSet is already of
     // the same data type, so simply check `fromExp.dataType` can implicitly cast to `toType` and
-    // both `fromExp.dataType` and `toType` is numeric type or not.
-    case InSet(Cast(fromExp, toType: NumericType, _, _), hset)
+    // both `fromExp.dataType` and `toType` is numeric type (or time type) or not.
+    case InSet(Cast(fromExp, toType @ (_: NumericType | _: TimeType), _, _), hset)
         if hset.nonEmpty && canImplicitlyCast(fromExp, toType, toType) =>
       val (nullList, canCastList) =
         castLiterals(fromExp.dataType, toType, hset.map(v => Literal.create(v, toType)).toSeq)
@@ -433,7 +435,7 @@ object UnwrapCastInBinaryComparison extends Rule[LogicalPlan] {
    */
   private def castLiterals(
       fromType: DataType,
-      toType: NumericType,
+      toType: DataType,
       list: Seq[Expression]): (ArrayBuffer[Literal], ArrayBuffer[Literal]) = {
     // There are 3 kinds of literals in the list:
     // 1. null literals
@@ -457,7 +459,7 @@ object UnwrapCastInBinaryComparison extends Rule[LogicalPlan] {
     list.foreach {
       case lit @ Literal(null, _) => nullList += lit
       case NonNullLiteral(value, _) =>
-        val newValue = Cast(Literal(value), fromType, ansiEnabled = false).eval()
+        val newValue = Cast(Literal(value, toType), fromType, ansiEnabled = false).eval()
         val valueRoundTrip = Cast(Literal(newValue, fromType), toType).eval()
         if (newValue != null && ordering.compare(value, valueRoundTrip) == 0) {
           canCastList += Literal(newValue, fromType)
@@ -470,7 +472,7 @@ object UnwrapCastInBinaryComparison extends Rule[LogicalPlan] {
   /**
    * Check if the input `fromExp` can be safely cast to `toType` without any loss of precision,
    * i.e., the conversion is injective. Note this only handles the case when both sides are of
-   * numeric type.
+   * numeric type, or of time type.
    */
   private def canImplicitlyCast(
       fromExp: Expression,
@@ -478,7 +480,7 @@ object UnwrapCastInBinaryComparison extends Rule[LogicalPlan] {
       literalType: DataType): Boolean = {
     DataTypeUtils.sameType(toType, literalType) &&
       !fromExp.foldable &&
-      toType.isInstanceOf[NumericType] &&
+      (toType.isInstanceOf[NumericType] || toType.isInstanceOf[TimeType]) &&
       canUnwrapCast(fromExp.dataType, toType)
   }
 
@@ -490,6 +492,9 @@ object UnwrapCastInBinaryComparison extends Rule[LogicalPlan] {
     case (LongType, FloatType) => false
     case (LongType, DoubleType) => false
     case _ if from.isInstanceOf[NumericType] => Cast.canUpCast(from, to)
+    // A time(p) value has at most p fractional digits, so casting it to a larger precision keeps
+    // the value. Casting a literal back to time(p) truncates it, i.e. rounds it down.
+    case (f: TimeType, t: TimeType) => f.precision < t.precision
     case _ => false
   }
 
diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/optimizer/UnwrapCastInBinaryComparisonSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/optimizer/UnwrapCastInBinaryComparisonSuite.scala
index 9e8e31c69c3c..7b8ec6170f24 100644
--- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/optimizer/UnwrapCastInBinaryComparisonSuite.scala
+++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/optimizer/UnwrapCastInBinaryComparisonSuite.scala
@@ -17,7 +17,7 @@
 
 package org.apache.spark.sql.catalyst.optimizer
 
-import java.time.{LocalDate, LocalDateTime, ZoneId}
+import java.time.{LocalDate, LocalDateTime, LocalTime, ZoneId}
 
 import scala.collection.immutable.HashSet
 
@@ -43,7 +43,8 @@ class UnwrapCastInBinaryComparisonSuite extends PlanTest with ExpressionEvalHelp
   }
 
   val testRelation: LocalRelation = LocalRelation($"a".short, $"b".float,
-    $"c".decimal(5, 2), $"d".boolean, $"e".timestamp, $"f".timestampNTZ, $"g".date)
+    $"c".decimal(5, 2), $"d".boolean, $"e".timestamp, $"f".timestampNTZ, $"g".date,
+    AttributeReference("h", TimeType(6))())
   val f: BoundReference = $"a".short.canBeNull.at(0)
   val f2: BoundReference = $"b".float.canBeNull.at(1)
   val f3: BoundReference = $"c".decimal(5, 2).canBeNull.at(2)
@@ -51,6 +52,7 @@ class UnwrapCastInBinaryComparisonSuite extends PlanTest with ExpressionEvalHelp
   val f5: BoundReference = $"e".timestamp.notNull.at(4)
   val f6: BoundReference = $"f".timestampNTZ.canBeNull.at(5)
   val f7: BoundReference = $"g".date.canBeNull.at(6)
+  val f8: BoundReference = AttributeReference("h", TimeType(6))().canBeNull.at(7)
 
   test("unwrap casts when literal == max") {
     val v = Short.MaxValue
@@ -502,6 +504,56 @@ class UnwrapCastInBinaryComparisonSuite extends PlanTest with ExpressionEvalHelp
     doTest(tsNtzLit4)
   }
 
+  test("SPARK-60355: Support unwrap time type to time type with a larger precision") {
+    // The literal is representable as TIME(6), so the cast is moved to the literal side.
+    Seq(time("12:00:00.1", 9), time("00:00:00", 9), time("23:59:59.999999", 7)).foreach { lit =>
+      val v = Literal(lit.value, f8.dataType)
+      assertEquivalent(Cast(f8, lit.dataType) > lit, f8 > v)
+      assertEquivalent(Cast(f8, lit.dataType) >= lit, f8 >= v)
+      assertEquivalent(Cast(f8, lit.dataType) === lit, f8 === v)
+      assertEquivalent(Cast(f8, lit.dataType) <=> lit, f8 <=> v)
+      assertEquivalent(Cast(f8, lit.dataType) <= lit, f8 <= v)
+      assertEquivalent(Cast(f8, lit.dataType) < lit, f8 < v)
+      assertEquivalent(lit < Cast(f8, lit.dataType), v < f8)
+    }
+
+    // The literal has more fractional digits than TIME(6), so it is truncated to TIME(6), which
+    // rounds it down.
+    Seq(
+      time("12:00:00.100000001", 9) -> time("12:00:00.1", 6),
+      time("23:59:59.999999999", 9) -> time("23:59:59.999999", 6),
+      time("12:00:00.1000005", 7) -> time("12:00:00.1", 6)
+    ).foreach { case (lit, v) =>
+      assertEquivalent(Cast(f8, lit.dataType) > lit, f8 > v)
+      assertEquivalent(Cast(f8, lit.dataType) >= lit, f8 > v)
+      assertEquivalent(Cast(f8, lit.dataType) === lit, falseIfNotNull(f8))
+      assertEquivalent(Cast(f8, lit.dataType) <=> lit, FalseLiteral)
+      assertEquivalent(Cast(f8, lit.dataType) <= lit, f8 <= v)
+      assertEquivalent(Cast(f8, lit.dataType) < lit, f8 <= v)
+      assertEquivalent(lit < Cast(f8, lit.dataType), v < f8)
+    }
+
+    // Neither a null literal nor a cast to a smaller precision is unwrapped.
+    Seq(
+      Cast(f8, TimeType(9)) === Literal(null, TimeType(9)),
+      Cast(f8, TimeType(3)) === time("12:00:00.1", 3)
+    ).foreach { e =>
+      assertEquivalent(e, e)
+    }
+
+    checkInAndInSet(
+      In(Cast(f8, TimeType(9)),
+        Seq(time("12:00:00.1", 9), time("12:00:00.100000001", 9), time("00:00:00", 9))),
+      f8.in(time("12:00:00.1", 6), time("00:00:00", 6)))
+    checkInAndInSet(
+      In(Cast(f8, TimeType(9)), Seq(time("12:00:00.100000001", 9))),
+      falseIfNotNull(f8))
+    val nullLit = Literal(null, TimeType(9))
+    checkInAndInSet(
+      In(Cast(f8, TimeType(9)), Seq(nullLit, time("12:00:00.1", 9))),
+      f8.in(nullLit.cast(TimeType(6)), time("12:00:00.1", 6)))
+  }
+
   private val ts1 = LocalDateTime.of(2023, 1, 1, 23, 59, 59, 99999000)
   private val ts2 = LocalDateTime.of(2023, 1, 1, 23, 59, 59, 999998000)
   private val ts3 = LocalDateTime.of(9999, 12, 31, 23, 59, 59, 999999999)
@@ -509,6 +561,15 @@ class UnwrapCastInBinaryComparisonSuite extends PlanTest with ExpressionEvalHelp
 
   private val dt1 = java.sql.Date.valueOf("2023-01-01")
 
+  private val tm1 = LocalTime.of(12, 0, 0, 100000000)
+  private val tm2 = LocalTime.of(12, 0, 0, 100001000)
+  private val tm3 = LocalTime.of(23, 59, 59, 999999000)
+  private val tm4 = LocalTime.MIDNIGHT
+  private val tm5 = LocalTime.of(12, 0, 0, 99999000)
+
+  private def time(s: String, precision: Int): Literal =
+    Literal(LocalTime.parse(s).toNanoOfDay, TimeType(precision))
+
   private def castInt(e: Expression): Expression = Cast(e, IntegerType)
   private def castDouble(e: Expression): Expression = Cast(e, DoubleType)
   private def castDecimal2(e: Expression): Expression = Cast(e, DecimalType(10, 4))
@@ -530,16 +591,18 @@ class UnwrapCastInBinaryComparisonSuite extends PlanTest with ExpressionEvalHelp
 
     if (evaluate) {
       Seq(
-        (100.toShort, 3.14.toFloat, decimal2(100), true, ts1, ts1, dt1),
-        (-300.toShort, 3.1415927.toFloat, decimal2(-3000.50), false, ts2, ts2, dt1),
-        (null, Float.NaN, decimal2(12345.6789), null, null, null, null),
-        (null, null, null, null, null, null, null),
-        (Short.MaxValue, Float.PositiveInfinity, decimal2(Short.MaxValue), true, ts3, ts3, dt1),
-        (Short.MinValue, Float.NegativeInfinity, decimal2(Short.MinValue), false, ts4, ts4, dt1),
-        (0.toShort, Float.MaxValue, decimal2(0), null, null, null, null),
-        (0.toShort, Float.MinValue, decimal2(0.01), null, null, null, null)
+        (100.toShort, 3.14.toFloat, decimal2(100), true, ts1, ts1, dt1, tm1),
+        (-300.toShort, 3.1415927.toFloat, decimal2(-3000.50), false, ts2, ts2, dt1, tm2),
+        (null, Float.NaN, decimal2(12345.6789), null, null, null, null, null),
+        (null, null, null, null, null, null, null, null),
+        (Short.MaxValue, Float.PositiveInfinity, decimal2(Short.MaxValue), true, ts3, ts3, dt1,
+          tm3),
+        (Short.MinValue, Float.NegativeInfinity, decimal2(Short.MinValue), false, ts4, ts4, dt1,
+          tm4),
+        (0.toShort, Float.MaxValue, decimal2(0), null, null, null, null, tm5),
+        (0.toShort, Float.MinValue, decimal2(0.01), null, null, null, null, null)
       ).foreach(v => {
-        val row = create_row(v._1, v._2, v._3, v._4, v._5, v._6, v._7)
+        val row = create_row(v._1, v._2, v._3, v._4, v._5, v._6, v._7, v._8)
         checkEvaluation(e1, e2.eval(row), row)
       })
     }
diff --git a/sql/core/src/test/scala/org/apache/spark/sql/execution/datasources/parquet/ParquetFilterSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/execution/datasources/parquet/ParquetFilterSuite.scala
index cbcb1c9843f0..ab5925f34d99 100644
--- a/sql/core/src/test/scala/org/apache/spark/sql/execution/datasources/parquet/ParquetFilterSuite.scala
+++ b/sql/core/src/test/scala/org/apache/spark/sql/execution/datasources/parquet/ParquetFilterSuite.scala
@@ -2958,6 +2958,37 @@ abstract class ParquetFilterSuite extends ParquetTest with SharedSparkSession {
         |  optional int64 t(TIME(MICROS,true));
         |}""".stripMargin))
   }
+
+  test("SPARK-60355: filter pushdown - time compared with a time of a larger precision") {
+    // Comparing a TIME(6) column with a TIME(9) literal casts the column to TIME(9).
+    // UnwrapCastInBinaryComparison moves the cast to the literal side, so the comparison is pushed
+    // down. A literal with sub-microsecond digits is truncated to TIME(6) and the comparison is
+    // adjusted, e.g. `t < 12:00:00.100000001` is pushed as `t <= 12:00:00.1`.
+    def time(s: String): LocalTime = LocalTime.parse(s)
+    def time9(s: String): Literal = Literal(time(s).toNanoOfDay, TimeType(9))
+    val data = Seq("12:00:00.099999", "12:00:00.1", "12:00:00.100001").map(s => Tuple1(time(s)))
+    withNestedParquetDataFrame(data) { case (inputDF, colName, resultFun) =>
+      implicit val df: DataFrame = inputDF
+
+      val tAttr = df(colName).expr
+      assert(df(colName).expr.dataType === TimeType())
+
+      checkFilterPredicate(tAttr === time9("12:00:00.1"), classOf[Eq[_]],
+        resultFun(time("12:00:00.1")))
+      checkFilterPredicate(tAttr <=> time9("12:00:00.1"), classOf[Eq[_]],
+        resultFun(time("12:00:00.1")))
+      checkFilterPredicate(time9("12:00:00.1") < tAttr, classOf[Gt[_]],
+        resultFun(time("12:00:00.100001")))
+      checkFilterPredicate(tAttr > time9("12:00:00.100000001"), classOf[Gt[_]],
+        resultFun(time("12:00:00.100001")))
+      checkFilterPredicate(tAttr < time9("12:00:00.100000001"), classOf[LtEq[_]],
+        Seq("12:00:00.099999", "12:00:00.1").map(s => Row(resultFun(time(s)))))
+      checkFilterPredicate(
+        In(tAttr, Seq("12:00:00.1", "12:00:00.100000001", "12:00:00.100001").map(time9)),
+        classOf[Operators.Or],
+        Seq("12:00:00.1", "12:00:00.100001").map(s => Row(resultFun(time(s)))))
+    }
+  }
 }
 
 @ExtendedSQLTest
diff --git a/sql/core/src/test/scala/org/apache/spark/sql/execution/datasources/v2/DataSourceV2StrategySuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/execution/datasources/v2/DataSourceV2StrategySuite.scala
index f5380ea085c0..fabebe4f4f69 100644
--- a/sql/core/src/test/scala/org/apache/spark/sql/execution/datasources/v2/DataSourceV2StrategySuite.scala
+++ b/sql/core/src/test/scala/org/apache/spark/sql/execution/datasources/v2/DataSourceV2StrategySuite.scala
@@ -17,6 +17,7 @@
 
 package org.apache.spark.sql.execution.datasources.v2
 
+import java.time.LocalTime
 import java.util.{EnumSet, OptionalLong}
 
 import org.apache.spark.SparkConf
@@ -42,7 +43,7 @@ import org.apache.spark.sql.internal.SQLConf
 import org.apache.spark.sql.internal.connector.SupportsPushDownCatalystFilters
 import org.apache.spark.sql.sources.{BaseRelation, TableScan}
 import org.apache.spark.sql.test.SharedSparkSession
-import org.apache.spark.sql.types.{ArrayType, BooleanType, DoubleType, FloatType, IntegerType, LongType, StringType, StructField, StructType, TimestampType, VariantType}
+import org.apache.spark.sql.types.{ArrayType, BooleanType, DoubleType, FloatType, IntegerType, LongType, StringType, StructField, StructType, TimestampType, TimeType, VariantType}
 import org.apache.spark.sql.util.CaseInsensitiveStringMap
 import org.apache.spark.udf.worker.UDFWorkerSpecification
 import org.apache.spark.unsafe.types.UTF8String
@@ -1918,6 +1919,16 @@ class DataSourceV2StrategySuite extends SharedSparkSession {
       runtimeFilter(Cast($"cint".int, StringType), Array[Any](UTF8String.fromString("1")))).isEmpty)
   }
 
+  test("SPARK-60355: translate runtime IN filter on a time column cast to a larger precision") {
+    // values with more fractional digits than the column type are dropped and nulls are kept,
+    // the same as `UnwrapCastInBinaryComparison` does for `InSet`
+    val noon = LocalTime.NOON.toNanoOfDay
+    val ctime = AttributeReference("ctime", TimeType(6))()
+    val in = runtimeFilter(Cast(ctime, TimeType(9)), Array[Any](noon, noon + 1, null))
+    checkRuntimeFilter(in, "ctime",
+      Set(LiteralValue(noon, TimeType(6)), LiteralValue(null, TimeType(6))))
+  }
+
   private def runtimeFilter(child: Expression, values: Array[Any]): InSubqueryExec = {
     val plan = SubqueryExec("dpp", LocalTableScanExec(Nil, Nil, None))
     InSubqueryExec(child, plan, ExprId(0), isDynamicPruning = true, resultBroadcast = null,


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@spark.apache.org
For additional commands, e-mail: commits-help@spark.apache.org