This is an automated email from the ASF dual-hosted git repository.
JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git
The following commit(s) were added to refs/heads/master by this push:
new b24751731a [format] Drop ORC float bounds that hide NaN or -0.0
(#10337)
b24751731a is described below
commit b24751731a22fa9788205897b6554f247a3fe300
Author: Burak Kalaycı <[email protected]>
AuthorDate: Mon Oct 12 16:47:07 2026 +0300
[format] Drop ORC float bounds that hide NaN or -0.0 (#10337)
---
.../apache/paimon/table/OrcSignedZeroReadTest.java | 199 +++++++++++++++++++++
.../orc/filter/OrcPredicateFunctionVisitor.java | 28 ++-
.../format/orc/filter/OrcSimpleStatsExtractor.java | 29 +++
.../format/orc/OrcFloatingPointStatsTest.java | 160 +++++++++++++++++
.../format/orc/OrcSimpleStatsExtractorTest.java | 17 ++
.../format/orc/filter/OrcFilterConverterTest.java | 28 +++
6 files changed, 460 insertions(+), 1 deletion(-)
diff --git
a/paimon-core/src/test/java/org/apache/paimon/table/OrcSignedZeroReadTest.java
b/paimon-core/src/test/java/org/apache/paimon/table/OrcSignedZeroReadTest.java
new file mode 100644
index 0000000000..75d157d15a
--- /dev/null
+++
b/paimon-core/src/test/java/org/apache/paimon/table/OrcSignedZeroReadTest.java
@@ -0,0 +1,199 @@
+/*
+ * 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.paimon.table;
+
+import org.apache.paimon.append.AppendCompactTask;
+import org.apache.paimon.data.BinaryRow;
+import org.apache.paimon.data.GenericRow;
+import org.apache.paimon.data.InternalRow;
+import org.apache.paimon.fs.Path;
+import org.apache.paimon.fs.local.LocalFileIO;
+import org.apache.paimon.io.DataFileMeta;
+import org.apache.paimon.operation.BaseAppendFileStoreWrite;
+import org.apache.paimon.operation.FileStoreWrite;
+import org.apache.paimon.predicate.Predicate;
+import org.apache.paimon.predicate.PredicateBuilder;
+import org.apache.paimon.reader.RecordReader;
+import org.apache.paimon.schema.FileSystemSchemaManager;
+import org.apache.paimon.schema.Schema;
+import org.apache.paimon.schema.SchemaUtils;
+import org.apache.paimon.schema.TableSchema;
+import org.apache.paimon.table.sink.BatchTableCommit;
+import org.apache.paimon.table.sink.BatchTableWrite;
+import org.apache.paimon.table.sink.BatchWriteBuilder;
+import org.apache.paimon.table.sink.CommitMessage;
+import org.apache.paimon.table.sink.TableWriteImpl;
+import org.apache.paimon.table.source.DataSplit;
+import org.apache.paimon.table.source.ReadBuilder;
+import org.apache.paimon.table.source.Split;
+import org.apache.paimon.types.DataField;
+import org.apache.paimon.types.DataType;
+import org.apache.paimon.types.DataTypes;
+import org.apache.paimon.types.RowType;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Table reads for an ORC append file whose footer max is {@code -0.0} while a
{@code +0.0} row is
+ * still present. File stats and ORC SearchArgument both have to leave that
row visible.
+ */
+class OrcSignedZeroReadTest {
+
+ @TempDir java.nio.file.Path tempDir;
+
+ @ParameterizedTest
+ @ValueSource(strings = {"float", "double"})
+ public void testPositiveZeroSurvivesNegativeZeroUpperBound(String
typeName) throws Exception {
+ boolean isFloat = "float".equals(typeName);
+ DataType type = isFloat ? DataTypes.FLOAT() : DataTypes.DOUBLE();
+ FileStoreTable table = createTable(type);
+ writeFile(table, isFloat);
+ writeFile(table, isFloat);
+
+ assertThat(count(table, null)).isEqualTo(6);
+ assertMatches(table, isFloat);
+
+ compact(table);
+ assertMatches(table, isFloat);
+ }
+
+ @Test
+ public void testFiniteDoubleFilterStillPlans() throws Exception {
+ FileStoreTable table = createTable(DataTypes.DOUBLE());
+ writeFinite(table);
+ Predicate filter = new PredicateBuilder(table.rowType()).equal(0,
1.0d);
+ assertThat(count(table, filter)).isEqualTo(1);
+ }
+
+ private void assertMatches(FileStoreTable table, boolean isFloat) throws
Exception {
+ PredicateBuilder builder = new PredicateBuilder(table.rowType());
+ Object positiveZero;
+ Object negativeZero;
+ if (isFloat) {
+ positiveZero = Float.valueOf(0.0f);
+ negativeZero = -0.0f;
+ } else {
+ positiveZero = 0.0d;
+ negativeZero = -0.0d;
+ }
+ assertThat(count(table, builder.equal(0,
positiveZero))).as("equal(+0.0)").isEqualTo(2);
+ assertThat(count(table, builder.greaterThan(0, negativeZero)))
+ .as("greaterThan(-0.0)")
+ .isEqualTo(2);
+ }
+
+ private FileStoreTable createTable(DataType type) throws Exception {
+ Path path = new Path(tempDir.toString() + "/" +
type.getTypeRoot().name());
+ Map<String, String> options = new HashMap<>();
+ options.put("file.format", "orc");
+ options.put("bucket", "-1");
+ options.put("async-file-write", "false");
+ RowType rowType = new RowType(Collections.singletonList(new
DataField(0, "v", type)));
+ TableSchema schema =
+ SchemaUtils.forceCommit(
+ new FileSystemSchemaManager(LocalFileIO.create(),
path),
+ new Schema(
+ rowType.getFields(),
+ Collections.emptyList(),
+ Collections.emptyList(),
+ options,
+ ""));
+ return new AppendOnlyFileStoreTable(LocalFileIO.create(), path,
schema);
+ }
+
+ private void writeFile(FileStoreTable table, boolean isFloat) throws
Exception {
+ Object negativeOne;
+ Object negativeZero;
+ Object positiveZero;
+ if (isFloat) {
+ negativeOne = -1.0f;
+ negativeZero = -0.0f;
+ positiveZero = 0.0f;
+ } else {
+ negativeOne = -1.0d;
+ negativeZero = -0.0d;
+ positiveZero = 0.0d;
+ }
+ BatchWriteBuilder builder = table.newBatchWriteBuilder();
+ try (BatchTableWrite write = builder.newWrite();
+ BatchTableCommit commit = builder.newCommit()) {
+ write.write(GenericRow.of(negativeOne));
+ write.write(GenericRow.of(negativeZero));
+ write.write(GenericRow.of(positiveZero));
+ commit.commit(write.prepareCommit());
+ }
+ }
+
+ private void writeFinite(FileStoreTable table) throws Exception {
+ BatchWriteBuilder builder = table.newBatchWriteBuilder();
+ try (BatchTableWrite write = builder.newWrite();
+ BatchTableCommit commit = builder.newCommit()) {
+ write.write(GenericRow.of(1.0d));
+ write.write(GenericRow.of(2.0d));
+ commit.commit(write.prepareCommit());
+ }
+ }
+
+ private int count(FileStoreTable table, Predicate filter) throws Exception
{
+ ReadBuilder builder = table.newReadBuilder();
+ if (filter != null) {
+ builder.withFilter(filter);
+ assertThat(builder.newScan().plan().splits())
+ .as("filter planned no splits")
+ .isNotEmpty();
+ }
+ int[] matched = new int[1];
+ try (RecordReader<InternalRow> reader =
+
builder.newRead().executeFilter().createReader(builder.newScan().plan())) {
+ reader.forEachRemaining(row -> matched[0]++);
+ }
+ return matched[0];
+ }
+
+ private void compact(FileStoreTable table) throws Exception {
+ List<Split> splits = table.newReadBuilder().newScan().plan().splits();
+ assertThat(splits).isNotEmpty();
+ List<DataFileMeta> files = new ArrayList<>();
+ BinaryRow partition = ((DataSplit) splits.get(0)).partition();
+ for (Split split : splits) {
+ files.addAll(((DataSplit) split).dataFiles());
+ }
+ assertThat(files.size()).isGreaterThan(1);
+ BatchWriteBuilder builder = table.newBatchWriteBuilder();
+ try (BatchTableWrite write = builder.newWrite();
+ BatchTableCommit commit = builder.newCommit()) {
+ FileStoreWrite<?> fileStoreWrite = ((TableWriteImpl<?>)
write).getWrite();
+ CommitMessage message =
+ new AppendCompactTask(partition, files)
+ .doCompact(table, (BaseAppendFileStoreWrite)
fileStoreWrite);
+ commit.commit(Collections.singletonList(message));
+ }
+ }
+}
diff --git
a/paimon-format/src/main/java/org/apache/paimon/format/orc/filter/OrcPredicateFunctionVisitor.java
b/paimon-format/src/main/java/org/apache/paimon/format/orc/filter/OrcPredicateFunctionVisitor.java
index 1e5825f96a..b41282e8b0 100644
---
a/paimon-format/src/main/java/org/apache/paimon/format/orc/filter/OrcPredicateFunctionVisitor.java
+++
b/paimon-format/src/main/java/org/apache/paimon/format/orc/filter/OrcPredicateFunctionVisitor.java
@@ -185,6 +185,11 @@ public class OrcPredicateFunctionVisitor
if (colType == null) {
return Optional.empty();
}
+ for (Object literal : literals) {
+ if (signedZeroPushdown(colType, literal)) {
+ return Optional.empty();
+ }
+ }
Object[] orcLiterals = new Object[literals.size()];
for (int i = 0; i < literals.size(); i++) {
@@ -238,7 +243,7 @@ public class OrcPredicateFunctionVisitor
Object literal,
TriFunction<String, PredicateLeaf.Type, Serializable,
OrcFilters.Predicate> func) {
PredicateLeaf.Type litType = toOrcType(fieldRef.type());
- if (litType == null) {
+ if (litType == null || signedZeroPushdown(litType, literal)) {
return Optional.empty();
}
// fetch literal and ensure it is serializable
@@ -249,6 +254,27 @@ public class OrcPredicateFunctionVisitor
: Optional.empty();
}
+ /**
+ * ORC SearchArgument uses primitive comparisons, so {@code -0.0} and
{@code +0.0} compare equal
+ * and a row group can be dropped while it still holds the other zero.
Leave the predicate for
+ * the engine.
+ */
+ private static boolean signedZeroPushdown(PredicateLeaf.Type litType,
Object literal) {
+ if (litType != PredicateLeaf.Type.FLOAT || literal == null) {
+ return false;
+ }
+ if (literal instanceof Double) {
+ long bits = Double.doubleToRawLongBits((Double) literal);
+ return bits == Double.doubleToRawLongBits(0.0d)
+ || bits == Double.doubleToRawLongBits(-0.0d);
+ }
+ if (literal instanceof Float) {
+ int bits = Float.floatToRawIntBits((Float) literal);
+ return bits == Float.floatToRawIntBits(0.0f) || bits ==
Float.floatToRawIntBits(-0.0f);
+ }
+ return false;
+ }
+
@Nullable
private static Object toOrcObject(PredicateLeaf.Type litType, Object
literalObj) {
if (literalObj == null) {
diff --git
a/paimon-format/src/main/java/org/apache/paimon/format/orc/filter/OrcSimpleStatsExtractor.java
b/paimon-format/src/main/java/org/apache/paimon/format/orc/filter/OrcSimpleStatsExtractor.java
index f044596708..bb26215e5c 100644
---
a/paimon-format/src/main/java/org/apache/paimon/format/orc/filter/OrcSimpleStatsExtractor.java
+++
b/paimon-format/src/main/java/org/apache/paimon/format/orc/filter/OrcSimpleStatsExtractor.java
@@ -200,6 +200,9 @@ public class OrcSimpleStatsExtractor implements
SimpleStatsExtractor {
case FLOAT:
assertStatsClass(field, stats, DoubleColumnStatistics.class);
DoubleColumnStatistics floatStats = (DoubleColumnStatistics)
stats;
+ if (orcFloatingPointBoundsUnusable(floatStats)) {
+ return new SimpleColStats(null, null, nullCount);
+ }
return new SimpleColStats(
(float) floatStats.getMinimum(),
(float) floatStats.getMaximum(),
@@ -207,6 +210,9 @@ public class OrcSimpleStatsExtractor implements
SimpleStatsExtractor {
case DOUBLE:
assertStatsClass(field, stats, DoubleColumnStatistics.class);
DoubleColumnStatistics doubleStats = (DoubleColumnStatistics)
stats;
+ if (orcFloatingPointBoundsUnusable(doubleStats)) {
+ return new SimpleColStats(null, null, nullCount);
+ }
return new SimpleColStats(
doubleStats.getMinimum(), doubleStats.getMaximum(),
nullCount);
case DATE:
@@ -240,6 +246,29 @@ public class OrcSimpleStatsExtractor implements
SimpleStatsExtractor {
}
}
+ /**
+ * ORC updates double min/max with primitive {@code <} / {@code >}. NaN
never replaces a finite
+ * bound, {@code -0.0} never replaces {@code +0.0} as a minimum, and
{@code +0.0} never replaces
+ * {@code -0.0} as a maximum. Any of those can make a predicate skip a
file that still contains
+ * a matching row. A missing bound is not used for skipping.
+ */
+ private static boolean
orcFloatingPointBoundsUnusable(DoubleColumnStatistics stats) {
+ double minimum = stats.getMinimum();
+ double maximum = stats.getMaximum();
+ if (Double.isNaN(minimum) || Double.isNaN(maximum) ||
Double.isNaN(stats.getSum())) {
+ return true;
+ }
+ return isPositiveZero(minimum) || isNegativeZero(maximum);
+ }
+
+ private static boolean isPositiveZero(double value) {
+ return Double.doubleToRawLongBits(value) ==
Double.doubleToRawLongBits(0.0d);
+ }
+
+ private static boolean isNegativeZero(double value) {
+ return Double.doubleToRawLongBits(value) ==
Double.doubleToRawLongBits(-0.0d);
+ }
+
private void assertStatsClass(
DataField field,
ColumnStatistics stats,
diff --git
a/paimon-format/src/test/java/org/apache/paimon/format/orc/OrcFloatingPointStatsTest.java
b/paimon-format/src/test/java/org/apache/paimon/format/orc/OrcFloatingPointStatsTest.java
new file mode 100644
index 0000000000..0089566fb7
--- /dev/null
+++
b/paimon-format/src/test/java/org/apache/paimon/format/orc/OrcFloatingPointStatsTest.java
@@ -0,0 +1,160 @@
+/*
+ * 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.paimon.format.orc;
+
+import org.apache.paimon.data.GenericRow;
+import org.apache.paimon.format.FileFormat;
+import org.apache.paimon.format.FormatWriter;
+import org.apache.paimon.format.FormatWriterFactory;
+import org.apache.paimon.format.SimpleColStats;
+import org.apache.paimon.format.SimpleStatsExtractor;
+import org.apache.paimon.fs.FileIO;
+import org.apache.paimon.fs.Path;
+import org.apache.paimon.fs.PositionOutputStream;
+import org.apache.paimon.fs.local.LocalFileIO;
+import org.apache.paimon.options.Options;
+import org.apache.paimon.statistics.SimpleColStatsCollector;
+import org.apache.paimon.types.DataType;
+import org.apache.paimon.types.DataTypes;
+import org.apache.paimon.types.RowType;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/** ORC footer stats use primitive comparisons, which hide NaN and -0.0. */
+class OrcFloatingPointStatsTest {
+
+ @TempDir java.nio.file.Path tempDir;
+
+ private final FileIO fileIO = new LocalFileIO();
+
+ @Test
+ public void testDoubleNaNIsNotExcludedByTheUpperBound() throws Exception {
+ SimpleColStats stats = extract(DataTypes.DOUBLE(), new Double[] {1.0d,
Double.NaN});
+
+ assertThat(boundKeepsNaN(stats.max()))
+ .as("max %s excludes a NaN that was written", stats.max())
+ .isTrue();
+ }
+
+ @Test
+ public void testFloatNaNIsNotExcludedByTheUpperBound() throws Exception {
+ SimpleColStats stats = extract(DataTypes.FLOAT(), new Float[] {1.0f,
Float.NaN});
+
+ assertThat(boundKeepsNaN(stats.max()))
+ .as("max %s excludes a NaN that was written", stats.max())
+ .isTrue();
+ }
+
+ @Test
+ public void testDoubleNegativeZeroIsNotExcludedByTheLowerBound() throws
Exception {
+ SimpleColStats stats = extract(DataTypes.DOUBLE(), new Double[] {0.0d,
-0.0d});
+
+ assertThat(boundKeepsNegativeZero(stats.min()))
+ .as("min %s excludes a -0.0 that was written", stats.min())
+ .isTrue();
+ }
+
+ @Test
+ public void testFloatNegativeZeroIsNotExcludedByTheLowerBound() throws
Exception {
+ SimpleColStats stats = extract(DataTypes.FLOAT(), new Float[] {0.0f,
-0.0f});
+
+ assertThat(boundKeepsNegativeZero(stats.min()))
+ .as("min %s excludes a -0.0 that was written", stats.min())
+ .isTrue();
+ }
+
+ @Test
+ public void testFiniteDoubleBoundsAreKept() throws Exception {
+ SimpleColStats stats = extract(DataTypes.DOUBLE(), new Double[] {1.0d,
3.0d});
+
+ assertThat(stats.min()).isEqualTo(1.0d);
+ assertThat(stats.max()).isEqualTo(3.0d);
+ }
+
+ @Test
+ public void testFiniteFloatBoundsAreKept() throws Exception {
+ SimpleColStats stats = extract(DataTypes.FLOAT(), new Float[] {1.0f,
3.0f});
+
+ assertThat(stats.min()).isEqualTo(1.0f);
+ assertThat(stats.max()).isEqualTo(3.0f);
+ }
+
+ @Test
+ public void testDoubleNegativeZeroUpperBoundDoesNotHidePositiveZero()
throws Exception {
+ SimpleColStats stats = extract(DataTypes.DOUBLE(), new Double[]
{-1.0d, -0.0d, 0.0d});
+
+ assertThat(stats.max()).as("max %s hides a +0.0 that was written",
stats.max()).isNull();
+ }
+
+ @Test
+ public void testFloatNegativeZeroUpperBoundDoesNotHidePositiveZero()
throws Exception {
+ SimpleColStats stats = extract(DataTypes.FLOAT(), new Float[] {-1.0f,
-0.0f, 0.0f});
+
+ assertThat(stats.max()).as("max %s hides a +0.0 that was written",
stats.max()).isNull();
+ }
+
+ private SimpleColStats extract(DataType type, Object[] values) throws
Exception {
+ FileFormat format = FileFormat.fromIdentifier("orc", new Options());
+ RowType rowType = RowType.of(type);
+ FormatWriterFactory writerFactory =
format.createWriterFactory(rowType);
+ Path path = new Path(tempDir.toString() + "/stats.orc");
+ PositionOutputStream out = fileIO.newOutputStream(path, false);
+ FormatWriter writer = writerFactory.create(out, "LZ4");
+ for (Object value : values) {
+ writer.addElement(GenericRow.of(value));
+ }
+ writer.close();
+
+ SimpleColStatsCollector.Factory[] collectors =
+ new SimpleColStatsCollector.Factory[]
{SimpleColStatsCollector.from("full")};
+ SimpleStatsExtractor extractor = format.createStatsExtractor(rowType,
collectors).get();
+ return extractor.extract(fileIO, path, fileIO.getFileSize(path))[0];
+ }
+
+ /** A missing bound cannot skip the file. A NaN bound is the greatest
value. */
+ private static boolean boundKeepsNaN(Object bound) {
+ if (bound == null) {
+ return true;
+ }
+ if (bound instanceof Double) {
+ return Double.isNaN((Double) bound);
+ }
+ if (bound instanceof Float) {
+ return Float.isNaN((Float) bound);
+ }
+ return false;
+ }
+
+ /** A missing bound cannot skip the file. Otherwise the bound must be
negative zero. */
+ private static boolean boundKeepsNegativeZero(Object bound) {
+ if (bound == null) {
+ return true;
+ }
+ if (bound instanceof Double) {
+ return Double.doubleToRawLongBits((Double) bound) ==
Double.doubleToRawLongBits(-0.0d);
+ }
+ if (bound instanceof Float) {
+ return Float.floatToRawIntBits((Float) bound) ==
Float.floatToRawIntBits(-0.0f);
+ }
+ return false;
+ }
+}
diff --git
a/paimon-format/src/test/java/org/apache/paimon/format/orc/OrcSimpleStatsExtractorTest.java
b/paimon-format/src/test/java/org/apache/paimon/format/orc/OrcSimpleStatsExtractorTest.java
index e2c68ab60d..676ec9b48d 100644
---
a/paimon-format/src/test/java/org/apache/paimon/format/orc/OrcSimpleStatsExtractorTest.java
+++
b/paimon-format/src/test/java/org/apache/paimon/format/orc/OrcSimpleStatsExtractorTest.java
@@ -88,10 +88,27 @@ public class OrcSimpleStatsExtractorTest extends
SimpleColStatsExtractorTest {
case BINARY:
case VARBINARY:
return new SimpleColStats(null, null, stats.nullCount());
+ case FLOAT:
+ case DOUBLE:
+ // ORC cannot prove a +0.0 minimum is not hiding -0.0.
+ if (isPositiveZero(stats.min())) {
+ return new SimpleColStats(null, null, stats.nullCount());
+ }
+ return stats;
}
return stats;
}
+ private static boolean isPositiveZero(Object value) {
+ if (value instanceof Float) {
+ return Float.floatToRawIntBits((Float) value) ==
Float.floatToRawIntBits(0.0f);
+ }
+ if (value instanceof Double) {
+ return Double.doubleToRawLongBits((Double) value) ==
Double.doubleToRawLongBits(0.0d);
+ }
+ return false;
+ }
+
@Override
protected String fileCompression() {
return "LZ4";
diff --git
a/paimon-format/src/test/java/org/apache/paimon/format/orc/filter/OrcFilterConverterTest.java
b/paimon-format/src/test/java/org/apache/paimon/format/orc/filter/OrcFilterConverterTest.java
index ead0762b2a..47f16d4d4d 100644
---
a/paimon-format/src/test/java/org/apache/paimon/format/orc/filter/OrcFilterConverterTest.java
+++
b/paimon-format/src/test/java/org/apache/paimon/format/orc/filter/OrcFilterConverterTest.java
@@ -297,6 +297,34 @@ public class OrcFilterConverterTest {
.isEqualTo(Optional.empty());
}
+ @Test
+ public void testSignedZeroIsNotPushedDown() {
+ PredicateBuilder builder =
+ new PredicateBuilder(
+ new RowType(
+ Arrays.asList(
+ new DataField(0, "floatField", new
FloatType()),
+ new DataField(1, "doubleField", new
DoubleType()))));
+
+ assertThat(builder.equal(0,
0.0f).visit(OrcPredicateFunctionVisitor.VISITOR))
+ .isEqualTo(Optional.empty());
+ assertThat(builder.equal(0,
-0.0f).visit(OrcPredicateFunctionVisitor.VISITOR))
+ .isEqualTo(Optional.empty());
+ assertThat(builder.greaterThan(0,
-0.0f).visit(OrcPredicateFunctionVisitor.VISITOR))
+ .isEqualTo(Optional.empty());
+ assertThat(builder.equal(1,
0.0d).visit(OrcPredicateFunctionVisitor.VISITOR))
+ .isEqualTo(Optional.empty());
+ assertThat(builder.equal(1,
-0.0d).visit(OrcPredicateFunctionVisitor.VISITOR))
+ .isEqualTo(Optional.empty());
+ assertThat(builder.greaterThan(1,
-0.0d).visit(OrcPredicateFunctionVisitor.VISITOR))
+ .isEqualTo(Optional.empty());
+ assertThat(
+ builder.in(1, Arrays.asList(-1.0d, 0.0d))
+ .visit(OrcPredicateFunctionVisitor.VISITOR))
+ .isEqualTo(Optional.empty());
+ assertThat(builder.equal(1,
1.0d).visit(OrcPredicateFunctionVisitor.VISITOR)).isPresent();
+ }
+
@Test
public void testNotLike() {
PredicateBuilder builder =