Search in sources :

Example 6 with Quantifier

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

the class SelectExpression method subsumedBy.

@Nonnull
@Override
public Iterable<MatchInfo> subsumedBy(@Nonnull final RelationalExpression candidateExpression, @Nonnull final AliasMap aliasMap, @Nonnull final IdentityBiMap<Quantifier, PartialMatch> partialMatchMap) {
    // TODO This method should be simplified by adding some structure to it.
    final Collection<MatchInfo> matchInfos = PartialMatch.matchesFromMap(partialMatchMap);
    Verify.verify(this != candidateExpression);
    if (getClass() != candidateExpression.getClass()) {
        return ImmutableList.of();
    }
    final SelectExpression otherSelectExpression = (SelectExpression) candidateExpression;
    // merge parameter maps -- early out if a binding clashes
    final ImmutableList<Map<CorrelationIdentifier, ComparisonRange>> parameterBindingMaps = matchInfos.stream().map(MatchInfo::getParameterBindingMap).collect(ImmutableList.toImmutableList());
    final Optional<Map<CorrelationIdentifier, ComparisonRange>> mergedParameterBindingMapOptional = MatchInfo.tryMergeParameterBindings(parameterBindingMaps);
    if (!mergedParameterBindingMapOptional.isPresent()) {
        return ImmutableList.of();
    }
    final Map<CorrelationIdentifier, ComparisonRange> mergedParameterBindingMap = mergedParameterBindingMapOptional.get();
    final ImmutableSet.Builder<CorrelationIdentifier> matchedCorrelatedToBuilder = ImmutableSet.builder();
    // for-each quantifiers. Also keep track of all aliases the matched quantifiers are correlated to.
    for (final Quantifier quantifier : getQuantifiers()) {
        if (partialMatchMap.containsKeyUnwrapped(quantifier)) {
            if (quantifier instanceof Quantifier.ForEach) {
                // current quantifier is matched
                final PartialMatch childPartialMatch = Objects.requireNonNull(partialMatchMap.getUnwrapped(quantifier));
                if (!childPartialMatch.getQueryExpression().computeUnmatchedForEachQuantifiers(childPartialMatch).isEmpty()) {
                    return ImmutableList.of();
                }
            }
            matchedCorrelatedToBuilder.addAll(quantifier.getCorrelatedTo());
        }
    }
    for (final Value resultValue : getResultValues()) {
        matchedCorrelatedToBuilder.addAll(resultValue.getCorrelatedTo());
    }
    final ImmutableSet<CorrelationIdentifier> matchedCorrelatedTo = matchedCorrelatedToBuilder.build();
    if (getQuantifiers().stream().anyMatch(quantifier -> quantifier instanceof Quantifier.ForEach && !partialMatchMap.containsKeyUnwrapped(quantifier))) {
        return ImmutableList.of();
    }
    final boolean allNonMatchedQuantifiersIndependent = getQuantifiers().stream().filter(quantifier -> !partialMatchMap.containsKeyUnwrapped(quantifier)).noneMatch(quantifier -> matchedCorrelatedTo.contains(quantifier.getAlias()));
    if (!allNonMatchedQuantifiersIndependent) {
        return ImmutableList.of();
    }
    // Loop through all for each quantifiers on the other side to ensure that they are all matched.
    // If any are not matched we cannot establish a match at all.
    final boolean allOtherForEachQuantifiersMatched = otherSelectExpression.getQuantifiers().stream().filter(quantifier -> quantifier instanceof Quantifier.ForEach).allMatch(quantifier -> aliasMap.containsTarget(quantifier.getAlias()));
    // would help us here to make sure the additional non-matched quantifier is not eliminating records.
    if (!allOtherForEachQuantifiersMatched) {
        return ImmutableList.of();
    }
    // 
    // Map predicates on the query side to predicates on the candidate side. Record parameter bindings and/or
    // compensations for each mapped predicate.
    // A predicate on this side (the query side) can cause us to filter out rows, a mapped predicate (for that
    // predicate) can only filter out fewer rows which is correct and can be compensated for. The important part
    // is that we must not have predicates on the other (candidate) side at the end of this mapping process which
    // would mean that the candidate eliminates records that the query side may not eliminate. If we detect that
    // case we MUST not create a match.
    // 
    final ImmutableList.Builder<Iterable<PredicateMapping>> predicateMappingsBuilder = ImmutableList.builder();
    // 
    if (getPredicates().isEmpty()) {
        final boolean allNonFiltering = otherSelectExpression.getPredicates().stream().allMatch(queryPredicate -> queryPredicate instanceof Placeholder || queryPredicate.isTautology());
        if (allNonFiltering) {
            return MatchInfo.tryMerge(partialMatchMap, mergedParameterBindingMap, PredicateMap.empty()).map(ImmutableList::of).orElse(ImmutableList.of());
        } else {
            return ImmutableList.of();
        }
    }
    for (final QueryPredicate predicate : getPredicates()) {
        final Set<PredicateMapping> impliedMappingsForPredicate = predicate.findImpliedMappings(aliasMap, otherSelectExpression.getPredicates());
        predicateMappingsBuilder.add(impliedMappingsForPredicate);
    }
    // 
    // We now have a multimap from predicates on the query side to predicates on the candidate side. In the trivial
    // case this multimap only contains singular mappings for a query predicate. If it doesn't we need to enumerate
    // through their cross product exhaustively. Each complete and non-contradictory element of that cross product
    // can lead to a match.
    // 
    final EnumeratingIterable<PredicateMapping> crossedMappings = CrossProduct.crossProduct(predicateMappingsBuilder.build());
    return IterableHelpers.flatMap(crossedMappings, predicateMappings -> {
        final Set<QueryPredicate> unmappedOtherPredicates = Sets.newIdentityHashSet();
        unmappedOtherPredicates.addAll(otherSelectExpression.getPredicates());
        final Map<CorrelationIdentifier, ComparisonRange> parameterBindingMap = Maps.newHashMap();
        final PredicateMap.Builder predicateMapBuilder = PredicateMap.builder();
        for (final PredicateMapping predicateMapping : predicateMappings) {
            predicateMapBuilder.put(predicateMapping.getQueryPredicate(), predicateMapping);
            unmappedOtherPredicates.remove(predicateMapping.getCandidatePredicate());
            final Optional<CorrelationIdentifier> parameterAliasOptional = predicateMapping.getParameterAliasOptional();
            final Optional<ComparisonRange> comparisonRangeOptional = predicateMapping.getComparisonRangeOptional();
            if (parameterAliasOptional.isPresent() && comparisonRangeOptional.isPresent()) {
                parameterBindingMap.put(parameterAliasOptional.get(), comparisonRangeOptional.get());
            }
        }
        // 
        // Last chance for unmapped predicates - if there is a placeholder or a tautology on the other side that is still
        // unmapped, we can (and should) remove it from the unmapped other set now. The reasoning is that this predicate is
        // not filtering so it does not cause records to be filtered that are not filtered on the query side.
        // 
        unmappedOtherPredicates.removeIf(queryPredicate -> queryPredicate instanceof Placeholder || queryPredicate.isTautology());
        if (!unmappedOtherPredicates.isEmpty()) {
            return ImmutableList.of();
        }
        final Optional<? extends PredicateMap> predicateMapOptional = predicateMapBuilder.buildMaybe();
        return predicateMapOptional.map(predicateMap -> {
            final Optional<Map<CorrelationIdentifier, ComparisonRange>> allParameterBindingMapOptional = MatchInfo.tryMergeParameterBindings(ImmutableList.of(mergedParameterBindingMap, parameterBindingMap));
            return allParameterBindingMapOptional.flatMap(allParameterBindingMap -> MatchInfo.tryMerge(partialMatchMap, allParameterBindingMap, predicateMap)).map(ImmutableList::of).orElse(ImmutableList.of());
        }).orElse(ImmutableList.of());
    });
}
Also used : ValuePredicate(com.apple.foundationdb.record.query.predicates.ValuePredicate) RelationalExpressionWithPredicates(com.apple.foundationdb.record.query.plan.temp.RelationalExpressionWithPredicates) Quantifier(com.apple.foundationdb.record.query.plan.temp.Quantifier) PredicateWithValue(com.apple.foundationdb.record.query.predicates.PredicateWithValue) ValueComparisonRangePredicate(com.apple.foundationdb.record.query.predicates.ValueComparisonRangePredicate) Function(java.util.function.Function) PredicateMap(com.apple.foundationdb.record.query.plan.temp.PredicateMap) Multimaps(com.google.common.collect.Multimaps) PartialMatch(com.apple.foundationdb.record.query.plan.temp.PartialMatch) HashMultimap(com.google.common.collect.HashMultimap) ImmutableList(com.google.common.collect.ImmutableList) ComparisonRange(com.apple.foundationdb.record.query.plan.temp.ComparisonRange) IterableHelpers(com.apple.foundationdb.record.query.plan.temp.IterableHelpers) Map(java.util.Map) Compensation(com.apple.foundationdb.record.query.plan.temp.Compensation) IdentityBiMap(com.apple.foundationdb.record.query.plan.temp.IdentityBiMap) Placeholder(com.apple.foundationdb.record.query.predicates.ValueComparisonRangePredicate.Placeholder) AliasMap(com.apple.foundationdb.record.query.plan.temp.AliasMap) Nonnull(javax.annotation.Nonnull) PredicateMapping(com.apple.foundationdb.record.query.plan.temp.PredicateMultiMap.PredicateMapping) Verify(com.google.common.base.Verify) ImmutableSet(com.google.common.collect.ImmutableSet) Equivalence(com.google.common.base.Equivalence) ImmutableMap(com.google.common.collect.ImmutableMap) Collection(java.util.Collection) Set(java.util.Set) InternalPlannerGraphRewritable(com.apple.foundationdb.record.query.plan.temp.explain.InternalPlannerGraphRewritable) QueryPredicate(com.apple.foundationdb.record.query.predicates.QueryPredicate) EnumeratingIterable(com.apple.foundationdb.record.query.combinatorics.EnumeratingIterable) Streams(com.google.common.collect.Streams) AndPredicate(com.apple.foundationdb.record.query.predicates.AndPredicate) Maps(com.google.common.collect.Maps) Collectors(java.util.stream.Collectors) Sets(com.google.common.collect.Sets) RelationalExpression(com.apple.foundationdb.record.query.plan.temp.RelationalExpression) Objects(java.util.Objects) Value(com.apple.foundationdb.record.query.predicates.Value) Comparisons(com.apple.foundationdb.record.query.expressions.Comparisons) List(java.util.List) Stream(java.util.stream.Stream) Sargable(com.apple.foundationdb.record.query.predicates.ValueComparisonRangePredicate.Sargable) CorrelationIdentifier(com.apple.foundationdb.record.query.plan.temp.CorrelationIdentifier) CrossProduct(com.apple.foundationdb.record.query.combinatorics.CrossProduct) MatchInfo(com.apple.foundationdb.record.query.plan.temp.MatchInfo) Optional(java.util.Optional) API(com.apple.foundationdb.annotation.API) PlannerGraph(com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph) Placeholder(com.apple.foundationdb.record.query.predicates.ValueComparisonRangePredicate.Placeholder) EnumeratingIterable(com.apple.foundationdb.record.query.combinatorics.EnumeratingIterable) ImmutableList(com.google.common.collect.ImmutableList) PredicateMap(com.apple.foundationdb.record.query.plan.temp.PredicateMap) ImmutableSet(com.google.common.collect.ImmutableSet) PartialMatch(com.apple.foundationdb.record.query.plan.temp.PartialMatch) QueryPredicate(com.apple.foundationdb.record.query.predicates.QueryPredicate) Optional(java.util.Optional) PredicateMapping(com.apple.foundationdb.record.query.plan.temp.PredicateMultiMap.PredicateMapping) MatchInfo(com.apple.foundationdb.record.query.plan.temp.MatchInfo) CorrelationIdentifier(com.apple.foundationdb.record.query.plan.temp.CorrelationIdentifier) PredicateWithValue(com.apple.foundationdb.record.query.predicates.PredicateWithValue) Value(com.apple.foundationdb.record.query.predicates.Value) Quantifier(com.apple.foundationdb.record.query.plan.temp.Quantifier) ComparisonRange(com.apple.foundationdb.record.query.plan.temp.ComparisonRange) PredicateMap(com.apple.foundationdb.record.query.plan.temp.PredicateMap) Map(java.util.Map) IdentityBiMap(com.apple.foundationdb.record.query.plan.temp.IdentityBiMap) AliasMap(com.apple.foundationdb.record.query.plan.temp.AliasMap) ImmutableMap(com.google.common.collect.ImmutableMap) Nonnull(javax.annotation.Nonnull)

