You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@druid.apache.org by ji...@apache.org on 2020/04/11 08:18:24 UTC
[druid] branch master updated: fix issue with group by limit
pushdown for extractionFn, expressions, joins, etc (#9662)
This is an automated email from the ASF dual-hosted git repository.
jihoonson pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git
The following commit(s) were added to refs/heads/master by this push:
new 0ff926b fix issue with group by limit pushdown for extractionFn, expressions, joins, etc (#9662)
0ff926b is described below
commit 0ff926b1a1367be88fb6f5f4baaa74ca849bd8bf
Author: Clint Wylie <cw...@apache.org>
AuthorDate: Sat Apr 11 01:18:11 2020 -0700
fix issue with group by limit pushdown for extractionFn, expressions, joins, etc (#9662)
* fix issue with group by limit pushdown for extractionFn, expressions, joins, etc
* remove unused
* fix test
* revert unintended change
* more tests
* consider capabilities for StringGroupByColumnSelectorStrategy
* fix test
* fix and more test
* revert because im scared
---
.../epinephelinae/GroupByQueryEngineV2.java | 81 +++++---
...uildingStringGroupByColumnSelectorStrategy.java | 2 +-
.../StringGroupByColumnSelectorStrategy.java | 14 +-
.../apache/druid/query/topn/BaseTopNAlgorithm.java | 6 +-
.../org/apache/druid/segment/Capabilities.java | 71 -------
.../org/apache/druid/segment/ColumnProcessors.java | 8 +-
.../druid/segment/DimensionHandlerUtils.java | 24 ++-
.../segment/QueryableIndexStorageAdapter.java | 6 -
.../segment/RowBasedColumnSelectorFactory.java | 3 +-
.../druid/segment/RowBasedStorageAdapter.java | 6 -
.../org/apache/druid/segment/StorageAdapter.java | 1 -
.../apache/druid/segment/column/ColumnBuilder.java | 2 +
.../druid/segment/column/ColumnCapabilities.java | 30 +++
.../segment/column/ColumnCapabilitiesImpl.java | 70 +++++--
.../segment/incremental/IncrementalIndex.java | 6 +
.../IncrementalIndexStorageAdapter.java | 8 -
.../join/HashJoinSegmentStorageAdapter.java | 11 --
.../table/IndexedTableColumnSelectorFactory.java | 3 +
.../epinephelinae/GroupByQueryEngineV2Test.java | 141 ++++++++++++++
.../topn/TopNMetricSpecOptimizationsTest.java | 7 -
.../QueryableIndexColumnCapabilitiesTest.java | 213 +++++++++++++++++++++
.../segment/RowBasedColumnSelectorFactoryTest.java | 144 ++++++++++++++
.../segment/column/ColumnCapabilitiesTest.java | 49 +++++
.../join/HashJoinSegmentStorageAdapterTest.java | 10 +-
.../virtual/ExpressionVirtualColumnTest.java | 24 +++
.../apache/druid/sql/calcite/CalciteQueryTest.java | 41 ++++
.../druid/sql/calcite/util/CalciteTests.java | 14 +-
27 files changed, 820 insertions(+), 175 deletions(-)
diff --git a/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/GroupByQueryEngineV2.java b/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/GroupByQueryEngineV2.java
index 8e3fe05..823f5f2 100644
--- a/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/GroupByQueryEngineV2.java
+++ b/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/GroupByQueryEngineV2.java
@@ -52,6 +52,7 @@ import org.apache.druid.query.groupby.epinephelinae.column.NullableNumericGroupB
import org.apache.druid.query.groupby.epinephelinae.column.StringGroupByColumnSelectorStrategy;
import org.apache.druid.query.groupby.epinephelinae.vector.VectorGroupByEngine;
import org.apache.druid.query.groupby.orderby.DefaultLimitSpec;
+import org.apache.druid.query.groupby.orderby.OrderByColumnSpec;
import org.apache.druid.query.groupby.strategy.GroupByStrategyV2;
import org.apache.druid.query.ordering.StringComparator;
import org.apache.druid.segment.ColumnSelectorFactory;
@@ -74,6 +75,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.function.Function;
+import java.util.stream.Stream;
/**
* Class that knows how to process a groupBy query on a single {@link StorageAdapter}. It returns a {@link Sequence}
@@ -335,6 +337,42 @@ public class GroupByQueryEngineV2
});
}
+ public static void convertRowTypesToOutputTypes(
+ final List<DimensionSpec> dimensionSpecs,
+ final ResultRow resultRow,
+ final int resultRowDimensionStart
+ )
+ {
+ for (int i = 0; i < dimensionSpecs.size(); i++) {
+ DimensionSpec dimSpec = dimensionSpecs.get(i);
+ final int resultRowIndex = resultRowDimensionStart + i;
+ final ValueType outputType = dimSpec.getOutputType();
+
+ resultRow.set(
+ resultRowIndex,
+ DimensionHandlerUtils.convertObjectToType(resultRow.get(resultRowIndex), outputType)
+ );
+ }
+ }
+
+ /**
+ * check if a column will operate correctly with {@link LimitedBufferHashGrouper} for query limit pushdown
+ */
+ public static boolean canPushDownLimit(ColumnSelectorFactory columnSelectorFactory, String columnName)
+ {
+ ColumnCapabilities capabilities = columnSelectorFactory.getColumnCapabilities(columnName);
+ if (capabilities != null) {
+ // strings can be pushed down if dictionaries are sorted and unique per id
+ if (capabilities.getType() == ValueType.STRING) {
+ return capabilities.areDictionaryValuesSorted().and(capabilities.areDictionaryValuesUnique()).isTrue();
+ }
+ // party on
+ return true;
+ }
+ // we don't know what we don't know, don't assume otherwise
+ return false;
+ }
+
private static class GroupByStrategyFactory
implements ColumnSelectorStrategyFactory<GroupByColumnSelectorStrategy>
{
@@ -349,7 +387,7 @@ public class GroupByQueryEngineV2
case STRING:
DimensionSelector dimSelector = (DimensionSelector) selector;
if (dimSelector.getValueCardinality() >= 0) {
- return new StringGroupByColumnSelectorStrategy(dimSelector::lookupName);
+ return new StringGroupByColumnSelectorStrategy(dimSelector::lookupName, capabilities);
} else {
return new DictionaryBuildingStringGroupByColumnSelectorStrategy();
}
@@ -555,16 +593,31 @@ public class GroupByQueryEngineV2
@Override
protected Grouper<ByteBuffer> newGrouper()
{
- Grouper grouper = null;
+ Grouper<ByteBuffer> grouper = null;
+ final ColumnSelectorFactory selectorFactory = cursor.getColumnSelectorFactory();
final DefaultLimitSpec limitSpec = query.isApplyLimitPushDown() &&
querySpecificConfig.isApplyLimitPushDownToSegment() ?
(DefaultLimitSpec) query.getLimitSpec() : null;
+
+ final boolean canDoLimitPushdown;
if (limitSpec != null) {
- LimitedBufferHashGrouper limitGrouper = new LimitedBufferHashGrouper<>(
+ // there is perhaps a more graceful way this could be handled a bit more selectively, but for now just avoid
+ // pushdown if it will prove problematic by checking grouping and ordering columns
+
+ canDoLimitPushdown = Stream.concat(
+ query.getDimensions().stream().map(DimensionSpec::getDimension),
+ limitSpec.getColumns().stream().map(OrderByColumnSpec::getDimension)
+ ).allMatch(col -> GroupByQueryEngineV2.canPushDownLimit(selectorFactory, col));
+ } else {
+ canDoLimitPushdown = false;
+ }
+
+ if (canDoLimitPushdown) {
+ LimitedBufferHashGrouper<ByteBuffer> limitGrouper = new LimitedBufferHashGrouper<>(
Suppliers.ofInstance(buffer),
keySerde,
AggregatorAdapters.factorizeBuffered(
- cursor.getColumnSelectorFactory(),
+ selectorFactory,
query.getAggregatorSpecs()
),
querySpecificConfig.getBufferGrouperMaxSize(),
@@ -592,7 +645,7 @@ public class GroupByQueryEngineV2
Suppliers.ofInstance(buffer),
keySerde,
AggregatorAdapters.factorizeBuffered(
- cursor.getColumnSelectorFactory(),
+ selectorFactory,
query.getAggregatorSpecs()
),
querySpecificConfig.getBufferGrouperMaxSize(),
@@ -834,24 +887,6 @@ public class GroupByQueryEngineV2
}
}
- public static void convertRowTypesToOutputTypes(
- final List<DimensionSpec> dimensionSpecs,
- final ResultRow resultRow,
- final int resultRowDimensionStart
- )
- {
- for (int i = 0; i < dimensionSpecs.size(); i++) {
- DimensionSpec dimSpec = dimensionSpecs.get(i);
- final int resultRowIndex = resultRowDimensionStart + i;
- final ValueType outputType = dimSpec.getOutputType();
-
- resultRow.set(
- resultRowIndex,
- DimensionHandlerUtils.convertObjectToType(resultRow.get(resultRowIndex), outputType)
- );
- }
- }
-
private static class GroupByEngineKeySerde implements Grouper.KeySerde<ByteBuffer>
{
private final int keySize;
diff --git a/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/column/DictionaryBuildingStringGroupByColumnSelectorStrategy.java b/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/column/DictionaryBuildingStringGroupByColumnSelectorStrategy.java
index b6121af..025819c 100644
--- a/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/column/DictionaryBuildingStringGroupByColumnSelectorStrategy.java
+++ b/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/column/DictionaryBuildingStringGroupByColumnSelectorStrategy.java
@@ -54,7 +54,7 @@ public class DictionaryBuildingStringGroupByColumnSelectorStrategy extends Strin
public DictionaryBuildingStringGroupByColumnSelectorStrategy()
{
- super(null);
+ super(null, null);
}
@Override
diff --git a/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/column/StringGroupByColumnSelectorStrategy.java b/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/column/StringGroupByColumnSelectorStrategy.java
index 2959323..78bfdc6 100644
--- a/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/column/StringGroupByColumnSelectorStrategy.java
+++ b/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/column/StringGroupByColumnSelectorStrategy.java
@@ -27,6 +27,7 @@ import org.apache.druid.query.ordering.StringComparator;
import org.apache.druid.query.ordering.StringComparators;
import org.apache.druid.segment.ColumnValueSelector;
import org.apache.druid.segment.DimensionSelector;
+import org.apache.druid.segment.column.ColumnCapabilities;
import org.apache.druid.segment.data.IndexedInts;
import javax.annotation.Nullable;
@@ -36,11 +37,15 @@ import java.util.function.IntFunction;
public class StringGroupByColumnSelectorStrategy implements GroupByColumnSelectorStrategy
{
@Nullable
+ private final ColumnCapabilities capabilities;
+
+ @Nullable
private final IntFunction<String> dictionaryLookup;
- public StringGroupByColumnSelectorStrategy(IntFunction<String> dictionaryLookup)
+ public StringGroupByColumnSelectorStrategy(IntFunction<String> dictionaryLookup, ColumnCapabilities capabilities)
{
this.dictionaryLookup = dictionaryLookup;
+ this.capabilities = capabilities;
}
@Override
@@ -148,7 +153,12 @@ public class StringGroupByColumnSelectorStrategy implements GroupByColumnSelecto
@Override
public Grouper.BufferComparator bufferComparator(int keyBufferPosition, @Nullable StringComparator stringComparator)
{
- if (stringComparator == null || StringComparators.LEXICOGRAPHIC.equals(stringComparator)) {
+ final boolean canCompareInts =
+ capabilities != null &&
+ capabilities.hasBitmapIndexes() &&
+ capabilities.areDictionaryValuesSorted().and(capabilities.areDictionaryValuesUnique()).isTrue();
+
+ if (canCompareInts && (stringComparator == null || StringComparators.LEXICOGRAPHIC.equals(stringComparator))) {
return (lhsBuffer, rhsBuffer, lhsPosition, rhsPosition) -> Integer.compare(
lhsBuffer.getInt(lhsPosition + keyBufferPosition),
rhsBuffer.getInt(rhsPosition + keyBufferPosition)
diff --git a/processing/src/main/java/org/apache/druid/query/topn/BaseTopNAlgorithm.java b/processing/src/main/java/org/apache/druid/query/topn/BaseTopNAlgorithm.java
index 3a1fdbd..b8b04ad 100644
--- a/processing/src/main/java/org/apache/druid/query/topn/BaseTopNAlgorithm.java
+++ b/processing/src/main/java/org/apache/druid/query/topn/BaseTopNAlgorithm.java
@@ -25,11 +25,11 @@ import org.apache.druid.java.util.common.Pair;
import org.apache.druid.query.aggregation.Aggregator;
import org.apache.druid.query.aggregation.AggregatorFactory;
import org.apache.druid.query.aggregation.BufferAggregator;
-import org.apache.druid.segment.Capabilities;
import org.apache.druid.segment.Cursor;
import org.apache.druid.segment.DimensionSelector;
import org.apache.druid.segment.IdLookup;
import org.apache.druid.segment.StorageAdapter;
+import org.apache.druid.segment.column.ColumnCapabilities;
import javax.annotation.Nullable;
import java.util.Arrays;
@@ -268,8 +268,8 @@ public abstract class BaseTopNAlgorithm<DimValSelector, DimValAggregateStore, Pa
@Override
public void skipTo(String previousStop)
{
- Capabilities capabilities = storageAdapter.getCapabilities();
- if (capabilities.dimensionValuesSorted()) {
+ ColumnCapabilities capabilities = storageAdapter.getColumnCapabilities(query.getDimensionSpec().getDimension());
+ if (capabilities != null && capabilities.areDictionaryValuesSorted().isTrue()) {
this.previousStop = previousStop;
}
}
diff --git a/processing/src/main/java/org/apache/druid/segment/Capabilities.java b/processing/src/main/java/org/apache/druid/segment/Capabilities.java
deleted file mode 100644
index 75976c8..0000000
--- a/processing/src/main/java/org/apache/druid/segment/Capabilities.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.segment;
-
-/**
- */
-
-public class Capabilities
-{
- private final boolean dimensionValuesSorted;
-
- public static CapabilitiesBuilder builder()
- {
- return new CapabilitiesBuilder();
- }
-
- private Capabilities(
- boolean dimensionValuesSorted
- )
- {
- this.dimensionValuesSorted = dimensionValuesSorted;
- }
-
- /**
- * Is dimension value dictionary sorted?
- * @return
- */
- public boolean dimensionValuesSorted()
- {
- return dimensionValuesSorted;
- }
-
- public static class CapabilitiesBuilder
- {
- private boolean dimensionValuesSorted = false;
-
- private CapabilitiesBuilder()
- {
- }
-
- public CapabilitiesBuilder dimensionValuesSorted(boolean value)
- {
- dimensionValuesSorted = value;
- return this;
- }
-
- public Capabilities build()
- {
- return new Capabilities(
- dimensionValuesSorted
- );
- }
- }
-}
diff --git a/processing/src/main/java/org/apache/druid/segment/ColumnProcessors.java b/processing/src/main/java/org/apache/druid/segment/ColumnProcessors.java
index bc67317..5fb698a 100644
--- a/processing/src/main/java/org/apache/druid/segment/ColumnProcessors.java
+++ b/processing/src/main/java/org/apache/druid/segment/ColumnProcessors.java
@@ -25,6 +25,7 @@ import org.apache.druid.java.util.common.ISE;
import org.apache.druid.math.expr.Expr;
import org.apache.druid.query.dimension.DefaultDimensionSpec;
import org.apache.druid.query.dimension.DimensionSpec;
+import org.apache.druid.query.extraction.ExtractionFn;
import org.apache.druid.segment.column.ColumnCapabilities;
import org.apache.druid.segment.column.ColumnCapabilitiesImpl;
import org.apache.druid.segment.column.ValueType;
@@ -91,6 +92,8 @@ public class ColumnProcessors
return new ColumnCapabilitiesImpl()
.setType(ValueType.STRING)
+ .setDictionaryValuesSorted(dimensionSpec.getExtractionFn().preservesOrdering())
+ .setDictionaryValuesUnique(dimensionSpec.getExtractionFn().getExtractionType() == ExtractionFn.ExtractionType.ONE_TO_ONE)
.setHasMultipleValues(dimensionSpec.mustDecorate() || mayBeMultiValue(dimensionCapabilities));
} else {
// No transformation. Pass through.
@@ -129,7 +132,10 @@ public class ColumnProcessors
return makeProcessor(expr.getBindingIfIdentifier(), processorFactory, selectorFactory);
} else {
return makeProcessorInternal(
- factory -> new ColumnCapabilitiesImpl().setType(exprTypeHint).setHasMultipleValues(true),
+ factory -> new ColumnCapabilitiesImpl().setType(exprTypeHint)
+ .setHasMultipleValues(true)
+ .setDictionaryValuesUnique(false)
+ .setDictionaryValuesSorted(false),
factory -> ExpressionSelectors.makeDimensionSelector(factory, expr, null),
factory -> ExpressionSelectors.makeColumnValueSelector(factory, expr),
processorFactory,
diff --git a/processing/src/main/java/org/apache/druid/segment/DimensionHandlerUtils.java b/processing/src/main/java/org/apache/druid/segment/DimensionHandlerUtils.java
index 073f75d..0c8b3c2 100644
--- a/processing/src/main/java/org/apache/druid/segment/DimensionHandlerUtils.java
+++ b/processing/src/main/java/org/apache/druid/segment/DimensionHandlerUtils.java
@@ -34,6 +34,7 @@ import org.apache.druid.query.dimension.ColumnSelectorStrategy;
import org.apache.druid.query.dimension.ColumnSelectorStrategyFactory;
import org.apache.druid.query.dimension.DefaultDimensionSpec;
import org.apache.druid.query.dimension.DimensionSpec;
+import org.apache.druid.query.extraction.ExtractionFn;
import org.apache.druid.segment.column.ColumnCapabilities;
import org.apache.druid.segment.column.ColumnCapabilitiesImpl;
import org.apache.druid.segment.column.ValueType;
@@ -54,15 +55,17 @@ public final class DimensionHandlerUtils
public static final Float ZERO_FLOAT = 0.0f;
public static final Long ZERO_LONG = 0L;
+ public static final ColumnCapabilities DEFAULT_STRING_CAPABILITIES =
+ new ColumnCapabilitiesImpl().setType(ValueType.STRING)
+ .setDictionaryEncoded(false)
+ .setDictionaryValuesUnique(false)
+ .setDictionaryValuesSorted(false)
+ .setHasBitmapIndexes(false);
+
private DimensionHandlerUtils()
{
}
- public static final ColumnCapabilities DEFAULT_STRING_CAPABILITIES =
- new ColumnCapabilitiesImpl().setType(ValueType.STRING)
- .setDictionaryEncoded(true)
- .setHasBitmapIndexes(true);
-
public static DimensionHandler<?, ?, ?> getHandlerFromCapabilities(
String dimensionName,
@Nullable ColumnCapabilities capabilities,
@@ -219,7 +222,16 @@ public final class DimensionHandlerUtils
// Currently, all extractionFns output Strings, so the column will return String values via a
// DimensionSelector if an extractionFn is present.
if (dimSpec.getExtractionFn() != null) {
- capabilities = DEFAULT_STRING_CAPABILITIES;
+ ExtractionFn fn = dimSpec.getExtractionFn();
+ capabilities = ColumnCapabilitiesImpl.copyOf(capabilities)
+ .setType(ValueType.STRING)
+ .setDictionaryValuesUnique(
+ capabilities.isDictionaryEncoded() &&
+ fn.getExtractionType() == ExtractionFn.ExtractionType.ONE_TO_ONE
+ )
+ .setDictionaryValuesSorted(
+ capabilities.isDictionaryEncoded() && fn.preservesOrdering()
+ );
}
// DimensionSpec's decorate only operates on DimensionSelectors, so if a spec mustDecorate(),
diff --git a/processing/src/main/java/org/apache/druid/segment/QueryableIndexStorageAdapter.java b/processing/src/main/java/org/apache/druid/segment/QueryableIndexStorageAdapter.java
index 70f4c9c..e9aa01e 100644
--- a/processing/src/main/java/org/apache/druid/segment/QueryableIndexStorageAdapter.java
+++ b/processing/src/main/java/org/apache/druid/segment/QueryableIndexStorageAdapter.java
@@ -167,12 +167,6 @@ public class QueryableIndexStorageAdapter implements StorageAdapter
}
@Override
- public Capabilities getCapabilities()
- {
- return Capabilities.builder().dimensionValuesSorted(true).build();
- }
-
- @Override
@Nullable
public ColumnCapabilities getColumnCapabilities(String column)
{
diff --git a/processing/src/main/java/org/apache/druid/segment/RowBasedColumnSelectorFactory.java b/processing/src/main/java/org/apache/druid/segment/RowBasedColumnSelectorFactory.java
index 620fbfe..7b21d1f 100644
--- a/processing/src/main/java/org/apache/druid/segment/RowBasedColumnSelectorFactory.java
+++ b/processing/src/main/java/org/apache/druid/segment/RowBasedColumnSelectorFactory.java
@@ -107,7 +107,8 @@ public class RowBasedColumnSelectorFactory<T> implements ColumnSelectorFactory
if (valueType != null) {
return new ColumnCapabilitiesImpl()
.setType(valueType)
-
+ .setDictionaryValuesUnique(false)
+ .setDictionaryValuesSorted(false)
// Numeric types should be reported as complete, but not STRING or COMPLEX (because we don't have full info)
.setIsComplete(valueType.isNumeric());
} else {
diff --git a/processing/src/main/java/org/apache/druid/segment/RowBasedStorageAdapter.java b/processing/src/main/java/org/apache/druid/segment/RowBasedStorageAdapter.java
index b9f7b87..0c1f315 100644
--- a/processing/src/main/java/org/apache/druid/segment/RowBasedStorageAdapter.java
+++ b/processing/src/main/java/org/apache/druid/segment/RowBasedStorageAdapter.java
@@ -112,12 +112,6 @@ public class RowBasedStorageAdapter<RowType> implements StorageAdapter
return null;
}
- @Override
- public Capabilities getCapabilities()
- {
- return Capabilities.builder().dimensionValuesSorted(false).build();
- }
-
@Nullable
@Override
public ColumnCapabilities getColumnCapabilities(String column)
diff --git a/processing/src/main/java/org/apache/druid/segment/StorageAdapter.java b/processing/src/main/java/org/apache/druid/segment/StorageAdapter.java
index 89c2fdb..6fa16a6 100644
--- a/processing/src/main/java/org/apache/druid/segment/StorageAdapter.java
+++ b/processing/src/main/java/org/apache/druid/segment/StorageAdapter.java
@@ -47,7 +47,6 @@ public interface StorageAdapter extends CursorFactory
Comparable getMinValue(String column);
@Nullable
Comparable getMaxValue(String column);
- Capabilities getCapabilities();
/**
* Returns capabilities of a particular column, if known. May be null if the column doesn't exist, or if
diff --git a/processing/src/main/java/org/apache/druid/segment/column/ColumnBuilder.java b/processing/src/main/java/org/apache/druid/segment/column/ColumnBuilder.java
index 86b9b86..1b7163e 100644
--- a/processing/src/main/java/org/apache/druid/segment/column/ColumnBuilder.java
+++ b/processing/src/main/java/org/apache/druid/segment/column/ColumnBuilder.java
@@ -114,6 +114,8 @@ public class ColumnBuilder
.setType(type)
.setDictionaryEncoded(dictionaryEncoded)
.setHasBitmapIndexes(bitmapIndex != null)
+ .setDictionaryValuesSorted(dictionaryEncoded)
+ .setDictionaryValuesUnique(dictionaryEncoded)
.setHasSpatialIndexes(spatialIndex != null)
.setHasMultipleValues(hasMultipleValues)
.setIsComplete(true)
diff --git a/processing/src/main/java/org/apache/druid/segment/column/ColumnCapabilities.java b/processing/src/main/java/org/apache/druid/segment/column/ColumnCapabilities.java
index 2f95954..53f7440 100644
--- a/processing/src/main/java/org/apache/druid/segment/column/ColumnCapabilities.java
+++ b/processing/src/main/java/org/apache/druid/segment/column/ColumnCapabilities.java
@@ -26,6 +26,8 @@ public interface ColumnCapabilities
ValueType getType();
boolean isDictionaryEncoded();
+ Capable areDictionaryValuesSorted();
+ Capable areDictionaryValuesUnique();
boolean isRunLengthEncoded();
boolean hasBitmapIndexes();
boolean hasSpatialIndexes();
@@ -38,6 +40,34 @@ public interface ColumnCapabilities
* fail to set {@link #hasMultipleValues()} even when the associated column really could have multiple values.
* Until this situation is sorted out, if this method returns false, callers are encouraged to ignore
* {@link #hasMultipleValues()} and treat that property as if it were unknown.
+ *
+ * todo: replace all booleans with {@link Capable} and this method can be dropped
*/
boolean isComplete();
+
+
+ enum Capable
+ {
+ FALSE,
+ TRUE,
+ UNKNOWN;
+
+ public boolean isTrue()
+ {
+ return this == TRUE;
+ }
+
+ public Capable and(Capable other)
+ {
+ if (this == UNKNOWN || other == UNKNOWN) {
+ return UNKNOWN;
+ }
+ return this == TRUE && other == TRUE ? TRUE : FALSE;
+ }
+
+ public static Capable of(boolean bool)
+ {
+ return bool ? TRUE : FALSE;
+ }
+ }
}
diff --git a/processing/src/main/java/org/apache/druid/segment/column/ColumnCapabilitiesImpl.java b/processing/src/main/java/org/apache/druid/segment/column/ColumnCapabilitiesImpl.java
index 6b94b22..bee28eb 100644
--- a/processing/src/main/java/org/apache/druid/segment/column/ColumnCapabilitiesImpl.java
+++ b/processing/src/main/java/org/apache/druid/segment/column/ColumnCapabilitiesImpl.java
@@ -31,6 +31,15 @@ import javax.annotation.Nullable;
*/
public class ColumnCapabilitiesImpl implements ColumnCapabilities
{
+ public static ColumnCapabilitiesImpl copyOf(final ColumnCapabilities other)
+ {
+ final ColumnCapabilitiesImpl capabilities = new ColumnCapabilitiesImpl();
+ capabilities.merge(other);
+ capabilities.setFilterable(other.isFilterable());
+ capabilities.setIsComplete(other.isComplete());
+ return capabilities;
+ }
+
@Nullable
private ValueType type = null;
@@ -40,23 +49,16 @@ public class ColumnCapabilitiesImpl implements ColumnCapabilities
private boolean hasSpatialIndexes = false;
private boolean hasMultipleValues = false;
- // This is a query time concept and not persisted in the segment files.
+ // These capabilities are computed at query time and not persisted in the segment files.
+ @JsonIgnore
+ private Capable dictionaryValuesSorted = Capable.UNKNOWN;
+ @JsonIgnore
+ private Capable dictionaryValuesUnique = Capable.UNKNOWN;
@JsonIgnore
private boolean filterable;
-
-
@JsonIgnore
private boolean complete = false;
- public static ColumnCapabilitiesImpl copyOf(final ColumnCapabilities other)
- {
- final ColumnCapabilitiesImpl capabilities = new ColumnCapabilitiesImpl();
- capabilities.merge(other);
- capabilities.setFilterable(other.isFilterable());
- capabilities.setIsComplete(other.isComplete());
- return capabilities;
- }
-
@Override
@JsonProperty
public ValueType getType()
@@ -84,6 +86,30 @@ public class ColumnCapabilitiesImpl implements ColumnCapabilities
}
@Override
+ public Capable areDictionaryValuesSorted()
+ {
+ return dictionaryValuesSorted;
+ }
+
+ public ColumnCapabilitiesImpl setDictionaryValuesSorted(boolean dictionaryValuesSorted)
+ {
+ this.dictionaryValuesSorted = Capable.of(dictionaryValuesSorted);
+ return this;
+ }
+
+ @Override
+ public Capable areDictionaryValuesUnique()
+ {
+ return dictionaryValuesUnique;
+ }
+
+ public ColumnCapabilitiesImpl setDictionaryValuesUnique(boolean dictionaryValuesUnique)
+ {
+ this.dictionaryValuesUnique = Capable.of(dictionaryValuesUnique);
+ return this;
+ }
+
+ @Override
@JsonProperty
public boolean isRunLengthEncoded()
{
@@ -123,6 +149,12 @@ public class ColumnCapabilitiesImpl implements ColumnCapabilities
return hasMultipleValues;
}
+ public ColumnCapabilitiesImpl setHasMultipleValues(boolean hasMultipleValues)
+ {
+ this.hasMultipleValues = hasMultipleValues;
+ return this;
+ }
+
@Override
public boolean isFilterable()
{
@@ -133,22 +165,16 @@ public class ColumnCapabilitiesImpl implements ColumnCapabilities
filterable;
}
- @Override
- public boolean isComplete()
- {
- return complete;
- }
-
public ColumnCapabilitiesImpl setFilterable(boolean filterable)
{
this.filterable = filterable;
return this;
}
- public ColumnCapabilitiesImpl setHasMultipleValues(boolean hasMultipleValues)
+ @Override
+ public boolean isComplete()
{
- this.hasMultipleValues = hasMultipleValues;
- return this;
+ return complete;
}
public ColumnCapabilitiesImpl setIsComplete(boolean complete)
@@ -178,5 +204,7 @@ public class ColumnCapabilitiesImpl implements ColumnCapabilities
this.hasMultipleValues |= other.hasMultipleValues();
this.complete &= other.isComplete(); // these should always be the same?
this.filterable &= other.isFilterable();
+ this.dictionaryValuesSorted = this.dictionaryValuesSorted.and(other.areDictionaryValuesSorted());
+ this.dictionaryValuesUnique = this.dictionaryValuesUnique.and(other.areDictionaryValuesUnique());
}
}
diff --git a/processing/src/main/java/org/apache/druid/segment/incremental/IncrementalIndex.java b/processing/src/main/java/org/apache/druid/segment/incremental/IncrementalIndex.java
index 82b9276..c169d15 100644
--- a/processing/src/main/java/org/apache/druid/segment/incremental/IncrementalIndex.java
+++ b/processing/src/main/java/org/apache/druid/segment/incremental/IncrementalIndex.java
@@ -665,6 +665,8 @@ public abstract class IncrementalIndex<AggregatorType> extends AbstractIndex imp
capabilities.setType(ValueType.STRING);
capabilities.setDictionaryEncoded(true);
capabilities.setHasBitmapIndexes(true);
+ capabilities.setDictionaryValuesSorted(false);
+ capabilities.setDictionaryValuesUnique(true);
capabilities.setIsComplete(true);
columnCapabilities.put(dimension, capabilities);
}
@@ -924,6 +926,10 @@ public abstract class IncrementalIndex<AggregatorType> extends AbstractIndex imp
ColumnCapabilitiesImpl capabilities = new ColumnCapabilitiesImpl();
capabilities.setDictionaryEncoded(type == ValueType.STRING);
capabilities.setHasBitmapIndexes(type == ValueType.STRING);
+ if (type == ValueType.STRING) {
+ capabilities.setDictionaryValuesUnique(true);
+ capabilities.setDictionaryValuesSorted(false);
+ }
capabilities.setType(type);
capabilities.setIsComplete(true);
return capabilities;
diff --git a/processing/src/main/java/org/apache/druid/segment/incremental/IncrementalIndexStorageAdapter.java b/processing/src/main/java/org/apache/druid/segment/incremental/IncrementalIndexStorageAdapter.java
index 2dd613b..cc792a5 100644
--- a/processing/src/main/java/org/apache/druid/segment/incremental/IncrementalIndexStorageAdapter.java
+++ b/processing/src/main/java/org/apache/druid/segment/incremental/IncrementalIndexStorageAdapter.java
@@ -29,7 +29,6 @@ import org.apache.druid.query.BaseQuery;
import org.apache.druid.query.QueryMetrics;
import org.apache.druid.query.filter.Filter;
import org.apache.druid.query.filter.ValueMatcher;
-import org.apache.druid.segment.Capabilities;
import org.apache.druid.segment.ColumnSelectorFactory;
import org.apache.druid.segment.Cursor;
import org.apache.druid.segment.DimensionDictionarySelector;
@@ -140,13 +139,6 @@ public class IncrementalIndexStorageAdapter implements StorageAdapter
return indexer.getMaxValue();
}
-
- @Override
- public Capabilities getCapabilities()
- {
- return Capabilities.builder().dimensionValuesSorted(false).build();
- }
-
@Override
public ColumnCapabilities getColumnCapabilities(String column)
{
diff --git a/processing/src/main/java/org/apache/druid/segment/join/HashJoinSegmentStorageAdapter.java b/processing/src/main/java/org/apache/druid/segment/join/HashJoinSegmentStorageAdapter.java
index 40ed7fe..46f522c 100644
--- a/processing/src/main/java/org/apache/druid/segment/join/HashJoinSegmentStorageAdapter.java
+++ b/processing/src/main/java/org/apache/druid/segment/join/HashJoinSegmentStorageAdapter.java
@@ -26,7 +26,6 @@ import org.apache.druid.java.util.common.guava.Sequence;
import org.apache.druid.java.util.common.guava.Sequences;
import org.apache.druid.query.QueryMetrics;
import org.apache.druid.query.filter.Filter;
-import org.apache.druid.segment.Capabilities;
import org.apache.druid.segment.Cursor;
import org.apache.druid.segment.Metadata;
import org.apache.druid.segment.StorageAdapter;
@@ -146,16 +145,6 @@ public class HashJoinSegmentStorageAdapter implements StorageAdapter
}
}
- @Override
- public Capabilities getCapabilities()
- {
- // Dictionaries in the joinables may not be sorted. Unfortunately this API does not let us be granular about what
- // is and isn't sorted, so return false globally. At the time of this writing, the only query affected by this
- // is a topN with lexicographic sort and 'previousStop' set (it will not be able to skip values based on
- // dictionary code).
- return Capabilities.builder().dimensionValuesSorted(false).build();
- }
-
@Nullable
@Override
public ColumnCapabilities getColumnCapabilities(String column)
diff --git a/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTableColumnSelectorFactory.java b/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTableColumnSelectorFactory.java
index 2b0b5c7..7e6466d 100644
--- a/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTableColumnSelectorFactory.java
+++ b/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTableColumnSelectorFactory.java
@@ -56,6 +56,9 @@ public class IndexedTableColumnSelectorFactory implements ColumnSelectorFactory
capabilities.setDictionaryEncoded(true);
}
+ capabilities.setDictionaryValuesSorted(false);
+ capabilities.setDictionaryValuesUnique(false);
+
return capabilities.setIsComplete(true);
} else {
return null;
diff --git a/processing/src/test/java/org/apache/druid/query/groupby/epinephelinae/GroupByQueryEngineV2Test.java b/processing/src/test/java/org/apache/druid/query/groupby/epinephelinae/GroupByQueryEngineV2Test.java
new file mode 100644
index 0000000..d71d70a
--- /dev/null
+++ b/processing/src/test/java/org/apache/druid/query/groupby/epinephelinae/GroupByQueryEngineV2Test.java
@@ -0,0 +1,141 @@
+/*
+ * 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.query.groupby.epinephelinae;
+
+import org.apache.druid.segment.ColumnSelectorFactory;
+import org.apache.druid.segment.column.ColumnCapabilities;
+import org.apache.druid.segment.column.ColumnCapabilitiesImpl;
+import org.apache.druid.segment.column.ValueType;
+import org.easymock.EasyMock;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class GroupByQueryEngineV2Test
+{
+ private static final String DIM = "d0";
+ ColumnSelectorFactory factory;
+
+ @Before
+ public void setUp()
+ {
+ factory = EasyMock.createMock(ColumnSelectorFactory.class);
+ }
+
+ @Test
+ public void testCanPushDownLimitForSegmentStringSelector()
+ {
+ ColumnCapabilities capabilities = new ColumnCapabilitiesImpl().setType(ValueType.STRING)
+ .setHasBitmapIndexes(true)
+ .setHasMultipleValues(false)
+ .setDictionaryEncoded(true)
+ .setDictionaryValuesSorted(true)
+ .setDictionaryValuesUnique(true)
+ .setIsComplete(true);
+ EasyMock.expect(factory.getColumnCapabilities(DIM)).andReturn(capabilities).once();
+ EasyMock.replay(factory);
+ Assert.assertTrue(GroupByQueryEngineV2.canPushDownLimit(factory, DIM));
+ EasyMock.verify(factory);
+ }
+
+ @Test
+ public void testCanPushDownLimitForIncrementalStringSelector()
+ {
+ ColumnCapabilities capabilities = new ColumnCapabilitiesImpl().setType(ValueType.STRING)
+ .setHasBitmapIndexes(false)
+ .setHasMultipleValues(false)
+ .setDictionaryEncoded(false)
+ .setDictionaryValuesSorted(false)
+ .setDictionaryValuesUnique(true)
+ .setIsComplete(true);
+ EasyMock.expect(factory.getColumnCapabilities(DIM)).andReturn(capabilities).once();
+ EasyMock.replay(factory);
+ Assert.assertFalse(GroupByQueryEngineV2.canPushDownLimit(factory, DIM));
+ EasyMock.verify(factory);
+ }
+
+ @Test
+ public void testCanPushDownLimitForExpressionStringSelector()
+ {
+ ColumnCapabilities capabilities = new ColumnCapabilitiesImpl().setType(ValueType.STRING)
+ .setHasBitmapIndexes(false)
+ .setHasMultipleValues(false)
+ .setDictionaryEncoded(false)
+ .setDictionaryValuesSorted(false)
+ .setDictionaryValuesUnique(false)
+ .setIsComplete(true);
+ EasyMock.expect(factory.getColumnCapabilities(DIM)).andReturn(capabilities).once();
+ EasyMock.replay(factory);
+ Assert.assertFalse(GroupByQueryEngineV2.canPushDownLimit(factory, DIM));
+ EasyMock.verify(factory);
+ }
+
+ @Test
+ public void testCanPushDownLimitForJoinStringSelector()
+ {
+ ColumnCapabilities capabilities = new ColumnCapabilitiesImpl().setType(ValueType.STRING)
+ .setHasBitmapIndexes(false)
+ .setHasMultipleValues(false)
+ .setDictionaryEncoded(true)
+ .setDictionaryValuesSorted(false)
+ .setDictionaryValuesUnique(false)
+ .setIsComplete(true);
+ EasyMock.expect(factory.getColumnCapabilities(DIM)).andReturn(capabilities).once();
+ EasyMock.replay(factory);
+ Assert.assertFalse(GroupByQueryEngineV2.canPushDownLimit(factory, DIM));
+ EasyMock.verify(factory);
+ }
+
+ @Test
+ public void testCanPushDownLimitForNumericSelector()
+ {
+ ColumnCapabilitiesImpl capabilities = new ColumnCapabilitiesImpl().setType(ValueType.LONG)
+ .setHasBitmapIndexes(false)
+ .setHasMultipleValues(false)
+ .setDictionaryEncoded(false)
+ .setDictionaryValuesSorted(false)
+ .setDictionaryValuesUnique(false)
+ .setIsComplete(true);
+ EasyMock.expect(factory.getColumnCapabilities(DIM)).andReturn(capabilities).anyTimes();
+ EasyMock.replay(factory);
+ Assert.assertTrue(GroupByQueryEngineV2.canPushDownLimit(factory, DIM));
+ capabilities.setType(ValueType.DOUBLE);
+ Assert.assertTrue(GroupByQueryEngineV2.canPushDownLimit(factory, DIM));
+ capabilities.setType(ValueType.FLOAT);
+ Assert.assertTrue(GroupByQueryEngineV2.canPushDownLimit(factory, DIM));
+ EasyMock.verify(factory);
+ }
+
+ @Test
+ public void testCanPushDownLimitForComplexSelector()
+ {
+ ColumnCapabilitiesImpl capabilities = new ColumnCapabilitiesImpl().setType(ValueType.COMPLEX)
+ .setHasBitmapIndexes(false)
+ .setHasMultipleValues(false)
+ .setDictionaryEncoded(false)
+ .setDictionaryValuesSorted(false)
+ .setDictionaryValuesUnique(false)
+ .setIsComplete(true);
+ EasyMock.expect(factory.getColumnCapabilities(DIM)).andReturn(capabilities).once();
+ EasyMock.replay(factory);
+ Assert.assertTrue(GroupByQueryEngineV2.canPushDownLimit(factory, DIM));
+ EasyMock.verify(factory);
+ }
+}
diff --git a/processing/src/test/java/org/apache/druid/query/topn/TopNMetricSpecOptimizationsTest.java b/processing/src/test/java/org/apache/druid/query/topn/TopNMetricSpecOptimizationsTest.java
index b23c70e..4d322b7 100644
--- a/processing/src/test/java/org/apache/druid/query/topn/TopNMetricSpecOptimizationsTest.java
+++ b/processing/src/test/java/org/apache/druid/query/topn/TopNMetricSpecOptimizationsTest.java
@@ -35,7 +35,6 @@ import org.apache.druid.query.aggregation.DoubleMinAggregatorFactory;
import org.apache.druid.query.filter.Filter;
import org.apache.druid.query.filter.ValueMatcher;
import org.apache.druid.query.monomorphicprocessing.RuntimeShapeInspector;
-import org.apache.druid.segment.Capabilities;
import org.apache.druid.segment.Cursor;
import org.apache.druid.segment.DimensionSelector;
import org.apache.druid.segment.IdLookup;
@@ -296,12 +295,6 @@ public class TopNMetricSpecOptimizationsTest
return null;
}
- @Override
- public Capabilities getCapabilities()
- {
- return Capabilities.builder().dimensionValuesSorted(true).build();
- }
-
@Nullable
@Override
public ColumnCapabilities getColumnCapabilities(String column)
diff --git a/processing/src/test/java/org/apache/druid/segment/QueryableIndexColumnCapabilitiesTest.java b/processing/src/test/java/org/apache/druid/segment/QueryableIndexColumnCapabilitiesTest.java
new file mode 100644
index 0000000..dc102e6
--- /dev/null
+++ b/processing/src/test/java/org/apache/druid/segment/QueryableIndexColumnCapabilitiesTest.java
@@ -0,0 +1,213 @@
+/*
+ * 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.segment;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import org.apache.druid.data.input.InputRow;
+import org.apache.druid.data.input.impl.DimensionSchema;
+import org.apache.druid.data.input.impl.DimensionsSpec;
+import org.apache.druid.data.input.impl.DoubleDimensionSchema;
+import org.apache.druid.data.input.impl.FloatDimensionSchema;
+import org.apache.druid.data.input.impl.LongDimensionSchema;
+import org.apache.druid.data.input.impl.MapInputRowParser;
+import org.apache.druid.data.input.impl.TimeAndDimsParseSpec;
+import org.apache.druid.data.input.impl.TimestampSpec;
+import org.apache.druid.java.util.common.DateTimes;
+import org.apache.druid.query.aggregation.CountAggregatorFactory;
+import org.apache.druid.query.aggregation.DoubleSumAggregatorFactory;
+import org.apache.druid.query.aggregation.FloatSumAggregatorFactory;
+import org.apache.druid.query.aggregation.LongSumAggregatorFactory;
+import org.apache.druid.query.aggregation.hyperloglog.HyperUniquesAggregatorFactory;
+import org.apache.druid.segment.column.ColumnCapabilities;
+import org.apache.druid.segment.column.ColumnHolder;
+import org.apache.druid.segment.column.ValueType;
+import org.apache.druid.segment.incremental.IncrementalIndex;
+import org.apache.druid.segment.incremental.IncrementalIndexSchema;
+import org.apache.druid.testing.InitializedNullHandlingTest;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class QueryableIndexColumnCapabilitiesTest extends InitializedNullHandlingTest
+{
+ @ClassRule
+ public static TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ private static IncrementalIndex INC_INDEX;
+ private static QueryableIndex MMAP_INDEX;
+
+ @BeforeClass
+ public static void setup() throws IOException
+ {
+ List<InputRow> rows = new ArrayList<>();
+ MapInputRowParser parser = new MapInputRowParser(
+ new TimeAndDimsParseSpec(
+ new TimestampSpec("time", "auto", null),
+ new DimensionsSpec(
+ ImmutableList.<DimensionSchema>builder()
+ .addAll(DimensionsSpec.getDefaultSchemas(ImmutableList.of("d1", "d2")))
+ .add(new DoubleDimensionSchema("d3"))
+ .add(new FloatDimensionSchema("d4"))
+ .add(new LongDimensionSchema("d5"))
+ .build(),
+ null,
+ null
+ )
+ )
+ );
+ Map<String, Object> event =
+ ImmutableMap.<String, Object>builder().put("time", DateTimes.nowUtc().getMillis())
+ .put("d1", "some string")
+ .put("d2", ImmutableList.of("some", "list"))
+ .put("d3", 1.234)
+ .put("d4", 1.234f)
+ .put("d5", 10L)
+ .build();
+ rows.add(Iterables.getOnlyElement(parser.parseBatch(event)));
+ IndexBuilder builder = IndexBuilder.create()
+ .rows(rows)
+ .schema(
+ new IncrementalIndexSchema.Builder()
+ .withMetrics(
+ new CountAggregatorFactory("cnt"),
+ new DoubleSumAggregatorFactory("m1", "d3"),
+ new FloatSumAggregatorFactory("m2", "d4"),
+ new LongSumAggregatorFactory("m3", "d5"),
+ new HyperUniquesAggregatorFactory("m4", "d1")
+ )
+ .withDimensionsSpec(parser)
+ .withRollup(false)
+ .build()
+ )
+ .tmpDir(temporaryFolder.newFolder());
+ INC_INDEX = builder.buildIncrementalIndex();
+ MMAP_INDEX = builder.buildMMappedIndex();
+ }
+
+ @AfterClass
+ public static void teardown()
+ {
+ INC_INDEX.close();
+ MMAP_INDEX.close();
+ }
+
+ @Test
+ public void testNumericColumns()
+ {
+ // incremental index
+ assertNonStringColumnCapabilities(INC_INDEX.getCapabilities(ColumnHolder.TIME_COLUMN_NAME), ValueType.LONG);
+ assertNonStringColumnCapabilities(INC_INDEX.getCapabilities("d3"), ValueType.DOUBLE);
+ assertNonStringColumnCapabilities(INC_INDEX.getCapabilities("d4"), ValueType.FLOAT);
+ assertNonStringColumnCapabilities(INC_INDEX.getCapabilities("d5"), ValueType.LONG);
+ assertNonStringColumnCapabilities(INC_INDEX.getCapabilities("m1"), ValueType.DOUBLE);
+ assertNonStringColumnCapabilities(INC_INDEX.getCapabilities("m2"), ValueType.FLOAT);
+ assertNonStringColumnCapabilities(INC_INDEX.getCapabilities("m3"), ValueType.LONG);
+
+ // segment index
+ assertNonStringColumnCapabilities(
+ MMAP_INDEX.getColumnHolder(ColumnHolder.TIME_COLUMN_NAME).getCapabilities(),
+ ValueType.LONG
+ );
+ assertNonStringColumnCapabilities(MMAP_INDEX.getColumnHolder("d3").getCapabilities(), ValueType.DOUBLE);
+ assertNonStringColumnCapabilities(MMAP_INDEX.getColumnHolder("d4").getCapabilities(), ValueType.FLOAT);
+ assertNonStringColumnCapabilities(MMAP_INDEX.getColumnHolder("d5").getCapabilities(), ValueType.LONG);
+ assertNonStringColumnCapabilities(MMAP_INDEX.getColumnHolder("m1").getCapabilities(), ValueType.DOUBLE);
+ assertNonStringColumnCapabilities(MMAP_INDEX.getColumnHolder("m2").getCapabilities(), ValueType.FLOAT);
+ assertNonStringColumnCapabilities(MMAP_INDEX.getColumnHolder("m3").getCapabilities(), ValueType.LONG);
+ }
+
+ @Test
+ public void testStringColumn()
+ {
+ ColumnCapabilities caps = INC_INDEX.getCapabilities("d1");
+ Assert.assertEquals(ValueType.STRING, caps.getType());
+ Assert.assertTrue(caps.hasBitmapIndexes());
+ Assert.assertTrue(caps.isDictionaryEncoded());
+ Assert.assertFalse(caps.areDictionaryValuesSorted().isTrue());
+ Assert.assertTrue(caps.areDictionaryValuesUnique().isTrue());
+ Assert.assertFalse(caps.hasMultipleValues());
+ Assert.assertFalse(caps.hasSpatialIndexes());
+ Assert.assertTrue(caps.isComplete());
+
+ caps = MMAP_INDEX.getColumnHolder("d1").getCapabilities();
+ Assert.assertEquals(ValueType.STRING, caps.getType());
+ Assert.assertTrue(caps.hasBitmapIndexes());
+ Assert.assertTrue(caps.isDictionaryEncoded());
+ Assert.assertTrue(caps.areDictionaryValuesSorted().isTrue());
+ Assert.assertTrue(caps.areDictionaryValuesUnique().isTrue());
+ Assert.assertFalse(caps.hasMultipleValues());
+ Assert.assertFalse(caps.hasSpatialIndexes());
+ Assert.assertTrue(caps.isComplete());
+ }
+
+ @Test
+ public void testMultiStringColumn()
+ {
+ ColumnCapabilities caps = INC_INDEX.getCapabilities("d2");
+ Assert.assertEquals(ValueType.STRING, caps.getType());
+ Assert.assertTrue(caps.hasBitmapIndexes());
+ Assert.assertTrue(caps.isDictionaryEncoded());
+ Assert.assertFalse(caps.areDictionaryValuesSorted().isTrue());
+ Assert.assertTrue(caps.areDictionaryValuesUnique().isTrue());
+ Assert.assertTrue(caps.hasMultipleValues());
+ Assert.assertFalse(caps.hasSpatialIndexes());
+ Assert.assertTrue(caps.isComplete());
+
+ caps = MMAP_INDEX.getColumnHolder("d2").getCapabilities();
+ Assert.assertEquals(ValueType.STRING, caps.getType());
+ Assert.assertTrue(caps.hasBitmapIndexes());
+ Assert.assertTrue(caps.isDictionaryEncoded());
+ Assert.assertTrue(caps.areDictionaryValuesSorted().isTrue());
+ Assert.assertTrue(caps.areDictionaryValuesUnique().isTrue());
+ Assert.assertTrue(caps.hasMultipleValues());
+ Assert.assertFalse(caps.hasSpatialIndexes());
+ Assert.assertTrue(caps.isComplete());
+ }
+
+ @Test
+ public void testComplexColumn()
+ {
+ assertNonStringColumnCapabilities(INC_INDEX.getCapabilities("m4"), ValueType.COMPLEX);
+ assertNonStringColumnCapabilities(MMAP_INDEX.getColumnHolder("m4").getCapabilities(), ValueType.COMPLEX);
+ }
+
+
+ private void assertNonStringColumnCapabilities(ColumnCapabilities caps, ValueType valueType)
+ {
+ Assert.assertEquals(valueType, caps.getType());
+ Assert.assertFalse(caps.hasBitmapIndexes());
+ Assert.assertFalse(caps.isDictionaryEncoded());
+ Assert.assertFalse(caps.areDictionaryValuesSorted().isTrue());
+ Assert.assertFalse(caps.areDictionaryValuesUnique().isTrue());
+ Assert.assertFalse(caps.hasMultipleValues());
+ Assert.assertFalse(caps.hasSpatialIndexes());
+ Assert.assertTrue(caps.isComplete());
+ }
+}
diff --git a/processing/src/test/java/org/apache/druid/segment/RowBasedColumnSelectorFactoryTest.java b/processing/src/test/java/org/apache/druid/segment/RowBasedColumnSelectorFactoryTest.java
new file mode 100644
index 0000000..8886c7d
--- /dev/null
+++ b/processing/src/test/java/org/apache/druid/segment/RowBasedColumnSelectorFactoryTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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.segment;
+
+import org.apache.druid.segment.column.ColumnCapabilities;
+import org.apache.druid.segment.column.ColumnHolder;
+import org.apache.druid.segment.column.RowSignature;
+import org.apache.druid.segment.column.ValueType;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class RowBasedColumnSelectorFactoryTest
+{
+ private static final String STRING_COLUMN_NAME = "string";
+ private static final String LONG_COLUMN_NAME = "long";
+ private static final String FLOAT_COLUMN_NAME = "float";
+ private static final String DOUBLE_COLUMN_NAME = "double";
+ private static final String COMPLEX_COLUMN_NAME = "complex";
+
+ private static final RowSignature ROW_SIGNATURE = RowSignature.builder()
+ .add(ColumnHolder.TIME_COLUMN_NAME, ValueType.LONG)
+ .add(STRING_COLUMN_NAME, ValueType.STRING)
+ .add(LONG_COLUMN_NAME, ValueType.LONG)
+ .add(FLOAT_COLUMN_NAME, ValueType.FLOAT)
+ .add(DOUBLE_COLUMN_NAME, ValueType.DOUBLE)
+ .add(COMPLEX_COLUMN_NAME, ValueType.COMPLEX)
+ .build();
+
+ @Test
+ public void testCapabilitiesTime()
+ {
+ // time column takes a special path
+ ColumnCapabilities caps =
+ RowBasedColumnSelectorFactory.getColumnCapabilities(ROW_SIGNATURE, ColumnHolder.TIME_COLUMN_NAME);
+ Assert.assertEquals(ValueType.LONG, caps.getType());
+ Assert.assertFalse(caps.hasBitmapIndexes());
+ Assert.assertFalse(caps.isDictionaryEncoded());
+ Assert.assertFalse(caps.areDictionaryValuesSorted().isTrue());
+ Assert.assertFalse(caps.areDictionaryValuesUnique().isTrue());
+ Assert.assertFalse(caps.hasMultipleValues());
+ Assert.assertFalse(caps.hasSpatialIndexes());
+ Assert.assertTrue(caps.isComplete());
+ }
+
+ @Test
+ public void testCapabilitiesString()
+ {
+ ColumnCapabilities caps =
+ RowBasedColumnSelectorFactory.getColumnCapabilities(ROW_SIGNATURE, STRING_COLUMN_NAME);
+ Assert.assertEquals(ValueType.STRING, caps.getType());
+ Assert.assertFalse(caps.hasBitmapIndexes());
+ Assert.assertFalse(caps.isDictionaryEncoded());
+ Assert.assertFalse(caps.areDictionaryValuesSorted().isTrue());
+ Assert.assertFalse(caps.areDictionaryValuesUnique().isTrue());
+ Assert.assertFalse(caps.hasMultipleValues());
+ Assert.assertFalse(caps.hasSpatialIndexes());
+ Assert.assertFalse(caps.isComplete());
+ }
+
+ @Test
+ public void testCapabilitiesLong()
+ {
+ ColumnCapabilities caps =
+ RowBasedColumnSelectorFactory.getColumnCapabilities(ROW_SIGNATURE, LONG_COLUMN_NAME);
+ Assert.assertEquals(ValueType.LONG, caps.getType());
+ Assert.assertFalse(caps.hasBitmapIndexes());
+ Assert.assertFalse(caps.isDictionaryEncoded());
+ Assert.assertFalse(caps.areDictionaryValuesSorted().isTrue());
+ Assert.assertFalse(caps.areDictionaryValuesUnique().isTrue());
+ Assert.assertFalse(caps.hasMultipleValues());
+ Assert.assertFalse(caps.hasSpatialIndexes());
+ Assert.assertTrue(caps.isComplete());
+ }
+
+ @Test
+ public void testCapabilitiesFloat()
+ {
+ ColumnCapabilities caps =
+ RowBasedColumnSelectorFactory.getColumnCapabilities(ROW_SIGNATURE, FLOAT_COLUMN_NAME);
+ Assert.assertEquals(ValueType.FLOAT, caps.getType());
+ Assert.assertFalse(caps.hasBitmapIndexes());
+ Assert.assertFalse(caps.isDictionaryEncoded());
+ Assert.assertFalse(caps.areDictionaryValuesSorted().isTrue());
+ Assert.assertFalse(caps.areDictionaryValuesUnique().isTrue());
+ Assert.assertFalse(caps.hasMultipleValues());
+ Assert.assertFalse(caps.hasSpatialIndexes());
+ Assert.assertTrue(caps.isComplete());
+ }
+
+ @Test
+ public void testCapabilitiesDouble()
+ {
+ ColumnCapabilities caps =
+ RowBasedColumnSelectorFactory.getColumnCapabilities(ROW_SIGNATURE, DOUBLE_COLUMN_NAME);
+ Assert.assertEquals(ValueType.DOUBLE, caps.getType());
+ Assert.assertFalse(caps.hasBitmapIndexes());
+ Assert.assertFalse(caps.isDictionaryEncoded());
+ Assert.assertFalse(caps.areDictionaryValuesSorted().isTrue());
+ Assert.assertFalse(caps.areDictionaryValuesUnique().isTrue());
+ Assert.assertFalse(caps.hasMultipleValues());
+ Assert.assertFalse(caps.hasSpatialIndexes());
+ Assert.assertTrue(caps.isComplete());
+ }
+
+ @Test
+ public void testCapabilitiesComplex()
+ {
+ ColumnCapabilities caps =
+ RowBasedColumnSelectorFactory.getColumnCapabilities(ROW_SIGNATURE, COMPLEX_COLUMN_NAME);
+ Assert.assertEquals(ValueType.COMPLEX, caps.getType());
+ Assert.assertFalse(caps.hasBitmapIndexes());
+ Assert.assertFalse(caps.isDictionaryEncoded());
+ Assert.assertFalse(caps.areDictionaryValuesSorted().isTrue());
+ Assert.assertFalse(caps.areDictionaryValuesUnique().isTrue());
+ Assert.assertFalse(caps.hasMultipleValues());
+ Assert.assertFalse(caps.hasSpatialIndexes());
+ Assert.assertFalse(caps.isComplete());
+ }
+
+ @Test
+ public void testCapabilitiesUnknownColumn()
+ {
+ ColumnCapabilities caps =
+ RowBasedColumnSelectorFactory.getColumnCapabilities(ROW_SIGNATURE, "wat");
+ Assert.assertNull(caps);
+ }
+}
diff --git a/processing/src/test/java/org/apache/druid/segment/column/ColumnCapabilitiesTest.java b/processing/src/test/java/org/apache/druid/segment/column/ColumnCapabilitiesTest.java
new file mode 100644
index 0000000..e0b3025
--- /dev/null
+++ b/processing/src/test/java/org/apache/druid/segment/column/ColumnCapabilitiesTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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.segment.column;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ColumnCapabilitiesTest
+{
+ @Test
+ public void testCapableAnd()
+ {
+ Assert.assertTrue(ColumnCapabilities.Capable.TRUE.and(ColumnCapabilities.Capable.TRUE).isTrue());
+ Assert.assertFalse(ColumnCapabilities.Capable.TRUE.and(ColumnCapabilities.Capable.FALSE).isTrue());
+ Assert.assertFalse(ColumnCapabilities.Capable.TRUE.and(ColumnCapabilities.Capable.UNKNOWN).isTrue());
+
+ Assert.assertFalse(ColumnCapabilities.Capable.FALSE.and(ColumnCapabilities.Capable.TRUE).isTrue());
+ Assert.assertFalse(ColumnCapabilities.Capable.FALSE.and(ColumnCapabilities.Capable.FALSE).isTrue());
+ Assert.assertFalse(ColumnCapabilities.Capable.FALSE.and(ColumnCapabilities.Capable.UNKNOWN).isTrue());
+
+ Assert.assertFalse(ColumnCapabilities.Capable.UNKNOWN.and(ColumnCapabilities.Capable.TRUE).isTrue());
+ Assert.assertFalse(ColumnCapabilities.Capable.UNKNOWN.and(ColumnCapabilities.Capable.FALSE).isTrue());
+ Assert.assertFalse(ColumnCapabilities.Capable.UNKNOWN.and(ColumnCapabilities.Capable.UNKNOWN).isTrue());
+ }
+
+ @Test
+ public void testCapableOfBoolean()
+ {
+ Assert.assertEquals(ColumnCapabilities.Capable.TRUE, ColumnCapabilities.Capable.of(true));
+ Assert.assertEquals(ColumnCapabilities.Capable.FALSE, ColumnCapabilities.Capable.of(false));
+ }
+}
diff --git a/processing/src/test/java/org/apache/druid/segment/join/HashJoinSegmentStorageAdapterTest.java b/processing/src/test/java/org/apache/druid/segment/join/HashJoinSegmentStorageAdapterTest.java
index b2eaa14..b8f30ff 100644
--- a/processing/src/test/java/org/apache/druid/segment/join/HashJoinSegmentStorageAdapterTest.java
+++ b/processing/src/test/java/org/apache/druid/segment/join/HashJoinSegmentStorageAdapterTest.java
@@ -196,12 +196,6 @@ public class HashJoinSegmentStorageAdapterTest extends BaseHashJoinSegmentStorag
}
@Test
- public void test_getCapabilities_factToCountry()
- {
- Assert.assertFalse(makeFactToCountrySegment().getCapabilities().dimensionValuesSorted());
- }
-
- @Test
public void test_getColumnCapabilities_factToCountryFactColumn()
{
final ColumnCapabilities capabilities = makeFactToCountrySegment().getColumnCapabilities("countryIsoCode");
@@ -209,6 +203,8 @@ public class HashJoinSegmentStorageAdapterTest extends BaseHashJoinSegmentStorag
Assert.assertEquals(ValueType.STRING, capabilities.getType());
Assert.assertTrue(capabilities.hasBitmapIndexes());
Assert.assertTrue(capabilities.isDictionaryEncoded());
+ Assert.assertTrue(capabilities.areDictionaryValuesSorted().isTrue());
+ Assert.assertTrue(capabilities.areDictionaryValuesUnique().isTrue());
}
@Test
@@ -220,6 +216,8 @@ public class HashJoinSegmentStorageAdapterTest extends BaseHashJoinSegmentStorag
Assert.assertEquals(ValueType.STRING, capabilities.getType());
Assert.assertFalse(capabilities.hasBitmapIndexes());
+ Assert.assertFalse(capabilities.areDictionaryValuesUnique().isTrue());
+ Assert.assertFalse(capabilities.areDictionaryValuesSorted().isTrue());
Assert.assertTrue(capabilities.isDictionaryEncoded());
}
diff --git a/processing/src/test/java/org/apache/druid/segment/virtual/ExpressionVirtualColumnTest.java b/processing/src/test/java/org/apache/druid/segment/virtual/ExpressionVirtualColumnTest.java
index 5cd87af..526c63c 100644
--- a/processing/src/test/java/org/apache/druid/segment/virtual/ExpressionVirtualColumnTest.java
+++ b/processing/src/test/java/org/apache/druid/segment/virtual/ExpressionVirtualColumnTest.java
@@ -804,4 +804,28 @@ public class ExpressionVirtualColumnTest extends InitializedNullHandlingTest
Assert.assertTrue(selector.getObject().isNumericNull());
}
}
+
+ @Test
+ public void testCapabilities()
+ {
+ ColumnCapabilities caps = X_PLUS_Y.capabilities("expr");
+ Assert.assertEquals(ValueType.FLOAT, caps.getType());
+ Assert.assertFalse(caps.hasBitmapIndexes());
+ Assert.assertFalse(caps.isDictionaryEncoded());
+ Assert.assertFalse(caps.areDictionaryValuesSorted().isTrue());
+ Assert.assertFalse(caps.areDictionaryValuesUnique().isTrue());
+ Assert.assertTrue(caps.hasMultipleValues());
+ Assert.assertFalse(caps.hasSpatialIndexes());
+ Assert.assertFalse(caps.isComplete());
+
+ caps = Z_CONCAT_X.capabilities("expr");
+ Assert.assertEquals(ValueType.STRING, caps.getType());
+ Assert.assertFalse(caps.hasBitmapIndexes());
+ Assert.assertFalse(caps.isDictionaryEncoded());
+ Assert.assertFalse(caps.areDictionaryValuesSorted().isTrue());
+ Assert.assertFalse(caps.areDictionaryValuesUnique().isTrue());
+ Assert.assertTrue(caps.hasMultipleValues());
+ Assert.assertFalse(caps.hasSpatialIndexes());
+ Assert.assertFalse(caps.isComplete());
+ }
}
diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java
index fcb5530..fae92fb 100644
--- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java
+++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java
@@ -7395,6 +7395,47 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
}
@Test
+ public void testGroupByLimitPushdownExtraction() throws Exception
+ {
+ cannotVectorize();
+
+ testQuery(
+ "SELECT dim4, substring(dim5, 1, 1), count(*) FROM druid.numfoo WHERE dim4 = 'a' GROUP BY 1,2 LIMIT 2",
+ ImmutableList.of(
+ GroupByQuery.builder()
+ .setDataSource(CalciteTests.DATASOURCE3)
+ .setInterval(querySegmentSpec(Filtration.eternity()))
+ .setGranularity(Granularities.ALL)
+ .setDimensions(
+ dimensions(
+ new DefaultDimensionSpec("v0", "_d0"),
+ new ExtractionDimensionSpec("dim5", "_d1", new SubstringDimExtractionFn(0, 1))
+ )
+ )
+ .setVirtualColumns(expressionVirtualColumn("v0", "'a'", ValueType.STRING))
+ .setDimFilter(selector("dim4", "a", null))
+ .setAggregatorSpecs(
+ aggregators(
+ new CountAggregatorFactory("a0")
+ )
+ )
+ .setLimitSpec(
+ new DefaultLimitSpec(
+ ImmutableList.of(),
+ 2
+ )
+ )
+ .setContext(QUERY_CONTEXT_DEFAULT)
+ .build()
+ ),
+ ImmutableList.of(
+ new Object[]{"a", "a", 2L},
+ new Object[]{"a", "b", 1L}
+ )
+ );
+ }
+
+ @Test
public void testFilterOnTimeFloor() throws Exception
{
testQuery(
diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java b/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java
index 5ae1390..2792b2d 100644
--- a/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java
+++ b/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java
@@ -241,7 +241,7 @@ public class CalciteTests
new TimestampSpec(TIMESTAMP_COLUMN, "iso", null),
new DimensionsSpec(
ImmutableList.<DimensionSchema>builder()
- .addAll(DimensionsSpec.getDefaultSchemas(ImmutableList.of("dim1", "dim2", "dim3")))
+ .addAll(DimensionsSpec.getDefaultSchemas(ImmutableList.of("dim1", "dim2", "dim3", "dim4", "dim5")))
.add(new DoubleDimensionSchema("d1"))
.add(new DoubleDimensionSchema("d2"))
.add(new FloatDimensionSchema("f1"))
@@ -382,6 +382,8 @@ public class CalciteTests
.put("dim1", "")
.put("dim2", ImmutableList.of("a"))
.put("dim3", ImmutableList.of("a", "b"))
+ .put("dim4", "a")
+ .put("dim5", "aa")
.build(),
PARSER_NUMERIC_DIMS
),
@@ -399,6 +401,8 @@ public class CalciteTests
.put("dim1", "10.1")
.put("dim2", ImmutableList.of())
.put("dim3", ImmutableList.of("b", "c"))
+ .put("dim4", "a")
+ .put("dim5", "ab")
.build(),
PARSER_NUMERIC_DIMS
),
@@ -416,6 +420,8 @@ public class CalciteTests
.put("dim1", "2")
.put("dim2", ImmutableList.of(""))
.put("dim3", ImmutableList.of("d"))
+ .put("dim4", "a")
+ .put("dim5", "ba")
.build(),
PARSER_NUMERIC_DIMS
),
@@ -427,6 +433,8 @@ public class CalciteTests
.put("dim1", "1")
.put("dim2", ImmutableList.of("a"))
.put("dim3", ImmutableList.of(""))
+ .put("dim4", "b")
+ .put("dim5", "ad")
.build(),
PARSER_NUMERIC_DIMS
),
@@ -438,6 +446,8 @@ public class CalciteTests
.put("dim1", "def")
.put("dim2", ImmutableList.of("abc"))
.put("dim3", ImmutableList.of())
+ .put("dim4", "b")
+ .put("dim5", "aa")
.build(),
PARSER_NUMERIC_DIMS
),
@@ -447,6 +457,8 @@ public class CalciteTests
.put("m1", "6.0")
.put("m2", "6.0")
.put("dim1", "abc")
+ .put("dim4", "b")
+ .put("dim5", "ab")
.build(),
PARSER_NUMERIC_DIMS
)
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@druid.apache.org
For additional commands, e-mail: commits-help@druid.apache.org