Skip to content

Commit 0a83b8f

Browse files
committed
Removed the default eval implementation from Expression, and added a bunch of override's in classes I touched.
1 parent 0307db0 commit 0a83b8f

File tree

8 files changed

+89
-45
lines changed

8 files changed

+89
-45
lines changed

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/unresolved.scala

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
package org.apache.spark.sql.catalyst.analysis
1919

2020
import org.apache.spark.sql.catalyst.{errors, trees}
21-
import org.apache.spark.sql.catalyst.expressions.{Alias, Attribute, Expression, NamedExpression}
21+
import org.apache.spark.sql.catalyst.errors.TreeNodeException
22+
import org.apache.spark.sql.catalyst.expressions._
2223
import org.apache.spark.sql.catalyst.plans.logical.BaseRelation
2324
import org.apache.spark.sql.catalyst.trees.TreeNode
2425

@@ -36,34 +37,41 @@ case class UnresolvedRelation(
3637
databaseName: Option[String],
3738
tableName: String,
3839
alias: Option[String] = None) extends BaseRelation {
39-
def output = Nil
40+
override def output = Nil
4041
override lazy val resolved = false
4142
}
4243

4344
/**
4445
* Holds the name of an attribute that has yet to be resolved.
4546
*/
4647
case class UnresolvedAttribute(name: String) extends Attribute with trees.LeafNode[Expression] {
47-
def exprId = throw new UnresolvedException(this, "exprId")
48-
def dataType = throw new UnresolvedException(this, "dataType")
49-
def nullable = throw new UnresolvedException(this, "nullable")
50-
def qualifiers = throw new UnresolvedException(this, "qualifiers")
48+
override def exprId = throw new UnresolvedException(this, "exprId")
49+
override def dataType = throw new UnresolvedException(this, "dataType")
50+
override def nullable = throw new UnresolvedException(this, "nullable")
51+
override def qualifiers = throw new UnresolvedException(this, "qualifiers")
5152
override lazy val resolved = false
5253

53-
def newInstance = this
54-
def withQualifiers(newQualifiers: Seq[String]) = this
54+
override def newInstance = this
55+
override def withQualifiers(newQualifiers: Seq[String]) = this
56+
57+
// Unresolved attributes are transient at compile time and don't get evaluated during execution.
58+
override def eval(input: Row = null): EvaluatedType =
59+
throw new TreeNodeException(this, s"No function to evaluate expression. type: ${this.nodeName}")
5560

5661
override def toString: String = s"'$name"
5762
}
5863

5964
case class UnresolvedFunction(name: String, children: Seq[Expression]) extends Expression {
60-
def exprId = throw new UnresolvedException(this, "exprId")
61-
def dataType = throw new UnresolvedException(this, "dataType")
65+
override def dataType = throw new UnresolvedException(this, "dataType")
6266
override def foldable = throw new UnresolvedException(this, "foldable")
63-
def nullable = throw new UnresolvedException(this, "nullable")
64-
def qualifiers = throw new UnresolvedException(this, "qualifiers")
65-
def references = children.flatMap(_.references).toSet
67+
override def nullable = throw new UnresolvedException(this, "nullable")
68+
override def references = children.flatMap(_.references).toSet
6669
override lazy val resolved = false
70+
71+
// Unresolved functions are transient at compile time and don't get evaluated during execution.
72+
override def eval(input: Row = null): EvaluatedType =
73+
throw new TreeNodeException(this, s"No function to evaluate expression. type: ${this.nodeName}")
74+
6775
override def toString = s"'$name(${children.mkString(",")})"
6876
}
6977

