peter-toth commented on code in PR #59996:
URL: https://github.com/apache/spark/pull/59996#discussion_r4443792649


##########
sql/core/src/test/scala/org/apache/spark/sql/DatasetOptimizationSuite.scala:
##########
@@ -132,6 +132,29 @@ class DatasetOptimizationSuite extends SharedSparkSession {
     }
   }
 
+  test("Nested projections bind to the fields emitted by the serializer") {
+    val ds = spark.range(1).map { _ =>
+      Tuple1((Map("k" -> ("map-a", "map-b")), ("other-a", "other-b")))
+    }
+    val query = ds.selectExpr("_1._1['k']._2", "_1._2._1")
+    // The map and its sibling share a serializer. Visiting the map converters 
twice causes
+    // pruning to fall back to the original schema, so the projection must use 
its ordinals.
+    assert(query.queryExecution.optimizedPlan.collectFirst {
+      case s: SerializeFromObject => s.schema
+    }.contains(ds.schema))
+    checkAnswer(query, Row("map-b", "other-a"))
+  }
+
+  test("Nested projections resolve case-insensitive references against the 
serializer") {
+    withSQLConf(SQLConf.CASE_SENSITIVE.key -> "false") {
+      val input = Seq((Seq(("unused", "wanted")), 1)).toDF("Items", "Id")
+      val ds = input.map(row => row)(ExpressionEncoder(input.schema))
+      val query = ds.selectExpr("items[0]._2", "ITEMS._2", "iD")
+      checkAnswer(query, Row("wanted", Seq("wanted"), 1))

Review Comment:
   **Finding 3.** Both spellings here select `_2`, so this test can't tell 
whether `identifyRootFields` sees the normalized names. I changed 
`objects.scala:235` to `SchemaPruning.identifyRootFields(p.projectList, 
Seq.empty)`:
   
   - both new tests still pass
   - `selectExpr("s._1", "S._3")` over a struct column `S` fails with 
`FIELD_NOT_FOUND`, as on base
   - this test with `ITEMS._1` fails the same way
   
   With the change below, the test passes here, fails on base 
(`ArrayIndexOutOfBoundsException`), and fails with that mutation 
(`FIELD_NOT_FOUND`):
   
   ```suggestion
         val query = ds.selectExpr("items[0]._2", "ITEMS._1", "iD")
         checkAnswer(query, Row("wanted", Seq("unused"), 1))
   ```
   



##########
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/objects.scala:
##########
@@ -221,25 +222,36 @@ object ObjectSerializerPruning extends Rule[LogicalPlan] {
       val usedRefs = p.references
       val prunedSerializer = s.serializer.filter(usedRefs.contains)
 
-      val rootFields = SchemaPruning.identifyRootFields(p.projectList, 
Seq.empty)
-
-      if (conf.serializerNestedSchemaPruningEnabled && rootFields.nonEmpty) {
-        // Prunes nested fields in serializers.
-        val prunedSchema = SchemaPruning.pruneSchema(
-          DataTypeUtils.fromAttributes(prunedSerializer.map(_.toAttribute)), 
rootFields)
-        val nestedPrunedSerializer = prunedSerializer.zipWithIndex.map { case 
(serializer, idx) =>
-          pruneSerializer(serializer, prunedSchema(idx).dataType)
-        }
-
-        // Builds new projection.
-        val projectionOverSchema = ProjectionOverSchema(prunedSchema, 
AttributeSet(s.output))
-        val newProjects = p.projectList.map(_.transformDown {
-          case projectionOverSchema(expr) => expr
-        }).map { case expr: NamedExpression => expr }
-        p.copy(projectList = newProjects,
-          child = SerializeFromObject(nestedPrunedSerializer, s.child))
-      } else {
+      if (!conf.serializerNestedSchemaPruningEnabled) {
         p.copy(child = SerializeFromObject(prunedSerializer, s.child))
+      } else {
+        // Normalize the root name so references such as items and ITEMS do 
not become
+        // distinct fields in pruneSchema during case-insensitive analysis.

Review Comment:
   **Finding 4.** Minor: the normalized names also feed the rebuild below. 
`items[0]._2` alone has one root, so `pruneSchema` handles it fine. It fails on 
base because `ProjectionOverSchema` looks up `a.name` by exact name 
(`ProjectionOverSchema.scala:40`) and leaves the access unchanged. This keeps 
the concrete case from 
[r4437932629](https://github.com/apache/spark/pull/59996#discussion_r4437932629)
 and adds the second use:
   
   ```suggestion
           // Use the serializer's names for references to its output. 
Otherwise:
           // - pruneSchema treats items and ITEMS as distinct roots.
           // - ProjectionOverSchema leaves an access through items unchanged, 
since it looks up
           //   Items by exact name.
   ```
   



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