use of com.apple.foundationdb.record.query.plan.temp.IdentityBiMap 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());
});
}
use of com.apple.foundationdb.record.query.plan.temp.IdentityBiMap in project fdb-record-layer by FoundationDB.
the class ImplementInJoinRule method getInSourcesForRequestedOrdering.
@Nonnull
@SuppressWarnings("unchecked")
private ImmutableList<InSource> getInSourcesForRequestedOrdering(@Nonnull final Map<CorrelationIdentifier, Quantifier> explodeAliasToQuantifierMap, @Nonnull final Set<CorrelationIdentifier> explodeAliases, @Nonnull final IdentityBiMap<Quantifier.ForEach, ExplodeExpression> quantifierToExplodeBiMap, @Nonnull final Ordering providedInnerOrdering, @Nonnull final RequestedOrdering requestedOrdering) {
final var availableExplodeAliases = Sets.newLinkedHashSet(explodeAliases);
final var requestedOrderingKeyParts = requestedOrdering.getOrderingKeyParts();
final var sourcesBuilder = ImmutableList.<InSource>builder();
final var resultOrderingKeyPartsBuilder = ImmutableList.<KeyPart>builder();
final var innerOrderingKeyParts = providedInnerOrdering.getOrderingKeyParts();
final var innerEqualityBoundKeyMap = providedInnerOrdering.getEqualityBoundKeyMap();
final var resultOrderingEqualityBoundKeyMap = HashMultimap.create(innerEqualityBoundKeyMap);
for (var i = 0; i < requestedOrderingKeyParts.size() && !availableExplodeAliases.isEmpty(); i++) {
final var requestedOrderingKeyPart = requestedOrderingKeyParts.get(i);
final var comparisons = innerEqualityBoundKeyMap.get(requestedOrderingKeyPart.getNormalizedKeyExpression());
if (comparisons.isEmpty()) {
return ImmutableList.of();
}
final var comparisonsCorrelatedTo = comparisons.stream().flatMap(comparison -> comparison.getCorrelatedTo().stream()).collect(ImmutableSet.toImmutableSet());
if (comparisonsCorrelatedTo.size() > 1) {
return ImmutableList.of();
}
if (Sets.intersection(comparisonsCorrelatedTo, explodeAliases).isEmpty()) {
//
continue;
}
final var explodeAlias = Iterables.getOnlyElement(comparisonsCorrelatedTo);
//
if (!availableExplodeAliases.contains(explodeAlias)) {
return ImmutableList.of();
}
//
// We need to find the one quantifier over an explode expression that we can use to establish
// the requested order.
//
final var explodeQuantifier = Objects.requireNonNull(explodeAliasToQuantifierMap.get(explodeAlias));
final var explodeExpression = Objects.requireNonNull(quantifierToExplodeBiMap.getUnwrapped(explodeQuantifier));
//
// At this point we have a bound key expression that matches the requested order at this position,
// and we have our hands on a particular explode expression leading us directly do the in source.
//
final var explodeResultValues = explodeExpression.getResultValues();
if (explodeResultValues.size() != 1) {
return ImmutableList.of();
}
final var explodeValue = Iterables.getOnlyElement(explodeResultValues);
final InSource inSource;
if (explodeValue instanceof LiteralValue<?>) {
final Object literalValue = ((LiteralValue<?>) explodeValue).getLiteralValue();
if (literalValue instanceof List<?>) {
inSource = new SortedInValuesSource(CORRELATION.bindingName(explodeQuantifier.getAlias().getId()), (List<Object>) literalValue, requestedOrderingKeyPart.isReverse());
} else {
return ImmutableList.of();
}
} else if (explodeValue instanceof QuantifiedColumnValue) {
inSource = new SortedInParameterSource(CORRELATION.bindingName(explodeQuantifier.getAlias().getId()), ((QuantifiedColumnValue) explodeValue).getAlias().getId(), requestedOrderingKeyPart.isReverse());
} else {
return ImmutableList.of();
}
availableExplodeAliases.remove(explodeAlias);
sourcesBuilder.add(inSource);
resultOrderingEqualityBoundKeyMap.removeAll(requestedOrderingKeyPart.getNormalizedKeyExpression());
resultOrderingKeyPartsBuilder.add(requestedOrderingKeyPart);
}
if (availableExplodeAliases.isEmpty()) {
//
// All available explode aliases have been depleted. Create an ordering and check against the requested
// ordering.
//
resultOrderingKeyPartsBuilder.addAll(innerOrderingKeyParts);
final var resultOrdering = new Ordering(resultOrderingEqualityBoundKeyMap, resultOrderingKeyPartsBuilder.build(), providedInnerOrdering.isDistinct());
return Ordering.satisfiesRequestedOrdering(resultOrdering, requestedOrdering) ? sourcesBuilder.build() : ImmutableList.of();
} else {
//
for (final var explodeAlias : availableExplodeAliases) {
final var explodeQuantifier = Objects.requireNonNull(explodeAliasToQuantifierMap.get(explodeAlias));
final var explodeExpression = Objects.requireNonNull(quantifierToExplodeBiMap.getUnwrapped(explodeQuantifier));
final var explodeResultValues = explodeExpression.getResultValues();
if (explodeResultValues.size() != 1) {
return ImmutableList.of();
}
final var explodeValue = Iterables.getOnlyElement(explodeResultValues);
final InSource inSource;
if (explodeValue instanceof LiteralValue<?>) {
final Object literalValue = ((LiteralValue<?>) explodeValue).getLiteralValue();
if (literalValue instanceof List<?>) {
inSource = new InValuesSource(CORRELATION.bindingName(explodeQuantifier.getAlias().getId()), (List<Object>) literalValue);
} else {
return ImmutableList.of();
}
} else if (explodeValue instanceof QuantifiedColumnValue) {
inSource = new InParameterSource(CORRELATION.bindingName(explodeQuantifier.getAlias().getId()), ((QuantifiedColumnValue) explodeValue).getAlias().getId());
} else {
return ImmutableList.of();
}
sourcesBuilder.add(inSource);
}
}
//
return sourcesBuilder.build();
}
Aggregations