Example 7 with Quantifier

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

the class ExpressionMatcherTest method anyRefMatcher.

@Test
public void anyRefMatcher() {
    // create a matcher and expression to match
    BindingMatcher<? extends ExpressionRef<? extends RelationalExpression>> matcher = ReferenceMatchers.anyRef();
    Quantifier.ForEach quantifier = Quantifier.forEach(GroupExpressionRef.of(new RecordQueryScanPlan(ScanComparisons.EMPTY, false)));
    ExpressionRef<RelationalExpression> root = GroupExpressionRef.of(new LogicalFilterExpression(ImmutableList.of(new QueryComponentPredicate(Query.field("test").equalsValue(5))), quantifier));
    // try to match to expression
    Optional<PlannerBindings> newBindings = matcher.bindMatches(PlannerBindings.empty(), root).findFirst();
    // check the the bindings are what we expect, and that none of the existing ones were clobbered
    assertTrue(newBindings.isPresent());
    PlannerBindings allBindings = newBindings.get().mergedWith(getExistingBindings());
    assertExistingBindingsSurvived(allBindings);
    assertTrue(newBindings.get().containsKey(matcher));
    assertTrue(allBindings.containsKey(matcher));
    assertEquals(root, allBindings.get(matcher));
}
Also used : RelationalExpression(com.apple.foundationdb.record.query.plan.temp.RelationalExpression) RecordQueryScanPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryScanPlan) QueryComponentPredicate(com.apple.foundationdb.record.query.predicates.QueryComponentPredicate) LogicalFilterExpression(com.apple.foundationdb.record.query.plan.temp.expressions.LogicalFilterExpression) Quantifier(com.apple.foundationdb.record.query.plan.temp.Quantifier) Test(org.junit.jupiter.api.Test)

