diff --git a/README.md b/README.md index b4c9a5e80a..d886de962c 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ The Record Layer is a Java API providing a record-oriented store on top of Found across one or more record types, and a query planner capable of automatic selection of indexes. * **Many record stores, shared schema** - The Record Layer provides the - the ability to support many discrete record store instances, all with + ability to support many discrete record store instances, all with a shared (and evolving) schema. For example, rather than modeling a single database in which to store all users' data, each user can be given their own record store, perhaps sharded across different FDB diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/AggregateIndexExpansionVisitor.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/AggregateIndexExpansionVisitor.java index 46ca7b8cba..4b0b500cf6 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/AggregateIndexExpansionVisitor.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/AggregateIndexExpansionVisitor.java @@ -163,7 +163,7 @@ private Quantifier constructSelectWhere(@Nonnull final Quantifier.ForEach baseQu // add an RCV column representing the grouping columns as the first result set column final var groupingValue = RecordConstructorValue.ofColumns(groupingValues .stream() - .map(v -> Column.of(((FieldValue)v).getLastField(), v)) + .map(Column::unnamedOf) // REMOVE: name is important? .collect(Collectors.toList())); // flow all underlying quantifiers in their own QOV columns. @@ -184,6 +184,7 @@ private Quantifier constructSelectWhere(@Nonnull final Quantifier.ForEach baseQu return Quantifier.forEach(GroupExpressionRef.of(GraphExpansion.ofOthers(allExpansionsBuilder.build()).buildSelect())); } + @SuppressWarnings("deprecation") @Nonnull private Quantifier constructGroupBy(@Nonnull final CorrelationIdentifier baseQuantifierCorrelationIdentifier, @Nonnull final List groupedValue, @@ -229,12 +230,12 @@ private Pair> constructSelectHavin final var placeholder = v.asPlaceholder(CorrelationIdentifier.uniqueID(ValueComparisonRangePredicate.Placeholder.class)); placeholderAliases.add(placeholder.getAlias()); selectHavingGraphExpansionBuilder - .addResultColumn(Column.of(field.getLastField(), field)) + .addResultColumn(Column.unnamedOf(field)) .addPlaceholder(placeholder) .addPredicate(placeholder); }); } - selectHavingGraphExpansionBuilder.addResultColumn(Column.of(aggregateValueReference.getLastField(), aggregateValueReference)); // TODO should we also add the aggregate reference as a placeholder? + selectHavingGraphExpansionBuilder.addResultColumn(Column.unnamedOf(aggregateValueReference)); // TODO should we also add the aggregate reference as a placeholder? // REMOVE: name is important? return Pair.of(selectHavingGraphExpansionBuilder.build().buildSelect(), placeholderAliases.build()); } diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/AggregateIndexMatchCandidate.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/AggregateIndexMatchCandidate.java index 975f5117eb..68c840b6e6 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/AggregateIndexMatchCandidate.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/AggregateIndexMatchCandidate.java @@ -353,17 +353,18 @@ private static Type reset(@Nonnull final Type type) { private static void addCoveringField(@Nonnull IndexKeyValueToPartialRecord.Builder builder, @Nonnull FieldValue fieldValue, @Nonnull AvailableFields.FieldData fieldData) { + // TODO field names are for debugging purposes only, we should probably use field ordinals here instead. final var simplifiedFieldValue = (FieldValue)fieldValue.simplify(AliasMap.emptyMap(), ImmutableSet.of()); - for (final Type.Record.Field field : simplifiedFieldValue.getFieldPrefix()) { - Verify.verify(field.getFieldNameOptional().isPresent()); - builder = builder.getFieldBuilder(field.getFieldName()); + for (final var maybeFieldName : simplifiedFieldValue.getFieldPrefix().getFieldNames()) { + Verify.verify(maybeFieldName.isPresent()); + builder = builder.getFieldBuilder(maybeFieldName.get()); } // TODO not sure what to do with the null standing requirement - final Type.Record.Field field = simplifiedFieldValue.getLastField(); - Verify.verify(field.getFieldNameOptional().isPresent()); - final String fieldName = field.getFieldName(); + final var maybeFieldName = simplifiedFieldValue.getLastFieldName(); + Verify.verify(maybeFieldName.isPresent()); + final String fieldName = maybeFieldName.get(); if (!builder.hasField(fieldName)) { builder.addField(fieldName, fieldData.getSource(), fieldData.getCopyIfPredicate(), fieldData.getOrdinalPath()); } diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/ValueIndexScanMatchCandidate.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/ValueIndexScanMatchCandidate.java index 8b6ab2ec9f..11aab3ca7d 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/ValueIndexScanMatchCandidate.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/ValueIndexScanMatchCandidate.java @@ -332,20 +332,20 @@ private static ScanComparisons toScanComparisons(@Nonnull final List true, fieldData.getOrdinalPath()); } diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/matching/structure/ValueMatchers.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/matching/structure/ValueMatchers.java index 784e203c82..d5b31fec59 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/matching/structure/ValueMatchers.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/matching/structure/ValueMatchers.java @@ -54,11 +54,13 @@ public static BindingMatcher anyValue() { return typed(Value.class); } + @Deprecated // should access fields by ordinals @Nonnull public static BindingMatcher fieldValueWithFieldNames(@Nonnull final String fieldPathAsString) { return fieldValueWithFieldNames(anyValue(), fieldPathAsString); } + @Deprecated // should access fields by ordinals @Nonnull public static BindingMatcher fieldValueWithFieldNames(@Nonnull final BindingMatcher downstreamValue, @Nonnull final String fieldPathAsString) { @@ -69,6 +71,7 @@ public static BindingMatcher fieldValueWithFieldNa return fieldValueWithFieldNames(downstreamValue, exactly(fieldPathMatchers)); } + @Deprecated // should access fields by ordinals @Nonnull public static BindingMatcher fieldValueWithFieldNames(@Nonnull final BindingMatcher downstreamValue, @Nonnull final CollectionMatcher downstreamFieldPath) { @@ -86,21 +89,27 @@ public static BindingMatcher fieldValueWithFieldNa matchingAllOf(FieldValue.class, ImmutableList.of(downstreamValueMatcher, downstreamFieldPathMatcher))); } + @SuppressWarnings("UnstableApiUsage") @Nonnull public static BindingMatcher fieldValueWithFieldPath(@Nonnull final BindingMatcher downstreamValue, - @Nonnull final CollectionMatcher downstreamFieldPath) { + @Nonnull final CollectionMatcher downstreamFieldPathOrdinals, + @Nonnull final CollectionMatcher downstreamFieldPathTypes) { final TypedMatcherWithExtractAndDownstream downstreamValueMatcher = typedWithDownstream(FieldValue.class, Extractor.of(FieldValue::getChild, name -> "child(" + name + ")"), downstreamValue); - final TypedMatcherWithExtractAndDownstream downstreamFieldPathMatcher = + final TypedMatcherWithExtractAndDownstream downstreamFieldPathOrdinalsMatcher = typedWithDownstream(FieldValue.class, - Extractor.of(FieldValue::getFields, name -> "fieldPath(" + name + ")"), - downstreamFieldPath); + Extractor.of(f -> f.getFieldPathOrdinals().asList(), name -> "fieldPathOrdinals(" + name + ")"), + downstreamFieldPathOrdinals); + final TypedMatcherWithExtractAndDownstream downstreamFieldPathTypesMatcher = + typedWithDownstream(FieldValue.class, + Extractor.of(FieldValue::getFieldPathTypes, name -> "fieldPathTypes(" + name + ")"), + downstreamFieldPathTypes); return typedWithDownstream(FieldValue.class, Extractor.identity(), - matchingAllOf(FieldValue.class, ImmutableList.of(downstreamValueMatcher, downstreamFieldPathMatcher))); + matchingAllOf(FieldValue.class, ImmutableList.of(downstreamValueMatcher, downstreamFieldPathOrdinalsMatcher, downstreamFieldPathTypesMatcher))); } public static BindingMatcher sumAggregationValue() { @@ -144,6 +153,13 @@ public static BindingMatcher currentObjectValue() { @Nonnull public static BindingMatcher streamableAggregateValue() { - return typed(StreamableAggregateValue.class); + return streamableAggregateValue(exactly(ImmutableList.of(anyValue()))); + } + + @Nonnull + public static BindingMatcher streamableAggregateValue(@Nonnull final CollectionMatcher downstreamValues) { + return typedWithDownstream(StreamableAggregateValue.class, + Extractor.of(StreamableAggregateValue::getChildren, name -> "children(" + name + ")"), + downstreamValues); } } diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/properties/DistinctRecordsProperty.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/properties/DistinctRecordsProperty.java index ff79d886c4..cde0bcfe5a 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/properties/DistinctRecordsProperty.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/properties/DistinctRecordsProperty.java @@ -119,7 +119,7 @@ public Boolean visitInComparandJoinPlan(@Nonnull final RecordQueryInComparandJoi @Nonnull @Override public Boolean visitAggregateIndexPlan(@Nonnull final RecordQueryAggregateIndexPlan aggregateIndexPlan) { - return visitIndexPlan(aggregateIndexPlan.getIndexPlan()); + return true; } @Nonnull diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/properties/OrderingProperty.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/properties/OrderingProperty.java index f10a9726a9..e4cc45cb7a 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/properties/OrderingProperty.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/properties/OrderingProperty.java @@ -82,6 +82,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.List; +import java.util.Optional; import java.util.function.BinaryOperator; import java.util.stream.Stream; @@ -110,6 +111,7 @@ public String toString() { @API(API.Status.EXPERIMENTAL) @SuppressWarnings("java:S3776") public static class OrderingVisitor implements RecordQueryPlanVisitor { + @SuppressWarnings("deprecation") @Nonnull @Override public Ordering visitPredicatesFilterPlan(@Nonnull final RecordQueryPredicatesFilterPlan predicatesFilterPlan) { @@ -132,9 +134,9 @@ public Ordering visitPredicatesFilterPlan(@Nonnull final RecordQueryPredicatesFi } final var fieldValue = (FieldValue)valuePredicate.getValue(); - if (fieldValue.getFields() + if (fieldValue.getFieldPathNamesMaybe() .stream() - .anyMatch(field -> field.getFieldNameOptional().isEmpty())) { + .anyMatch(Optional::isEmpty)) { return Stream.of(); } diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/CountValue.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/CountValue.java index 7a23b39857..54d00ee2e8 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/CountValue.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/CountValue.java @@ -51,7 +51,7 @@ * A counting aggregate value. */ @API(API.Status.EXPERIMENTAL) -public class CountValue implements AggregateValue, StreamableAggregateValue, IndexableAggregateValue { +public class CountValue implements Value, AggregateValue, StreamableAggregateValue, IndexableAggregateValue { private static final ObjectPlanHash BASE_HASH = new ObjectPlanHash("Count-Value"); @Nonnull diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/FieldValue.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/FieldValue.java index a13c8179b4..8a734f7b39 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/FieldValue.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/FieldValue.java @@ -25,6 +25,7 @@ import com.apple.foundationdb.record.EvaluationContext; import com.apple.foundationdb.record.ObjectPlanHash; import com.apple.foundationdb.record.PlanHashable; +import com.apple.foundationdb.record.RecordCoreException; import com.apple.foundationdb.record.provider.foundationdb.FDBRecordStoreBase; import com.apple.foundationdb.record.query.plan.cascades.AliasMap; import com.apple.foundationdb.record.query.plan.cascades.Formatter; @@ -36,9 +37,9 @@ import com.google.common.base.Verify; import com.google.common.collect.Comparators; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Streams; import com.google.common.primitives.ImmutableIntArray; import com.google.protobuf.Message; -import org.apache.commons.lang3.tuple.Pair; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -61,22 +62,9 @@ public class FieldValue implements ValueWithChild { @Nonnull private final FieldPath fieldPath; - /** - * This encapsulates the ordinals of the field path encoded by this {@link FieldValue}. It serves two purposes: - *
    - *
  • checking whether a {@link FieldValue} subsumes another {@link FieldValue}.
  • - *
  • evaluating a {@link Message} to get the corresponding field value object.
  • - *
