This is an automated email from the ASF dual-hosted git repository.

gianm 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 62b98500a42 fix: Erroneous pruning for non-STRING typed filters. 
(#19415)
62b98500a42 is described below

commit 62b98500a427d5161643ef84dc194ba2433803c4
Author: Gian Merlino <[email protected]>
AuthorDate: Wed May 6 09:49:32 2026 -0700

    fix: Erroneous pruning for non-STRING typed filters. (#19415)
    
    Fixes #19408 by skipping pruning for RangeFilter with matchValueType
    other than STRING.
---
 .../apache/druid/query/filter/EqualityFilter.java  |  5 +++
 .../org/apache/druid/query/filter/RangeFilter.java | 22 ++++++-----
 .../apache/druid/query/filter/TypedInFilter.java   |  5 +++
 .../query/filter/FilterSegmentPrunerTest.java      | 43 ++++++++++++++++++++++
 .../druid/segment/filter/EqualityFilterTests.java  | 24 +++++++++---
 .../apache/druid/segment/filter/InFilterTests.java | 27 +++++---------
 .../druid/segment/filter/RangeFilterTests.java     | 18 +++++----
 7 files changed, 103 insertions(+), 41 deletions(-)

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 c7a70f03c05..482a96c9906 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
@@ -214,6 +214,11 @@ public class EqualityFilter extends 
AbstractOptimizableDimFilter implements Filt
     if (!Objects.equals(getColumn(), dimension)) {
       return null;
     }
+    // The RangeSet returned here is compared lexicographically against shard 
boundaries (see DimensionRangeShardSpec),
+    // so it is only OK to return one for STRING comparison.
+    if (!matchValueType.is(ValueType.STRING)) {
+      return null;
+    }
     RangeSet<String> retSet = TreeRangeSet.create();
     if (matchValueEval.isArray()) {
       
retSet.add(Range.singleton(Arrays.deepToString(matchValueEval.asArray())));
diff --git 
a/processing/src/main/java/org/apache/druid/query/filter/RangeFilter.java 
b/processing/src/main/java/org/apache/druid/query/filter/RangeFilter.java
index 527b5912208..0d3aeb344ae 100644
--- a/processing/src/main/java/org/apache/druid/query/filter/RangeFilter.java
+++ b/processing/src/main/java/org/apache/druid/query/filter/RangeFilter.java
@@ -271,23 +271,25 @@ public class RangeFilter extends 
AbstractOptimizableDimFilter implements Filter
       return null;
     }
 
-    // We need to return a RangeSet<String>, but we have Object, not String.  
We align with the interface by
-    // converting things to String, but we'd probably be better off adjusting 
the interface to something that is
-    // more type aware in the future
+    // The RangeSet returned here is compared lexicographically against shard 
boundaries (see DimensionRangeShardSpec),
+    // so it is only OK to return one for STRING comparison.
+    if (!matchValueType.is(ValueType.STRING)) {
+      return null;
+    }
 
-    final Supplier<String> lowerString = () -> lowerEval.isArray() ? 
Arrays.deepToString(lowerEval.asArray()) : lowerEval.asString();
-    final Supplier<String> upperString = () -> upperEval.isArray() ? 
Arrays.deepToString(upperEval.asArray()) : upperEval.asString();
-    RangeSet<String> retSet = TreeRangeSet.create();
+    final String lowerString = hasLowerBound() ? lowerEval.asString() : null;
+    final String upperString = hasUpperBound() ? upperEval.asString() : null;
+    final RangeSet<String> retSet = TreeRangeSet.create();
     final Range<String> range;
     if (!hasLowerBound()) {
-      range = isUpperOpen() ? Range.lessThan(upperString.get()) : 
Range.atMost(upperString.get());
+      range = isUpperOpen() ? Range.lessThan(upperString) : 
Range.atMost(upperString);
     } else if (!hasUpperBound()) {
-      range = isLowerOpen() ? Range.greaterThan(lowerString.get()) : 
Range.atLeast(lowerString.get());
+      range = isLowerOpen() ? Range.greaterThan(lowerString) : 
Range.atLeast(lowerString);
     } else {
       range = Range.range(
-          lowerString.get(),
+          lowerString,
           isLowerOpen() ? BoundType.OPEN : BoundType.CLOSED,
-          upperString.get(),
+          upperString,
           isUpperOpen() ? BoundType.OPEN : BoundType.CLOSED
       );
     }
diff --git 
a/processing/src/main/java/org/apache/druid/query/filter/TypedInFilter.java 
b/processing/src/main/java/org/apache/druid/query/filter/TypedInFilter.java
index 247c0dcaada..8bcd3e7075f 100644
--- a/processing/src/main/java/org/apache/druid/query/filter/TypedInFilter.java
+++ b/processing/src/main/java/org/apache/druid/query/filter/TypedInFilter.java
@@ -258,6 +258,11 @@ public class TypedInFilter extends 
AbstractOptimizableDimFilter implements Filte
     if (!Objects.equals(getColumn(), dimension)) {
       return null;
     }
+    // The RangeSet returned here is compared lexicographically against shard 
boundaries (see DimensionRangeShardSpec),
+    // so it is only OK to return one for STRING comparison.
+    if (!matchValueType.is(ValueType.STRING)) {
+      return null;
+    }
     RangeSet<String> retSet = TreeRangeSet.create();
     for (Object value : sortedMatchValues.get()) {
       String valueEquivalent = Evals.asString(value);
diff --git 
a/processing/src/test/java/org/apache/druid/query/filter/FilterSegmentPrunerTest.java
 
b/processing/src/test/java/org/apache/druid/query/filter/FilterSegmentPrunerTest.java
index 63da8751544..d5c0d38beba 100644
--- 
a/processing/src/test/java/org/apache/druid/query/filter/FilterSegmentPrunerTest.java
+++ 
b/processing/src/test/java/org/apache/druid/query/filter/FilterSegmentPrunerTest.java
@@ -286,6 +286,49 @@ class FilterSegmentPrunerTest
                   .verify();
   }
 
+  @Test
+  void testPruneNumericRange()
+  {
+    // Regression test for https://github.com/apache/druid/issues/19408
+    final String interval = "2026-01-01T00:00:00Z/2026-01-02T00:00:00Z";
+    final DataSegment segLargeIds = makeDataSegment(interval, makeRange("id", 
0, "100", "200"));
+
+    final DimFilter filter = new RangeFilter("id", ColumnType.LONG, 80L, null, 
false, false, null);
+    final FilterSegmentPruner pruner = new FilterSegmentPruner(filter, null, 
null);
+
+    Assertions.assertTrue(pruner.include(segLargeIds));
+  }
+
+  @Test
+  void testPruneNumericEquality()
+  {
+    // Regression test for https://github.com/apache/druid/issues/19408. The 
string "1" is not within the
+    // ["1.1", "1.9"] shard range, but the LONG value 1L matches rows with 
values like "1.1" or "1.9", so the
+    // segment must not be pruned.
+    final String interval = "2026-01-01T00:00:00Z/2026-01-02T00:00:00Z";
+    final DataSegment seg = makeDataSegment(interval, makeRange("id", 0, 
"1.1", "1.9"));
+
+    final DimFilter filter = new EqualityFilter("id", ColumnType.LONG, 1L, 
null);
+    final FilterSegmentPruner pruner = new FilterSegmentPruner(filter, null, 
null);
+
+    Assertions.assertTrue(pruner.include(seg));
+  }
+
+  @Test
+  void testPruneNumericIn()
+  {
+    // Regression test for https://github.com/apache/druid/issues/19408. The 
strings "1" and "2" are not within the
+    // ["1.1", "1.9"] shard range, but the LONG values 1L and 2L match rows 
with values like "1.1" or "1.9", so the
+    // segment must not be pruned.
+    final String interval = "2026-01-01T00:00:00Z/2026-01-02T00:00:00Z";
+    final DataSegment seg = makeDataSegment(interval, makeRange("id", 0, 
"1.1", "1.9"));
+
+    final DimFilter filter = new TypedInFilter("id", ColumnType.LONG, 
List.of(1L, 2L), null, null);
+    final FilterSegmentPruner pruner = new FilterSegmentPruner(filter, null, 
null);
+
+    Assertions.assertTrue(pruner.include(seg));
+  }
+
   private ShardSpec makeRange(
       String column,
       int partitionNumber,
diff --git 
a/processing/src/test/java/org/apache/druid/segment/filter/EqualityFilterTests.java
 
b/processing/src/test/java/org/apache/druid/segment/filter/EqualityFilterTests.java
index aa35112b67c..375e16e7fc5 100644
--- 
a/processing/src/test/java/org/apache/druid/segment/filter/EqualityFilterTests.java
+++ 
b/processing/src/test/java/org/apache/druid/segment/filter/EqualityFilterTests.java
@@ -1602,12 +1602,24 @@ public class EqualityFilterTests
       Assert.assertEquals(set, filter.getDimensionRangeSet("x"));
       Assert.assertNull(filter.getDimensionRangeSet("y"));
 
-      ExprEval<?> eval = ExprEval.ofType(ExpressionType.STRING_ARRAY, new 
Object[]{"abc", "def"});
-      filter = new EqualityFilter("x", ColumnType.STRING_ARRAY, eval.value(), 
null);
-      set = TreeRangeSet.create();
-      set.add(Range.singleton(Arrays.deepToString(eval.asArray())));
-      Assert.assertEquals(set, filter.getDimensionRangeSet("x"));
-      Assert.assertNull(filter.getDimensionRangeSet("y"));
+      // Non-STRING match value types must not return a RangeSet.
+      Assert.assertNull(
+          new EqualityFilter("x", ColumnType.LONG, 1L, 
null).getDimensionRangeSet("x")
+      );
+      Assert.assertNull(
+          new EqualityFilter("x", ColumnType.DOUBLE, 1.5, 
null).getDimensionRangeSet("x")
+      );
+      Assert.assertNull(
+          new EqualityFilter("x", ColumnType.FLOAT, 1.5f, 
null).getDimensionRangeSet("x")
+      );
+      Assert.assertNull(
+          new EqualityFilter(
+              "x",
+              ColumnType.STRING_ARRAY,
+              ExprEval.ofType(ExpressionType.STRING_ARRAY, new Object[]{"abc", 
"def"}).value(),
+              null
+          ).getDimensionRangeSet("x")
+      );
     }
 
     @Test
diff --git 
a/processing/src/test/java/org/apache/druid/segment/filter/InFilterTests.java 
b/processing/src/test/java/org/apache/druid/segment/filter/InFilterTests.java
index 67dcdde27af..31d87502363 100644
--- 
a/processing/src/test/java/org/apache/druid/segment/filter/InFilterTests.java
+++ 
b/processing/src/test/java/org/apache/druid/segment/filter/InFilterTests.java
@@ -686,23 +686,16 @@ public class InFilterTests
       RangeSet<String> range = filter.getDimensionRangeSet("x");
       Assert.assertTrue(range.contains("b"));
 
-      filter = inFilter("x", ColumnType.LONG, Arrays.asList(null, 1L, 2L, 3L));
-      filter2 = inFilter("x", ColumnType.LONG, Arrays.asList(3L, 1L, null, 
2L));
-      Assert.assertEquals(filter.getDimensionRangeSet("x"), 
filter2.getDimensionRangeSet("x"));
-      range = filter.getDimensionRangeSet("x");
-      Assert.assertTrue(range.contains("2"));
-
-      filter = inFilter("x", ColumnType.DOUBLE, Arrays.asList(null, 1.1, 2.2, 
3.3));
-      filter2 = inFilter("x", ColumnType.DOUBLE, Arrays.asList(3.3, 1.1, null, 
2.2));
-      range = filter.getDimensionRangeSet("x");
-      Assert.assertEquals(filter.getDimensionRangeSet("x"), 
filter2.getDimensionRangeSet("x"));
-      Assert.assertTrue(range.contains("2.2"));
-
-      filter = inFilter("x", ColumnType.FLOAT, Arrays.asList(null, 1.1f, 2.2f, 
3.3f));
-      filter2 = inFilter("x", ColumnType.FLOAT, Arrays.asList(3.3f, 1.1f, 
null, 2.2f));
-      range = filter.getDimensionRangeSet("x");
-      Assert.assertEquals(filter.getDimensionRangeSet("x"), 
filter2.getDimensionRangeSet("x"));
-      Assert.assertTrue(range.contains("2.2"));
+      // Non-STRING match value types must not return a RangeSet.
+      Assert.assertNull(
+          inFilter("x", ColumnType.LONG, Arrays.asList(null, 1L, 2L, 
3L)).getDimensionRangeSet("x")
+      );
+      Assert.assertNull(
+          inFilter("x", ColumnType.DOUBLE, Arrays.asList(null, 1.1, 2.2, 
3.3)).getDimensionRangeSet("x")
+      );
+      Assert.assertNull(
+          inFilter("x", ColumnType.FLOAT, Arrays.asList(null, 1.1f, 2.2f, 
3.3f)).getDimensionRangeSet("x")
+      );
     }
 
     @Test
diff --git 
a/processing/src/test/java/org/apache/druid/segment/filter/RangeFilterTests.java
 
b/processing/src/test/java/org/apache/druid/segment/filter/RangeFilterTests.java
index da3f8b31aa9..efef85c952c 100644
--- 
a/processing/src/test/java/org/apache/druid/segment/filter/RangeFilterTests.java
+++ 
b/processing/src/test/java/org/apache/druid/segment/filter/RangeFilterTests.java
@@ -35,8 +35,6 @@ import org.apache.druid.error.DruidException;
 import org.apache.druid.jackson.DefaultObjectMapper;
 import org.apache.druid.java.util.common.IAE;
 import org.apache.druid.java.util.common.Pair;
-import org.apache.druid.math.expr.ExprEval;
-import org.apache.druid.math.expr.ExpressionType;
 import org.apache.druid.query.filter.Filter;
 import org.apache.druid.query.filter.FilterTuning;
 import org.apache.druid.query.filter.NotDimFilter;
@@ -2035,12 +2033,16 @@ public class RangeFilterTests
       Assert.assertEquals(set, filter.getDimensionRangeSet("x"));
       Assert.assertNull(filter.getDimensionRangeSet("y"));
 
-      ExprEval<?> evalLower = ExprEval.ofType(ExpressionType.STRING_ARRAY, new 
Object[]{"abc", "def"});
-      filter = new RangeFilter("x", ColumnType.STRING_ARRAY, 
evalLower.value(), null, true, false, null);
-      set = TreeRangeSet.create();
-      set.add(Range.greaterThan(Arrays.deepToString(evalLower.asArray())));
-      Assert.assertEquals(set, filter.getDimensionRangeSet("x"));
-      Assert.assertNull(filter.getDimensionRangeSet("y"));
+      // Non-STRING match value types must not return a RangeSet.
+      Assert.assertNull(
+          new RangeFilter("x", ColumnType.LONG, 80L, null, false, false, 
null).getDimensionRangeSet("x")
+      );
+      Assert.assertNull(
+          new RangeFilter("x", ColumnType.DOUBLE, 1.5, 2.5, false, false, 
null).getDimensionRangeSet("x")
+      );
+      Assert.assertNull(
+          new RangeFilter("x", ColumnType.FLOAT, null, 7.5f, false, true, 
null).getDimensionRangeSet("x")
+      );
     }
 
     @Test


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to