Example 8 with Quantifier

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

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

the class MergeValue method pivotAndMergeValues.

public static List<? extends MergeValue> pivotAndMergeValues(@Nonnull final List<? extends Quantifier> quantifiers) {
    Verify.verify(!quantifiers.isEmpty());
    int numberOfFields = -1;
    final ImmutableList.Builder<List<? extends QuantifiedColumnValue>> allFlowedValuesBuilder = ImmutableList.builder();
    for (final Quantifier quantifier : quantifiers) {
        final List<? extends QuantifiedColumnValue> flowedValues = quantifier.getFlowedValues();
        allFlowedValuesBuilder.add(flowedValues);
        if (numberOfFields == -1 || numberOfFields > flowedValues.size()) {
            numberOfFields = flowedValues.size();
        }
    }
    final ImmutableList<List<? extends QuantifiedColumnValue>> allFlowedValues = allFlowedValuesBuilder.build();
    final ImmutableList.Builder<MergeValue> mergeValuesBuilder = ImmutableList.builder();
    for (int i = 0; i < numberOfFields; i++) {
        final ImmutableList.Builder<QuantifiedColumnValue> toBeMergedValuesBuilder = ImmutableList.builder();
        for (final List<? extends QuantifiedColumnValue> allFlowedValuesFromQuantifier : allFlowedValues) {
            final QuantifiedColumnValue quantifiedColumnValue = allFlowedValuesFromQuantifier.get(i);
            toBeMergedValuesBuilder.add(quantifiedColumnValue);
        }
        mergeValuesBuilder.add(new MergeValue(toBeMergedValuesBuilder.build()));
    }
    return mergeValuesBuilder.build();
}
Also used : ImmutableList(com.google.common.collect.ImmutableList) List(java.util.List) ImmutableList(com.google.common.collect.ImmutableList) Quantifier(com.apple.foundationdb.record.query.plan.temp.Quantifier)

