Skip to content

Commit a5c4a13

Browse files
authored
Prepare the Cascades planner for multi-stage planning by enabling property computation on arbitrary expressions (#3321)
This PR prepares the Cascades Planner for multiple planner stages. As only the last such stage produces `RecordQueryPlan`, we need to remove every explicit reference to `RecordQueryPlan` from the innards of the planner. This PR specifically changes the `PropertiesMap` that uses `RecordQueryPlan` with a generic version that can either use `RelationalExpression` or `RecordQueryPlan` based on which planner stage we might be in. The second big change concerns the way properties are maintained and how state management works for visitors that are used by properties. Currently, there are properties which can be computed on plans only and others which can be computed on any kind of expression. There were also differences in how properties managed their state between different properties. The following changes were made: 1. properties are singletons (or multitons; enumerated singletons) 2. properties can create a visitor using `createVisitor()` 3. all properties use visitors that work on all kinds of expressions 4. properties that are not meaningful for all kinds of expressions (plans vs all expressions) can refuse to return a proper result for those (by throwing a `RecordCoreException`) 5. see `SimpleExpressionVisitor` and `ExpressionProperty.toExpressionVisitor(..)` for details on how to transform a plan visitor to a generic expression visitor and how to transform an old-style simplistic expression visitor. 6. previous classes called `...Property` are not properties anymore if they cannot be expressed using 1. and 2. Those _properties_ have been demoted to visitors
1 parent 212d279 commit a5c4a13

File tree

124 files changed

+2574
-1937
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

124 files changed

+2574
-1937
lines changed

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/combinatorics/TopologicalSort.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,10 @@ public static <T> Optional<List<T>> anyTopologicalOrderPermutation(@Nonnull fina
730730
* @return a permutation of the set that is topologically correctly ordered with respect to {@code dependsOnFn}
731731
*/
732732
public static <T> Optional<List<T>> anyTopologicalOrderPermutation(@Nonnull final PartiallyOrderedSet<T> partiallyOrderedSet) {
733+
if (partiallyOrderedSet.getDependencyMap().isEmpty()) {
734+
// if there are no dependencies, just return a list copy of the set
735+
return Optional.of(ImmutableList.copyOf(partiallyOrderedSet.getSet()));
736+
}
733737
return anyTopologicalOrderPermutation(partiallyOrderedSet.getSet(),
734738
() -> new KahnIterable<>(PartiallyOrderedSet.of(partiallyOrderedSet.getSet(), partiallyOrderedSet.getDependencyMap().inverse())));
735739
}

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/RecordQueryPlanComplexityException.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
import com.apple.foundationdb.annotation.API;
2424
import com.apple.foundationdb.record.RecordCoreException;
2525
import com.apple.foundationdb.record.logging.LogMessageKeys;
26+
import com.apple.foundationdb.record.query.plan.cascades.explain.ExplainPlanVisitor;
2627
import com.apple.foundationdb.record.query.plan.explain.ExplainLevel;
27-
import com.apple.foundationdb.record.query.plan.explain.ExplainPlanVisitor;
2828
import com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan;
2929

3030
import javax.annotation.Nonnull;

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/RecordQueryPlanner.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,7 @@
7474
import com.apple.foundationdb.record.query.expressions.RecordTypeKeyComparison;
7575
import com.apple.foundationdb.record.query.plan.cascades.ComparisonRange;
7676
import com.apple.foundationdb.record.query.plan.cascades.ComparisonRanges;
77-
import com.apple.foundationdb.record.query.plan.cascades.explain.PlannerGraphProperty;
78-
import com.apple.foundationdb.record.query.plan.cascades.properties.ComparisonsProperty;
79-
import com.apple.foundationdb.record.query.plan.cascades.properties.FieldWithComparisonCountProperty;
77+
import com.apple.foundationdb.record.query.plan.cascades.explain.PlannerGraphVisitor;
8078
import com.apple.foundationdb.record.query.plan.cascades.typing.Type;
8179
import com.apple.foundationdb.record.query.plan.planning.BooleanNormalizer;
8280
import com.apple.foundationdb.record.query.plan.planning.FilterSatisfiedMask;
@@ -131,6 +129,9 @@
131129
import java.util.function.Supplier;
132130
import java.util.stream.Collectors;
133131

132+
import static com.apple.foundationdb.record.query.plan.cascades.properties.ComparisonsProperty.comparisons;
133+
import static com.apple.foundationdb.record.query.plan.cascades.properties.FieldWithComparisonCountProperty.fieldWithComparisonCount;
134+
134135
/**
135136
* The query planner.
136137
*
@@ -333,7 +334,7 @@ public RecordQueryPlan plan(@Nonnull RecordQuery query, @Nonnull ParameterRelati
333334

334335
if (logger.isTraceEnabled()) {
335336
logger.trace(KeyValueLogMessage.of("explain of plan",
336-
"explain", PlannerGraphProperty.explain(plan)));
337+
"explain", PlannerGraphVisitor.explain(plan)));
337338
}
338339

339340
return plan;
@@ -543,7 +544,8 @@ private ScoredPlan planFilter(@Nonnull PlanContext planContext, @Nonnull QueryCo
543544
final ScoredPlan withInJoin = planFilterWithInJoin(planContext, inExtractor, needOrdering);
544545
if (withInAsOrUnion != null) {
545546
if (withInJoin == null || withInAsOrUnion.score > withInJoin.score ||
546-
FieldWithComparisonCountProperty.evaluate(withInAsOrUnion.getPlan()) < FieldWithComparisonCountProperty.evaluate(withInJoin.getPlan())) {
547+
fieldWithComparisonCount().evaluate(withInAsOrUnion.getPlan()) <
548+
fieldWithComparisonCount().evaluate(withInJoin.getPlan())) {
547549
return withInAsOrUnion;
548550
}
549551
}
@@ -943,7 +945,7 @@ private ScoredPlan computePlanProperties(@Nonnull PlanContext planContext, @Nonn
943945
}
944946

945947
protected Set<Comparisons.Comparison> computeSargedComparisons(@Nonnull final RecordQueryPlan plan) {
946-
return ComparisonsProperty.evaluate(plan);
948+
return comparisons().evaluate(plan);
947949
}
948950

949951
@Nullable

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/RecordQueryPlannerConfiguration.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import com.apple.foundationdb.record.RecordPlannerConfigurationProto;
2626
import com.apple.foundationdb.record.query.RecordQuery;
2727
import com.apple.foundationdb.record.query.plan.cascades.CascadesRule;
28-
import com.apple.foundationdb.record.query.plan.cascades.PlannerRuleSet;
28+
import com.apple.foundationdb.record.query.plan.cascades.PlanningRuleSet;
2929
import com.apple.foundationdb.record.query.plan.cascades.rules.PredicateToLogicalUnionRule;
3030
import com.apple.foundationdb.record.query.plan.plans.QueryPlan;
3131
import com.apple.foundationdb.record.query.plan.serialization.PlanSerialization;
@@ -605,11 +605,11 @@ public Builder setDisabledTransformationRules(@Nonnull final Set<Class<? extends
605605
* for any planning effort.
606606
* @param disabledTransformationRuleNames a set of rule names identifying (via simple class name)
607607
* transformation rules
608-
* @param plannerRuleSet a {@link PlannerRuleSet} that is used to resolve the rule name to a rule class
608+
* @param planningRuleSet a {@link PlanningRuleSet} that is used to resolve the rule name to a rule class
609609
* @return this builder
610610
*/
611611
@Nonnull
612-
public Builder setDisabledTransformationRuleNames(@Nonnull final Set<String> disabledTransformationRuleNames, @Nonnull PlannerRuleSet plannerRuleSet) {
612+
public Builder setDisabledTransformationRuleNames(@Nonnull final Set<String> disabledTransformationRuleNames, @Nonnull PlanningRuleSet planningRuleSet) {
613613
protoBuilder.clearDisabledTransformationRules()
614614
.addAllDisabledTransformationRules(disabledTransformationRuleNames);
615615
return this;

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/CascadesCostModel.java

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,10 @@
2626
import com.apple.foundationdb.record.query.plan.QueryPlanner.IndexScanPreference;
2727
import com.apple.foundationdb.record.query.plan.RecordQueryPlannerConfiguration;
2828
import com.apple.foundationdb.record.query.plan.cascades.expressions.RelationalExpression;
29-
import com.apple.foundationdb.record.query.plan.cascades.properties.CardinalitiesProperty;
3029
import com.apple.foundationdb.record.query.plan.cascades.properties.CardinalitiesProperty.Cardinalities;
3130
import com.apple.foundationdb.record.query.plan.cascades.properties.CardinalitiesProperty.Cardinality;
32-
import com.apple.foundationdb.record.query.plan.cascades.properties.ComparisonsProperty;
33-
import com.apple.foundationdb.record.query.plan.cascades.properties.FindExpressionProperty;
31+
import com.apple.foundationdb.record.query.plan.cascades.properties.ExpressionDepthProperty;
3432
import com.apple.foundationdb.record.query.plan.cascades.properties.NormalizedResidualPredicateProperty;
35-
import com.apple.foundationdb.record.query.plan.cascades.properties.RelationalExpressionDepthProperty;
36-
import com.apple.foundationdb.record.query.plan.cascades.properties.TypeFilterCountProperty;
37-
import com.apple.foundationdb.record.query.plan.cascades.properties.UnmatchedFieldsCountProperty;
3833
import com.apple.foundationdb.record.query.plan.plans.RecordQueryCoveringIndexPlan;
3934
import com.apple.foundationdb.record.query.plan.plans.RecordQueryFetchFromPartialRecordPlan;
4035
import com.apple.foundationdb.record.query.plan.plans.RecordQueryInJoinPlan;
@@ -55,11 +50,18 @@
5550
import java.util.function.Supplier;
5651

5752
import static com.apple.foundationdb.record.Bindings.Internal.CORRELATION;
53+
import static com.apple.foundationdb.record.query.plan.cascades.properties.CardinalitiesProperty.cardinalities;
54+
import static com.apple.foundationdb.record.query.plan.cascades.properties.ComparisonsProperty.comparisons;
55+
import static com.apple.foundationdb.record.query.plan.cascades.properties.ExpressionDepthProperty.fetchDepth;
56+
import static com.apple.foundationdb.record.query.plan.cascades.properties.ExpressionDepthProperty.typeFilterDepth;
57+
import static com.apple.foundationdb.record.query.plan.cascades.properties.TypeFilterCountProperty.typeFilterCount;
58+
import static com.apple.foundationdb.record.query.plan.cascades.properties.UnmatchedFieldsCountProperty.unmatchedFieldsCount;
5859

5960
/**
6061
* A comparator implementing the current heuristic cost model for the {@link CascadesPlanner}.
6162
*/
6263
@API(API.Status.EXPERIMENTAL)
64+
@SuppressWarnings("PMD.TooManyStaticImports")
6365
public class CascadesCostModel implements Comparator<RelationalExpression> {
6466
@Nonnull
6567
private static final Set<Class<? extends RelationalExpression>> interestingPlanClasses =
@@ -89,13 +91,13 @@ public int compare(@Nonnull RelationalExpression a, @Nonnull RelationalExpressio
8991
}
9092

9193
final Map<Class<? extends RelationalExpression>, Set<RelationalExpression>> planOpsMapA =
92-
FindExpressionProperty.evaluate(interestingPlanClasses, a);
94+
FindExpressionVisitor.evaluate(interestingPlanClasses, a);
9395

9496
final Map<Class<? extends RelationalExpression>, Set<RelationalExpression>> planOpsMapB =
95-
FindExpressionProperty.evaluate(interestingPlanClasses, b);
97+
FindExpressionVisitor.evaluate(interestingPlanClasses, b);
9698

97-
final Cardinalities cardinalitiesA = CardinalitiesProperty.evaluate(a);
98-
final Cardinalities cardinalitiesB = CardinalitiesProperty.evaluate(b);
99+
final Cardinalities cardinalitiesA = cardinalities().evaluate(a);
100+
final Cardinalities cardinalitiesB = cardinalities().evaluate(b);
99101

100102
//
101103
// Technically, both cardinalities at runtime must be the same. The question is if we can actually
@@ -155,8 +157,8 @@ public int compare(@Nonnull RelationalExpression a, @Nonnull RelationalExpressio
155157
return inPlanVsOtherOptional.getAsInt();
156158
}
157159

158-
final int typeFilterCountA = TypeFilterCountProperty.evaluate(a);
159-
final int typeFilterCountB = TypeFilterCountProperty.evaluate(b);
160+
final int typeFilterCountA = typeFilterCount().evaluate(a);
161+
final int typeFilterCountB = typeFilterCount().evaluate(b);
160162

161163
// special case
162164
// if one plan is a primary scan with a type filter and the other one is an index scan with the same number of
@@ -174,8 +176,8 @@ public int compare(@Nonnull RelationalExpression a, @Nonnull RelationalExpressio
174176
return typeFilterCountCompare;
175177
}
176178

177-
int typeFilterPositionCompare = Integer.compare(RelationalExpressionDepthProperty.TYPE_FILTER_DEPTH.evaluate(b),
178-
RelationalExpressionDepthProperty.TYPE_FILTER_DEPTH.evaluate(a)); // prefer the one with a deeper type filter
179+
// prefer the one with a deeper type filter
180+
int typeFilterPositionCompare = Integer.compare(typeFilterDepth().evaluate(b), typeFilterDepth().evaluate(a));
179181
if (typeFilterPositionCompare != 0) {
180182
return typeFilterPositionCompare;
181183
}
@@ -193,8 +195,8 @@ public int compare(@Nonnull RelationalExpression a, @Nonnull RelationalExpressio
193195
return numFetchesCompare;
194196
}
195197

196-
final int fetchDepthB = RelationalExpressionDepthProperty.FETCH_DEPTH.evaluate(b);
197-
final int fetchDepthA = RelationalExpressionDepthProperty.FETCH_DEPTH.evaluate(a);
198+
final int fetchDepthB = fetchDepth().evaluate(b);
199+
final int fetchDepthA = fetchDepth().evaluate(a);
198200
int fetchPositionCompare = Integer.compare(fetchDepthA, fetchDepthB);
199201
if (fetchPositionCompare != 0) {
200202
return fetchPositionCompare;
@@ -211,14 +213,14 @@ public int compare(@Nonnull RelationalExpression a, @Nonnull RelationalExpressio
211213
}
212214
}
213215

214-
int distinctFilterPositionCompare = Integer.compare(RelationalExpressionDepthProperty.DISTINCT_FILTER_DEPTH.evaluate(b),
215-
RelationalExpressionDepthProperty.DISTINCT_FILTER_DEPTH.evaluate(a));
216+
int distinctFilterPositionCompare = Integer.compare(ExpressionDepthProperty.distinctDepth().evaluate(b),
217+
ExpressionDepthProperty.distinctDepth().evaluate(a));
216218
if (distinctFilterPositionCompare != 0) {
217219
return distinctFilterPositionCompare;
218220
}
219221

220-
int ufpA = UnmatchedFieldsCountProperty.evaluate(a);
221-
int ufpB = UnmatchedFieldsCountProperty.evaluate(b);
222+
int ufpA = unmatchedFieldsCount().evaluate(a);
223+
int ufpB = unmatchedFieldsCount().evaluate(b);
222224
if (ufpA != ufpB) {
223225
return Integer.compare(ufpA, ufpB);
224226
}
@@ -266,9 +268,9 @@ public int compare(@Nonnull RelationalExpression a, @Nonnull RelationalExpressio
266268

267269
@Nonnull
268270
private Cardinality maxOfMaxCardinalitiesOfAllDataAccesses(@Nonnull final Map<Class<? extends RelationalExpression>, Set<RelationalExpression>> planOpsMap) {
269-
return FindExpressionProperty.slice(planOpsMap, RecordQueryScanPlan.class, RecordQueryPlanWithIndex.class, RecordQueryCoveringIndexPlan.class)
271+
return FindExpressionVisitor.slice(planOpsMap, RecordQueryScanPlan.class, RecordQueryPlanWithIndex.class, RecordQueryCoveringIndexPlan.class)
270272
.stream()
271-
.map(plan -> CardinalitiesProperty.evaluate(plan).getMaxCardinality())
273+
.map(plan -> cardinalities().evaluate(plan).getMaxCardinality())
272274
.reduce(Cardinality.ofCardinality(0),
273275
(l, r) -> {
274276
if (l.isUnknown()) {
@@ -314,8 +316,8 @@ private OptionalInt comparePrimaryScanToIndexScan(@Nonnull RelationalExpression
314316
isSingularIndexScanWithFetch(planOpsMapIndexScan)) {
315317

316318
if (typeFilterCountPrimaryScan > 0 && typeFilterCountIndexScan == 0) {
317-
final var primaryScanComparisons = ComparisonsProperty.evaluate(primaryScan);
318-
final var indexScanComparisons = ComparisonsProperty.evaluate(indexScan);
319+
final var primaryScanComparisons = comparisons().evaluate(primaryScan);
320+
final var indexScanComparisons = comparisons().evaluate(indexScan);
319321

320322
//
321323
// The primary scan side has a type filter in it, the index scan side does not. The primary side
@@ -373,7 +375,7 @@ private OptionalInt compareInOperator(@Nonnull final RelationalExpression leftEx
373375

374376
// If no scan comparison on the in union side uses a comparison to the in-values, then the in union
375377
// plan is not useful.
376-
final Set<Comparisons.Comparison> scanComparisonsSet = ComparisonsProperty.evaluate(leftExpression);
378+
final Set<Comparisons.Comparison> scanComparisonsSet = comparisons().evaluate(leftExpression);
377379

378380
final ImmutableSet<CorrelationIdentifier> scanComparisonsCorrelatedTo =
379381
scanComparisonsSet
@@ -429,6 +431,6 @@ private static OptionalInt flipFlop(final Supplier<OptionalInt> variantA,
429431

430432
@SafeVarargs
431433
private static int count(@Nonnull final Map<Class<? extends RelationalExpression>, Set<RelationalExpression>> expressionsMap, @Nonnull final Class<? extends RelationalExpression>... interestingClasses) {
432-
return FindExpressionProperty.slice(expressionsMap, interestingClasses).size();
434+
return FindExpressionVisitor.slice(expressionsMap, interestingClasses).size();
433435
}
434436
}

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/CascadesPlanner.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import com.apple.foundationdb.record.query.IndexQueryabilityFilter;
3131
import com.apple.foundationdb.record.query.ParameterRelationshipGraph;
3232
import com.apple.foundationdb.record.query.RecordQuery;
33-
import com.apple.foundationdb.record.query.plan.explain.ExplainPlanVisitor;
3433
import com.apple.foundationdb.record.query.plan.QueryPlanConstraint;
3534
import com.apple.foundationdb.record.query.plan.QueryPlanInfo;
3635
import com.apple.foundationdb.record.query.plan.QueryPlanInfoKeys;
@@ -42,11 +41,12 @@
4241
import com.apple.foundationdb.record.query.plan.cascades.debug.Debugger;
4342
import com.apple.foundationdb.record.query.plan.cascades.debug.Debugger.Location;
4443
import com.apple.foundationdb.record.query.plan.cascades.debug.RestartException;
45-
import com.apple.foundationdb.record.query.plan.cascades.explain.PlannerGraphProperty;
44+
import com.apple.foundationdb.record.query.plan.cascades.explain.PlannerGraphVisitor;
4645
import com.apple.foundationdb.record.query.plan.cascades.expressions.RelationalExpression;
4746
import com.apple.foundationdb.record.query.plan.cascades.matching.structure.BindingMatcher;
4847
import com.apple.foundationdb.record.query.plan.cascades.matching.structure.PlannerBindings;
4948
import com.apple.foundationdb.record.query.plan.cascades.matching.structure.ReferenceMatchers;
49+
import com.apple.foundationdb.record.query.plan.cascades.explain.ExplainPlanVisitor;
5050
import com.apple.foundationdb.record.query.plan.plans.QueryPlan;
5151
import com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan;
5252
import com.google.common.base.Suppliers;
@@ -83,7 +83,7 @@
8383
* Like many optimization frameworks, Cascades is driven by sets of {@link CascadesRule}s that can be defined for
8484
* {@link RelationalExpression}s, {@link PartialMatch}es and {@link MatchPartition}s, each of which describes a
8585
* particular transformation and encapsulates the logic for determining its applicability and applying it. The planner
86-
* searches through its {@link PlannerRuleSet} to find a matching rule and then executes that rule, creating zero or
86+
* searches through its {@link PlanningRuleSet} to find a matching rule and then executes that rule, creating zero or
8787
* more additional {@code PlannerExpression}s and/or zero or more additional {@link PartialMatch}es. A rule is defined by:
8888
* </p>
8989
* <ul>
@@ -206,7 +206,7 @@ public class CascadesPlanner implements QueryPlanner {
206206
@Nonnull
207207
private final RecordStoreState recordStoreState;
208208
@Nonnull
209-
private final PlannerRuleSet ruleSet;
209+
private final PlanningRuleSet ruleSet;
210210
@Nonnull
211211
private Reference currentRoot;
212212
@Nonnull
@@ -222,7 +222,7 @@ public CascadesPlanner(@Nonnull RecordMetaData metaData, @Nonnull RecordStoreSta
222222
this(metaData, recordStoreState, defaultPlannerRuleSet());
223223
}
224224

225-
public CascadesPlanner(@Nonnull RecordMetaData metaData, @Nonnull RecordStoreState recordStoreState, @Nonnull PlannerRuleSet ruleSet) {
225+
public CascadesPlanner(@Nonnull RecordMetaData metaData, @Nonnull RecordStoreState recordStoreState, @Nonnull PlanningRuleSet ruleSet) {
226226
this.configuration = RecordQueryPlannerConfiguration.builder().build();
227227
this.metaData = metaData;
228228
this.recordStoreState = recordStoreState;
@@ -375,7 +375,7 @@ private RecordQueryPlan resultOrFail() {
375375
if (singleRoot instanceof RecordQueryPlan) {
376376
if (logger.isDebugEnabled()) {
377377
logger.debug(KeyValueLogMessage.of("GML explain of plan",
378-
"explain", PlannerGraphProperty.explain(singleRoot)));
378+
"explain", PlannerGraphVisitor.explain(singleRoot)));
379379
logger.debug(KeyValueLogMessage.of("string explain of plan",
380380
"explain", ExplainPlanVisitor.toStringForDebugging((RecordQueryPlan)singleRoot)));
381381
}
@@ -656,7 +656,7 @@ public RelationalExpression getExpression() {
656656
}
657657

658658
@Nonnull
659-
protected PlannerRuleSet getRules() {
659+
protected PlanningRuleSet getRules() {
660660
return ruleSet;
661661
}
662662
}
@@ -1125,10 +1125,10 @@ public String toString() {
11251125

11261126
/**
11271127
* Returns the default set of transformation rules.
1128-
* @return a {@link PlannerRuleSet} using the default set of transformation rules
1128+
* @return a {@link PlanningRuleSet} using the default set of transformation rules
11291129
*/
11301130
@Nonnull
1131-
public static PlannerRuleSet defaultPlannerRuleSet() {
1132-
return PlannerRuleSet.DEFAULT;
1131+
public static PlanningRuleSet defaultPlannerRuleSet() {
1132+
return PlanningRuleSet.DEFAULT;
11331133
}
11341134
}

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/CascadesRule.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public CascadesRule(@Nonnull BindingMatcher<T> matcher, Collection<PlannerConstr
8282
* Returns the class of the operator at the root of the binding expression, if this rule uses a non-trivial binding.
8383
* Used primarily for indexing rules for more efficient rule search.
8484
* @return the class of the root of this rule's binding, or <code>Optional.empty()</code> if the rule matches anything
85-
* @see PlannerRuleSet
85+
* @see PlanningRuleSet
8686
*/
8787
@Nonnull
8888
@Override

0 commit comments

Comments
 (0)