You are viewing a plain text version of this content. The canonical link for it is here.
Posted to reviews@spark.apache.org by GitBox <gi...@apache.org> on 2022/12/16 02:57:06 UTC
[GitHub] [spark] HeartSaVioR opened a new pull request, #39082: [SPARK-41539][SQL] Remap stats and constraints against output in logical plan for LogicalRDD
HeartSaVioR opened a new pull request, #39082:
URL: https://github.com/apache/spark/pull/39082
### What changes were proposed in this pull request?
This PR proposes to remap stats and constraints against the output in logical for LogicalRDD, like we remap stats and constraints against the "new" output when we call newInstance.
### Why are the changes needed?
The output in logical plan and optimized plan can be "slightly" different (we observed the difference of exprId), and then the query can fail due to the invalid attribute reference(s) in stats and constraints for LogicalRDD.
### Does this PR introduce _any_ user-facing change?
No.
### How was this patch tested?
Modified test cases.
--
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: reviews-unsubscribe@spark.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org
[GitHub] [spark] HeartSaVioR commented on pull request #39082: [SPARK-41539][SQL] Remap stats and constraints against output in logical plan for LogicalRDD
Posted by GitBox <gi...@apache.org>.
HeartSaVioR commented on PR #39082:
URL: https://github.com/apache/spark/pull/39082#issuecomment-1360891268
Thanks all for reviewing! Merging to master.
--
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: reviews-unsubscribe@spark.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org
[GitHub] [spark] HeartSaVioR commented on a diff in pull request #39082: [SPARK-41539][SQL] Remap stats and constraints against output in logical plan for LogicalRDD
Posted by GitBox <gi...@apache.org>.
HeartSaVioR commented on code in PR #39082:
URL: https://github.com/apache/spark/pull/39082#discussion_r1053715659
##########
sql/core/src/main/scala/org/apache/spark/sql/execution/ExistingRDD.scala:
##########
@@ -183,16 +172,80 @@ object LogicalRDD {
}
}
+ val logicalPlan = originDataset.logicalPlan
val optimizedPlan = originDataset.queryExecution.optimizedPlan
val executedPlan = originDataset.queryExecution.executedPlan
+ val (stats, constraints) = rewriteStatsAndConstraints(logicalPlan, optimizedPlan)
+
LogicalRDD(
originDataset.logicalPlan.output,
rdd,
firstLeafPartitioning(executedPlan.outputPartitioning),
executedPlan.outputOrdering,
isStreaming
- )(originDataset.sparkSession, Some(optimizedPlan.stats), Some(optimizedPlan.constraints))
Review Comment:
We tried this before, and realized that this could break existing use case when someone is trying to checkpoint "subtree" of logical plan. Given that we know exprId can differ, it would break expressions in above node(s).
The actual example is merge into materialize source of Delta Lake. This performs join with source DF and target table with merge condition as join condition (here the condition is built with logical plan), and source DF can checkpoint and be replaced with LogicalRDD. It should produce the same output to not break join condition.
https://github.com/delta-io/delta/blob/4e51a9969708080b9ac002462f20f64000288978/core/src/main/scala/org/apache/spark/sql/delta/commands/MergeIntoCommand.scala#L458-L472
https://github.com/delta-io/delta/blob/master/core/src/main/scala/org/apache/spark/sql/delta/commands/merge/MergeIntoMaterializeSource.scala#L245-L251
--
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: reviews-unsubscribe@spark.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org
[GitHub] [spark] viirya commented on a diff in pull request #39082: [SPARK-41539][SQL] Remap stats and constraints against output in logical plan for LogicalRDD
Posted by GitBox <gi...@apache.org>.
viirya commented on code in PR #39082:
URL: https://github.com/apache/spark/pull/39082#discussion_r1054008208
##########
sql/core/src/main/scala/org/apache/spark/sql/execution/ExistingRDD.scala:
##########
@@ -183,16 +172,80 @@ object LogicalRDD {
}
}
+ val logicalPlan = originDataset.logicalPlan
val optimizedPlan = originDataset.queryExecution.optimizedPlan
val executedPlan = originDataset.queryExecution.executedPlan
+ val (stats, constraints) = rewriteStatsAndConstraints(logicalPlan, optimizedPlan)
+
LogicalRDD(
originDataset.logicalPlan.output,
rdd,
firstLeafPartitioning(executedPlan.outputPartitioning),
executedPlan.outputOrdering,
isStreaming
- )(originDataset.sparkSession, Some(optimizedPlan.stats), Some(optimizedPlan.constraints))
+ )(originDataset.sparkSession, stats, constraints)
+ }
+
+ private[sql] def buildOutputAssocForRewrite(
+ source: Seq[Attribute],
+ destination: Seq[Attribute]): Option[Map[Attribute, Attribute]] = {
+ // We check the name and type, allowing nullability, exprId, metadata, qualifier be different
+ // E.g. This could happen during optimization phase.
+ val rewrite = source.zip(destination).flatMap { case (attr1, attr2) =>
+ if (attr1.name == attr2.name && attr1.dataType == attr2.dataType) {
+ Some(attr1 -> attr2)
+ } else {
+ None
+ }
+ }.toMap
+
+ if (rewrite.size == source.size) {
+ Some(rewrite)
+ } else {
+ None
+ }
+ }
+
+ private[sql] def rewriteStatsAndConstraints(
+ logicalPlan: LogicalPlan,
+ optimizedPlan: LogicalPlan): (Option[Statistics], Option[ExpressionSet]) = {
+ val rewrite = buildOutputAssocForRewrite(optimizedPlan.output, logicalPlan.output)
+
+ rewrite.map { rw =>
+ val rewrittenStatistics = rewriteStatistics(optimizedPlan.stats, rw)
+ val rewrittenConstraints = rewriteConstraints(optimizedPlan.constraints, rw)
+
+ (Some(rewrittenStatistics), Some(rewrittenConstraints))
+ }.getOrElse {
+ // can't rewrite stats and constraints, give up
+ logWarning("The output columns are expected to the same (for name and type) for output " +
Review Comment:
I guess without stats and constraints (when you cannot build the valid map), the query should still be executed to the same result as they are only for optimizing query. So I think even for the case, we still can run the query instead of failing it.
And, even you fail the query, I'm not sure what users can do to make valid stats and constraints avaiable?
##########
sql/core/src/main/scala/org/apache/spark/sql/execution/ExistingRDD.scala:
##########
@@ -183,16 +172,80 @@ object LogicalRDD {
}
}
+ val logicalPlan = originDataset.logicalPlan
val optimizedPlan = originDataset.queryExecution.optimizedPlan
val executedPlan = originDataset.queryExecution.executedPlan
+ val (stats, constraints) = rewriteStatsAndConstraints(logicalPlan, optimizedPlan)
+
LogicalRDD(
originDataset.logicalPlan.output,
rdd,
firstLeafPartitioning(executedPlan.outputPartitioning),
executedPlan.outputOrdering,
isStreaming
- )(originDataset.sparkSession, Some(optimizedPlan.stats), Some(optimizedPlan.constraints))
+ )(originDataset.sparkSession, stats, constraints)
+ }
+
+ private[sql] def buildOutputAssocForRewrite(
+ source: Seq[Attribute],
+ destination: Seq[Attribute]): Option[Map[Attribute, Attribute]] = {
+ // We check the name and type, allowing nullability, exprId, metadata, qualifier be different
+ // E.g. This could happen during optimization phase.
+ val rewrite = source.zip(destination).flatMap { case (attr1, attr2) =>
+ if (attr1.name == attr2.name && attr1.dataType == attr2.dataType) {
+ Some(attr1 -> attr2)
+ } else {
+ None
+ }
+ }.toMap
+
+ if (rewrite.size == source.size) {
+ Some(rewrite)
+ } else {
+ None
+ }
+ }
+
+ private[sql] def rewriteStatsAndConstraints(
+ logicalPlan: LogicalPlan,
+ optimizedPlan: LogicalPlan): (Option[Statistics], Option[ExpressionSet]) = {
+ val rewrite = buildOutputAssocForRewrite(optimizedPlan.output, logicalPlan.output)
+
+ rewrite.map { rw =>
+ val rewrittenStatistics = rewriteStatistics(optimizedPlan.stats, rw)
+ val rewrittenConstraints = rewriteConstraints(optimizedPlan.constraints, rw)
+
+ (Some(rewrittenStatistics), Some(rewrittenConstraints))
+ }.getOrElse {
+ // can't rewrite stats and constraints, give up
+ logWarning("The output columns are expected to the same (for name and type) for output " +
Review Comment:
I guess without stats and constraints (when you cannot build the valid map), the query should still be executed to the same result as they are only for optimizing query. So I think even for the case, we still can run the query instead of failing it.
And, even you fail the query, I'm not sure what users can do to make valid stats and constraints available?
--
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: reviews-unsubscribe@spark.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org
[GitHub] [spark] HeartSaVioR commented on a diff in pull request #39082: [SPARK-41539][SQL] Remap stats and constraints against output in logical plan for LogicalRDD
Posted by GitBox <gi...@apache.org>.
HeartSaVioR commented on code in PR #39082:
URL: https://github.com/apache/spark/pull/39082#discussion_r1054009827
##########
sql/core/src/main/scala/org/apache/spark/sql/execution/ExistingRDD.scala:
##########
@@ -183,16 +172,80 @@ object LogicalRDD {
}
}
+ val logicalPlan = originDataset.logicalPlan
val optimizedPlan = originDataset.queryExecution.optimizedPlan
val executedPlan = originDataset.queryExecution.executedPlan
+ val (stats, constraints) = rewriteStatsAndConstraints(logicalPlan, optimizedPlan)
+
LogicalRDD(
originDataset.logicalPlan.output,
rdd,
firstLeafPartitioning(executedPlan.outputPartitioning),
executedPlan.outputOrdering,
isStreaming
- )(originDataset.sparkSession, Some(optimizedPlan.stats), Some(optimizedPlan.constraints))
+ )(originDataset.sparkSession, stats, constraints)
+ }
+
+ private[sql] def buildOutputAssocForRewrite(
+ source: Seq[Attribute],
+ destination: Seq[Attribute]): Option[Map[Attribute, Attribute]] = {
+ // We check the name and type, allowing nullability, exprId, metadata, qualifier be different
+ // E.g. This could happen during optimization phase.
+ val rewrite = source.zip(destination).flatMap { case (attr1, attr2) =>
+ if (attr1.name == attr2.name && attr1.dataType == attr2.dataType) {
+ Some(attr1 -> attr2)
+ } else {
+ None
+ }
+ }.toMap
+
+ if (rewrite.size == source.size) {
+ Some(rewrite)
+ } else {
+ None
+ }
+ }
+
+ private[sql] def rewriteStatsAndConstraints(
+ logicalPlan: LogicalPlan,
+ optimizedPlan: LogicalPlan): (Option[Statistics], Option[ExpressionSet]) = {
+ val rewrite = buildOutputAssocForRewrite(optimizedPlan.output, logicalPlan.output)
+
+ rewrite.map { rw =>
+ val rewrittenStatistics = rewriteStatistics(optimizedPlan.stats, rw)
+ val rewrittenConstraints = rewriteConstraints(optimizedPlan.constraints, rw)
+
+ (Some(rewrittenStatistics), Some(rewrittenConstraints))
+ }.getOrElse {
+ // can't rewrite stats and constraints, give up
+ logWarning("The output columns are expected to the same (for name and type) for output " +
Review Comment:
If the query is failing, it clearly denotes the internal bug of Spark and we expect users to report this. But I kind of agree that they shouldn't get blocked with this if the latter part still works fine.
--
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: reviews-unsubscribe@spark.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org
[GitHub] [spark] HeartSaVioR commented on pull request #39082: [SPARK-41539][SQL] Remap stats and constraints against output in logical plan for LogicalRDD
Posted by GitBox <gi...@apache.org>.
HeartSaVioR commented on PR #39082:
URL: https://github.com/apache/spark/pull/39082#issuecomment-1354129713
cc. @cloud-fan @viirya @juliuszsompolski Please take a look. Thanks!
--
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: reviews-unsubscribe@spark.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org
[GitHub] [spark] HeartSaVioR commented on a diff in pull request #39082: [SPARK-41539][SQL] Remap stats and constraints against output in logical plan for LogicalRDD
Posted by GitBox <gi...@apache.org>.
HeartSaVioR commented on code in PR #39082:
URL: https://github.com/apache/spark/pull/39082#discussion_r1050326351
##########
sql/core/src/main/scala/org/apache/spark/sql/execution/ExistingRDD.scala:
##########
@@ -183,16 +172,80 @@ object LogicalRDD {
}
}
+ val logicalPlan = originDataset.logicalPlan
val optimizedPlan = originDataset.queryExecution.optimizedPlan
val executedPlan = originDataset.queryExecution.executedPlan
+ val (stats, constraints) = rewriteStatsAndConstraints(logicalPlan, optimizedPlan)
+
LogicalRDD(
originDataset.logicalPlan.output,
rdd,
firstLeafPartitioning(executedPlan.outputPartitioning),
executedPlan.outputOrdering,
isStreaming
- )(originDataset.sparkSession, Some(optimizedPlan.stats), Some(optimizedPlan.constraints))
+ )(originDataset.sparkSession, stats, constraints)
+ }
+
+ private[sql] def buildOutputAssocForRewrite(
+ source: Seq[Attribute],
+ destination: Seq[Attribute]): Option[Map[Attribute, Attribute]] = {
+ // We check the name and type, allowing nullability, exprId, metadata, qualifier be different
+ // E.g. This could happen during optimization phase.
+ val rewrite = source.zip(destination).flatMap { case (attr1, attr2) =>
+ if (attr1.name == attr2.name && attr1.dataType == attr2.dataType) {
+ Some(attr1 -> attr2)
+ } else {
+ None
+ }
+ }.toMap
+
+ if (rewrite.size == source.size) {
+ Some(rewrite)
+ } else {
+ None
+ }
+ }
+
+ private[sql] def rewriteStatsAndConstraints(
+ logicalPlan: LogicalPlan,
+ optimizedPlan: LogicalPlan): (Option[Statistics], Option[ExpressionSet]) = {
+ val rewrite = buildOutputAssocForRewrite(optimizedPlan.output, logicalPlan.output)
+
+ rewrite.map { rw =>
+ val rewrittenStatistics = rewriteStatistics(optimizedPlan.stats, rw)
+ val rewrittenConstraints = rewriteConstraints(optimizedPlan.constraints, rw)
+
+ (Some(rewrittenStatistics), Some(rewrittenConstraints))
+ }.getOrElse {
+ // can't rewrite stats and constraints, give up
+ logWarning("The output columns are expected to the same (for name and type) for output " +
Review Comment:
This is something I want to discuss. I see the difference of exprId which makes sense, but is it possible to see other difference as well? If it's possible, is LogicalRDD instance even safe?
Suppose we are here, should we raise an internal error instead, or it is still a valid case and we log and give up?
--
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: reviews-unsubscribe@spark.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org
[GitHub] [spark] HeartSaVioR commented on a diff in pull request #39082: [SPARK-41539][SQL] Remap stats and constraints against output in logical plan for LogicalRDD
Posted by GitBox <gi...@apache.org>.
HeartSaVioR commented on code in PR #39082:
URL: https://github.com/apache/spark/pull/39082#discussion_r1051685454
##########
sql/core/src/main/scala/org/apache/spark/sql/execution/ExistingRDD.scala:
##########
@@ -183,16 +172,80 @@ object LogicalRDD {
}
}
+ val logicalPlan = originDataset.logicalPlan
val optimizedPlan = originDataset.queryExecution.optimizedPlan
val executedPlan = originDataset.queryExecution.executedPlan
+ val (stats, constraints) = rewriteStatsAndConstraints(logicalPlan, optimizedPlan)
+
LogicalRDD(
originDataset.logicalPlan.output,
rdd,
firstLeafPartitioning(executedPlan.outputPartitioning),
executedPlan.outputOrdering,
isStreaming
- )(originDataset.sparkSession, Some(optimizedPlan.stats), Some(optimizedPlan.constraints))
+ )(originDataset.sparkSession, stats, constraints)
+ }
+
+ private[sql] def buildOutputAssocForRewrite(
+ source: Seq[Attribute],
+ destination: Seq[Attribute]): Option[Map[Attribute, Attribute]] = {
+ // We check the name and type, allowing nullability, exprId, metadata, qualifier be different
+ // E.g. This could happen during optimization phase.
+ val rewrite = source.zip(destination).flatMap { case (attr1, attr2) =>
+ if (attr1.name == attr2.name && attr1.dataType == attr2.dataType) {
+ Some(attr1 -> attr2)
+ } else {
+ None
+ }
+ }.toMap
+
+ if (rewrite.size == source.size) {
+ Some(rewrite)
+ } else {
+ None
+ }
+ }
+
+ private[sql] def rewriteStatsAndConstraints(
+ logicalPlan: LogicalPlan,
+ optimizedPlan: LogicalPlan): (Option[Statistics], Option[ExpressionSet]) = {
+ val rewrite = buildOutputAssocForRewrite(optimizedPlan.output, logicalPlan.output)
+
+ rewrite.map { rw =>
+ val rewrittenStatistics = rewriteStatistics(optimizedPlan.stats, rw)
+ val rewrittenConstraints = rewriteConstraints(optimizedPlan.constraints, rw)
+
+ (Some(rewrittenStatistics), Some(rewrittenConstraints))
+ }.getOrElse {
+ // can't rewrite stats and constraints, give up
+ logWarning("The output columns are expected to the same (for name and type) for output " +
Review Comment:
I've talked offline with @sigmod , his answer is
* expr id can change
* nullability can change (true to false, not vice versa)
* data type and order shouldn't change
That said, it seems to be valid to assert here, though I'd hear more voice on this.
--
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: reviews-unsubscribe@spark.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org
[GitHub] [spark] HeartSaVioR closed pull request #39082: [SPARK-41539][SQL] Remap stats and constraints against output in logical plan for LogicalRDD
Posted by GitBox <gi...@apache.org>.
HeartSaVioR closed pull request #39082: [SPARK-41539][SQL] Remap stats and constraints against output in logical plan for LogicalRDD
URL: https://github.com/apache/spark/pull/39082
--
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: reviews-unsubscribe@spark.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org
[GitHub] [spark] HeartSaVioR commented on a diff in pull request #39082: [SPARK-41539][SQL] Remap stats and constraints against output in logical plan for LogicalRDD
Posted by GitBox <gi...@apache.org>.
HeartSaVioR commented on code in PR #39082:
URL: https://github.com/apache/spark/pull/39082#discussion_r1053715659
##########
sql/core/src/main/scala/org/apache/spark/sql/execution/ExistingRDD.scala:
##########
@@ -183,16 +172,80 @@ object LogicalRDD {
}
}
+ val logicalPlan = originDataset.logicalPlan
val optimizedPlan = originDataset.queryExecution.optimizedPlan
val executedPlan = originDataset.queryExecution.executedPlan
+ val (stats, constraints) = rewriteStatsAndConstraints(logicalPlan, optimizedPlan)
+
LogicalRDD(
originDataset.logicalPlan.output,
rdd,
firstLeafPartitioning(executedPlan.outputPartitioning),
executedPlan.outputOrdering,
isStreaming
- )(originDataset.sparkSession, Some(optimizedPlan.stats), Some(optimizedPlan.constraints))
Review Comment:
We tried this before, and realized that this could break existing use case when someone is trying to checkpoint "subtree" of logical plan. Given that we know exprId can differ, it would break expressions in above node(s).
The actual example is merge into materialize source of Delta Lake. This performs join with source DF and target table with merge condition as join condition (here the condition is built with logical plan), and source DF can checkpoint and be replaced with LogicalRDD (should produce the same output to not break join condition).
https://github.com/delta-io/delta/blob/4e51a9969708080b9ac002462f20f64000288978/core/src/main/scala/org/apache/spark/sql/delta/commands/MergeIntoCommand.scala#L458-L472
https://github.com/delta-io/delta/blob/master/core/src/main/scala/org/apache/spark/sql/delta/commands/merge/MergeIntoMaterializeSource.scala#L245-L251
--
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: reviews-unsubscribe@spark.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org
[GitHub] [spark] juliuszsompolski commented on a diff in pull request #39082: [SPARK-41539][SQL] Remap stats and constraints against output in logical plan for LogicalRDD
Posted by GitBox <gi...@apache.org>.
juliuszsompolski commented on code in PR #39082:
URL: https://github.com/apache/spark/pull/39082#discussion_r1050762435
##########
sql/core/src/main/scala/org/apache/spark/sql/execution/ExistingRDD.scala:
##########
@@ -183,16 +172,80 @@ object LogicalRDD {
}
}
+ val logicalPlan = originDataset.logicalPlan
val optimizedPlan = originDataset.queryExecution.optimizedPlan
val executedPlan = originDataset.queryExecution.executedPlan
+ val (stats, constraints) = rewriteStatsAndConstraints(logicalPlan, optimizedPlan)
+
LogicalRDD(
originDataset.logicalPlan.output,
rdd,
firstLeafPartitioning(executedPlan.outputPartitioning),
executedPlan.outputOrdering,
isStreaming
- )(originDataset.sparkSession, Some(optimizedPlan.stats), Some(optimizedPlan.constraints))
+ )(originDataset.sparkSession, stats, constraints)
+ }
+
+ private[sql] def buildOutputAssocForRewrite(
+ source: Seq[Attribute],
+ destination: Seq[Attribute]): Option[Map[Attribute, Attribute]] = {
+ // We check the name and type, allowing nullability, exprId, metadata, qualifier be different
+ // E.g. This could happen during optimization phase.
+ val rewrite = source.zip(destination).flatMap { case (attr1, attr2) =>
+ if (attr1.name == attr2.name && attr1.dataType == attr2.dataType) {
+ Some(attr1 -> attr2)
+ } else {
+ None
+ }
+ }.toMap
+
+ if (rewrite.size == source.size) {
+ Some(rewrite)
+ } else {
+ None
+ }
+ }
+
+ private[sql] def rewriteStatsAndConstraints(
+ logicalPlan: LogicalPlan,
+ optimizedPlan: LogicalPlan): (Option[Statistics], Option[ExpressionSet]) = {
+ val rewrite = buildOutputAssocForRewrite(optimizedPlan.output, logicalPlan.output)
+
+ rewrite.map { rw =>
+ val rewrittenStatistics = rewriteStatistics(optimizedPlan.stats, rw)
+ val rewrittenConstraints = rewriteConstraints(optimizedPlan.constraints, rw)
+
+ (Some(rewrittenStatistics), Some(rewrittenConstraints))
+ }.getOrElse {
+ // can't rewrite stats and constraints, give up
+ logWarning("The output columns are expected to the same (for name and type) for output " +
Review Comment:
I am not aware / can't think of places where the output of analyzed plan and optimized plan would differ in terms of number of columns or their order. Or could some internal thing break that? (some metadata column? some internal hidden column?)
Could the data type differ? (because e.g. of optimizing nullability? optimizing tighter types?)
I'm not an expert in this area, so only speculating.
--
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: reviews-unsubscribe@spark.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org
[GitHub] [spark] HeartSaVioR commented on a diff in pull request #39082: [SPARK-41539][SQL] Remap stats and constraints against output in logical plan for LogicalRDD
Posted by GitBox <gi...@apache.org>.
HeartSaVioR commented on code in PR #39082:
URL: https://github.com/apache/spark/pull/39082#discussion_r1051306854
##########
sql/core/src/main/scala/org/apache/spark/sql/execution/ExistingRDD.scala:
##########
@@ -183,16 +172,80 @@ object LogicalRDD {
}
}
+ val logicalPlan = originDataset.logicalPlan
val optimizedPlan = originDataset.queryExecution.optimizedPlan
val executedPlan = originDataset.queryExecution.executedPlan
+ val (stats, constraints) = rewriteStatsAndConstraints(logicalPlan, optimizedPlan)
+
LogicalRDD(
originDataset.logicalPlan.output,
rdd,
firstLeafPartitioning(executedPlan.outputPartitioning),
executedPlan.outputOrdering,
isStreaming
- )(originDataset.sparkSession, Some(optimizedPlan.stats), Some(optimizedPlan.constraints))
+ )(originDataset.sparkSession, stats, constraints)
+ }
+
+ private[sql] def buildOutputAssocForRewrite(
+ source: Seq[Attribute],
+ destination: Seq[Attribute]): Option[Map[Attribute, Attribute]] = {
+ // We check the name and type, allowing nullability, exprId, metadata, qualifier be different
+ // E.g. This could happen during optimization phase.
+ val rewrite = source.zip(destination).flatMap { case (attr1, attr2) =>
+ if (attr1.name == attr2.name && attr1.dataType == attr2.dataType) {
+ Some(attr1 -> attr2)
+ } else {
+ None
+ }
+ }.toMap
+
+ if (rewrite.size == source.size) {
+ Some(rewrite)
+ } else {
+ None
+ }
+ }
+
+ private[sql] def rewriteStatsAndConstraints(
+ logicalPlan: LogicalPlan,
+ optimizedPlan: LogicalPlan): (Option[Statistics], Option[ExpressionSet]) = {
+ val rewrite = buildOutputAssocForRewrite(optimizedPlan.output, logicalPlan.output)
+
+ rewrite.map { rw =>
+ val rewrittenStatistics = rewriteStatistics(optimizedPlan.stats, rw)
+ val rewrittenConstraints = rewriteConstraints(optimizedPlan.constraints, rw)
+
+ (Some(rewrittenStatistics), Some(rewrittenConstraints))
+ }.getOrElse {
+ // can't rewrite stats and constraints, give up
+ logWarning("The output columns are expected to the same (for name and type) for output " +
Review Comment:
Yeah my understanding is that the final output after optimization should be semantically same. Looks like exprId can change, but I'm not sure what else we allow to be changed. I'm also just speculating.
My feeling is that changes on type or nullability could also break DSv1 sink, if the change is not done with "compatible way". e.g. table in sink has a column nullability set to "false", and optimizer somehow (shouldn't happen but) changes nullability of a column from "false" to "true" (again, shouldn't happen). Same applies to data type as well. I feel column order also matters, but not sure we pretend that the consumer should access column by 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: reviews-unsubscribe@spark.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org
[GitHub] [spark] HeartSaVioR commented on a diff in pull request #39082: [SPARK-41539][SQL] Remap stats and constraints against output in logical plan for LogicalRDD
Posted by GitBox <gi...@apache.org>.
HeartSaVioR commented on code in PR #39082:
URL: https://github.com/apache/spark/pull/39082#discussion_r1051306854
##########
sql/core/src/main/scala/org/apache/spark/sql/execution/ExistingRDD.scala:
##########
@@ -183,16 +172,80 @@ object LogicalRDD {
}
}
+ val logicalPlan = originDataset.logicalPlan
val optimizedPlan = originDataset.queryExecution.optimizedPlan
val executedPlan = originDataset.queryExecution.executedPlan
+ val (stats, constraints) = rewriteStatsAndConstraints(logicalPlan, optimizedPlan)
+
LogicalRDD(
originDataset.logicalPlan.output,
rdd,
firstLeafPartitioning(executedPlan.outputPartitioning),
executedPlan.outputOrdering,
isStreaming
- )(originDataset.sparkSession, Some(optimizedPlan.stats), Some(optimizedPlan.constraints))
+ )(originDataset.sparkSession, stats, constraints)
+ }
+
+ private[sql] def buildOutputAssocForRewrite(
+ source: Seq[Attribute],
+ destination: Seq[Attribute]): Option[Map[Attribute, Attribute]] = {
+ // We check the name and type, allowing nullability, exprId, metadata, qualifier be different
+ // E.g. This could happen during optimization phase.
+ val rewrite = source.zip(destination).flatMap { case (attr1, attr2) =>
+ if (attr1.name == attr2.name && attr1.dataType == attr2.dataType) {
+ Some(attr1 -> attr2)
+ } else {
+ None
+ }
+ }.toMap
+
+ if (rewrite.size == source.size) {
+ Some(rewrite)
+ } else {
+ None
+ }
+ }
+
+ private[sql] def rewriteStatsAndConstraints(
+ logicalPlan: LogicalPlan,
+ optimizedPlan: LogicalPlan): (Option[Statistics], Option[ExpressionSet]) = {
+ val rewrite = buildOutputAssocForRewrite(optimizedPlan.output, logicalPlan.output)
+
+ rewrite.map { rw =>
+ val rewrittenStatistics = rewriteStatistics(optimizedPlan.stats, rw)
+ val rewrittenConstraints = rewriteConstraints(optimizedPlan.constraints, rw)
+
+ (Some(rewrittenStatistics), Some(rewrittenConstraints))
+ }.getOrElse {
+ // can't rewrite stats and constraints, give up
+ logWarning("The output columns are expected to the same (for name and type) for output " +
Review Comment:
Yeah my understanding is that the final output after optimization should be semantically same. Looks like exprId can change, but I'm not sure what else we allow to be changed.
My feeling is that changes on type or nullability could also break DSv1 sink, if the change is not done with "compatible way". e.g. table in sink has a column nullability set to "false", and optimizer somehow (shouldn't happen but) changes nullability of a column from "false" to "true" (again, shouldn't happen). Same applies to data type as well. I feel column order also matters, but not sure we pretend that the consumer should access column by 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: reviews-unsubscribe@spark.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org
[GitHub] [spark] cloud-fan commented on a diff in pull request #39082: [SPARK-41539][SQL] Remap stats and constraints against output in logical plan for LogicalRDD
Posted by GitBox <gi...@apache.org>.
cloud-fan commented on code in PR #39082:
URL: https://github.com/apache/spark/pull/39082#discussion_r1053513047
##########
sql/core/src/main/scala/org/apache/spark/sql/execution/ExistingRDD.scala:
##########
@@ -183,16 +172,80 @@ object LogicalRDD {
}
}
+ val logicalPlan = originDataset.logicalPlan
val optimizedPlan = originDataset.queryExecution.optimizedPlan
val executedPlan = originDataset.queryExecution.executedPlan
+ val (stats, constraints) = rewriteStatsAndConstraints(logicalPlan, optimizedPlan)
+
LogicalRDD(
originDataset.logicalPlan.output,
rdd,
firstLeafPartitioning(executedPlan.outputPartitioning),
executedPlan.outputOrdering,
isStreaming
- )(originDataset.sparkSession, Some(optimizedPlan.stats), Some(optimizedPlan.constraints))
Review Comment:
Ah the problem is here we use stats of the optimized plan, but the `LogicalRDD` is built with logical plan output.
Can we simply use `optimizedPlan.output` to build `LogicalRDD`?
--
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: reviews-unsubscribe@spark.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org
[GitHub] [spark] HeartSaVioR commented on a diff in pull request #39082: [SPARK-41539][SQL] Remap stats and constraints against output in logical plan for LogicalRDD
Posted by GitBox <gi...@apache.org>.
HeartSaVioR commented on code in PR #39082:
URL: https://github.com/apache/spark/pull/39082#discussion_r1053715659
##########
sql/core/src/main/scala/org/apache/spark/sql/execution/ExistingRDD.scala:
##########
@@ -183,16 +172,80 @@ object LogicalRDD {
}
}
+ val logicalPlan = originDataset.logicalPlan
val optimizedPlan = originDataset.queryExecution.optimizedPlan
val executedPlan = originDataset.queryExecution.executedPlan
+ val (stats, constraints) = rewriteStatsAndConstraints(logicalPlan, optimizedPlan)
+
LogicalRDD(
originDataset.logicalPlan.output,
rdd,
firstLeafPartitioning(executedPlan.outputPartitioning),
executedPlan.outputOrdering,
isStreaming
- )(originDataset.sparkSession, Some(optimizedPlan.stats), Some(optimizedPlan.constraints))
Review Comment:
We tried this before, and realized that this could break existing use case when someone is trying to checkpoint "subtree" of logical plan. Given that we know exprId can differ, it would break expressions in above node(s).
One of the actual example is merge into materialize source of Delta Lake. This performs join with source DF and target table with merge condition as join condition (here the condition is built with logical plan), and source DF can checkpoint and be replaced with LogicalRDD. It should produce the same output to not break join condition.
https://github.com/delta-io/delta/blob/4e51a9969708080b9ac002462f20f64000288978/core/src/main/scala/org/apache/spark/sql/delta/commands/MergeIntoCommand.scala#L458-L472
https://github.com/delta-io/delta/blob/master/core/src/main/scala/org/apache/spark/sql/delta/commands/merge/MergeIntoMaterializeSource.scala#L245-L251
--
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: reviews-unsubscribe@spark.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org
[GitHub] [spark] HeartSaVioR commented on pull request #39082: [SPARK-41539][SQL] Remap stats and constraints against output in logical plan for LogicalRDD
Posted by GitBox <gi...@apache.org>.
HeartSaVioR commented on PR #39082:
URL: https://github.com/apache/spark/pull/39082#issuecomment-1357261191
cc. @cloud-fan @viirya Friendly reminder.
--
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: reviews-unsubscribe@spark.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org