Skip to content

Commit 964600c

Browse files
authored
resolves #3344: multi phase planning (#3328)
This PR introduces multiphase planning in the `CascadesPlanner`. A planner phase transitions between planner stages: ``` stages: INITIAL ---------------> CANONICAL ---------------> PLANNED phases: REWRITING PLANNING ``` The current planner's work is now realized as a planner phase called `PLANNING` that transitions from planner stage `CANONICAL` to planner stage `PLANNED`. `Reference`s maintain the planner stages of the phase that created them and can be modified by calling `Reference.advancePlannerStage()` which is currently done during `ExploreGroup`. `CascadesPlanner.Task`s use a planner phase to resolve the rule set and the cost model that belong to the phase. This PR also introduces the `REWRITING` phase which is to be filled out in subsequent PRs. Currently the ruleset of that phase only contains `FinalizeExpressionsRule` which is required to make progress and to allow us to transition to the next planner stage and in turn next planner stage. When any phase is initiated in the planner, we will explore the expression DAG in much the same way as before using the corresponding planner rule set whose rules then yield new expressions. A rule is either an `ExplorationRule` or an `ImplementationRule`. When an `ImplementationRule` yields an expression it is considered a final expression and is treated exactly the same way as previously`RecordQueryPlan`s. That means we optimize that final expressions' inputs using the planner phase's cost model. For `REWRITING` we use `RewritingCostModel` which consistently picks the DAG with smallest semantic hash. In that way we construct a DAG bottom up consisting of the cheapest expression DAG according to `RewritingCostModel`. The first task to be pushed and the last task in the task stack will eventually initiate `PlannerPhase.PLANNING` which then kicks off another round of regular planning. There are other changes in this PR as well that are necessary in the same unit of work: - `References` distinguish between _exploratory_ and _final_ members. Exploration rules yield and memoize exploratory expressions while implementation rules yield and memoize final expressions. - Final expression members are treated in the same way as previously `RecordQueryPlan`s. An exploratory expression cannot be a `RecordQueryPlan` - `Memoizer` has been refactored to be more safe and hopefully easier to be used. There were a few problems in the code not calling the right methods for memoization that were fixed. - All phases explore and implement `RelationalExpression` except the last `PLANNING` phase which produces `RecordQueryPlan`s. There is a non-trivial amount of code that makes that somewhat less visible and to some degree polymorphic. - most rules now extend `ExplorationCascadesRule` or `ImplementationCascadesRule` which also determines which yields and memoization API are available to the rule. Did I mention that I wanted to make this more safe? - During `OptimizeGroup.execute()` when we prune the group we previously would just pick a `RelationalExpression` as the sole survivor if there were no plans. That logic is changed to only affect final expressions, i.e. exploratory expressions are kept in the group even after pruning. If there are no final expressions in the reference before pruning, there will not be a final expression in the reference after pruning. - improved Javadocs
1 parent b668b35 commit 964600c

File tree

300 files changed

+7045
-5504
lines changed

Some content is hidden

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

300 files changed

