From 279b3818f0e02f6a2ff389532f1e6118598a7c24 Mon Sep 17 00:00:00 2001 From: Soumyava <93540295+somu-imply@users.noreply.github.com> Date: Thu, 14 Sep 2023 21:24:14 -0700 Subject: [PATCH] Make Unnest work with nullif operator (#14993) This is due to the recursive filter creation in unnest storage adapter not performing correctly in case of an empty children. This PR addresses the issue --- .../druid/segment/UnnestStorageAdapter.java | 7 ++- .../sql/calcite/CalciteArraysQueryTest.java | 48 +++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/processing/src/main/java/org/apache/druid/segment/UnnestStorageAdapter.java b/processing/src/main/java/org/apache/druid/segment/UnnestStorageAdapter.java index 83694d9618d..02f8c0064aa 100644 --- a/processing/src/main/java/org/apache/druid/segment/UnnestStorageAdapter.java +++ b/processing/src/main/java/org/apache/druid/segment/UnnestStorageAdapter.java @@ -478,13 +478,16 @@ public class UnnestStorageAdapter implements StorageAdapter for (Filter filter : queryFilter.getFilters()) { if (filter.getRequiredColumns().contains(outputColumnName)) { if (filter instanceof AndFilter) { - preFilterList.add(new AndFilter(recursiveRewriteOnUnnestFilters( + List andChildFilters = recursiveRewriteOnUnnestFilters( (BooleanFilter) filter, inputColumn, inputColumnCapabilites, filterSplitter, isTopLevelAndFilter - ))); + ); + if (!andChildFilters.isEmpty()) { + preFilterList.add(new AndFilter(andChildFilters)); + } } else if (filter instanceof OrFilter) { // in case of Or Fiters, we set isTopLevelAndFilter to false that prevents pushing down any child filters to base List orChildFilters = recursiveRewriteOnUnnestFilters( diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteArraysQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteArraysQueryTest.java index 4842044892d..df4e9b62cc9 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteArraysQueryTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteArraysQueryTest.java @@ -4807,4 +4807,52 @@ public class CalciteArraysQueryTest extends BaseCalciteQueryTest ) ); } + + @Test + public void testUnnestVirtualWithColumnsAndNullIf() + { + skipVectorize(); + cannotVectorize(); + testQuery( + "select c,m2 from druid.foo, unnest(ARRAY[\"m1\", \"m2\"]) as u(c) where NULLIF(c,m2) IS NULL", + QUERY_CONTEXT_UNNEST, + ImmutableList.of( + Druids.newScanQueryBuilder() + .dataSource(UnnestDataSource.create( + new TableDataSource(CalciteTests.DATASOURCE1), + expressionVirtualColumn("j0.unnest", "array(\"m1\",\"m2\")", ColumnType.FLOAT_ARRAY), + null + )) + .intervals(querySegmentSpec(Filtration.eternity())) + .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) + .filters( + useDefault ? expressionFilter("(\"j0.unnest\" == \"m2\")") : + or( + expressionFilter("(\"j0.unnest\" == \"m2\")"), + and( + isNull("j0.unnest"), + not(expressionFilter("(\"j0.unnest\" == \"m2\")")) + ) + )) + .legacy(false) + .context(QUERY_CONTEXT_UNNEST) + .columns(ImmutableList.of("j0.unnest", "m2")) + .build() + ), + ImmutableList.of( + new Object[]{1.0f, 1.0D}, + new Object[]{1.0f, 1.0D}, + new Object[]{2.0f, 2.0D}, + new Object[]{2.0f, 2.0D}, + new Object[]{3.0f, 3.0D}, + new Object[]{3.0f, 3.0D}, + new Object[]{4.0f, 4.0D}, + new Object[]{4.0f, 4.0D}, + new Object[]{5.0f, 5.0D}, + new Object[]{5.0f, 5.0D}, + new Object[]{6.0f, 6.0D}, + new Object[]{6.0f, 6.0D} + ) + ); + } }