Search in sources :

Example 6 with PartialMatch

use of com.apple.foundationdb.record.query.plan.temp.PartialMatch in project fdb-record-layer by FoundationDB.

the class SelectDataAccessRule method inject.

@Nonnull
@Override
protected ExpressionRef<? extends RelationalExpression> inject(@Nonnull SelectExpression selectExpression, @Nonnull List<? extends PartialMatch> completeMatches, @Nonnull final ExpressionRef<? extends RelationalExpression> compensatedScanGraph) {
    final Set<Quantifier.ForEach> unmatchedQuantifiers = computeIntersectedUnmatchedForEachQuantifiers(selectExpression, completeMatches);
    if (unmatchedQuantifiers.isEmpty()) {
        return compensatedScanGraph;
    }
    // 
    // Create a new SelectExpression that contains all the unmatched for each quantifiers as well as the
    // compensated scan graph.
    // 
    final ImmutableList.Builder<Quantifier.ForEach> allQuantifiersBuilder = ImmutableList.builder();
    unmatchedQuantifiers.stream().map(quantifier -> Quantifier.forEachBuilder().from(quantifier).build(quantifier.getRangesOver())).forEach(allQuantifiersBuilder::add);
    final Quantifier.ForEach compensatedScanQuantifier = Quantifier.forEach(compensatedScanGraph);
    allQuantifiersBuilder.add(compensatedScanQuantifier);
    return GroupExpressionRef.of(new SelectExpression(compensatedScanQuantifier.getFlowedValues(), allQuantifiersBuilder.build(), ImmutableList.of()));
}
Also used : CascadesPlanner(com.apple.foundationdb.record.query.plan.temp.CascadesPlanner) MatchPartitionMatchers.ofExpressionAndMatches(com.apple.foundationdb.record.query.plan.temp.matchers.MatchPartitionMatchers.ofExpressionAndMatches) Quantifier(com.apple.foundationdb.record.query.plan.temp.Quantifier) MatchPartition(com.apple.foundationdb.record.query.plan.temp.MatchPartition) Set(java.util.Set) PartialMatchMatchers.completeMatch(com.apple.foundationdb.record.query.plan.temp.matchers.PartialMatchMatchers.completeMatch) SelectExpression(com.apple.foundationdb.record.query.plan.temp.expressions.SelectExpression) GroupExpressionRef(com.apple.foundationdb.record.query.plan.temp.GroupExpressionRef) RelationalExpressionMatchers.ofType(com.apple.foundationdb.record.query.plan.temp.matchers.RelationalExpressionMatchers.ofType) RelationalExpression(com.apple.foundationdb.record.query.plan.temp.RelationalExpression) PartialMatch(com.apple.foundationdb.record.query.plan.temp.PartialMatch) List(java.util.List) BindingMatcher(com.apple.foundationdb.record.query.plan.temp.matchers.BindingMatcher) ImmutableList(com.google.common.collect.ImmutableList) API(com.apple.foundationdb.annotation.API) ExpressionRef(com.apple.foundationdb.record.query.plan.temp.ExpressionRef) MultiMatcher.some(com.apple.foundationdb.record.query.plan.temp.matchers.MultiMatcher.some) Nonnull(javax.annotation.Nonnull) ImmutableList(com.google.common.collect.ImmutableList) Quantifier(com.apple.foundationdb.record.query.plan.temp.Quantifier) SelectExpression(com.apple.foundationdb.record.query.plan.temp.expressions.SelectExpression) Nonnull(javax.annotation.Nonnull)

Example 7 with PartialMatch

use of com.apple.foundationdb.record.query.plan.temp.PartialMatch in project fdb-record-layer by FoundationDB.

the class MatchPartitionMatchers method ofExpressionOptionalAndMatches.