+7045
-5504
lines changed

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/metadata/expressions/FieldKeyExpression.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ public Quantifier.ForEach explodeField(@Nonnull Quantifier.ForEach baseQuantifie
221221
.build();
222222
switch (fanType) {
223223
case FanOut:
224-
return Quantifier.forEach(Reference.of(
224+
return Quantifier.forEach(Reference.initialOf(
225225
ExplodeExpression.explodeField(baseQuantifier, fieldNames)));
226226
default:
227227
throw new RecordCoreException("unrecognized fan type");

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/FDBDatabaseFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ public long getInitialDelayMillis() {
340340
*/
341341
public void setInitialDelayMillis(long initialDelayMillis) {
342342
if (initialDelayMillis < 0) {
343-
throw new RecordCoreException("Cannot set initial delay milleseconds to less than zero");
343+
throw new RecordCoreException("Cannot set initial delay milliseconds to less than zero");
344344
} else if (initialDelayMillis > maxDelayMillis) {
345345
throw new RecordCoreException("Cannot set initial delay to greater than maximum delay");
346346
}

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/expressions/OneOfThemWithComparison.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,14 @@ public GraphExpansion expand(@Nonnull final Quantifier.ForEach baseQuantifier,
107107
.addAll(fieldNamePrefix)
108108
.add(getFieldName())
109109
.build();
110-
final Quantifier.ForEach childBase = Quantifier.forEach(Reference.of(ExplodeExpression.explodeField(baseQuantifier, fieldNames)));
110+
final Quantifier.ForEach childBase = Quantifier.forEach(Reference.initialOf(ExplodeExpression.explodeField(baseQuantifier, fieldNames)));
111111
final SelectExpression selectExpression =
112112
GraphExpansion.builder()
113113
.addQuantifier(childBase)
114114
.addPredicate(childBase.getFlowedObjectValue().withComparison(comparison))
115115
.build()
116116
.buildSimpleSelectOverQuantifier(childBase);
117-
final Quantifier.Existential childQuantifier = Quantifier.existential(Reference.of(selectExpression));
117+
final Quantifier.Existential childQuantifier = Quantifier.existential(Reference.initialOf(selectExpression));
118118

119119
// create a query component that creates a path to this prefix and then applies this to it
120120
// this is needed for reapplication of the component if the sub query cannot be matched or only matched with

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/expressions/OneOfThemWithComponent.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,13 @@ public GraphExpansion expand(@Nonnull final Quantifier.ForEach baseQuantifier,
108108
.addAll(fieldNamePrefix)
109109
.add(getFieldName())
110110
.build();
111-
final Quantifier.ForEach childBase = Quantifier.forEach(Reference.of(ExplodeExpression.explodeField(baseQuantifier, fieldNames)));
111+
final Quantifier.ForEach childBase = Quantifier.forEach(Reference.initialOf(ExplodeExpression.explodeField(baseQuantifier, fieldNames)));
112112
final GraphExpansion graphExpansion = getChild().expand(childBase, outerQuantifierSupplier, Collections.emptyList());
113113
final SelectExpression selectExpression =
114114
GraphExpansion.ofOthers(GraphExpansion.builder().addQuantifier(childBase).build(), graphExpansion)
115115
.buildSimpleSelectOverQuantifier(childBase);
116116

117-
Quantifier.Existential childQuantifier = Quantifier.existential(Reference.of(selectExpression));
117+
Quantifier.Existential childQuantifier = Quantifier.existential(Reference.initialOf(selectExpression));
118118

119119
// create a query component that creates a path to this prefix and then applies this to it
120120
// this is needed for reapplication of the component if the sub query cannot be matched or only matched with

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/expressions/QueryRecordFunctionWithComparison.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ public GraphExpansion expand(@Nonnull final Quantifier.ForEach baseQuantifier,
149149
.addResultValue(rankValue)
150150
.build();
151151
final var rankSelectExpression = rankExpansion.buildSelect();
152-
final var rankQuantifier = Quantifier.forEach(Reference.of(rankSelectExpression));
152+
final var rankQuantifier = Quantifier.forEach(Reference.initialOf(rankSelectExpression));
153153

154154
//
155155
// Construct another select expression that applies the predicate on the rank value as well as adds a join
@@ -174,7 +174,7 @@ public GraphExpansion expand(@Nonnull final Quantifier.ForEach baseQuantifier,
174174
final var rankAndJoiningPredicateSelectExpression =
175175
GraphExpansion.ofOthers(rankComparisonExpansion, selfJoinPredicateExpansion).buildSelect();
176176
final var rankComparisonQuantifier =
177-
Quantifier.existential(Reference.of(rankAndJoiningPredicateSelectExpression));
177+
Quantifier.existential(Reference.initialOf(rankAndJoiningPredicateSelectExpression));
178178

179179
// create a query component that creates a path to this prefix and then applies this to it
180180
// this is needed for reapplication of the component if the sub query cannot be matched or only matched with
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* HeuristicPlanner.java
3+
*
4+
* This source file is part of the FoundationDB open source project
5+
*
6+
* Copyright 2015-2025 Apple Inc. and the FoundationDB project authors
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
21+
package com.apple.foundationdb.record.query.plan;
22+
23+
import java.lang.annotation.Documented;
24+
import java.lang.annotation.ElementType;
25+
import java.lang.annotation.Retention;
26+
import java.lang.annotation.RetentionPolicy;
27+
import java.lang.annotation.Target;
28+
29+
/**
30+
* An annotation used on public types, fields, and methods to indicate their usage by the heuristic query planner.
31+
*/
32+
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD})
33+
@Retention(RetentionPolicy.CLASS)
34+
@Documented
35+
public @interface HeuristicPlanner {
36+
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +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.PlannerRule;
2829
import com.apple.foundationdb.record.query.plan.cascades.PlanningRuleSet;
2930
import com.apple.foundationdb.record.query.plan.cascades.rules.PredicateToLogicalUnionRule;
3031
import com.apple.foundationdb.record.query.plan.plans.QueryPlan;
@@ -274,7 +275,7 @@ public RecordQueryPlannerSortConfiguration getSortConfiguration() {
274275
* @param rule in question
275276
* @return {@code true} is enabled, {@code false} otherwise
276277
*/
277-
public boolean isRuleEnabled(@Nonnull CascadesRule<?> rule) {
278+
public boolean isRuleEnabled(@Nonnull PlannerRule<?, ?> rule) {
278279
return !disabledTransformationRules.contains(rule.getClass().getSimpleName());
279280
}
280281

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

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -192,21 +192,18 @@ public Set<CorrelationIdentifier> getCorrelatedTo() {
192192
public ComposedBitmapIndexQueryPlan translateCorrelations(@Nonnull final TranslationMap translationMap,
193193
final boolean shouldSimplifyValues,
194194
@Nonnull final List<? extends Quantifier> translatedQuantifiers) {
195+
if (translationMap.definesOnlyIdentities()) {
196+
return this;
197+
}
198+
195199
final var translatedIndexPlansBuilder = ImmutableList.<RecordQueryCoveringIndexPlan>builder();
196-
boolean allAreSame = true;
197200
for (final var indexPlan : indexPlans) {
198201
final var translatedIndexPlan =
199202
indexPlan.translateCorrelations(translationMap, shouldSimplifyValues, translatedQuantifiers);
200-
if (translatedIndexPlan != indexPlan) {
201-
allAreSame = false;
202-
}
203203
translatedIndexPlansBuilder.add(translatedIndexPlan);
204204
}
205205

206-
if (!allAreSame) {
207-
return new ComposedBitmapIndexQueryPlan(translatedIndexPlansBuilder.build(), composer);
208-
}
209-
return this;
206+
return new ComposedBitmapIndexQueryPlan(translatedIndexPlansBuilder.build(), composer);
210207
}
211208

212209
@Nonnull

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,8 @@ public MatchCandidate expand(@Nonnull final Supplier<Quantifier.ForEach> baseQua
144144

145145
// 4. add sort on top, if necessary, this will be absorbed later on as an ordering property of the match candidate.
146146
final var maybeWithSort = placeHolderAliases.isEmpty()
147-
? Reference.of(selectHaving) // single group, sort by constant
148-
: Reference.of(new MatchableSortExpression(placeHolderAliases, isReverse, selectHaving));
147+
? Reference.initialOf(selectHaving) // single group, sort by constant
148+
: Reference.initialOf(new MatchableSortExpression(placeHolderAliases, isReverse, selectHaving));
149149

150150
final var traversal = Traversal.withRoot(maybeWithSort);
151151
return new AggregateIndexMatchCandidate(index,
@@ -213,7 +213,7 @@ private NonnullPair<Quantifier, List<Placeholder>> constructSelectWhereAndPlaceh
213213
builder.addAllQuantifiers(baseExpansion.getQuantifiers());
214214
allExpansionsBuilder.add(builder.build());
215215

216-
return NonnullPair.of(Quantifier.forEach(Reference.of(GraphExpansion.ofOthers(allExpansionsBuilder.build()).buildSelect())), baseExpansion.getPlaceholders());
216+
return NonnullPair.of(Quantifier.forEach(Reference.initialOf(GraphExpansion.ofOthers(allExpansionsBuilder.build()).buildSelect())), baseExpansion.getPlaceholders());
217217
}
218218

219219
@Nonnull
@@ -268,7 +268,7 @@ protected NonnullPair<Quantifier, List<Placeholder>> constructGroupBy(@Nonnull f
268268
groupingColsValue.getResultType().getFields().isEmpty() ? null : groupingColsValue,
269269
RecordConstructorValue.ofUnnamed(ImmutableList.of(aggregateValue)),
270270
GroupByExpression::nestedResults, selectWhereQun);
271-
final var groupByReference = Reference.of(groupByExpression);
271+
final var groupByReference = Reference.initialOf(groupByExpression);
272272
final var groupByQuantifier = Quantifier.forEach(groupByReference);
273273
return NonnullPair.of(groupByQuantifier, ImmutableList.of());
274274
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ protected NonnullPair<Quantifier, List<Placeholder>> constructGroupBy(@Nonnull f
118118

119119
final var pulledUpGroupingValues = ImmutableList.<Value>builder().addAll(explicitPulledUpGroupingValues).add(implicitGroupingValue).build();
120120
final var groupingColsValue = RecordConstructorValue.ofUnnamed(pulledUpGroupingValues);
121-
return NonnullPair.of(Quantifier.forEach(Reference.of(
121+
return NonnullPair.of(Quantifier.forEach(Reference.initialOf(
122122
new GroupByExpression(groupingColsValue, RecordConstructorValue.ofUnnamed(ImmutableList.of(aggregateValue)),
123123
GroupByExpression::nestedResults, selectWhereQun))), ImmutableList.of(placeHolder));
124124
}

0 commit comments

Comments
 (0)