Search in sources :

Example 6 with PlannerRuleCall

use of com.apple.foundationdb.record.query.plan.temp.PlannerRuleCall 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 7 with PlannerRuleCall

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

the class PushSetOperationThroughFetchRule method onMatch.

@Override
@SuppressWarnings("java:S1905")
public void onMatch(@Nonnull PlannerRuleCall call) {
    final PlannerBindings bindings = call.getBindings();
    final RecordQuerySetPlan setOperationPlan = bindings.get(getMatcher());
    final List<? extends Quantifier.Physical> quantifiersOverFetches = bindings.getAll(quantifierOverFetchMatcher);
    // if set operation is dynamic all quantifiers must have fetches
    if (setOperationPlan.isDynamic()) {
        if (quantifiersOverFetches.size() < setOperationPlan.getQuantifiers().size()) {
            return;
        }
    } else {
        if (quantifiersOverFetches.size() <= 1) {
            // pulling up the fetch is meaningless in this case
            return;
        }
    }
    final List<? extends RecordQueryFetchFromPartialRecordPlan> fetchPlans = bindings.getAll(fetchPlanMatcher);
    final ImmutableList<TranslateValueFunction> dependentFunctions = fetchPlans.stream().map(RecordQueryFetchFromPartialRecordPlan::getPushValueFunction).collect(ImmutableList.toImmutableList());
    Verify.verify(quantifiersOverFetches.size() == fetchPlans.size());
    Verify.verify(fetchPlans.size() == dependentFunctions.size());
    final List<? extends Value> requiredValues = setOperationPlan.getRequiredValues(CorrelationIdentifier.uniqueID());
    final Set<CorrelationIdentifier> pushableAliases = setOperationPlan.tryPushValues(dependentFunctions, quantifiersOverFetches, requiredValues);
    // if set operation is dynamic all aliases must be pushable
    if (setOperationPlan.isDynamic()) {
        if (pushableAliases.size() < setOperationPlan.getQuantifiers().size()) {
            return;
        }
    } else {
        if (pushableAliases.size() <= 1) {
            // pulling up the fetch is meaningless in this case
            return;
        }
    }
    final ImmutableList.Builder<Quantifier.Physical> pushableQuantifiersBuilder = ImmutableList.builder();
    final ImmutableList.Builder<RecordQueryFetchFromPartialRecordPlan> pushableFetchPlansBuilder = ImmutableList.builder();
    final ImmutableList.Builder<TranslateValueFunction> pushableDependentFunctionsBuilder = ImmutableList.builder();
    for (int i = 0; i < quantifiersOverFetches.size(); i++) {
        final Quantifier.Physical quantifier = quantifiersOverFetches.get(i);
        if (pushableAliases.contains(quantifier.getAlias())) {
            pushableQuantifiersBuilder.add(quantifier);
            pushableFetchPlansBuilder.add(fetchPlans.get(i));
            pushableDependentFunctionsBuilder.add(dependentFunctions.get(i));
        }
    }
    final ImmutableList<Quantifier.Physical> pushableQuantifiers = pushableQuantifiersBuilder.build();
    final ImmutableList<RecordQueryFetchFromPartialRecordPlan> pushableFetchPlans = pushableFetchPlansBuilder.build();
    final ImmutableList<TranslateValueFunction> pushableDependentFunctions = pushableDependentFunctionsBuilder.build();
    final ImmutableList<Quantifier.Physical> nonPushableQuantifiers = setOperationPlan.getQuantifiers().stream().map(quantifier -> (Quantifier.Physical) quantifier).filter(quantifier -> !pushableAliases.contains(quantifier.getAlias())).collect(ImmutableList.toImmutableList());
    final List<? extends ExpressionRef<RecordQueryPlan>> newPushedInnerPlans = pushableFetchPlans.stream().map(RecordQueryFetchFromPartialRecordPlan::getChild).map(GroupExpressionRef::of).collect(ImmutableList.toImmutableList());
    Verify.verify(pushableQuantifiers.size() + nonPushableQuantifiers.size() == setOperationPlan.getQuantifiers().size());
    final TranslateValueFunction combinedTranslateValueFunction = setOperationPlan.pushValueFunction(pushableDependentFunctions);
    final RecordQuerySetPlan newSetOperationPlan = setOperationPlan.withChildrenReferences(newPushedInnerPlans);
    final RecordQueryFetchFromPartialRecordPlan newFetchPlan = new RecordQueryFetchFromPartialRecordPlan(newSetOperationPlan, combinedTranslateValueFunction);
    if (nonPushableQuantifiers.isEmpty()) {
        call.yield(GroupExpressionRef.of(newFetchPlan));
    } else {
        final List<ExpressionRef<? extends RecordQueryPlan>> newFetchPlanAndResidualInners = Streams.concat(Stream.of(GroupExpressionRef.of(newFetchPlan)), nonPushableQuantifiers.stream().map(Quantifier.Physical::getRangesOver).map(RecordQueryPlan::narrowReference)).collect(ImmutableList.toImmutableList());
        call.yield(GroupExpressionRef.of(setOperationPlan.withChildrenReferences(newFetchPlanAndResidualInners)));
    }
}
Also used : TranslateValueFunction(com.apple.foundationdb.record.query.plan.plans.TranslateValueFunction) PlannerRuleCall(com.apple.foundationdb.record.query.plan.temp.PlannerRuleCall) RecordQueryFetchFromPartialRecordPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryFetchFromPartialRecordPlan) PlannerRule(com.apple.foundationdb.record.query.plan.temp.PlannerRule) Quantifier(com.apple.foundationdb.record.query.plan.temp.Quantifier) RecordQueryPlanMatchers.anyPlan(com.apple.foundationdb.record.query.plan.temp.matchers.RecordQueryPlanMatchers.anyPlan) GroupExpressionRef(com.apple.foundationdb.record.query.plan.temp.GroupExpressionRef) RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) RecordQueryPlanMatchers.fetchFromPartialRecordPlan(com.apple.foundationdb.record.query.plan.temp.matchers.RecordQueryPlanMatchers.fetchFromPartialRecordPlan) ImmutableList(com.google.common.collect.ImmutableList) RelationalExpressionMatchers.ofTypeOwning(com.apple.foundationdb.record.query.plan.temp.matchers.RelationalExpressionMatchers.ofTypeOwning) 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) Verify(com.google.common.base.Verify) RecordQuerySetPlan(com.apple.foundationdb.record.query.plan.plans.RecordQuerySetPlan) QuantifierMatchers.physicalQuantifier(com.apple.foundationdb.record.query.plan.temp.matchers.QuantifierMatchers.physicalQuantifier) Set(java.util.Set) PlannerBindings(com.apple.foundationdb.record.query.plan.temp.matchers.PlannerBindings) Streams(com.google.common.collect.Streams) Value(com.apple.foundationdb.record.query.predicates.Value) List(java.util.List) BindingMatcher(com.apple.foundationdb.record.query.plan.temp.matchers.BindingMatcher) Stream(java.util.stream.Stream) CorrelationIdentifier(com.apple.foundationdb.record.query.plan.temp.CorrelationIdentifier) API(com.apple.foundationdb.annotation.API) ImmutableList(com.google.common.collect.ImmutableList) GroupExpressionRef(com.apple.foundationdb.record.query.plan.temp.GroupExpressionRef) ExpressionRef(com.apple.foundationdb.record.query.plan.temp.ExpressionRef) PlannerBindings(com.apple.foundationdb.record.query.plan.temp.matchers.PlannerBindings) RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) TranslateValueFunction(com.apple.foundationdb.record.query.plan.plans.TranslateValueFunction) RecordQueryFetchFromPartialRecordPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryFetchFromPartialRecordPlan) RecordQuerySetPlan(com.apple.foundationdb.record.query.plan.plans.RecordQuerySetPlan) CorrelationIdentifier(com.apple.foundationdb.record.query.plan.temp.CorrelationIdentifier) Quantifier(com.apple.foundationdb.record.query.plan.temp.Quantifier) QuantifierMatchers.physicalQuantifier(com.apple.foundationdb.record.query.plan.temp.matchers.QuantifierMatchers.physicalQuantifier)

Example 8 with PlannerRuleCall

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

the class PushTypeFilterBelowFilterRule method onMatch.

@Override
public void onMatch(@Nonnull PlannerRuleCall call) {
    final PlannerBindings bindings = call.getBindings();
    final ExpressionRef<? extends RelationalExpression> inner = bindings.get(innerMatcher);
    final Quantifier.Physical qun = bindings.get(qunMatcher);
    final List<? extends QueryPredicate> predicates = bindings.getAll(predMatcher);
    final Collection<String> recordTypes = bindings.get(root).getRecordTypes();
    final RecordQueryTypeFilterPlan newTypeFilterPlan = new RecordQueryTypeFilterPlan(Quantifier.physical(inner), recordTypes);
    final Quantifier.Physical newQun = Quantifier.physical(call.ref(newTypeFilterPlan));
    final List<QueryPredicate> rebasedPredicates = predicates.stream().map(queryPredicate -> queryPredicate.rebase(Quantifiers.translate(qun, newQun))).collect(ImmutableList.toImmutableList());
    call.yield(GroupExpressionRef.of(new RecordQueryPredicatesFilterPlan(newQun, rebasedPredicates)));
}
Also used : PlannerRuleCall(com.apple.foundationdb.record.query.plan.temp.PlannerRuleCall) RecordQueryFilterPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryFilterPlan) PlannerRule(com.apple.foundationdb.record.query.plan.temp.PlannerRule) Quantifier(com.apple.foundationdb.record.query.plan.temp.Quantifier) Quantifiers(com.apple.foundationdb.record.query.plan.temp.Quantifiers) GroupExpressionRef(com.apple.foundationdb.record.query.plan.temp.GroupExpressionRef) RecordQueryPlanMatchers.predicatesFilter(com.apple.foundationdb.record.query.plan.temp.matchers.RecordQueryPlanMatchers.predicatesFilter) ImmutableList(com.google.common.collect.ImmutableList) RecordQueryPlanMatchers.typeFilter(com.apple.foundationdb.record.query.plan.temp.matchers.RecordQueryPlanMatchers.typeFilter) ReferenceMatchers.anyRefOverOnlyPlans(com.apple.foundationdb.record.query.plan.temp.matchers.ReferenceMatchers.anyRefOverOnlyPlans) ExpressionRef(com.apple.foundationdb.record.query.plan.temp.ExpressionRef) Nonnull(javax.annotation.Nonnull) QuantifierMatchers.physicalQuantifier(com.apple.foundationdb.record.query.plan.temp.matchers.QuantifierMatchers.physicalQuantifier) QueryPredicateMatchers.anyPredicate(com.apple.foundationdb.record.query.plan.temp.matchers.QueryPredicateMatchers.anyPredicate) Collection(java.util.Collection) MultiMatcher.all(com.apple.foundationdb.record.query.plan.temp.matchers.MultiMatcher.all) PlannerBindings(com.apple.foundationdb.record.query.plan.temp.matchers.PlannerBindings) QueryPredicate(com.apple.foundationdb.record.query.predicates.QueryPredicate) RecordQueryPredicatesFilterPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPredicatesFilterPlan) QuantifierMatchers.physicalQuantifierOverRef(com.apple.foundationdb.record.query.plan.temp.matchers.QuantifierMatchers.physicalQuantifierOverRef) RelationalExpression(com.apple.foundationdb.record.query.plan.temp.RelationalExpression) List(java.util.List) BindingMatcher(com.apple.foundationdb.record.query.plan.temp.matchers.BindingMatcher) API(com.apple.foundationdb.annotation.API) RecordQueryTypeFilterPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryTypeFilterPlan) ListMatcher.exactly(com.apple.foundationdb.record.query.plan.temp.matchers.ListMatcher.exactly) QueryPredicate(com.apple.foundationdb.record.query.predicates.QueryPredicate) RecordQueryPredicatesFilterPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPredicatesFilterPlan) PlannerBindings(com.apple.foundationdb.record.query.plan.temp.matchers.PlannerBindings) Quantifier(com.apple.foundationdb.record.query.plan.temp.Quantifier) QuantifierMatchers.physicalQuantifier(com.apple.foundationdb.record.query.plan.temp.matchers.QuantifierMatchers.physicalQuantifier) RecordQueryTypeFilterPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryTypeFilterPlan)

Example 9 with PlannerRuleCall

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

the class NormalizePredicatesRule method onMatch.

@Override
public void onMatch(@Nonnull PlannerRuleCall call) {
    final PlannerBindings bindings = call.getBindings();
    final SelectExpression selectExpression = bindings.get(root);
    final Collection<? extends QueryPredicate> predicates = bindings.get(predicatesMatcher);
    final Collection<? extends Quantifier> quantifiers = bindings.get(innerQuantifiersMatcher);
    // create one big conjuncted predicate
    final QueryPredicate conjunctedPredicate = AndPredicate.and(predicates);
    final BooleanPredicateNormalizer cnfNormalizer = BooleanPredicateNormalizer.forConfiguration(BooleanPredicateNormalizer.Mode.CNF, call.getContext().getPlannerConfiguration());
    cnfNormalizer.normalize(conjunctedPredicate, false).ifPresent(cnfPredicate -> call.yield(call.ref(new SelectExpression(selectExpression.getResultValues(), quantifiers.stream().map(quantifier -> quantifier.toBuilder().build(quantifier.getRangesOver())).collect(ImmutableList.toImmutableList()), AndPredicate.conjuncts(cnfPredicate)))));
    final BooleanPredicateNormalizer dnfNormalizer = BooleanPredicateNormalizer.forConfiguration(BooleanPredicateNormalizer.Mode.DNF, call.getContext().getPlannerConfiguration());
    dnfNormalizer.normalize(conjunctedPredicate, false).ifPresent(dnfPredicate -> call.yield(call.ref(new SelectExpression(selectExpression.getResultValues(), quantifiers.stream().map(quantifier -> quantifier.toBuilder().build(quantifier.getRangesOver())).collect(ImmutableList.toImmutableList()), ImmutableList.of(dnfPredicate)))));
}
Also used : PlannerRuleCall(com.apple.foundationdb.record.query.plan.temp.PlannerRuleCall) RelationalExpressionMatchers(com.apple.foundationdb.record.query.plan.temp.matchers.RelationalExpressionMatchers) PlannerRule(com.apple.foundationdb.record.query.plan.temp.PlannerRule) QueryPredicateMatchers.anyPredicate(com.apple.foundationdb.record.query.plan.temp.matchers.QueryPredicateMatchers.anyPredicate) Collection(java.util.Collection) Quantifier(com.apple.foundationdb.record.query.plan.temp.Quantifier) MultiMatcher.all(com.apple.foundationdb.record.query.plan.temp.matchers.MultiMatcher.all) BooleanPredicateNormalizer(com.apple.foundationdb.record.query.plan.planning.BooleanPredicateNormalizer) CollectionMatcher(com.apple.foundationdb.record.query.plan.temp.matchers.CollectionMatcher) PlannerBindings(com.apple.foundationdb.record.query.plan.temp.matchers.PlannerBindings) QueryPredicate(com.apple.foundationdb.record.query.predicates.QueryPredicate) SelectExpression(com.apple.foundationdb.record.query.plan.temp.expressions.SelectExpression) AndPredicate(com.apple.foundationdb.record.query.predicates.AndPredicate) BindingMatcher(com.apple.foundationdb.record.query.plan.temp.matchers.BindingMatcher) ImmutableList(com.google.common.collect.ImmutableList) API(com.apple.foundationdb.annotation.API) QuantifierMatchers.anyQuantifier(com.apple.foundationdb.record.query.plan.temp.matchers.QuantifierMatchers.anyQuantifier) Nonnull(javax.annotation.Nonnull) QueryPredicate(com.apple.foundationdb.record.query.predicates.QueryPredicate) PlannerBindings(com.apple.foundationdb.record.query.plan.temp.matchers.PlannerBindings) BooleanPredicateNormalizer(com.apple.foundationdb.record.query.plan.planning.BooleanPredicateNormalizer) SelectExpression(com.apple.foundationdb.record.query.plan.temp.expressions.SelectExpression)

Example 10 with PlannerRuleCall

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

the class PushFilterThroughFetchRule method onMatch.

@Override
public void onMatch(@Nonnull PlannerRuleCall call) {
    final PlannerBindings bindings = call.getBindings();
    final RecordQueryPredicatesFilterPlan filterPlan = bindings.get(root);
    final RecordQueryFetchFromPartialRecordPlan fetchPlan = bindings.get(fetchPlanMatcher);
    final Quantifier.Physical quantifierOverFetch = bindings.get(quantifierOverFetchMatcher);
    final RecordQueryPlan innerPlan = bindings.get(innerPlanMatcher);
    final List<? extends QueryPredicate> queryPredicates = filterPlan.getPredicates();
    final ImmutableList.Builder<QueryPredicate> pushedPredicatesBuilder = ImmutableList.builder();
    final ImmutableList.Builder<QueryPredicate> residualPredicatesBuilder = ImmutableList.builder();
    final CorrelationIdentifier newInnerAlias = CorrelationIdentifier.uniqueID();
    for (final QueryPredicate queryPredicate : queryPredicates) {
        final Optional<QueryPredicate> pushedPredicateOptional = queryPredicate.replaceLeavesMaybe(leafPredicate -> pushLeafPredicate(fetchPlan, newInnerAlias, leafPredicate));
        if (pushedPredicateOptional.isPresent()) {
            pushedPredicatesBuilder.add(pushedPredicateOptional.get());
        } else {
            residualPredicatesBuilder.add(queryPredicate);
        }
    }
    final ImmutableList<QueryPredicate> pushedPredicates = pushedPredicatesBuilder.build();
    final ImmutableList<QueryPredicate> residualPredicates = residualPredicatesBuilder.build();
    Verify.verify(pushedPredicates.size() + residualPredicates.size() == queryPredicates.size());
    // case 1
    if (pushedPredicates.isEmpty()) {
        return;
    }
    // for case 2 and case 3 we can at least build a FILTER(inner, pushedPredicates) as that is
    // required both for case 2 nd 3
    final Quantifier.Physical newInnerQuantifier = Quantifier.physical(GroupExpressionRef.of(innerPlan), newInnerAlias);
    final RecordQueryPredicatesFilterPlan pushedFilterPlan = new RecordQueryPredicatesFilterPlan(newInnerQuantifier, pushedPredicates);
    final RecordQueryFetchFromPartialRecordPlan newFetchPlan = new RecordQueryFetchFromPartialRecordPlan(pushedFilterPlan, fetchPlan.getPushValueFunction());
    if (residualPredicates.isEmpty()) {
        // case 2
        call.yield(call.ref(newFetchPlan));
    } else {
        // case 3
        // create yet another physical quantifier on top of the fetch
        final Quantifier.Physical newQuantifierOverFetch = Quantifier.physical(GroupExpressionRef.of(newFetchPlan));
        final AliasMap translationMap = AliasMap.of(quantifierOverFetch.getAlias(), newQuantifierOverFetch.getAlias());
        // rebase all residual predicates to use that quantifier's alias
        final ImmutableList<QueryPredicate> rebasedResidualPredicates = residualPredicates.stream().map(residualPredicate -> residualPredicate.rebase(translationMap)).collect(ImmutableList.toImmutableList());
        call.yield(GroupExpressionRef.of(new RecordQueryPredicatesFilterPlan(newQuantifierOverFetch, rebasedResidualPredicates)));
    }
}
Also used : RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) TranslateValueFunction(com.apple.foundationdb.record.query.plan.plans.TranslateValueFunction) PlannerRuleCall(com.apple.foundationdb.record.query.plan.temp.PlannerRuleCall) RecordQueryFetchFromPartialRecordPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryFetchFromPartialRecordPlan) PlannerRule(com.apple.foundationdb.record.query.plan.temp.PlannerRule) Quantifier(com.apple.foundationdb.record.query.plan.temp.Quantifier) RecordQueryPlanMatchers.anyPlan(com.apple.foundationdb.record.query.plan.temp.matchers.RecordQueryPlanMatchers.anyPlan) PredicateWithValue(com.apple.foundationdb.record.query.predicates.PredicateWithValue) GroupExpressionRef(com.apple.foundationdb.record.query.plan.temp.GroupExpressionRef) RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) RecordQueryPlanMatchers.predicatesFilter(com.apple.foundationdb.record.query.plan.temp.matchers.RecordQueryPlanMatchers.predicatesFilter) ImmutableList(com.google.common.collect.ImmutableList) AliasMap(com.apple.foundationdb.record.query.plan.temp.AliasMap) Nonnull(javax.annotation.Nonnull) Nullable(javax.annotation.Nullable) Verify(com.google.common.base.Verify) QuantifierMatchers.physicalQuantifier(com.apple.foundationdb.record.query.plan.temp.matchers.QuantifierMatchers.physicalQuantifier) PlannerBindings(com.apple.foundationdb.record.query.plan.temp.matchers.PlannerBindings) QueryPredicate(com.apple.foundationdb.record.query.predicates.QueryPredicate) RecordQueryPredicatesFilterPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPredicatesFilterPlan) QueryComponentPredicate(com.apple.foundationdb.record.query.predicates.QueryComponentPredicate) Value(com.apple.foundationdb.record.query.predicates.Value) List(java.util.List) BindingMatcher(com.apple.foundationdb.record.query.plan.temp.matchers.BindingMatcher) CorrelationIdentifier(com.apple.foundationdb.record.query.plan.temp.CorrelationIdentifier) Optional(java.util.Optional) API(com.apple.foundationdb.annotation.API) RecordQueryPlanMatchers(com.apple.foundationdb.record.query.plan.temp.matchers.RecordQueryPlanMatchers) QueryPredicate(com.apple.foundationdb.record.query.predicates.QueryPredicate) ImmutableList(com.google.common.collect.ImmutableList) AliasMap(com.apple.foundationdb.record.query.plan.temp.AliasMap) RecordQueryFetchFromPartialRecordPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryFetchFromPartialRecordPlan) RecordQueryPredicatesFilterPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPredicatesFilterPlan) CorrelationIdentifier(com.apple.foundationdb.record.query.plan.temp.CorrelationIdentifier) PlannerBindings(com.apple.foundationdb.record.query.plan.temp.matchers.PlannerBindings) Quantifier(com.apple.foundationdb.record.query.plan.temp.Quantifier) QuantifierMatchers.physicalQuantifier(com.apple.foundationdb.record.query.plan.temp.matchers.QuantifierMatchers.physicalQuantifier)