public static <O extends Optional<RelationalExpression>, C extends Collection<? extends PartialMatch>> BindingMatcher<MatchPartition> ofExpressionOptionalAndMatches(@Nonnull final BindingMatcher<O> downstreamExpressionOptional, @Nonnull final BindingMatcher<C> downstreamMatches) {
    return typedWithDownstream(MatchPartition.class, Extractor.identity(), AllOfMatcher.matchingAllOf(MatchPartition.class, ImmutableList.of(typedWithDownstream(MatchPartition.class, Extractor.of(matchPartition -> {
        final Set<PartialMatch> partialMatches = matchPartition.getPartialMatches();
        final Iterator<PartialMatch> iterator = partialMatches.iterator();
        RelationalExpression lastExpression = null;
        while (iterator.hasNext()) {
            final PartialMatch partialMatch = iterator.next();
            if (lastExpression == null) {
                lastExpression = partialMatch.getQueryExpression();
            } else if (lastExpression != partialMatch.getQueryExpression()) {
                return Optional.empty();
            }
        }
        return Optional.ofNullable(lastExpression);
    }, name -> "expressionOptional(" + name + ")"), downstreamExpressionOptional), typedWithDownstream(MatchPartition.class, Extractor.of(MatchPartition::getPartialMatches, name -> "partialMatches(" + name + ")"), downstreamMatches))));
}
Also used : RelationalExpression(com.apple.foundationdb.record.query.plan.temp.RelationalExpression) PartialMatch(com.apple.foundationdb.record.query.plan.temp.PartialMatch) MatchPartition(com.apple.foundationdb.record.query.plan.temp.MatchPartition)

Example 8 with PartialMatch

use of com.apple.foundationdb.record.query.plan.temp.PartialMatch in project fdb-record-layer by FoundationDB.

the class AbstractDataAccessRule method computeBoundKeyPartMap.

@Nonnull
private static Map<QueryPredicate, BoundKeyPart> computeBoundKeyPartMap(final PartialMatch partialMatch) {
    final MatchInfo matchInfo = partialMatch.getMatchInfo();
    final Map<CorrelationIdentifier, ComparisonRange> boundParameterPrefixMap = partialMatch.getBoundParameterPrefixMap();
    return matchInfo.getBoundKeyParts().stream().filter(boundKeyPart -> {
        // make sure that matching actually bound the part and that it can be used in a scan
        return matchInfo.getParameterAliasForBoundKeyPart(boundKeyPart).map(boundParameterPrefixMap::containsKey).orElse(false);
    }).peek(// make sure we got a predicate mapping
    boundKeyPart -> Objects.requireNonNull(boundKeyPart.getQueryPredicate())).collect(Collectors.toMap(BoundKeyPart::getQueryPredicate, Function.identity(), (a, b) -> {
        if (matchInfo.getCandidatePredicateForBoundKeyPart(a) == matchInfo.getCandidatePredicateForBoundKeyPart(b) && a.getComparisonRangeType() == b.getComparisonRangeType()) {
            return a;
        }
        throw new RecordCoreException("merge conflict");
    }, Maps::newIdentityHashMap));
}
Also used : PlannerRuleCall(com.apple.foundationdb.record.query.plan.temp.PlannerRuleCall) OrderingAttribute(com.apple.foundationdb.record.query.plan.temp.OrderingAttribute) CascadesPlanner(com.apple.foundationdb.record.query.plan.temp.CascadesPlanner) LinkedIdentitySet(com.apple.foundationdb.record.query.plan.temp.LinkedIdentitySet) GroupExpressionRef(com.apple.foundationdb.record.query.plan.temp.GroupExpressionRef) PartialMatch(com.apple.foundationdb.record.query.plan.temp.PartialMatch) Pair(org.apache.commons.lang3.tuple.Pair) RecordCoreException(com.apple.foundationdb.record.RecordCoreException) ComparisonRange(com.apple.foundationdb.record.query.plan.temp.ComparisonRange) Map(java.util.Map) IndexScanExpression(com.apple.foundationdb.record.query.plan.temp.expressions.IndexScanExpression) MatchCandidate(com.apple.foundationdb.record.query.plan.temp.MatchCandidate) PrimaryScanExpression(com.apple.foundationdb.record.query.plan.temp.expressions.PrimaryScanExpression) RequestedOrdering(com.apple.foundationdb.record.query.plan.temp.RequestedOrdering) KeyExpression(com.apple.foundationdb.record.metadata.expressions.KeyExpression) ImmutableSet(com.google.common.collect.ImmutableSet) ImmutableMap(com.google.common.collect.ImmutableMap) Collection(java.util.Collection) MatchPartition(com.apple.foundationdb.record.query.plan.temp.MatchPartition) Set(java.util.Set) PlannerBindings(com.apple.foundationdb.record.query.plan.temp.matchers.PlannerBindings) ReferencedFieldsAttribute(com.apple.foundationdb.record.query.plan.temp.ReferencedFieldsAttribute) Collectors(java.util.stream.Collectors) Objects(java.util.Objects) BoundKeyPart(com.apple.foundationdb.record.query.plan.temp.BoundKeyPart) List(java.util.List) Stream(java.util.stream.Stream) CorrelationIdentifier(com.apple.foundationdb.record.query.plan.temp.CorrelationIdentifier) MatchInfo(com.apple.foundationdb.record.query.plan.temp.MatchInfo) Optional(java.util.Optional) API(com.apple.foundationdb.annotation.API) IntStream(java.util.stream.IntStream) Iterables(com.google.common.collect.Iterables) PlannerRule(com.apple.foundationdb.record.query.plan.temp.PlannerRule) Quantifier(com.apple.foundationdb.record.query.plan.temp.Quantifier) Ordering(com.apple.foundationdb.record.query.plan.temp.Ordering) Function(java.util.function.Function) Key(com.apple.foundationdb.record.metadata.Key) LinkedHashMap(java.util.LinkedHashMap) Lists(com.google.common.collect.Lists) ImmutableList(com.google.common.collect.ImmutableList) Compensation(com.apple.foundationdb.record.query.plan.temp.Compensation) StreamSupport(java.util.stream.StreamSupport) ExpressionRef(com.apple.foundationdb.record.query.plan.temp.ExpressionRef) Nonnull(javax.annotation.Nonnull) Nullable(javax.annotation.Nullable) ChooseK(com.apple.foundationdb.record.query.combinatorics.ChooseK) LogicalIntersectionExpression(com.apple.foundationdb.record.query.plan.temp.expressions.LogicalIntersectionExpression) Iterator(java.util.Iterator) LogicalDistinctExpression(com.apple.foundationdb.record.query.plan.temp.expressions.LogicalDistinctExpression) PartialOrder(com.apple.foundationdb.record.query.combinatorics.PartialOrder) QueryPredicate(com.apple.foundationdb.record.query.predicates.QueryPredicate) KeyPart(com.apple.foundationdb.record.query.plan.temp.KeyPart) Maps(com.google.common.collect.Maps) RelationalExpression(com.apple.foundationdb.record.query.plan.temp.RelationalExpression) BindingMatcher(com.apple.foundationdb.record.query.plan.temp.matchers.BindingMatcher) PrimaryScanMatchCandidate(com.apple.foundationdb.record.query.plan.temp.PrimaryScanMatchCandidate) Comparator(java.util.Comparator) ValueIndexScanMatchCandidate(com.apple.foundationdb.record.query.plan.temp.ValueIndexScanMatchCandidate) RecordCoreException(com.apple.foundationdb.record.RecordCoreException) MatchInfo(com.apple.foundationdb.record.query.plan.temp.MatchInfo) CorrelationIdentifier(com.apple.foundationdb.record.query.plan.temp.CorrelationIdentifier) ComparisonRange(com.apple.foundationdb.record.query.plan.temp.ComparisonRange) Nonnull(javax.annotation.Nonnull)

