use native nvl expression for SQL NVL and 2 argument COALESCE (#13897)

* use custom case operator conversion instead of direct operator conversion, to produce native nvl expression for SQL NVL and 2 argument COALESCE, and add optimization for certain case filters from coalesce and nvl statements
This commit is contained in:
Clint Wylie 2023-03-09 05:46:17 -08:00 committed by GitHub
parent 90d8f67e3d
commit 48ac5ce50b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 418 additions and 58 deletions

View File

@ -376,6 +376,9 @@ public class Parser
.filter(x -> unappliedBindings.contains(x.getBinding()))
.collect(Collectors.toList());
if (args.isEmpty()) {
return expr;
}
final List<IdentifierExpr> lambdaArgs = new ArrayList<>();
// construct lambda args from list of args to apply. Identifiers in a lambda body have artificial 'binding' values

View File

@ -133,6 +133,7 @@ public class ExpressionPlan
*/
public Expr getExpression()
{
Parser.validateExpr(expression, analysis);
return expression;
}
@ -145,9 +146,11 @@ public class ExpressionPlan
public Expr getAppliedExpression()
{
if (is(Trait.NEEDS_APPLIED)) {
return Parser.applyUnappliedBindings(expression, analysis, unappliedInputs);
final Expr applied = Parser.applyUnappliedBindings(expression, analysis, unappliedInputs);
Parser.validateExpr(applied, applied.analyzeInputs());
return applied;
}
return expression;
return getExpression();
}
/**
@ -165,9 +168,11 @@ public class ExpressionPlan
"Accumulator cannot be implicitly transformed, if it is an ARRAY or multi-valued type it must"
+ " be used explicitly as such"
);
return Parser.foldUnappliedBindings(expression, analysis, unappliedInputs, accumulatorId);
final Expr folded = Parser.foldUnappliedBindings(expression, analysis, unappliedInputs, accumulatorId);
Parser.validateExpr(folded, folded.analyzeInputs());
return folded;
}
return expression;
return getExpression();
}
/**

View File

@ -24,7 +24,6 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import org.apache.druid.math.expr.Expr;
import org.apache.druid.math.expr.ExpressionType;
import org.apache.druid.math.expr.Parser;
import org.apache.druid.segment.ColumnInspector;
import org.apache.druid.segment.column.ColumnCapabilities;
import org.apache.druid.segment.column.ColumnType;
@ -55,7 +54,6 @@ public class ExpressionPlanner
public static ExpressionPlan plan(ColumnInspector inspector, Expr expression)
{
final Expr.BindingAnalysis analysis = expression.analyzeInputs();
Parser.validateExpr(expression, analysis);
EnumSet<ExpressionPlan.Trait> traits = EnumSet.noneOf(ExpressionPlan.Trait.class);
Set<String> noCapabilities = new HashSet<>();
@ -87,7 +85,7 @@ public class ExpressionPlanner
boolean isSingleInputMappable = false;
boolean isSingleInputScalar = capabilities.hasMultipleValues().isFalse();
if (capabilities.is(ValueType.STRING)) {
isSingleInputScalar &= capabilities.isDictionaryEncoded().isTrue();
isSingleInputScalar = isSingleInputScalar && capabilities.isDictionaryEncoded().isTrue();
isSingleInputMappable = capabilities.isDictionaryEncoded().isTrue() &&
!capabilities.hasMultipleValues().isUnknown();
}

View File

@ -680,6 +680,13 @@ public class ParserTest extends InitializedNullHandlingTest
"(map ([x] -> (case_searched [(== x b), b, (== x g), g, Other])), [x])",
ImmutableList.of("x")
);
validateApplyUnapplied(
"array_overlap(nvl(x, 'other'), ['a', 'b', 'other'])",
"(array_overlap [(nvl [x, other]), [a, b, other]])",
"(array_overlap [(map ([x] -> (nvl [x, other])), [x]), [a, b, other]])",
ImmutableList.of("x")
);
}
@Test

View File

@ -1019,7 +1019,7 @@ public class MultiValuedDimensionTest extends InitializedNullHandlingTest
{
expectedException.expect(RuntimeException.class);
expectedException.expectMessage(
"Invalid expression: (concat [(map ([x] -> (concat [x, othertags])), [tags]), tags]); [tags] used as both scalar and array variables"
"Invalid expression: (concat [(cartesian_map ([x, othertags] -> (concat [x, othertags])), [tags, othertags]), tags]); [tags] used as both scalar and array variables"
);
GroupByQuery query = GroupByQuery
.builder()

View File

@ -513,6 +513,12 @@ public class OperatorConversions
return this;
}
public OperatorBuilder sqlKind(SqlKind kind)
{
this.kind = kind;
return this;
}
/**
* Creates a {@link SqlFunction} from this builder.
*/

View File

@ -0,0 +1,169 @@
/*
* 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.druid.sql.calcite.expression.builtin;
import com.google.common.collect.ImmutableList;
import org.apache.calcite.rex.RexCall;
import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.query.filter.AndDimFilter;
import org.apache.druid.query.filter.DimFilter;
import org.apache.druid.query.filter.ExpressionDimFilter;
import org.apache.druid.query.filter.OrDimFilter;
import org.apache.druid.query.filter.SelectorDimFilter;
import org.apache.druid.segment.column.RowSignature;
import org.apache.druid.sql.calcite.expression.DruidExpression;
import org.apache.druid.sql.calcite.expression.Expressions;
import org.apache.druid.sql.calcite.expression.OperatorConversions;
import org.apache.druid.sql.calcite.expression.SqlOperatorConversion;
import org.apache.druid.sql.calcite.planner.Calcites;
import org.apache.druid.sql.calcite.planner.PlannerContext;
import org.apache.druid.sql.calcite.rel.VirtualColumnRegistry;
import javax.annotation.Nullable;
import java.util.List;
public class CaseOperatorConversion implements SqlOperatorConversion
{
@Override
public SqlOperator calciteOperator()
{
return SqlStdOperatorTable.CASE;
}
@Nullable
@Override
public DruidExpression toDruidExpression(PlannerContext plannerContext, RowSignature rowSignature, RexNode rexNode)
{
final List<RexNode> operands = ((RexCall) rexNode).getOperands();
final List<DruidExpression> druidExpressions = Expressions.toDruidExpressions(
plannerContext,
rowSignature,
operands
);
// coalesce and nvl are rewritten during planning as case statements
// rewrite simple case_searched(notnull(expr1), expr1, expr2) to nvl(expr1, expr2) since the latter is vectorized
// at the native layer
// this conversion won't help if the condition expression is only part of then expression, like if the input
// expression to coalesce was an expression itself, but this is better than nothing
if (druidExpressions.size() == 3) {
final DruidExpression condition = druidExpressions.get(0);
final DruidExpression thenExpression = druidExpressions.get(1);
final DruidExpression elseExpression = druidExpressions.get(2);
final String thenNotNull = StringUtils.format("notnull(%s)", thenExpression.getExpression());
if (condition.getExpression().equals(thenNotNull)) {
return DruidExpression.ofFunctionCall(
Calcites.getColumnTypeForRelDataType(
rexNode.getType()),
"nvl",
ImmutableList.of(thenExpression, elseExpression)
);
}
}
return OperatorConversions.convertDirectCall(
plannerContext,
rowSignature,
rexNode,
"case_searched"
);
}
@Nullable
@Override
public DimFilter toDruidFilter(
PlannerContext plannerContext,
RowSignature rowSignature,
@Nullable VirtualColumnRegistry virtualColumnRegistry,
RexNode rexNode
)
{
final RexCall call = (RexCall) rexNode;
final List<RexNode> operands = call.getOperands();
final List<DruidExpression> druidExpressions = Expressions.toDruidExpressions(
plannerContext,
rowSignature,
operands
);
// rewrite case_searched(notnull(someColumn), then, else) into better native filters
// or(then, and(else, isNull(someColumn))
if (druidExpressions.size() == 3) {
final DruidExpression condition = druidExpressions.get(0);
final DruidExpression thenExpression = druidExpressions.get(1);
final DruidExpression elseExpression = druidExpressions.get(2);
if (condition.getExpression().startsWith("notnull") && condition.getArguments().get(0).isDirectColumnAccess()) {
DimFilter thenFilter = null, elseFilter = null;
final DimFilter isNull = new SelectorDimFilter(
condition.getArguments().get(0).getDirectColumn(),
null,
null
);
if (call.getOperands().get(1) instanceof RexCall) {
final RexCall thenCall = (RexCall) call.getOperands().get(1);
final SqlOperatorConversion thenConversion = plannerContext.getPlannerToolbox()
.operatorTable()
.lookupOperatorConversion(thenCall.getOperator());
if (thenConversion != null) {
thenFilter = thenConversion.toDruidFilter(plannerContext, rowSignature, virtualColumnRegistry, thenCall);
}
}
if (call.getOperands().get(2) instanceof RexLiteral) {
if (call.getOperands().get(2).isAlwaysTrue()) {
return new OrDimFilter(thenFilter, isNull);
} else {
// else is always false, we can leave it out
return thenFilter;
}
} else if (call.getOperands().get(2) instanceof RexCall) {
RexCall elseCall = (RexCall) call.getOperands().get(2);
SqlOperatorConversion elseConversion = plannerContext.getPlannerToolbox()
.operatorTable()
.lookupOperatorConversion(elseCall.getOperator());
if (elseConversion != null) {
elseFilter = elseConversion.toDruidFilter(plannerContext, rowSignature, virtualColumnRegistry, elseCall);
}
}
// if either then or else filters produced a native filter (that wasn't just another expression filter)
// make sure we have filters for both sides by filling in the gaps with expression filter
if (thenFilter != null && !(thenFilter instanceof ExpressionDimFilter) && elseFilter == null) {
elseFilter = new ExpressionDimFilter(elseExpression.getExpression(), plannerContext.getExprMacroTable());
} else if (thenFilter == null && elseFilter != null && !(elseFilter instanceof ExpressionDimFilter)) {
thenFilter = new ExpressionDimFilter(thenExpression.getExpression(), plannerContext.getExprMacroTable());
}
if (thenFilter != null && elseFilter != null) {
return new OrDimFilter(thenFilter, new AndDimFilter(elseFilter, isNull));
}
}
}
// no special cases (..ha ha!) so fall through to defaul thandling
return SqlOperatorConversion.super.toDruidFilter(plannerContext, rowSignature, virtualColumnRegistry, rexNode);
}
}

View File

@ -70,6 +70,7 @@ import org.apache.druid.sql.calcite.expression.builtin.ArrayQuantileOperatorConv
import org.apache.druid.sql.calcite.expression.builtin.ArraySliceOperatorConversion;
import org.apache.druid.sql.calcite.expression.builtin.ArrayToStringOperatorConversion;
import org.apache.druid.sql.calcite.expression.builtin.BTrimOperatorConversion;
import org.apache.druid.sql.calcite.expression.builtin.CaseOperatorConversion;
import org.apache.druid.sql.calcite.expression.builtin.CastOperatorConversion;
import org.apache.druid.sql.calcite.expression.builtin.CeilOperatorConversion;
import org.apache.druid.sql.calcite.expression.builtin.ComplexDecodeBase64OperatorConversion;
@ -330,7 +331,7 @@ public class DruidOperatorTable implements SqlOperatorTable
private static final List<SqlOperatorConversion> STANDARD_OPERATOR_CONVERSIONS =
ImmutableList.<SqlOperatorConversion>builder()
.add(new DirectOperatorConversion(SqlStdOperatorTable.ABS, "abs"))
.add(new DirectOperatorConversion(SqlStdOperatorTable.CASE, "case_searched"))
.add(new CaseOperatorConversion())
.add(new DirectOperatorConversion(SqlStdOperatorTable.CHAR_LENGTH, "strlen"))
.add(CHARACTER_LENGTH_CONVERSION)
.add(new AliasedOperatorConversion(CHARACTER_LENGTH_CONVERSION, "LENGTH"))

View File

@ -734,8 +734,7 @@ public class DruidQuery
final boolean forceExpressionVirtualColumns =
plannerContext.getPlannerConfig().isForceExpressionVirtualColumns();
virtualColumnRegistry.visitAllSubExpressions((expression) -> {
if (!forceExpressionVirtualColumns
&& expression.getType() == DruidExpression.NodeType.SPECIALIZED) {
if (!forceExpressionVirtualColumns && expression.getType() == DruidExpression.NodeType.SPECIALIZED) {
// add the expression to the top level of the registry as a standalone virtual column
final String name = virtualColumnRegistry.getOrCreateVirtualColumnForExpression(
expression,

View File

@ -1216,7 +1216,7 @@ public class CalciteMultiValueStringQueryTest extends BaseCalciteQueryTest
.setVirtualColumns(
expressionVirtualColumn(
"v0",
"case_searched(notnull(\"v1\"),\"v1\",'no b')",
"nvl(\"v1\",'no b')",
ColumnType.STRING
),
new ListFilteredVirtualColumn(
@ -1283,7 +1283,7 @@ public class CalciteMultiValueStringQueryTest extends BaseCalciteQueryTest
.setVirtualColumns(
expressionVirtualColumn(
"v0",
"case_searched(notnull(\"v1\"),\"v1\",\"dim1\")",
"nvl(\"v1\",\"dim1\")",
ColumnType.STRING
),
new ListFilteredVirtualColumn(
@ -1341,7 +1341,7 @@ public class CalciteMultiValueStringQueryTest extends BaseCalciteQueryTest
.setVirtualColumns(
expressionVirtualColumn(
"v0",
"case_searched(notnull(\"v1\"),\"v1\",'no b')",
"nvl(\"v1\",'no b')",
ColumnType.STRING
),
new ListFilteredVirtualColumn(
@ -1854,6 +1854,7 @@ public class CalciteMultiValueStringQueryTest extends BaseCalciteQueryTest
@Test
public void testMultiValueStringOverlapFilterCoalesceNvl()
{
cannotVectorize();
testQuery(
"SELECT COALESCE(dim3, 'other') FROM druid.numfoo "
+ "WHERE MV_OVERLAP(COALESCE(MV_TO_ARRAY(dim3), ARRAY['other']), ARRAY['a', 'b', 'other']) OR "
@ -1865,7 +1866,7 @@ public class CalciteMultiValueStringQueryTest extends BaseCalciteQueryTest
.virtualColumns(
new ExpressionVirtualColumn(
"v0",
"case_searched(notnull(\"dim3\"),\"dim3\",'other')",
"nvl(\"dim3\",'other')",
ColumnType.STRING,
queryFramework().macroTable()
)
@ -1907,6 +1908,99 @@ public class CalciteMultiValueStringQueryTest extends BaseCalciteQueryTest
);
}
@Test
public void testMultiValueStringOverlapFilterCoalesceSingleValue()
{
testQuery(
"SELECT COALESCE(dim3, 'other') FROM druid.numfoo "
+ "WHERE MV_OVERLAP(COALESCE(dim3, 'other'), ARRAY['a', 'b', 'other']) LIMIT 5",
ImmutableList.of(
newScanQueryBuilder()
.dataSource(CalciteTests.DATASOURCE3)
.eternityInterval()
.virtualColumns(
new ExpressionVirtualColumn(
"v0",
"nvl(\"dim3\",'other')",
ColumnType.STRING,
queryFramework().macroTable()
)
)
.filters(
new OrDimFilter(
new InDimFilter("dim3", ImmutableSet.of("a", "b", "other")),
new SelectorDimFilter("dim3", null, null)
)
)
.columns("v0")
.resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST)
.limit(5)
.context(QUERY_CONTEXT_DEFAULT)
.build()
),
NullHandling.replaceWithDefault()
? ImmutableList.of(
new Object[]{"[\"a\",\"b\"]"},
new Object[]{"[\"b\",\"c\"]"},
new Object[]{"other"},
new Object[]{"other"},
new Object[]{"other"}
)
: ImmutableList.of(
new Object[]{"[\"a\",\"b\"]"},
new Object[]{"[\"b\",\"c\"]"},
new Object[]{"other"},
new Object[]{"other"}
)
);
}
@Test
public void testMultiValueStringOverlapFilterCoalesceSingleValueOtherColumn()
{
testQuery(
"SELECT COALESCE(dim3, dim2) FROM druid.numfoo "
+ "WHERE MV_OVERLAP(COALESCE(dim3, dim2), ARRAY['a', 'b', 'other']) LIMIT 5",
ImmutableList.of(
newScanQueryBuilder()
.dataSource(CalciteTests.DATASOURCE3)
.eternityInterval()
.virtualColumns(
new ExpressionVirtualColumn(
"v0",
"nvl(\"dim3\",\"dim2\")",
ColumnType.STRING,
queryFramework().macroTable()
)
)
.filters(
new OrDimFilter(
new InDimFilter("dim3", ImmutableSet.of("a", "b", "other")),
new AndDimFilter(
new InDimFilter("dim2", ImmutableSet.of("a", "b", "other")),
new SelectorDimFilter("dim3", null, null)
)
)
)
.columns("v0")
.resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST)
.limit(5)
.context(QUERY_CONTEXT_DEFAULT)
.build()
),
NullHandling.replaceWithDefault()
? ImmutableList.of(
new Object[]{"[\"a\",\"b\"]"},
new Object[]{"[\"b\",\"c\"]"},
new Object[]{"a"}
)
: ImmutableList.of(
new Object[]{"[\"a\",\"b\"]"},
new Object[]{"[\"b\",\"c\"]"}
)
);
}
@Test
public void testMultiValueStringOverlapFilterInconsistentUsage()
{
@ -1920,17 +2014,4 @@ public class CalciteMultiValueStringQueryTest extends BaseCalciteQueryTest
);
}
@Test
public void testMultiValueStringOverlapFilterInconsistentUsage2()
{
testQueryThrows(
"SELECT COALESCE(dim3, 'other') FROM druid.numfoo "
+ "WHERE MV_OVERLAP(COALESCE(dim3, 'other'), ARRAY['a', 'b', 'other']) LIMIT 5",
e -> {
e.expect(RuntimeException.class);
e.expectMessage("Invalid expression: (case_searched [(notnull [dim3]), (array_overlap [dim3, [a, b, other]]), 1]); [dim3] used as both scalar and array variables");
}
);
}
}

View File

@ -683,7 +683,7 @@ public class CalciteParameterQueryTest extends BaseCalciteQueryTest
.setVirtualColumns(
expressionVirtualColumn(
"v0",
"case_searched(notnull(\"dim2\"),\"dim2\",'parameter')",
"nvl(\"dim2\",'parameter')",
ColumnType.STRING
)
)
@ -718,7 +718,7 @@ public class CalciteParameterQueryTest extends BaseCalciteQueryTest
.setVirtualColumns(
expressionVirtualColumn(
"v0",
"case_searched(notnull(\"dim2\"),\"dim2\",'parameter')",
"nvl(\"dim2\",'parameter')",
ColumnType.STRING
)
)

View File

@ -82,10 +82,12 @@ import org.apache.druid.query.dimension.ExtractionDimensionSpec;
import org.apache.druid.query.expression.TestExprMacroTable;
import org.apache.druid.query.extraction.RegexDimExtractionFn;
import org.apache.druid.query.extraction.SubstringDimExtractionFn;
import org.apache.druid.query.filter.AndDimFilter;
import org.apache.druid.query.filter.BoundDimFilter;
import org.apache.druid.query.filter.DimFilter;
import org.apache.druid.query.filter.InDimFilter;
import org.apache.druid.query.filter.LikeDimFilter;
import org.apache.druid.query.filter.OrDimFilter;
import org.apache.druid.query.filter.RegexDimFilter;
import org.apache.druid.query.filter.SelectorDimFilter;
import org.apache.druid.query.groupby.GroupByQuery;
@ -3877,7 +3879,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
.setVirtualColumns(
expressionVirtualColumn(
"v0",
"case_searched(notnull(\"dim2\"),\"dim2\",\"dim1\")",
"nvl(\"dim2\",\"dim1\")",
ColumnType.STRING
)
)
@ -3902,6 +3904,109 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
);
}
@Test
public void testCoalesceColumnsFilter()
{
msqCompatible();
// Cannot vectorize due to virtual columns.
cannotVectorize();
testQuery(
"SELECT COALESCE(dim2, dim1), COUNT(*) FROM druid.foo WHERE COALESCE(dim2, dim1) IN ('a', 'abc') GROUP BY COALESCE(dim2, dim1)\n",
ImmutableList.of(
GroupByQuery.builder()
.setDataSource(CalciteTests.DATASOURCE1)
.setInterval(querySegmentSpec(Filtration.eternity()))
.setGranularity(Granularities.ALL)
.setVirtualColumns(
expressionVirtualColumn(
"v0",
"nvl(\"dim2\",\"dim1\")",
ColumnType.STRING
)
)
.setDimFilter(
new OrDimFilter(
new AndDimFilter(
selector("dim1", "a", null),
selector("dim2", null, null)
),
new AndDimFilter(
selector("dim1", "abc", null),
selector("dim2", null, null)
),
new InDimFilter(
"dim2",
ImmutableSet.of("a", "abc")
)
)
)
.setDimensions(dimensions(new DefaultDimensionSpec("v0", "d0", ColumnType.STRING)))
.setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0")))
.setContext(QUERY_CONTEXT_DEFAULT)
.build()
),
ImmutableList.of(
new Object[]{"a", 2L},
new Object[]{"abc", 2L}
)
);
}
@Test
public void testCoalesceMoreColumns()
{
msqCompatible();
// Cannot vectorize due to virtual columns.
cannotVectorize();
testQuery(
"SELECT COALESCE(dim2, dim1), COALESCE(dim2, dim3, dim1), COUNT(*) FROM druid.foo GROUP BY COALESCE(dim2, dim1), COALESCE(dim2, dim3, dim1)\n",
ImmutableList.of(
GroupByQuery.builder()
.setDataSource(CalciteTests.DATASOURCE1)
.setInterval(querySegmentSpec(Filtration.eternity()))
.setGranularity(Granularities.ALL)
.setVirtualColumns(
expressionVirtualColumn(
"v0",
"nvl(\"dim2\",\"dim1\")",
ColumnType.STRING
),
expressionVirtualColumn(
"v1",
"case_searched(notnull(\"dim2\"),\"dim2\",notnull(\"dim3\"),\"dim3\",\"dim1\")",
ColumnType.STRING
)
)
.setDimensions(
dimensions(
new DefaultDimensionSpec("v0", "d0", ColumnType.STRING),
new DefaultDimensionSpec("v1", "d1", ColumnType.STRING)
)
)
.setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0")))
.setContext(QUERY_CONTEXT_DEFAULT)
.build()
),
NullHandling.replaceWithDefault() ?
ImmutableList.of(
new Object[]{"10.1", "b", 1L},
new Object[]{"10.1", "c", 1L},
new Object[]{"2", "d", 1L},
new Object[]{"a", "a", 3L},
new Object[]{"abc", "abc", 2L}
) :
ImmutableList.of(
new Object[]{"", "", 1L},
new Object[]{"10.1", "b", 1L},
new Object[]{"10.1", "c", 1L},
new Object[]{"a", "a", 3L},
new Object[]{"abc", "abc", 2L}
)
);
}
@Test
public void testColumnIsNull()
{
@ -10527,7 +10632,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
.setVirtualColumns(
expressionVirtualColumn(
"v0",
"case_searched(notnull(\"dim2\"),\"dim2\",'')",
"nvl(\"dim2\",'')",
ColumnType.STRING
),
expressionVirtualColumn(
@ -10578,9 +10683,6 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
{
requireMergeBuffers(3);
// Cannot vectorize due to virtual columns.
cannotVectorize();
testQuery(
"SELECT dim2, gran, SUM(cnt), GROUPING(gran, dim2)\n"
+ "FROM (SELECT FLOOR(__time TO MONTH) AS gran, COALESCE(dim2, '') dim2, cnt FROM druid.foo) AS x\n"
@ -10593,7 +10695,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
.setVirtualColumns(
expressionVirtualColumn(
"v0",
"case_searched(notnull(\"dim2\"),\"dim2\",'')",
"nvl(\"dim2\",'')",
ColumnType.STRING
),
expressionVirtualColumn(
@ -10745,7 +10847,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
.setVirtualColumns(
expressionVirtualColumn(
"v0",
"case_searched(notnull(\"dim2\"),\"dim2\",'')",
"nvl(\"dim2\",'')",
ColumnType.STRING
),
expressionVirtualColumn(
@ -10788,9 +10890,6 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
@Test
public void testGroupByRollupDifferentOrder()
{
// Cannot vectorize due to virtual columns.
cannotVectorize();
// Like "testGroupByRollup", but the ROLLUP exprs are in the reverse order.
testQuery(
"SELECT dim2, gran, SUM(cnt)\n"
@ -10809,7 +10908,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
),
expressionVirtualColumn(
"v1",
"case_searched(notnull(\"dim2\"),\"dim2\",'')",
"nvl(\"dim2\",'')",
ColumnType.STRING
)
)
@ -10861,7 +10960,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
.setVirtualColumns(
expressionVirtualColumn(
"v0",
"case_searched(notnull(\"dim2\"),\"dim2\",'')",
"nvl(\"dim2\",'')",
ColumnType.STRING
),
expressionVirtualColumn(
@ -10922,7 +11021,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
.setVirtualColumns(
expressionVirtualColumn(
"v0",
"case_searched(notnull(\"dim2\"),\"dim2\",'')",
"nvl(\"dim2\",'')",
ColumnType.STRING
),
expressionVirtualColumn(
@ -10984,7 +11083,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
.setVirtualColumns(
expressionVirtualColumn(
"v0",
"case_searched(notnull(\"dim2\"),\"dim2\",'')",
"nvl(\"dim2\",'')",
ColumnType.STRING
),
expressionVirtualColumn(
@ -11024,9 +11123,6 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
@Test
public void testGroupingSetsWithOrderByDimension()
{
// Cannot vectorize due to virtual columns.
cannotVectorize();
testQuery(
"SELECT dim2, gran, SUM(cnt)\n"
+ "FROM (SELECT FLOOR(__time TO MONTH) AS gran, COALESCE(dim2, '') dim2, cnt FROM druid.foo) AS x\n"
@ -11040,7 +11136,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
.setVirtualColumns(
expressionVirtualColumn(
"v0",
"case_searched(notnull(\"dim2\"),\"dim2\",'')",
"nvl(\"dim2\",'')",
ColumnType.STRING
),
expressionVirtualColumn(
@ -11113,7 +11209,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
.setVirtualColumns(
expressionVirtualColumn(
"v0",
"case_searched(notnull(\"dim2\"),\"dim2\",'')",
"nvl(\"dim2\",'')",
ColumnType.STRING
),
expressionVirtualColumn(
@ -11182,7 +11278,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
.setVirtualColumns(
expressionVirtualColumn(
"v0",
"case_searched(notnull(\"dim2\"),\"dim2\",'')",
"nvl(\"dim2\",'')",
ColumnType.STRING
),
expressionVirtualColumn(
@ -12471,8 +12567,6 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
public void testNvlColumns()
{
msqCompatible();
// Cannot vectorize due to usage of expressions.
cannotVectorize();
testQuery(
"SELECT NVL(dim2, dim1), COUNT(*) FROM druid.foo GROUP BY NVL(dim2, dim1)\n",
@ -12484,7 +12578,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
.setVirtualColumns(
expressionVirtualColumn(
"v0",
"case_searched(notnull(\"dim2\"),\"dim2\",\"dim1\")",
"nvl(\"dim2\",\"dim1\")",
ColumnType.STRING
)
)
@ -12926,9 +13020,6 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
@Test
public void testGroupingSetsWithLimit()
{
// Cannot vectorize due to virtual columns.
cannotVectorize();
testQuery(
"SELECT dim2, gran, SUM(cnt)\n"
+ "FROM (SELECT FLOOR(__time TO MONTH) AS gran, COALESCE(dim2, '') dim2, cnt FROM druid.foo) AS x\n"
@ -12941,7 +13032,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
.setVirtualColumns(
expressionVirtualColumn(
"v0",
"case_searched(notnull(\"dim2\"),\"dim2\",'')",
"nvl(\"dim2\",'')",
ColumnType.STRING
),
expressionVirtualColumn(
@ -13008,7 +13099,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
.setVirtualColumns(
expressionVirtualColumn(
"v0",
"case_searched(notnull(\"dim2\"),\"dim2\",'')",
"nvl(\"dim2\",'')",
ColumnType.STRING
),
expressionVirtualColumn(