Skip to content

Commit f8c69a7

Browse files
committed
build somewhat stable
1 parent a7b022d commit f8c69a7

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;
@@ -60,12 +59,14 @@
6059
import com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan;
6160
import com.apple.foundationdb.record.query.plan.plans.RecordQueryUnorderedPrimaryKeyDistinctPlan;
6261
import com.apple.foundationdb.record.util.pair.NonnullPair;
62+
import com.apple.foundationdb.record.util.pair.Pair;
6363
import com.google.common.base.Suppliers;
6464
import com.google.common.base.Verify;
6565
import com.google.common.collect.ImmutableList;
6666
import com.google.common.collect.ImmutableMap;
6767
import com.google.common.collect.ImmutableSet;
6868
import com.google.common.collect.ImmutableSetMultimap;
69+
import com.google.common.collect.Iterables;
6970
import com.google.common.collect.Lists;
7071
import com.google.common.collect.Maps;
7172
import org.slf4j.Logger;
@@ -77,6 +78,8 @@
7778
import java.util.BitSet;
7879
import java.util.Collection;
7980
import java.util.Comparator;
81+
import java.util.HashMap;
82+
import java.util.LinkedHashMap;
8083
import java.util.LinkedHashSet;
8184
import java.util.List;
8285
import java.util.Map;
@@ -85,6 +88,8 @@
8588
import java.util.Set;
8689
import java.util.function.Function;
8790
import java.util.function.Supplier;
91+
import java.util.stream.Collectors;
92+
import java.util.stream.Stream;
8893

8994
import static com.apple.foundationdb.record.query.plan.cascades.properties.CardinalitiesProperty.cardinalities;
9095

@@ -128,6 +133,97 @@ protected BindingMatcher<R> getExpressionMatcher() {
128133
return expressionMatcher;
129134
}
130135

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

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

0 commit comments

Comments
 (0)