@@ -79,15 +87,15 @@ case class Star(
7987
mapFunction: Attribute => Expression = identity[Attribute])
8088
extends Attribute with trees.LeafNode[Expression] {
8189

82-
def name = throw new UnresolvedException(this, "exprId")
83-
def exprId = throw new UnresolvedException(this, "exprId")
84-
def dataType = throw new UnresolvedException(this, "dataType")
85-
def nullable = throw new UnresolvedException(this, "nullable")
86-
def qualifiers = throw new UnresolvedException(this, "qualifiers")
90+
override def name = throw new UnresolvedException(this, "exprId")
91+
override def exprId = throw new UnresolvedException(this, "exprId")
92+
override def dataType = throw new UnresolvedException(this, "dataType")
93+
override def nullable = throw new UnresolvedException(this, "nullable")
94+
override def qualifiers = throw new UnresolvedException(this, "qualifiers")
8795
override lazy val resolved = false
8896

89-
def newInstance = this
90-
def withQualifiers(newQualifiers: Seq[String]) = this
97+
override def newInstance = this
98+
override def withQualifiers(newQualifiers: Seq[String]) = this
9199

92100
def expand(input: Seq[Attribute]): Seq[NamedExpression] = {
93101
val expandedAttributes: Seq[Attribute] = table match {
@@ -104,5 +112,9 @@ case class Star(
104112
mappedAttributes
105113
}
106114

115+
// Star gets expanded at runtime so we never evaluate a Star.
116+
override def eval(input: Row = null): EvaluatedType =
117+
throw new TreeNodeException(this, s"No function to evaluate expression. type: ${this.nodeName}")
118+
107119
override def toString = table.map(_ + ".").getOrElse("") + "*"
108120
}

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/Expression.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,7 @@ abstract class Expression extends TreeNode[Expression] {
5050
def references: Set[Attribute]
5151

5252
/** Returns the result of evaluating this expression on a given input Row */
53-
def eval(input: Row = null): EvaluatedType =
54-
throw new TreeNodeException(this, s"No function to evaluate expression. type: ${this.nodeName}")
53+
def eval(input: Row = null): EvaluatedType
5554

5655
/**
5756
* Returns `true` if this expression and all its children have been resolved to a specific schema

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/SortOrder.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
package org.apache.spark.sql.catalyst.expressions
1919

20+
import org.apache.spark.sql.catalyst.errors.TreeNodeException
21+
2022
abstract sealed class SortDirection
2123
case object Ascending extends SortDirection
2224
case object Descending extends SortDirection
@@ -26,7 +28,12 @@ case object Descending extends SortDirection
2628
* transformations over expression will descend into its child.
2729
*/
2830
case class SortOrder(child: Expression, direction: SortDirection) extends UnaryExpression {
29-
def dataType = child.dataType
30-
def nullable = child.nullable
31+
override def dataType = child.dataType
32+
override def nullable = child.nullable
33+
34+
// SortOrder itself is never evaluated.
35+
override def eval(input: Row = null): EvaluatedType =
36+
throw new TreeNodeException(this, s"No function to evaluate expression. type: ${this.nodeName}")
37+
3138
override def toString = s"$child ${if (direction == Ascending) "ASC" else "DESC"}"
3239
}

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/aggregates.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package org.apache.spark.sql.catalyst.expressions
1919

2020
import org.apache.spark.sql.catalyst.types._
2121
import org.apache.spark.sql.catalyst.trees
22+
import org.apache.spark.sql.catalyst.errors.TreeNodeException
2223

2324
abstract class AggregateExpression extends Expression {
2425
self: Product =>
@@ -28,6 +29,13 @@ abstract class AggregateExpression extends Expression {
2829
* of input rows/
2930
*/
3031
def newInstance(): AggregateFunction
32+
33+
/**
34+
* [[AggregateExpression.eval]] should never be invoked because [[AggregateExpression]]'s are
35+
* replaced with a physical aggregate operator at runtime.
36+
*/
37+
override def eval(input: Row = null): EvaluatedType =
38+
throw new TreeNodeException(this, s"No function to evaluate expression. type: ${this.nodeName}")
3139
}
3240

3341
/**

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/namedExpressions.scala

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package org.apache.spark.sql.catalyst.expressions
1919

2020
import org.apache.spark.sql.catalyst.trees
2121
import org.apache.spark.sql.catalyst.analysis.UnresolvedAttribute
22+
import org.apache.spark.sql.catalyst.errors.TreeNodeException
2223
import org.apache.spark.sql.catalyst.types._
2324

2425
object NamedExpression {
@@ -58,9 +59,9 @@ abstract class Attribute extends NamedExpression {
5859

5960
def withQualifiers(newQualifiers: Seq[String]): Attribute
6061

61-
def references = Set(this)
6262
def toAttribute = this
6363
def newInstance: Attribute
64+
override def references = Set(this)
6465
}
6566

6667
/**
@@ -77,15 +78,15 @@ case class Alias(child: Expression, name: String)
7778
(val exprId: ExprId = NamedExpression.newExprId, val qualifiers: Seq[String] = Nil)
7879
extends NamedExpression with trees.UnaryNode[Expression] {
7980

80-
type EvaluatedType = Any
81+
override type EvaluatedType = Any
8182

8283
override def eval(input: Row) = child.eval(input)
8384

84-
def dataType = child.dataType
85-
def nullable = child.nullable
86-
def references = child.references
85+
override def dataType = child.dataType
86+
override def nullable = child.nullable
87+
override def references = child.references
8788

88-
def toAttribute = {
89+
override def toAttribute = {
8990
if (resolved) {
9091
AttributeReference(name, child.dataType, child.nullable)(exprId, qualifiers)
9192
} else {
@@ -127,7 +128,7 @@ case class AttributeReference(name: String, dataType: DataType, nullable: Boolea
127128
h
128129
}
129130

130-
def newInstance = AttributeReference(name, dataType, nullable)(qualifiers = qualifiers)
131+
override def newInstance = AttributeReference(name, dataType, nullable)(qualifiers = qualifiers)
131132

132133
/**
133134
* Returns a copy of this [[AttributeReference]] with changed nullability.
@@ -143,13 +144,17 @@ case class AttributeReference(name: String, dataType: DataType, nullable: Boolea
143144
/**
144145
* Returns a copy of this [[AttributeReference]] with new qualifiers.
145146
*/
146-
def withQualifiers(newQualifiers: Seq[String]) = {
147+
override def withQualifiers(newQualifiers: Seq[String]) = {
147148
if (newQualifiers == qualifiers) {
148149
this
149150
} else {
150151
AttributeReference(name, dataType, nullable)(exprId, newQualifiers)
151152
}
152153
}
153154

155+
// Unresolved attributes are transient at compile time and don't get evaluated during execution.
156+
override def eval(input: Row = null): EvaluatedType =
157+
throw new TreeNodeException(this, s"No function to evaluate expression. type: ${this.nodeName}")
158+
154159
override def toString: String = s"$name#${exprId.id}$typeSuffix"
155160
}

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/plans/physical/partitioning.scala

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717

1818
package org.apache.spark.sql.catalyst.plans.physical
1919

20-
import org.apache.spark.sql.catalyst.expressions.{Expression, SortOrder}
20+
import org.apache.spark.sql.catalyst.errors.TreeNodeException
21+
import org.apache.spark.sql.catalyst.expressions.{Expression, Row, SortOrder}
2122
import org.apache.spark.sql.catalyst.types.IntegerType
2223

2324
/**
@@ -139,12 +140,12 @@ case class HashPartitioning(expressions: Seq[Expression], numPartitions: Int)
139140
extends Expression
140141
with Partitioning {
141142

142-
def children = expressions
143-
def references = expressions.flatMap(_.references).toSet
144-
def nullable = false
145-
def dataType = IntegerType
143+
override def children = expressions
144+
override def references = expressions.flatMap(_.references).toSet
145+
override def nullable = false
146+
override def dataType = IntegerType
146147

147-
lazy val clusteringSet = expressions.toSet
148+
private[this] lazy val clusteringSet = expressions.toSet
148149

149150
override def satisfies(required: Distribution): Boolean = required match {
150151
case UnspecifiedDistribution => true
@@ -158,6 +159,9 @@ case class HashPartitioning(expressions: Seq[Expression], numPartitions: Int)
158159
case h: HashPartitioning if h == this => true
159160
case _ => false
160161
}
162+
163+
override def eval(input: Row = null): EvaluatedType =
164+
throw new TreeNodeException(this, s"No function to evaluate expression. type: ${this.nodeName}")
161165
}
162166

163167
/**
@@ -168,17 +172,20 @@ case class HashPartitioning(expressions: Seq[Expression], numPartitions: Int)
168172
* partition.
169173
* - Each partition will have a `min` and `max` row, relative to the given ordering. All rows
170174
* that are in between `min` and `max` in this `ordering` will reside in this partition.
175+
*
176+
* This class extends expression primarily so that transformations over expression will descend
177+
* into its child.
171178
*/
172179
case class RangePartitioning(ordering: Seq[SortOrder], numPartitions: Int)
173180
extends Expression
174181
with Partitioning {
175182

176-
def children = ordering
177-
def references = ordering.flatMap(_.references).toSet
178-
def nullable = false
179-
def dataType = IntegerType
183+
override def children = ordering
184+
override def references = ordering.flatMap(_.references).toSet
185+
override def nullable = false
186+
override def dataType = IntegerType
180187

181-
lazy val clusteringSet = ordering.map(_.child).toSet
188+
private[this] lazy val clusteringSet = ordering.map(_.child).toSet
182189

183190
override def satisfies(required: Distribution): Boolean = required match {
184191
case UnspecifiedDistribution => true
@@ -195,4 +202,7 @@ case class RangePartitioning(ordering: Seq[SortOrder], numPartitions: Int)
195202
case r: RangePartitioning if r == this => true
196203
case _ => false
197204
}
205+
206+
override def eval(input: Row): EvaluatedType =
207+
throw new TreeNodeException(this, s"No function to evaluate expression. type: ${this.nodeName}")
198208
}

sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ExpressionEvaluationSuite.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,10 @@ class ExpressionEvaluationSuite extends FunSuite {
100100
(null, false, null) ::
101101
(null, null, null) :: Nil)
102102

103-
def booleanLogicTest(name: String, op: (Expression, Expression) => Expression, truthTable: Seq[(Any, Any, Any)]) {
103+
def booleanLogicTest(
104+
name: String,
105+
op: (Expression, Expression) => Expression,
106+
truthTable: Seq[(Any, Any, Any)]) {
104107
test(s"3VL $name") {
105108
truthTable.foreach {
106109
case (l,r,answer) =>

sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/optimizer/ConstantFoldingSuite.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import org.apache.spark.sql.catalyst.analysis.EliminateAnalysisOperators
2121
import org.apache.spark.sql.catalyst.expressions._
2222
import org.apache.spark.sql.catalyst.plans.logical.{LocalRelation, LogicalPlan}
2323
import org.apache.spark.sql.catalyst.rules.RuleExecutor
24-
import org.apache.spark.sql.catalyst.types.IntegerType
24+
import org.apache.spark.sql.catalyst.types.{DoubleType, IntegerType}
2525

2626
// For implicit conversions
2727
import org.apache.spark.sql.catalyst.dsl.plans._

0 commit comments

Comments
 (0)