Rodrigo Coin Curvo created SPARK-59682:
------------------------------------------

             Summary: [SQL] Excluding RemoveRedundantAliases causes typed 
Dataset execution to fail with unsupported ObjectType projection
                 Key: SPARK-59682
                 URL: https://issues.apache.org/jira/browse/SPARK-59682
             Project: Spark
          Issue Type: Bug
          Components: Optimizer
    Affects Versions: 4.2.0
            Reporter: Rodrigo Coin Curvo


I had to exclude RemoveRedundantAliases from the optimizer due to having issues 
with execution plans. By excluding the rule everything worked so far. But then 
I ran into this problem while trying to use VACUUM with Delta Lake. 

I asked an LLM to investigate the root cause, and below is the report with a 
minimal reproduction code.
h2. Summary

Spark 4.2.0 fails to execute a valid typed Dataset plan when
{{RemoveRedundantAliases}} is excluded through
{{{}spark.sql.optimizer.excludedRules{}}}.

The failure is raised by {{{}InterpretedUnsafeProjection{}}}:

 

{{org.apache.spark.SparkException: [INTERNAL_ERROR]
The data type 'object' is not supported in generating a writer function for a
struct field, array element, map key or map value.}}

The same plan succeeds with the default optimizer rules. The failure does not
require Delta Lake, ThriftServer, Hive, object storage, or a catalog.
h2. Version

Tested with the current Apache Spark release 
[4.2.0|https://spark.apache.org/downloads]:

{{Apache Spark 4.2.0
Scala 2.13.18
Java 17.0.19}}
h2. Minimal reproduction

Save the following as {{repro.scala}} and run it with {{{}spark-shell{}}}:

 
{code:java}
import org.apache.spark.sql.Encoderscase class Status(path: String, length: 
Long, isDir: Boolean, modificationTime: Long)
case class NameAndSize(path: String, length: Long)val input = 
spark.createDataset(
  spark.sparkContext.parallelize(
    Seq(
      Status("probe/file-a.parquet", 1L, false, 1L),
      Status("probe/file-a.parquet", 2L, false, 2L),
      Status("probe/file-b.parquet", 3L, false, 3L)),
    numSlices = 2))(
  Encoders.product[Status])val keyed = input.groupByKey((f: Status) => 
f.path)(Encoders.STRING)
val grouped = keyed.mapGroups(
  (_, values) => values.maxBy(_.modificationTime)
)(Encoders.product[Status])val output = grouped.mapPartitions(
  iterator => iterator.map(f => NameAndSize(f.path, f.length))
)(Encoders.product[NameAndSize])println(s"EXCLUDED_RULES=${spark.conf.getOption("spark.sql.optimizer.excludedRules")}")
output.explain(true)
println(s"COUNT=${output.count()}"){code}
 

Run the failing case in a worker-executing local cluster:
{code:java}
docker run --rm -i \
  -v "$PWD/repro.scala:/tmp/repro.scala:ro" \
  apache/spark:4.2.0-scala2.13-java17-python3-r-ubuntu \
  bash -lc '/opt/spark/bin/spark-shell \
    --master local-cluster[2,1,1024] \
    --conf 
spark.sql.optimizer.excludedRules=org.apache.spark.sql.catalyst.optimizer.RemoveRedundantAliases
 \
    --conf spark.driver.host=127.0.0.1 \
    --conf spark.driver.bindAddress=127.0.0.1 < /tmp/repro.scala' {code}
For the control case, omit the {{spark.sql.optimizer.excludedRules}} option.
h2. Observed results

Control case, default optimizer rules:

{{EXCLUDED_RULES=None}}

{{COUNT=2}}

Failing case, with {{RemoveRedundantAliases}} excluded:

{{EXCLUDED_RULES=Some(org.apache.spark.sql.catalyst.optimizer.RemoveRedundantAliases)}}

{{org.apache.spark.SparkException: [INTERNAL_ERROR]
The data type 'object' is not supported in generating a writer function for a
struct field, array element, map key or map value.}}

The failing optimized/physical plan contains this additional projection:
{noformat}
SerializeFromObject (... NameAndSize fields ...)
+- MapPartitions, obj: NameAndSize
   +- Project [obj AS obj]
      +- MapGroups, obj: Status
         +- Sort
            +- Exchange{noformat}
The control plan removes {{Project [obj AS obj]}} and succeeds.
h2. Suspected cause

{{RemoveRedundantAliases}} removes the redundant object projection when enabled.
When excluded, the projection remains executable around a typed Dataset
{{{}ObjectType{}}}. {{ProjectExec}} then creates an {{{}UnsafeProjection{}}}, 
which reaches
the unsupported-type fallback in
{{{}InterpretedUnsafeProjection.generateFieldWriter{}}}.

The relevant Spark source files are:
 * 
[{{Optimizer.scala}}|https://github.com/apache/spark/blob/v4.2.0/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/Optimizer.scala]
 * 
[{{objects.scala}}|https://github.com/apache/spark/blob/v4.2.0/sql/core/src/main/scala/org/apache/spark/sql/execution/objects.scala]
 * 
[{{InterpretedUnsafeProjection.scala}}|https://github.com/apache/spark/blob/v4.2.0/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/InterpretedUnsafeProjection.scala]

h2. Expected behavior

Excluding an optimizer rule should not cause a valid typed Dataset plan to
fail with an internal unsupported-type exception. The plan should either
execute successfully or Spark should reject the unsupported configuration
explicitly during analysis/optimization.
h2. Workaround

Do not exclude:

{{org.apache.spark.sql.catalyst.optimizer.RemoveRedundantAliases}}



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to