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.analysis 19 20import org.scalatest.BeforeAndAfter 21 22import org.apache.spark.sql.AnalysisException 23import org.apache.spark.sql.catalyst.expressions.{Literal, Rand} 24import org.apache.spark.sql.catalyst.expressions.aggregate.Count 25import org.apache.spark.sql.catalyst.plans.PlanTest 26import org.apache.spark.sql.types.{LongType, NullType} 27 28/** 29 * Unit tests for [[ResolveInlineTables]]. Note that there are also test cases defined in 30 * end-to-end tests (in sql/core module) for verifying the correct error messages are shown 31 * in negative cases. 32 */ 33class ResolveInlineTablesSuite extends PlanTest with BeforeAndAfter { 34 35 private def lit(v: Any): Literal = Literal(v) 36 37 test("validate inputs are foldable") { 38 ResolveInlineTables.validateInputEvaluable( 39 UnresolvedInlineTable(Seq("c1", "c2"), Seq(Seq(lit(1))))) 40 41 // nondeterministic (rand) should not work 42 intercept[AnalysisException] { 43 ResolveInlineTables.validateInputEvaluable( 44 UnresolvedInlineTable(Seq("c1"), Seq(Seq(Rand(1))))) 45 } 46 47 // aggregate should not work 48 intercept[AnalysisException] { 49 ResolveInlineTables.validateInputEvaluable( 50 UnresolvedInlineTable(Seq("c1"), Seq(Seq(Count(lit(1)))))) 51 } 52 53 // unresolved attribute should not work 54 intercept[AnalysisException] { 55 ResolveInlineTables.validateInputEvaluable( 56 UnresolvedInlineTable(Seq("c1"), Seq(Seq(UnresolvedAttribute("A"))))) 57 } 58 } 59 60 test("validate input dimensions") { 61 ResolveInlineTables.validateInputDimension( 62 UnresolvedInlineTable(Seq("c1"), Seq(Seq(lit(1)), Seq(lit(2))))) 63 64 // num alias != data dimension 65 intercept[AnalysisException] { 66 ResolveInlineTables.validateInputDimension( 67 UnresolvedInlineTable(Seq("c1", "c2"), Seq(Seq(lit(1)), Seq(lit(2))))) 68 } 69 70 // num alias == data dimension, but data themselves are inconsistent 71 intercept[AnalysisException] { 72 ResolveInlineTables.validateInputDimension( 73 UnresolvedInlineTable(Seq("c1"), Seq(Seq(lit(1)), Seq(lit(21), lit(22))))) 74 } 75 } 76 77 test("do not fire the rule if not all expressions are resolved") { 78 val table = UnresolvedInlineTable(Seq("c1", "c2"), Seq(Seq(UnresolvedAttribute("A")))) 79 assert(ResolveInlineTables(table) == table) 80 } 81 82 test("convert") { 83 val table = UnresolvedInlineTable(Seq("c1"), Seq(Seq(lit(1)), Seq(lit(2L)))) 84 val converted = ResolveInlineTables.convert(table) 85 86 assert(converted.output.map(_.dataType) == Seq(LongType)) 87 assert(converted.data.size == 2) 88 assert(converted.data(0).getLong(0) == 1L) 89 assert(converted.data(1).getLong(0) == 2L) 90 } 91 92 test("nullability inference in convert") { 93 val table1 = UnresolvedInlineTable(Seq("c1"), Seq(Seq(lit(1)), Seq(lit(2L)))) 94 val converted1 = ResolveInlineTables.convert(table1) 95 assert(!converted1.schema.fields(0).nullable) 96 97 val table2 = UnresolvedInlineTable(Seq("c1"), Seq(Seq(lit(1)), Seq(Literal(null, NullType)))) 98 val converted2 = ResolveInlineTables.convert(table2) 99 assert(converted2.schema.fields(0).nullable) 100 } 101} 102