Skip to content
Snippets Groups Projects
Commit 4bf46097 authored by Reynold Xin's avatar Reynold Xin
Browse files

[SPARK-13882][SQL] Remove org.apache.spark.sql.execution.local

## What changes were proposed in this pull request?
We introduced some local operators in org.apache.spark.sql.execution.local package but never fully wired the engine to actually use these. We still plan to implement a full local mode, but it's probably going to be fairly different from what the current iterator-based local mode would look like. Based on what we know right now, we might want a push-based columnar version of these operators.

Let's just remove them for now, and we can always re-introduced them in the future by looking at branch-1.6.

## How was this patch tested?
This is simply dead code removal.

Author: Reynold Xin <rxin@databricks.com>

Closes #11705 from rxin/SPARK-13882.
parent 17eec0a7
No related branches found
No related tags found
No related merge requests found
Showing
with 0 additions and 726 deletions
/*
* 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.execution.local
import org.mockito.Mockito.{mock, when}
import org.apache.spark.broadcast.TorrentBroadcast
import org.apache.spark.sql.catalyst.dsl.expressions._
import org.apache.spark.sql.catalyst.expressions.{Expression, UnsafeProjection}
import org.apache.spark.sql.execution.joins.{BuildLeft, BuildRight, BuildSide, HashedRelation}
import org.apache.spark.sql.internal.SQLConf
class HashJoinNodeSuite extends LocalNodeTest {
// Test all combinations of the two dimensions: with/out unsafe and build sides
private val buildSides = Seq(BuildLeft, BuildRight)
buildSides.foreach { buildSide =>
testJoin(buildSide)
}
/**
* Builds a [[HashedRelation]] based on a resolved `buildKeys`
* and a resolved `buildNode`.
*/
private def buildHashedRelation(
conf: SQLConf,
buildKeys: Seq[Expression],
buildNode: LocalNode): HashedRelation = {
val buildSideKeyGenerator = UnsafeProjection.create(buildKeys, buildNode.output)
buildNode.prepare()
buildNode.open()
val hashedRelation = HashedRelation(buildNode, buildSideKeyGenerator)
buildNode.close()
hashedRelation
}
/**
* Test inner hash join with varying degrees of matches.
*/
private def testJoin(buildSide: BuildSide): Unit = {
val testNamePrefix = buildSide
val someData = (1 to 100).map { i => (i, "burger" + i) }.toArray
val conf = new SQLConf
// Actual test body
def runTest(leftInput: Array[(Int, String)], rightInput: Array[(Int, String)]): Unit = {
val rightInputMap = rightInput.toMap
val leftNode = new DummyNode(joinNameAttributes, leftInput)
val rightNode = new DummyNode(joinNicknameAttributes, rightInput)
val makeBinaryHashJoinNode = (node1: LocalNode, node2: LocalNode) => {
val binaryHashJoinNode =
BinaryHashJoinNode(conf, Seq('id1), Seq('id2), buildSide, node1, node2)
resolveExpressions(binaryHashJoinNode)
}
val makeBroadcastJoinNode = (node1: LocalNode, node2: LocalNode) => {
val leftKeys = Seq('id1.attr)
val rightKeys = Seq('id2.attr)
// Figure out the build side and stream side.
val (buildNode, buildKeys, streamedNode, streamedKeys) = buildSide match {
case BuildLeft => (node1, leftKeys, node2, rightKeys)
case BuildRight => (node2, rightKeys, node1, leftKeys)
}
// Resolve the expressions of the build side and then create a HashedRelation.
val resolvedBuildNode = resolveExpressions(buildNode)
val resolvedBuildKeys = resolveExpressions(buildKeys, resolvedBuildNode)
val hashedRelation = buildHashedRelation(conf, resolvedBuildKeys, resolvedBuildNode)
val broadcastHashedRelation = mock(classOf[TorrentBroadcast[HashedRelation]])
when(broadcastHashedRelation.value).thenReturn(hashedRelation)
val hashJoinNode =
BroadcastHashJoinNode(
conf,
streamedKeys,
streamedNode,
buildSide,
resolvedBuildNode.output,
broadcastHashedRelation)
resolveExpressions(hashJoinNode)
}
val expectedOutput = leftInput
.filter { case (k, _) => rightInputMap.contains(k) }
.map { case (k, v) => (k, v, k, rightInputMap(k)) }
Seq(makeBinaryHashJoinNode, makeBroadcastJoinNode).foreach { makeNode =>
val makeUnsafeNode = wrapForUnsafe(makeNode)
val hashJoinNode = makeUnsafeNode(leftNode, rightNode)
val actualOutput = hashJoinNode.collect().map { row =>
// (id, name, id, nickname)
(row.getInt(0), row.getString(1), row.getInt(2), row.getString(3))
}
assert(actualOutput === expectedOutput)
}
}
test(s"$testNamePrefix: empty") {
runTest(Array.empty, Array.empty)
runTest(someData, Array.empty)
runTest(Array.empty, someData)
}
test(s"$testNamePrefix: no matches") {
val someIrrelevantData = (10000 to 100100).map { i => (i, "piper" + i) }.toArray
runTest(someData, Array.empty)
runTest(Array.empty, someData)
runTest(someData, someIrrelevantData)
runTest(someIrrelevantData, someData)
}
test(s"$testNamePrefix: partial matches") {
val someOtherData = (50 to 150).map { i => (i, "finnegan" + i) }.toArray
runTest(someData, someOtherData)
runTest(someOtherData, someData)
}
test(s"$testNamePrefix: full matches") {
val someSuperRelevantData = someData.map { case (k, v) => (k, "cooper" + v) }.toArray
runTest(someData, someSuperRelevantData)
runTest(someSuperRelevantData, someData)
}
}
}
/*
* 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.execution.local
class IntersectNodeSuite extends LocalNodeTest {
test("basic") {
val n = 100
val leftData = (1 to n).filter { i => i % 2 == 0 }.map { i => (i, i) }.toArray
val rightData = (1 to n).filter { i => i % 3 == 0 }.map { i => (i, i) }.toArray
val leftNode = new DummyNode(kvIntAttributes, leftData)
val rightNode = new DummyNode(kvIntAttributes, rightData)
val intersectNode = new IntersectNode(conf, leftNode, rightNode)
val expectedOutput = leftData.intersect(rightData)
val actualOutput = intersectNode.collect().map { case row =>
(row.getInt(0), row.getInt(1))
}
assert(actualOutput === expectedOutput)
}
}
/*
* 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.execution.local
class LimitNodeSuite extends LocalNodeTest {
private def testLimit(inputData: Array[(Int, Int)] = Array.empty, limit: Int = 10): Unit = {
val inputNode = new DummyNode(kvIntAttributes, inputData)
val limitNode = new LimitNode(conf, limit, inputNode)
val expectedOutput = inputData.take(limit)
val actualOutput = limitNode.collect().map { case row =>
(row.getInt(0), row.getInt(1))
}
assert(actualOutput === expectedOutput)
}
test("empty") {
testLimit()
}
test("basic") {
testLimit((1 to 100).map { i => (i, i) }.toArray, 20)
}
}
/*
* 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.execution.local
class LocalNodeSuite extends LocalNodeTest {
private val data = (1 to 100).map { i => (i, i) }.toArray
test("basic open, next, fetch, close") {
val node = new DummyNode(kvIntAttributes, data)
assert(!node.isOpen)
node.open()
assert(node.isOpen)
data.foreach { case (k, v) =>
assert(node.next())
// fetch should be idempotent
val fetched = node.fetch()
assert(node.fetch() === fetched)
assert(node.fetch() === fetched)
assert(node.fetch().numFields === 2)
assert(node.fetch().getInt(0) === k)
assert(node.fetch().getInt(1) === v)
}
assert(!node.next())
node.close()
assert(!node.isOpen)
}
test("asIterator") {
val node = new DummyNode(kvIntAttributes, data)
val iter = node.asIterator
node.open()
data.foreach { case (k, v) =>
// hasNext should be idempotent
assert(iter.hasNext)
assert(iter.hasNext)
val item = iter.next()
assert(item.numFields === 2)
assert(item.getInt(0) === k)
assert(item.getInt(1) === v)
}
intercept[NoSuchElementException] {
iter.next()
}
node.close()
}
test("collect") {
val node = new DummyNode(kvIntAttributes, data)
node.open()
val collected = node.collect()
assert(collected.size === data.size)
assert(collected.forall(_.size === 2))
assert(collected.map { case row => (row.getInt(0), row.getInt(0)) } === data)
node.close()
}
}
/*
* 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.execution.local
import org.apache.spark.SparkFunSuite
import org.apache.spark.sql.catalyst.analysis.UnresolvedAttribute
import org.apache.spark.sql.catalyst.expressions.{AttributeReference, Expression}
import org.apache.spark.sql.internal.SQLConf
import org.apache.spark.sql.types.{IntegerType, StringType}
class LocalNodeTest extends SparkFunSuite {
protected val conf: SQLConf = new SQLConf
protected val kvIntAttributes = Seq(
AttributeReference("k", IntegerType)(),
AttributeReference("v", IntegerType)())
protected val joinNameAttributes = Seq(
AttributeReference("id1", IntegerType)(),
AttributeReference("name", StringType)())
protected val joinNicknameAttributes = Seq(
AttributeReference("id2", IntegerType)(),
AttributeReference("nickname", StringType)())
/**
* Wrap a function processing two [[LocalNode]]s such that:
* (1) all input rows are automatically converted to unsafe rows
* (2) all output rows are automatically converted back to safe rows
*/
protected def wrapForUnsafe(
f: (LocalNode, LocalNode) => LocalNode): (LocalNode, LocalNode) => LocalNode = {
(left: LocalNode, right: LocalNode) => {
val _left = ConvertToUnsafeNode(conf, left)
val _right = ConvertToUnsafeNode(conf, right)
val r = f(_left, _right)
ConvertToSafeNode(conf, r)
}
}
/**
* Recursively resolve all expressions in a [[LocalNode]] using the node's attributes.
*/
protected def resolveExpressions(outputNode: LocalNode): LocalNode = {
outputNode transform {
case node: LocalNode =>
val inputMap = node.output.map { a => (a.name, a) }.toMap
node transformExpressions {
case UnresolvedAttribute(Seq(u)) =>
inputMap.getOrElse(u,
sys.error(s"Invalid Test: Cannot resolve $u given input $inputMap"))
}
}
}
/**
* Resolve all expressions in `expressions` based on the `output` of `localNode`.
* It assumes that all expressions in the `localNode` are resolved.
*/
protected def resolveExpressions(
expressions: Seq[Expression],
localNode: LocalNode): Seq[Expression] = {
require(localNode.expressions.forall(_.resolved))
val inputMap = localNode.output.map { a => (a.name, a) }.toMap
expressions.map { expression =>
expression.transformUp {
case UnresolvedAttribute(Seq(u)) =>
inputMap.getOrElse(u,
sys.error(s"Invalid Test: Cannot resolve $u given input $inputMap"))
}
}
}
}
/*
* 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.execution.local
import org.apache.spark.sql.catalyst.dsl.expressions._
import org.apache.spark.sql.catalyst.plans.{FullOuter, JoinType, LeftOuter, RightOuter}
import org.apache.spark.sql.execution.joins.{BuildLeft, BuildRight, BuildSide}
import org.apache.spark.sql.internal.SQLConf
class NestedLoopJoinNodeSuite extends LocalNodeTest {
// Test all combinations of the three dimensions: with/out unsafe, build sides, and join types
private val buildSides = Seq(BuildLeft, BuildRight)
private val joinTypes = Seq(LeftOuter, RightOuter, FullOuter)
buildSides.foreach { buildSide =>
joinTypes.foreach { joinType =>
testJoin(buildSide, joinType)
}
}
/**
* Test outer nested loop joins with varying degrees of matches.
*/
private def testJoin(buildSide: BuildSide, joinType: JoinType): Unit = {
val testNamePrefix = s"$buildSide / $joinType"
val someData = (1 to 100).map { i => (i, "burger" + i) }.toArray
val conf = new SQLConf
// Actual test body
def runTest(
joinType: JoinType,
leftInput: Array[(Int, String)],
rightInput: Array[(Int, String)]): Unit = {
val leftNode = new DummyNode(joinNameAttributes, leftInput)
val rightNode = new DummyNode(joinNicknameAttributes, rightInput)
val cond = 'id1 === 'id2
val makeNode = (node1: LocalNode, node2: LocalNode) => {
resolveExpressions(
new NestedLoopJoinNode(conf, node1, node2, buildSide, joinType, Some(cond)))
}
val makeUnsafeNode = wrapForUnsafe(makeNode)
val hashJoinNode = makeUnsafeNode(leftNode, rightNode)
val expectedOutput = generateExpectedOutput(leftInput, rightInput, joinType)
val actualOutput = hashJoinNode.collect().map { row =>
// (
// id, name,
// id, nickname
// )
(
Option(row.get(0)).map(_.asInstanceOf[Int]), Option(row.getString(1)),
Option(row.get(2)).map(_.asInstanceOf[Int]), Option(row.getString(3))
)
}
assert(actualOutput.toSet === expectedOutput.toSet)
}
test(s"$testNamePrefix: empty") {
runTest(joinType, Array.empty, Array.empty)
}
test(s"$testNamePrefix: no matches") {
val someIrrelevantData = (10000 to 10100).map { i => (i, "piper" + i) }.toArray
runTest(joinType, someData, Array.empty)
runTest(joinType, Array.empty, someData)
runTest(joinType, someData, someIrrelevantData)
runTest(joinType, someIrrelevantData, someData)
}
test(s"$testNamePrefix: partial matches") {
val someOtherData = (50 to 150).map { i => (i, "finnegan" + i) }.toArray
runTest(joinType, someData, someOtherData)
runTest(joinType, someOtherData, someData)
}
test(s"$testNamePrefix: full matches") {
val someSuperRelevantData = someData.map { case (k, v) => (k, "cooper" + v) }
runTest(joinType, someData, someSuperRelevantData)
runTest(joinType, someSuperRelevantData, someData)
}
}
/**
* Helper method to generate the expected output of a test based on the join type.
*/
private def generateExpectedOutput(
leftInput: Array[(Int, String)],
rightInput: Array[(Int, String)],
joinType: JoinType): Array[(Option[Int], Option[String], Option[Int], Option[String])] = {
joinType match {
case LeftOuter =>
val rightInputMap = rightInput.toMap
leftInput.map { case (k, v) =>
val rightKey = rightInputMap.get(k).map { _ => k }
val rightValue = rightInputMap.get(k)
(Some(k), Some(v), rightKey, rightValue)
}
case RightOuter =>
val leftInputMap = leftInput.toMap
rightInput.map { case (k, v) =>
val leftKey = leftInputMap.get(k).map { _ => k }
val leftValue = leftInputMap.get(k)
(leftKey, leftValue, Some(k), Some(v))
}
case FullOuter =>
val leftInputMap = leftInput.toMap
val rightInputMap = rightInput.toMap
val leftOutput = leftInput.map { case (k, v) =>
val rightKey = rightInputMap.get(k).map { _ => k }
val rightValue = rightInputMap.get(k)
(Some(k), Some(v), rightKey, rightValue)
}
val rightOutput = rightInput.map { case (k, v) =>
val leftKey = leftInputMap.get(k).map { _ => k }
val leftValue = leftInputMap.get(k)
(leftKey, leftValue, Some(k), Some(v))
}
(leftOutput ++ rightOutput).distinct
case other =>
throw new IllegalArgumentException(s"Join type $other is not applicable")
}
}
}
/*
* 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.execution.local
import org.apache.spark.sql.catalyst.expressions.{AttributeReference, NamedExpression}
import org.apache.spark.sql.types.{IntegerType, StringType}
class ProjectNodeSuite extends LocalNodeTest {
private val pieAttributes = Seq(
AttributeReference("id", IntegerType)(),
AttributeReference("age", IntegerType)(),
AttributeReference("name", StringType)())
private def testProject(inputData: Array[(Int, Int, String)] = Array.empty): Unit = {
val inputNode = new DummyNode(pieAttributes, inputData)
val columns = Seq[NamedExpression](inputNode.output(0), inputNode.output(2))
val projectNode = new ProjectNode(conf, columns, inputNode)
val expectedOutput = inputData.map { case (id, age, name) => (id, name) }
val actualOutput = projectNode.collect().map { case row =>
(row.getInt(0), row.getString(1))
}
assert(actualOutput === expectedOutput)
}
test("empty") {
testProject()
}
test("basic") {
testProject((1 to 100).map { i => (i, i + 1, "pie" + i) }.toArray)
}
}
/*
* 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.execution.local
import org.apache.spark.util.random.{BernoulliCellSampler, PoissonSampler}
class SampleNodeSuite extends LocalNodeTest {
private def testSample(withReplacement: Boolean): Unit = {
val seed = 0L
val lowerb = 0.0
val upperb = 0.3
val maybeOut = if (withReplacement) "" else "out"
test(s"with$maybeOut replacement") {
val inputData = (1 to 1000).map { i => (i, i) }.toArray
val inputNode = new DummyNode(kvIntAttributes, inputData)
val sampleNode = new SampleNode(conf, lowerb, upperb, withReplacement, seed, inputNode)
val sampler =
if (withReplacement) {
new PoissonSampler[(Int, Int)](upperb - lowerb, useGapSamplingIfPossible = false)
} else {
new BernoulliCellSampler[(Int, Int)](lowerb, upperb)
}
sampler.setSeed(seed)
val expectedOutput = sampler.sample(inputData.iterator).toArray
val actualOutput = sampleNode.collect().map { case row =>
(row.getInt(0), row.getInt(1))
}
assert(actualOutput === expectedOutput)
}
}
testSample(withReplacement = true)
testSample(withReplacement = false)
}
/*
* 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.execution.local
import scala.util.Random
import org.apache.spark.sql.catalyst.expressions._
import org.apache.spark.sql.catalyst.expressions.SortOrder
class TakeOrderedAndProjectNodeSuite extends LocalNodeTest {
private def testTakeOrderedAndProject(desc: Boolean): Unit = {
val limit = 10
val ascOrDesc = if (desc) "desc" else "asc"
test(ascOrDesc) {
val inputData = Random.shuffle((1 to 100).toList).map { i => (i, i) }.toArray
val inputNode = new DummyNode(kvIntAttributes, inputData)
val firstColumn = inputNode.output(0)
val sortDirection = if (desc) Descending else Ascending
val sortOrder = SortOrder(firstColumn, sortDirection)
val takeOrderAndProjectNode = new TakeOrderedAndProjectNode(
conf, limit, Seq(sortOrder), Some(Seq(firstColumn)), inputNode)
val expectedOutput = inputData
.map { case (k, _) => k }
.sortBy { k => k * (if (desc) -1 else 1) }
.take(limit)
val actualOutput = takeOrderAndProjectNode.collect().map { row => row.getInt(0) }
assert(actualOutput === expectedOutput)
}
}
testTakeOrderedAndProject(desc = false)
testTakeOrderedAndProject(desc = true)
}
/*
* 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.execution.local
class UnionNodeSuite extends LocalNodeTest {
private def testUnion(inputData: Seq[Array[(Int, Int)]]): Unit = {
val inputNodes = inputData.map { data =>
new DummyNode(kvIntAttributes, data)
}
val unionNode = new UnionNode(conf, inputNodes)
val expectedOutput = inputData.flatten
val actualOutput = unionNode.collect().map { case row =>
(row.getInt(0), row.getInt(1))
}
assert(actualOutput === expectedOutput)
}
test("empty") {
testUnion(Seq(Array.empty))
testUnion(Seq(Array.empty, Array.empty))
}
test("self") {
val data = (1 to 100).map { i => (i, i) }.toArray
testUnion(Seq(data))
testUnion(Seq(data, data))
testUnion(Seq(data, data, data))
}
test("basic") {
val zero = Array.empty[(Int, Int)]
val one = (1 to 100).map { i => (i, i) }.toArray
val two = (50 to 150).map { i => (i, i) }.toArray
val three = (800 to 900).map { i => (i, i) }.toArray
testUnion(Seq(zero, one, two, three))
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment