mirror of https://github.com/apache/druid.git
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:
parent
90d8f67e3d
commit
48ac5ce50b
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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"))
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
)
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in New Issue