diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/expressions/Comparisons.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/expressions/Comparisons.java index 1305ab01c4..2adfe8f5cb 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/expressions/Comparisons.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/expressions/Comparisons.java @@ -73,6 +73,7 @@ import com.apple.foundationdb.record.util.ProtoUtils; import com.apple.foundationdb.tuple.ByteArrayUtil; import com.apple.foundationdb.tuple.ByteArrayUtil2; +import com.apple.foundationdb.tuple.Tuple; import com.google.auto.service.AutoService; import com.google.common.base.Suppliers; import com.google.common.base.Verify; @@ -211,11 +212,9 @@ private static Comparable toComparable(@Nullable Object obj) { } } - @Nullable - public static Object toClassWithRealEquals(@Nullable Object obj) { - if (obj == null) { - return null; - } else if (obj instanceof ByteString) { + @Nonnull + public static Object toClassWithRealEquals(@Nonnull Object obj) { + if (obj instanceof ByteString) { return obj; } else if (obj instanceof byte[]) { return ByteString.copyFrom((byte[])obj); @@ -231,30 +230,30 @@ public static Object toClassWithRealEquals(@Nullable Object obj) { } @SuppressWarnings("unchecked") - public static int compare(@Nullable Object fieldValue, @Nullable Object comparand) { - if (fieldValue == null) { - if (comparand == null) { - return 0; - } else { - return -1; - } - } else if (comparand == null) { - return 1; + public static int compare(@Nonnull Object fieldValue, @Nonnull Object comparand) { + return toComparable(fieldValue).compareTo(toComparable(comparand)); + } + + @SpotBugsSuppressWarnings("NP_BOOLEAN_RETURN_NULL") + private static boolean compareEquals(@Nonnull Object value, @Nonnull Object comparand) { + if (value instanceof Message) { + return MessageHelpers.compareMessageEquals(value, comparand); } else { - return toComparable(fieldValue).compareTo(toComparable(comparand)); + return toClassWithRealEquals(value).equals(toClassWithRealEquals(comparand)); } } - @Nullable @SpotBugsSuppressWarnings("NP_BOOLEAN_RETURN_NULL") - private static Boolean compareEquals(Object value, Object comparand) { - if (value == null || comparand == null) { - return null; + private static boolean compareNotDistinctFrom(@Nullable Object value, @Nullable Object comparand) { + if (value == null && comparand == null) { + return true; + } else if (value == null || comparand == null) { + return false; } else { if (value instanceof Message) { return MessageHelpers.compareMessageEquals(value, comparand); } else { - return toClassWithRealEquals(value).equals(toClassWithRealEquals(comparand)); + return toClassWithRealEquals(Objects.requireNonNull(value)).equals(toClassWithRealEquals(Objects.requireNonNull(comparand))); } } } @@ -283,6 +282,9 @@ private static Boolean compareStartsWith(@Nullable Object value, @Nullable Objec @Nullable @SpotBugsSuppressWarnings("NP_BOOLEAN_RETURN_NULL") private static Boolean compareLike(@Nullable Object value, @Nullable Object pattern) { + if (value == null) { + return null; + } if (!(value instanceof String)) { throw new RecordCoreException("Illegal comparand value type: " + value); } @@ -311,7 +313,12 @@ private static Boolean compareListStartsWith(@Nullable Object value, @Nonnull Li if (i > list.size()) { return false; } - if (!toClassWithRealEquals(comparand.get(i)).equals(toClassWithRealEquals(list.get(i)))) { + if (comparand.get(i) == null && list.get(i) == null) { + continue; + } + if (comparand.get(i) == null || list.get(i) == null) { + return false; + } else if (!toClassWithRealEquals(comparand.get(i)).equals(toClassWithRealEquals(list.get(i)))) { return false; } } @@ -336,11 +343,12 @@ private static Boolean compareIn(@Nullable Object value, @Nullable Object compar return true; } } else { - if (toClassWithRealEquals(value).equals(toClassWithRealEquals(comparandItem))) { + if (comparandItem == null) { + hasNull = true; + } else if (toClassWithRealEquals(value).equals(toClassWithRealEquals(comparandItem))) { return true; } } - hasNull |= comparandItem == null; } return hasNull ? null : false; } else { @@ -632,7 +640,9 @@ public enum Type { @API(API.Status.EXPERIMENTAL) SORT(false), @API(API.Status.EXPERIMENTAL) - LIKE; + LIKE, + IS_DISTINCT_FROM(false), + NOT_DISTINCT_FROM(true); @Nonnull private static final Supplier> protoEnumBiMapSupplier = @@ -682,7 +692,7 @@ private static BiMap getProtoEnumBiMap() { } @Nullable - public static Type invertComparisonType(@Nonnull final Comparisons.Type type) { + public static Type invertComparisonType(@Nonnull final Type type) { if (type.isUnary()) { return null; } @@ -705,28 +715,44 @@ public static Type invertComparisonType(@Nonnull final Comparisons.Type type) { @Nullable @SpotBugsSuppressWarnings("NP_BOOLEAN_RETURN_NULL") public static Boolean evalComparison(@Nonnull Type type, @Nullable Object value, @Nullable Object comparand) { - if (value == null) { - return null; - } switch (type) { case STARTS_WITH: return compareStartsWith(value, comparand); case IN: return compareIn(value, comparand); case EQUALS: + if (value == null || comparand == null) { + return null; + } return compareEquals(value, comparand); case NOT_EQUALS: - if (comparand == null) { + if (value == null || comparand == null) { return null; } return !compareEquals(value, comparand); + case IS_DISTINCT_FROM: + return !compareNotDistinctFrom(value, comparand); + case NOT_DISTINCT_FROM: + return compareNotDistinctFrom(value, comparand); case LESS_THAN: + if (value == null || comparand == null) { + return null; + } return compare(value, comparand) < 0; case LESS_THAN_OR_EQUALS: + if (value == null || comparand == null) { + return null; + } return compare(value, comparand) <= 0; case GREATER_THAN: + if (value == null || comparand == null) { + return null; + } return compare(value, comparand) > 0; case GREATER_THAN_OR_EQUALS: + if (value == null || comparand == null) { + return null; + } return compare(value, comparand) >= 0; case LIKE: return compareLike(value, comparand); @@ -823,7 +849,7 @@ default Object getComparand() { /** * Get whether the comparison is with the result of a multi-column key. - * If so, {@link #getComparand} will return a {@link com.apple.foundationdb.tuple.Tuple}. + * If so, {@link #getComparand} will return a {@link Tuple}. * @return {@code true} if the comparand is for multiple key columns */ default boolean hasMultiColumnComparand() { @@ -1302,9 +1328,7 @@ public ConstrainedBoolean semanticEqualsTyped(@Nonnull final Comparison other, @ public Boolean eval(@Nullable FDBRecordStoreBase store, @Nonnull EvaluationContext context, @Nullable Object value) { // this is at evaluation time --> always use the context binding final Object comparand = getComparand(store, context); - if (comparand == null) { - return null; - } else if (comparand == COMPARISON_SKIPPED_BINDING) { + if (comparand == COMPARISON_SKIPPED_BINDING) { return Boolean.TRUE; } else { return evalComparison(type, value, comparand); @@ -1620,9 +1644,7 @@ public ConstrainedBoolean semanticEqualsTyped(@Nonnull final Comparison other, @ public Boolean eval(@Nullable FDBRecordStoreBase store, @Nonnull EvaluationContext context, @Nullable Object v) { // this is at evaluation time --> always use the context binding final Object comparand = getComparand(store, context); - if (comparand == null) { - return null; - } else if (comparand == COMPARISON_SKIPPED_BINDING) { + if (comparand == COMPARISON_SKIPPED_BINDING) { return Boolean.TRUE; } else { return evalComparison(type, v, comparand); @@ -1739,7 +1761,7 @@ public static class ListComparison implements Comparison { @SuppressWarnings("rawtypes") private final List comparand; @Nullable - private final Descriptors.FieldDescriptor.JavaType javaType; + private final JavaType javaType; @Nonnull @SuppressWarnings("rawtypes") @@ -1757,8 +1779,8 @@ public ListComparison(@Nonnull Type type, @Nonnull List comparand) { default: throw new RecordCoreException("ListComparison only supports EQUALS, NOT_EQUALS, STARTS_WITH and IN"); } - if (comparand == null || (this.type == Type.IN && comparand.stream().anyMatch(o -> o == null))) { - throw new NullPointerException("List comparand is null, or contains null"); + if (this.type == Type.IN && comparand.stream().anyMatch(o -> o == null)) { + throw new NullPointerException("List comparand contains null"); } if (comparand.isEmpty()) { javaType = null; @@ -1772,10 +1794,10 @@ public ListComparison(@Nonnull Type type, @Nonnull List comparand) { } } this.comparand = comparand; - this.comparandListWithEqualsSupplier = Suppliers.memoize(() -> Lists.transform(comparand, Comparisons::toClassWithRealEquals)); + this.comparandListWithEqualsSupplier = Suppliers.memoize(() -> Lists.transform(comparand, obj -> obj != null ? toClassWithRealEquals(obj) : null)); } - private static Descriptors.FieldDescriptor.JavaType getJavaType(@Nonnull Object o) { + private static JavaType getJavaType(@Nonnull Object o) { if (o instanceof Boolean) { return JavaType.BOOLEAN; } else if (o instanceof ByteString || o instanceof byte[]) { diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/RangeConstraints.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/RangeConstraints.java index d0fe0d7103..1731b89b85 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/RangeConstraints.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/RangeConstraints.java @@ -763,6 +763,8 @@ private boolean canBeUsedInScanPrefix(@Nonnull final Comparisons.Comparison comp case STARTS_WITH: case NOT_NULL: case IS_NULL: + case NOT_DISTINCT_FROM: + case IS_DISTINCT_FROM: return true; case TEXT_CONTAINS_ALL: case TEXT_CONTAINS_ALL_WITHIN: diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/RelOpValue.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/RelOpValue.java index 0ab1f9c693..7226ff035d 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/RelOpValue.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/RelOpValue.java @@ -184,6 +184,7 @@ private static Optional promoteOperandsAndCreatePredicate(@Nulla @Nonnull Value leftChild, @Nonnull Value rightChild, @Nonnull final Comparisons.Type comparisonType) { + // maximumType may return null, but only for non-primitive types which is not possible here final var maxtype = Verify.verifyNotNull(Type.maximumType(leftChild.getResultType(), rightChild.getResultType())); @@ -255,6 +256,8 @@ private static Comparisons.Type swapBinaryComparisonOperator(@Nonnull Comparison switch (type) { case EQUALS: case NOT_EQUALS: + case NOT_DISTINCT_FROM: + case IS_DISTINCT_FROM: return type; case LESS_THAN: return Comparisons.Type.GREATER_THAN; @@ -290,6 +293,7 @@ private static Value encapsulate(@Nonnull final String functionName, } else { final Typed arg1 = arguments.get(1); final Type res1 = arg1.getResultType(); + SemanticException.check(res1.isPrimitive() || res1.isEnum() || res1.isUuid(), SemanticException.ErrorCode.COMPARAND_TO_COMPARISON_IS_OF_COMPLEX_TYPE); final BinaryPhysicalOperator physicalOperator = @@ -451,6 +455,36 @@ private static Value encapsulate(@Nonnull BuiltInFunction builtInFunction } } + /** + * The {@code isDistinctFrom} function. + */ + @AutoService(BuiltInFunction.class) + public static class IsDistinctFromFn extends BuiltInFunction { + public IsDistinctFromFn() { + super("isDistinctFrom", + List.of(new Type.Any(), new Type.Any()), IsDistinctFromFn::encapsulate); + } + + private static Value encapsulate(@Nonnull BuiltInFunction builtInFunction, @Nonnull final List arguments) { + return RelOpValue.encapsulate(builtInFunction.getFunctionName(), Comparisons.Type.IS_DISTINCT_FROM, arguments); + } + } + + /** + * The {@code notDistinctFrom} function. + */ + @AutoService(BuiltInFunction.class) + public static class NotDistinctFromFn extends BuiltInFunction { + public NotDistinctFromFn() { + super("notDistinctFrom", + List.of(new Type.Any(), new Type.Any()), NotDistinctFromFn::encapsulate); + } + + private static Value encapsulate(@Nonnull BuiltInFunction builtInFunction, @Nonnull final List arguments) { + return RelOpValue.encapsulate(builtInFunction.getFunctionName(), Comparisons.Type.NOT_DISTINCT_FROM, arguments); + } + } + private enum BinaryPhysicalOperator { // TODO think about equality epsilon for floating-point types. EQ_BU(Comparisons.Type.EQUALS, Type.TypeCode.BOOLEAN, Type.TypeCode.UNKNOWN, (l, r) -> null), @@ -764,6 +798,7 @@ private enum BinaryPhysicalOperator { GTE_UID(Comparisons.Type.GREATER_THAN_OR_EQUALS, Type.TypeCode.UNKNOWN, Type.TypeCode.UUID, (l, r) -> null), GTE_IDU(Comparisons.Type.GREATER_THAN_OR_EQUALS, Type.TypeCode.UUID, Type.TypeCode.UNKNOWN, (l, r) -> null), + EQ_BN(Comparisons.Type.EQUALS, Type.TypeCode.BOOLEAN, Type.TypeCode.NULL, (l, r) -> null), EQ_IN(Comparisons.Type.EQUALS, Type.TypeCode.INT, Type.TypeCode.NULL, (l, r) -> null), EQ_LN(Comparisons.Type.EQUALS, Type.TypeCode.LONG, Type.TypeCode.NULL, (l, r) -> null), @@ -894,6 +929,119 @@ private enum BinaryPhysicalOperator { GT_IDN(Comparisons.Type.GREATER_THAN, Type.TypeCode.UUID, Type.TypeCode.NULL, (l, r) -> null), GTE_NID(Comparisons.Type.GREATER_THAN_OR_EQUALS, Type.TypeCode.NULL, Type.TypeCode.UUID, (l, r) -> null), GTE_IDN(Comparisons.Type.GREATER_THAN_OR_EQUALS, Type.TypeCode.UUID, Type.TypeCode.NULL, (l, r) -> null), + + IS_DISTINCT_FROM_BU(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.BOOLEAN, Type.TypeCode.NULL, (l, r) -> Objects.nonNull(l)), + IS_DISTINCT_FROM_BB(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.BOOLEAN, Type.TypeCode.BOOLEAN, (l, r) -> (boolean)l != (boolean)r), + IS_DISTINCT_FROM_IU(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.INT, Type.TypeCode.NULL, (l, r) -> Objects.nonNull(l)), + IS_DISTINCT_FROM_II(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.INT, Type.TypeCode.INT, (l, r) -> (int)l != (int)r), + IS_DISTINCT_FROM_IL(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.INT, Type.TypeCode.LONG, (l, r) -> (int)l != (long)r), + IS_DISTINCT_FROM_IF(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.INT, Type.TypeCode.FLOAT, (l, r) -> (int)l != (float)r), + IS_DISTINCT_FROM_ID(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.INT, Type.TypeCode.DOUBLE, (l, r) -> (int)l != (double)r), + IS_DISTINCT_FROM_LU(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.LONG, Type.TypeCode.NULL, (l, r) -> Objects.nonNull(l)), + IS_DISTINCT_FROM_LI(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.LONG, Type.TypeCode.INT, (l, r) -> (long)l != (int)r), + IS_DISTINCT_FROM_LL(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.LONG, Type.TypeCode.LONG, (l, r) -> (long)l != (long)r), + IS_DISTINCT_FROM_LF(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.LONG, Type.TypeCode.FLOAT, (l, r) -> (long)l != (float)r), + IS_DISTINCT_FROM_LD(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.LONG, Type.TypeCode.DOUBLE, (l, r) -> (long)l != (double)r), + IS_DISTINCT_FROM_FU(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.FLOAT, Type.TypeCode.NULL, (l, r) -> Objects.nonNull(l)), + IS_DISTINCT_FROM_FI(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.FLOAT, Type.TypeCode.INT, (l, r) -> (float)l != (int)r), + IS_DISTINCT_FROM_FL(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.FLOAT, Type.TypeCode.LONG, (l, r) -> (float)l != (long)r), + IS_DISTINCT_FROM_FF(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.FLOAT, Type.TypeCode.FLOAT, (l, r) -> (float)l != (float)r), + IS_DISTINCT_FROM_FD(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.FLOAT, Type.TypeCode.DOUBLE, (l, r) -> (float)l != (double)r), + IS_DISTINCT_FROM_DU(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.DOUBLE, Type.TypeCode.NULL, (l, r) -> Objects.nonNull(l)), + IS_DISTINCT_FROM_DI(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.DOUBLE, Type.TypeCode.INT, (l, r) -> (double)l != (int)r), + IS_DISTINCT_FROM_DL(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.DOUBLE, Type.TypeCode.LONG, (l, r) -> (double)l != (long)r), + IS_DISTINCT_FROM_DF(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.DOUBLE, Type.TypeCode.FLOAT, (l, r) -> (double)l != (float)r), + IS_DISTINCT_FROM_DD(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.DOUBLE, Type.TypeCode.DOUBLE, (l, r) -> (double)l != (double)r), + IS_DISTINCT_FROM_SU(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.STRING, Type.TypeCode.NULL, (l, r) -> Objects.nonNull(l)), + IS_DISTINCT_FROM_SS(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.STRING, Type.TypeCode.STRING, (l, r) -> !l.equals(r)), // TODO: locale-aware comparison + IS_DISTINCT_FROM_UB(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.NULL, Type.TypeCode.BOOLEAN, (l, r) -> Objects.nonNull(r)), + IS_DISTINCT_FROM_UI(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.NULL, Type.TypeCode.INT, (l, r) -> Objects.nonNull(r)), + IS_DISTINCT_FROM_UL(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.NULL, Type.TypeCode.LONG, (l, r) -> Objects.nonNull(r)), + IS_DISTINCT_FROM_UF(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.NULL, Type.TypeCode.FLOAT, (l, r) -> Objects.nonNull(r)), + IS_DISTINCT_FROM_UD(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.NULL, Type.TypeCode.DOUBLE, (l, r) -> Objects.nonNull(r)), + IS_DISTINCT_FROM_US(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.NULL, Type.TypeCode.STRING, (l, r) -> Objects.nonNull(r)), + IS_DISTINCT_FROM_UV(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.NULL, Type.TypeCode.VERSION, (l, r) -> Objects.nonNull(r)), + IS_DISTINCT_FROM_VU(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.VERSION, Type.TypeCode.NULL, (l, r) -> null), + IS_DISTINCT_FROM_VV(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.VERSION, Type.TypeCode.VERSION, (l, r) -> !l.equals(r)), + IS_DISTINCT_FROM_BYU(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.BYTES, Type.TypeCode.NULL, (l, r) -> Objects.nonNull(l)), + IS_DISTINCT_FROM_BYBY(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.BYTES, Type.TypeCode.BYTES, (l, r) -> Comparisons.evalComparison(Comparisons.Type.IS_DISTINCT_FROM, l, r)), + IS_DISTINCT_FROM_UBY(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.NULL, Type.TypeCode.BYTES, (l, r) -> Objects.nonNull(r)), + IS_DISTINCT_FROM_EE(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.ENUM, Type.TypeCode.ENUM, (l, r) -> Comparisons.evalComparison(Comparisons.Type.IS_DISTINCT_FROM, l, r)), + IS_DISTINCT_FROM_ES(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.ENUM, Type.TypeCode.STRING, (l, r) -> { + final var otherValue = PromoteValue.PhysicalOperator.stringToEnumValue(((Descriptors.EnumValueDescriptor) l).getType(), (String) r); + return Comparisons.evalComparison(Comparisons.Type.IS_DISTINCT_FROM, l, otherValue); + }), + IS_DISTINCT_FROM_SE(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.STRING, Type.TypeCode.ENUM, (l, r) -> { + final var otherValue = PromoteValue.PhysicalOperator.stringToEnumValue(((Descriptors.EnumValueDescriptor) r).getType(), (String) l); + return Comparisons.evalComparison(Comparisons.Type.EQUALS, otherValue, r); + }), + IS_DISTINCT_FROM_EU(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.ENUM, Type.TypeCode.NULL, (l, r) -> Objects.nonNull(l)), + IS_DISTINCT_FROM_UE(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.NULL, Type.TypeCode.ENUM, (l, r) -> Objects.nonNull(r)), + IS_DISTINCT_FROM_IDID(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.UUID, Type.TypeCode.UUID, (l, r) -> Comparisons.evalComparison(Comparisons.Type.IS_DISTINCT_FROM, l, r)), + IS_DISTINCT_FROM_IDS(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.UUID, Type.TypeCode.STRING, (l, r) -> Comparisons.evalComparison(Comparisons.Type.IS_DISTINCT_FROM, l, PromoteValue.PhysicalOperator.stringToUuidValue((String) r))), + IS_DISTINCT_FROM_SID(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.STRING, Type.TypeCode.UUID, (l, r) -> Comparisons.evalComparison(Comparisons.Type.IS_DISTINCT_FROM, PromoteValue.PhysicalOperator.stringToUuidValue((String) l), r)), + IS_DISTINCT_FROM_UID(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.NULL, Type.TypeCode.UUID, (l, r) -> Objects.nonNull(r)), + IS_DISTINCT_FROM_IDU(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.UUID, Type.TypeCode.NULL, (l, r) -> Objects.nonNull(l)), + IS_DISTINCT_FROM_NN(Comparisons.Type.IS_DISTINCT_FROM, Type.TypeCode.NULL, Type.TypeCode.NULL, (l, r) -> false), + + NOT_DISTINCT_FROM_BU(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.BOOLEAN, Type.TypeCode.NULL, (l, r) -> Objects.isNull(l)), + NOT_DISTINCT_FROM_BB(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.BOOLEAN, Type.TypeCode.BOOLEAN, (l, r) -> (boolean)l == (boolean)r), + NOT_DISTINCT_FROM_IU(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.INT, Type.TypeCode.NULL, (l, r) -> Objects.isNull(l)), + NOT_DISTINCT_FROM_II(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.INT, Type.TypeCode.INT, (l, r) -> (int)l == (int)r), + NOT_DISTINCT_FROM_IL(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.INT, Type.TypeCode.LONG, (l, r) -> (int)l == (long)r), + NOT_DISTINCT_FROM_IF(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.INT, Type.TypeCode.FLOAT, (l, r) -> (int)l == (float)r), + NOT_DISTINCT_FROM_ID(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.INT, Type.TypeCode.DOUBLE, (l, r) -> (int)l == (double)r), + NOT_DISTINCT_FROM_LU(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.LONG, Type.TypeCode.NULL, (l, r) -> Objects.isNull(l)), + NOT_DISTINCT_FROM_LI(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.LONG, Type.TypeCode.INT, (l, r) -> (long)l == (int)r), + NOT_DISTINCT_FROM_LL(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.LONG, Type.TypeCode.LONG, (l, r) -> (long)l == (long)r), + NOT_DISTINCT_FROM_LF(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.LONG, Type.TypeCode.FLOAT, (l, r) -> (long)l == (float)r), + NOT_DISTINCT_FROM_LD(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.LONG, Type.TypeCode.DOUBLE, (l, r) -> (long)l == (double)r), + NOT_DISTINCT_FROM_FU(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.FLOAT, Type.TypeCode.NULL, (l, r) -> Objects.isNull(l)), + NOT_DISTINCT_FROM_FI(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.FLOAT, Type.TypeCode.INT, (l, r) -> (float)l == (int)r), + NOT_DISTINCT_FROM_FL(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.FLOAT, Type.TypeCode.LONG, (l, r) -> (float)l == (long)r), + NOT_DISTINCT_FROM_FF(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.FLOAT, Type.TypeCode.FLOAT, (l, r) -> (float)l == (float)r), + NOT_DISTINCT_FROM_FD(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.FLOAT, Type.TypeCode.DOUBLE, (l, r) -> (float)l == (double)r), + NOT_DISTINCT_FROM_DU(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.DOUBLE, Type.TypeCode.NULL, (l, r) -> Objects.isNull(l)), + NOT_DISTINCT_FROM_DI(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.DOUBLE, Type.TypeCode.INT, (l, r) -> (double)l == (int)r), + NOT_DISTINCT_FROM_DL(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.DOUBLE, Type.TypeCode.LONG, (l, r) -> (double)l == (long)r), + NOT_DISTINCT_FROM_DF(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.DOUBLE, Type.TypeCode.FLOAT, (l, r) -> (double)l == (float)r), + NOT_DISTINCT_FROM_DD(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.DOUBLE, Type.TypeCode.DOUBLE, (l, r) -> (double)l == (double)r), + NOT_DISTINCT_FROM_SU(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.STRING, Type.TypeCode.NULL, (l, r) -> Objects.isNull(l)), + NOT_DISTINCT_FROM_SS(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.STRING, Type.TypeCode.STRING, Object::equals), // TODO: locale-aware comparison + NOT_DISTINCT_FROM_UB(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.NULL, Type.TypeCode.BOOLEAN, (l, r) -> Objects.isNull(r)), + NOT_DISTINCT_FROM_UI(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.NULL, Type.TypeCode.INT, (l, r) -> Objects.isNull(r)), + NOT_DISTINCT_FROM_UL(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.NULL, Type.TypeCode.LONG, (l, r) -> Objects.isNull(r)), + NOT_DISTINCT_FROM_UF(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.NULL, Type.TypeCode.FLOAT, (l, r) -> Objects.isNull(r)), + NOT_DISTINCT_FROM_UD(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.NULL, Type.TypeCode.DOUBLE, (l, r) -> Objects.isNull(r)), + NOT_DISTINCT_FROM_US(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.NULL, Type.TypeCode.STRING, (l, r) -> Objects.isNull(r)), + NOT_DISTINCT_FROM_UV(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.NULL, Type.TypeCode.VERSION, (l, r) -> Objects.isNull(r)), + NOT_DISTINCT_FROM_VU(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.VERSION, Type.TypeCode.NULL, (l, r) -> Objects.isNull(r)), + NOT_DISTINCT_FROM_VV(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.VERSION, Type.TypeCode.VERSION, Object::equals), + + NOT_DISTINCT_FROM_BYU(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.BYTES, Type.TypeCode.NULL, (l, r) -> Objects.isNull(l)), + NOT_DISTINCT_FROM_BYBY(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.BYTES, Type.TypeCode.BYTES, (l, r) -> Comparisons.evalComparison(Comparisons.Type.NOT_DISTINCT_FROM, l, r)), + NOT_DISTINCT_FROM_UBY(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.NULL, Type.TypeCode.BYTES, (l, r) -> Objects.isNull(r)), + + NOT_DISTINCT_FROM_EE(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.ENUM, Type.TypeCode.ENUM, (l, r) -> Comparisons.evalComparison(Comparisons.Type.NOT_DISTINCT_FROM, l, r)), + NOT_DISTINCT_FROM_ES(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.ENUM, Type.TypeCode.STRING, (l, r) -> { + final var otherValue = PromoteValue.PhysicalOperator.stringToEnumValue(((Descriptors.EnumValueDescriptor) l).getType(), (String) r); + return Comparisons.evalComparison(Comparisons.Type.NOT_DISTINCT_FROM, l, otherValue); + }), + NOT_DISTINCT_FROM_SE(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.STRING, Type.TypeCode.ENUM, (l, r) -> { + final var otherValue = PromoteValue.PhysicalOperator.stringToEnumValue(((Descriptors.EnumValueDescriptor) r).getType(), (String) l); + return Comparisons.evalComparison(Comparisons.Type.NOT_DISTINCT_FROM, otherValue, r); + }), + NOT_DISTINCT_FROM_EU(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.ENUM, Type.TypeCode.NULL, (l, r) -> Objects.isNull(l)), + NOT_DISTINCT_FROM_UE(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.NULL, Type.TypeCode.ENUM, (l, r) -> Objects.isNull(r)), + + NOT_DISTINCT_FROM_IDID(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.UUID, Type.TypeCode.UUID, (l, r) -> Comparisons.evalComparison(Comparisons.Type.NOT_DISTINCT_FROM, l, r)), + NOT_DISTINCT_FROM_IDS(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.UUID, Type.TypeCode.STRING, (l, r) -> Comparisons.evalComparison(Comparisons.Type.NOT_DISTINCT_FROM, l, PromoteValue.PhysicalOperator.stringToUuidValue((String) r))), + NOT_DISTINCT_FROM_SID(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.STRING, Type.TypeCode.UUID, (l, r) -> Comparisons.evalComparison(Comparisons.Type.NOT_DISTINCT_FROM, PromoteValue.PhysicalOperator.stringToUuidValue((String) l), r)), + NOT_DISTINCT_FROM_UID(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.NULL, Type.TypeCode.UUID, (l, r) -> Objects.isNull(r)), + NOT_DISTINCT_FROM_IDU(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.UUID, Type.TypeCode.NULL, (l, r) -> Objects.isNull(l)), + + NOT_DISTINCT_FROM_NN(Comparisons.Type.NOT_DISTINCT_FROM, Type.TypeCode.NULL, Type.TypeCode.NULL, (l, r) -> true), + ; // We can pass down UUID or String till here. @@ -926,8 +1074,15 @@ private enum BinaryPhysicalOperator { @Nullable public Object eval(@Nullable final Object arg1, @Nullable final Object arg2) { + // handle nulls if (arg1 == null || arg2 == null) { - return null; + if (type == Comparisons.Type.IS_DISTINCT_FROM) { + return arg1 != null || arg2 != null; + } else if (type == Comparisons.Type.NOT_DISTINCT_FROM) { + return arg1 == null && arg2 == null; + } else { + return null; + } } return evaluateFunction.apply(arg1, arg2); } diff --git a/fdb-record-layer-core/src/main/proto/record_query_plan.proto b/fdb-record-layer-core/src/main/proto/record_query_plan.proto index e10c907404..88d51ed5ab 100644 --- a/fdb-record-layer-core/src/main/proto/record_query_plan.proto +++ b/fdb-record-layer-core/src/main/proto/record_query_plan.proto @@ -931,7 +931,6 @@ message PBinaryRelOpValue { GTE_SID = 268; GTE_UID = 269; GTE_IDU = 270; - EQ_BN = 271; EQ_IN = 272; EQ_LN = 273; @@ -1062,6 +1061,101 @@ message PBinaryRelOpValue { GT_IDN = 398; GTE_NID = 399; GTE_IDN = 400; + IS_DISTINCT_FROM_BU = 401; + IS_DISTINCT_FROM_BB = 402; + IS_DISTINCT_FROM_IU = 403; + IS_DISTINCT_FROM_II = 404; + IS_DISTINCT_FROM_IL = 405; + IS_DISTINCT_FROM_IF = 406; + IS_DISTINCT_FROM_ID = 407; + IS_DISTINCT_FROM_LU = 408; + IS_DISTINCT_FROM_LI = 409; + IS_DISTINCT_FROM_LL = 410; + IS_DISTINCT_FROM_LF = 411; + IS_DISTINCT_FROM_LD = 412; + IS_DISTINCT_FROM_FU = 413; + IS_DISTINCT_FROM_FI = 414; + IS_DISTINCT_FROM_FL = 415; + IS_DISTINCT_FROM_FF = 416; + IS_DISTINCT_FROM_FD = 417; + IS_DISTINCT_FROM_DU = 418; + IS_DISTINCT_FROM_DI = 419; + IS_DISTINCT_FROM_DL = 420; + IS_DISTINCT_FROM_DF = 421; + IS_DISTINCT_FROM_DD = 422; + IS_DISTINCT_FROM_SU = 423; + IS_DISTINCT_FROM_SS = 424; + IS_DISTINCT_FROM_UB = 425; + IS_DISTINCT_FROM_UI = 426; + IS_DISTINCT_FROM_UL = 427; + IS_DISTINCT_FROM_UF = 428; + IS_DISTINCT_FROM_UD = 429; + IS_DISTINCT_FROM_US = 430; + IS_DISTINCT_FROM_UV = 431; + IS_DISTINCT_FROM_VU = 432; + IS_DISTINCT_FROM_VV = 433; + IS_DISTINCT_FROM_BYU = 434; + IS_DISTINCT_FROM_BYBY = 435; + IS_DISTINCT_FROM_UBY = 436; + IS_DISTINCT_FROM_EE = 437; + IS_DISTINCT_FROM_ES = 438; + IS_DISTINCT_FROM_SE = 439; + IS_DISTINCT_FROM_EU = 440; + IS_DISTINCT_FROM_UE = 441; + IS_DISTINCT_FROM_IDID = 442; + IS_DISTINCT_FROM_IDS = 443; + IS_DISTINCT_FROM_SID = 444; + IS_DISTINCT_FROM_UID = 445; + IS_DISTINCT_FROM_IDU = 446; + IS_DISTINCT_FROM_NN = 447; + NOT_DISTINCT_FROM_BU = 448; + NOT_DISTINCT_FROM_BB = 449; + NOT_DISTINCT_FROM_IU = 450; + NOT_DISTINCT_FROM_II = 451; + NOT_DISTINCT_FROM_IL = 452; + NOT_DISTINCT_FROM_IF = 453; + NOT_DISTINCT_FROM_ID = 454; + NOT_DISTINCT_FROM_LU = 455; + NOT_DISTINCT_FROM_LI = 456; + NOT_DISTINCT_FROM_LL = 457; + NOT_DISTINCT_FROM_LF = 458; + NOT_DISTINCT_FROM_LD = 459; + NOT_DISTINCT_FROM_FU = 460; + NOT_DISTINCT_FROM_FI = 461; + NOT_DISTINCT_FROM_FL = 462; + NOT_DISTINCT_FROM_FF = 463; + NOT_DISTINCT_FROM_FD = 464; + NOT_DISTINCT_FROM_DU = 465; + NOT_DISTINCT_FROM_DI = 466; + NOT_DISTINCT_FROM_DL = 467; + NOT_DISTINCT_FROM_DF = 468; + NOT_DISTINCT_FROM_DD = 469; + NOT_DISTINCT_FROM_SU = 470; + NOT_DISTINCT_FROM_SS = 471; + + NOT_DISTINCT_FROM_UB = 472; + NOT_DISTINCT_FROM_UI = 473; + NOT_DISTINCT_FROM_UL = 474; + NOT_DISTINCT_FROM_UF = 475; + NOT_DISTINCT_FROM_UD = 476; + NOT_DISTINCT_FROM_US = 477; + NOT_DISTINCT_FROM_UV = 478; + NOT_DISTINCT_FROM_VU = 479; + NOT_DISTINCT_FROM_VV = 480; + NOT_DISTINCT_FROM_BYU = 481; + NOT_DISTINCT_FROM_BYBY = 482; + NOT_DISTINCT_FROM_UBY = 483; + NOT_DISTINCT_FROM_EE = 484; + NOT_DISTINCT_FROM_ES = 485; + NOT_DISTINCT_FROM_SE = 486; + NOT_DISTINCT_FROM_EU = 487; + NOT_DISTINCT_FROM_UE = 488; + NOT_DISTINCT_FROM_IDID = 489; + NOT_DISTINCT_FROM_IDS = 490; + NOT_DISTINCT_FROM_SID = 491; + NOT_DISTINCT_FROM_UID = 492; + NOT_DISTINCT_FROM_IDU = 493; + NOT_DISTINCT_FROM_NN = 494; } optional PRelOpValue super = 1; optional PBinaryPhysicalOperator operator = 2; @@ -1189,6 +1283,8 @@ message PComparison { TEXT_CONTAINS_ANY_PREFIX = 17; SORT = 18; LIKE = 19; + IS_DISTINCT_FROM = 20; + NOT_DISTINCT_FROM = 21; } extensions 5000 to max; diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/expressions/QueryExpressionTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/expressions/QueryExpressionTest.java index 76667987b2..e6960b3909 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/expressions/QueryExpressionTest.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/expressions/QueryExpressionTest.java @@ -412,7 +412,7 @@ protected void testParameterComparison(String name, String field, Object val1, C final Bindings bindings = Bindings.newBuilder() .set("fooParam", val2) .build(); - if (val1 != null && val2 != null && (type == Comparisons.Type.IN && !(val2 instanceof List) || type.name().startsWith("TEXT_"))) { + if (val1 != null && val2 != null && (type == Comparisons.Type.IN && !(val2 instanceof List)) || type.name().startsWith("TEXT_")) { assertThrows(name, RecordCoreException.class, () -> evaluate(qc, bindings, rec)); } else { diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/plan/cascades/BooleanValueTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/plan/cascades/BooleanValueTest.java index 4446060840..87cbbe3424 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/plan/cascades/BooleanValueTest.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/plan/cascades/BooleanValueTest.java @@ -139,6 +139,10 @@ public Stream provideArguments(final ExtensionContext conte Arguments.of(List.of(BOOL_FALSE, BOOL_TRUE), new RelOpValue.EqualsFn(), ConstantPredicate.FALSE), Arguments.of(List.of(BOOL_TRUE, BOOL_TRUE), new RelOpValue.NotEqualsFn(), ConstantPredicate.FALSE), Arguments.of(List.of(BOOL_FALSE, BOOL_TRUE), new RelOpValue.NotEqualsFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(BOOL_TRUE, BOOL_FALSE), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(BOOL_TRUE, BOOL_TRUE), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(BOOL_TRUE, BOOL_FALSE), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(BOOL_TRUE, BOOL_TRUE), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), Arguments.of(List.of(INT_1, INT_1), new RelOpValue.EqualsFn(), ConstantPredicate.TRUE), Arguments.of(List.of(INT_1, INT_2), new RelOpValue.EqualsFn(), ConstantPredicate.FALSE), @@ -154,6 +158,10 @@ public Stream provideArguments(final ExtensionContext conte Arguments.of(List.of(INT_1, INT_2), new RelOpValue.GteFn(), ConstantPredicate.FALSE), Arguments.of(List.of(INT_2, INT_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), Arguments.of(List.of(INT_1, INT_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(INT_1, INT_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(INT_1, INT_2), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(INT_1, INT_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(INT_1, INT_2), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), Arguments.of(List.of(LONG_1, LONG_1), new RelOpValue.EqualsFn(), ConstantPredicate.TRUE), Arguments.of(List.of(LONG_1, LONG_2), new RelOpValue.EqualsFn(), ConstantPredicate.FALSE), @@ -169,6 +177,10 @@ public Stream provideArguments(final ExtensionContext conte Arguments.of(List.of(LONG_1, LONG_2), new RelOpValue.GteFn(), ConstantPredicate.FALSE), Arguments.of(List.of(LONG_2, LONG_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), Arguments.of(List.of(LONG_1, LONG_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(LONG_1, LONG_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(LONG_1, LONG_2), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(LONG_1, LONG_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(LONG_1, LONG_2), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), Arguments.of(List.of(FLOAT_1, FLOAT_1), new RelOpValue.EqualsFn(), ConstantPredicate.TRUE), Arguments.of(List.of(FLOAT_1, FLOAT_2), new RelOpValue.EqualsFn(), ConstantPredicate.FALSE), @@ -184,6 +196,10 @@ public Stream provideArguments(final ExtensionContext conte Arguments.of(List.of(FLOAT_1, FLOAT_2), new RelOpValue.GteFn(), ConstantPredicate.FALSE), Arguments.of(List.of(FLOAT_2, FLOAT_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), Arguments.of(List.of(FLOAT_1, FLOAT_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(FLOAT_1, FLOAT_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(FLOAT_1, FLOAT_2), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(FLOAT_1, FLOAT_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(FLOAT_1, FLOAT_2), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), Arguments.of(List.of(DOUBLE_1, DOUBLE_1), new RelOpValue.EqualsFn(), ConstantPredicate.TRUE), Arguments.of(List.of(DOUBLE_1, DOUBLE_2), new RelOpValue.EqualsFn(), ConstantPredicate.FALSE), @@ -199,6 +215,10 @@ public Stream provideArguments(final ExtensionContext conte Arguments.of(List.of(DOUBLE_1, DOUBLE_2), new RelOpValue.GteFn(), ConstantPredicate.FALSE), Arguments.of(List.of(DOUBLE_2, DOUBLE_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), Arguments.of(List.of(DOUBLE_1, DOUBLE_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(DOUBLE_1, DOUBLE_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(DOUBLE_1, DOUBLE_2), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(DOUBLE_1, DOUBLE_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(DOUBLE_1, DOUBLE_2), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), Arguments.of(List.of(STRING_1, STRING_1), new RelOpValue.EqualsFn(), ConstantPredicate.TRUE), Arguments.of(List.of(STRING_1, STRING_2), new RelOpValue.EqualsFn(), ConstantPredicate.FALSE), @@ -214,11 +234,19 @@ public Stream provideArguments(final ExtensionContext conte Arguments.of(List.of(STRING_1, STRING_2), new RelOpValue.GteFn(), ConstantPredicate.FALSE), Arguments.of(List.of(STRING_2, STRING_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), Arguments.of(List.of(STRING_1, STRING_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(STRING_1, STRING_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(STRING_1, STRING_2), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(STRING_1, STRING_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(STRING_1, STRING_2), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), Arguments.of(List.of(BYTES_1, BYTES_1), new RelOpValue.EqualsFn(), ConstantPredicate.TRUE), Arguments.of(List.of(BYTES_1, BYTES_2), new RelOpValue.EqualsFn(), ConstantPredicate.FALSE), Arguments.of(List.of(BYTES_1, BYTES_1), new RelOpValue.NotEqualsFn(), ConstantPredicate.FALSE), Arguments.of(List.of(BYTES_1, BYTES_2), new RelOpValue.NotEqualsFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(BYTES_1, BYTES_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(BYTES_1, BYTES_2), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(BYTES_1, BYTES_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(BYTES_1, BYTES_2), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), Arguments.of(List.of(LONG_1, INT_1), new RelOpValue.EqualsFn(), ConstantPredicate.TRUE), Arguments.of(List.of(LONG_1, INT_2), new RelOpValue.EqualsFn(), ConstantPredicate.FALSE), @@ -234,6 +262,10 @@ public Stream provideArguments(final ExtensionContext conte Arguments.of(List.of(LONG_1, INT_2), new RelOpValue.GteFn(), ConstantPredicate.FALSE), Arguments.of(List.of(LONG_2, INT_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), Arguments.of(List.of(LONG_1, INT_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(LONG_1, INT_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(LONG_1, INT_2), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(LONG_1, INT_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(LONG_1, INT_2), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), Arguments.of(List.of(INT_1, LONG_1), new RelOpValue.EqualsFn(), ConstantPredicate.TRUE), Arguments.of(List.of(INT_1, LONG_2), new RelOpValue.EqualsFn(), ConstantPredicate.FALSE), @@ -249,6 +281,10 @@ public Stream provideArguments(final ExtensionContext conte Arguments.of(List.of(INT_1, LONG_2), new RelOpValue.GteFn(), ConstantPredicate.FALSE), Arguments.of(List.of(INT_2, LONG_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), Arguments.of(List.of(INT_1, LONG_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(INT_1, LONG_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(INT_1, LONG_2), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(INT_1, LONG_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(INT_1, LONG_2), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), Arguments.of(List.of(FLOAT_1, INT_1), new RelOpValue.EqualsFn(), ConstantPredicate.TRUE), Arguments.of(List.of(FLOAT_1, INT_2), new RelOpValue.EqualsFn(), ConstantPredicate.FALSE), @@ -264,6 +300,10 @@ public Stream provideArguments(final ExtensionContext conte Arguments.of(List.of(FLOAT_1, INT_2), new RelOpValue.GteFn(), ConstantPredicate.FALSE), Arguments.of(List.of(FLOAT_2, INT_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), Arguments.of(List.of(FLOAT_1, INT_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(FLOAT_1, INT_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(FLOAT_1, INT_2), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(FLOAT_1, INT_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(FLOAT_1, INT_2), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), Arguments.of(List.of(INT_1, FLOAT_1), new RelOpValue.EqualsFn(), ConstantPredicate.TRUE), Arguments.of(List.of(INT_1, FLOAT_2), new RelOpValue.EqualsFn(), ConstantPredicate.FALSE), @@ -279,6 +319,10 @@ public Stream provideArguments(final ExtensionContext conte Arguments.of(List.of(INT_1, FLOAT_2), new RelOpValue.GteFn(), ConstantPredicate.FALSE), Arguments.of(List.of(INT_2, FLOAT_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), Arguments.of(List.of(INT_1, FLOAT_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(INT_1, FLOAT_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(INT_1, FLOAT_2), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(INT_1, FLOAT_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(INT_1, FLOAT_2), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), Arguments.of(List.of(DOUBLE_1, INT_1), new RelOpValue.EqualsFn(), ConstantPredicate.TRUE), Arguments.of(List.of(DOUBLE_1, INT_2), new RelOpValue.EqualsFn(), ConstantPredicate.FALSE), @@ -294,6 +338,10 @@ public Stream provideArguments(final ExtensionContext conte Arguments.of(List.of(DOUBLE_1, INT_2), new RelOpValue.GteFn(), ConstantPredicate.FALSE), Arguments.of(List.of(DOUBLE_2, INT_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), Arguments.of(List.of(DOUBLE_1, INT_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(DOUBLE_1, INT_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(DOUBLE_1, INT_2), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(DOUBLE_1, INT_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(DOUBLE_1, INT_2), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), Arguments.of(List.of(INT_1, DOUBLE_1), new RelOpValue.EqualsFn(), ConstantPredicate.TRUE), Arguments.of(List.of(INT_1, DOUBLE_2), new RelOpValue.EqualsFn(), ConstantPredicate.FALSE), @@ -309,6 +357,10 @@ public Stream provideArguments(final ExtensionContext conte Arguments.of(List.of(INT_1, DOUBLE_2), new RelOpValue.GteFn(), ConstantPredicate.FALSE), Arguments.of(List.of(INT_2, DOUBLE_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), Arguments.of(List.of(INT_1, DOUBLE_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(INT_1, DOUBLE_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(INT_1, DOUBLE_2), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(INT_1, DOUBLE_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(INT_1, DOUBLE_2), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), Arguments.of(List.of(FLOAT_1, LONG_1), new RelOpValue.EqualsFn(), ConstantPredicate.TRUE), Arguments.of(List.of(FLOAT_1, LONG_2), new RelOpValue.EqualsFn(), ConstantPredicate.FALSE), @@ -324,6 +376,10 @@ public Stream provideArguments(final ExtensionContext conte Arguments.of(List.of(FLOAT_1, LONG_2), new RelOpValue.GteFn(), ConstantPredicate.FALSE), Arguments.of(List.of(FLOAT_2, LONG_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), Arguments.of(List.of(FLOAT_1, LONG_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(FLOAT_1, LONG_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(FLOAT_1, LONG_2), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(FLOAT_1, LONG_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(FLOAT_1, LONG_2), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), Arguments.of(List.of(LONG_1, FLOAT_1), new RelOpValue.EqualsFn(), ConstantPredicate.TRUE), Arguments.of(List.of(LONG_1, FLOAT_2), new RelOpValue.EqualsFn(), ConstantPredicate.FALSE), @@ -339,6 +395,10 @@ public Stream provideArguments(final ExtensionContext conte Arguments.of(List.of(LONG_1, FLOAT_2), new RelOpValue.GteFn(), ConstantPredicate.FALSE), Arguments.of(List.of(LONG_2, FLOAT_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), Arguments.of(List.of(LONG_1, FLOAT_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(LONG_1, FLOAT_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(LONG_1, FLOAT_2), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(LONG_1, FLOAT_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(LONG_1, FLOAT_2), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), Arguments.of(List.of(DOUBLE_1, LONG_1), new RelOpValue.EqualsFn(), ConstantPredicate.TRUE), Arguments.of(List.of(DOUBLE_1, LONG_2), new RelOpValue.EqualsFn(), ConstantPredicate.FALSE), @@ -354,7 +414,10 @@ public Stream provideArguments(final ExtensionContext conte Arguments.of(List.of(DOUBLE_1, LONG_2), new RelOpValue.GteFn(), ConstantPredicate.FALSE), Arguments.of(List.of(DOUBLE_2, LONG_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), Arguments.of(List.of(DOUBLE_1, LONG_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), - + Arguments.of(List.of(DOUBLE_1, LONG_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(DOUBLE_1, LONG_2), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(DOUBLE_1, LONG_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(DOUBLE_1, LONG_2), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), Arguments.of(List.of(LONG_1, DOUBLE_1), new RelOpValue.EqualsFn(), ConstantPredicate.TRUE), Arguments.of(List.of(LONG_1, DOUBLE_2), new RelOpValue.EqualsFn(), ConstantPredicate.FALSE), @@ -370,6 +433,10 @@ public Stream provideArguments(final ExtensionContext conte Arguments.of(List.of(LONG_1, DOUBLE_2), new RelOpValue.GteFn(), ConstantPredicate.FALSE), Arguments.of(List.of(LONG_2, DOUBLE_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), Arguments.of(List.of(LONG_1, DOUBLE_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(LONG_1, DOUBLE_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(LONG_1, DOUBLE_2), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(LONG_1, DOUBLE_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(LONG_1, DOUBLE_2), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), Arguments.of(List.of(DOUBLE_1, FLOAT_1), new RelOpValue.EqualsFn(), ConstantPredicate.TRUE), Arguments.of(List.of(DOUBLE_1, FLOAT_2), new RelOpValue.EqualsFn(), ConstantPredicate.FALSE), @@ -385,6 +452,10 @@ public Stream provideArguments(final ExtensionContext conte Arguments.of(List.of(DOUBLE_1, FLOAT_2), new RelOpValue.GteFn(), ConstantPredicate.FALSE), Arguments.of(List.of(DOUBLE_2, FLOAT_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), Arguments.of(List.of(DOUBLE_1, FLOAT_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(DOUBLE_1, FLOAT_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(DOUBLE_1, FLOAT_2), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(DOUBLE_1, FLOAT_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(DOUBLE_1, FLOAT_2), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), Arguments.of(List.of(FLOAT_1, DOUBLE_1), new RelOpValue.EqualsFn(), ConstantPredicate.TRUE), Arguments.of(List.of(FLOAT_1, DOUBLE_2), new RelOpValue.EqualsFn(), ConstantPredicate.FALSE), @@ -400,6 +471,10 @@ public Stream provideArguments(final ExtensionContext conte Arguments.of(List.of(FLOAT_1, DOUBLE_2), new RelOpValue.GteFn(), ConstantPredicate.FALSE), Arguments.of(List.of(FLOAT_2, DOUBLE_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), Arguments.of(List.of(FLOAT_1, DOUBLE_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(FLOAT_1, DOUBLE_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(FLOAT_1, DOUBLE_2), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(FLOAT_1, DOUBLE_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(FLOAT_1, DOUBLE_2), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), Arguments.of(List.of(INT_NULL, INT_NULL), new RelOpValue.EqualsFn(), ConstantPredicate.NULL), Arguments.of(List.of(INT_NULL, INT_2), new RelOpValue.EqualsFn(), ConstantPredicate.NULL), @@ -415,6 +490,10 @@ public Stream provideArguments(final ExtensionContext conte Arguments.of(List.of(INT_NULL, INT_2), new RelOpValue.GteFn(), ConstantPredicate.NULL), Arguments.of(List.of(INT_2, INT_NULL), new RelOpValue.GteFn(), ConstantPredicate.NULL), Arguments.of(List.of(INT_NULL, INT_NULL), new RelOpValue.GteFn(), ConstantPredicate.NULL), + Arguments.of(List.of(INT_NULL, INT_NULL), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(INT_NULL, INT_2), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(INT_NULL, INT_NULL), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(INT_NULL, INT_2), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), Arguments.of(List.of(BOOL_NULL, BOOL_NULL), new RelOpValue.EqualsFn(), ConstantPredicate.NULL), Arguments.of(List.of(BOOL_NULL, BOOL_FALSE), new RelOpValue.EqualsFn(), ConstantPredicate.NULL), @@ -422,6 +501,12 @@ public Stream provideArguments(final ExtensionContext conte Arguments.of(List.of(BOOL_NULL, BOOL_NULL), new RelOpValue.NotEqualsFn(), ConstantPredicate.NULL), Arguments.of(List.of(BOOL_NULL, BOOL_FALSE), new RelOpValue.NotEqualsFn(), ConstantPredicate.NULL), Arguments.of(List.of(BOOL_NULL, BOOL_TRUE), new RelOpValue.NotEqualsFn(), ConstantPredicate.NULL), + Arguments.of(List.of(BOOL_NULL, BOOL_NULL), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(BOOL_NULL, BOOL_FALSE), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(BOOL_NULL, BOOL_TRUE), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(BOOL_NULL, BOOL_NULL), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(BOOL_NULL, BOOL_FALSE), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(BOOL_NULL, BOOL_TRUE), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), Arguments.of(List.of(NULL, LONG_1), new RelOpValue.EqualsFn(), ConstantPredicate.NULL), Arguments.of(List.of(NULL, LONG_NULL), new RelOpValue.EqualsFn(), ConstantPredicate.NULL), @@ -501,6 +586,36 @@ public Stream provideArguments(final ExtensionContext conte Arguments.of(List.of(NULL, STRING_NULL), new RelOpValue.GteFn(), ConstantPredicate.NULL), Arguments.of(List.of(NULL, UUID_1), new RelOpValue.GteFn(), ConstantPredicate.NULL), Arguments.of(List.of(NULL, UUID_NULL), new RelOpValue.GteFn(), ConstantPredicate.NULL), + Arguments.of(List.of(NULL, LONG_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(NULL, LONG_NULL), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(NULL, INT_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(NULL, INT_NULL), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(NULL, FLOAT_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(NULL, FLOAT_NULL), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(NULL, DOUBLE_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(NULL, DOUBLE_NULL), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(NULL, STRING_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(NULL, STRING_NULL), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(NULL, UUID_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(NULL, UUID_NULL), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(NULL, BOOL_FALSE), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(NULL, BOOL_TRUE), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(NULL, BOOL_NULL), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(NULL, LONG_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(NULL, LONG_NULL), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(NULL, INT_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(NULL, INT_NULL), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(NULL, FLOAT_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(NULL, FLOAT_NULL), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(NULL, DOUBLE_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(NULL, DOUBLE_NULL), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(NULL, STRING_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(NULL, STRING_NULL), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(NULL, UUID_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(NULL, UUID_NULL), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(NULL, BOOL_FALSE), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(NULL, BOOL_TRUE), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(NULL, BOOL_NULL), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), Arguments.of(List.of(F_ENUM_1, F_ENUM_2), new RelOpValue.EqualsFn(), new ValuePredicate(F_ENUM_1, new Comparisons.ValueComparison(Comparisons.Type.EQUALS, F_ENUM_2))), @@ -510,24 +625,32 @@ public Stream provideArguments(final ExtensionContext conte Arguments.of(List.of(UUID_STRING_1, UUID_1), new RelOpValue.LteFn(), ConstantPredicate.TRUE), Arguments.of(List.of(UUID_STRING_1, UUID_1), new RelOpValue.GtFn(), ConstantPredicate.FALSE), Arguments.of(List.of(UUID_STRING_1, UUID_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(UUID_STRING_1, UUID_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(UUID_STRING_1, UUID_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), Arguments.of(List.of(UUID_1, UUID_1), new RelOpValue.EqualsFn(), ConstantPredicate.TRUE), Arguments.of(List.of(UUID_1, UUID_1), new RelOpValue.NotEqualsFn(), ConstantPredicate.FALSE), Arguments.of(List.of(UUID_1, UUID_1), new RelOpValue.LtFn(), ConstantPredicate.FALSE), Arguments.of(List.of(UUID_1, UUID_1), new RelOpValue.LteFn(), ConstantPredicate.TRUE), Arguments.of(List.of(UUID_1, UUID_1), new RelOpValue.GtFn(), ConstantPredicate.FALSE), Arguments.of(List.of(UUID_1, UUID_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(UUID_1, UUID_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(UUID_1, UUID_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.FALSE), Arguments.of(List.of(UUID_STRING_2, UUID_1), new RelOpValue.EqualsFn(), ConstantPredicate.FALSE), Arguments.of(List.of(UUID_STRING_2, UUID_1), new RelOpValue.NotEqualsFn(), ConstantPredicate.TRUE), Arguments.of(List.of(UUID_STRING_2, UUID_1), new RelOpValue.LtFn(), ConstantPredicate.FALSE), Arguments.of(List.of(UUID_STRING_2, UUID_1), new RelOpValue.LteFn(), ConstantPredicate.FALSE), Arguments.of(List.of(UUID_STRING_2, UUID_1), new RelOpValue.GtFn(), ConstantPredicate.TRUE), Arguments.of(List.of(UUID_STRING_2, UUID_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(UUID_STRING_2, UUID_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(UUID_STRING_2, UUID_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), Arguments.of(List.of(UUID_2, UUID_1), new RelOpValue.EqualsFn(), ConstantPredicate.FALSE), Arguments.of(List.of(UUID_2, UUID_1), new RelOpValue.NotEqualsFn(), ConstantPredicate.TRUE), Arguments.of(List.of(UUID_2, UUID_1), new RelOpValue.LtFn(), ConstantPredicate.FALSE), Arguments.of(List.of(UUID_2, UUID_1), new RelOpValue.LteFn(), ConstantPredicate.FALSE), Arguments.of(List.of(UUID_2, UUID_1), new RelOpValue.GtFn(), ConstantPredicate.TRUE), Arguments.of(List.of(UUID_2, UUID_1), new RelOpValue.GteFn(), ConstantPredicate.TRUE), + Arguments.of(List.of(UUID_2, UUID_1), new RelOpValue.NotDistinctFromFn(), ConstantPredicate.FALSE), + Arguments.of(List.of(UUID_2, UUID_1), new RelOpValue.IsDistinctFromFn(), ConstantPredicate.TRUE), /* translation of predicates involving a field value, make sure field value is always LHS */ Arguments.of(List.of(F, INT_1), new RelOpValue.EqualsFn(), new ValuePredicate(F, new Comparisons.SimpleComparison(Comparisons.Type.EQUALS, 1))), @@ -542,6 +665,11 @@ public Stream provideArguments(final ExtensionContext conte Arguments.of(List.of(INT_1, F), new RelOpValue.LteFn(), new ValuePredicate(F, new Comparisons.SimpleComparison(Comparisons.Type.GREATER_THAN_OR_EQUALS, 1))), Arguments.of(List.of(F, INT_1), new RelOpValue.GteFn(), new ValuePredicate(F, new Comparisons.SimpleComparison(Comparisons.Type.GREATER_THAN_OR_EQUALS, 1))), Arguments.of(List.of(INT_1, F), new RelOpValue.GteFn(), new ValuePredicate(F, new Comparisons.SimpleComparison(Comparisons.Type.LESS_THAN_OR_EQUALS, 1))), + Arguments.of(List.of(F, INT_1), new RelOpValue.NotDistinctFromFn(), new ValuePredicate(F, new Comparisons.SimpleComparison(Comparisons.Type.NOT_DISTINCT_FROM, 1))), + Arguments.of(List.of(INT_1, F), new RelOpValue.NotDistinctFromFn(), new ValuePredicate(F, new Comparisons.SimpleComparison(Comparisons.Type.NOT_DISTINCT_FROM, 1))), + Arguments.of(List.of(F, INT_1), new RelOpValue.IsDistinctFromFn(), new ValuePredicate(F, new Comparisons.SimpleComparison(Comparisons.Type.IS_DISTINCT_FROM, 1))), + Arguments.of(List.of(INT_1, F), new RelOpValue.IsDistinctFromFn(), new ValuePredicate(F, new Comparisons.SimpleComparison(Comparisons.Type.IS_DISTINCT_FROM, 1))), + Arguments.of(List.of(INT_1), new RelOpValue.IsNullFn(), ConstantPredicate.FALSE), Arguments.of(List.of(INT_1), new RelOpValue.NotNullFn(), ConstantPredicate.TRUE), @@ -801,7 +929,7 @@ void testEval(List args, BuiltInFunction function, QueryPredicate result) value = verifySerialization((Value)value); Object actual = ((Value)value).evalWithoutStore(evalContext); Object expected = result.evalWithoutStore(evalContext); - Assertions.assertEquals(actual, expected); + Assertions.assertEquals(expected, actual); } else { Assertions.assertThrows(SemanticException.class, () -> function.encapsulate(args)); } diff --git a/fdb-relational-core/src/main/antlr/RelationalParser.g4 b/fdb-relational-core/src/main/antlr/RelationalParser.g4 index 876c7b3223..f7f04f1317 100644 --- a/fdb-relational-core/src/main/antlr/RelationalParser.g4 +++ b/fdb-relational-core/src/main/antlr/RelationalParser.g4 @@ -1178,6 +1178,7 @@ unaryOperator comparisonOperator : '=' | '>' | '<' | '<' '=' | '>' '=' | '<' '>' | '!' '=' // | '<' '=' '>' // no support for null-safe equality + | IS NOT? DISTINCT FROM ; logicalOperator diff --git a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/functions/SqlFunctionCatalogImpl.java b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/functions/SqlFunctionCatalogImpl.java index 63f05d65ac..d3e5766fd4 100644 --- a/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/functions/SqlFunctionCatalogImpl.java +++ b/fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/functions/SqlFunctionCatalogImpl.java @@ -144,6 +144,8 @@ private static ImmutableMap BuiltInFunctionCatalog.resolve("coalesce", argumentsCount)) .put("is null", argumentsCount -> BuiltInFunctionCatalog.resolve("isNull", argumentsCount)) .put("is not null", argumentsCount -> BuiltInFunctionCatalog.resolve("notNull", argumentsCount)) + .put("isdistinctfrom", argumentsCount -> BuiltInFunctionCatalog.resolve("isDistinctFrom", argumentsCount)) + .put("isnotdistinctfrom", argumentsCount -> BuiltInFunctionCatalog.resolve("notDistinctFrom", argumentsCount)) .put("range", argumentsCount -> BuiltInFunctionCatalog.resolve("range", argumentsCount)) .put("__pattern_for_like", argumentsCount -> BuiltInFunctionCatalog.resolve("patternForLike", argumentsCount)) .put("__internal_array", argumentsCount -> BuiltInFunctionCatalog.resolve("array", argumentsCount)) diff --git a/yaml-tests/src/test/java/YamlIntegrationTests.java b/yaml-tests/src/test/java/YamlIntegrationTests.java index cb17073ae3..01d33ce76e 100644 --- a/yaml-tests/src/test/java/YamlIntegrationTests.java +++ b/yaml-tests/src/test/java/YamlIntegrationTests.java @@ -206,6 +206,11 @@ void like(YamlTest.Runner runner) throws Exception { runner.runYamsql("like.yamsql"); } + @TestTemplate + void distinctFrom(YamlTest.Runner runner) throws Exception { + runner.runYamsql("distinct-from.yamsql"); + } + @TestTemplate void functions(YamlTest.Runner runner) throws Exception { runner.runYamsql("functions.yamsql"); diff --git a/yaml-tests/src/test/resources/distinct-from.metrics.binpb b/yaml-tests/src/test/resources/distinct-from.metrics.binpb new file mode 100644 index 0000000000..efc2543695 --- /dev/null +++ b/yaml-tests/src/test/resources/distinct-from.metrics.binpb @@ -0,0 +1,185 @@ + +U +distinct-from-tests>EXPLAIN select * from t1 WHERE col2 is distinct from null +g (.082@\COVERING(I2 <,> -> [COL2: KEY[0], ID: KEY[2]]) | FILTER _.COL2 IS_DISTINCT_FROM NULL | FETCH digraph G { + fontname=courier; + rankdir=BT; + splines=polyline; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(INT AS ID, )" ]; + 2 [ label=<
Predicate Filter
WHERE q55.COL2 IS_DISTINCT_FROM NULL
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 3 [ label=<
Covering Index Scan
range: <-∞, ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 4 [ label=<
Index
I2
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 3 -> 2 [ label=< q55> label="q55" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q57> label="q57" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +} +N +distinct-from-tests7EXPLAIN select * from t1 WHERE col1 is DISTINCT from 10 +sl L(00㗢86@lCOVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER _.COL1 IS_DISTINCT_FROM promote(@c10 AS INT) | FETCH digraph G { + fontname=courier; + rankdir=BT; + splines=polyline; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(INT AS ID, )" ]; + 2 [ label=<
Predicate Filter
WHERE q55.COL1 IS_DISTINCT_FROM promote(@c10 AS INT)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 3 [ label=<
Covering Index Scan
range: <-∞, ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 4 [ label=<
Index
I1
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 3 -> 2 [ label=< q55> label="q55" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q57> label="q57" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +} +P +distinct-from-tests9EXPLAIN select * from t1 WHERE null is distinct from col2 +g (.082@\COVERING(I2 <,> -> [COL2: KEY[0], ID: KEY[2]]) | FILTER _.COL2 IS_DISTINCT_FROM NULL | FETCH digraph G { + fontname=courier; + rankdir=BT; + splines=polyline; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(INT AS ID, )" ]; + 2 [ label=<
Predicate Filter
WHERE q55.COL2 IS_DISTINCT_FROM NULL
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 3 [ label=<
Covering Index Scan
range: <-∞, ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 4 [ label=<
Index
I2
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 3 -> 2 [ label=< q55> label="q55" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q57> label="q57" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +} +N +distinct-from-tests7EXPLAIN select * from t1 WHERE 10 is distinct from col1 +ɋrl L(0086@kCOVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER _.COL1 IS_DISTINCT_FROM promote(@c6 AS INT) | FETCH digraph G { + fontname=courier; + rankdir=BT; + splines=polyline; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(INT AS ID, )" ]; + 2 [ label=<
Predicate Filter
WHERE q55.COL1 IS_DISTINCT_FROM promote(@c6 AS INT)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 3 [ label=<
Covering Index Scan
range: <-∞, ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 4 [ label=<
Index
I1
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 3 -> 2 [ label=< q55> label="q55" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q57> label="q57" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +} +P +distinct-from-tests9EXPLAIN select * from t1 WHERE null is distinct from null +rl ݫP(0088@ZCOVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER NULL IS_DISTINCT_FROM NULL | FETCH digraph G { + fontname=courier; + rankdir=BT; + splines=polyline; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(INT AS ID, )" ]; + 2 [ label=<
Predicate Filter
WHERE NULL IS_DISTINCT_FROM NULL
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 3 [ label=<
Covering Index Scan
range: <-∞, ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 4 [ label=<
Index
I1
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 3 -> 2 [ label=< q54> label="q54" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q56> label="q56" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +} +L +distinct-from-tests5EXPLAIN select * from t1 WHERE 10 is distinct from 10 +l (00Ƞ88@XCOVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER @c6 IS_DISTINCT_FROM @c6 | FETCH digraph G { + fontname=courier; + rankdir=BT; + splines=polyline; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(INT AS ID, )" ]; + 2 [ label=<
Predicate Filter
WHERE @c6 IS_DISTINCT_FROM @c6
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 3 [ label=<
Covering Index Scan
range: <-∞, ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 4 [ label=<
Index
I1
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 3 -> 2 [ label=< q54> label="q54" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q56> label="q56" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +} +] +not-distinct-from-testsBEXPLAIN select * from t1 WHERE col2 is not distinct from null + f (.0^82@]COVERING(I2 <,> -> [COL2: KEY[0], ID: KEY[2]]) | FILTER _.COL2 NOT_DISTINCT_FROM NULL | FETCH digraph G { + fontname=courier; + rankdir=BT; + splines=polyline; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(INT AS ID, )" ]; + 2 [ label=<
Predicate Filter
WHERE q55.COL2 NOT_DISTINCT_FROM NULL
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 3 [ label=<
Covering Index Scan
range: <-∞, ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 4 [ label=<
Index
I2
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 3 -> 2 [ label=< q55> label="q55" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q57> label="q57" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +} +V +not-distinct-from-tests;EXPLAIN select * from t1 WHERE col1 is not distinct from 20 +k 远 (00ӡ86@mCOVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER _.COL1 NOT_DISTINCT_FROM promote(@c11 AS INT) | FETCH digraph G { + fontname=courier; + rankdir=BT; + splines=polyline; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(INT AS ID, )" ]; + 2 [ label=<
Predicate Filter
WHERE q55.COL1 NOT_DISTINCT_FROM promote(@c11 AS INT)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 3 [ label=<
Covering Index Scan
range: <-∞, ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 4 [ label=<
Index
I1
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 3 -> 2 [ label=< q55> label="q55" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q57> label="q57" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +} +X +not-distinct-from-tests=EXPLAIN select * from t1 WHERE null is not distinct from col2 +f ˋ(.0c82@]COVERING(I2 <,> -> [COL2: KEY[0], ID: KEY[2]]) | FILTER _.COL2 NOT_DISTINCT_FROM NULL | FETCH digraph G { + fontname=courier; + rankdir=BT; + splines=polyline; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(INT AS ID, )" ]; + 2 [ label=<
Predicate Filter
WHERE q55.COL2 NOT_DISTINCT_FROM NULL
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 3 [ label=<
Covering Index Scan
range: <-∞, ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 4 [ label=<
Index
I2
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 3 -> 2 [ label=< q55> label="q55" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q57> label="q57" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +} +V +not-distinct-from-tests;EXPLAIN select * from t1 WHERE 20 is not distinct from col1 +k (0086@lCOVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER _.COL1 NOT_DISTINCT_FROM promote(@c6 AS INT) | FETCH digraph G { + fontname=courier; + rankdir=BT; + splines=polyline; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(INT AS ID, )" ]; + 2 [ label=<
Predicate Filter
WHERE q55.COL1 NOT_DISTINCT_FROM promote(@c6 AS INT)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 3 [ label=<
Covering Index Scan
range: <-∞, ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 4 [ label=<
Index
I1
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 3 -> 2 [ label=< q55> label="q55" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q57> label="q57" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +} +_ +not-distinct-from-testsDEXPLAIN select count(*) from t1 WHERE null is not distinct from null + (908U@COVERING(I2 <,> -> [COL2: KEY[0], ID: KEY[2]]) | FILTER NULL NOT_DISTINCT_FROM NULL | FETCH | MAP (_ AS _0) | AGG (count_star(*) AS _0) | ON EMPTY NULL | MAP (coalesce_long(_._0._0, promote(0l AS LONG)) AS _0)digraph G { + fontname=courier; + rankdir=BT; + splines=polyline; + 1 [ label=<
Value Computation
MAP (coalesce_long(q6._0._0, promote(0l AS LONG)) AS _0)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS _0)" ]; + 2 [ label=<
Value Computation
$q6 OR NULL
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS _0 AS _0)" ]; + 3 [ label=<
Streaming Aggregate
COLLECT (count_star(*) AS _0)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS _0 AS _0)" ]; + 4 [ label=<
Value Computation
MAP (q61 AS _0)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, AS _0)" ]; + 5 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(INT AS ID, )" ]; + 6 [ label=<
Predicate Filter
WHERE NULL NOT_DISTINCT_FROM NULL
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 7 [ label=<
Covering Index Scan
range: <-∞, ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 8 [ label=<
Index
I2
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 3 -> 2 [ label=< q6> label="q6" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 3 [ label=< q107> label="q107" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 4 [ label=< q61> label="q61" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 6 -> 5 [ label=< q73> label="q73" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 7 -> 6 [ label=< q71> label="q71" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 8 -> 7 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q6> label="q6" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +} +[ +not-distinct-from-tests@EXPLAIN select count(*) from t1 WHERE 10 is not distinct from 10 +Ɍ ؝ (90㮈8U@COVERING(I2 <,> -> [COL2: KEY[0], ID: KEY[2]]) | FILTER @c9 NOT_DISTINCT_FROM @c9 | FETCH | MAP (_ AS _0) | AGG (count_star(*) AS _0) | ON EMPTY NULL | MAP (coalesce_long(_._0._0, promote(0l AS LONG)) AS _0)digraph G { + fontname=courier; + rankdir=BT; + splines=polyline; + 1 [ label=<
Value Computation
MAP (coalesce_long(q6._0._0, promote(0l AS LONG)) AS _0)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS _0)" ]; + 2 [ label=<
Value Computation
$q6 OR NULL
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS _0 AS _0)" ]; + 3 [ label=<
Streaming Aggregate
COLLECT (count_star(*) AS _0)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS _0 AS _0)" ]; + 4 [ label=<
Value Computation
MAP (q61 AS _0)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, AS _0)" ]; + 5 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(INT AS ID, )" ]; + 6 [ label=<
Predicate Filter
WHERE @c9 NOT_DISTINCT_FROM @c9
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 7 [ label=<
Covering Index Scan
range: <-∞, ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 8 [ label=<
Index
I2
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS ID, )" ]; + 3 -> 2 [ label=< q6> label="q6" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 3 [ label=< q107> label="q107" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 4 [ label=< q61> label="q61" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 6 -> 5 [ label=< q73> label="q73" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 7 -> 6 [ label=< q71> label="q71" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 8 -> 7 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q6> label="q6" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +} \ No newline at end of file diff --git a/yaml-tests/src/test/resources/distinct-from.metrics.yaml b/yaml-tests/src/test/resources/distinct-from.metrics.yaml new file mode 100644 index 0000000000..4c15eb3807 --- /dev/null +++ b/yaml-tests/src/test/resources/distinct-from.metrics.yaml @@ -0,0 +1,136 @@ +distinct-from-tests: +- query: EXPLAIN select * from t1 WHERE col2 is distinct from null + explain: 'COVERING(I2 <,> -> [COL2: KEY[0], ID: KEY[2]]) | FILTER _.COL2 IS_DISTINCT_FROM + NULL | FETCH' + task_count: 434 + task_total_time_ms: 42 + transform_count: 103 + transform_time_ms: 20 + transform_yield_count: 46 + insert_time_ms: 2 + insert_new_count: 50 + insert_reused_count: 7 +- query: EXPLAIN select * from t1 WHERE col1 is DISTINCT from 10 + explain: 'COVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER _.COL1 IS_DISTINCT_FROM + promote(@c10 AS INT) | FETCH' + task_count: 450 + task_total_time_ms: 241 + transform_count: 108 + transform_time_ms: 160 + transform_yield_count: 48 + insert_time_ms: 15 + insert_new_count: 54 + insert_reused_count: 7 +- query: EXPLAIN select * from t1 WHERE null is distinct from col2 + explain: 'COVERING(I2 <,> -> [COL2: KEY[0], ID: KEY[2]]) | FILTER _.COL2 IS_DISTINCT_FROM + NULL | FETCH' + task_count: 434 + task_total_time_ms: 42 + transform_count: 103 + transform_time_ms: 20 + transform_yield_count: 46 + insert_time_ms: 2 + insert_new_count: 50 + insert_reused_count: 7 +- query: EXPLAIN select * from t1 WHERE 10 is distinct from col1 + explain: 'COVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER _.COL1 IS_DISTINCT_FROM + promote(@c6 AS INT) | FETCH' + task_count: 450 + task_total_time_ms: 240 + transform_count: 108 + transform_time_ms: 160 + transform_yield_count: 48 + insert_time_ms: 14 + insert_new_count: 54 + insert_reused_count: 7 +- query: EXPLAIN select * from t1 WHERE null is distinct from null + explain: 'COVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER NULL IS_DISTINCT_FROM + NULL | FETCH' + task_count: 473 + task_total_time_ms: 239 + transform_count: 108 + transform_time_ms: 168 + transform_yield_count: 48 + insert_time_ms: 8 + insert_new_count: 56 + insert_reused_count: 8 +- query: EXPLAIN select * from t1 WHERE 10 is distinct from 10 + explain: 'COVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER @c6 IS_DISTINCT_FROM + @c6 | FETCH' + task_count: 473 + task_total_time_ms: 43 + transform_count: 108 + transform_time_ms: 20 + transform_yield_count: 48 + insert_time_ms: 3 + insert_new_count: 56 + insert_reused_count: 8 +not-distinct-from-tests: +- query: EXPLAIN select * from t1 WHERE col2 is not distinct from null + explain: 'COVERING(I2 <,> -> [COL2: KEY[0], ID: KEY[2]]) | FILTER _.COL2 NOT_DISTINCT_FROM + NULL | FETCH' + task_count: 434 + task_total_time_ms: 26 + transform_count: 102 + transform_time_ms: 11 + transform_yield_count: 46 + insert_time_ms: 1 + insert_new_count: 50 + insert_reused_count: 7 +- query: EXPLAIN select * from t1 WHERE col1 is not distinct from 20 + explain: 'COVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER _.COL1 NOT_DISTINCT_FROM + promote(@c11 AS INT) | FETCH' + task_count: 450 + task_total_time_ms: 43 + transform_count: 107 + transform_time_ms: 19 + transform_yield_count: 48 + insert_time_ms: 2 + insert_new_count: 54 + insert_reused_count: 7 +- query: EXPLAIN select * from t1 WHERE null is not distinct from col2 + explain: 'COVERING(I2 <,> -> [COL2: KEY[0], ID: KEY[2]]) | FILTER _.COL2 NOT_DISTINCT_FROM + NULL | FETCH' + task_count: 434 + task_total_time_ms: 32 + transform_count: 102 + transform_time_ms: 15 + transform_yield_count: 46 + insert_time_ms: 1 + insert_new_count: 50 + insert_reused_count: 7 +- query: EXPLAIN select * from t1 WHERE 20 is not distinct from col1 + explain: 'COVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER _.COL1 NOT_DISTINCT_FROM + promote(@c6 AS INT) | FETCH' + task_count: 450 + task_total_time_ms: 42 + transform_count: 107 + transform_time_ms: 18 + transform_yield_count: 48 + insert_time_ms: 3 + insert_new_count: 54 + insert_reused_count: 7 +- query: EXPLAIN select count(*) from t1 WHERE null is not distinct from null + explain: 'COVERING(I2 <,> -> [COL2: KEY[0], ID: KEY[2]]) | FILTER NULL NOT_DISTINCT_FROM + NULL | FETCH | MAP (_ AS _0) | AGG (count_star(*) AS _0) | ON EMPTY NULL | + MAP (coalesce_long(_._0._0, promote(0l AS LONG)) AS _0)' + task_count: 755 + task_total_time_ms: 65 + transform_count: 180 + transform_time_ms: 37 + transform_yield_count: 57 + insert_time_ms: 4 + insert_new_count: 85 + insert_reused_count: 4 +- query: EXPLAIN select count(*) from t1 WHERE 10 is not distinct from 10 + explain: 'COVERING(I2 <,> -> [COL2: KEY[0], ID: KEY[2]]) | FILTER @c9 NOT_DISTINCT_FROM + @c9 | FETCH | MAP (_ AS _0) | AGG (count_star(*) AS _0) | ON EMPTY NULL | + MAP (coalesce_long(_._0._0, promote(0l AS LONG)) AS _0)' + task_count: 755 + task_total_time_ms: 39 + transform_count: 180 + transform_time_ms: 19 + transform_yield_count: 57 + insert_time_ms: 2 + insert_new_count: 85 + insert_reused_count: 4 diff --git a/yaml-tests/src/test/resources/distinct-from.yamsql b/yaml-tests/src/test/resources/distinct-from.yamsql new file mode 100644 index 0000000000..6c3e97a1c8 --- /dev/null +++ b/yaml-tests/src/test/resources/distinct-from.yamsql @@ -0,0 +1,101 @@ +# +# distinct-from.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2021-2024 Apple Inc. and the FoundationDB project authors +# +# Licensed 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. + + +--- +options: + supported_version: !current_version +--- +schema_template: + create table t1(id integer, col1 integer, col2 integer, primary key(id)) + create index i1 as select col1 from t1 + create index cnt as select count(*) from t1 + create index i2 as select col2 from t1 +--- +setup: + steps: + - query: INSERT INTO T1 + VALUES (1, 10, 1), + (2, 10, null), + (3, 10, 3), + (4, 10, null), + (5, 10, 5), + (6, 20, null), + (7, 20, null), + (8, 20, null), + (9, 20, 9), + (10, 20, 10) +--- +test_block: + name: distinct-from-tests + tests: + - + - query: select * from t1 WHERE col2 is distinct from null + - explain: "COVERING(I2 <,> -> [COL2: KEY[0], ID: KEY[2]]) | FILTER _.COL2 IS_DISTINCT_FROM NULL | FETCH" + - result: [{ID: 1, 10, 1}, {ID: 3, 10, 3}, {ID: 5, 10, 5}, {ID: 9, 20, 9}, {ID: 10, 20, 10}] + - + - query: select * from t1 WHERE col1 is DISTINCT from 10 + - explain: "COVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER _.COL1 IS_DISTINCT_FROM promote(@c10 AS INT) | FETCH" + - result: [{ID: 6, 20, !null }, {ID: 7, 20, !null }, {ID: 8, 20, !null }, {ID: 9, 20, 9}, {ID: 10, 20, 10}] + - + - query: select * from t1 WHERE null is distinct from col2 + - explain: "COVERING(I2 <,> -> [COL2: KEY[0], ID: KEY[2]]) | FILTER _.COL2 IS_DISTINCT_FROM NULL | FETCH" + - result: [{ID: 1, 10, 1}, {ID: 3, 10, 3}, {ID: 5, 10, 5}, {ID: 9, 20, 9}, {ID: 10, 20, 10}] + - + - query: select * from t1 WHERE 10 is distinct from col1 + - explain: "COVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER _.COL1 IS_DISTINCT_FROM promote(@c6 AS INT) | FETCH" + - result: [{ID: 6, 20, !null }, {ID: 7, 20, !null }, {ID: 8, 20, !null }, {ID: 9, 20, 9}, {ID: 10, 20, 10}] + - + - query: select * from t1 WHERE null is distinct from null + - explain: "COVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER NULL IS_DISTINCT_FROM NULL | FETCH" + - result: [] + - + - query: select * from t1 WHERE 10 is distinct from 10 + - explain: "COVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER @c6 IS_DISTINCT_FROM @c6 | FETCH" + - result: [] + +--- +test_block: + name: not-distinct-from-tests + tests: + - + - query: select * from t1 WHERE col2 is not distinct from null + - explain: "COVERING(I2 <,> -> [COL2: KEY[0], ID: KEY[2]]) | FILTER _.COL2 NOT_DISTINCT_FROM NULL | FETCH" + - result: [{ID: 2, 10, !null }, {ID: 4, 10, !null }, {ID: 6, 20, !null }, {ID: 7, 20, !null }, {ID: 8, 20, !null }] + - + - query: select * from t1 WHERE col1 is not distinct from 20 + - explain: "COVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER _.COL1 NOT_DISTINCT_FROM promote(@c11 AS INT) | FETCH" + - result: [{ID: 6, 20, !null }, {ID: 7, 20, !null }, {ID: 8, 20, !null }, {ID: 9, 20, 9}, {ID: 10, 20, 10}] + - + - query: select * from t1 WHERE null is not distinct from col2 + - explain: "COVERING(I2 <,> -> [COL2: KEY[0], ID: KEY[2]]) | FILTER _.COL2 NOT_DISTINCT_FROM NULL | FETCH" + - result: [{ID: 2, 10, !null }, {ID: 4, 10, !null }, {ID: 6, 20, !null }, {ID: 7, 20, !null }, {ID: 8, 20, !null }] + - + - query: select * from t1 WHERE 20 is not distinct from col1 + - explain: "COVERING(I1 <,> -> [COL1: KEY[0], ID: KEY[2]]) | FILTER _.COL1 NOT_DISTINCT_FROM promote(@c6 AS INT) | FETCH" + - result: [{ID: 6, 20, !null }, {ID: 7, 20, !null }, {ID: 8, 20, !null }, {ID: 9, 20, 9}, {ID: 10, 20, 10}] + - + - query: select count(*) from t1 WHERE null is not distinct from null + - explain: "COVERING(I2 <,> -> [COL2: KEY[0], ID: KEY[2]]) | FILTER NULL NOT_DISTINCT_FROM NULL | FETCH | MAP (_ AS _0) | AGG (count_star(*) AS _0) | ON EMPTY NULL | MAP (coalesce_long(_._0._0, promote(0l AS LONG)) AS _0)" + - result: [{10}] + - + - query: select count(*) from t1 WHERE 10 is not distinct from 10 + - explain: "COVERING(I2 <,> -> [COL2: KEY[0], ID: KEY[2]]) | FILTER @c9 NOT_DISTINCT_FROM @c9 | FETCH | MAP (_ AS _0) | AGG (count_star(*) AS _0) | ON EMPTY NULL | MAP (coalesce_long(_._0._0, promote(0l AS LONG)) AS _0)" + - result: [{10}] +...