Example 9 with PartialMatch

use of com.apple.foundationdb.record.query.plan.temp.PartialMatch in project fdb-record-layer by FoundationDB.

the class AbstractDataAccessRule method onMatch.

/**
 * Method that does the leg work to create the appropriate expression dag for data access using value indexes or
 * value index-like scans (primary scans).
 *
 * Conceptually we do the following work:
 *
 * <ul>
 * <li> This method yields a scan plan for each matching primary candidate ({@link PrimaryScanMatchCandidate}).
 *      There is only ever going to be exactly one {@link PrimaryScanMatchCandidate} for a primary key. Due to the
 *      candidate being solely based upon a primary key, the match structure is somewhat limited. In essence, there
 *      is an implicit guarantee that we can always create a primary scan for a data source.
 * </li>
 * <li> This method yields an index scan plan for each matching value index candidate
 *      ({@link ValueIndexScanMatchCandidate}).
 * </li>
 * <li> This method yields the combinatorial expansion of intersections of distinct-ed index scan plans.
 * </li>
 * </ul>
 *
 * The work described above is semantically correct in a sense that it creates a search space that can be explored
 * and pruned in suitable ways that will eventually converge into an optimal data access plan.
 *
 * We can choose to create an index scan for every index that is available regardless what the coverage
 * of an index is. The coverage of an index is a measurement that tells us how well an index can answer what a
 * filter (or by extension a query) asks for. For instance, a high number of search arguments used in the index scan
 * can be associated with high coverage (as in the index scan covers more of the query) and vice versa.
 *
 * Similarly, we can choose to create the intersection of all possible combinations of suitable scans over indexes
 * (that we have matches for). Since we create a logical intersection of these access plans we can leave it up to
 * the respective implementation rules (e.g., {@link ImplementIntersectionRule}) to do the right thing and implement
 * the physical plan for the intersection if possible (e.g. ensuring compatibly ordered legs, etc.).
 *
 * In fact, the two before-mentioned approaches are completely valid with respect to correctness of the plan and
 * the guaranteed creation of the optimal plan. However, in reality using this approach, although valid and probably
 * the conceptually better and more orthogonal approach, will result in a ballooning of the search space very quickly.
 * While that may be acceptable for group-by engines and only few index access paths, in an OLTP world where there
 * are potentially dozens of indexes, memory footprint and the sheer number of tasks that would be created for
 * subsequent exploration and implementation of all these alternatives make the purist approach to planning these
 * indexes infeasible.
 *
 * Thus we would like to eliminate unnecessary exploration by avoiding variations we know can never be successful
 * either in creating a successful executable plan (e.g. logical expression may not ever be able to produce a
 * compatible ordering) or cannot ever create an optimal plan. In a nutshell, we try to utilize additional
 * information that is available in addition to the matching partition in order to make decisions about which
 * expression variation to create and which to avoid:
 *
 * <ul>
 * <li> For a matching primary scan candidate ({@link PrimaryScanMatchCandidate})
 *      we will not create a primary scan if the scan is incompatible with an interesting order that has been
 *      communicated downwards in the graph.
 * </li>
 * <li> For a matching index scan candidate ({@link ValueIndexScanMatchCandidate})
 *      we will not create an index scan if the scan is incompatible with an interesting order that has been
 *      communicated downwards in the graph.
 * </li>
 * <li> We will only create a scan if there is no other index scan with a greater coverage (think of coverage
 *      as the assumed amount of filtering or currently the number of bound predicates) for the search arguments
 *      which are bound by the query.
 *      For instance, an index scan {@code INDEX SCAN(i1, a = [5, 5], b = [10, 10])} is still planned along
 *      {@code INDEX SCAN(i2, x = ["hello", "hello"], y = ["world", "world"], z = [10, inf])} even though
 *      the latter utilizes three search arguments while the former one only uses two. However, an index scan
 *      {@code INDEX SCAN(i1, a = [5, 5], b = [10, 10])} is not created (and yielded) if there we also
 *      have a choice to plan {@code INDEX SCAN(i2, b = [10, 10], a = [5, 5], c = ["Guten", "Morgen"])} as that
 *      index {@code i2} has a higher coverage compared to {@code i1} <em>and</em> all bound arguments in the scan
 *      over {@code i2} are also bound in the scan over {@code i1}.
 * <li>
 *      We will only create intersections of scans if we can already establish that the logical intersection
 *      can be implemented by a {@link com.apple.foundationdb.record.query.plan.plans.RecordQueryIntersectionPlan}.
 *      That requires that the legs of the intersection are compatibly ordered <em>and</em> that that ordering follows
 *      a potentially required ordering.
 * </li>
 * </ul>
 *
 * @param call the call associated with this planner rule execution
 */
