From 0fd9fcb13f3c030a1700b2f935e780a389af7f25 Mon Sep 17 00:00:00 2001 From: Pavel Pereslegin Date: Mon, 6 Apr 2026 10:09:17 +0300 Subject: [PATCH 1/2] IGNITE-26613 Use compare() in binary comparisons for floating point types --- .../sql/engine/ItFloatingPointTest.java | 128 ++++++++++---- .../engine/exec/exp/IgniteExpressions.java | 40 +++++ .../sql/engine/externalize/RelJson.java | 18 +- .../exp/SqlExpressionFactoryImplTest.java | 161 ++++++++++++++++++ .../expressions/IgnitePredicateTest.java | 22 ++- ...lFloatingPointValuesSerializationTest.java | 95 +++++++++++ 6 files changed, 429 insertions(+), 35 deletions(-) create mode 100644 modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/SpecialFloatingPointValuesSerializationTest.java diff --git a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItFloatingPointTest.java b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItFloatingPointTest.java index c27fee5c2592..b759dff61d62 100644 --- a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItFloatingPointTest.java +++ b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItFloatingPointTest.java @@ -17,17 +17,14 @@ package org.apache.ignite.internal.sql.engine; -import static java.util.stream.Collectors.toList; import static org.apache.ignite.internal.TestWrappers.unwrapIgniteImpl; import static org.apache.ignite.internal.sql.engine.util.QueryChecker.containsIndexScan; import static org.apache.ignite.internal.sql.engine.util.QueryChecker.containsIndexScanIgnoreBounds; import static org.apache.ignite.internal.sql.engine.util.QueryChecker.containsTableScan; -import static org.hamcrest.MatcherAssert.assertThat; import java.util.List; import org.apache.ignite.Ignite; import org.apache.ignite.internal.app.IgniteImpl; -import org.hamcrest.Matchers; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -205,15 +202,15 @@ void testEqualityComparison() { assertQuery("SELECT fn FROM test WHERE fn = '-Infinity'::FLOAT").returns(Float.NEGATIVE_INFINITY).check(); assertQuery("SELECT f FROM test WHERE f = '+Infinity'::FLOAT").returns(Float.POSITIVE_INFINITY).check(); assertQuery("SELECT fn FROM test WHERE fn = '+Infinity'::FLOAT").returns(Float.POSITIVE_INFINITY).check(); - assertQuery("SELECT f FROM test WHERE f = 'NaN'::FLOAT").returnNothing().check(); // NaN never equals - assertQuery("SELECT fn FROM test WHERE fn = 'NaN'::FLOAT").returnNothing().check(); // NaN never equals + assertQuery("SELECT f FROM test WHERE f = 'NaN'::FLOAT").returns(Float.NaN).check(); + assertQuery("SELECT fn FROM test WHERE fn = 'NaN'::FLOAT").returns(Float.NaN).check(); assertQuery("SELECT d FROM test WHERE d = '-Infinity'::DOUBLE").returns(Double.NEGATIVE_INFINITY).check(); assertQuery("SELECT dn FROM test WHERE dn = '-Infinity'::DOUBLE").returns(Double.NEGATIVE_INFINITY).check(); assertQuery("SELECT d FROM test WHERE d = '+Infinity'::DOUBLE").returns(Double.POSITIVE_INFINITY).check(); assertQuery("SELECT dn FROM test WHERE dn = '+Infinity'::DOUBLE").returns(Double.POSITIVE_INFINITY).check(); - assertQuery("SELECT d FROM test WHERE d = 'NaN'::DOUBLE").returnNothing().check(); // NaN never equals - assertQuery("SELECT dn FROM test WHERE dn = 'NaN'::DOUBLE").returnNothing().check(); // NaN never equals + assertQuery("SELECT d FROM test WHERE d = 'NaN'::DOUBLE").returns(Double.NaN).check(); + assertQuery("SELECT dn FROM test WHERE dn = 'NaN'::DOUBLE").returns(Double.NaN).check(); } @Test @@ -228,6 +225,7 @@ void testNonEqualityComparisonWithIndexScan() { .returns(-1.0f) .returns(1.0f) .returns(Float.POSITIVE_INFINITY) + .returns(Float.NaN) .check(); assertQuery("SELECT /*+ FORCE_INDEX(test_fn_idx) */ fn FROM test WHERE fn > ?") .withParam(Float.NEGATIVE_INFINITY) @@ -238,6 +236,7 @@ void testNonEqualityComparisonWithIndexScan() { .returns(-1.0f) .returns(1.0f) .returns(Float.POSITIVE_INFINITY) + .returns(Float.NaN) .check(); assertQuery("SELECT /*+ FORCE_INDEX(test_f_idx) */ f FROM test WHERE f > ?") .withParam(Float.NaN) @@ -255,6 +254,7 @@ void testNonEqualityComparisonWithIndexScan() { .returns(-0.0d).returns(0.0d) .returns(-1.0d).returns(1.0d) .returns(Double.POSITIVE_INFINITY) + .returns(Double.NaN) .check(); assertQuery("SELECT /*+ FORCE_INDEX(test_dn_idx) */ dn FROM test WHERE dn > '-Infinity'::DOUBLE") .matches(containsIndexScan("PUBLIC", "TEST", "TEST_DN_IDX")) @@ -264,6 +264,7 @@ void testNonEqualityComparisonWithIndexScan() { .returns(-1.0d) .returns(1.0d) .returns(Double.POSITIVE_INFINITY) + .returns(Double.NaN) .check(); assertQuery("SELECT /*+ FORCE_INDEX(test_d_idx) */ d FROM test WHERE d > 'NaN'::DOUBLE") .matches(containsIndexScan("PUBLIC", "TEST", "TEST_D_IDX")) @@ -280,43 +281,69 @@ void testNonEqualityComparisonWithIndexScan() { assertQuery("SELECT /*+ FORCE_INDEX(test_f_idx) */ f FROM test WHERE f < ?") .withParam(+0.0f) .matches(containsIndexScan("PUBLIC", "TEST", "TEST_F_IDX")) + .returns(-0.0f) .returns(-1.0f) .returns(Float.NEGATIVE_INFINITY) .check(); assertQuery("SELECT /*+ FORCE_INDEX(test_fn_idx) */ fn FROM test WHERE fn < ?") .withParam(+0.0f) .matches(containsIndexScan("PUBLIC", "TEST", "TEST_FN_IDX")) + .returns(-0.0f) .returns(-1.0f) .returns(Float.NEGATIVE_INFINITY) .check(); assertQuery("SELECT /*+ FORCE_INDEX(test_f_idx) */ f FROM test WHERE f < ?") .withParam(Float.NaN) .matches(containsIndexScan("PUBLIC", "TEST", "TEST_F_IDX")) - .returnNothing() + .returns(Float.NEGATIVE_INFINITY) + .returns(-0.0f) + .returns(0.0f) + .returns(-1.0f) + .returns(1.0f) + .returns(Float.POSITIVE_INFINITY) .check(); assertQuery("SELECT /*+ FORCE_INDEX(test_fn_idx) */ fn FROM test WHERE fn < ?") .withParam(Float.NaN) .matches(containsIndexScan("PUBLIC", "TEST", "TEST_FN_IDX")) - .returnNothing() + .returns(Float.NEGATIVE_INFINITY) + .returns(-0.0f) + .returns(0.0f) + .returns(0.0f) + .returns(-1.0f) + .returns(1.0f) + .returns(Float.POSITIVE_INFINITY) .check(); assertQuery("SELECT /*+ FORCE_INDEX(test_d_idx) */ d FROM test WHERE d < +0.0::DOUBLE") .matches(containsIndexScan("PUBLIC", "TEST", "TEST_D_IDX")) + .returns(-0.0d) .returns(-1.0d) .returns(Double.NEGATIVE_INFINITY) .check(); assertQuery("SELECT /*+ FORCE_INDEX(test_dn_idx) */ dn FROM test WHERE dn < +0.0::DOUBLE") .matches(containsIndexScan("PUBLIC", "TEST", "TEST_DN_IDX")) + .returns(-0.0d) .returns(-1.0d) .returns(Double.NEGATIVE_INFINITY) .check(); assertQuery("SELECT /*+ FORCE_INDEX(test_d_idx) */ d FROM test WHERE d < '-NaN'::DOUBLE") .matches(containsIndexScan("PUBLIC", "TEST", "TEST_D_IDX")) - .returnNothing() + .returns(Double.NEGATIVE_INFINITY) + .returns(-0.0d) + .returns(0.0d) + .returns(-1.0d) + .returns(1.0d) + .returns(Double.POSITIVE_INFINITY) .check(); assertQuery("SELECT /*+ FORCE_INDEX(test_dn_idx) */ dn FROM test WHERE dn < '-NaN'::DOUBLE") .matches(containsIndexScan("PUBLIC", "TEST", "TEST_DN_IDX")) - .returnNothing() + .returns(Double.NEGATIVE_INFINITY) + .returns(-0.0d) + .returns(0.0d) + .returns(0.0d) + .returns(-1.0d) + .returns(1.0d) + .returns(Double.POSITIVE_INFINITY) .check(); } } @@ -326,13 +353,18 @@ void testNonEqualityComparisonWithTableScan() { { // Greater-than assertQuery("SELECT /*+ NO_INDEX */ f FROM test WHERE f > -0.0::FLOAT") .matches(containsTableScan("PUBLIC", "TEST")) + .returns(0.0f) .returns(1.0f) .returns(Float.POSITIVE_INFINITY) + .returns(Float.NaN) .check(); - assertQuery("SELECT /*+ NO_INDEX */ fn FROM test WHERE fn > -0.0::FLOAT") + assertQuery("SELECT /*+ NO_INDEX */ id, fn FROM test WHERE fn > -0.0::FLOAT") .matches(containsTableScan("PUBLIC", "TEST")) - .returns(1.0f) - .returns(Float.POSITIVE_INFINITY) + .returns(0, 0.0f) + .returns(5, 0.0f) + .returns(7, 1.0f) + .returns(2, Float.POSITIVE_INFINITY) + .returns(3, Float.NaN) .check(); assertQuery("SELECT /*+ NO_INDEX */ f FROM test WHERE f > ?") .withParam(Float.NaN) @@ -347,13 +379,18 @@ void testNonEqualityComparisonWithTableScan() { assertQuery("SELECT /*+ NO_INDEX */ d FROM test WHERE d > -0.0::DOUBLE") .matches(containsTableScan("PUBLIC", "TEST")) + .returns(0.0d) .returns(1.0d) .returns(Double.POSITIVE_INFINITY) + .returns(Double.NaN) .check(); - assertQuery("SELECT /*+ NO_INDEX */ dn FROM test WHERE dn > -0.0::DOUBLE") + assertQuery("SELECT /*+ NO_INDEX */ id, dn FROM test WHERE dn > -0.0::DOUBLE") .matches(containsTableScan("PUBLIC", "TEST")) - .returns(1.0d) - .returns(Double.POSITIVE_INFINITY) + .returns(0, 0.0d) + .returns(5, 0.0d) + .returns(7, 1.0d) + .returns(2, Double.POSITIVE_INFINITY) + .returns(3, Double.NaN) .check(); assertQuery("SELECT /*+ NO_INDEX */ d FROM test WHERE d > 'NaN'::DOUBLE") .matches(containsTableScan("PUBLIC", "TEST")) @@ -368,42 +405,68 @@ void testNonEqualityComparisonWithTableScan() { { // Lesser-than assertQuery("SELECT /*+ NO_INDEX */ f FROM test WHERE f < +0.0::FLOAT") .matches(containsTableScan("PUBLIC", "TEST")) + .returns(-0.0f) .returns(-1.0f) .returns(Float.NEGATIVE_INFINITY) .check(); assertQuery("SELECT /*+ NO_INDEX */ fn FROM test WHERE fn < +0.0::FLOAT") .matches(containsTableScan("PUBLIC", "TEST")) + .returns(-0.0f) .returns(-1.0f) .returns(Float.NEGATIVE_INFINITY) .check(); assertQuery("SELECT /*+ NO_INDEX */ f FROM test WHERE f < ?") .withParam(Float.NaN) .matches(containsTableScan("PUBLIC", "TEST")) - .returnNothing() + .returns(Float.NEGATIVE_INFINITY) + .returns(-1.0f) + .returns(-0.0f) + .returns(0.0f) + .returns(1.0f) + .returns(Float.POSITIVE_INFINITY) .check(); assertQuery("SELECT /*+ NO_INDEX */ fn FROM test WHERE fn < ?") .withParam(Float.NaN) .matches(containsTableScan("PUBLIC", "TEST")) - .returnNothing() + .returns(Float.NEGATIVE_INFINITY) + .returns(-1.0f) + .returns(-0.0f) + .returns(0.0f) + .returns(0.0f) + .returns(1.0f) + .returns(Float.POSITIVE_INFINITY) .check(); assertQuery("SELECT /*+ NO_INDEX */ d FROM test WHERE d < +0.0::DOUBLE") .matches(containsTableScan("PUBLIC", "TEST")) + .returns(-0.0d) .returns(-1.0d) .returns(Double.NEGATIVE_INFINITY) .check(); assertQuery("SELECT /*+ NO_INDEX */ dn FROM test WHERE dn < +0.0::DOUBLE") .matches(containsTableScan("PUBLIC", "TEST")) + .returns(-0.0d) .returns(-1.0d) .returns(Double.NEGATIVE_INFINITY) .check(); assertQuery("SELECT /*+ NO_INDEX */ d FROM test WHERE d < 'NaN'::DOUBLE") .matches(containsTableScan("PUBLIC", "TEST")) - .returnNothing() + .returns(Double.NEGATIVE_INFINITY) + .returns(-1.0d) + .returns(-0.0d) + .returns(0.0d) + .returns(1.0d) + .returns(Double.POSITIVE_INFINITY) .check(); assertQuery("SELECT /*+ NO_INDEX */ dn FROM test WHERE dn < 'NaN'::DOUBLE") .matches(containsTableScan("PUBLIC", "TEST")) - .returnNothing() + .returns(Double.NEGATIVE_INFINITY) + .returns(-1.0d) + .returns(-0.0d) + .returns(0.0d) + .returns(0.0d) + .returns(1.0d) + .returns(Double.POSITIVE_INFINITY) .check(); } } @@ -424,27 +487,32 @@ void testIsDistinctFrom() { .check(); assertQuery("SELECT f FROM test WHERE f IS DISTINCT FROM '-0.0'::FLOAT") + .returns(0.0f) // 0.0f <> -0.0f .returns(1.0f).returns(-1.0f) .returns(Float.NEGATIVE_INFINITY).returns(Float.POSITIVE_INFINITY) .returns(Float.NaN) .returns((Object) null) .check(); assertQuery("SELECT d FROM test WHERE d IS DISTINCT FROM '-0.0'::DOUBLE") + .returns(0.0d) // 0.0d <> -0.0d .returns(1.0d).returns(-1.0d) .returns(Double.NEGATIVE_INFINITY).returns(Double.POSITIVE_INFINITY) .returns(Double.NaN) .returns((Object) null) .check(); - List floats = sql("SELECT f FROM test WHERE f IS DISTINCT FROM 'NaN'::FLOAT") - .stream().flatMap(List::stream).map(Float.class::cast).collect(toList()); - assertThat(floats, Matchers.hasSize(8)); - assertThat(floats, Matchers.hasItem(Float.NaN)); // NaN not equal to NaN - - List doubles = sql("SELECT d FROM test WHERE d IS DISTINCT FROM 'NaN'::DOUBLE") - .stream().flatMap(List::stream).map(Double.class::cast).collect(toList()); - assertThat(doubles, Matchers.hasSize(8)); - assertThat(doubles, Matchers.hasItem(Double.NaN)); // NaN not equal to NaN + assertQuery("SELECT f FROM test WHERE f IS DISTINCT FROM 'NaN'::FLOAT") + .returns(0.0f).returns(-0.0f) + .returns(1.0f).returns(-1.0f) + .returns(Float.NEGATIVE_INFINITY).returns(Float.POSITIVE_INFINITY) + .returns((Object) null) + .check(); + assertQuery("SELECT d FROM test WHERE d IS DISTINCT FROM 'NaN'::DOUBLE") + .returns(0.0d).returns(-0.0d) + .returns(1.0d).returns(-1.0d) + .returns(Double.NEGATIVE_INFINITY).returns(Double.POSITIVE_INFINITY) + .returns((Object) null) + .check(); } @Test diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteExpressions.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteExpressions.java index 0be9c1f07353..808fc7fcaa69 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteExpressions.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteExpressions.java @@ -18,6 +18,7 @@ package org.apache.ignite.internal.sql.engine.exec.exp; import java.lang.reflect.Type; +import java.util.EnumSet; import org.apache.calcite.linq4j.tree.Expression; import org.apache.calcite.linq4j.tree.ExpressionType; import org.apache.calcite.linq4j.tree.Expressions; @@ -29,6 +30,16 @@ /** Calcite liq4j expressions customized for Ignite. */ public class IgniteExpressions { + /** Comparison expression types that need special handling for floating-point types. */ + private static final EnumSet COMPARISON_OR_EQUALS_OPERATORS = EnumSet.of( + ExpressionType.Equal, + ExpressionType.NotEqual, + ExpressionType.LessThan, + ExpressionType.LessThanOrEqual, + ExpressionType.GreaterThan, + ExpressionType.GreaterThanOrEqual + ); + /** Make binary expression with arithmetic operations override. */ public static Expression makeBinary(ExpressionType binaryType, Expression left, Expression right) { switch (binaryType) { @@ -41,6 +52,14 @@ public static Expression makeBinary(ExpressionType binaryType, Expression left, case Divide: return divideExact(left, right); default: + if (COMPARISON_OR_EQUALS_OPERATORS.contains(binaryType)) { + Expression floatingPointCmp = compareFloatingPoint(binaryType, left, right); + + if (floatingPointCmp != null) { + return floatingPointCmp; + } + } + return Expressions.makeBinary(binaryType, left, right); } } @@ -182,4 +201,25 @@ private static Type larger(Type type0, Type type1) { return Double.TYPE; } } + + /** + * Generates a comparison expression for floating-point types using {@link Float#compare(float, float)} + * or {@link Double#compare(double, double)} instead of primitive comparison operators. + */ + private static @Nullable Expression compareFloatingPoint(ExpressionType binaryType, Expression left, Expression right) { + Type leftType = left.getType(); + Type rightType = right.getType(); + + boolean isFloat = leftType == Float.TYPE || rightType == Float.TYPE; + boolean isDouble = leftType == Double.TYPE || rightType == Double.TYPE; + + if (!isFloat && !isDouble) { + return null; + } + + Class targetClass = isDouble ? Double.class : Float.class; + Expression cmp = Expressions.call(targetClass, "compare", left, right); + + return Expressions.makeBinary(binaryType, cmp, Expressions.constant(0)); + } } diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/externalize/RelJson.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/externalize/RelJson.java index b9365cc2c713..40d51acb091f 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/externalize/RelJson.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/externalize/RelJson.java @@ -420,7 +420,19 @@ private Object toJson(RexNode node) { RexLiteral literal = (RexLiteral) node; Object value = literal.getValue3(); map = map(); - map.put("literal", toJson(value)); + // Convert the approximate numeric negative zero (-0.0) value to a string to preserve the minus sign, + // otherwise due to USE_BIG_DECIMAL_FOR_FLOATS jackson will create a BigDecimal from a double (-0.0) + // and this will result in the loss of the minus sign. + // Other special values +Infinity/-Infinity/NaN are serialized by jackson as strings. + // The value in RexLiteral for approximate types is always of type Double (see RexLiteral#valueMatchesType). + if (value instanceof Double + && isApproximateNumeric(node.getType()) + && isNegativeZero((Double) value)) { + map.put("literal", "-0.0"); + } else { + map.put("literal", toJson(value)); + } + map.put("type", toJson(node.getType())); return map; @@ -1062,4 +1074,8 @@ private List toRexList(RelInput relInput, List operands) { } return list; } + + private static boolean isNegativeZero(double d) { + return d == 0.0 && Double.doubleToRawLongBits(d) != 0; + } } diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/SqlExpressionFactoryImplTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/SqlExpressionFactoryImplTest.java index d8379a5330a6..99dcdb80f8f5 100644 --- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/SqlExpressionFactoryImplTest.java +++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/SqlExpressionFactoryImplTest.java @@ -48,6 +48,7 @@ import org.apache.calcite.rex.RexLiteral; import org.apache.calcite.rex.RexLocalRef; import org.apache.calcite.rex.RexNode; +import org.apache.calcite.sql.SqlOperator; import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.sql.type.BasicSqlType; import org.apache.calcite.sql.type.SqlTypeName; @@ -828,6 +829,166 @@ private static Stream numericLiterals() { ); } + @ParameterizedTest + @MethodSource("doubleSpecialValuesArgs") + public void testDoubleSpecialValuesComparison( + SqlOperator op, + double leftVal, + double rightVal, + boolean expectedResult + ) { + RexBuilder rexBuilder = Commons.rexBuilder(); + IgniteTypeFactory tf = Commons.typeFactory(); + + RelDataType colType = tf.createSqlType(SqlTypeName.DOUBLE); + RelDataType rowType = new Builder(tf) + .add("c1", colType) + .add("c2", colType) + .build(); + + RexInputRef ref1 = rexBuilder.makeInputRef(rowType, 0); + RexInputRef ref2 = rexBuilder.makeInputRef(rowType, 1); + RexNode filter = rexBuilder.makeCall(op, List.of(ref1, ref2)); + + SqlPredicate predicate = expFactory.predicate(filter, rowType); + assertEquals(expectedResult, predicate.test(ctx, new Object[]{leftVal, rightVal}), + "Failed for " + leftVal + " " + op.getName() + " " + rightVal); + } + + private static List doubleSpecialValuesArgs() { + return makeSpecialFloatArgs( + Double.NaN, + Double.POSITIVE_INFINITY, + Double.NEGATIVE_INFINITY, + 1.0d, + 0.0d, + -0.0d + ); + } + + @ParameterizedTest + @MethodSource("floatSpecialValuesArgs") + public void testFloatSpecialValues( + SqlOperator op, + float left, + float right, + boolean expected + ) { + RexBuilder rexBuilder = Commons.rexBuilder(); + IgniteTypeFactory tf = Commons.typeFactory(); + + RelDataType colType = tf.createSqlType(SqlTypeName.REAL); + RelDataType rowType = new Builder(tf) + .add("c1", colType) + .add("c2", colType) + .build(); + + RexInputRef ref1 = rexBuilder.makeInputRef(rowType, 0); + RexInputRef ref2 = rexBuilder.makeInputRef(rowType, 1); + RexNode filter = rexBuilder.makeCall(op, List.of(ref1, ref2)); + + SqlPredicate predicate = expFactory.predicate(filter, rowType); + assertEquals(expected, predicate.test(ctx, new Object[]{left, right}), + "Failed for " + left + " " + op.getName() + " " + right); + } + + private static List floatSpecialValuesArgs() { + return makeSpecialFloatArgs( + Float.NaN, + Float.POSITIVE_INFINITY, + Float.NEGATIVE_INFINITY, + 1.0f, + 0.0f, + -0.0f + ); + } + + private static List makeSpecialFloatArgs( + Number nan, + Number posInf, + Number negInf, + Number one, + Number zero, + Number negZero + ) { + List args = new ArrayList<>(); + + // NaN = NaN : true + args.add(Arguments.of(SqlStdOperatorTable.EQUALS, nan, nan, true)); + // NaN = 1.0 : false + args.add(Arguments.of(SqlStdOperatorTable.EQUALS, nan, one, false)); + // 1.0 = NaN : false + args.add(Arguments.of(SqlStdOperatorTable.EQUALS, one, nan, false)); + // -0.0 = -0.0 : true + args.add(Arguments.of(SqlStdOperatorTable.EQUALS, negZero, negZero, true)); + // -0.0 = 0.0 : false + args.add(Arguments.of(SqlStdOperatorTable.EQUALS, negZero, zero, false)); + // 1.0 = 1.0 : true + args.add(Arguments.of(SqlStdOperatorTable.EQUALS, one, one, true)); + + // NaN <> NaN should be false + args.add(Arguments.of(SqlStdOperatorTable.NOT_EQUALS, nan, nan, false)); + // NaN <> 1.0 should be true + args.add(Arguments.of(SqlStdOperatorTable.NOT_EQUALS, nan, one, true)); + // -0.0 <> -0.0 : false + args.add(Arguments.of(SqlStdOperatorTable.NOT_EQUALS, negZero, negZero, false)); + // -0.0 <> 0.0 : true + args.add(Arguments.of(SqlStdOperatorTable.NOT_EQUALS, negZero, zero, true)); + + // NaN > any non-NaN value : true + args.add(Arguments.of(SqlStdOperatorTable.GREATER_THAN, nan, one, true)); + args.add(Arguments.of(SqlStdOperatorTable.GREATER_THAN, nan, posInf, true)); + // any non-NaN > NaN : false + args.add(Arguments.of(SqlStdOperatorTable.GREATER_THAN, one, nan, false)); + // NaN > NaN : false + args.add(Arguments.of(SqlStdOperatorTable.GREATER_THAN, nan, nan, false)); + // -0.0 > -0.0 : false + args.add(Arguments.of(SqlStdOperatorTable.GREATER_THAN, negZero, negZero, false)); + // -0.0 > 0.0 : false + args.add(Arguments.of(SqlStdOperatorTable.GREATER_THAN, negZero, zero, false)); + // 0.0 > -0.0 : true + args.add(Arguments.of(SqlStdOperatorTable.GREATER_THAN, zero, negZero, true)); + + // NaN >= NaN : true + args.add(Arguments.of(SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, nan, nan, true)); + // 1.0 >= NaN : false + args.add(Arguments.of(SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, one, nan, false)); + // -0.0 >= -0.0 : true + args.add(Arguments.of(SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, negZero, negZero, true)); + // -0.0 >= 0.0 : false + args.add(Arguments.of(SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, negZero, zero, false)); + // 0.0 >= -0.0 : true + args.add(Arguments.of(SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, zero, negZero, true)); + + // 1.0 < NaN : true + args.add(Arguments.of(SqlStdOperatorTable.LESS_THAN, one, nan, true)); + // NaN < 1.0 : false + args.add(Arguments.of(SqlStdOperatorTable.LESS_THAN, nan, one, false)); + // NaN < NaN : false + args.add(Arguments.of(SqlStdOperatorTable.LESS_THAN, nan, nan, false)); + // -0.0 < -0.0 : false + args.add(Arguments.of(SqlStdOperatorTable.LESS_THAN, negZero, negZero, false)); + // -0.0 < 0.0 : true + args.add(Arguments.of(SqlStdOperatorTable.LESS_THAN, negZero, zero, true)); + // 0.0 < -0.0 : false + args.add(Arguments.of(SqlStdOperatorTable.LESS_THAN, zero, negZero, false)); + + // NaN <= NaN : true + args.add(Arguments.of(SqlStdOperatorTable.LESS_THAN_OR_EQUAL, nan, nan, true)); + // -0.0 <= -0.0 : true + args.add(Arguments.of(SqlStdOperatorTable.LESS_THAN_OR_EQUAL, negZero, negZero, true)); + // -0.0 <= 0.0 : true + args.add(Arguments.of(SqlStdOperatorTable.LESS_THAN_OR_EQUAL, negZero, zero, true)); + // 0.0 <= -0.0 : false + args.add(Arguments.of(SqlStdOperatorTable.LESS_THAN_OR_EQUAL, zero, negZero, false)); + + args.add(Arguments.of(SqlStdOperatorTable.EQUALS, posInf, posInf, true)); + args.add(Arguments.of(SqlStdOperatorTable.LESS_THAN, negInf, posInf, true)); + args.add(Arguments.of(SqlStdOperatorTable.EQUALS, zero, zero, true)); + + return args; + } + private static List rowSourceTestArgs() { EnumSet ignoredTypes = EnumSet.of( // TODO https://issues.apache.org/jira/browse/IGNITE-17373 diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/expressions/IgnitePredicateTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/expressions/IgnitePredicateTest.java index 14af0f067503..7e2d188ceff7 100644 --- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/expressions/IgnitePredicateTest.java +++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/expressions/IgnitePredicateTest.java @@ -378,18 +378,20 @@ void floatTest() throws Exception { IgnitePredicate predicate = factory.predicate("val > 0", inputType); assertThat(predicate.test(context, row(-100.5f)), is(false)); + assertThat(predicate.test(context, row(-0.0f)), is(false)); assertThat(predicate.test(context, row(0.0f)), is(false)); assertThat(predicate.test(context, row(100.5f)), is(true)); assertThat(predicate.test(context, row(null)), is(false)); assertThat(predicate.test(context, row(Float.POSITIVE_INFINITY)), is(true)); assertThat(predicate.test(context, row(Float.NEGATIVE_INFINITY)), is(false)); - assertThat(predicate.test(context, row(Float.NaN)), is(false)); + assertThat(predicate.test(context, row(Float.NaN)), is(true)); } { IgnitePredicate predicate = factory.predicate("val <> 0", inputType); assertThat(predicate.test(context, row(-100.5f)), is(true)); + assertThat(predicate.test(context, row(-0.0f)), is(true)); assertThat(predicate.test(context, row(0.0f)), is(false)); assertThat(predicate.test(context, row(100.5f)), is(true)); assertThat(predicate.test(context, row(null)), is(false)); @@ -402,6 +404,7 @@ void floatTest() throws Exception { IgnitePredicate predicate = factory.predicate("val <= 0", inputType); assertThat(predicate.test(context, row(-100.5f)), is(true)); + assertThat(predicate.test(context, row(-0.0f)), is(true)); assertThat(predicate.test(context, row(0.0f)), is(true)); assertThat(predicate.test(context, row(100.5f)), is(false)); assertThat(predicate.test(context, row(null)), is(false)); @@ -414,6 +417,7 @@ void floatTest() throws Exception { IgnitePredicate predicate = factory.predicate("val IS NULL", inputType); assertThat(predicate.test(context, row(-100.5f)), is(false)); + assertThat(predicate.test(context, row(-0.0f)), is(false)); assertThat(predicate.test(context, row(0.0f)), is(false)); assertThat(predicate.test(context, row(100.5f)), is(false)); assertThat(predicate.test(context, row(null)), is(true)); @@ -426,6 +430,7 @@ void floatTest() throws Exception { IgnitePredicate predicate = factory.predicate("val IS NOT NULL", inputType); assertThat(predicate.test(context, row(-100.5f)), is(true)); + assertThat(predicate.test(context, row(-0.0f)), is(true)); assertThat(predicate.test(context, row(0.0f)), is(true)); assertThat(predicate.test(context, row(100.5f)), is(true)); assertThat(predicate.test(context, row(null)), is(false)); @@ -438,6 +443,7 @@ void floatTest() throws Exception { IgnitePredicate predicate = factory.predicate("val < 0.1", inputType); assertThat(predicate.test(context, row(-100.5f)), is(true)); + assertThat(predicate.test(context, row(-0.0f)), is(true)); assertThat(predicate.test(context, row(0.0f)), is(true)); assertThat(predicate.test(context, row(100.5f)), is(false)); assertThat(predicate.test(context, row(null)), is(false)); @@ -450,12 +456,13 @@ void floatTest() throws Exception { IgnitePredicate predicate = factory.predicate("val > 0.1", inputType); assertThat(predicate.test(context, row(-100.5f)), is(false)); + assertThat(predicate.test(context, row(-0.0f)), is(false)); assertThat(predicate.test(context, row(0.0f)), is(false)); assertThat(predicate.test(context, row(100.5f)), is(true)); assertThat(predicate.test(context, row(null)), is(false)); assertThat(predicate.test(context, row(Float.POSITIVE_INFINITY)), is(true)); assertThat(predicate.test(context, row(Float.NEGATIVE_INFINITY)), is(false)); - assertThat(predicate.test(context, row(Float.NaN)), is(false)); + assertThat(predicate.test(context, row(Float.NaN)), is(true)); } } @@ -469,18 +476,20 @@ void doubleTest() throws Exception { IgnitePredicate predicate = factory.predicate("val > 0", inputType); assertThat(predicate.test(context, row(-100.5)), is(false)); + assertThat(predicate.test(context, row(-0.0d)), is(false)); assertThat(predicate.test(context, row(0.0)), is(false)); assertThat(predicate.test(context, row(100.5)), is(true)); assertThat(predicate.test(context, row(null)), is(false)); assertThat(predicate.test(context, row(Double.POSITIVE_INFINITY)), is(true)); assertThat(predicate.test(context, row(Double.NEGATIVE_INFINITY)), is(false)); - assertThat(predicate.test(context, row(Double.NaN)), is(false)); + assertThat(predicate.test(context, row(Double.NaN)), is(true)); } { IgnitePredicate predicate = factory.predicate("val <> 0", inputType); assertThat(predicate.test(context, row(-100.5)), is(true)); + assertThat(predicate.test(context, row(-0.0d)), is(true)); assertThat(predicate.test(context, row(0.0)), is(false)); assertThat(predicate.test(context, row(100.5)), is(true)); assertThat(predicate.test(context, row(null)), is(false)); @@ -493,6 +502,7 @@ void doubleTest() throws Exception { IgnitePredicate predicate = factory.predicate("val <= 0", inputType); assertThat(predicate.test(context, row(-100.5)), is(true)); + assertThat(predicate.test(context, row(-0.0d)), is(true)); assertThat(predicate.test(context, row(0.0)), is(true)); assertThat(predicate.test(context, row(100.5)), is(false)); assertThat(predicate.test(context, row(null)), is(false)); @@ -505,6 +515,7 @@ void doubleTest() throws Exception { IgnitePredicate predicate = factory.predicate("val IS NULL", inputType); assertThat(predicate.test(context, row(-100.5)), is(false)); + assertThat(predicate.test(context, row(-0.0d)), is(false)); assertThat(predicate.test(context, row(0.0)), is(false)); assertThat(predicate.test(context, row(100.5)), is(false)); assertThat(predicate.test(context, row(null)), is(true)); @@ -517,6 +528,7 @@ void doubleTest() throws Exception { IgnitePredicate predicate = factory.predicate("val IS NOT NULL", inputType); assertThat(predicate.test(context, row(-100.5)), is(true)); + assertThat(predicate.test(context, row(-0.0d)), is(true)); assertThat(predicate.test(context, row(0.0)), is(true)); assertThat(predicate.test(context, row(100.5)), is(true)); assertThat(predicate.test(context, row(null)), is(false)); @@ -529,6 +541,7 @@ void doubleTest() throws Exception { IgnitePredicate predicate = factory.predicate("val < 0.1", inputType); assertThat(predicate.test(context, row(-100.5)), is(true)); + assertThat(predicate.test(context, row(-0.0d)), is(true)); assertThat(predicate.test(context, row(0.0)), is(true)); assertThat(predicate.test(context, row(100.5)), is(false)); assertThat(predicate.test(context, row(null)), is(false)); @@ -541,12 +554,13 @@ void doubleTest() throws Exception { IgnitePredicate predicate = factory.predicate("val > 0.1", inputType); assertThat(predicate.test(context, row(-100.5)), is(false)); + assertThat(predicate.test(context, row(-0.0d)), is(false)); assertThat(predicate.test(context, row(0.0)), is(false)); assertThat(predicate.test(context, row(100.5)), is(true)); assertThat(predicate.test(context, row(null)), is(false)); assertThat(predicate.test(context, row(Double.POSITIVE_INFINITY)), is(true)); assertThat(predicate.test(context, row(Double.NEGATIVE_INFINITY)), is(false)); - assertThat(predicate.test(context, row(Double.NaN)), is(false)); + assertThat(predicate.test(context, row(Double.NaN)), is(true)); } } diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/SpecialFloatingPointValuesSerializationTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/SpecialFloatingPointValuesSerializationTest.java new file mode 100644 index 000000000000..6a506dc320b0 --- /dev/null +++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/SpecialFloatingPointValuesSerializationTest.java @@ -0,0 +1,95 @@ +/* + * 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.ignite.internal.sql.engine.planner; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.util.List; +import java.util.stream.Stream; +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.rex.RexNode; +import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.ignite.internal.sql.engine.rel.IgniteRel; +import org.apache.ignite.internal.sql.engine.rel.ProjectableFilterableTableScan; +import org.apache.ignite.internal.sql.engine.rel.explain.ExplainUtils; +import org.apache.ignite.internal.sql.engine.schema.IgniteSchema; +import org.apache.ignite.internal.sql.engine.trait.IgniteDistributions; +import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +/** + * Test to verify that special floating point values are correctly serialized and deserialized in the planner. + */ +public class SpecialFloatingPointValuesSerializationTest extends AbstractPlannerTest { + @ParameterizedTest + @MethodSource("specialValuesArgs") + void testSpecialValues(SqlTypeName typeName, String condition, String expectedCompiledCondition) throws Exception { + String sql = "SELECT c1 FROM test WHERE " + condition; + RelDataType sqlType = TYPE_FACTORY.createSqlType(typeName); + + IgniteSchema schema = createSchemaFrom( + tableBuilder -> + tableBuilder.name("TEST") + .addColumn("C1", IgniteTypeFactory.relDataTypeToNative(sqlType)) + .size(400) + .distribution(IgniteDistributions.single()) + ); + + IgniteRel plan = physicalPlan(sql, List.of(schema), null, List.of(), null); + + checkSplitAndSerialization(plan, List.of(schema)); + + log.info("statement: {}\n{}", sql, ExplainUtils.toString(plan)); + + var tableScan = (ProjectableFilterableTableScan) plan; + RexNode rexNode = tableScan.condition(); + + assertThat(rexNode, notNullValue()); + assertThat(rexNode.toString(), equalTo(expectedCompiledCondition)); + } + + private static Stream specialValuesArgs() { + return Stream.of( + Arguments.of(SqlTypeName.REAL, "c1 = -0.0::REAL", "=($t0, -0.0E0)"), + Arguments.of(SqlTypeName.REAL, "c1 = '-0.0'::REAL", "=($t0, -0.0E0)"), + Arguments.of(SqlTypeName.REAL, "c1 = 0.0::REAL", "=($t0, 0.0E0)"), + Arguments.of(SqlTypeName.REAL, "c1 = '0.0'::REAL", "=($t0, 0.0E0)"), + Arguments.of(SqlTypeName.REAL, "c1 = 'NaN'::REAL", "=($t0, NaN)"), + Arguments.of(SqlTypeName.REAL, "c1 = 'Infinity'::REAL", "=($t0, Infinity)"), + Arguments.of(SqlTypeName.REAL, "c1 = '-Infinity'::REAL", "=($t0, -Infinity)"), + Arguments.of(SqlTypeName.FLOAT, "c1 = -0.0::FLOAT", "=(CAST($t0):FLOAT, -0.0E0)"), + Arguments.of(SqlTypeName.FLOAT, "c1 = '-0.0'::FLOAT", "=(CAST($t0):FLOAT, -0.0E0)"), + Arguments.of(SqlTypeName.FLOAT, "c1 = 0.0::FLOAT", "=(CAST($t0):FLOAT, 0.0E0)"), + Arguments.of(SqlTypeName.FLOAT, "c1 = '0.0'::FLOAT", "=(CAST($t0):FLOAT, 0.0E0)"), + Arguments.of(SqlTypeName.FLOAT, "c1 = 'NaN'::FLOAT", "=(CAST($t0):FLOAT, NaN)"), + Arguments.of(SqlTypeName.FLOAT, "c1 = 'Infinity'::FLOAT", "=(CAST($t0):FLOAT, Infinity)"), + Arguments.of(SqlTypeName.FLOAT, "c1 = '-Infinity'::FLOAT", "=(CAST($t0):FLOAT, -Infinity)"), + Arguments.of(SqlTypeName.DOUBLE, "c1 = -0.0::DOUBLE", "=($t0, -0.0E0)"), + Arguments.of(SqlTypeName.DOUBLE, "c1 = '-0.0'::DOUBLE", "=($t0, -0.0E0)"), + Arguments.of(SqlTypeName.DOUBLE, "c1 = 0.0::DOUBLE", "=($t0, 0.0E0)"), + Arguments.of(SqlTypeName.DOUBLE, "c1 = '0.0'::DOUBLE", "=($t0, 0.0E0)"), + Arguments.of(SqlTypeName.DOUBLE, "c1 = 'NaN'::DOUBLE", "=($t0, NaN)"), + Arguments.of(SqlTypeName.DOUBLE, "c1 = 'Infinity'::DOUBLE", "=($t0, Infinity)"), + Arguments.of(SqlTypeName.DOUBLE, "c1 = '-Infinity'::DOUBLE", "=($t0, -Infinity)") + ); + } +} From 756ca3a5f8d5b8554403ec6bd7936f20f605a40d Mon Sep 17 00:00:00 2001 From: Pavel Pereslegin Date: Wed, 8 Apr 2026 13:00:00 +0300 Subject: [PATCH 2/2] IGNITE-26613 (minor) Combine expression tests --- .../exp/SqlExpressionFactoryImplTest.java | 141 ++++++++---------- 1 file changed, 60 insertions(+), 81 deletions(-) diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/SqlExpressionFactoryImplTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/SqlExpressionFactoryImplTest.java index 99dcdb80f8f5..107808b4fa91 100644 --- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/SqlExpressionFactoryImplTest.java +++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/SqlExpressionFactoryImplTest.java @@ -830,54 +830,18 @@ private static Stream numericLiterals() { } @ParameterizedTest - @MethodSource("doubleSpecialValuesArgs") - public void testDoubleSpecialValuesComparison( + @MethodSource("approximateTypeSpecialValuesComparisonArgs") + public void testApproximateTypeSpecialValuesComparison( + SqlTypeName type, SqlOperator op, - double leftVal, - double rightVal, - boolean expectedResult - ) { - RexBuilder rexBuilder = Commons.rexBuilder(); - IgniteTypeFactory tf = Commons.typeFactory(); - - RelDataType colType = tf.createSqlType(SqlTypeName.DOUBLE); - RelDataType rowType = new Builder(tf) - .add("c1", colType) - .add("c2", colType) - .build(); - - RexInputRef ref1 = rexBuilder.makeInputRef(rowType, 0); - RexInputRef ref2 = rexBuilder.makeInputRef(rowType, 1); - RexNode filter = rexBuilder.makeCall(op, List.of(ref1, ref2)); - - SqlPredicate predicate = expFactory.predicate(filter, rowType); - assertEquals(expectedResult, predicate.test(ctx, new Object[]{leftVal, rightVal}), - "Failed for " + leftVal + " " + op.getName() + " " + rightVal); - } - - private static List doubleSpecialValuesArgs() { - return makeSpecialFloatArgs( - Double.NaN, - Double.POSITIVE_INFINITY, - Double.NEGATIVE_INFINITY, - 1.0d, - 0.0d, - -0.0d - ); - } - - @ParameterizedTest - @MethodSource("floatSpecialValuesArgs") - public void testFloatSpecialValues( - SqlOperator op, - float left, - float right, + Number left, + Number right, boolean expected ) { RexBuilder rexBuilder = Commons.rexBuilder(); IgniteTypeFactory tf = Commons.typeFactory(); - RelDataType colType = tf.createSqlType(SqlTypeName.REAL); + RelDataType colType = tf.createSqlType(type); RelDataType rowType = new Builder(tf) .add("c1", colType) .add("c2", colType) @@ -892,18 +856,33 @@ public void testFloatSpecialValues( "Failed for " + left + " " + op.getName() + " " + right); } - private static List floatSpecialValuesArgs() { - return makeSpecialFloatArgs( + private static List approximateTypeSpecialValuesComparisonArgs() { + List args = new ArrayList<>(); + + args.addAll(makeApproximateTypeSpecialValuesComparisonArgs( + SqlTypeName.FLOAT, Float.NaN, Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY, 1.0f, 0.0f, -0.0f - ); + )); + args.addAll(makeApproximateTypeSpecialValuesComparisonArgs( + SqlTypeName.DOUBLE, + Double.NaN, + Double.POSITIVE_INFINITY, + Double.NEGATIVE_INFINITY, + 1.0d, + 0.0d, + -0.0d + )); + + return args; } - private static List makeSpecialFloatArgs( + private static List makeApproximateTypeSpecialValuesComparisonArgs( + SqlTypeName type, Number nan, Number posInf, Number negInf, @@ -914,77 +893,77 @@ private static List makeSpecialFloatArgs( List args = new ArrayList<>(); // NaN = NaN : true - args.add(Arguments.of(SqlStdOperatorTable.EQUALS, nan, nan, true)); + args.add(Arguments.of(type, SqlStdOperatorTable.EQUALS, nan, nan, true)); // NaN = 1.0 : false - args.add(Arguments.of(SqlStdOperatorTable.EQUALS, nan, one, false)); + args.add(Arguments.of(type, SqlStdOperatorTable.EQUALS, nan, one, false)); // 1.0 = NaN : false - args.add(Arguments.of(SqlStdOperatorTable.EQUALS, one, nan, false)); + args.add(Arguments.of(type, SqlStdOperatorTable.EQUALS, one, nan, false)); // -0.0 = -0.0 : true - args.add(Arguments.of(SqlStdOperatorTable.EQUALS, negZero, negZero, true)); + args.add(Arguments.of(type, SqlStdOperatorTable.EQUALS, negZero, negZero, true)); // -0.0 = 0.0 : false - args.add(Arguments.of(SqlStdOperatorTable.EQUALS, negZero, zero, false)); + args.add(Arguments.of(type, SqlStdOperatorTable.EQUALS, negZero, zero, false)); // 1.0 = 1.0 : true - args.add(Arguments.of(SqlStdOperatorTable.EQUALS, one, one, true)); + args.add(Arguments.of(type, SqlStdOperatorTable.EQUALS, one, one, true)); // NaN <> NaN should be false - args.add(Arguments.of(SqlStdOperatorTable.NOT_EQUALS, nan, nan, false)); + args.add(Arguments.of(type, SqlStdOperatorTable.NOT_EQUALS, nan, nan, false)); // NaN <> 1.0 should be true - args.add(Arguments.of(SqlStdOperatorTable.NOT_EQUALS, nan, one, true)); + args.add(Arguments.of(type, SqlStdOperatorTable.NOT_EQUALS, nan, one, true)); // -0.0 <> -0.0 : false - args.add(Arguments.of(SqlStdOperatorTable.NOT_EQUALS, negZero, negZero, false)); + args.add(Arguments.of(type, SqlStdOperatorTable.NOT_EQUALS, negZero, negZero, false)); // -0.0 <> 0.0 : true - args.add(Arguments.of(SqlStdOperatorTable.NOT_EQUALS, negZero, zero, true)); + args.add(Arguments.of(type, SqlStdOperatorTable.NOT_EQUALS, negZero, zero, true)); // NaN > any non-NaN value : true - args.add(Arguments.of(SqlStdOperatorTable.GREATER_THAN, nan, one, true)); - args.add(Arguments.of(SqlStdOperatorTable.GREATER_THAN, nan, posInf, true)); + args.add(Arguments.of(type, SqlStdOperatorTable.GREATER_THAN, nan, one, true)); + args.add(Arguments.of(type, SqlStdOperatorTable.GREATER_THAN, nan, posInf, true)); // any non-NaN > NaN : false - args.add(Arguments.of(SqlStdOperatorTable.GREATER_THAN, one, nan, false)); + args.add(Arguments.of(type, SqlStdOperatorTable.GREATER_THAN, one, nan, false)); // NaN > NaN : false - args.add(Arguments.of(SqlStdOperatorTable.GREATER_THAN, nan, nan, false)); + args.add(Arguments.of(type, SqlStdOperatorTable.GREATER_THAN, nan, nan, false)); // -0.0 > -0.0 : false - args.add(Arguments.of(SqlStdOperatorTable.GREATER_THAN, negZero, negZero, false)); + args.add(Arguments.of(type, SqlStdOperatorTable.GREATER_THAN, negZero, negZero, false)); // -0.0 > 0.0 : false - args.add(Arguments.of(SqlStdOperatorTable.GREATER_THAN, negZero, zero, false)); + args.add(Arguments.of(type, SqlStdOperatorTable.GREATER_THAN, negZero, zero, false)); // 0.0 > -0.0 : true - args.add(Arguments.of(SqlStdOperatorTable.GREATER_THAN, zero, negZero, true)); + args.add(Arguments.of(type, SqlStdOperatorTable.GREATER_THAN, zero, negZero, true)); // NaN >= NaN : true - args.add(Arguments.of(SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, nan, nan, true)); + args.add(Arguments.of(type, SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, nan, nan, true)); // 1.0 >= NaN : false - args.add(Arguments.of(SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, one, nan, false)); + args.add(Arguments.of(type, SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, one, nan, false)); // -0.0 >= -0.0 : true - args.add(Arguments.of(SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, negZero, negZero, true)); + args.add(Arguments.of(type, SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, negZero, negZero, true)); // -0.0 >= 0.0 : false - args.add(Arguments.of(SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, negZero, zero, false)); + args.add(Arguments.of(type, SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, negZero, zero, false)); // 0.0 >= -0.0 : true - args.add(Arguments.of(SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, zero, negZero, true)); + args.add(Arguments.of(type, SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, zero, negZero, true)); // 1.0 < NaN : true - args.add(Arguments.of(SqlStdOperatorTable.LESS_THAN, one, nan, true)); + args.add(Arguments.of(type, SqlStdOperatorTable.LESS_THAN, one, nan, true)); // NaN < 1.0 : false - args.add(Arguments.of(SqlStdOperatorTable.LESS_THAN, nan, one, false)); + args.add(Arguments.of(type, SqlStdOperatorTable.LESS_THAN, nan, one, false)); // NaN < NaN : false - args.add(Arguments.of(SqlStdOperatorTable.LESS_THAN, nan, nan, false)); + args.add(Arguments.of(type, SqlStdOperatorTable.LESS_THAN, nan, nan, false)); // -0.0 < -0.0 : false - args.add(Arguments.of(SqlStdOperatorTable.LESS_THAN, negZero, negZero, false)); + args.add(Arguments.of(type, SqlStdOperatorTable.LESS_THAN, negZero, negZero, false)); // -0.0 < 0.0 : true - args.add(Arguments.of(SqlStdOperatorTable.LESS_THAN, negZero, zero, true)); + args.add(Arguments.of(type, SqlStdOperatorTable.LESS_THAN, negZero, zero, true)); // 0.0 < -0.0 : false - args.add(Arguments.of(SqlStdOperatorTable.LESS_THAN, zero, negZero, false)); + args.add(Arguments.of(type, SqlStdOperatorTable.LESS_THAN, zero, negZero, false)); // NaN <= NaN : true - args.add(Arguments.of(SqlStdOperatorTable.LESS_THAN_OR_EQUAL, nan, nan, true)); + args.add(Arguments.of(type, SqlStdOperatorTable.LESS_THAN_OR_EQUAL, nan, nan, true)); // -0.0 <= -0.0 : true - args.add(Arguments.of(SqlStdOperatorTable.LESS_THAN_OR_EQUAL, negZero, negZero, true)); + args.add(Arguments.of(type, SqlStdOperatorTable.LESS_THAN_OR_EQUAL, negZero, negZero, true)); // -0.0 <= 0.0 : true - args.add(Arguments.of(SqlStdOperatorTable.LESS_THAN_OR_EQUAL, negZero, zero, true)); + args.add(Arguments.of(type, SqlStdOperatorTable.LESS_THAN_OR_EQUAL, negZero, zero, true)); // 0.0 <= -0.0 : false - args.add(Arguments.of(SqlStdOperatorTable.LESS_THAN_OR_EQUAL, zero, negZero, false)); + args.add(Arguments.of(type, SqlStdOperatorTable.LESS_THAN_OR_EQUAL, zero, negZero, false)); - args.add(Arguments.of(SqlStdOperatorTable.EQUALS, posInf, posInf, true)); - args.add(Arguments.of(SqlStdOperatorTable.LESS_THAN, negInf, posInf, true)); - args.add(Arguments.of(SqlStdOperatorTable.EQUALS, zero, zero, true)); + args.add(Arguments.of(type, SqlStdOperatorTable.EQUALS, posInf, posInf, true)); + args.add(Arguments.of(type, SqlStdOperatorTable.LESS_THAN, negInf, posInf, true)); + args.add(Arguments.of(type, SqlStdOperatorTable.EQUALS, zero, zero, true)); return args; }