Aggregations

PlannerRule (com.apple.foundationdb.record.query.plan.temp.PlannerRule)10 PlannerRuleCall (com.apple.foundationdb.record.query.plan.temp.PlannerRuleCall)10 Quantifier (com.apple.foundationdb.record.query.plan.temp.Quantifier)10 BindingMatcher (com.apple.foundationdb.record.query.plan.temp.matchers.BindingMatcher)10 ImmutableList (com.google.common.collect.ImmutableList)10 Nonnull (javax.annotation.Nonnull)10 API (com.apple.foundationdb.annotation.API)9 List (java.util.List)9 GroupExpressionRef (com.apple.foundationdb.record.query.plan.temp.GroupExpressionRef)7 PlannerBindings (com.apple.foundationdb.record.query.plan.temp.matchers.PlannerBindings)7 Collection (java.util.Collection)6 RecordQueryPlan (com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan)5 Quantifiers (com.apple.foundationdb.record.query.plan.temp.Quantifiers)5 QueryPredicate (com.apple.foundationdb.record.query.predicates.QueryPredicate)5 Iterables (com.google.common.collect.Iterables)5 ExpressionRef (com.apple.foundationdb.record.query.plan.temp.ExpressionRef)4 KeyPart (com.apple.foundationdb.record.query.plan.temp.KeyPart)4 Ordering (com.apple.foundationdb.record.query.plan.temp.Ordering)4 OrderingAttribute (com.apple.foundationdb.record.query.plan.temp.OrderingAttribute)4 RequestedOrdering (com.apple.foundationdb.record.query.plan.temp.RequestedOrdering)4