- */ - @Nonnull - private final ImmutableIntArray fieldOrdinals; - - private FieldValue(@Nonnull Value childValue, @Nonnull FieldPath fieldPath, @Nonnull ImmutableIntArray fieldOrdinals) { - Preconditions.checkArgument(fieldOrdinals.length() == fieldPath.fields.size()); - Preconditions.checkArgument(fieldOrdinals.stream().allMatch(f -> f >= 0)); + private FieldValue(@Nonnull Value childValue, @Nonnull FieldPath fieldPath) { this.childValue = childValue; this.fieldPath = fieldPath; - this.fieldOrdinals = fieldOrdinals; } @Nonnull @@ -84,37 +72,42 @@ public FieldPath getFieldPath() { return fieldPath; } + @Deprecated // should access fields by ordinals @Nonnull - public List getFields() { - return fieldPath.getFields(); + public List getFieldPathNames() { + return fieldPath.getFieldNames().stream().map(maybe -> maybe.orElseThrow(() -> new RecordCoreException("field name should have been set"))).collect(Collectors.toList()); } @Nonnull - public ImmutableIntArray getFieldOrdinals() { - return fieldOrdinals; + public List getFieldPathTypes() { + return fieldPath.getFieldTypes(); } @Nonnull - public List getFieldPathNames() { - return getFields().stream() - .map(Field::getFieldName) - .collect(ImmutableList.toImmutableList()); + public ImmutableIntArray getFieldPathOrdinals() { + return fieldPath.fieldOrdinals; + } + + @Deprecated // should access fields by ordinals + @Nonnull + public List> getFieldPathNamesMaybe() { + return fieldPath.getFieldNames(); } @Nonnull - public List getFieldPrefix() { + public FieldPath getFieldPrefix() { return fieldPath.getFieldPrefix(); } @Nonnull - public Field getLastField() { - return fieldPath.getLastField(); + public Optional getLastFieldName() { + return fieldPath.getLastFieldName(); } @Nonnull @Override public Type getResultType() { - return getLastField().getFieldType(); + return fieldPath.getLastFieldType(); } @Nonnull @@ -126,7 +119,7 @@ public Value getChild() { @Nonnull @Override public FieldValue withNewChild(@Nonnull final Value child) { - return new FieldValue(child, fieldPath, fieldOrdinals); + return new FieldValue(child, fieldPath); } @Override @@ -135,7 +128,7 @@ public Object eval(@Nonnull final FDBRecordStoreBase stor if (!(childResult instanceof Message)) { return null; } - final var fieldValue = MessageValue.getFieldValueForFieldOrdinals((Message)childResult, fieldOrdinals); + final var fieldValue = MessageValue.getFieldValueForFieldOrdinals((Message)childResult, fieldPath.getFieldOrdinals()); // // If the last step in the field path is an array that is also nullable, then we need to unwrap the value // wrapper. @@ -164,7 +157,7 @@ public boolean subsumedBy(@Nullable final Value other, @Nonnull final AliasMap e return false; } final var otherFieldValue = (FieldValue)other; - return fieldOrdinals.equals(otherFieldValue.fieldOrdinals) && + return fieldPath.getFieldOrdinals().equals(otherFieldValue.getFieldPath().getFieldOrdinals()) && childValue.subsumedBy(otherFieldValue.childValue, equivalenceMap); } @@ -175,7 +168,7 @@ public int hashCodeWithoutChildren() { @Override public int planHash(@Nonnull final PlanHashKind hashKind) { - return PlanHashable.objectsPlanHash(hashKind, BASE_HASH, getFields().stream().map(Field::getFieldName).collect(ImmutableList.toImmutableList())); + return PlanHashable.objectsPlanHash(hashKind, BASE_HASH, getFieldPathNames()); } @Override @@ -207,7 +200,7 @@ public boolean equals(final Object other) { } @Nonnull - private static Pair resolveFieldPath(@Nonnull final Type inputType, @Nonnull final List accessors) { + private static FieldPath resolveFieldPath(@Nonnull final Type inputType, @Nonnull final List accessors) { final var accessorPathBuilder = ImmutableList.builder(); final var fieldOrdinals = ImmutableIntArray.builder(); var currentType = inputType; @@ -233,62 +226,28 @@ private static Pair resolveFieldPath(@Nonnull fina accessorPathBuilder.add(field); currentType = field.getFieldType(); } - return Pair.of(new FieldPath(accessorPathBuilder.build()), fieldOrdinals.build()); - } - - @Nonnull - private static ImmutableIntArray resolveFieldOrdinals(@Nonnull final Type inputType, @Nonnull final List fields) { - final var fieldOrdinals = ImmutableIntArray.builder(); - var currentType = inputType; - for (final var field : fields) { - SemanticException.check(currentType.getTypeCode() == Type.TypeCode.RECORD, SemanticException.ErrorCode.FIELD_ACCESS_INPUT_NON_RECORD_TYPE, - String.format("field '%s' can only be resolved on records", field.getFieldNameOptional().isPresent() ? field.getFieldName() : "#" + field.getFieldIndex())); - final var recordType = (Type.Record)currentType; - final var fieldName = field.getFieldNameOptional(); - if (fieldName.isPresent()) { - final var fieldOrdinalsMap = Objects.requireNonNull(recordType.getFieldNameToOrdinalMap()); - SemanticException.check(fieldOrdinalsMap.containsKey(fieldName.get()), SemanticException.ErrorCode.RECORD_DOES_NOT_CONTAIN_FIELD); - fieldOrdinals.add(fieldOrdinalsMap.get(fieldName.get())); - } else { - // field is using the index as an accessor. - Verify.verify(field.getFieldIndexOptional().isPresent()); - final var fieldOrdinalsMap = Objects.requireNonNull(recordType.getFieldIndexToOrdinalMap()); - SemanticException.check(fieldOrdinalsMap.containsKey(field.getFieldIndex()), SemanticException.ErrorCode.RECORD_DOES_NOT_CONTAIN_FIELD); - fieldOrdinals.add(fieldOrdinalsMap.get(field.getFieldIndex())); - } - currentType = field.getFieldType(); - if (currentType.getTypeCode() == Type.TypeCode.ARRAY ) { - currentType = Objects.requireNonNull(((Type.Array)currentType).getElementType()); - } - } - return fieldOrdinals.build(); + return new FieldPath(accessorPathBuilder.build(), fieldOrdinals.build()); } @Nonnull public static FieldValue ofFieldName(@Nonnull Value childValue, @Nonnull final String fieldName) { final var resolved = resolveFieldPath(childValue.getResultType(), ImmutableList.of(new Accessor(fieldName, -1))); - return new FieldValue(childValue, resolved.getKey(), resolved.getValue()); + return new FieldValue(childValue, resolved); } public static FieldValue ofFieldNames(@Nonnull Value childValue, @Nonnull final List fieldNames) { final var resolved = resolveFieldPath(childValue.getResultType(), fieldNames.stream().map(fieldName -> new Accessor(fieldName, -1)).collect(ImmutableList.toImmutableList())); - return new FieldValue(childValue, resolved.getKey(), resolved.getValue()); - } - - public static FieldValue ofAccessors(@Nonnull Value childValue, @Nonnull final List accessors) { - final var resolved = resolveFieldPath(childValue.getResultType(), accessors); - return new FieldValue(childValue, resolved.getKey(), resolved.getValue()); + return new FieldValue(childValue, resolved); } - public static FieldValue ofFields(@Nonnull Value childValue, @Nonnull final List fields) { - return new FieldValue(childValue, new FieldPath(fields), resolveFieldOrdinals(childValue.getResultType(), fields)); + public static FieldValue ofFields(@Nonnull Value childValue, @Nonnull final FieldPath fieldPath) { + return new FieldValue(childValue, fieldPath); } - public static FieldValue ofFieldsAndFuseIfPossible(@Nonnull Value childValue, @Nonnull final List fields) { + public static FieldValue ofFieldsAndFuseIfPossible(@Nonnull Value childValue, @Nonnull final FieldPath fields) { if (childValue instanceof FieldValue) { final var childFieldValue = (FieldValue)childValue; - return FieldValue.ofFields(childFieldValue.getChild(), - ImmutableList.builder().addAll(childFieldValue.getFields()).addAll(fields).build()); + return FieldValue.ofFields(childFieldValue.getChild(), childFieldValue.fieldPath.withSuffix(fields)); } return FieldValue.ofFields(childValue, fields); } @@ -296,23 +255,26 @@ public static FieldValue ofFieldsAndFuseIfPossible(@Nonnull Value childValue, @N @Nonnull public static FieldValue ofOrdinalNumber(@Nonnull Value childValue, final int ordinalNumber) { final var resolved = resolveFieldPath(childValue.getResultType(), ImmutableList.of(new Accessor(null, ordinalNumber))); - return new FieldValue(childValue, resolved.getKey(), resolved.getValue()); + return new FieldValue(childValue, resolved); } @Nonnull - public static Optional> stripFieldPrefixMaybe(@Nonnull List fieldPath, - @Nonnull List potentialPrefixPath) { + public static Optional stripFieldPrefixMaybe(@Nonnull FieldPath fieldPath, + @Nonnull FieldPath potentialPrefixPath) { if (fieldPath.size() < potentialPrefixPath.size()) { return Optional.empty(); } + final var fieldPathOrdinals = fieldPath.getFieldOrdinals(); + final var fieldPathTypes = fieldPath.getFieldTypes(); + final var potentialPrefixFieldOrdinals = potentialPrefixPath.getFieldOrdinals(); + final var potentialPrefixFieldTypes = potentialPrefixPath.getFieldTypes(); for (int i = 0; i < potentialPrefixPath.size(); i++) { - if (!potentialPrefixPath.get(i).equals(fieldPath.get(i))) { + if (fieldPathOrdinals.get(i) != potentialPrefixFieldOrdinals.get(i) || !fieldPathTypes.get(i).equals(potentialPrefixFieldTypes.get(i))) { return Optional.empty(); } } - - return Optional.of(ImmutableList.copyOf(fieldPath.subList(potentialPrefixPath.size(), fieldPath.size()))); + return Optional.of(fieldPath.subList(potentialPrefixPath.size(), fieldPath.size())); } /** @@ -344,21 +306,42 @@ public int getOrdinalFieldNumber() { */ @SuppressWarnings("UnstableApiUsage") public static class FieldPath { - private static final FieldPath EMPTY = new FieldPath(ImmutableList.of()); + private static final FieldPath EMPTY = new FieldPath(ImmutableList.of(), ImmutableIntArray.of()); private static final Comparator COMPARATOR = - Comparator.comparing(FieldPath::getFields, Comparators.lexicographical(Comparator.naturalOrder())); + Comparator.comparing(f -> f.fieldOrdinals.asList(), Comparators.lexicographical(Comparator.naturalOrder())); @Nonnull - private final List fields; + private final List> fieldNames; - public FieldPath(@Nonnull final List fields) { - this.fields = ImmutableList.copyOf(fields); + @Nonnull + private final List fieldTypes; + + /** + * This encapsulates the ordinals of the field path encoded by this {@link FieldValue}. It serves two purposes: + *
    + *
  • checking whether a {@link FieldValue} subsumes another {@link FieldValue}.
  • + *
  • evaluating a {@link Message} to get the corresponding field value object.
  • + *
+ */ + @Nonnull + private final ImmutableIntArray fieldOrdinals; + + FieldPath(@Nonnull final List fields, @Nonnull final ImmutableIntArray fieldOrdinals) { + Preconditions.checkArgument(fieldOrdinals.length() == fields.size()); + Preconditions.checkArgument(fieldOrdinals.stream().allMatch(f -> f >= 0)); + this.fieldNames = fields.stream().map(Field::getFieldNameOptional).collect(Collectors.toList()); + this.fieldTypes = fields.stream().map(Field::getFieldType).collect(Collectors.toList()); + this.fieldOrdinals = fieldOrdinals; } - @Nonnull - public List getFields() { - return fields; + public FieldPath(@Nonnull final List> fieldNames, @Nonnull final List fieldTypes, @Nonnull final ImmutableIntArray fieldOrdinals) { + Preconditions.checkArgument(fieldNames.size() == fieldTypes.size()); + Preconditions.checkArgument(fieldTypes.size() == fieldOrdinals.length()); + Preconditions.checkArgument(fieldOrdinals.stream().allMatch(f -> f >= 0)); + this.fieldNames = ImmutableList.copyOf(fieldNames); + this.fieldTypes = ImmutableList.copyOf(fieldTypes); + this.fieldOrdinals = fieldOrdinals; // already immutable. } @Override @@ -369,56 +352,129 @@ public boolean equals(final Object o) { if (!(o instanceof FieldPath)) { return false; } - final FieldPath fieldPath = (FieldPath)o; - return getFields().equals(fieldPath.getFields()); + final FieldPath otherFieldPath = (FieldPath)o; + return fieldNames.equals(otherFieldPath.fieldNames) + && fieldTypes.equals(otherFieldPath.fieldTypes) + && fieldOrdinals.equals(otherFieldPath.getFieldOrdinals()); } @Override public int hashCode() { - return Objects.hash(getFields()); + return Objects.hash(fieldNames, fieldOrdinals, fieldTypes); } @Override @Nonnull public String toString() { - return fields.stream() - .map(field -> { - if (field.getFieldNameOptional().isPresent()) { - return "." + field.getFieldName(); - } else if (field.getFieldIndexOptional().isPresent()) { - return "#" + field.getFieldIndex(); - } - return "(null)"; - }) + return Streams.zip(fieldNames.stream(), fieldOrdinals.stream().boxed(), + (maybeFieldName, fieldOrdinal) -> maybeFieldName.map(s -> "." + s).orElseGet(() -> "#" + fieldOrdinal)) .collect(Collectors.joining()); } @Nonnull - public List getFieldPrefix() { - return fields.subList(0, getFields().size() - 1); + public List> getFieldNames() { + return fieldNames; } @Nonnull - public Field getLastField() { - return fields.get(getFields().size() - 1); + public FieldPath getFieldPrefix() { + Preconditions.checkArgument(size() > 1); + return subList(0, size() - 1); + } + + @Nonnull + public Optional getLastFieldName() { + Preconditions.checkArgument(!isEmpty()); + return fieldNames.get(size() - 1); + } + + public int getLastFieldOrdinal() { + Preconditions.checkArgument(!isEmpty()); + return fieldOrdinals.get(size() - 1); + } + + @Nonnull + public Type getLastFieldType() { + Preconditions.checkArgument(!isEmpty()); + return fieldTypes.get(size() - 1); + } + + @Nonnull + public ImmutableIntArray getFieldOrdinals() { + return fieldOrdinals; + } + + public int size() { + return fieldTypes.size(); + } + + public boolean isEmpty() { + return fieldTypes.isEmpty(); + } + + @Nonnull + public FieldPath subList(int fromIndex, int toIndex) { + return new FieldPath(fieldNames.subList(fromIndex, toIndex), + fieldTypes.subList(fromIndex, toIndex), + fieldOrdinals.subArray(fromIndex, toIndex)); + } + + @Nonnull + public FieldPath skip(int count) { + Preconditions.checkArgument(count >= 0); + Preconditions.checkArgument(count <= size()); + + if (count == 0) { + return this; + } else if (count == size()) { + return empty(); + } + + return subList(count, size()); + } + + @Nonnull + public List getFieldTypes() { + return fieldTypes; } public boolean isPrefixOf(@Nonnull final FieldPath otherFieldPath) { - final var otherFields = otherFieldPath.getFields(); - for (int i = 0; i < fields.size(); i++) { - final Field otherField = otherFields.get(i); - if (!fields.get(i).equals(otherField)) { + if (otherFieldPath.size() < size()) { + return false; + } + for (int i = 0; i < size(); i++) { + if (otherFieldPath.fieldOrdinals.get(i) != fieldOrdinals.get(i) || !otherFieldPath.fieldTypes.get(i).equals(fieldTypes.get(i))) { return false; } } return true; } + @Nonnull + public FieldPath withSuffix(@Nonnull final FieldPath suffix) { + if (suffix.isEmpty() && this.isEmpty()) { + return empty(); + } else if (suffix.isEmpty()) { + return this; + } else if (this.isEmpty()) { + return suffix; + } + return new FieldPath(ImmutableList.>builder().addAll(fieldNames).addAll(suffix.fieldNames).build(), + ImmutableList.builder().addAll(fieldTypes).addAll(suffix.fieldTypes).build(), + ImmutableIntArray.builder().addAll(fieldOrdinals).addAll(suffix.fieldOrdinals).build()); + } + @Nonnull public static FieldPath empty() { return EMPTY; } + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + @Nonnull + public static FieldPath flat(@Nonnull final Optional fieldName, @Nonnull final Type fieldType, @Nonnull final Integer fieldOrdinal) { + return new FieldPath(ImmutableList.of(fieldName), ImmutableList.of(fieldType), ImmutableIntArray.of(fieldOrdinal)); + } + @Nonnull public static Comparator comparator() { return COMPARATOR; diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/IndexOnlyAggregateValue.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/IndexOnlyAggregateValue.java index 11d13902a8..4bb506f838 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/IndexOnlyAggregateValue.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/IndexOnlyAggregateValue.java @@ -1,5 +1,5 @@ /* - * IndexBackedAggregateValue.java + * IndexOnlyAggregateValue.java * * This source file is part of the FoundationDB open source project * @@ -49,9 +49,9 @@ * This value will be absorbed by a matching aggregation index at optimisation phase. */ @API(API.Status.EXPERIMENTAL) -public abstract class IndexOnlyAggregateValue implements AggregateValue, Value.CompileTimeValue, ValueWithChild { +public abstract class IndexOnlyAggregateValue implements Value, AggregateValue, Value.CompileTimeValue, ValueWithChild, IndexableAggregateValue { - private static final ObjectPlanHash BASE_HASH = new ObjectPlanHash("Index-Backed-Aggregate-Value"); + private static final ObjectPlanHash BASE_HASH = new ObjectPlanHash("Index-Only-Aggregate-Value"); enum PhysicalOperator { MAX_EVER_LONG, @@ -136,7 +136,7 @@ public boolean equals(final Object other) { return semanticEquals(other, AliasMap.identitiesFor(getCorrelatedTo())); } - static class MinEverLong extends IndexOnlyAggregateValue implements IndexableAggregateValue { + static class MinEverLong extends IndexOnlyAggregateValue { /** * Creates a new instance of {@link MinEverLong}. @@ -172,7 +172,7 @@ public ValueWithChild withNewChild(@Nonnull final Value rebasedChild) { } } - static class MaxEverLong extends IndexOnlyAggregateValue implements IndexableAggregateValue { + static class MaxEverLong extends IndexOnlyAggregateValue { /** * Creates a new instance of {@link MaxEverLong}. diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/IndexableAggregateValue.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/IndexableAggregateValue.java index 26f4374930..4ba9948f03 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/IndexableAggregateValue.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/IndexableAggregateValue.java @@ -28,7 +28,7 @@ * Tag interface for {@link AggregateValue} that are backed by an aggregate index. */ @API(API.Status.EXPERIMENTAL) -public interface IndexableAggregateValue { +public interface IndexableAggregateValue extends Value { @Nonnull String getIndexName(); diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/MessageValue.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/MessageValue.java index 8480cfda6c..7356f40d12 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/MessageValue.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/MessageValue.java @@ -99,10 +99,10 @@ public static Object getFieldValueForFieldOrdinals(@Nonnull MessageOrBuilder mes throw new RecordCoreException("empty list of fields"); } MessageOrBuilder current = message; - int fieldOrdinalIndex; + int fieldOrdinal; // Notice that up to fieldOrdinals.length() - 2 are calling getFieldMessageOnMessageByOrdinal, and fieldOrdinals.length() - 1 is calling getFieldOnMessageByOrdinal - for (fieldOrdinalIndex = 0; fieldOrdinalIndex < fieldOrdinals.length() - 1; fieldOrdinalIndex++) { - current = getFieldMessageOnMessageByOrdinal(current, fieldOrdinals.get(fieldOrdinalIndex)); + for (fieldOrdinal = 0; fieldOrdinal < fieldOrdinals.length() - 1; fieldOrdinal++) { + current = getFieldMessageOnMessageByOrdinal(current, fieldOrdinals.get(fieldOrdinal)); if (current == null) { return null; } diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/StreamableAggregateValue.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/StreamableAggregateValue.java index d15e85841e..a0ffa10dab 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/StreamableAggregateValue.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/StreamableAggregateValue.java @@ -21,6 +21,7 @@ package com.apple.foundationdb.record.query.plan.cascades.values; import com.apple.foundationdb.annotation.API; +import com.apple.foundationdb.record.query.plan.cascades.TreeLike; /** * Tag interface used to mark {@link AggregateValue} implementations that can be calculated by a @@ -28,5 +29,5 @@ * This is mainly used for matching. */ @API(API.Status.EXPERIMENTAL) -public interface StreamableAggregateValue { +public interface StreamableAggregateValue extends Value { } diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/TransformValue.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/TransformValue.java index f05df03b20..f7fe7bd707 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/TransformValue.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/TransformValue.java @@ -45,6 +45,7 @@ import com.google.protobuf.Descriptors; import com.google.protobuf.DynamicMessage; import com.google.protobuf.Message; +import org.apache.commons.lang3.tuple.Pair; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -174,22 +175,23 @@ public Object evalSubtree(@Nonnull final FDBRecordStoreBase< return value.eval(store, context); } else { Verify.verifyNotNull(descriptor); - final var fieldIndexToFieldMap = Verify.verifyNotNull(trieNode.getFieldIndexToFieldMap()); + final var fieldOrdinalToFieldInfoMap = Verify.verifyNotNull(trieNode.getFieldOrdinalToFieldMap()); final var childrenMap = Verify.verifyNotNull(trieNode.getChildrenMap()); final var subRecord = (M)current; final var fieldDescriptors = Objects.requireNonNull(descriptor).getFields(); final var resultMessageBuilder = DynamicMessage.newBuilder(descriptor); - for (final var fieldDescriptor : fieldDescriptors) { - final var field = fieldIndexToFieldMap.get(fieldDescriptor.getNumber()); - if (field != null) { - final var fieldTrieNode = Verify.verifyNotNull(childrenMap.get(field)); + for (int i = 0; i < Objects.requireNonNull(descriptor).getFields().size(); ++i) { + final var fieldDescriptor = fieldDescriptors.get(i); + final var fieldOrdinalAndType = fieldOrdinalToFieldInfoMap.get(i); + if (fieldOrdinalAndType != null) { + final var fieldTrieNode = Verify.verifyNotNull(childrenMap.get(fieldOrdinalAndType)); var fieldResult = evalSubtree(store, context, fieldTrieNode, fieldDescriptor.getJavaType() == Descriptors.FieldDescriptor.JavaType.MESSAGE ? fieldDescriptor.getMessageType() : null, subRecord == null ? null : subRecord.getField(fieldDescriptor)); - final var fieldType = field.getFieldType(); + final var fieldType = fieldOrdinalAndType.getValue(); if (fieldType.getTypeCode() == Type.TypeCode.ARRAY && fieldType.isNullable()) { final var wrappedDescriptor = fieldDescriptor.getMessageType(); final var wrapperBuilder = DynamicMessage.newBuilder(wrappedDescriptor); @@ -263,8 +265,8 @@ private static Map checkAndPrepareTransformMap(@Nonnull final // TODO check this using the type, not just the type code! For that to work we need isAssignableTo() checking // in the type system, so we can account for e.g. differences in nullabilities between the types. SemanticException.check(entry.getKey() - .getLastField() - .getFieldType().getTypeCode().equals(entry.getValue().getResultType().getTypeCode()), SemanticException.ErrorCode.ASSIGNMENT_WRONG_TYPE); + .getLastFieldType() + .getTypeCode().equals(entry.getValue().getResultType().getTypeCode()), SemanticException.ErrorCode.ASSIGNMENT_WRONG_TYPE); } return Maps.newLinkedHashMap(transformMap); } @@ -295,9 +297,10 @@ private static List checkAndPrepareOrderedFieldPaths(@Nonnull final M */ @Nonnull public static TrieNode computeTrieForFieldPaths(@Nonnull final Collection orderedFieldPaths, @Nonnull final Map transformMap) { - return computeTrieForFieldPaths(new FieldPath(ImmutableList.of()), transformMap, Iterators.peekingIterator(orderedFieldPaths.iterator())); + return computeTrieForFieldPaths(FieldPath.empty(), transformMap, Iterators.peekingIterator(orderedFieldPaths.iterator())); } + @SuppressWarnings("UnstableApiUsage") @Nonnull private static TrieNode computeTrieForFieldPaths(@Nonnull final FieldPath prefix, @Nonnull final Map transformMap, @@ -306,22 +309,19 @@ private static TrieNode computeTrieForFieldPaths(@Nonnull final FieldPath prefix orderedFieldPathIterator.next(); return new TrieNode(Verify.verifyNotNull(transformMap.get(prefix)), null); } - final var childrenMapBuilder = Maps.newLinkedHashMap(); + final var childrenMapBuilder = Maps., TrieNode>newLinkedHashMap(); while (orderedFieldPathIterator.hasNext()) { final var fieldPath = orderedFieldPathIterator.peek(); if (!prefix.isPrefixOf(fieldPath)) { break; } - final var prefixFields = prefix.getFields(); - final var currentField = fieldPath.getFields().get(prefixFields.size()); - final var nestedPrefix = new FieldPath(ImmutableList.builder() - .addAll(prefixFields) - .add(currentField) - .build()); + final var prefixLength = prefix.size(); + final var currentField = FieldPath.flat(fieldPath.getFieldNames().get(prefixLength), fieldPath.getFieldTypes().get(prefixLength), fieldPath.getFieldOrdinals().get(prefixLength)); + final var nestedPrefix = prefix.withSuffix(currentField); final var currentTrie = computeTrieForFieldPaths(nestedPrefix, transformMap, orderedFieldPathIterator); - childrenMapBuilder.put(currentField, currentTrie); + childrenMapBuilder.put(Pair.of(currentField.getLastFieldOrdinal(), currentField.getLastFieldType()), currentTrie); } return new TrieNode(null, childrenMapBuilder); @@ -358,14 +358,14 @@ public static class TrieNode { @Nullable private final Value value; @Nullable - private final Map childrenMap; + private final Map, TrieNode> childrenMap; @Nonnull - private final Supplier> fieldIndexToFieldMapSupplier; + private final Supplier>> fieldOrdinalToFieldMapSupplier; - public TrieNode(@Nullable final Value value, @Nullable final Map childrenMap) { + public TrieNode(@Nullable final Value value, @Nullable final Map, TrieNode> childrenMap) { this.value = value; this.childrenMap = childrenMap == null ? null : Maps.newLinkedHashMap(childrenMap); - this.fieldIndexToFieldMapSupplier = Suppliers.memoize(() -> computeFieldIndexToFieldMap(childrenMap)); + this.fieldOrdinalToFieldMapSupplier = Suppliers.memoize(() -> computeFieldOrdinalToFieldMap(childrenMap)); } @Nullable @@ -374,23 +374,23 @@ public Value getValue() { } @Nullable - public Map getChildrenMap() { + public Map, TrieNode> getChildrenMap() { return childrenMap; } @Nullable - public Map getFieldIndexToFieldMap() { - return fieldIndexToFieldMapSupplier.get(); + public Map> getFieldOrdinalToFieldMap() { + return fieldOrdinalToFieldMapSupplier.get(); } @Nullable - private static Map computeFieldIndexToFieldMap(@Nullable final Map childrenMap) { + private static Map> computeFieldOrdinalToFieldMap(@Nullable final Map, TrieNode> childrenMap) { if (childrenMap == null) { return null; } - final var resultBuilder = ImmutableMap.builder(); + final var resultBuilder = ImmutableMap.>builder(); for (final var entry : childrenMap.entrySet()) { - resultBuilder.put(entry.getKey().getFieldIndex(), entry.getKey()); + resultBuilder.put(entry.getKey().getLeft(), entry.getKey()); } return resultBuilder.build(); } diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/Values.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/Values.java index 678aea33f8..da2cf7e2bf 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/Values.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/Values.java @@ -20,6 +20,7 @@ package com.apple.foundationdb.record.query.plan.cascades.values; +import com.apple.foundationdb.record.query.expressions.Field; import com.apple.foundationdb.record.query.plan.cascades.AliasMap; import com.apple.foundationdb.record.query.plan.cascades.CorrelationIdentifier; import com.apple.foundationdb.record.query.plan.cascades.typing.Type; @@ -27,12 +28,15 @@ import com.google.common.base.Verify; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Streams; import javax.annotation.Nonnull; import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.function.Function; import java.util.function.Supplier; +import java.util.stream.IntStream; /** * Helper class for dealing with {@link Value}s. @@ -91,10 +95,13 @@ public static Set primitiveAccessorsForType(@Nonnull final Type type, final var recordType = (Type.Record)type; final var fields = recordType.getFields(); + int i = 0; for (final var field : fields) { - primitiveAccessorsForType(field.getFieldType(), () -> FieldValue.ofFieldsAndFuseIfPossible(baseValueSupplier.get(), ImmutableList.of(field)), constantAliases).stream() + final int finalI1 = i; + primitiveAccessorsForType(field.getFieldType(), () -> FieldValue.ofFieldsAndFuseIfPossible(baseValueSupplier.get(), FieldValue.FieldPath.flat(field.getFieldNameOptional(), field.getFieldType(), finalI1)), constantAliases).stream() .map(orderingValue -> orderingValue.simplify(DefaultValueSimplificationRuleSet.ofSimplificationRules(), AliasMap.emptyMap(), constantAliases)) .forEach(orderingValuesBuilder::add); + i++; } return orderingValuesBuilder.build(); diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/CompensateRecordConstructorRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/CompensateRecordConstructorRule.java index 2bcd567fb6..afa581ef76 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/CompensateRecordConstructorRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/CompensateRecordConstructorRule.java @@ -23,16 +23,18 @@ import com.apple.foundationdb.annotation.API; import com.apple.foundationdb.record.query.plan.cascades.LinkedIdentityMap; import com.apple.foundationdb.record.query.plan.cascades.matching.structure.BindingMatcher; +import com.apple.foundationdb.record.query.plan.cascades.values.FieldValue; import com.apple.foundationdb.record.query.plan.cascades.values.RecordConstructorValue; import com.apple.foundationdb.record.query.plan.cascades.values.Value; import com.apple.foundationdb.record.query.plan.cascades.values.simplification.MatchOrCompensateFieldValueRule.FieldValueCompensation; -import com.google.common.collect.ImmutableList; +import com.google.common.collect.Streams; import org.apache.commons.lang3.tuple.Pair; import javax.annotation.Nonnull; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.IntStream; import java.util.stream.Stream; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.MultiMatcher.all; @@ -58,24 +60,26 @@ public CompensateRecordConstructorRule() { super(rootMatcher); } + @SuppressWarnings("UnstableApiUsage") @Override public void onMatch(@Nonnull final ValueComputationRuleCall, Map>> call) { final var bindings = call.getBindings(); final var recordConstructorValue = bindings.get(rootMatcher); final var mergedMatchedValuesMap = - recordConstructorValue.getColumns() - .stream() - .flatMap(column -> { - final var childResult = call.getResult(column.getValue()); - return childResult != null ? Stream.of(Pair.of(column, childResult.getResult())) : Stream.empty(); + Streams.zip(recordConstructorValue.getColumns().stream(), IntStream.range(0, recordConstructorValue.getColumns().size()).boxed(), Pair::of) + .flatMap(columnOrdinalPair -> { + final var childResult = call.getResult(columnOrdinalPair.getLeft().getValue()); + return childResult != null ? Stream.of(Pair.of(columnOrdinalPair, childResult.getResult())) : Stream.empty(); }) .map(columnWithResult -> { - final var column = columnWithResult.getLeft(); + final var columnOrdinalPair = columnWithResult.getLeft(); + final var column = columnOrdinalPair.getLeft(); + final var ordinal = columnOrdinalPair.getRight(); final var matchedValuesMap = columnWithResult.getRight(); // - // No we have a column and the result we computed for all columns that do have results associated with them, + // Now we have a column and the result we computed for all columns that do have results associated with them, // i.e. the columns flowing results of values we care about. // final var newMatchedValuesMap = new LinkedIdentityMap>(); @@ -83,8 +87,8 @@ public void onMatch(@Nonnull final ValueComputationRuleCall innerChildMatcher = anyValue(); @Nonnull - private static final CollectionMatcher innerFieldPathMatcher = all(anyObject()); + private static final CollectionMatcher innerFieldPathOrdinalsMatcher = all(anyObject()); + + @Nonnull + private static final CollectionMatcher innerFieldPathTypesMatcher = all(anyObject()); @Nonnull private static final BindingMatcher innerFieldValueMatcher = - ValueMatchers.fieldValueWithFieldPath(innerChildMatcher, innerFieldPathMatcher); + ValueMatchers.fieldValueWithFieldPath(innerChildMatcher, innerFieldPathOrdinalsMatcher, innerFieldPathTypesMatcher); @Nonnull - private static final CollectionMatcher outerFieldPathMatcher = all(anyObject()); + private static final CollectionMatcher outerFieldPathOrdinalsMatcher = all(anyObject()); + + @Nonnull + private static final CollectionMatcher outerFieldPathTypesMatcher = all(anyObject()); @Nonnull private static final BindingMatcher rootMatcher = - ValueMatchers.fieldValueWithFieldPath(innerFieldValueMatcher, outerFieldPathMatcher); + ValueMatchers.fieldValueWithFieldPath(innerFieldValueMatcher, outerFieldPathOrdinalsMatcher, outerFieldPathTypesMatcher); public ComposeFieldValueOverFieldValueRule() { super(rootMatcher); @@ -70,11 +77,15 @@ public void onMatch(@Nonnull final ValueSimplificationRuleCall call) { final var bindings = call.getBindings(); final var innerChild = bindings.get(innerChildMatcher); - final var innerFieldPath = bindings.get(innerFieldPathMatcher); - Verify.verify(!innerFieldPath.isEmpty()); - final var outerFieldPath = bindings.get(outerFieldPathMatcher); - Verify.verify(!outerFieldPath.isEmpty()); - - call.yield(FieldValue.ofFields(innerChild, ImmutableList.builder().addAll(innerFieldPath).addAll(outerFieldPath).build())); + final var innerFieldPathOrdinals = bindings.get(innerFieldPathOrdinalsMatcher); + final var innerFieldPathTypes = bindings.get(innerFieldPathTypesMatcher); + Verify.verify(!innerFieldPathOrdinals.isEmpty()); + Verify.verify(!innerFieldPathTypes.isEmpty()); + final var outer = bindings.get(rootMatcher); + final var outerFieldPathOrdinals = bindings.get(outerFieldPathOrdinalsMatcher); + final var outerFieldPathTypes = bindings.get(outerFieldPathTypesMatcher); + Verify.verify(!outerFieldPathOrdinals.isEmpty()); + Verify.verify(!outerFieldPathTypes.isEmpty()); + call.yield(FieldValue.ofFields(innerChild, ((FieldValue)innerChild).getFieldPath().withSuffix(outer.getFieldPath()))); } } diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/ComposeFieldValueOverRecordConstructorRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/ComposeFieldValueOverRecordConstructorRule.java index fe7ba53566..ca2e7436d8 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/ComposeFieldValueOverRecordConstructorRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/ComposeFieldValueOverRecordConstructorRule.java @@ -27,6 +27,7 @@ import com.apple.foundationdb.record.query.plan.cascades.matching.structure.BindingMatcher; import com.apple.foundationdb.record.query.plan.cascades.matching.structure.CollectionMatcher; import com.apple.foundationdb.record.query.plan.cascades.matching.structure.ValueMatchers; +import com.apple.foundationdb.record.query.plan.cascades.typing.Type; import com.apple.foundationdb.record.query.plan.cascades.typing.Type.Record.Field; import com.apple.foundationdb.record.query.plan.cascades.values.FieldValue; import com.apple.foundationdb.record.query.plan.cascades.values.RecordConstructorValue; @@ -54,11 +55,14 @@ public class ComposeFieldValueOverRecordConstructorRule extends ValueSimplificat recordConstructorValue(all(anyValue())); @Nonnull - private static final CollectionMatcher fieldPathMatcher = all(anyObject()); + private static final CollectionMatcher fieldPathOrdinalsMatcher = all(anyObject()); + + @Nonnull + private static final CollectionMatcher fieldPathTypesMatcher = all(anyObject()); @Nonnull private static final BindingMatcher rootMatcher = - ValueMatchers.fieldValueWithFieldPath(recordConstructorMatcher, fieldPathMatcher); + ValueMatchers.fieldValueWithFieldPath(recordConstructorMatcher, fieldPathOrdinalsMatcher, fieldPathTypesMatcher); public ComposeFieldValueOverRecordConstructorRule() { super(rootMatcher); @@ -69,31 +73,29 @@ public ComposeFieldValueOverRecordConstructorRule() { public void onMatch(@Nonnull final ValueSimplificationRuleCall call) { final var bindings = call.getBindings(); - final var fieldPath = bindings.get(fieldPathMatcher); - Verify.verify(!fieldPath.isEmpty()); + final var fieldPathOrdinals = bindings.get(fieldPathOrdinalsMatcher); + Verify.verify(!fieldPathOrdinals.isEmpty()); + final var fieldPathTypes = bindings.get(fieldPathTypesMatcher); + Verify.verify(!fieldPathTypes.isEmpty()); final var recordConstructor = bindings.get(recordConstructorMatcher); - final var firstField = Objects.requireNonNull(Iterables.getFirst(fieldPath, null)); - final var column = findColumn(recordConstructor, firstField); - if (fieldPath.size() == 1) { + final var firstFieldOrdinal = Objects.requireNonNull(Iterables.getFirst(fieldPathOrdinals, null)); + final var fieldFieldType = Objects.requireNonNull(Iterables.getFirst(fieldPathTypes, null)); + final var column = findColumn(recordConstructor, firstFieldOrdinal, fieldFieldType); + + final var root = bindings.get(rootMatcher); + if (fieldPathOrdinals.size() == 1) { // just return the child call.yield(column.getValue()); } else { - call.yield(FieldValue.ofFields(column.getValue(), - fieldPath.stream() - .skip(1L) - .collect(ImmutableList.toImmutableList()))); + call.yield(FieldValue.ofFields(column.getValue(), root.getFieldPath().skip(1))); } } @Nonnull - private static Column findColumn(@Nonnull final RecordConstructorValue recordConstructorValue, @Nonnull final Field field) { - for (final var column : recordConstructorValue.getColumns()) { - if (field.getFieldIndex() == column.getField().getFieldIndex()) { - Verify.verify(field.getFieldNameOptional().equals(column.getField().getFieldNameOptional())); - return column; - } - } - throw new RecordCoreException("should have found field by field name"); + private static Column findColumn(@Nonnull final RecordConstructorValue recordConstructorValue, final int fieldOrdinal, @Nonnull Type fieldType) { + final var result = recordConstructorValue.getColumns().get(fieldOrdinal); + Verify.verify(result.getField().getFieldType().equals(fieldType)); + return result; } } diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/MatchOrCompensateFieldValueRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/MatchOrCompensateFieldValueRule.java index 1307b8e147..1179bc888c 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/MatchOrCompensateFieldValueRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/MatchOrCompensateFieldValueRule.java @@ -47,11 +47,14 @@ @SuppressWarnings("PMD.TooManyStaticImports") public class MatchOrCompensateFieldValueRule extends ValueComputationRule, Map>, FieldValue> { @Nonnull - private static final CollectionMatcher fieldPathMatcher = all(anyObject()); + private static final CollectionMatcher fieldPathOrdinalsMatcher = all(anyObject()); + + @Nonnull + private static final CollectionMatcher fieldPathTypesMatcher = all(anyObject()); @Nonnull private static final BindingMatcher rootMatcher = - ValueMatchers.fieldValueWithFieldPath(anyValue(), fieldPathMatcher); + ValueMatchers.fieldValueWithFieldPath(anyValue(), fieldPathOrdinalsMatcher, fieldPathTypesMatcher); public MatchOrCompensateFieldValueRule() { super(rootMatcher); @@ -80,7 +83,7 @@ public void onMatch(@Nonnull final ValueComputationRuleCall { if (pathSuffix.isEmpty()) { newMatchedValuesMap.put(toBePulledUpValue, Function.identity()); @@ -94,7 +97,7 @@ public void onMatch(@Nonnull final ValueComputationRuleCall newMatchedValuesMap.put(toBePulledUpValue, fieldValueCompensation.withSuffix(pathSuffix))); } } @@ -108,22 +111,23 @@ public void onMatch(@Nonnull final ValueComputationRuleCall { @Nonnull - private final List fieldPath; + private final FieldValue.FieldPath fieldPath; @Nonnull private final Function downstreamCompensation; - public FieldValueCompensation(@Nonnull final List fieldPath) { + public FieldValueCompensation(@Nonnull final FieldValue.FieldPath fieldPath) { this(fieldPath, Function.identity()); } - public FieldValueCompensation(@Nonnull final List fieldPath, @Nonnull final Function downstreamCompensation) { - this.fieldPath = ImmutableList.copyOf(fieldPath); + public FieldValueCompensation(@Nonnull final FieldValue.FieldPath fieldPath, @Nonnull final Function downstreamCompensation) { + this.fieldPath = fieldPath; this.downstreamCompensation = downstreamCompensation; } + @Nonnull - public List getFieldPath() { + public FieldValue.FieldPath getFieldPath() { return fieldPath; } @@ -133,7 +137,7 @@ public Value apply(final Value value) { return downstreamCompensation.apply(FieldValue.ofFieldsAndFuseIfPossible(value, fieldPath)); } - public Function withSuffix(@Nonnull final List suffixFieldPath) { + public Function withSuffix(@Nonnull final FieldValue.FieldPath suffixFieldPath) { if (suffixFieldPath.isEmpty()) { return downstreamCompensation; } diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/plans/RecordQueryAggregateIndexPlan.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/plans/RecordQueryAggregateIndexPlan.java index c31a03d6c1..4f6dfddb6c 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/plans/RecordQueryAggregateIndexPlan.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/plans/RecordQueryAggregateIndexPlan.java @@ -247,7 +247,7 @@ public boolean equalsWithoutChildren(@Nonnull RelationalExpression otherExpressi return indexPlan.structuralEquals(other.indexPlan, equivalencesMap) && recordTypeName.equals(other.recordTypeName) && toRecord.equals(other.toRecord) && - resultValue.equalsWithoutChildren(other.resultValue, equivalencesMap); + resultValue.semanticEquals(other.resultValue, equivalencesMap); } @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") @@ -279,22 +279,14 @@ public int getComplexity() { @Override public int planHash(@Nonnull final PlanHashKind hashKind) { switch (hashKind) { - case LEGACY: - return indexPlan.planHash(hashKind); case FOR_CONTINUATION: case STRUCTURAL_WITHOUT_LITERALS: - return PlanHashable.objectsPlanHash(hashKind, BASE_HASH, indexPlan); + return PlanHashable.objectsPlanHash(hashKind, BASE_HASH, indexPlan, resultValue); default: throw new UnsupportedOperationException("Hash kind " + hashKind.name() + " is not supported"); } } - @Nonnull - @Override - public List getQuantifiers() { - return ImmutableList.of(); - } - @Nonnull @Override public PlannerGraph rewritePlannerGraph(@Nonnull final List childGraphs) { diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/indexes/RankIndexTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/indexes/RankIndexTest.java index bf44d59133..e9319acde5 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/indexes/RankIndexTest.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/indexes/RankIndexTest.java @@ -157,6 +157,7 @@ /** * Tests for {@code RANK} type indexes. */ +@SuppressWarnings("deprecation") @Tag(Tags.RequiresFDB) class RankIndexTest extends FDBRecordStoreQueryTestBase { diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBAndQueryToIntersectionTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBAndQueryToIntersectionTest.java index 0636012d92..37f44195c4 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBAndQueryToIntersectionTest.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBAndQueryToIntersectionTest.java @@ -106,6 +106,7 @@ /** * Tests related to planning a query with an AND clause into an intersection plan. */ +@SuppressWarnings("deprecation") @Tag(Tags.RequiresFDB) public class FDBAndQueryToIntersectionTest extends FDBRecordStoreQueryTestBase { /** diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBCoveringIndexQueryTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBCoveringIndexQueryTest.java index 3744284255..3b94ee758c 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBCoveringIndexQueryTest.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBCoveringIndexQueryTest.java @@ -89,6 +89,7 @@ /** * Tests related to planning queries that can use covering indexes. */ +@SuppressWarnings("deprecation") @Tag(Tags.RequiresFDB) class FDBCoveringIndexQueryTest extends FDBRecordStoreQueryTestBase { /** diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBInQueryTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBInQueryTest.java index c3d82bcc53..92f95bcd70 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBInQueryTest.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBInQueryTest.java @@ -121,6 +121,7 @@ /** * Tests related to planning queries with an IN clause. */ +@SuppressWarnings("deprecation") @Tag(Tags.RequiresFDB) class FDBInQueryTest extends FDBRecordStoreQueryTestBase { /** diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBNestedFieldQueryTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBNestedFieldQueryTest.java index b99bbbacc7..690f40c51a 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBNestedFieldQueryTest.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBNestedFieldQueryTest.java @@ -93,6 +93,7 @@ /** * Tests of the planner's ability to handle nested fields. */ +@SuppressWarnings("deprecation") @Tag(Tags.RequiresFDB) class FDBNestedFieldQueryTest extends FDBRecordStoreQueryTestBase { /** diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBOrQueryToUnionTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBOrQueryToUnionTest.java index b662010c54..c840d877d9 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBOrQueryToUnionTest.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBOrQueryToUnionTest.java @@ -111,6 +111,7 @@ /** * Tests related to planning a query with an OR clause into a union plan. */ +@SuppressWarnings("deprecation") @Tag(Tags.RequiresFDB) class FDBOrQueryToUnionTest extends FDBRecordStoreQueryTestBase { /** diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBRecordStoreQueryTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBRecordStoreQueryTest.java index 9b444eacd4..674443b213 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBRecordStoreQueryTest.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBRecordStoreQueryTest.java @@ -237,6 +237,7 @@ void queryByteString() throws Exception { /** * Verify that simple queries execute properly with continuations. */ + @SuppressWarnings("deprecation") @DualPlannerTest void queryWithContinuation() throws Exception { setupSimpleRecordStore(null, (i, builder) -> { diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBSimpleQueryGraphTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBSimpleQueryGraphTest.java index 57b0e93813..b1c69a9af3 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBSimpleQueryGraphTest.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBSimpleQueryGraphTest.java @@ -85,6 +85,7 @@ /** * Tests of query planning and execution for queries on records with repeated fields. */ +@SuppressWarnings("deprecation") @Tag(Tags.RequiresFDB) public class FDBSimpleQueryGraphTest extends FDBRecordStoreQueryTestBase { @DualPlannerTest(planner = DualPlannerTest.Planner.CASCADES) diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/GroupByTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/GroupByTest.java index 1afd1aaa00..d4202bb55c 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/GroupByTest.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/GroupByTest.java @@ -70,6 +70,7 @@ /** * test suite for {@code GROUP BY} expression planning and execution. */ +@SuppressWarnings("deprecation") @Tag(Tags.RequiresFDB) public class GroupByTest extends FDBRecordStoreQueryTestBase { diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/plan/cascades/TransformValueTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/plan/cascades/TransformValueTest.java index 9c7d67df7d..0975a63c31 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/plan/cascades/TransformValueTest.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/plan/cascades/TransformValueTest.java @@ -33,6 +33,7 @@ import com.google.common.base.Verify; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.apache.commons.lang3.tuple.Pair; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -68,30 +69,30 @@ void testTransformTrie() { Assertions.assertNull(transformTrie.getValue()); var childrenMap = transformTrie.getChildrenMap(); Assertions.assertNotNull(childrenMap); - Assertions.assertTrue(childrenMap.containsKey(aField)); + Assertions.assertTrue(childrenMap.containsKey(Pair.of(0, aField.getFieldType()))); Assertions.assertEquals(1, childrenMap.size()); - var aTrie = childrenMap.get(aField); + var aTrie = childrenMap.get(Pair.of(0, aField.getFieldType())); Assertions.assertNull(aTrie.getValue()); childrenMap = aTrie.getChildrenMap(); Assertions.assertNotNull(childrenMap); - Assertions.assertTrue(childrenMap.containsKey(aaField)); - Assertions.assertTrue(childrenMap.containsKey(abField)); + Assertions.assertTrue(childrenMap.containsKey(Pair.of(0, aaField.getFieldType()))); + Assertions.assertTrue(childrenMap.containsKey(Pair.of(1, abField.getFieldType()))); Assertions.assertEquals(2, childrenMap.size()); - var aaTrie = childrenMap.get(aaField); + var aaTrie = childrenMap.get(Pair.of(0, aaField.getFieldType())); Assertions.assertNull(aaTrie.getValue()); childrenMap = aaTrie.getChildrenMap(); Assertions.assertNotNull(childrenMap); - Assertions.assertTrue(childrenMap.containsKey(aaaField)); - Assertions.assertTrue(childrenMap.containsKey(aabField)); + Assertions.assertTrue(childrenMap.containsKey(Pair.of(0, aaaField.getFieldType()))); + Assertions.assertTrue(childrenMap.containsKey(Pair.of(1, aabField.getFieldType()))); Assertions.assertEquals(2, childrenMap.size()); - var aaaTrie = childrenMap.get(aaaField); + var aaaTrie = childrenMap.get(Pair.of(0, aaaField.getFieldType())); Assertions.assertNull(aaaTrie.getChildrenMap()); Assertions.assertNotNull(aaaTrie.getValue()); Assertions.assertEquals(aaaTrie.getValue(), transformMap.get(a_aa_aaa.getFieldPath())); - var aabTrie = childrenMap.get(aabField); + var aabTrie = childrenMap.get(Pair.of(1, aabField.getFieldType())); Assertions.assertEquals(aabTrie.getValue(), transformMap.get(a_aa_aab.getFieldPath())); childrenMap = aTrie.getChildrenMap(); - var abTrie = childrenMap.get(abField); + var abTrie = childrenMap.get(Pair.of(1, abField.getFieldType())); Assertions.assertNull(abTrie.getChildrenMap()); Assertions.assertNotNull(abTrie.getValue()); Assertions.assertEquals(abTrie.getValue(), transformMap.get(a_ab.getFieldPath())); diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/plan/cascades/view/FieldValueMatcher.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/plan/cascades/view/FieldValueMatcher.java index 5d4c533a7c..7c0bcb48b9 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/plan/cascades/view/FieldValueMatcher.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/query/plan/cascades/view/FieldValueMatcher.java @@ -44,10 +44,11 @@ public FieldValueMatcher(@Nonnull String fieldName) { this(Collections.singletonList(fieldName)); } + @SuppressWarnings("deprecation") @Override protected boolean matchesSafely(final Value element) { return element instanceof FieldValue && - ((FieldValue)element).getFields().equals(fieldPath); + ((FieldValue)element).getFieldPathNames().equals(fieldPath); } @Override