diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/CascadesRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/CascadesRule.java index 8aaabaff66..1501528372 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/CascadesRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/CascadesRule.java @@ -27,7 +27,6 @@ import javax.annotation.Nonnull; import java.util.Collection; -import java.util.Optional; import java.util.Set; /** @@ -77,15 +76,15 @@ public CascadesRule(@Nonnull BindingMatcher matcher, CollectionOptional.empty() if the rule matches anything - * @see PlanningRuleSet + * {@inheritDoc} + * + *

By default, this is derived from {@link BindingMatcher#getRootClasses()}. Subclasses that need to opt into the + * always-rules bucket should override this to return an empty set. */ @Nonnull @Override - public Optional> getRootOperator() { - return Optional.of(matcher.getRootClass()); + public Set> getRootOperators() { + return matcher.getRootClasses(); } @Nonnull diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/PlannerRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/PlannerRule.java index 81e81bc51a..4064af50bf 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/PlannerRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/PlannerRule.java @@ -24,7 +24,7 @@ import com.apple.foundationdb.record.query.plan.cascades.matching.structure.BindingMatcher; import javax.annotation.Nonnull; -import java.util.Optional; +import java.util.Set; /** * Basic rule interface. @@ -37,13 +37,15 @@ @API(API.Status.EXPERIMENTAL) public interface PlannerRule { /** - * Returns the class of the operator at the root of the binding expression, if this rule uses a non-trivial binding. - * Used primarily for indexing rules for more efficient rule search. - * @return the class of the root of this rule's binding, or Optional.empty() if the rule matches anything + * Returns the set of concrete operator classes that this rule should be indexed under in a rule set. This is used + * by the planner to quickly pick the subset of rules that could possibly fire on a visited node. If the set is + * empty, the rule will go into the always-rules bucket. Otherwise, the rule is indexed under each class returned. + * @return a (possibly empty) set of classes * @see PlanningRuleSet + * @see BindingMatcher#getRootClasses() */ @Nonnull - Optional> getRootOperator(); + Set> getRootOperators(); void onMatch(@Nonnull C call); diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/matching/structure/BindingMatcher.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/matching/structure/BindingMatcher.java index 497ea47dde..e6774d87da 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/matching/structure/BindingMatcher.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/matching/structure/BindingMatcher.java @@ -30,6 +30,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Set; import java.util.stream.Stream; /** @@ -52,15 +53,27 @@ public interface BindingMatcher { String INDENTATION = " "; /** - * Get a class that this matcher can match. Ideally, it should be the lowest such class, but it may not be. - * A planner will generally use this method to quickly determine a set of rules that could match an expression, - * without considering each rule and trying to apply it. A good implementation of this method helps the planner - * match rules efficiently. - * @return a class object for a class that is a super class of every planner expression this matcher can match + * Returns the dispatch class for this matcher, i.e., the upper bound of the type {@code T} that this matcher binds + * to. The class returned here is used by {@link #bindMatches} to gate the dispatching; it is also the class on + * which {@code where(…)} constraints operate. For purposes of rule-set indexing (i.e., picking the subset of rules + * that could possibly fire on a given visited node), see {@link #getRootClasses()}, which can be narrower than the + * upper-bound class returned here. + * @return the class object for the dispatch class */ @Nonnull Class getRootClass(); + /** + * Get the set of concrete classes under which the enclosing rule of this matcher should be indexed in a rule set. + * + *

The default returns {@code Set.of(getRootClass())}, which is appropriate for most matchers. Matchers that need + * to bind more than one subclass should override this in order to benefit from targeted indexing. + */ + @Nonnull + default Set> getRootClasses() { + return Set.of(getRootClass()); + } + /** * Attempt to match this matcher against the given object. * Note that implementations should only attempt to match the given object with this matcher and delegate @@ -105,7 +118,8 @@ default Stream bindMatches(@Nonnull RecordQueryPlannerConfigura default BindingMatcher where(@Nonnull final BindingMatcher downstream) { return TypedMatcherWithExtractAndDownstream.typedWithDownstream(getRootClass(), Extractor.identity(), - AllOfMatcher.matchingAllOf(getRootClass(), ImmutableList.of(this, downstream))); + AllOfMatcher.matchingAllOf(getRootClass(), ImmutableList.of(this, downstream)), + getRootClasses()); } /** diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/matching/structure/TypedMatcher.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/matching/structure/TypedMatcher.java index a0452c6149..8c6f78974e 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/matching/structure/TypedMatcher.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/matching/structure/TypedMatcher.java @@ -22,8 +22,13 @@ import com.apple.foundationdb.annotation.API; import com.apple.foundationdb.record.query.plan.RecordQueryPlannerConfiguration; +import com.apple.foundationdb.record.query.plan.cascades.debug.Debugger; +import com.google.common.base.Verify; +import com.google.common.collect.ImmutableSet; import javax.annotation.Nonnull; +import java.util.Set; +import java.util.stream.Collectors; import java.util.stream.Stream; /** @@ -40,30 +45,73 @@ public class TypedMatcher implements BindingMatcher { @Nonnull private final Class bindableClass; + @Nonnull + private final Set> rootClasses; public TypedMatcher(@Nonnull final Class bindableClass) { + this(bindableClass, ImmutableSet.of(bindableClass)); + } + + public TypedMatcher(@Nonnull final Class bindableClass, + @Nonnull final Set> rootClasses) { + // Sanity-check that every advertised root class is a (sub-)type of `bindableClass`. + Debugger.sanityCheck(() -> rootClasses.forEach(c -> Verify.verify(bindableClass.isAssignableFrom(c), + "rootClass %s is not assignable from bindableClass %s", c, bindableClass))); + this.bindableClass = bindableClass; + this.rootClasses = ImmutableSet.copyOf(rootClasses); } @Nonnull @Override - public Class getRootClass() { + public final Class getRootClass() { return bindableClass; } + @Nonnull + @Override + public final Set> getRootClasses() { + return rootClasses; + } + + /** + * Returns whether the {@link #rootClasses} set contains only {@link #bindableClass} itself. + */ + private boolean rootClassesIsTrivial() { + return rootClasses.size() == 1 && rootClasses.contains(bindableClass); + } + @Nonnull @Override public Stream bindMatchesSafely(@Nonnull RecordQueryPlannerConfiguration plannerConfiguration, @Nonnull PlannerBindings outerBindings, @Nonnull T in) { + // If `rootClasses` is not the trivial default `{bindableClass}`, sanity-check that the class of `in` is + // (exactly) one of the `rootClasses`. When `rootClasses` is a narrower set than the default, the matcher + // relies on the rule-set indexing path to only invoke it for instances of one of those classes. + // + // In the trivial default case this check is skipped on purpose, as `bindableClass` may be an abstract base + // class, and the check would then fail if `in.getClass()` is a concrete subclass. + if (!rootClassesIsTrivial()) { + Debugger.sanityCheck(() -> Verify.verify(rootClasses.contains(in.getClass()), + "`TypedMatcher` invoked with class `%s`; expected one of %s", in.getClass(), rootClasses)); + } + return Stream.of(PlannerBindings.from(this, in)); } @Override public String explainMatcher(@Nonnull final Class atLeastType, @Nonnull final String boundId, @Nonnull final String indentation) { - if (getRootClass().isAssignableFrom(atLeastType)) { + // Don't bother emitting a redundant `case _: «RootClass»` type guard if it’s already established (according to + // `atLeastType`) that `boundId` is at least of type `getRootClass()`. Just emit a `_` wildcard pattern. + if (rootClassesIsTrivial() && getRootClass().isAssignableFrom(atLeastType)) { return "case _ => success "; - } else { - return "case _: " + getRootClass().getSimpleName() + " => success "; } + + // Emit a typed wildcard pattern. Print a `(… | …)` union type if there are multiple root classes. + final String alternatives = rootClasses.stream() + .map(Class::getSimpleName) + .collect(Collectors.joining(" | ")); + final String pattern = rootClasses.size() > 1 ? "(" + alternatives + ")" : alternatives; + return "case _: " + pattern + " => success "; } @Nonnull diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/matching/structure/TypedMatcherWithExtractAndDownstream.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/matching/structure/TypedMatcherWithExtractAndDownstream.java index fcaccd563a..b03a4f1ac2 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/matching/structure/TypedMatcherWithExtractAndDownstream.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/matching/structure/TypedMatcherWithExtractAndDownstream.java @@ -24,6 +24,7 @@ import com.apple.foundationdb.record.query.plan.RecordQueryPlannerConfiguration; import javax.annotation.Nonnull; +import java.util.Set; import java.util.stream.Stream; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.BindingMatcher.newLine; @@ -48,6 +49,15 @@ protected TypedMatcherWithExtractAndDownstream(@Nonnull final Class bindableC this.downstream = downstream; } + protected TypedMatcherWithExtractAndDownstream(@Nonnull final Class bindableClass, + @Nonnull final Extractor extractor, + @Nonnull final BindingMatcher downstream, + @Nonnull final Set> rootClasses) { + super(bindableClass, rootClasses); + this.extractor = extractor; + this.downstream = downstream; + } + @Nonnull public Extractor getExtractor() { return extractor; @@ -91,4 +101,12 @@ public static TypedMatcherWithExtractAndDownstream typedWith @Nonnull final BindingMatcher downstream) { return new TypedMatcherWithExtractAndDownstream<>(bindableClass, extractor, downstream); } + + @Nonnull + public static TypedMatcherWithExtractAndDownstream typedWithDownstream(@Nonnull final Class bindableClass, + @Nonnull final Extractor extractor, + @Nonnull final BindingMatcher downstream, + @Nonnull final Set> concreteRootClasses) { + return new TypedMatcherWithExtractAndDownstream<>(bindableClass, extractor, downstream, concreteRootClasses); + } } 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 44828fbe6d..d52c8e4c50 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 @@ -37,6 +37,7 @@ import com.apple.foundationdb.record.query.plan.cascades.values.VariadicFunctionValue; import com.apple.foundationdb.tuple.TupleOrdering; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import javax.annotation.Nonnull; import java.util.Arrays; @@ -57,11 +58,24 @@ private ValueMatchers() { // do not instantiate } + /** + * Returns a matcher that binds any {@link Value}. + */ @Nonnull public static BindingMatcher anyValue() { return typed(Value.class); } + /** + * Returns a matcher that binds any {@link Value} whose concrete class is one of the given {@code subclasses}. + * The resulting matcher advertises the {@code subclasses} via {@link BindingMatcher#getRootClasses()} so that the + * rule set indexes the enclosing rule under each concrete subclass individually. + */ + @Nonnull + public static BindingMatcher anyValueOfType(@Nonnull final ImmutableSet> subclasses) { + return new TypedMatcher<>(Value.class, subclasses); + } + @Nonnull public static BindingMatcher anyFieldValue() { return typed(FieldValue.class); diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/AbsorptionRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/AbsorptionRule.java index 959dddf0cc..9b677c196d 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/AbsorptionRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/AbsorptionRule.java @@ -34,7 +34,7 @@ import javax.annotation.Nonnull; import java.util.Collection; -import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.MultiMatcher.all; @@ -68,8 +68,8 @@ public AbsorptionRule(@Nonnull final Class

majorClass, @Nonnull @Override - public Optional> getRootOperator() { - return Optional.empty(); + public Set> getRootOperators() { + return Set.of(); } @Override diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/AnnulmentAndRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/AnnulmentAndRule.java index 809b6d9ea7..db5805d51a 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/AnnulmentAndRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/AnnulmentAndRule.java @@ -28,7 +28,6 @@ import com.apple.foundationdb.record.query.plan.cascades.predicates.QueryPredicate; import javax.annotation.Nonnull; -import java.util.Optional; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.MultiMatcher.all; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.QueryPredicateMatchers.anyPredicate; @@ -50,12 +49,6 @@ public AnnulmentAndRule() { super(rootMatcher); } - @Nonnull - @Override - public Optional> getRootOperator() { - return Optional.of(AndPredicate.class); - } - @Override public void onMatch(@Nonnull final QueryPredicateSimplificationRuleCall call) { final var bindings = call.getBindings(); diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/AnnulmentOrRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/AnnulmentOrRule.java index 19f09b406b..6485248c62 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/AnnulmentOrRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/AnnulmentOrRule.java @@ -28,7 +28,6 @@ import com.apple.foundationdb.record.query.plan.cascades.predicates.QueryPredicate; import javax.annotation.Nonnull; -import java.util.Optional; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.MultiMatcher.all; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.QueryPredicateMatchers.anyPredicate; @@ -51,12 +50,6 @@ public AnnulmentOrRule() { super(rootMatcher); } - @Nonnull - @Override - public Optional> getRootOperator() { - return Optional.of(OrPredicate.class); - } - @Override public void onMatch(@Nonnull final QueryPredicateSimplificationRuleCall call) { final var bindings = call.getBindings(); diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/DeMorgansTheoremRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/DeMorgansTheoremRule.java index b31af01ea8..7388a6e549 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/DeMorgansTheoremRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/DeMorgansTheoremRule.java @@ -33,7 +33,6 @@ import javax.annotation.Nonnull; import java.util.Collection; -import java.util.Optional; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.MultiMatcher.all; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.QueryPredicateMatchers.anyPredicate; @@ -65,12 +64,6 @@ public DeMorgansTheoremRule(@Nonnull final Class

majorClass, this.termMatcher = termMatcher; } - @Nonnull - @Override - public Optional> getRootOperator() { - return Optional.of(NotPredicate.class); - } - @Override public void onMatch(@Nonnull final QueryPredicateSimplificationRuleCall call) { final var bindings = call.getBindings(); diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/IdentityAndRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/IdentityAndRule.java index a29418fe3f..a51edddc39 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/IdentityAndRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/IdentityAndRule.java @@ -28,7 +28,6 @@ import com.google.common.collect.ImmutableList; import javax.annotation.Nonnull; -import java.util.Optional; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.MultiMatcher.all; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.QueryPredicateMatchers.anyPredicate; @@ -50,12 +49,6 @@ public IdentityAndRule() { super(rootMatcher); } - @Nonnull - @Override - public Optional> getRootOperator() { - return Optional.of(AndPredicate.class); - } - @Override public void onMatch(@Nonnull final QueryPredicateSimplificationRuleCall call) { final var bindings = call.getBindings(); diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/IdentityOrRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/IdentityOrRule.java index 210263c058..8c478473f8 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/IdentityOrRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/IdentityOrRule.java @@ -28,7 +28,6 @@ import com.google.common.collect.ImmutableList; import javax.annotation.Nonnull; -import java.util.Optional; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.MultiMatcher.all; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.QueryPredicateMatchers.anyPredicate; @@ -50,12 +49,6 @@ public IdentityOrRule() { super(rootMatcher); } - @Nonnull - @Override - public Optional> getRootOperator() { - return Optional.of(OrPredicate.class); - } - @Override public void onMatch(@Nonnull final QueryPredicateSimplificationRuleCall call) { final var bindings = call.getBindings(); diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/NormalFormRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/NormalFormRule.java index 46bb1c1d14..9795d8abb0 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/NormalFormRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/NormalFormRule.java @@ -26,7 +26,7 @@ import com.apple.foundationdb.record.query.plan.planning.BooleanPredicateNormalizer; import javax.annotation.Nonnull; -import java.util.Optional; +import java.util.Set; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.QueryPredicateMatchers.anyPredicate; @@ -48,9 +48,8 @@ public NormalFormRule(@Nonnull final BooleanPredicateNormalizer normalizer) { @Nonnull @Override - public Optional> getRootOperator() { - // always-rule - return Optional.empty(); + public Set> getRootOperators() { + return Set.of(); } @Override diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/NotOverComparisonRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/NotOverComparisonRule.java index 2fcc993631..0d19a2e3da 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/NotOverComparisonRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/NotOverComparisonRule.java @@ -29,7 +29,6 @@ import com.apple.foundationdb.record.query.plan.cascades.values.Value; import javax.annotation.Nonnull; -import java.util.Optional; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.QueryPredicateMatchers.anyComparison; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.QueryPredicateMatchers.notPredicate; @@ -56,12 +55,6 @@ public NotOverComparisonRule() { super(rootMatcher); } - @Nonnull - @Override - public Optional> getRootOperator() { - return Optional.of(NotPredicate.class); - } - @Override public void onMatch(@Nonnull final QueryPredicateSimplificationRuleCall call) { final var bindings = call.getBindings(); diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/ValuePredicateSimplificationRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/ValuePredicateSimplificationRule.java index 9c4e152a79..5560ba2d33 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/ValuePredicateSimplificationRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/simplification/ValuePredicateSimplificationRule.java @@ -52,8 +52,8 @@ public ValuePredicateSimplificationRule() { @Nonnull @Override - public Optional> getRootOperator() { - return Optional.empty(); + public Set> getRootOperators() { + return Set.of(); } @Override diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/rules/FinalizeExpressionsRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/rules/FinalizeExpressionsRule.java index 0d5f2ad2e4..d785de9c4f 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/rules/FinalizeExpressionsRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/rules/FinalizeExpressionsRule.java @@ -33,7 +33,7 @@ import com.google.common.collect.Streams; import javax.annotation.Nonnull; -import java.util.Optional; +import java.util.Set; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.AnyMatcher.any; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.ExpressionsPartitionMatchers.anyExpressionPartition; @@ -74,9 +74,8 @@ public FinalizeExpressionsRule() { @Nonnull @Override - public Optional> getRootOperator() { - // this is an all-rule - return Optional.empty(); + public Set> getRootOperators() { + return Set.of(); } @Override diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/rules/MatchIntermediateRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/rules/MatchIntermediateRule.java index e8ecb5160c..04420d1b73 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/rules/MatchIntermediateRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/rules/MatchIntermediateRule.java @@ -51,7 +51,6 @@ import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -160,8 +159,8 @@ public MatchIntermediateRule() { @Nonnull @Override - public Optional> getRootOperator() { - return Optional.empty(); + public Set> getRootOperators() { + return Set.of(); } @Override diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/rules/MatchLeafRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/rules/MatchLeafRule.java index 0b3d83183b..c9ae43a733 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/rules/MatchLeafRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/rules/MatchLeafRule.java @@ -40,7 +40,6 @@ import com.apple.foundationdb.record.query.plan.cascades.matching.structure.RelationalExpressionMatchers; import javax.annotation.Nonnull; -import java.util.Optional; import java.util.Set; /** @@ -59,17 +58,13 @@ public MatchLeafRule() { super(root); } - /** - * Note: Transformation rules for expressions are partitioned by the class of the root matcher. This does not work - * here as this rule is non-specific which means that it matches sub classes of {@link RelationalExpression} and not - * just {@link RelationalExpression} itself. In order for this rule to fall into the set of rules that is always - * utilized we return {@code Optional.empty()} here. - * @return {@code Optional.empty()} - */ @Nonnull @Override - public Optional> getRootOperator() { - return Optional.empty(); + public Set> getRootOperators() { + // Note: Transformation rules for expressions are partitioned by the class of the root matcher. This does not + // work here as this rule is non-specific, which means that it matches subclasses of `RelationalExpression` and + // not just `RelationalExpression` itself. So we need to make this an always-rule. + return Set.of(); } @Override diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/AbstractRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/AbstractRule.java index 75f8968d45..20112df28e 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/AbstractRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/AbstractRule.java @@ -21,7 +21,6 @@ package com.apple.foundationdb.record.query.plan.cascades.values.simplification; import com.apple.foundationdb.annotation.API; -import com.apple.foundationdb.record.query.plan.cascades.PlanningRuleSet; import com.apple.foundationdb.record.query.plan.cascades.Reference; import com.apple.foundationdb.record.query.plan.cascades.PlanContext; import com.apple.foundationdb.record.query.plan.cascades.PlannerRule; @@ -29,7 +28,7 @@ import com.apple.foundationdb.record.query.plan.cascades.matching.structure.BindingMatcher; import javax.annotation.Nonnull; -import java.util.Optional; +import java.util.Set; /** * The rule matching occurs in two stages: first, the planner examines the {@link #matcher} of the rule, @@ -69,15 +68,13 @@ public AbstractRule(@Nonnull BindingMatcher matcher) { } /** - * Returns the class of the operator at the root of the binding expression, if this rule uses a non-trivial binding. - * Used primarily for indexing rules for more efficient rule search. - * @return the class of the root of this rule's binding, or Optional.empty() if the rule matches anything - * @see PlanningRuleSet + * {@inheritDoc} + *

By default, the set is derived from {@code getMatcher().getRootClasses()}. */ @Nonnull @Override - public Optional> getRootOperator() { - return Optional.of(matcher.getRootClass()); + public Set> getRootOperators() { + return matcher.getRootClasses(); } @Nonnull diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/AbstractRuleSet.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/AbstractRuleSet.java index b9ab4e3dea..1e7a6ecca7 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/AbstractRuleSet.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/AbstractRuleSet.java @@ -39,7 +39,6 @@ import javax.annotation.Nonnull; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.function.Predicate; @@ -69,18 +68,21 @@ public class AbstractRuleSet { @SpotBugsSuppressWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE") protected AbstractRuleSet(@Nonnull final Set> rules, @Nonnull final SetMultimap, ? extends PlannerRule> dependencies) { + // Derive the `ruleIndex` and the list of `alwaysRules` from the root operators advertised by the rules. this.ruleIndex = MultimapBuilder.hashKeys().arrayListValues().build(); this.alwaysRules = new ArrayList<>(); - this.dependsOn = MultimapBuilder.hashKeys().hashSetValues().build(); for (final var rule : rules) { - Optional> root = rule.getRootOperator(); - if (root.isPresent()) { - ruleIndex.put(root.get(), rule); - } else { + final Set> roots = rule.getRootOperators(); + if (roots.isEmpty()) { alwaysRules.add(rule); + } else { + for (final Class root : roots) { + ruleIndex.put(root, rule); + } } } + this.dependsOn = MultimapBuilder.hashKeys().hashSetValues().build(); this.dependsOn.putAll(dependencies); this.rulesCache = CacheBuilder.newBuilder() diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/CollapseNullStrictValueOverNullValueRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/CollapseNullStrictValueOverNullValueRule.java index da343a050a..f0378b403e 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/CollapseNullStrictValueOverNullValueRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/CollapseNullStrictValueOverNullValueRule.java @@ -34,9 +34,9 @@ import com.google.common.collect.Streams; import javax.annotation.Nonnull; -import java.util.Optional; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.TypedMatcherWithPredicate.typedMatcherWithPredicate; +import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.ValueMatchers.anyValueOfType; /** * A rule that collapses a {@link Value} into a {@link NullValue} if it is strictly null-propagating and has at least @@ -60,19 +60,15 @@ public class CollapseNullStrictValueOverNullValueRule extends ValueSimplificatio SubscriptValue.class); @Nonnull - private static final BindingMatcher rootMatcher = typedMatcherWithPredicate(Value.class, - v -> VALUE_CLASSES.contains(v.getClass()) && hasNullValueChild(v)); + private static final BindingMatcher rootMatcher = + anyValueOfType(VALUE_CLASSES) + .where(typedMatcherWithPredicate(Value.class, + CollapseNullStrictValueOverNullValueRule::hasNullValueChild)); public CollapseNullStrictValueOverNullValueRule() { super(rootMatcher); } - @Nonnull - @Override - public Optional> getRootOperator() { - return Optional.empty(); - } - @Override public void onMatch(@Nonnull final ValueSimplificationRuleCall call) { final Value value = call.getBindings().get(rootMatcher); diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/CompensateToOrderedBytesValueRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/CompensateToOrderedBytesValueRule.java index df547df6da..533014b167 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/CompensateToOrderedBytesValueRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/CompensateToOrderedBytesValueRule.java @@ -30,7 +30,6 @@ import javax.annotation.Nonnull; import java.util.Map; -import java.util.Optional; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.ValueMatchers.anyValue; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.ValueMatchers.toOrderedBytesValue; @@ -50,12 +49,6 @@ public CompensateToOrderedBytesValueRule() { super(rootMatcher); } - @Nonnull - @Override - public Optional> getRootOperator() { - return Optional.of(ToOrderedBytesValue.class); - } - @Override public void onMatch(@Nonnull final ValueComputationRuleCall> call) { final var bindings = call.getBindings(); diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/DefaultOrderingPartRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/DefaultOrderingPartRule.java index 303028ec8d..a2dd95f261 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/DefaultOrderingPartRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/DefaultOrderingPartRule.java @@ -29,7 +29,7 @@ import javax.annotation.Nonnull; import java.util.Objects; -import java.util.Optional; +import java.util.Set; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.ValueMatchers.anyValue; @@ -57,8 +57,8 @@ public DefaultOrderingPartRule(@Nonnull final O sortOrder) { @Nonnull @Override - public Optional> getRootOperator() { - return Optional.empty(); + public Set> getRootOperators() { + return Set.of(); } @Override diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/ExpandRecordRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/ExpandRecordRule.java index 252b60dd4a..75f8a23aa8 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/ExpandRecordRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/ExpandRecordRule.java @@ -33,7 +33,7 @@ import javax.annotation.Nonnull; import java.util.List; import java.util.Objects; -import java.util.Optional; +import java.util.Set; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.MultiMatcher.all; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.NotMatcher.not; @@ -72,8 +72,8 @@ public ExpandRecordRule() { @Nonnull @Override - public Optional> getRootOperator() { - return Optional.empty(); + public Set> getRootOperators() { + return Set.of(); } @Override diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/MatchConstantValueRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/MatchConstantValueRule.java index 2d36f657d1..65270358e1 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/MatchConstantValueRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/MatchConstantValueRule.java @@ -35,7 +35,7 @@ import javax.annotation.Nonnull; import java.util.Objects; -import java.util.Optional; +import java.util.Set; /** * A rule that matches any {@link Value} (with the argument values) that is a constant expression. @@ -53,8 +53,8 @@ public MatchConstantValueRule() { @Nonnull @Override - public Optional> getRootOperator() { - return Optional.empty(); // this is an always-rule + public Set> getRootOperators() { + return Set.of(); } @Override diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/MatchFieldValueOverFieldValueRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/MatchFieldValueOverFieldValueRule.java index 66f971a78a..cd6d580467 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/MatchFieldValueOverFieldValueRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/MatchFieldValueOverFieldValueRule.java @@ -29,7 +29,6 @@ import javax.annotation.Nonnull; import java.util.Map; -import java.util.Optional; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.ValueMatchers.anyFieldValue; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.ValueMatchers.fieldValue; @@ -50,12 +49,6 @@ public MatchFieldValueOverFieldValueRule() { super(rootMatcher); } - @Nonnull - @Override - public Optional> getRootOperator() { - return Optional.of(FieldValue.class); - } - @Override public void onMatch(@Nonnull final ValueComputationRuleCall> call) { final var bindings = call.getBindings(); diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/MatchSimpleFieldValueRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/MatchSimpleFieldValueRule.java index 20aac4547c..218fb13a5c 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/MatchSimpleFieldValueRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/MatchSimpleFieldValueRule.java @@ -32,7 +32,6 @@ import javax.annotation.Nonnull; import java.util.Map; import java.util.Objects; -import java.util.Optional; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.ValueMatchers.fieldValue; @@ -50,12 +49,6 @@ public MatchSimpleFieldValueRule() { super(rootMatcher); } - @Nonnull - @Override - public Optional> getRootOperator() { - return Optional.of(FieldValue.class); - } - @Override public void onMatch(@Nonnull final ValueComputationRuleCall> call) { final var bindings = call.getBindings(); diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/MatchValueRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/MatchValueRule.java index 80f5d77c3e..93b09c4c8b 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/MatchValueRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/MatchValueRule.java @@ -33,7 +33,7 @@ import javax.annotation.Nonnull; import java.util.Objects; -import java.util.Optional; +import java.util.Set; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.ValueMatchers.anyValue; @@ -52,8 +52,8 @@ public MatchValueRule() { @Nonnull @Override - public Optional> getRootOperator() { - return Optional.empty(); + public Set> getRootOperators() { + return Set.of(); } @Override diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/ValueComputationRule.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/ValueComputationRule.java index f029aa0da5..54e099b2e2 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/ValueComputationRule.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/simplification/ValueComputationRule.java @@ -29,7 +29,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.List; -import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; /** @@ -62,8 +62,8 @@ static ValueComputationRule fromSimplificationR return new ValueComputationRule<>(simplificationRule.getMatcher()) { @Nonnull @Override - public Optional> getRootOperator() { - return simplificationRule.getRootOperator(); + public Set> getRootOperators() { + return simplificationRule.getRootOperators(); } @Override