aboutsummaryrefslogtreecommitdiff
path: root/sql/catalyst/src/test
diff options
context:
space:
mode:
authorpetermaxlee <petermaxlee@gmail.com>2016-08-19 09:19:47 +0800
committerWenchen Fan <wenchen@databricks.com>2016-08-19 09:19:47 +0800
commitf5472dda51b980a726346587257c22873ff708e3 (patch)
tree39511907b8f69c02626af8603013a94461388281 /sql/catalyst/src/test
parentb72bb62d421840f82d663c6b8e3922bd14383fbb (diff)
downloadspark-f5472dda51b980a726346587257c22873ff708e3.tar.gz
spark-f5472dda51b980a726346587257c22873ff708e3.tar.bz2
spark-f5472dda51b980a726346587257c22873ff708e3.zip
[SPARK-16947][SQL] Support type coercion and foldable expression for inline tables
## What changes were proposed in this pull request? This patch improves inline table support with the following: 1. Support type coercion. 2. Support using foldable expressions. Previously only literals were supported. 3. Improve error message handling. 4. Improve test coverage. ## How was this patch tested? Added a new unit test suite ResolveInlineTablesSuite and a new file-based end-to-end test inline-table.sql. Author: petermaxlee <petermaxlee@gmail.com> Closes #14676 from petermaxlee/SPARK-16947.
Diffstat (limited to 'sql/catalyst/src/test')
-rw-r--r--sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/analysis/ResolveInlineTablesSuite.scala101
-rw-r--r--sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/PlanParserSuite.scala22
2 files changed, 109 insertions, 14 deletions
diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/analysis/ResolveInlineTablesSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/analysis/ResolveInlineTablesSuite.scala
new file mode 100644
index 0000000000..920c6ea50f
--- /dev/null
+++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/analysis/ResolveInlineTablesSuite.scala
@@ -0,0 +1,101 @@
+/*
+ * 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.analysis
+
+import org.scalatest.BeforeAndAfter
+
+import org.apache.spark.sql.AnalysisException
+import org.apache.spark.sql.catalyst.expressions.{Literal, Rand}
+import org.apache.spark.sql.catalyst.expressions.aggregate.Count
+import org.apache.spark.sql.catalyst.plans.PlanTest
+import org.apache.spark.sql.types.{LongType, NullType}
+
+/**
+ * Unit tests for [[ResolveInlineTables]]. Note that there are also test cases defined in
+ * end-to-end tests (in sql/core module) for verifying the correct error messages are shown
+ * in negative cases.
+ */
+class ResolveInlineTablesSuite extends PlanTest with BeforeAndAfter {
+
+ private def lit(v: Any): Literal = Literal(v)
+
+ test("validate inputs are foldable") {
+ ResolveInlineTables.validateInputEvaluable(
+ UnresolvedInlineTable(Seq("c1", "c2"), Seq(Seq(lit(1)))))
+
+ // nondeterministic (rand) should not work
+ intercept[AnalysisException] {
+ ResolveInlineTables.validateInputEvaluable(
+ UnresolvedInlineTable(Seq("c1"), Seq(Seq(Rand(1)))))
+ }
+
+ // aggregate should not work
+ intercept[AnalysisException] {
+ ResolveInlineTables.validateInputEvaluable(
+ UnresolvedInlineTable(Seq("c1"), Seq(Seq(Count(lit(1))))))
+ }
+
+ // unresolved attribute should not work
+ intercept[AnalysisException] {
+ ResolveInlineTables.validateInputEvaluable(
+ UnresolvedInlineTable(Seq("c1"), Seq(Seq(UnresolvedAttribute("A")))))
+ }
+ }
+
+ test("validate input dimensions") {
+ ResolveInlineTables.validateInputDimension(
+ UnresolvedInlineTable(Seq("c1"), Seq(Seq(lit(1)), Seq(lit(2)))))
+
+ // num alias != data dimension
+ intercept[AnalysisException] {
+ ResolveInlineTables.validateInputDimension(
+ UnresolvedInlineTable(Seq("c1", "c2"), Seq(Seq(lit(1)), Seq(lit(2)))))
+ }
+
+ // num alias == data dimension, but data themselves are inconsistent
+ intercept[AnalysisException] {
+ ResolveInlineTables.validateInputDimension(
+ UnresolvedInlineTable(Seq("c1"), Seq(Seq(lit(1)), Seq(lit(21), lit(22)))))
+ }
+ }
+
+ test("do not fire the rule if not all expressions are resolved") {
+ val table = UnresolvedInlineTable(Seq("c1", "c2"), Seq(Seq(UnresolvedAttribute("A"))))
+ assert(ResolveInlineTables(table) == table)
+ }
+
+ test("convert") {
+ val table = UnresolvedInlineTable(Seq("c1"), Seq(Seq(lit(1)), Seq(lit(2L))))
+ val converted = ResolveInlineTables.convert(table)
+
+ assert(converted.output.map(_.dataType) == Seq(LongType))
+ assert(converted.data.size == 2)
+ assert(converted.data(0).getLong(0) == 1L)
+ assert(converted.data(1).getLong(0) == 2L)
+ }
+
+ test("nullability inference in convert") {
+ val table1 = UnresolvedInlineTable(Seq("c1"), Seq(Seq(lit(1)), Seq(lit(2L))))
+ val converted1 = ResolveInlineTables.convert(table1)
+ assert(!converted1.schema.fields(0).nullable)
+
+ val table2 = UnresolvedInlineTable(Seq("c1"), Seq(Seq(lit(1)), Seq(Literal(null, NullType))))
+ val converted2 = ResolveInlineTables.convert(table2)
+ assert(converted2.schema.fields(0).nullable)
+ }
+}
diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/PlanParserSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/PlanParserSuite.scala
index cbe4a022e7..2fcbfc7067 100644
--- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/PlanParserSuite.scala
+++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/PlanParserSuite.scala
@@ -17,9 +17,8 @@
package org.apache.spark.sql.catalyst.parser
-import org.apache.spark.sql.Row
import org.apache.spark.sql.catalyst.FunctionIdentifier
-import org.apache.spark.sql.catalyst.analysis.{UnresolvedGenerator, UnresolvedTableValuedFunction}
+import org.apache.spark.sql.catalyst.analysis.{UnresolvedGenerator, UnresolvedInlineTable, UnresolvedTableValuedFunction}
import org.apache.spark.sql.catalyst.expressions._
import org.apache.spark.sql.catalyst.plans._
import org.apache.spark.sql.catalyst.plans.logical._
@@ -433,19 +432,14 @@ class PlanParserSuite extends PlanTest {
}
test("inline table") {
- assertEqual("values 1, 2, 3, 4", LocalRelation.fromExternalRows(
- Seq('col1.int),
- Seq(1, 2, 3, 4).map(x => Row(x))))
+ assertEqual("values 1, 2, 3, 4",
+ UnresolvedInlineTable(Seq("col1"), Seq(1, 2, 3, 4).map(x => Seq(Literal(x)))))
+
assertEqual(
- "values (1, 'a'), (2, 'b'), (3, 'c') as tbl(a, b)",
- LocalRelation.fromExternalRows(
- Seq('a.int, 'b.string),
- Seq((1, "a"), (2, "b"), (3, "c")).map(x => Row(x._1, x._2))).as("tbl"))
- intercept("values (a, 'a'), (b, 'b')",
- "All expressions in an inline table must be constants.")
- intercept("values (1, 'a'), (2, 'b') as tbl(a, b, c)",
- "Number of aliases must match the number of fields in an inline table.")
- intercept[ArrayIndexOutOfBoundsException](parsePlan("values (1, 'a'), (2, 'b', 5Y)"))
+ "values (1, 'a'), (2, 'b') as tbl(a, b)",
+ UnresolvedInlineTable(
+ Seq("a", "b"),
+ Seq(Literal(1), Literal("a")) :: Seq(Literal(2), Literal("b")) :: Nil).as("tbl"))
}
test("simple select query with !> and !<") {