@Override
@SuppressWarnings("java:S135")
public void onMatch(@Nonnull PlannerRuleCall call) {
    final PlannerBindings bindings = call.getBindings();
    final List<? extends PartialMatch> completeMatches = bindings.getAll(getCompleteMatchMatcher());
    final R expression = bindings.get(getExpressionMatcher());
    // 
    if (completeMatches.isEmpty()) {
        return;
    }
    // 
    // return if there is no pre-determined interesting ordering
    // 
    final Optional<Set<RequestedOrdering>> requestedOrderingsOptional = call.getInterestingProperty(OrderingAttribute.ORDERING);
    if (requestedOrderingsOptional.isEmpty()) {
        return;
    }
    final Set<RequestedOrdering> requestedOrderings = requestedOrderingsOptional.get();
    // 
    // group matches by candidates
    // 
    final LinkedHashMap<MatchCandidate, ? extends ImmutableList<? extends PartialMatch>> completeMatchMap = completeMatches.stream().collect(Collectors.groupingBy(PartialMatch::getMatchCandidate, LinkedHashMap::new, ImmutableList.toImmutableList()));
    // find the best match for a candidate as there may be more than one due to partial matching
    final ImmutableSet<PartialMatch> maximumCoverageMatchPerCandidate = completeMatchMap.entrySet().stream().flatMap(entry -> {
        final List<? extends PartialMatch> completeMatchesForCandidate = entry.getValue();
        final Optional<? extends PartialMatch> bestMatchForCandidateOptional = completeMatchesForCandidate.stream().max(Comparator.comparing(PartialMatch::getNumBoundParameterPrefix));
        return bestMatchForCandidateOptional.map(Stream::of).orElse(Stream.empty());
    }).collect(ImmutableSet.toImmutableSet());
    final List<PartialMatch> bestMaximumCoverageMatches = maximumCoverageMatches(maximumCoverageMatchPerCandidate, requestedOrderings);
    if (bestMaximumCoverageMatches.isEmpty()) {
        return;
    }
    // create scans for all best matches
    final Map<PartialMatch, RelationalExpression> bestMatchToExpressionMap = createScansForMatches(bestMaximumCoverageMatches);
    final ExpressionRef<RelationalExpression> toBeInjectedReference = GroupExpressionRef.empty();
    // create single scan accesses
    for (final PartialMatch bestMatch : bestMaximumCoverageMatches) {
        final RelationalExpression dataAccessAndCompensationExpression = compensateSingleDataAccess(bestMatch, bestMatchToExpressionMap.get(bestMatch));
        toBeInjectedReference.insert(dataAccessAndCompensationExpression);
    }
    final Map<PartialMatch, RelationalExpression> bestMatchToDistinctExpressionMap = distinctMatchToScanMap(bestMatchToExpressionMap);
    @Nullable final KeyExpression commonPrimaryKey = call.getContext().getCommonPrimaryKey();
    if (commonPrimaryKey != null) {
        final var commonPrimaryKeyParts = commonPrimaryKey.normalizeKeyForPositions();
        final var boundPartitions = Lists.<List<PartialMatch>>newArrayList();
        // create intersections for all n choose k partitions from k = 2 .. n
        IntStream.range(2, bestMaximumCoverageMatches.size() + 1).mapToObj(k -> ChooseK.chooseK(bestMaximumCoverageMatches, k)).flatMap(iterable -> StreamSupport.stream(iterable.spliterator(), false)).forEach(boundPartitions::add);
        boundPartitions.stream().flatMap(partition -> createIntersectionAndCompensation(commonPrimaryKeyParts, bestMatchToDistinctExpressionMap, partition, requestedOrderings).stream()).forEach(toBeInjectedReference::insert);
    }
    call.yield(inject(expression, completeMatches, toBeInjectedReference));
}
Also used : PlannerRuleCall(com.apple.foundationdb.record.query.plan.temp.PlannerRuleCall) OrderingAttribute(com.apple.foundationdb.record.query.plan.temp.OrderingAttribute) CascadesPlanner(com.apple.foundationdb.record.query.plan.temp.CascadesPlanner) LinkedIdentitySet(com.apple.foundationdb.record.query.plan.temp.LinkedIdentitySet) GroupExpressionRef(com.apple.foundationdb.record.query.plan.temp.GroupExpressionRef) PartialMatch(com.apple.foundationdb.record.query.plan.temp.PartialMatch) Pair(org.apache.commons.lang3.tuple.Pair) RecordCoreException(com.apple.foundationdb.record.RecordCoreException) ComparisonRange(com.apple.foundationdb.record.query.plan.temp.ComparisonRange) Map(java.util.Map) IndexScanExpression(com.apple.foundationdb.record.query.plan.temp.expressions.IndexScanExpression) MatchCandidate(com.apple.foundationdb.record.query.plan.temp.MatchCandidate) PrimaryScanExpression(com.apple.foundationdb.record.query.plan.temp.expressions.PrimaryScanExpression) RequestedOrdering(com.apple.foundationdb.record.query.plan.temp.RequestedOrdering) KeyExpression(com.apple.foundationdb.record.metadata.expressions.KeyExpression) ImmutableSet(com.google.common.collect.ImmutableSet) ImmutableMap(com.google.common.collect.ImmutableMap) Collection(java.util.Collection) MatchPartition(com.apple.foundationdb.record.query.plan.temp.MatchPartition) Set(java.util.Set) PlannerBindings(com.apple.foundationdb.record.query.plan.temp.matchers.PlannerBindings) ReferencedFieldsAttribute(com.apple.foundationdb.record.query.plan.temp.ReferencedFieldsAttribute) Collectors(java.util.stream.Collectors) Objects(java.util.Objects) BoundKeyPart(com.apple.foundationdb.record.query.plan.temp.BoundKeyPart) List(java.util.List) Stream(java.util.stream.Stream) CorrelationIdentifier(com.apple.foundationdb.record.query.plan.temp.CorrelationIdentifier) MatchInfo(com.apple.foundationdb.record.query.plan.temp.MatchInfo) Optional(java.util.Optional) API(com.apple.foundationdb.annotation.API) IntStream(java.util.stream.IntStream) Iterables(com.google.common.collect.Iterables) PlannerRule(com.apple.foundationdb.record.query.plan.temp.PlannerRule) Quantifier(com.apple.foundationdb.record.query.plan.temp.Quantifier) Ordering(com.apple.foundationdb.record.query.plan.temp.Ordering) Function(java.util.function.Function) Key(com.apple.foundationdb.record.metadata.Key) LinkedHashMap(java.util.LinkedHashMap) Lists(com.google.common.collect.Lists) ImmutableList(com.google.common.collect.ImmutableList) Compensation(com.apple.foundationdb.record.query.plan.temp.Compensation) StreamSupport(java.util.stream.StreamSupport) ExpressionRef(com.apple.foundationdb.record.query.plan.temp.ExpressionRef) Nonnull(javax.annotation.Nonnull) Nullable(javax.annotation.Nullable) ChooseK(com.apple.foundationdb.record.query.combinatorics.ChooseK) LogicalIntersectionExpression(com.apple.foundationdb.record.query.plan.temp.expressions.LogicalIntersectionExpression) Iterator(java.util.Iterator) LogicalDistinctExpression(com.apple.foundationdb.record.query.plan.temp.expressions.LogicalDistinctExpression) PartialOrder(com.apple.foundationdb.record.query.combinatorics.PartialOrder) QueryPredicate(com.apple.foundationdb.record.query.predicates.QueryPredicate) KeyPart(com.apple.foundationdb.record.query.plan.temp.KeyPart) Maps(com.google.common.collect.Maps) RelationalExpression(com.apple.foundationdb.record.query.plan.temp.RelationalExpression) BindingMatcher(com.apple.foundationdb.record.query.plan.temp.matchers.BindingMatcher) PrimaryScanMatchCandidate(com.apple.foundationdb.record.query.plan.temp.PrimaryScanMatchCandidate) Comparator(java.util.Comparator) ValueIndexScanMatchCandidate(com.apple.foundationdb.record.query.plan.temp.ValueIndexScanMatchCandidate) RelationalExpression(com.apple.foundationdb.record.query.plan.temp.RelationalExpression) LinkedIdentitySet(com.apple.foundationdb.record.query.plan.temp.LinkedIdentitySet) ImmutableSet(com.google.common.collect.ImmutableSet) Set(java.util.Set) Optional(java.util.Optional) KeyExpression(com.apple.foundationdb.record.metadata.expressions.KeyExpression) RequestedOrdering(com.apple.foundationdb.record.query.plan.temp.RequestedOrdering) PartialMatch(com.apple.foundationdb.record.query.plan.temp.PartialMatch) PlannerBindings(com.apple.foundationdb.record.query.plan.temp.matchers.PlannerBindings) MatchCandidate(com.apple.foundationdb.record.query.plan.temp.MatchCandidate) PrimaryScanMatchCandidate(com.apple.foundationdb.record.query.plan.temp.PrimaryScanMatchCandidate) ValueIndexScanMatchCandidate(com.apple.foundationdb.record.query.plan.temp.ValueIndexScanMatchCandidate) List(java.util.List) ImmutableList(com.google.common.collect.ImmutableList) Stream(java.util.stream.Stream) IntStream(java.util.stream.IntStream) Nullable(javax.annotation.Nullable)

