1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 *    http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package org.apache.spark.sql.catalyst.optimizer
19
20import org.apache.spark.sql.catalyst.analysis._
21import org.apache.spark.sql.catalyst.dsl.expressions._
22import org.apache.spark.sql.catalyst.dsl.plans._
23import org.apache.spark.sql.catalyst.expressions._
24import org.apache.spark.sql.catalyst.expressions.Literal.{FalseLiteral, TrueLiteral}
25import org.apache.spark.sql.catalyst.plans.PlanTest
26import org.apache.spark.sql.catalyst.plans.logical._
27import org.apache.spark.sql.catalyst.rules._
28
29class BinaryComparisonSimplificationSuite extends PlanTest with PredicateHelper {
30
31  object Optimize extends RuleExecutor[LogicalPlan] {
32    val batches =
33      Batch("AnalysisNodes", Once,
34        EliminateSubqueryAliases) ::
35      Batch("Constant Folding", FixedPoint(50),
36        NullPropagation,
37        ConstantFolding,
38        BooleanSimplification,
39        SimplifyBinaryComparison,
40        PruneFilters) :: Nil
41  }
42
43  val nullableRelation = LocalRelation('a.int.withNullability(true))
44  val nonNullableRelation = LocalRelation('a.int.withNullability(false))
45
46  test("Preserve nullable exprs in general") {
47    for (e <- Seq('a === 'a, 'a <= 'a, 'a >= 'a, 'a < 'a, 'a > 'a)) {
48      val plan = nullableRelation.where(e).analyze
49      val actual = Optimize.execute(plan)
50      val correctAnswer = plan
51      comparePlans(actual, correctAnswer)
52    }
53  }
54
55  test("Preserve non-deterministic exprs") {
56    val plan = nonNullableRelation
57      .where(Rand(0) === Rand(0) && Rand(1) <=> Rand(1)).analyze
58    val actual = Optimize.execute(plan)
59    val correctAnswer = plan
60    comparePlans(actual, correctAnswer)
61  }
62
63  test("Nullable Simplification Primitive: <=>") {
64    val plan = nullableRelation.select('a <=> 'a).analyze
65    val actual = Optimize.execute(plan)
66    val correctAnswer = nullableRelation.select(Alias(TrueLiteral, "(a <=> a)")()).analyze
67    comparePlans(actual, correctAnswer)
68  }
69
70  test("Non-Nullable Simplification Primitive") {
71    val plan = nonNullableRelation
72      .select('a === 'a, 'a <=> 'a, 'a <= 'a, 'a >= 'a, 'a < 'a, 'a > 'a).analyze
73    val actual = Optimize.execute(plan)
74    val correctAnswer = nonNullableRelation
75      .select(
76        Alias(TrueLiteral, "(a = a)")(),
77        Alias(TrueLiteral, "(a <=> a)")(),
78        Alias(TrueLiteral, "(a <= a)")(),
79        Alias(TrueLiteral, "(a >= a)")(),
80        Alias(FalseLiteral, "(a < a)")(),
81        Alias(FalseLiteral, "(a > a)")())
82      .analyze
83    comparePlans(actual, correctAnswer)
84  }
85
86  test("Expression Normalization") {
87    val plan = nonNullableRelation.where(
88      'a * Literal(100) + Pi() === Pi() + Literal(100) * 'a &&
89      DateAdd(CurrentDate(), 'a + Literal(2)) <= DateAdd(CurrentDate(), Literal(2) + 'a))
90      .analyze
91    val actual = Optimize.execute(plan)
92    val correctAnswer = nonNullableRelation.analyze
93    comparePlans(actual, correctAnswer)
94  }
95}
96