diff --git a/docs/querying/sql-data-types.md b/docs/querying/sql-data-types.md index ad6869c5d2e9..e1b7454931f6 100644 --- a/docs/querying/sql-data-types.md +++ b/docs/querying/sql-data-types.md @@ -155,9 +155,9 @@ values are treated as zeroes. This was the default prior to Druid 28.0.0. The [`druid.expressions.useStrictBooleans`](../configuration/index.md#expression-processing-configurations) runtime property controls Druid's boolean logic mode. For the most SQL compliant behavior, set this to `true` (the default). -When `druid.expressions.useStrictBooleans = true`, Druid uses three-valued logic for +When `druid.expressions.useStrictBooleans = true`, Druid uses [three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic#SQL) for [expressions](math-expr.md) evaluation, such as `expression` virtual columns or `expression` filters. -If `druid.generic.useDefaultValueForNull = false` in addition to `druid.expressions.useStrictBooleans = true`, Druid also uses three-valued logic for native filters. +If `druid.generic.useDefaultValueForNull = false` (in combination with `druid.expressions.useStrictBooleans = true`), Druid also uses three-valued logic for native filters. When `druid.expressions.useStrictBooleans = false` (legacy mode), Druid uses two-valued logic for expressions, and if `druid.generic.useDefaultValueForNull = true`, Druid uses two-valued logic for native filters, even if `druid.expressions.useStrictBooleans = true`. diff --git a/extensions-core/druid-bloom-filter/src/main/java/org/apache/druid/query/filter/BloomDimFilter.java b/extensions-core/druid-bloom-filter/src/main/java/org/apache/druid/query/filter/BloomDimFilter.java index b8e98c0a75b2..159d62b292b5 100644 --- a/extensions-core/druid-bloom-filter/src/main/java/org/apache/druid/query/filter/BloomDimFilter.java +++ b/extensions-core/druid-bloom-filter/src/main/java/org/apache/druid/query/filter/BloomDimFilter.java @@ -98,6 +98,8 @@ public Filter toFilter() dimension, new DruidPredicateFactory() { + private final boolean isNullUnknown = !bloomKFilter.testBytes(null, 0, 0); + @Override public Predicate makeStringPredicate() { @@ -169,7 +171,7 @@ public boolean applyNull() @Override public boolean isNullInputUnknown() { - return !bloomKFilter.testBytes(null, 0, 0); + return isNullUnknown; } }, extractionFn, diff --git a/processing/src/main/java/org/apache/druid/query/filter/DruidPredicateFactory.java b/processing/src/main/java/org/apache/druid/query/filter/DruidPredicateFactory.java index 99a3d508a25b..19a7bf24abb4 100644 --- a/processing/src/main/java/org/apache/druid/query/filter/DruidPredicateFactory.java +++ b/processing/src/main/java/org/apache/druid/query/filter/DruidPredicateFactory.java @@ -22,6 +22,8 @@ import com.google.common.base.Predicate; import org.apache.druid.annotations.SubclassesMustOverrideEqualsAndHashCode; import org.apache.druid.java.util.common.UOE; +import org.apache.druid.query.BitmapResultFactory; +import org.apache.druid.query.filter.vector.ReadableVectorMatch; import org.apache.druid.segment.column.TypeSignature; import org.apache.druid.segment.column.ValueType; @@ -59,6 +61,16 @@ default Predicate makeObjectPredicate() return o -> stringPredicate.apply(null); } + /** + * Indicator for if null inputs should be considered 'unknown' matches when used for filter matching with + * {@link ValueMatcher#matches(boolean)}, + * {@link org.apache.druid.query.filter.vector.VectorValueMatcher#match(ReadableVectorMatch, boolean)}, or + * {@link org.apache.druid.segment.index.BitmapColumnIndex#computeBitmapResult(BitmapResultFactory, boolean)}. + * + * If returns true, unknown (null) inputs can automatically be considered matches if {@code includeUnknown} is set + * to true on these methods, else null inputs should be evaluated against the predicate as any other value to + * determine a match + */ default boolean isNullInputUnknown() { return true; diff --git a/processing/src/main/java/org/apache/druid/query/filter/EqualityFilter.java b/processing/src/main/java/org/apache/druid/query/filter/EqualityFilter.java index 3d8da3df0be7..636f95fecf56 100644 --- a/processing/src/main/java/org/apache/druid/query/filter/EqualityFilter.java +++ b/processing/src/main/java/org/apache/druid/query/filter/EqualityFilter.java @@ -537,7 +537,7 @@ public ValueMatcher makeDimensionProcessor(DimensionSelector selector, boolean m { final ExprEval castForComparison = ExprEval.castForEqualityComparison(matchValue, ExpressionType.STRING); if (castForComparison == null) { - return ValueMatchers.makeAlwaysFalseMatcher(selector, multiValue); + return ValueMatchers.makeAlwaysFalseDimensionMatcher(selector, multiValue); } return ValueMatchers.makeStringValueMatcher(selector, castForComparison.asString(), multiValue); } @@ -547,7 +547,7 @@ public ValueMatcher makeFloatProcessor(BaseFloatColumnValueSelector selector) { final ExprEval castForComparison = ExprEval.castForEqualityComparison(matchValue, ExpressionType.DOUBLE); if (castForComparison == null) { - return ValueMatchers.makeAlwaysFalseMatcher(selector); + return ValueMatchers.makeAlwaysFalseNumericMatcher(selector); } return ValueMatchers.makeFloatValueMatcher(selector, (float) castForComparison.asDouble()); } @@ -557,7 +557,7 @@ public ValueMatcher makeDoubleProcessor(BaseDoubleColumnValueSelector selector) { final ExprEval castForComparison = ExprEval.castForEqualityComparison(matchValue, ExpressionType.DOUBLE); if (castForComparison == null) { - return ValueMatchers.makeAlwaysFalseMatcher(selector); + return ValueMatchers.makeAlwaysFalseNumericMatcher(selector); } return ValueMatchers.makeDoubleValueMatcher(selector, castForComparison.asDouble()); } @@ -567,7 +567,7 @@ public ValueMatcher makeLongProcessor(BaseLongColumnValueSelector selector) { final ExprEval castForComparison = ExprEval.castForEqualityComparison(matchValue, ExpressionType.LONG); if (castForComparison == null) { - return ValueMatchers.makeAlwaysFalseMatcher(selector); + return ValueMatchers.makeAlwaysFalseNumericMatcher(selector); } return ValueMatchers.makeLongValueMatcher(selector, castForComparison.asLong()); } diff --git a/processing/src/main/java/org/apache/druid/query/filter/InDimFilter.java b/processing/src/main/java/org/apache/druid/query/filter/InDimFilter.java index 4ad40566b54f..479e1c9aed11 100644 --- a/processing/src/main/java/org/apache/druid/query/filter/InDimFilter.java +++ b/processing/src/main/java/org/apache/druid/query/filter/InDimFilter.java @@ -564,6 +564,7 @@ public static class InFilterDruidPredicateFactory implements DruidPredicateFacto private final Supplier longPredicateSupplier; private final Supplier floatPredicateSupplier; private final Supplier doublePredicateSupplier; + private final boolean hasNull; public InFilterDruidPredicateFactory( final ExtractionFn extractionFn, @@ -572,6 +573,7 @@ public InFilterDruidPredicateFactory( { this.extractionFn = extractionFn; this.values = values; + this.hasNull = !values.contains(null); // As the set of filtered values can be large, parsing them as numbers should be done only if needed, and // only once. Pass in a common long predicate supplier to all filters created by .toFilter(), so that we only @@ -630,7 +632,7 @@ public DruidDoublePredicate makeDoublePredicate() @Override public boolean isNullInputUnknown() { - return !values.contains(null); + return hasNull; } @Override diff --git a/processing/src/main/java/org/apache/druid/query/filter/SelectorPredicateFactory.java b/processing/src/main/java/org/apache/druid/query/filter/SelectorPredicateFactory.java index 94386b22a535..538b26474ab0 100644 --- a/processing/src/main/java/org/apache/druid/query/filter/SelectorPredicateFactory.java +++ b/processing/src/main/java/org/apache/druid/query/filter/SelectorPredicateFactory.java @@ -40,10 +40,12 @@ public class SelectorPredicateFactory implements DruidPredicateFactory private volatile DruidLongPredicate longPredicate; private volatile DruidFloatPredicate floatPredicate; private volatile DruidDoublePredicate doublePredicate; + private final boolean isNullUnknown; public SelectorPredicateFactory(@Nullable String value) { this.value = value; + this.isNullUnknown = value != null; } @Override @@ -76,7 +78,7 @@ public DruidDoublePredicate makeDoublePredicate() @Override public boolean isNullInputUnknown() { - return value != null; + return isNullUnknown; } private void initLongPredicate() diff --git a/processing/src/main/java/org/apache/druid/query/filter/ValueMatcher.java b/processing/src/main/java/org/apache/druid/query/filter/ValueMatcher.java index 598af108fba3..168b9d3860be 100644 --- a/processing/src/main/java/org/apache/druid/query/filter/ValueMatcher.java +++ b/processing/src/main/java/org/apache/druid/query/filter/ValueMatcher.java @@ -37,7 +37,7 @@ public interface ValueMatcher extends HotLoopCallee * @param includeUnknown mapping for Druid native two state logic system into SQL three-state logic system. If set * to true, this method should also return true if the result is 'unknown' to be a match, such * as from the input being null valued. Used primarily to allow - * {@link org.apache.druid.segment.filter.NotFilter} to invert a match in an SQL compliant + * {@link org.apache.druid.segment.filter.NotFilter} to invert a match in a SQL compliant * manner * @return true if the current row matches the condition, or is unknown and {@code includeUnknown} is set to true */ diff --git a/processing/src/main/java/org/apache/druid/query/filter/vector/VectorValueMatcher.java b/processing/src/main/java/org/apache/druid/query/filter/vector/VectorValueMatcher.java index df238c104eeb..bf418ebe0517 100644 --- a/processing/src/main/java/org/apache/druid/query/filter/vector/VectorValueMatcher.java +++ b/processing/src/main/java/org/apache/druid/query/filter/vector/VectorValueMatcher.java @@ -178,8 +178,8 @@ public ReadableVectorMatch match(ReadableVectorMatch mask, boolean includeUnknow if (row.size() == 0) { selection[numRows++] = rowNum; } else { - //noinspection SSBasedInspection - for (int j = 0; j < row.size(); j++) { + final int size = row.size(); + for (int j = 0; j < size; j++) { if (NullHandling.isNullOrEquivalent(selector.lookupName(row.get(j)))) { selection[numRows++] = rowNum; break; @@ -216,8 +216,8 @@ public ReadableVectorMatch match(ReadableVectorMatch mask, boolean includeUnknow if (row.size() == 0) { selection[numRows++] = rowNum; } else { - //noinspection SSBasedInspection - for (int j = 0; j < row.size(); j++) { + final int size = row.size(); + for (int j = 0; j < size; j++) { if (row.get(j) == nullId) { selection[numRows++] = rowNum; break; diff --git a/processing/src/main/java/org/apache/druid/segment/DimensionSelector.java b/processing/src/main/java/org/apache/druid/segment/DimensionSelector.java index e143317724e1..ebee86246cdd 100644 --- a/processing/src/main/java/org/apache/druid/segment/DimensionSelector.java +++ b/processing/src/main/java/org/apache/druid/segment/DimensionSelector.java @@ -187,9 +187,6 @@ static DimensionSelector multiConstant(@Nullable final List values) // does not report value cardinality, but otherwise behaves identically when used for grouping or selecting to a // normal multi-value dimension selector (getObject on a row with a single value returns the object instead of // the list) - if (values.get(0) == null) { - return NullDimensionSelectorHolder.NULL_DIMENSION_SELECTOR; - } return constant(values.get(0)); } else { return new ConstantMultiValueDimensionSelector(values); diff --git a/processing/src/main/java/org/apache/druid/segment/StringDimensionIndexer.java b/processing/src/main/java/org/apache/druid/segment/StringDimensionIndexer.java index aa0620b2b9e1..b887077027e3 100644 --- a/processing/src/main/java/org/apache/druid/segment/StringDimensionIndexer.java +++ b/processing/src/main/java/org/apache/druid/segment/StringDimensionIndexer.java @@ -391,11 +391,11 @@ public boolean matches(boolean includeUnknown) { if (includeUnknown) { IndexedInts row = getRow(); - if (row.size() == 0) { + final int size = row.size(); + if (size == 0) { return true; } - //noinspection SSBasedInspection - for (int i = 0; i < row.size(); i++) { + for (int i = 0; i < size; i++) { if (row.get(i) == nullValueId) { return true; } diff --git a/processing/src/main/java/org/apache/druid/segment/column/StringUtf8DictionaryEncodedColumn.java b/processing/src/main/java/org/apache/druid/segment/column/StringUtf8DictionaryEncodedColumn.java index b8f55c8f0a40..e91f264ac173 100644 --- a/processing/src/main/java/org/apache/druid/segment/column/StringUtf8DictionaryEncodedColumn.java +++ b/processing/src/main/java/org/apache/druid/segment/column/StringUtf8DictionaryEncodedColumn.java @@ -20,6 +20,7 @@ package org.apache.druid.segment.column; import com.google.common.base.Predicate; +import com.google.common.collect.Lists; import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.query.extraction.ExtractionFn; import org.apache.druid.query.filter.DruidPredicateFactory; @@ -798,14 +799,14 @@ public Object[] getObjectVector() for (int i = 0; i < offset.getCurrentVectorSize(); i++) { IndexedInts ithRow = vector[i]; - if (ithRow.size() == 0) { + final int size = ithRow.size(); + if (size == 0) { strings[i] = null; - } else if (ithRow.size() == 1) { + } else if (size == 1) { strings[i] = lookupName(ithRow.get(0)); } else { - List row = new ArrayList<>(ithRow.size()); - // noinspection SSBasedInspection - for (int j = 0; j < ithRow.size(); j++) { + List row = Lists.newArrayListWithCapacity(size); + for (int j = 0; j < size; j++) { row.add(lookupName(ithRow.get(j))); } strings[i] = row; diff --git a/processing/src/main/java/org/apache/druid/segment/filter/BoundFilter.java b/processing/src/main/java/org/apache/druid/segment/filter/BoundFilter.java index ac4117047ae5..a75da57654eb 100644 --- a/processing/src/main/java/org/apache/druid/segment/filter/BoundFilter.java +++ b/processing/src/main/java/org/apache/druid/segment/filter/BoundFilter.java @@ -343,6 +343,7 @@ static class BoundDimFilterDruidPredicateFactory implements DruidPredicateFactor private final Supplier longPredicateSupplier; private final Supplier floatPredicateSupplier; private final Supplier doublePredicateSupplier; + private final boolean isNullUnknown; BoundDimFilterDruidPredicateFactory(ExtractionFn extractionFn, BoundDimFilter boundDimFilter) { @@ -351,6 +352,11 @@ static class BoundDimFilterDruidPredicateFactory implements DruidPredicateFactor this.longPredicateSupplier = boundDimFilter.getLongPredicateSupplier(); this.floatPredicateSupplier = boundDimFilter.getFloatPredicateSupplier(); this.doublePredicateSupplier = boundDimFilter.getDoublePredicateSupplier(); + if (extractionFn != null) { + this.isNullUnknown = !doesMatch(extractionFn.apply(null), boundDimFilter); + } else { + this.isNullUnknown = !doesMatch(null, boundDimFilter); + } } @Override @@ -402,10 +408,7 @@ public DruidDoublePredicate makeDoublePredicate() @Override public boolean isNullInputUnknown() { - if (extractionFn != null) { - return !doesMatch(extractionFn.apply(null), boundDimFilter); - } - return !doesMatch(null, boundDimFilter); + return isNullUnknown; } @Override diff --git a/processing/src/main/java/org/apache/druid/segment/filter/ColumnComparisonFilter.java b/processing/src/main/java/org/apache/druid/segment/filter/ColumnComparisonFilter.java index a49a3e58d8b3..ed1590dc16ef 100644 --- a/processing/src/main/java/org/apache/druid/segment/filter/ColumnComparisonFilter.java +++ b/processing/src/main/java/org/apache/druid/segment/filter/ColumnComparisonFilter.java @@ -48,6 +48,11 @@ import java.util.function.Supplier; import java.util.stream.Collectors; +/** + * Compares values between columns, first converting them all to strings. This filter behaves like "not distinct from", + * e.g. given columns x and y, the SQL equivalent would be "x is not distinct from y" (and so ignores + * {@code includeUnknown}). + */ public class ColumnComparisonFilter implements Filter { private final List dimensions; @@ -87,8 +92,6 @@ public static ValueMatcher makeValueMatcher(final List> value @Override public boolean matches(boolean includeUnknown) { - // todo (clint): what to do about includeUnknown - // Keep all values to compare against each other. String[][] values = new String[valueGetters.size()][]; diff --git a/processing/src/main/java/org/apache/druid/segment/filter/DimensionPredicateFilter.java b/processing/src/main/java/org/apache/druid/segment/filter/DimensionPredicateFilter.java index d7b649fc5bf9..f321691e033d 100644 --- a/processing/src/main/java/org/apache/druid/segment/filter/DimensionPredicateFilter.java +++ b/processing/src/main/java/org/apache/druid/segment/filter/DimensionPredicateFilter.java @@ -146,12 +146,14 @@ static class DelegatingStringPredicateFactory implements DruidPredicateFactory private final Predicate baseStringPredicate; private final DruidPredicateFactory predicateFactory; private final ExtractionFn extractionFn; + private final boolean isNullUnknown; DelegatingStringPredicateFactory(DruidPredicateFactory predicateFactory, ExtractionFn extractionFn) { this.predicateFactory = predicateFactory; this.baseStringPredicate = predicateFactory.makeStringPredicate(); this.extractionFn = extractionFn; + this.isNullUnknown = !baseStringPredicate.apply(extractionFn.apply(null)); } @Override @@ -220,7 +222,7 @@ public boolean applyNull() @Override public boolean isNullInputUnknown() { - return !baseStringPredicate.apply(extractionFn.apply(null)); + return isNullUnknown; } @Override diff --git a/processing/src/main/java/org/apache/druid/segment/filter/PredicateValueMatcherFactory.java b/processing/src/main/java/org/apache/druid/segment/filter/PredicateValueMatcherFactory.java index 9fb133e22a4e..796177a4c79d 100644 --- a/processing/src/main/java/org/apache/druid/segment/filter/PredicateValueMatcherFactory.java +++ b/processing/src/main/java/org/apache/druid/segment/filter/PredicateValueMatcherFactory.java @@ -99,7 +99,7 @@ public ValueMatcher makeArrayProcessor( if (matchesNull) { return ValueMatchers.allTrue(); } - return ValueMatchers.makeAlwaysFalseMatcher(selector); + return ValueMatchers.makeAlwaysFalseObjectMatcher(selector); } else { // use the object predicate final Predicate predicate = predicateFactory.makeArrayPredicate(columnCapabilities); @@ -140,7 +140,7 @@ public ValueMatcher makeComplexProcessor(BaseObjectColumnValueSelector select if (predicateMatches) { return ValueMatchers.allTrue(); } - return ValueMatchers.makeAlwaysFalseMatcher(selector); + return ValueMatchers.makeAlwaysFalseObjectMatcher(selector); } else if (!isNumberOrString(selector.classOfObject())) { // if column is definitely not a number of string, use the object predicate final Predicate predicate = predicateFactory.makeObjectPredicate(); diff --git a/processing/src/main/java/org/apache/druid/segment/filter/ValueMatchers.java b/processing/src/main/java/org/apache/druid/segment/filter/ValueMatchers.java index 1db50b55a7c5..9b4ae9e58683 100644 --- a/processing/src/main/java/org/apache/druid/segment/filter/ValueMatchers.java +++ b/processing/src/main/java/org/apache/druid/segment/filter/ValueMatchers.java @@ -52,16 +52,26 @@ private ValueMatchers() // No instantiation. } + /** + * Matcher for constant 'true' condition, where all rows should always match + */ public static ValueMatcher allTrue() { return AllTrueValueMatcher.instance(); } + /** + * Matcher for constant 'false' condition, where rows will never be matched + */ public static ValueMatcher allFalse() { return AllFalseValueMatcher.instance(); } + /** + * Matcher for constant 'unknown' condition, such as a column of all null values, where rows will never match + * unless {@code includeUnknown} is set to true on the match function. + */ public static ValueMatcher allUnknown() { return AllUnknownValueMatcher.instance(); @@ -170,76 +180,93 @@ public void inspectRuntimeShape(RuntimeShapeInspector inspector) }; } - public static ValueMatcher makeLongValueMatcher(final BaseLongColumnValueSelector selector, final String value) - { - final Long matchVal = DimensionHandlerUtils.convertObjectToLong(value); - if (matchVal == null) { - return makeNumericNullValueMatcher(selector); - } - return makeLongValueMatcher(selector, matchVal); - } - - public static ValueMatcher makeLongValueMatcher(final BaseLongColumnValueSelector selector, long value) + /** + * Creates a predicate-based {@link ValueMatcher} for a float-typed selector. + * + * @param selector column selector + * @param predicateFactory predicate to match + */ + public static ValueMatcher makeFloatValueMatcher( + final BaseFloatColumnValueSelector selector, + final DruidPredicateFactory predicateFactory + ) { + final DruidFloatPredicate predicate = predicateFactory.makeFloatPredicate(); return new ValueMatcher() { @Override public boolean matches(boolean includeUnknown) { + final boolean matchNull = includeUnknown && predicateFactory.isNullInputUnknown(); if (selector.isNull()) { - return includeUnknown; + return matchNull || predicate.applyNull(); } - return selector.getLong() == value; + return predicate.applyFloat(selector.getFloat()); } @Override public void inspectRuntimeShape(RuntimeShapeInspector inspector) { inspector.visit("selector", selector); + inspector.visit("predicate", predicate); } }; } - public static ValueMatcher makeLongValueMatcher( - final BaseLongColumnValueSelector selector, - final DruidPredicateFactory predicateFactory - ) + /** + * Creates a constant-based {@link ValueMatcher} for a long-typed selector. + * + * @param selector column selector + * @param value value to match + */ + public static ValueMatcher makeLongValueMatcher(final BaseLongColumnValueSelector selector, final String value) + { + final Long matchVal = DimensionHandlerUtils.convertObjectToLong(value); + if (matchVal == null) { + return makeNumericNullValueMatcher(selector); + } + return makeLongValueMatcher(selector, matchVal); + } + + /** + * Creates a constant-based {@link ValueMatcher} for a long-typed selector. + * + * @param selector column selector + * @param value value to match + */ + public static ValueMatcher makeLongValueMatcher(final BaseLongColumnValueSelector selector, long value) { - final DruidLongPredicate predicate = predicateFactory.makeLongPredicate(); return new ValueMatcher() { @Override public boolean matches(boolean includeUnknown) { - final boolean matchNull = includeUnknown && predicateFactory.isNullInputUnknown(); if (selector.isNull()) { - return matchNull || predicate.applyNull(); + return includeUnknown; } - return predicate.applyLong(selector.getLong()); + return selector.getLong() == value; } @Override public void inspectRuntimeShape(RuntimeShapeInspector inspector) { inspector.visit("selector", selector); - inspector.visit("predicate", predicate); } }; } - /** - * Creates a predicate-based {@link ValueMatcher} for a float-typed selector. + * Creates a predicate-based {@link ValueMatcher} for a long-typed selector. * * @param selector column selector * @param predicateFactory predicate to match */ - public static ValueMatcher makeFloatValueMatcher( - final BaseFloatColumnValueSelector selector, + public static ValueMatcher makeLongValueMatcher( + final BaseLongColumnValueSelector selector, final DruidPredicateFactory predicateFactory ) { - final DruidFloatPredicate predicate = predicateFactory.makeFloatPredicate(); + final DruidLongPredicate predicate = predicateFactory.makeLongPredicate(); return new ValueMatcher() { @Override @@ -249,7 +276,7 @@ public boolean matches(boolean includeUnknown) if (selector.isNull()) { return matchNull || predicate.applyNull(); } - return predicate.applyFloat(selector.getFloat()); + return predicate.applyLong(selector.getLong()); } @Override @@ -261,6 +288,7 @@ public void inspectRuntimeShape(RuntimeShapeInspector inspector) }; } + /** * Creates a constant-based {@link ValueMatcher} for a double-typed selector. * @@ -280,6 +308,12 @@ public static ValueMatcher makeDoubleValueMatcher( return makeDoubleValueMatcher(selector, matchVal); } + /** + * Creates a constant-based {@link ValueMatcher} for a double-typed selector. + * + * @param selector column selector + * @param value value to match + */ public static ValueMatcher makeDoubleValueMatcher( final BaseDoubleColumnValueSelector selector, final double value @@ -339,7 +373,12 @@ public void inspectRuntimeShape(RuntimeShapeInspector inspector) }; } - public static ValueMatcher makeAlwaysFalseMatcher(final DimensionSelector selector, boolean multiValue) + /** + * Create a matcher that should always return false, except when {@code includeUnknown} is set, in which case only + * null values will be matched. This is typically used when the filter should never match any actual values, but + * still needs to be able to report 'unknown' matches. + */ + public static ValueMatcher makeAlwaysFalseDimensionMatcher(final DimensionSelector selector, boolean multiValue) { final IdLookup lookup = selector.idLookup(); // if the column doesn't have null @@ -351,11 +390,11 @@ public boolean matches(boolean includeUnknown) { if (includeUnknown) { IndexedInts row = selector.getRow(); - if (row.size() == 0) { + final int size = row.size(); + if (size == 0) { return true; } - //noinspection SSBasedInspection - for (int i = 0; i < row.size(); i++) { + for (int i = 0; i < size; i++) { if (NullHandling.isNullOrEquivalent(selector.lookupName(row.get(i)))) { return true; } @@ -384,11 +423,11 @@ public boolean matches(boolean includeUnknown) { if (includeUnknown) { IndexedInts row = selector.getRow(); - if (row.size() == 0) { + final int size = row.size(); + if (size == 0) { return true; } - //noinspection SSBasedInspection - for (int i = 0; i < row.size(); i++) { + for (int i = 0; i < size; i++) { if (row.get(i) == nullId) { return true; } @@ -422,7 +461,12 @@ public void inspectRuntimeShape(RuntimeShapeInspector inspector) } } - public static ValueMatcher makeAlwaysFalseMatcher(BaseNullableColumnValueSelector selector) + /** + * Create a matcher that should always return false, except when {@code includeUnknown} is set, in which case only + * null values will be matched. This is typically used when the filter should never match any actual values, but + * still needs to be able to report 'unknown' matches. + */ + public static ValueMatcher makeAlwaysFalseNumericMatcher(BaseNullableColumnValueSelector selector) { return new ValueMatcher() { @@ -440,7 +484,12 @@ public void inspectRuntimeShape(RuntimeShapeInspector inspector) }; } - public static ValueMatcher makeAlwaysFalseMatcher(BaseObjectColumnValueSelector selector) + /** + * Create a matcher that should always return false, except when {@code includeUnknown} is set, in which case only + * null values will be matched. This is typically used when the filter should never match any actual values, but + * still needs to be able to report 'unknown' matches. + */ + public static ValueMatcher makeAlwaysFalseObjectMatcher(BaseObjectColumnValueSelector selector) { return new ValueMatcher() { diff --git a/processing/src/main/java/org/apache/druid/segment/vector/FilteredVectorOffset.java b/processing/src/main/java/org/apache/druid/segment/vector/FilteredVectorOffset.java index 327fa6490dd6..c6da18edb8b8 100644 --- a/processing/src/main/java/org/apache/druid/segment/vector/FilteredVectorOffset.java +++ b/processing/src/main/java/org/apache/druid/segment/vector/FilteredVectorOffset.java @@ -129,8 +129,10 @@ private void advanceWhileVectorIsEmptyAndPopulateOffsets() return; } - final ReadableVectorMatch match = filterMatcher.match(VectorMatch.allTrue(baseOffset.getCurrentVectorSize()), - false); + final ReadableVectorMatch match = filterMatcher.match( + VectorMatch.allTrue(baseOffset.getCurrentVectorSize()), + false + ); if (match.isAllTrue(baseOffset.getCurrentVectorSize())) { currentVectorSize = baseOffset.getCurrentVectorSize(); diff --git a/processing/src/main/java/org/apache/druid/segment/virtual/ExpressionSelectors.java b/processing/src/main/java/org/apache/druid/segment/virtual/ExpressionSelectors.java index dedc5a241d7c..6597472b47b7 100644 --- a/processing/src/main/java/org/apache/druid/segment/virtual/ExpressionSelectors.java +++ b/processing/src/main/java/org/apache/druid/segment/virtual/ExpressionSelectors.java @@ -435,17 +435,17 @@ static Supplier supplierFromDimensionSelector(final DimensionSelector se if (row.size() == 1 && !coerceArray) { return selector.lookupName(row.get(0)); } else { + final int size = row.size(); // column selector factories hate you and use [] and [null] interchangeably for nullish data - if (row.size() == 0 || (row.size() == 1 && selector.getObject() == null)) { + if (size == 0 || (size == 1 && selector.getObject() == null)) { if (homogenize) { return new Object[]{null}; } else { return null; } } - final Object[] strings = new Object[row.size()]; - // noinspection SSBasedInspection - for (int i = 0; i < row.size(); i++) { + final Object[] strings = new Object[size]; + for (int i = 0; i < size; i++) { strings[i] = selector.lookupName(row.get(i)); } return strings;