Example 10 with Quantifier

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

the class PlannerRepl method processIdentifiers.

boolean processIdentifiers(final String potentialIdentifier, final Consumer<RelationalExpression> expressionConsumer, final Consumer<ExpressionRef<? extends RelationalExpression>> referenceConsumer, final Consumer<Quantifier> quantifierConsumer) {
    final State state = getCurrentState();
    final String upperCasePotentialIdentifier = potentialIdentifier.toUpperCase();
    if (upperCasePotentialIdentifier.startsWith("EXP")) {
        @Nullable final RelationalExpression expression = lookupInCache(state.getExpressionCache(), upperCasePotentialIdentifier, "EXP");
        if (expression == null) {
            return false;
        }
        expressionConsumer.accept(expression);
        return true;
    } else if (upperCasePotentialIdentifier.startsWith("REF")) {
        @Nullable final ExpressionRef<? extends RelationalExpression> reference = lookupInCache(state.getReferenceCache(), upperCasePotentialIdentifier, "REF");
        if (reference == null) {
            return false;
        }
        referenceConsumer.accept(reference);
        return true;
    } else if (upperCasePotentialIdentifier.startsWith("QUN")) {
        @Nullable final Quantifier quantifier = lookupInCache(state.getQuantifierCache(), upperCasePotentialIdentifier, "QUN");
        if (quantifier == null) {
            return false;
        }
        quantifierConsumer.accept(quantifier);
        return true;
    }
    return false;
}
Also used : RelationalExpression(com.apple.foundationdb.record.query.plan.temp.RelationalExpression) ExpressionRef(com.apple.foundationdb.record.query.plan.temp.ExpressionRef) Quantifier(com.apple.foundationdb.record.query.plan.temp.Quantifier) Nullable(javax.annotation.Nullable)

