-
Notifications
You must be signed in to change notification settings - Fork 28.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SPARK-34897][SQL] Support reconcile schemas based on index after nested column pruning #31993
Changes from 3 commits
2a3f136
9a9322b
e64eb75
a966bac
c04864a
fb81617
6112c9d
fc904ba
4d0b510
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -21,8 +21,9 @@ import org.apache.spark.sql.types._ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
object SchemaPruning { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* Filters the schema by the requested fields. For example, if the schema is struct<a:int, b:int>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* and given requested field are "a", the field "b" is pruned in the returned schema. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* Prunes the nested schema by the requested fields. For example, if the schema is | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* struct<a:int, b:int>, and given requested field are "a", the field "b" is pruned in the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* returned schema. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
cloud-fan marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* Note that schema field ordering at original schema is still preserved in pruned schema. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def pruneDataSchema( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -32,11 +33,10 @@ object SchemaPruning { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// in the resulting schema may differ from their ordering in the logical relation's | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// original schema | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
val mergedSchema = requestedRootFields | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.map { case root: RootField => StructType(Array(root.field)) } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.map { root: RootField => StructType(Array(root.field)) } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.reduceLeft(_ merge _) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
val dataSchemaFieldNames = dataSchema.fieldNames.toSet | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
val mergedDataSchema = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
StructType(mergedSchema.filter(f => dataSchemaFieldNames.contains(f.name))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
StructType(dataSchema.map(s => mergedSchema.find(_.name.equals(s.name)).getOrElse(s))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what's the actual difference? can you give a simple example? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems we don't prune anything from the root fields now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if this is the case please update the document of this method. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. spark.sql(
"""
|CREATE TABLE t1 (
| _col0 INT,
| _col1 STRING,
| _col2 STRUCT<c1: STRING, c2: STRING, c3: STRING, c4: BIGINT>)
|USING ORC
|""".stripMargin)
spark.sql("SELECT _col0, _col2.c1 FROM t1").show The origin schema is:
Before this PR, the
After this PR, the
It only prune nested schemas. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's wrong with the previous behavior? We can't sacrifice performance for all the cases only because the ORC by ordinal case is problematic. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes. spark/sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/FileSourceStrategy.scala Lines 208 to 213 in 7a5647a
spark/sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/v2/PushDownUtils.scala Lines 96 to 97 in e64eb75
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you provide the full code workflow to explain why this causes issues in ORC? I'm still not very sure. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is because Under nested column pruning, Spark will let data source use pruned schema as data schema to read files. E.g., Spark prune There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Hmm? In |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Sort the fields of mergedDataSchema according to their order in dataSchema, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// recursively. This makes mergedDataSchema a pruned schema of dataSchema | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sortLeftFieldsByRight(mergedDataSchema, dataSchema).asInstanceOf[StructType] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -81,6 +81,10 @@ object PushDownUtils extends PredicateHelper { | |
relation: DataSourceV2Relation, | ||
projects: Seq[NamedExpression], | ||
filters: Seq[Expression]): (Scan, Seq[AttributeReference]) = { | ||
val exprs = projects ++ filters | ||
val requiredColumns = AttributeSet(exprs.flatMap(_.references)) | ||
val neededOutput = relation.output.filter(requiredColumns.contains) | ||
|
||
scanBuilder match { | ||
case r: SupportsPushDownRequiredColumns if SQLConf.get.nestedSchemaPruningEnabled => | ||
val rootFields = SchemaPruning.identifyRootFields(projects, filters) | ||
|
@@ -89,14 +93,12 @@ object PushDownUtils extends PredicateHelper { | |
} else { | ||
new StructType() | ||
} | ||
r.pruneColumns(prunedSchema) | ||
val neededFieldNames = neededOutput.map(_.name).toSet | ||
r.pruneColumns(StructType(prunedSchema.filter(f => neededFieldNames.contains(f.name)))) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move filter logical from |
||
val scan = r.build() | ||
scan -> toOutputAttrs(scan.readSchema(), relation) | ||
|
||
case r: SupportsPushDownRequiredColumns => | ||
val exprs = projects ++ filters | ||
val requiredColumns = AttributeSet(exprs.flatMap(_.references)) | ||
val neededOutput = relation.output.filter(requiredColumns.contains) | ||
r.pruneColumns(neededOutput.toStructType) | ||
val scan = r.build() | ||
// always project, in case the relation's output has been updated and doesn't match | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the example is incorrect now. This method doesn't prune top-level fields.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change the example to
id int, struct<a:int, b:int>
.