Github user gatorsmile commented on a diff in the pull request:
https://github.com/apache/spark/pull/17541#discussion_r110523770
--- Diff:
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/plans/QueryPlan.scala
---
@@ -359,9 +359,59 @@ abstract class QueryPlan[PlanType <:
QueryPlan[PlanType]] extends TreeNode[PlanT
override protected def innerChildren: Seq[QueryPlan[_]] = subqueries
/**
- * Canonicalized copy of this query plan.
+ * Returns a plan where a best effort attempt has been made to transform
`this` in a way
+ * that preserves the result but removes cosmetic variations (case
sensitivity, ordering for
+ * commutative operations, expression id, etc.)
+ *
+ * Plans where `this.canonicalized == other.canonicalized` will always
evaluate to the same
+ * result.
+ *
+ * Some nodes should overwrite this to provide proper canonicalize logic.
+ */
+ lazy val canonicalized: PlanType = {
+ val canonicalizedChildren = children.map(_.canonicalized)
+ var id = -1
+ preCanonicalized.mapExpressions {
+ case a: Alias =>
+ id += 1
+ // As the root of the expression, Alias will always take an
arbitrary exprId, we need to
+ // normalize that for equality testing, by assigning expr id from
0 incrementally. The
+ // alias name doesn't matter and should be erased.
+ Alias(normalizeExprId(a.child), "")(ExprId(id), a.qualifier,
isGenerated = a.isGenerated)
+
+ case ar: AttributeReference if allAttributes.indexOf(ar.exprId) ==
-1 =>
+ // Top level `AttributeReference` may also be used for output like
`Alias`, we should
+ // normalize the epxrId too.
+ id += 1
+ ar.withExprId(ExprId(id))
+
+ case other => normalizeExprId(other)
+ }.withNewChildren(canonicalizedChildren)
+ }
+
+ /**
+ * Do some simple transformation on this plan before canonicalizing.
Implementations can override
+ * this method to provide customer canonicalize logic without rewriting
the whole logic.
*/
- protected lazy val canonicalized: PlanType = this
+ protected def preCanonicalized: PlanType = this
+
+ /**
+ * Normalize the exprIds in the given expression, by updating the exprId
in `AttributeReference`
+ * with its referenced ordinal from input attributes. It's similar to
`BindReferences` but we
+ * do not use `BindReferences` here as the plan may take the expression
as a parameter with type
+ * `Attribute`, and replace it with `BoundReference` will cause error.
+ */
+ protected def normalizeExprId[T <: Expression](e: T, input: AttributeSeq
= allAttributes): T = {
+ e.transformUp {
+ case ar: AttributeReference =>
+ val ordinal = input.indexOf(ar.exprId)
+ if (ordinal == -1) {
+ ar
--- End diff --
No need to normalize exprIds in this case?
---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at [email protected] or file a JIRA ticket
with INFRA.
---
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]