You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spark.apache.org by hv...@apache.org on 2017/02/07 09:55:03 UTC
spark git commit: [SPARK-18601][SQL] Simplify Create/Get complex
expression pairs in optimizer
Repository: spark
Updated Branches:
refs/heads/master d9043092c -> a97edc2cf
[SPARK-18601][SQL] Simplify Create/Get complex expression pairs in optimizer
## What changes were proposed in this pull request?
It often happens that a complex object (struct/map/array) is created only to get elements from it in an subsequent expression. We can add an optimizer rule for this.
## How was this patch tested?
unit-tests
Please review http://spark.apache.org/contributing.html before opening a pull request.
Author: Eyal Farago <ey...@nrgene.com>
Author: eyal farago <ey...@gmail.com>
Closes #16043 from eyalfa/SPARK-18601.
Project: http://git-wip-us.apache.org/repos/asf/spark/repo
Commit: http://git-wip-us.apache.org/repos/asf/spark/commit/a97edc2c
Tree: http://git-wip-us.apache.org/repos/asf/spark/tree/a97edc2c
Diff: http://git-wip-us.apache.org/repos/asf/spark/diff/a97edc2c
Branch: refs/heads/master
Commit: a97edc2cf43bba5f94b213c824131f1c2dccecd6
Parents: d904309
Author: Eyal Farago <ey...@nrgene.com>
Authored: Tue Feb 7 10:54:55 2017 +0100
Committer: Herman van Hovell <hv...@databricks.com>
Committed: Tue Feb 7 10:54:55 2017 +0100
----------------------------------------------------------------------
.../expressions/conditionalExpressions.scala | 4 +-
.../sql/catalyst/optimizer/ComplexTypes.scala | 75 +++++
.../sql/catalyst/optimizer/Optimizer.scala | 5 +-
.../sql/catalyst/optimizer/expressions.scala | 6 +
.../ConditionalExpressionSuite.scala | 8 +
.../optimizer/SimplifyConditionalSuite.scala | 12 +-
.../catalyst/optimizer/complexTypesSuite.scala | 321 +++++++++++++++++++
7 files changed, 427 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/spark/blob/a97edc2c/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/conditionalExpressions.scala
----------------------------------------------------------------------
diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/conditionalExpressions.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/conditionalExpressions.scala
index bacedec..ee365fe 100644
--- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/conditionalExpressions.scala
+++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/conditionalExpressions.scala
@@ -333,8 +333,8 @@ object CaseWhen {
object CaseKeyWhen {
def apply(key: Expression, branches: Seq[Expression]): CaseWhen = {
val cases = branches.grouped(2).flatMap {
- case cond :: value :: Nil => Some((EqualTo(key, cond), value))
- case value :: Nil => None
+ case Seq(cond, value) => Some((EqualTo(key, cond), value))
+ case Seq(value) => None
}.toArray.toSeq // force materialization to make the seq serializable
val elseValue = if (branches.size % 2 == 1) Some(branches.last) else None
CaseWhen(cases, elseValue)
http://git-wip-us.apache.org/repos/asf/spark/blob/a97edc2c/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/ComplexTypes.scala
----------------------------------------------------------------------
diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/ComplexTypes.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/ComplexTypes.scala
new file mode 100644
index 0000000..be0009e
--- /dev/null
+++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/ComplexTypes.scala
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.spark.sql.catalyst.optimizer
+
+import org.apache.spark.sql.catalyst.expressions._
+import org.apache.spark.sql.catalyst.plans.logical.LogicalPlan
+import org.apache.spark.sql.catalyst.rules.Rule
+
+/**
+* push down operations into [[CreateNamedStructLike]].
+*/
+object SimplifyCreateStructOps extends Rule[LogicalPlan] {
+ override def apply(plan: LogicalPlan): LogicalPlan = {
+ plan.transformExpressionsUp {
+ // push down field extraction
+ case GetStructField(createNamedStructLike: CreateNamedStructLike, ordinal, _) =>
+ createNamedStructLike.valExprs(ordinal)
+ }
+ }
+}
+
+/**
+* push down operations into [[CreateArray]].
+*/
+object SimplifyCreateArrayOps extends Rule[LogicalPlan] {
+ override def apply(plan: LogicalPlan): LogicalPlan = {
+ plan.transformExpressionsUp {
+ // push down field selection (array of structs)
+ case GetArrayStructFields(CreateArray(elems), field, ordinal, numFields, containsNull) =>
+ // instead f selecting the field on the entire array,
+ // select it from each member of the array.
+ // pushing down the operation this way open other optimizations opportunities
+ // (i.e. struct(...,x,...).x)
+ CreateArray(elems.map(GetStructField(_, ordinal, Some(field.name))))
+ // push down item selection.
+ case ga @ GetArrayItem(CreateArray(elems), IntegerLiteral(idx)) =>
+ // instead of creating the array and then selecting one row,
+ // remove array creation altgether.
+ if (idx >= 0 && idx < elems.size) {
+ // valid index
+ elems(idx)
+ } else {
+ // out of bounds, mimic the runtime behavior and return null
+ Literal(null, ga.dataType)
+ }
+ }
+ }
+}
+
+/**
+* push down operations into [[CreateMap]].
+*/
+object SimplifyCreateMapOps extends Rule[LogicalPlan] {
+ override def apply(plan: LogicalPlan): LogicalPlan = {
+ plan.transformExpressionsUp {
+ case GetMapValue(CreateMap(elems), key) => CaseKeyWhen(key, elems)
+ }
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/spark/blob/a97edc2c/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/Optimizer.scala
----------------------------------------------------------------------
diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/Optimizer.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/Optimizer.scala
index 55d37cc..d8e9d30 100644
--- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/Optimizer.scala
+++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/Optimizer.scala
@@ -110,7 +110,10 @@ abstract class Optimizer(sessionCatalog: SessionCatalog, conf: CatalystConf)
SimplifyCaseConversionExpressions,
RewriteCorrelatedScalarSubquery,
EliminateSerialization,
- RemoveAliasOnlyProject) ::
+ RemoveAliasOnlyProject,
+ SimplifyCreateStructOps,
+ SimplifyCreateArrayOps,
+ SimplifyCreateMapOps) ::
Batch("Check Cartesian Products", Once,
CheckCartesianProducts(conf)) ::
Batch("Decimal Optimizations", fixedPoint,
http://git-wip-us.apache.org/repos/asf/spark/blob/a97edc2c/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/expressions.scala
----------------------------------------------------------------------
diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/expressions.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/expressions.scala
index 5bfc0ce..4f593c8 100644
--- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/expressions.scala
+++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/expressions.scala
@@ -293,6 +293,12 @@ object SimplifyConditionals extends Rule[LogicalPlan] with PredicateHelper {
// from that. Note that CaseWhen.branches should never be empty, and as a result the
// headOption (rather than head) added above is just an extra (and unnecessary) safeguard.
branches.head._2
+
+ case CaseWhen(branches, _) if branches.exists(_._1 == TrueLiteral) =>
+ // a branc with a TRue condition eliminates all following branches,
+ // these branches can be pruned away
+ val (h, t) = branches.span(_._1 != TrueLiteral)
+ CaseWhen( h :+ t.head, None)
}
}
}
http://git-wip-us.apache.org/repos/asf/spark/blob/a97edc2c/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ConditionalExpressionSuite.scala
----------------------------------------------------------------------
diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ConditionalExpressionSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ConditionalExpressionSuite.scala
index b04ea41..3e11c3d 100644
--- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ConditionalExpressionSuite.scala
+++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ConditionalExpressionSuite.scala
@@ -137,4 +137,12 @@ class ConditionalExpressionSuite extends SparkFunSuite with ExpressionEvalHelper
checkEvaluation(CaseKeyWhen(c6, Seq(c5, c2, c4, c3)), null, row)
checkEvaluation(CaseKeyWhen(literalNull, Seq(c2, c5, c1, c6)), null, row)
}
+
+ test("case key whn - internal pattern matching expects a List while apply takes a Seq") {
+ val indexedSeq = IndexedSeq(Literal(1), Literal(42), Literal(42), Literal(1))
+ val caseKeyWhaen = CaseKeyWhen(Literal(12), indexedSeq)
+ assert(caseKeyWhaen.branches ==
+ IndexedSeq((Literal(12) === Literal(1), Literal(42)),
+ (Literal(12) === Literal(42), Literal(1))))
+ }
}
http://git-wip-us.apache.org/repos/asf/spark/blob/a97edc2c/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/optimizer/SimplifyConditionalSuite.scala
----------------------------------------------------------------------
diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/optimizer/SimplifyConditionalSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/optimizer/SimplifyConditionalSuite.scala
index c02fec3..adb3e8f 100644
--- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/optimizer/SimplifyConditionalSuite.scala
+++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/optimizer/SimplifyConditionalSuite.scala
@@ -88,6 +88,16 @@ class SimplifyConditionalSuite extends PlanTest with PredicateHelper {
// Make sure this doesn't trigger if there is a non-foldable branch before the true branch
assertEquivalent(
CaseWhen(normalBranch :: trueBranch :: normalBranch :: Nil, None),
- CaseWhen(normalBranch :: trueBranch :: normalBranch :: Nil, None))
+ CaseWhen(normalBranch :: trueBranch :: Nil, None))
+ }
+
+ test("simplify CaseWhen, prune branches following a definite true") {
+ assertEquivalent(
+ CaseWhen(normalBranch :: unreachableBranch ::
+ unreachableBranch :: nullBranch ::
+ trueBranch :: normalBranch ::
+ Nil,
+ None),
+ CaseWhen(normalBranch :: trueBranch :: Nil, None))
}
}
http://git-wip-us.apache.org/repos/asf/spark/blob/a97edc2c/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/optimizer/complexTypesSuite.scala
----------------------------------------------------------------------
diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/optimizer/complexTypesSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/optimizer/complexTypesSuite.scala
new file mode 100644
index 0000000..0a18858
--- /dev/null
+++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/optimizer/complexTypesSuite.scala
@@ -0,0 +1,321 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.spark.sql.catalyst.optimizer
+
+import org.apache.spark.sql.catalyst.dsl.expressions._
+import org.apache.spark.sql.catalyst.dsl.plans._
+import org.apache.spark.sql.catalyst.expressions._
+import org.apache.spark.sql.catalyst.plans.PlanTest
+import org.apache.spark.sql.catalyst.plans.logical.{LocalRelation, LogicalPlan, Range}
+import org.apache.spark.sql.catalyst.rules.RuleExecutor
+import org.apache.spark.sql.types._
+
+/**
+* SPARK-18601 discusses simplification direct access to complex types creators.
+* i.e. {{{create_named_struct(square, `x` * `x`).square}}} can be simplified to {{{`x` * `x`}}}.
+* sam applies to create_array and create_map
+*/
+class ComplexTypesSuite extends PlanTest{
+
+ object Optimizer extends RuleExecutor[LogicalPlan] {
+ val batches =
+ Batch("collapse projections", FixedPoint(10),
+ CollapseProject) ::
+ Batch("Constant Folding", FixedPoint(10),
+ NullPropagation(conf),
+ ConstantFolding,
+ BooleanSimplification,
+ SimplifyConditionals,
+ SimplifyBinaryComparison,
+ SimplifyCreateStructOps,
+ SimplifyCreateArrayOps,
+ SimplifyCreateMapOps) :: Nil
+ }
+
+ val idAtt = ('id).long.notNull
+
+ lazy val relation = LocalRelation(idAtt )
+
+ test("explicit get from namedStruct") {
+ val query = relation
+ .select(
+ GetStructField(
+ CreateNamedStruct(Seq("att", 'id )),
+ 0,
+ None) as "outerAtt").analyze
+ val expected = relation.select('id as "outerAtt").analyze
+
+ comparePlans(Optimizer execute query, expected)
+ }
+
+ test("explicit get from named_struct- expression maintains original deduced alias") {
+ val query = relation
+ .select(GetStructField(CreateNamedStruct(Seq("att", 'id)), 0, None))
+ .analyze
+
+ val expected = relation
+ .select('id as "named_struct(att, id).att")
+ .analyze
+
+ comparePlans(Optimizer execute query, expected)
+ }
+
+ test("collapsed getStructField ontop of namedStruct") {
+ val query = relation
+ .select(CreateNamedStruct(Seq("att", 'id)) as "struct1")
+ .select(GetStructField('struct1, 0, None) as "struct1Att")
+ .analyze
+ val expected = relation.select('id as "struct1Att").analyze
+ comparePlans(Optimizer execute query, expected)
+ }
+
+ test("collapse multiple CreateNamedStruct/GetStructField pairs") {
+ val query = relation
+ .select(
+ CreateNamedStruct(Seq(
+ "att1", 'id,
+ "att2", 'id * 'id)) as "struct1")
+ .select(
+ GetStructField('struct1, 0, None) as "struct1Att1",
+ GetStructField('struct1, 1, None) as "struct1Att2")
+ .analyze
+
+ val expected =
+ relation.
+ select(
+ 'id as "struct1Att1",
+ ('id * 'id) as "struct1Att2")
+ .analyze
+
+ comparePlans(Optimizer execute query, expected)
+ }
+
+ test("collapsed2 - deduced names") {
+ val query = relation
+ .select(
+ CreateNamedStruct(Seq(
+ "att1", 'id,
+ "att2", 'id * 'id)) as "struct1")
+ .select(
+ GetStructField('struct1, 0, None),
+ GetStructField('struct1, 1, None))
+ .analyze
+
+ val expected =
+ relation.
+ select(
+ 'id as "struct1.att1",
+ ('id * 'id) as "struct1.att2")
+ .analyze
+
+ comparePlans(Optimizer execute query, expected)
+ }
+
+ test("simplified array ops") {
+ val rel = relation.select(
+ CreateArray(Seq(
+ CreateNamedStruct(Seq(
+ "att1", 'id,
+ "att2", 'id * 'id)),
+ CreateNamedStruct(Seq(
+ "att1", 'id + 1,
+ "att2", ('id + 1) * ('id + 1))
+ ))
+ ) as "arr"
+ )
+ val query = rel
+ .select(
+ GetArrayStructFields('arr, StructField("att1", LongType, false), 0, 1, false) as "a1",
+ GetArrayItem('arr, 1) as "a2",
+ GetStructField(GetArrayItem('arr, 1), 0, None) as "a3",
+ GetArrayItem(
+ GetArrayStructFields('arr,
+ StructField("att1", LongType, false),
+ 0,
+ 1,
+ false),
+ 1) as "a4")
+ .analyze
+
+ val expected = relation
+ .select(
+ CreateArray(Seq('id, 'id + 1L)) as "a1",
+ CreateNamedStruct(Seq(
+ "att1", ('id + 1L),
+ "att2", (('id + 1L) * ('id + 1L)))) as "a2",
+ ('id + 1L) as "a3",
+ ('id + 1L) as "a4")
+ .analyze
+ comparePlans(Optimizer execute query, expected)
+ }
+
+ test("simplify map ops") {
+ val rel = relation
+ .select(
+ CreateMap(Seq(
+ "r1", CreateNamedStruct(Seq("att1", 'id)),
+ "r2", CreateNamedStruct(Seq("att1", ('id + 1L))))) as "m")
+ val query = rel
+ .select(
+ GetMapValue('m, "r1") as "a1",
+ GetStructField(GetMapValue('m, "r1"), 0, None) as "a2",
+ GetMapValue('m, "r32") as "a3",
+ GetStructField(GetMapValue('m, "r32"), 0, None) as "a4")
+ .analyze
+
+ val expected =
+ relation.select(
+ CreateNamedStruct(Seq("att1", 'id)) as "a1",
+ 'id as "a2",
+ Literal.create(
+ null,
+ StructType(
+ StructField("att1", LongType, nullable = false) :: Nil
+ )
+ ) as "a3",
+ Literal.create(null, LongType) as "a4")
+ .analyze
+ comparePlans(Optimizer execute query, expected)
+ }
+
+ test("simplify map ops, constant lookup, dynamic keys") {
+ val query = relation.select(
+ GetMapValue(
+ CreateMap(Seq(
+ 'id, ('id + 1L),
+ ('id + 1L), ('id + 2L),
+ ('id + 2L), ('id + 3L),
+ Literal(13L), 'id,
+ ('id + 3L), ('id + 4L),
+ ('id + 4L), ('id + 5L))),
+ 13L) as "a")
+ .analyze
+
+ val expected = relation
+ .select(
+ CaseWhen(Seq(
+ (EqualTo(13L, 'id), ('id + 1L)),
+ (EqualTo(13L, ('id + 1L)), ('id + 2L)),
+ (EqualTo(13L, ('id + 2L)), ('id + 3L)),
+ (Literal(true), 'id))) as "a")
+ .analyze
+ comparePlans(Optimizer execute query, expected)
+ }
+
+ test("simplify map ops, dynamic lookup, dynamic keys, lookup is equivalent to one of the keys") {
+ val query = relation
+ .select(
+ GetMapValue(
+ CreateMap(Seq(
+ 'id, ('id + 1L),
+ ('id + 1L), ('id + 2L),
+ ('id + 2L), ('id + 3L),
+ ('id + 3L), ('id + 4L),
+ ('id + 4L), ('id + 5L))),
+ ('id + 3L)) as "a")
+ .analyze
+ val expected = relation
+ .select(
+ CaseWhen(Seq(
+ (EqualTo('id + 3L, 'id), ('id + 1L)),
+ (EqualTo('id + 3L, ('id + 1L)), ('id + 2L)),
+ (EqualTo('id + 3L, ('id + 2L)), ('id + 3L)),
+ (Literal(true), ('id + 4L)))) as "a")
+ .analyze
+ comparePlans(Optimizer execute query, expected)
+ }
+
+ test("simplify map ops, no positive match") {
+ val rel = relation
+ .select(
+ GetMapValue(
+ CreateMap(Seq(
+ 'id, ('id + 1L),
+ ('id + 1L), ('id + 2L),
+ ('id + 2L), ('id + 3L),
+ ('id + 3L), ('id + 4L),
+ ('id + 4L), ('id + 5L))),
+ 'id + 30L) as "a")
+ .analyze
+ val expected = relation.select(
+ CaseWhen(Seq(
+ (EqualTo('id + 30L, 'id), ('id + 1L)),
+ (EqualTo('id + 30L, ('id + 1L)), ('id + 2L)),
+ (EqualTo('id + 30L, ('id + 2L)), ('id + 3L)),
+ (EqualTo('id + 30L, ('id + 3L)), ('id + 4L)),
+ (EqualTo('id + 30L, ('id + 4L)), ('id + 5L)))) as "a")
+ .analyze
+ comparePlans(Optimizer execute rel, expected)
+ }
+
+ test("simplify map ops, constant lookup, mixed keys, eliminated constants") {
+ val rel = relation
+ .select(
+ GetMapValue(
+ CreateMap(Seq(
+ 'id, ('id + 1L),
+ ('id + 1L), ('id + 2L),
+ ('id + 2L), ('id + 3L),
+ Literal(14L), 'id,
+ ('id + 3L), ('id + 4L),
+ ('id + 4L), ('id + 5L))),
+ 13L) as "a")
+ .analyze
+
+ val expected = relation
+ .select(
+ CaseKeyWhen(13L,
+ Seq('id, ('id + 1L),
+ ('id + 1L), ('id + 2L),
+ ('id + 2L), ('id + 3L),
+ ('id + 3L), ('id + 4L),
+ ('id + 4L), ('id + 5L))) as "a")
+ .analyze
+
+ comparePlans(Optimizer execute rel, expected)
+ }
+
+ test("simplify map ops, potential dynamic match with null value + an absolute constant match") {
+ val rel = relation
+ .select(
+ GetMapValue(
+ CreateMap(Seq(
+ 'id, ('id + 1L),
+ ('id + 1L), ('id + 2L),
+ ('id + 2L), Literal.create(null, LongType),
+ Literal(2L), 'id,
+ ('id + 3L), ('id + 4L),
+ ('id + 4L), ('id + 5L))),
+ 2L ) as "a")
+ .analyze
+
+ val expected = relation
+ .select(
+ CaseWhen(Seq(
+ (EqualTo(2L, 'id), ('id + 1L)),
+ // these two are possible matches, we can't tell untill runtime
+ (EqualTo(2L, ('id + 1L)), ('id + 2L)),
+ (EqualTo(2L, 'id + 2L), Literal.create(null, LongType)),
+ // this is a definite match (two constants),
+ // but it cannot override a potential match with ('id + 2L),
+ // which is exactly what [[Coalesce]] would do in this case.
+ (Literal.TrueLiteral, 'id))) as "a")
+ .analyze
+ comparePlans(Optimizer execute rel, expected)
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@spark.apache.org
For additional commands, e-mail: commits-help@spark.apache.org