Skip to content

Commit 99f2c43

Browse files
committed
build somewhat stable
1 parent 349dec0 commit 99f2c43

File tree

7 files changed

+236
-326
lines changed

7 files changed

+236
-326
lines changed

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

Lines changed: 101 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import com.apple.foundationdb.annotation.API;
2424
import com.apple.foundationdb.record.RecordCoreException;
2525
import com.apple.foundationdb.record.query.combinatorics.ChooseK;
26-
import com.apple.foundationdb.record.query.plan.cascades.AggregateIndexMatchCandidate;
2726
import com.apple.foundationdb.record.query.plan.cascades.CascadesPlanner;
2827
import com.apple.foundationdb.record.query.plan.cascades.CascadesRule;
2928
import com.apple.foundationdb.record.query.plan.cascades.CascadesRuleCall;
@@ -43,12 +42,12 @@
4342
import com.apple.foundationdb.record.query.plan.cascades.PlanContext;
4443
import com.apple.foundationdb.record.query.plan.cascades.PrimaryScanMatchCandidate;
4544
import com.apple.foundationdb.record.query.plan.cascades.Quantifier;
45+
import com.apple.foundationdb.record.query.plan.cascades.Quantifiers;
4646
import com.apple.foundationdb.record.query.plan.cascades.Reference;
4747
import com.apple.foundationdb.record.query.plan.cascades.ReferencedFieldsConstraint;
4848
import com.apple.foundationdb.record.query.plan.cascades.RequestedOrdering;
4949
import com.apple.foundationdb.record.query.plan.cascades.RequestedOrderingConstraint;
5050
import com.apple.foundationdb.record.query.plan.cascades.ValueIndexScanMatchCandidate;
51-
import com.apple.foundationdb.record.query.plan.cascades.WithPrimaryKeyMatchCandidate;
5251
import com.apple.foundationdb.record.query.plan.cascades.debug.Debugger;
5352
import com.apple.foundationdb.record.query.plan.cascades.expressions.LogicalDistinctExpression;
5453
import com.apple.foundationdb.record.query.plan.cascades.expressions.LogicalIntersectionExpression;
@@ -62,12 +61,14 @@
6261
import com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan;
6362
import com.apple.foundationdb.record.query.plan.plans.RecordQueryUnorderedPrimaryKeyDistinctPlan;
6463
import com.apple.foundationdb.record.util.pair.NonnullPair;
64+
import com.apple.foundationdb.record.util.pair.Pair;
6565
import com.google.common.base.Suppliers;
6666
import com.google.common.base.Verify;
6767
import com.google.common.collect.ImmutableList;
6868
import com.google.common.collect.ImmutableMap;
6969
import com.google.common.collect.ImmutableSet;
7070
import com.google.common.collect.ImmutableSetMultimap;
71+
import com.google.common.collect.Iterables;
7172
import com.google.common.collect.Lists;
7273
import com.google.common.collect.Maps;
7374
import org.slf4j.Logger;
@@ -79,6 +80,8 @@
7980
import java.util.BitSet;
8081
import java.util.Collection;
8182
import java.util.Comparator;
83+
import java.util.HashMap;
84+
import java.util.LinkedHashMap;
8285
import java.util.LinkedHashSet;
8386
import java.util.List;
8487
import java.util.Map;
@@ -87,6 +90,8 @@
8790
import java.util.Set;
8891
import java.util.function.Function;
8992
import java.util.function.Supplier;
93+
import java.util.stream.Collectors;
94+
import java.util.stream.Stream;
9095