Example 10 with PartialMatch

use of com.apple.foundationdb.record.query.plan.temp.PartialMatch in project fdb-record-layer by FoundationDB.

the class AbstractDataAccessRule method createIntersectionAndCompensation.

/**
 * Private helper method to plan an intersection and subsequently compensate it using the partial match structures
 * kept for all participating data accesses.
 * Planning the data access and its compensation for a given match is a two-step approach as we compute
 * the compensation for intersections by intersecting the {@link Compensation} for the single data accesses first
 * before using the resulting {@link Compensation} to compute the compensating expression for the entire
 * intersection.
 * @param commonPrimaryKeyParts normalized common primary key
 * @param matchToExpressionMap a map from match to single data access expression
 * @param partition a partition (i.e. a list of {@link PartialMatch}es that the caller would like to compute
 *        and intersected data access for
 * @param requestedOrderings a set of ordering that have been requested by consuming expressions/plan operators
 * @return a optional containing a new {@link RelationalExpression} that represents the data access and its
 *         compensation, {@code Optional.empty()} if this method was unable to compute the intersection expression
 */
@Nonnull
private static List<RelationalExpression> createIntersectionAndCompensation(@Nonnull final List<KeyExpression> commonPrimaryKeyParts, @Nonnull final Map<PartialMatch, RelationalExpression> matchToExpressionMap, @Nonnull final List<PartialMatch> partition, @Nonnull final Set<RequestedOrdering> requestedOrderings) {
    final var expressionsBuilder = ImmutableList.<RelationalExpression>builder();
    final var orderingPartialOrder = intersectionOrdering(partition);
    final ImmutableSet<BoundKeyPart> equalityBoundKeyParts = partition.stream().map(partialMatch -> partialMatch.getMatchInfo().getBoundKeyParts()).flatMap(boundOrderingKeyParts -> boundOrderingKeyParts.stream().filter(boundOrderingKey -> boundOrderingKey.getComparisonRangeType() == ComparisonRange.Type.EQUALITY)).collect(ImmutableSet.toImmutableSet());
    for (final var requestedOrdering : requestedOrderings) {
        final var satisfyingOrderingPartsOptional = Ordering.satisfiesKeyPartsOrdering(orderingPartialOrder, requestedOrdering.getOrderingKeyParts(), BoundKeyPart::getKeyPart);
        final var comparisonKeyOptional = satisfyingOrderingPartsOptional.map(parts -> parts.stream().filter(part -> !equalityBoundKeyParts.contains(part)).collect(ImmutableList.toImmutableList())).flatMap(parts -> comparisonKey(commonPrimaryKeyParts, equalityBoundKeyParts, parts));
        if (comparisonKeyOptional.isEmpty()) {
            continue;
        }
        final KeyExpression comparisonKey = comparisonKeyOptional.get();
        final var compensation = partition.stream().map(partialMatch -> partialMatch.compensate(partialMatch.getBoundParameterPrefixMap())).reduce(Compensation.impossibleCompensation(), Compensation::intersect);
        final ImmutableList<RelationalExpression> scans = partition.stream().map(partialMatch -> Objects.requireNonNull(matchToExpressionMap.get(partialMatch))).collect(ImmutableList.toImmutableList());
        final var logicalIntersectionExpression = LogicalIntersectionExpression.from(scans, comparisonKey);
        final var compensatedIntersection = compensation.isNeeded() ? compensation.apply(GroupExpressionRef.of(logicalIntersectionExpression)) : logicalIntersectionExpression;
        expressionsBuilder.add(compensatedIntersection);
    }
    return expressionsBuilder.build();
}
Also used : PlannerRuleCall(com.apple.foundationdb.record.query.plan.temp.PlannerRuleCall) OrderingAttribute(com.apple.foundationdb.record.query.plan.temp.OrderingAttribute) CascadesPlanner(com.apple.foundationdb.record.query.plan.temp.CascadesPlanner) LinkedIdentitySet(com.apple.foundationdb.record.query.plan.temp.LinkedIdentitySet) GroupExpressionRef(com.apple.foundationdb.record.query.plan.temp.GroupExpressionRef) PartialMatch(com.apple.foundationdb.record.query.plan.temp.PartialMatch) Pair(org.apache.commons.lang3.tuple.Pair) RecordCoreException(com.apple.foundationdb.record.RecordCoreException) ComparisonRange(com.apple.foundationdb.record.query.plan.temp.ComparisonRange) Map(java.util.Map) IndexScanExpression(com.apple.foundationdb.record.query.plan.temp.expressions.IndexScanExpression) MatchCandidate(com.apple.foundationdb.record.query.plan.temp.MatchCandidate) PrimaryScanExpression(com.apple.foundationdb.record.query.plan.temp.expressions.PrimaryScanExpression) RequestedOrdering(com.apple.foundationdb.record.query.plan.temp.RequestedOrdering) KeyExpression(com.apple.foundationdb.record.metadata.expressions.KeyExpression) ImmutableSet(com.google.common.collect.ImmutableSet) ImmutableMap(com.google.common.collect.ImmutableMap) Collection(java.util.Collection) MatchPartition(com.apple.foundationdb.record.query.plan.temp.MatchPartition) Set(java.util.Set) PlannerBindings(com.apple.foundationdb.record.query.plan.temp.matchers.PlannerBindings) ReferencedFieldsAttribute(com.apple.foundationdb.record.query.plan.temp.ReferencedFieldsAttribute) Collectors(java.util.stream.Collectors) Objects(java.util.Objects) BoundKeyPart(com.apple.foundationdb.record.query.plan.temp.BoundKeyPart) List(java.util.List) Stream(java.util.stream.Stream) CorrelationIdentifier(com.apple.foundationdb.record.query.plan.temp.CorrelationIdentifier) MatchInfo(com.apple.foundationdb.record.query.plan.temp.MatchInfo) Optional(java.util.Optional) API(com.apple.foundationdb.annotation.API) IntStream(java.util.stream.IntStream) Iterables(com.google.common.collect.Iterables) PlannerRule(com.apple.foundationdb.record.query.plan.temp.PlannerRule) Quantifier(com.apple.foundationdb.record.query.plan.temp.Quantifier) Ordering(com.apple.foundationdb.record.query.plan.temp.Ordering) Function(java.util.function.Function) Key(com.apple.foundationdb.record.metadata.Key) LinkedHashMap(java.util.LinkedHashMap) Lists(com.google.common.collect.Lists) ImmutableList(com.google.common.collect.ImmutableList) Compensation(com.apple.foundationdb.record.query.plan.temp.Compensation) StreamSupport(java.util.stream.StreamSupport) ExpressionRef(com.apple.foundationdb.record.query.plan.temp.ExpressionRef) Nonnull(javax.annotation.Nonnull) Nullable(javax.annotation.Nullable) ChooseK(com.apple.foundationdb.record.query.combinatorics.ChooseK) LogicalIntersectionExpression(com.apple.foundationdb.record.query.plan.temp.expressions.LogicalIntersectionExpression) Iterator(java.util.Iterator) LogicalDistinctExpression(com.apple.foundationdb.record.query.plan.temp.expressions.LogicalDistinctExpression) PartialOrder(com.apple.foundationdb.record.query.combinatorics.PartialOrder) QueryPredicate(com.apple.foundationdb.record.query.predicates.QueryPredicate) KeyPart(com.apple.foundationdb.record.query.plan.temp.KeyPart) Maps(com.google.common.collect.Maps) RelationalExpression(com.apple.foundationdb.record.query.plan.temp.RelationalExpression) BindingMatcher(com.apple.foundationdb.record.query.plan.temp.matchers.BindingMatcher) PrimaryScanMatchCandidate(com.apple.foundationdb.record.query.plan.temp.PrimaryScanMatchCandidate) Comparator(java.util.Comparator) ValueIndexScanMatchCandidate(com.apple.foundationdb.record.query.plan.temp.ValueIndexScanMatchCandidate) RelationalExpression(com.apple.foundationdb.record.query.plan.temp.RelationalExpression) Compensation(com.apple.foundationdb.record.query.plan.temp.Compensation) KeyExpression(com.apple.foundationdb.record.metadata.expressions.KeyExpression) BoundKeyPart(com.apple.foundationdb.record.query.plan.temp.BoundKeyPart) Nonnull(javax.annotation.Nonnull)

