blob: 8e455eb4419e09953ec0459b94ac3df1af7c1a7c [file] [log] [blame]
/*
* 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
import org.apache.spark.sql.catalyst.expressions._
import org.apache.spark.sql.functions._
import org.apache.spark.sql.internal.SQLConf
import org.apache.spark.sql.test.SharedSparkSession
class EmptyInSuite extends QueryTest
with SharedSparkSession {
import testImplicits._
val row = identity[(java.lang.Integer, java.lang.Double)](_)
lazy val t = Seq(
row((1, 1.0)),
row((null, 2.0))).toDF("a", "b")
test("IN with empty list") {
// This test has to be written in scala to construct a literal empty IN list, since that
// isn't valid syntax in SQL.
val emptylist = Seq.empty[Literal]
Seq(true, false).foreach { legacyNullInBehavior =>
// To observe execution behavior, disable the OptimizeIn rule which optimizes away empty lists
Seq(true, false).foreach { disableOptimizeIn =>
// Disable ConvertToLocalRelation since it would collapse the evaluation of the IN
// expression over the LocalRelation
var excludedRules = "org.apache.spark.sql.catalyst.optimizer.ConvertToLocalRelation"
if (disableOptimizeIn) {
excludedRules += ",org.apache.spark.sql.catalyst.optimizer.OptimizeIn"
}
withSQLConf(
SQLConf.OPTIMIZER_EXCLUDED_RULES.key -> excludedRules,
SQLConf.LEGACY_NULL_IN_EMPTY_LIST_BEHAVIOR.key -> legacyNullInBehavior.toString) {
val expectedResultForNullInEmpty =
if (legacyNullInBehavior) null else false
val df = t.select(col("a"), col("a").isin(emptylist: _*))
checkAnswer(
df,
Row(1, false) :: Row(null, expectedResultForNullInEmpty) :: Nil)
}
}
}
}
test("IN empty list behavior conf defaults") {
// Currently the fixed behavior is enabled when ANSI is on, and the legacy behavior when
// ANSI is off.
Seq(true, false).foreach { ansiEnabled =>
withSQLConf(SQLConf.ANSI_ENABLED.key -> ansiEnabled.toString) {
val legacyNullInBehavior = !ansiEnabled
assert(SQLConf.get.legacyNullInEmptyBehavior == legacyNullInBehavior)
val emptylist = Seq.empty[Literal]
val df = t.select(col("a"), col("a").isin(emptylist: _*))
val expectedResultForNullInEmpty =
if (legacyNullInBehavior) null else false
checkAnswer(
df,
Row(1, false) :: Row(null, expectedResultForNullInEmpty) :: Nil)
}
}
}
}