9196
/**
9297
* A rule that utilizes index matching information compiled by {@link CascadesPlanner} to create one or more
@@ -129,6 +134,97 @@ protected BindingMatcher<R> getExpressionMatcher() {
129134
return expressionMatcher;
130135
}
131136

137+
@Override
138+
public void onMatch(@Nonnull final CascadesRuleCall call) {
139+
final var bindings = call.getBindings();
140+
final var completeMatches = bindings.getAll(getCompleteMatchMatcher());
141+
if (completeMatches.isEmpty()) {
142+
return;
143+
}
144+
145+
final var expression = bindings.get(getExpressionMatcher());
146+
147+
//
148+
// return if there is no pre-determined interesting ordering
149+
//
150+
final var requestedOrderingsOptional = call.getPlannerConstraint(RequestedOrderingConstraint.REQUESTED_ORDERING);
151+
if (requestedOrderingsOptional.isEmpty()) {
152+
return;
153+
}
154+
155+
final var requestedOrderings = requestedOrderingsOptional.get();
156+
final var aliasToQuantifierMap = Quantifiers.aliasToQuantifierMap(expression.getQuantifiers());
157+
final var aliases = aliasToQuantifierMap.keySet();
158+
159+
// group all successful matches by their sets of compensated aliases
160+
final var matchPartitionByMatchAliasMap =
161+
completeMatches
162+
.stream()
163+
.flatMap(match -> {
164+
final var compensatedAliases = match.getCompensatedAliases();
165+
if (!compensatedAliases.containsAll(aliases)) {
166+
return Stream.empty();
167+
}
168+
final Set<CorrelationIdentifier> matchedForEachAliases =
169+
compensatedAliases.stream()
170+
.filter(matchedAlias ->
171+
Objects.requireNonNull(aliasToQuantifierMap.get(matchedAlias)) instanceof Quantifier.ForEach)
172+
.collect(ImmutableSet.toImmutableSet());
173+
if (matchedForEachAliases.size() == 1) {
174+
return Stream.of(NonnullPair.of(Iterables.getOnlyElement(matchedForEachAliases), match));
175+
}
176+
return Stream.empty();
177+
})
178+
.collect(Collectors.groupingBy(
179+
Pair::getLeft,
180+
LinkedHashMap::new,
181+
Collectors.mapping(Pair::getRight, ImmutableList.toImmutableList())));
182+
183+
// loop through all compensated alias sets and their associated match partitions
184+
for (final var matchPartitionByMatchAliasEntry : matchPartitionByMatchAliasMap.entrySet()) {
185+
final var matchPartitionForMatchedAlias =
186+
matchPartitionByMatchAliasEntry.getValue();
187+
188+
//
189+
// We do know that local predicates (which includes predicates only using the matchedAlias quantifier)
190+
// are definitely handled by the logic expressed by the partial matches of the current match partition.
191+
// Join predicates are different in a sense that there will be matches that handle those predicates and
192+
// there will be matches where these predicates will not be handled. We further need to sub-partition the
193+
// current match partition, by the predicates that are being handled by the matches.
194+
//
195+
// TODO this should just be exactly one key
196+
final var matchPartitionsForAliasesByPredicates =
197+
matchPartitionForMatchedAlias
198+
.stream()
199+
.collect(Collectors.groupingBy(match ->
200+
new LinkedIdentitySet<>(match.getRegularMatchInfo().getPredicateMap().keySet()),
201+
HashMap::new,
202+
ImmutableList.toImmutableList()));
203+
204+
//
205+
// Note that this works because there is only one for-each and potentially 0 - n existential quantifiers
206+
// that are covered by the match partition. Even though that logically forms a join, the existential
207+
// quantifiers do not mutate the result of the join, they only cause filtering, that is, the resulting
208+
// record is exactly what the for each quantifier produced filtered by the predicates expressed on the
209+
// existential quantifiers.
210+
//
211+
for (final var matchPartitionEntry : matchPartitionsForAliasesByPredicates.entrySet()) {
212+
final var matchPartition = matchPartitionEntry.getValue();
213+
214+
//
215+
// The current match partition covers all matches that match the aliases in matchedAliases
216+
// as well as all predicates in matchedPredicates. In other words we now have to compensate
217+
// for all the remaining quantifiers and all remaining predicates.
218+
//
219+
final var dataAccessExpressions =
220+
dataAccessForMatchPartition(call,
221+
requestedOrderings,
222+
matchPartition);
223+
call.yieldExpression(dataAccessExpressions);
224+
}
225+
}
226+
}
227+
132228
/**
133229
* Method that does the leg work to create the appropriate expression dag for data access using value indexes or
134230
* value index-like scans (primary scans).
@@ -419,44 +515,6 @@ protected Set<? extends RelationalExpression> dataAccessForMatchPartition(@Nonnu
419515
return intersectionInfoMapToExpressions(intersectionInfoMap);
420516
}
421517

422-
@Nonnull
423-
protected static Optional<List<Value>> commonRecordKeyValuesMaybe(@Nonnull Iterable<? extends PartialMatch> partialMatches) {
424-
List<Value> common = null;
425-
var first = true;
426-
for (final var partialMatch : partialMatches) {
427-
final var matchCandidate = partialMatch.getMatchCandidate();
428-
final var regularMatchInfo = partialMatch.getRegularMatchInfo();
429-
430-
final List<Value> key;
431-
if (matchCandidate instanceof WithPrimaryKeyMatchCandidate) {
432-
final var withPrimaryKeyMatchCandidate = (WithPrimaryKeyMatchCandidate)matchCandidate;
433-
final var keyMaybe = withPrimaryKeyMatchCandidate.getPrimaryKeyValuesMaybe();
434-
if (keyMaybe.isEmpty()) {
435-
return Optional.empty();
436-
}
437-
key = keyMaybe.get();
438-
} else if (matchCandidate instanceof AggregateIndexMatchCandidate) {
439-
final var aggregateIndexMatchCandidate = (AggregateIndexMatchCandidate)matchCandidate;
440-
final var rollUpToGroupingValues = regularMatchInfo.getRollUpToGroupingValues();
441-
if (rollUpToGroupingValues == null) {
442-
key = aggregateIndexMatchCandidate.getGroupingAndAggregateAccessors(Quantifier.current()).getLeft();
443-
} else {
444-
key = aggregateIndexMatchCandidate.getGroupingAndAggregateAccessors(rollUpToGroupingValues.size(),
445-
Quantifier.current()).getLeft();
446-
}
447-
} else {
448-
return Optional.empty();
449-
}
450-
if (first) {
451-
common = key;
452-
first = false;
453-
} else if (!common.equals(key)) {
454-
return Optional.empty();
455-
}
456-
}
457-
return Optional.ofNullable(common); // common can only be null if we didn't have any match candidates to start with
458-
}
459-
460518
@Nonnull
461519
private static BitSet[] newSquareBitMatrix(final int size) {
462520
BitSet[] matrix = new BitSet[size];
@@ -1033,14 +1091,14 @@ private static Ordering orderingFromOrderingParts(final @Nonnull List<MatchedOrd
10331091
* Private helper method to verify that a list of {@link Value}s can be used as a comparison key.
10341092
* ordering information coming from a match in form of a list of {@link Value}s.
10351093
* @param comparisonKeyValues a list of {@link Value}s
1036-
* @param commonPrimaryKeyValues common primary key
1094+
* @param commonRecordKeyValues common primary key
10371095
* @param equalityBoundKeyValues a set of equality-bound key parts
10381096
* @return a boolean that indicates if the list of values passed in can be used as comparison key
10391097
*/
10401098
protected static boolean isCompatibleComparisonKey(@Nonnull Collection<Value> comparisonKeyValues,
1041-
@Nonnull List<Value> commonPrimaryKeyValues,
1099+
@Nonnull List<Value> commonRecordKeyValues,
10421100
@Nonnull ImmutableSet<Value> equalityBoundKeyValues) {
1043-
return commonPrimaryKeyValues
1101+
return commonRecordKeyValues
10441102
.stream()
10451103
.filter(commonPrimaryKeyValue -> !equalityBoundKeyValues.contains(commonPrimaryKeyValue))
10461104
.allMatch(comparisonKeyValues::contains);

0 commit comments

Comments
 (0)