Aggregations

PartialMatch (com.apple.foundationdb.record.query.plan.temp.PartialMatch)11 RelationalExpression (com.apple.foundationdb.record.query.plan.temp.RelationalExpression)11 ImmutableList (com.google.common.collect.ImmutableList)9 List (java.util.List)9 Map (java.util.Map)9 Set (java.util.Set)9 Nonnull (javax.annotation.Nonnull)9 API (com.apple.foundationdb.annotation.API)8 Quantifier (com.apple.foundationdb.record.query.plan.temp.Quantifier)8 ImmutableMap (com.google.common.collect.ImmutableMap)8 ImmutableSet (com.google.common.collect.ImmutableSet)8 Objects (java.util.Objects)8 Optional (java.util.Optional)8 ComparisonRange (com.apple.foundationdb.record.query.plan.temp.ComparisonRange)7 CorrelationIdentifier (com.apple.foundationdb.record.query.plan.temp.CorrelationIdentifier)7 ExpressionRef (com.apple.foundationdb.record.query.plan.temp.ExpressionRef)7 GroupExpressionRef (com.apple.foundationdb.record.query.plan.temp.GroupExpressionRef)7 MatchCandidate (com.apple.foundationdb.record.query.plan.temp.MatchCandidate)7 MatchInfo (com.apple.foundationdb.record.query.plan.temp.MatchInfo)7 QueryPredicate (com.apple.foundationdb.record.query.predicates.QueryPredicate)7