@@ -25,6 +25,7 @@ import org.apache.gluten.extension.columnar.offload.OffloadSingleNode
2525import org .apache .gluten .sql .shims .SparkShimLoader
2626
2727import org .apache .spark .internal .Logging
28+ import org .apache .spark .sql .catalyst .expressions .Cast
2829import org .apache .spark .sql .execution ._
2930import org .apache .spark .sql .execution .aggregate .{HashAggregateExec , ObjectHashAggregateExec , SortAggregateExec }
3031import org .apache .spark .sql .execution .datasources .WriteFilesExec
@@ -33,7 +34,7 @@ import org.apache.spark.sql.execution.exchange.{BroadcastExchangeExec, ShuffleEx
3334import org .apache .spark .sql .execution .joins ._
3435import org .apache .spark .sql .execution .window .WindowExec
3536import org .apache .spark .sql .hive .HiveTableScanExecTransformer
36- import org .apache .spark .sql .types .{ArrayType , DataType , MapType , StructType }
37+ import org .apache .spark .sql .types .{ArrayType , DataType , MapType , StringType , StructType }
3738
3839object Validators {
3940 implicit class ValidatorBuilderImplicits (builder : Validator .Builder ) {
@@ -262,9 +263,23 @@ object Validators {
262263 case p if HiveTableScanExecTransformer .isHiveTableScan(p) => true
263264 case _ => false
264265 }
265- val hasNTZ = plan.output.exists(a => containsNTZ(a.dataType)) ||
266- plan.children.exists(_.output.exists(a => containsNTZ(a.dataType)))
267- if (isScan || ! hasNTZ) {
266+ // Allow: scans, NTZ-consuming ops (e.g. hour(timestamp_ntz)->int),
267+ // and explicit cast(varchar as timestamp_ntz). Fall back for everything else
268+ // that produces NTZ output (literals, pass-through, unknown conversions).
269+ val inputHasNTZ = plan.children.exists(_.output.exists(a => containsNTZ(a.dataType)))
270+ val outputHasNTZ = plan.output.exists(a => containsNTZ(a.dataType))
271+ val isVarcharToNtzCast = ! inputHasNTZ && outputHasNTZ && (plan match {
272+ case p : ProjectExec =>
273+ p.projectList.forall {
274+ expr =>
275+ ! containsNTZ(expr.dataType) || (expr match {
276+ case c : Cast => c.child.dataType == StringType
277+ case _ => false
278+ })
279+ }
280+ case _ => false
281+ })
282+ if (isScan || ! outputHasNTZ || isVarcharToNtzCast) {
268283 return pass()
269284 }
270285 }
0 commit comments