Aggregations

Quantifier (com.apple.foundationdb.record.query.plan.temp.Quantifier)23 Nonnull (javax.annotation.Nonnull)13 API (com.apple.foundationdb.annotation.API)10 ImmutableList (com.google.common.collect.ImmutableList)10 List (java.util.List)9 RelationalExpression (com.apple.foundationdb.record.query.plan.temp.RelationalExpression)8 CorrelationIdentifier (com.apple.foundationdb.record.query.plan.temp.CorrelationIdentifier)7 AliasMap (com.apple.foundationdb.record.query.plan.temp.AliasMap)6 BindingMatcher (com.apple.foundationdb.record.query.plan.temp.matchers.BindingMatcher)6 QueryPredicate (com.apple.foundationdb.record.query.predicates.QueryPredicate)6 Value (com.apple.foundationdb.record.query.predicates.Value)6 GroupExpressionRef (com.apple.foundationdb.record.query.plan.temp.GroupExpressionRef)5 PlannerRule (com.apple.foundationdb.record.query.plan.temp.PlannerRule)5 PlannerRuleCall (com.apple.foundationdb.record.query.plan.temp.PlannerRuleCall)5 ImmutableSet (com.google.common.collect.ImmutableSet)5 Collection (java.util.Collection)5 Set (java.util.Set)5 RecordQueryPlan (com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan)4 MatchInfo (com.apple.foundationdb.record.query.plan.temp.MatchInfo)4 PartialMatch (com.apple.foundationdb.record.query.plan.temp.PartialMatch)4