This is an automated email from the ASF dual-hosted git repository.
yashmayya pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git
The following commit(s) were added to refs/heads/master by this push:
new a1aab649f8d Refactor set operators to allow multiple children for
UNION / UNION ALL (#16990)
a1aab649f8d is described below
commit a1aab649f8d126bf55ee81f596b1a6eaaa223826
Author: Yash Mayya <[email protected]>
AuthorDate: Fri Oct 10 15:53:22 2025 -0700
Refactor set operators to allow multiple children for UNION / UNION ALL
(#16990)
---
...asedSetOperator.java => BinarySetOperator.java} | 49 ++++++++++++--
.../runtime/operator/set/IntersectAllOperator.java | 2 +-
.../runtime/operator/set/IntersectOperator.java | 2 +-
.../runtime/operator/set/MinusAllOperator.java | 2 +-
.../query/runtime/operator/set/MinusOperator.java | 2 +-
.../query/runtime/operator/set/SetOperator.java | 76 ++--------------------
.../runtime/operator/set/UnionAllOperator.java | 36 ++++++++--
.../query/runtime/operator/set/UnionOperator.java | 73 +++++++++++----------
.../runtime/operator/set/UnionAllOperatorTest.java | 7 +-
.../runtime/operator/set/UnionOperatorTest.java | 4 +-
.../query/runtime/queries/QueryRunnerTestBase.java | 3 +
.../src/test/resources/queries/SetOpsNonH2.json | 9 +++
12 files changed, 136 insertions(+), 129 deletions(-)
diff --git
a/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/RightRowSetBasedSetOperator.java
b/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/BinarySetOperator.java
similarity index 70%
rename from
pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/RightRowSetBasedSetOperator.java
rename to
pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/BinarySetOperator.java
index 0a2ff8e513b..7b9a5938772 100644
---
a/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/RightRowSetBasedSetOperator.java
+++
b/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/BinarySetOperator.java
@@ -18,6 +18,7 @@
*/
package org.apache.pinot.query.runtime.operator.set;
+import com.google.common.base.Preconditions;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import java.util.ArrayList;
@@ -31,25 +32,31 @@ import
org.apache.pinot.query.runtime.plan.OpChainExecutionContext;
/**
- * Abstract base class for set operators that process the right child operator
first in order to build a set of rows
- * that are then used to filter rows from the left child operator.
+ * Base class for set operators like INTERSECT and EXCEPT / MINUS that always
have two children.
*/
-public abstract class RightRowSetBasedSetOperator extends SetOperator {
+public abstract class BinarySetOperator extends SetOperator {
+
+ protected final MultiStageOperator _leftChildOperator;
+ protected final MultiStageOperator _rightChildOperator;
protected final Multiset<Record> _rightRowSet;
+ private MseBlock.Eos _eos;
+ private boolean _isRightChildOperatorProcessed;
- public RightRowSetBasedSetOperator(OpChainExecutionContext
opChainExecutionContext,
+ public BinarySetOperator(OpChainExecutionContext opChainExecutionContext,
List<MultiStageOperator> inputOperators,
DataSchema dataSchema) {
super(opChainExecutionContext, inputOperators, dataSchema);
+ Preconditions.checkArgument(inputOperators.size() == 2, "Binary set
operator should have 2 inputs");
+ _leftChildOperator = inputOperators.get(0);
+ _rightChildOperator = inputOperators.get(1);
_rightRowSet = HashMultiset.create();
}
/**
* Processes the right child operator and builds the set of rows that can be
used to filter the left child.
*
- * @return either a data block containing rows or an EoS block, never {@code
null}.
+ * @return EoS block after processing the right child completely.
*/
- @Override
protected MseBlock processRightOperator() {
MseBlock block = _rightChildOperator.nextBlock();
while (block.isData()) {
@@ -69,7 +76,6 @@ public abstract class RightRowSetBasedSetOperator extends
SetOperator {
*
* @return block containing matched rows or EoS, never {@code null}.
*/
- @Override
protected MseBlock processLeftOperator() {
// Keep reading the input blocks until we find a match row or all blocks
are processed.
// TODO: Consider batching the rows to improve performance.
@@ -92,6 +98,35 @@ public abstract class RightRowSetBasedSetOperator extends
SetOperator {
}
}
+ @Override
+ protected MseBlock getNextBlock() {
+ if (_eos != null) {
+ return _eos;
+ }
+
+ if (!_isRightChildOperatorProcessed) {
+ MseBlock mseBlock = processRightOperator();
+
+ if (mseBlock.isData()) {
+ return mseBlock;
+ } else if (mseBlock.isError()) {
+ _eos = (MseBlock.Eos) mseBlock;
+ return _eos;
+ } else if (mseBlock.isSuccess()) {
+ // If it's a regular EOS block, we continue to process the left child
operator.
+ _isRightChildOperatorProcessed = true;
+ }
+ }
+
+ MseBlock mseBlock = processLeftOperator();
+ if (mseBlock.isEos()) {
+ _eos = (MseBlock.Eos) mseBlock;
+ return _eos;
+ } else {
+ return mseBlock;
+ }
+ }
+
/**
* Returns true if the row matches the criteria defined by the set operation.
* <p>
diff --git
a/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/IntersectAllOperator.java
b/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/IntersectAllOperator.java
index 7b0ad8962f3..e5129edb992 100644
---
a/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/IntersectAllOperator.java
+++
b/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/IntersectAllOperator.java
@@ -30,7 +30,7 @@ import org.slf4j.LoggerFactory;
/**
* INTERSECT ALL operator.
*/
-public class IntersectAllOperator extends RightRowSetBasedSetOperator {
+public class IntersectAllOperator extends BinarySetOperator {
private static final Logger LOGGER =
LoggerFactory.getLogger(IntersectAllOperator.class);
private static final String EXPLAIN_NAME = "INTERSECT_ALL";
diff --git
a/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/IntersectOperator.java
b/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/IntersectOperator.java
index 96ef78082b2..981a821cccf 100644
---
a/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/IntersectOperator.java
+++
b/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/IntersectOperator.java
@@ -30,7 +30,7 @@ import org.slf4j.LoggerFactory;
/**
* Intersect operator.
*/
-public class IntersectOperator extends RightRowSetBasedSetOperator {
+public class IntersectOperator extends BinarySetOperator {
private static final Logger LOGGER =
LoggerFactory.getLogger(IntersectOperator.class);
private static final String EXPLAIN_NAME = "INTERSECT";
diff --git
a/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/MinusAllOperator.java
b/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/MinusAllOperator.java
index d500f096d5f..e81aa345bf4 100644
---
a/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/MinusAllOperator.java
+++
b/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/MinusAllOperator.java
@@ -30,7 +30,7 @@ import org.slf4j.LoggerFactory;
/**
* EXCEPT ALL operator.
*/
-public class MinusAllOperator extends RightRowSetBasedSetOperator {
+public class MinusAllOperator extends BinarySetOperator {
private static final Logger LOGGER =
LoggerFactory.getLogger(MinusAllOperator.class);
private static final String EXPLAIN_NAME = "MINUS_ALL";
diff --git
a/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/MinusOperator.java
b/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/MinusOperator.java
index 180101b3f80..7cb2857feb7 100644
---
a/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/MinusOperator.java
+++
b/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/MinusOperator.java
@@ -31,7 +31,7 @@ import org.slf4j.LoggerFactory;
/**
* Minus/Except operator.
*/
-public class MinusOperator extends RightRowSetBasedSetOperator {
+public class MinusOperator extends BinarySetOperator {
private static final Logger LOGGER =
LoggerFactory.getLogger(MinusOperator.class);
private static final String EXPLAIN_NAME = "MINUS";
diff --git
a/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/SetOperator.java
b/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/SetOperator.java
index dcfeb9397cc..6e870835ef2 100644
---
a/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/SetOperator.java
+++
b/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/SetOperator.java
@@ -18,43 +18,28 @@
*/
package org.apache.pinot.query.runtime.operator.set;
-import com.google.common.base.Preconditions;
import java.util.List;
import org.apache.pinot.common.datatable.StatMap;
import org.apache.pinot.common.utils.DataSchema;
-import org.apache.pinot.core.common.ExplainPlanRows;
-import org.apache.pinot.core.operator.ExecutionStatistics;
-import org.apache.pinot.query.runtime.blocks.MseBlock;
import org.apache.pinot.query.runtime.operator.MultiStageOperator;
import org.apache.pinot.query.runtime.plan.OpChainExecutionContext;
-import org.apache.pinot.segment.spi.IndexSegment;
/**
- * Set operator, which supports UNION, INTERSECT and EXCEPT.
- * This has two child operators, and the left child operator is the one that
is used to construct the result.
- * The right child operator is used to construct a set of rows that are used
to filter the left child operator.
- * The right child operator is consumed in a blocking manner, and the left
child operator is consumed in a non-blocking
- * UnionOperator: The right child operator is consumed in a blocking manner.
+ * Set operator, which supports UNION (ALL), INTERSECT (ALL) and EXCEPT /
MINUS (ALL).
*/
public abstract class SetOperator extends MultiStageOperator {
- protected final MultiStageOperator _leftChildOperator;
- protected final MultiStageOperator _rightChildOperator;
+ protected final List<MultiStageOperator> _inputOperators;
protected final DataSchema _dataSchema;
- private boolean _isRightChildOperatorProcessed;
- private MseBlock.Eos _eos;
private final StatMap<StatKey> _statMap = new StatMap<>(StatKey.class);
public SetOperator(OpChainExecutionContext opChainExecutionContext,
List<MultiStageOperator> inputOperators,
DataSchema dataSchema) {
super(opChainExecutionContext);
_dataSchema = dataSchema;
- Preconditions.checkState(inputOperators.size() == 2, "Set operator should
have 2 child operators");
- _leftChildOperator = inputOperators.get(0);
- _rightChildOperator = inputOperators.get(1);
- _isRightChildOperatorProcessed = false;
+ _inputOperators = inputOperators;
}
@Override
@@ -67,62 +52,9 @@ public abstract class SetOperator extends MultiStageOperator
{
@Override
public List<MultiStageOperator> getChildOperators() {
- return List.of(_leftChildOperator, _rightChildOperator);
+ return _inputOperators;
}
- @Override
- public void prepareForExplainPlan(ExplainPlanRows explainPlanRows) {
- super.prepareForExplainPlan(explainPlanRows);
- }
-
- @Override
- public void explainPlan(ExplainPlanRows explainPlanRows, int[] globalId, int
parentId) {
- super.explainPlan(explainPlanRows, globalId, parentId);
- }
-
- @Override
- public IndexSegment getIndexSegment() {
- return super.getIndexSegment();
- }
-
- @Override
- public ExecutionStatistics getExecutionStatistics() {
- return super.getExecutionStatistics();
- }
-
- @Override
- protected MseBlock getNextBlock() {
- if (_eos != null) {
- return _eos;
- }
-
- if (!_isRightChildOperatorProcessed) {
- MseBlock mseBlock = processRightOperator();
-
- if (mseBlock.isData()) {
- return mseBlock;
- } else if (mseBlock.isError()) {
- _eos = (MseBlock.Eos) mseBlock;
- return _eos;
- } else if (mseBlock.isSuccess()) {
- // If it's a regular EOS block, we continue to process the left child
operator.
- _isRightChildOperatorProcessed = true;
- }
- }
-
- MseBlock mseBlock = processLeftOperator();
- if (mseBlock.isEos()) {
- _eos = (MseBlock.Eos) mseBlock;
- return _eos;
- } else {
- return mseBlock;
- }
- }
-
- protected abstract MseBlock processLeftOperator();
-
- protected abstract MseBlock processRightOperator();
-
@Override
protected StatMap<?> copyStatMaps() {
return new StatMap<>(_statMap);
diff --git
a/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/UnionAllOperator.java
b/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/UnionAllOperator.java
index 8cd2f07a9cb..f858a27547f 100644
---
a/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/UnionAllOperator.java
+++
b/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/UnionAllOperator.java
@@ -28,12 +28,15 @@ import org.slf4j.LoggerFactory;
/**
- * Union operator for UNION ALL queries.
+ * Union operator for UNION ALL queries. Each child operator is fully drained
sequentially and all rows are returned.
*/
public class UnionAllOperator extends SetOperator {
private static final Logger LOGGER =
LoggerFactory.getLogger(UnionAllOperator.class);
private static final String EXPLAIN_NAME = "UNION_ALL";
+ private MseBlock _eosBlock = null;
+ private int _currentOperatorIndex = 0;
+
public UnionAllOperator(OpChainExecutionContext opChainExecutionContext,
List<MultiStageOperator> inputOperators,
DataSchema dataSchema) {
super(opChainExecutionContext, inputOperators, dataSchema);
@@ -55,12 +58,31 @@ public class UnionAllOperator extends SetOperator {
}
@Override
- protected MseBlock processRightOperator() {
- return _rightChildOperator.nextBlock();
- }
+ protected MseBlock getNextBlock()
+ throws Exception {
+ if (_eosBlock != null) {
+ return _eosBlock;
+ }
- @Override
- protected MseBlock processLeftOperator() {
- return _leftChildOperator.nextBlock();
+ while (_currentOperatorIndex < _inputOperators.size()) {
+ MultiStageOperator currentOperator =
_inputOperators.get(_currentOperatorIndex);
+ MseBlock block = currentOperator.nextBlock();
+ if (block.isError()) {
+ _eosBlock = block;
+ return block;
+ } else if (block.isSuccess()) {
+ _currentOperatorIndex++;
+ if (_currentOperatorIndex == _inputOperators.size()) {
+ _eosBlock = block;
+ return block;
+ }
+ } else if (block.isData()) {
+ return block;
+ }
+ }
+
+ // All input operators are exhausted, return EoS block.
+ assert _eosBlock != null;
+ return _eosBlock;
}
}
diff --git
a/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/UnionOperator.java
b/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/UnionOperator.java
index ff724f22bea..2d51edcdbc9 100644
---
a/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/UnionOperator.java
+++
b/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/set/UnionOperator.java
@@ -18,8 +18,10 @@
*/
package org.apache.pinot.query.runtime.operator.set;
+import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
import javax.annotation.Nullable;
import org.apache.pinot.common.utils.DataSchema;
import org.apache.pinot.core.data.table.Record;
@@ -33,53 +35,58 @@ import org.slf4j.LoggerFactory;
/**
* Union operator for UNION queries. Unlike {@link UnionAllOperator}, this
operator removes duplicate rows and only
- * returns distinct rows.
+ * returns distinct rows. Each child operator is fully drained sequentially
and distinct rows are returned.
*/
-public class UnionOperator extends RightRowSetBasedSetOperator {
+public class UnionOperator extends SetOperator {
private static final Logger LOGGER =
LoggerFactory.getLogger(UnionOperator.class);
private static final String EXPLAIN_NAME = "UNION";
+ private MseBlock _eosBlock = null;
+ private int _currentOperatorIndex = 0;
+ private final Set<Record> _seenRecords = new ObjectOpenHashSet<>();
+
public UnionOperator(OpChainExecutionContext opChainExecutionContext,
List<MultiStageOperator> inputOperators, DataSchema dataSchema) {
super(opChainExecutionContext, inputOperators, dataSchema);
}
@Override
- protected MseBlock processRightOperator() {
- MseBlock block = _rightChildOperator.nextBlock();
- while (block.isData()) {
- MseBlock.Data dataBlock = (MseBlock.Data) block;
- List<Object[]> rows = new ArrayList<>();
- for (Object[] row : dataBlock.asRowHeap().getRows()) {
- Record record = new Record(row);
- if (!_rightRowSet.contains(record)) {
- // Add a new unique row.
- rows.add(row);
- _rightRowSet.add(record);
+ protected MseBlock getNextBlock()
+ throws Exception {
+ if (_eosBlock != null) {
+ return _eosBlock;
+ }
+
+ while (_currentOperatorIndex < _inputOperators.size()) {
+ MultiStageOperator currentOperator =
_inputOperators.get(_currentOperatorIndex);
+ MseBlock block = currentOperator.nextBlock();
+ if (block.isError()) {
+ _eosBlock = block;
+ return block;
+ } else if (block.isSuccess()) {
+ _currentOperatorIndex++;
+ if (_currentOperatorIndex == _inputOperators.size()) {
+ _eosBlock = block;
+ return block;
+ }
+ } else if (block.isData()) {
+ List<Object[]> rows = new ArrayList<>();
+ for (Object[] row : ((MseBlock.Data) block).asRowHeap().getRows()) {
+ Record record = new Record(row);
+ // TODO: Use a more memory efficient way to track seen rows.
+ if (_seenRecords.add(record)) {
+ rows.add(row);
+ }
+ }
+ if (!rows.isEmpty()) {
+ return new RowHeapDataBlock(rows, _dataSchema);
}
- }
- checkTerminationAndSampleUsage();
- // If we have collected some rows, return them as a new block.
- if (!rows.isEmpty()) {
- return new RowHeapDataBlock(rows, _dataSchema);
- } else {
- block = _rightChildOperator.nextBlock();
}
}
- assert block.isEos();
- return block;
- }
- @Override
- protected boolean handleRowMatched(Object[] row) {
- if (!_rightRowSet.contains(new Record(row))) {
- // Row is unique, add it to the result and also to the row set to skip
later duplicates.
- _rightRowSet.add(new Record(row));
- return true;
- } else {
- // Row is a duplicate, skip it.
- return false;
- }
+ // All input operators are exhausted, return EoS block.
+ assert _eosBlock != null;
+ return _eosBlock;
}
@Override
diff --git
a/pinot-query-runtime/src/test/java/org/apache/pinot/query/runtime/operator/set/UnionAllOperatorTest.java
b/pinot-query-runtime/src/test/java/org/apache/pinot/query/runtime/operator/set/UnionAllOperatorTest.java
index fdfc494c1a9..79ecbbe8b93 100644
---
a/pinot-query-runtime/src/test/java/org/apache/pinot/query/runtime/operator/set/UnionAllOperatorTest.java
+++
b/pinot-query-runtime/src/test/java/org/apache/pinot/query/runtime/operator/set/UnionAllOperatorTest.java
@@ -42,6 +42,7 @@ public class UnionAllOperatorTest {
MultiStageOperator leftOperator = new
BlockListMultiStageOperator.Builder(schema)
.addRow(1, "AA")
.addRow(2, "BB")
+ .addRow(3, "aa")
.buildWithEos();
MultiStageOperator rightOperator = new
BlockListMultiStageOperator.Builder(schema)
.addRow(3, "aa")
@@ -58,11 +59,9 @@ public class UnionAllOperatorTest {
resultRows.addAll(((MseBlock.Data) result).asRowHeap().getRows());
result = unionAllOperator.nextBlock();
}
- // Note that UNION ALL does not guarantee the order of rows, and our
implementation adds rows from the right child
- // first
List<Object[]> expectedRows =
- Arrays.asList(new Object[]{3, "aa"}, new Object[]{4, "bb"}, new
Object[]{5, "cc"}, new Object[]{1, "AA"},
- new Object[]{2, "BB"});
+ Arrays.asList(new Object[]{1, "AA"}, new Object[]{2, "BB"}, new
Object[]{3, "aa"}, new Object[]{3, "aa"},
+ new Object[]{4, "bb"}, new Object[]{5, "cc"});
Assert.assertEquals(resultRows.size(), expectedRows.size());
for (int i = 0; i < resultRows.size(); i++) {
Assert.assertEquals(resultRows.get(i), expectedRows.get(i));
diff --git
a/pinot-query-runtime/src/test/java/org/apache/pinot/query/runtime/operator/set/UnionOperatorTest.java
b/pinot-query-runtime/src/test/java/org/apache/pinot/query/runtime/operator/set/UnionOperatorTest.java
index 971bbb34062..a69b8f0febd 100644
---
a/pinot-query-runtime/src/test/java/org/apache/pinot/query/runtime/operator/set/UnionOperatorTest.java
+++
b/pinot-query-runtime/src/test/java/org/apache/pinot/query/runtime/operator/set/UnionOperatorTest.java
@@ -60,8 +60,8 @@ public class UnionOperatorTest {
result = unionOperator.nextBlock();
}
List<Object[]> expectedRows =
- Arrays.asList(new Object[]{3, "aa"}, new Object[]{4, "bb"}, new
Object[]{5, "cc"}, new Object[]{2, "BB"},
- new Object[]{1, "AA"});
+ Arrays.asList(new Object[]{1, "AA"}, new Object[]{2, "BB"}, new
Object[]{3, "aa"}, new Object[]{4, "bb"},
+ new Object[]{5, "cc"});
Assert.assertEquals(resultRows.size(), expectedRows.size());
for (int i = 0; i < resultRows.size(); i++) {
Assert.assertEquals(resultRows.get(i), expectedRows.get(i));
diff --git
a/pinot-query-runtime/src/test/java/org/apache/pinot/query/runtime/queries/QueryRunnerTestBase.java
b/pinot-query-runtime/src/test/java/org/apache/pinot/query/runtime/queries/QueryRunnerTestBase.java
index 35cacc6ce20..44a54c76a3a 100644
---
a/pinot-query-runtime/src/test/java/org/apache/pinot/query/runtime/queries/QueryRunnerTestBase.java
+++
b/pinot-query-runtime/src/test/java/org/apache/pinot/query/runtime/queries/QueryRunnerTestBase.java
@@ -306,6 +306,9 @@ public abstract class QueryRunnerTestBase extends
QueryTestSet {
"Got unexpected value type: " + value.getClass() + " for BYTES
column, expected: String or byte[]");
return value;
case INT_ARRAY:
+ if (value instanceof List) {
+ return ((List) value).stream().mapToInt(i -> (int) i).toArray();
+ }
if (value instanceof JdbcArray) {
try {
Object[] array = (Object[]) ((JdbcArray) value).getArray();
diff --git a/pinot-query-runtime/src/test/resources/queries/SetOpsNonH2.json
b/pinot-query-runtime/src/test/resources/queries/SetOpsNonH2.json
index 2a0e8de5697..df5eeeb8aff 100644
--- a/pinot-query-runtime/src/test/resources/queries/SetOpsNonH2.json
+++ b/pinot-query-runtime/src/test/resources/queries/SetOpsNonH2.json
@@ -166,6 +166,15 @@
[4],
[2]
]
+ },
+ {
+ "description": "UNION with three children",
+ "sql": "WITH data AS (SELECT a FROM (VALUES(array [1, 2]), (array [3,
4]), (array [5, 6])) \"data\" (\"a\")) SELECT * FROM data",
+ "outputs": [
+ [[1, 2]],
+ [[3, 4]],
+ [[5, 6]]
+ ]
}
]
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]