Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,12 @@ object NestedColumnAliasing {
case _ => false
}

// Note that when we group by extractors with their references, we should remove
// cosmetic variations.
val exclusiveAttrSet = AttributeSet(exclusiveAttrs ++ otherRootReferences)
val aliasSub = nestedFieldReferences.asInstanceOf[Seq[ExtractValue]]
.filter(!_.references.subsetOf(exclusiveAttrSet))
.groupBy(_.references.head)
.groupBy(_.references.head.canonicalized.asInstanceOf[Attribute])

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch, @viirya @frankyin-factual !

.flatMap { case (attr, nestedFields: Seq[ExtractValue]) =>
// Remove redundant `ExtractValue`s if they share the same parent nest field.
// For example, when `a.b` and `a.b.c` are in project list, we only need to alias `a.b`.
Expand All @@ -174,9 +176,12 @@ object NestedColumnAliasing {

// If all nested fields of `attr` are used, we don't need to introduce new aliases.
// By default, ColumnPruning rule uses `attr` already.
// Note that we need to remove cosmetic variations first, so we only count a
// nested field once.
if (nestedFieldToAlias.nonEmpty &&
nestedFieldToAlias
.map { case (nestedField, _) => totalFieldNum(nestedField.dataType) }
dedupNestedFields.map(_.canonicalized.asInstanceOf[ExtractValue])

@maropu maropu Jul 5, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we don't need the cast here?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part is related to the query failure in the test? Looks it is just an optimization?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, we cannot avoid the generating duplicated aliases (name#1.first AS _gen_alias_52#52 and name#1.first AS _gen_alias_53#53) below? Is this technically difficult? (This is not related to this PR and just a question)

scala> sql("select name.first from contact_alias").explain()
== Physical Plan ==
*(2) Project [_gen_alias_52#52 AS first#50]
+- Exchange hashpartitioning(_gen_alias_53#53, _gen_alias_54#54, 100), false, [id=#46]
   +- *(1) Project [name#1.first AS _gen_alias_52#52, name#1.first AS _gen_alias_53#53, name#1.last AS _gen_alias_54#54]
      +- FileScan parquet [name#1,p#8] Batched: false, DataFilters: [], Format: Parquet, Location: InMemoryFileIndex[file:/tmp/contacts], PartitionFilters: [], PushedFilters: [], ReadSchema: struct<name:struct<first:string,last:string>>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to cosmetic variations, Extractors with different qualifiers, for example, will cause incorrect total number of fields.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the duplicated aliases, we can avoid it. We can work on canonicalized extractors when generating aliases.

But we also need to convert coming extractors to canonicalized versions when we look up into the alias map.

Currently the code looks clear. And it seems not a big deal, and I think it is rare case that there are multiple extractors with cosmetic difference. So currently I don't try to do that.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the duplicated aliases, we can avoid it. We can work on canonicalized extractors when generating aliases.
But we also need to convert coming extractors to canonicalized versions when we look up into the alias map.
Currently the code looks clear. And it seems not a big deal, and I think it is rare case that there are multiple extractors with cosmetic difference. So currently I don't try to do that.

Thanks for the explanation. Looks okay.

.distinct
.map { nestedField => totalFieldNum(nestedField.dataType) }
.sum < totalFieldNum(attr.dataType)) {
Some(attr.exprId -> nestedFieldToAlias)
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,18 @@ abstract class SchemaPruningSuite
Row(Row("Janet", null, "Jones"), "Jones") ::Nil)
}

testSchemaPruning("SPARK-32163: nested pruning should work even with cosmetic variations") {
withTempView("contact_alias") {
sql("select * from contacts")
.repartition(100, col("name.first"), col("name.last"))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This issue cannot happen in branch-3.0?

private def canProjectPushThrough(plan: LogicalPlan) = plan match {
case _: GlobalLimit => true
case _: LocalLimit => true
case _: Repartition => true
case _: Sample => true
case _ => false

@viirya viirya Jul 5, 2020

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test case is only for master branch. However this issue can happen in branch-3.0 too. I added another new test here, which is for branch-3.0.

But when we backport this to branch-3.0, we need to remove first test case as it will fail on checkScan(query, "struct<name:struct<first:string,last:string>>"), because branch-3.0 doesn't prune for repartition by expression.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, thanks for adding the test.

.selectExpr("name").createOrReplaceTempView("contact_alias")

val query = sql("select name.first from contact_alias")
checkScan(query, "struct<name:struct<first:string,last:string>>")
checkAnswer(query, Row("Jane") :: Row("John") :: Row("Jim") :: Row("Janet") ::Nil)
}
}

protected def testSchemaPruning(testName: String)(testThunk: => Unit): Unit = {
test(s"Spark vectorized reader - without partition data column - $testName") {
withSQLConf(vectorizedReaderEnabledKey -> "true") {
Expand Down