use of com.apple.foundationdb.record.query.predicates.Value in project fdb-record-layer by FoundationDB.
the class RecordQuerySetPlan method tryPushValues.
@Nonnull
@SuppressWarnings("java:S135")
default Set<CorrelationIdentifier> tryPushValues(@Nonnull final List<TranslateValueFunction> dependentFunctions, @Nonnull final List<? extends Quantifier> quantifiers, @Nonnull final Iterable<? extends Value> values) {
Verify.verify(!dependentFunctions.isEmpty());
Verify.verify(dependentFunctions.size() == quantifiers.size());
final Set<CorrelationIdentifier> candidatesAliases = quantifiers.stream().map(Quantifier::getAlias).collect(Collectors.toSet());
final CorrelationIdentifier newBaseAlias = CorrelationIdentifier.uniqueID();
final QuantifiedColumnValue newBaseColumnValue = QuantifiedColumnValue.of(newBaseAlias, 0);
for (final Value value : values) {
final AliasMap equivalencesMap = AliasMap.identitiesFor(ImmutableSet.of(newBaseAlias));
@Nullable Value previousPushedValue = null;
for (int i = 0; i < dependentFunctions.size(); i++) {
final TranslateValueFunction dependentFunction = dependentFunctions.get(i);
final Quantifier quantifier = quantifiers.get(i);
if (!candidatesAliases.contains(quantifier.getAlias())) {
continue;
}
final Optional<Value> pushedValueOptional = dependentFunction.translateValue(value, newBaseColumnValue);
if (!pushedValueOptional.isPresent()) {
candidatesAliases.remove(quantifier.getAlias());
continue;
}
if (previousPushedValue == null) {
previousPushedValue = pushedValueOptional.get();
} else {
if (!previousPushedValue.semanticEquals(pushedValueOptional.get(), equivalencesMap)) {
// something is really wrong as we cannot establish a proper genuine derivation path
return ImmutableSet.of();
}
}
}
}
return ImmutableSet.copyOf(candidatesAliases);
}
use of com.apple.foundationdb.record.query.predicates.Value in project fdb-record-layer by FoundationDB.
the class ValueIndexLikeExpansionVisitor method visitExpression.
@Nonnull
@Override
public GraphExpansion visitExpression(@Nonnull FieldKeyExpression fieldKeyExpression) {
final String fieldName = fieldKeyExpression.getFieldName();
final KeyExpression.FanType fanType = fieldKeyExpression.getFanType();
final VisitorState state = getCurrentState();
final List<String> fieldNamePrefix = state.getFieldNamePrefix();
final CorrelationIdentifier baseAlias = state.getBaseAlias();
final List<String> fieldNames = ImmutableList.<String>builder().addAll(fieldNamePrefix).add(fieldName).build();
final Value value;
final Placeholder predicate;
switch(fanType) {
case FanOut:
// explode this field and prefixes of this field
final Quantifier childBase = fieldKeyExpression.explodeField(baseAlias, fieldNamePrefix);
value = state.registerValue(QuantifiedObjectValue.of(childBase.getAlias()));
final GraphExpansion childExpansion;
if (state.isKey()) {
predicate = value.asPlaceholder(newParameterAlias());
childExpansion = GraphExpansion.ofPlaceholder(value, predicate);
} else {
childExpansion = GraphExpansion.ofResultValue(value);
}
final SelectExpression selectExpression = childExpansion.buildSelectWithBase(childBase);
final Quantifier childQuantifier = Quantifier.forEach(GroupExpressionRef.of(selectExpression));
final GraphExpansion.Sealed sealedChildExpansion = childExpansion.seal();
return sealedChildExpansion.derivedWithQuantifier(childQuantifier);
case None:
value = state.registerValue(new FieldValue(QuantifiedColumnValue.of(baseAlias, 0), fieldNames));
if (state.isKey()) {
predicate = value.asPlaceholder(newParameterAlias());
return GraphExpansion.ofPlaceholder(value, predicate);
}
return GraphExpansion.ofResultValue(value);
// TODO collect/concatenate function
case Concatenate:
default:
}
throw new UnsupportedOperationException();
}
use of com.apple.foundationdb.record.query.predicates.Value in project fdb-record-layer by FoundationDB.
the class ImplementInUnionRule method onMatch.
@SuppressWarnings({ "unchecked", "java:S135" })
@Override
public void onMatch(@Nonnull PlannerRuleCall call) {
final var context = call.getContext();
final var bindings = call.getBindings();
final var requestedOrderingsOptional = call.getInterestingProperty(OrderingAttribute.ORDERING);
if (requestedOrderingsOptional.isEmpty()) {
return;
}
final var requestedOrderings = requestedOrderingsOptional.get();
final var commonPrimaryKey = context.getCommonPrimaryKey();
if (commonPrimaryKey == null) {
return;
}
final var selectExpression = bindings.get(root);
if (!selectExpression.getPredicates().isEmpty()) {
return;
}
final var explodeQuantifiers = bindings.get(explodeQuantifiersMatcher);
if (explodeQuantifiers.isEmpty()) {
return;
}
final var explodeAliases = Quantifiers.aliases(explodeQuantifiers);
final var innerQuantifierOptional = findInnerQuantifier(selectExpression, explodeQuantifiers, explodeAliases);
if (innerQuantifierOptional.isEmpty()) {
return;
}
final var innerQuantifier = innerQuantifierOptional.get();
final List<? extends Value> resultValues = selectExpression.getResultValues();
if (resultValues.stream().anyMatch(resultValue -> !(resultValue instanceof QuantifiedColumnValue) || !((QuantifiedColumnValue) resultValue).getAlias().equals(innerQuantifier.getAlias()))) {
return;
}
final var explodeExpressions = bindings.getAll(explodeExpressionMatcher);
final var quantifierToExplodeBiMap = computeQuantifierToExplodeMap(explodeQuantifiers, explodeExpressions.stream().collect(LinkedIdentitySet.toLinkedIdentitySet()));
final var explodeToQuantifierBiMap = quantifierToExplodeBiMap.inverse();
final var sourcesBuilder = ImmutableList.<InSource>builder();
for (final var explodeExpression : explodeExpressions) {
final var explodeQuantifier = Objects.requireNonNull(explodeToQuantifierBiMap.getUnwrapped(explodeExpression));
final List<? extends Value> explodeResultValues = explodeExpression.getResultValues();
if (explodeResultValues.size() != 1) {
return;
}
final Value explodeValue = Iterables.getOnlyElement(explodeResultValues);
//
// Create the source for the in-union plan
//
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;
}
} else if (explodeValue instanceof QuantifiedColumnValue) {
inSource = new InParameterSource(CORRELATION.bindingName(explodeQuantifier.getAlias().getId()), ((QuantifiedColumnValue) explodeValue).getAlias().getId());
} else {
return;
}
sourcesBuilder.add(inSource);
}
final var inSources = sourcesBuilder.build();
final Map<Ordering, ImmutableList<RecordQueryPlan>> groupedByOrdering = innerQuantifier.getRangesOver().getMembers().stream().flatMap(relationalExpression -> relationalExpression.narrowMaybe(RecordQueryPlan.class).stream()).flatMap(plan -> {
final Optional<Ordering> orderingForLegOptional = OrderingProperty.evaluate(plan, context);
return orderingForLegOptional.stream().map(ordering -> Pair.of(ordering, plan));
}).collect(Collectors.groupingBy(Pair::getLeft, Collectors.mapping(Pair::getRight, ImmutableList.toImmutableList())));
final int attemptFailedInJoinAsUnionMaxSize = call.getContext().getPlannerConfiguration().getAttemptFailedInJoinAsUnionMaxSize();
for (final Map.Entry<Ordering, ImmutableList<RecordQueryPlan>> providedOrderingEntry : groupedByOrdering.entrySet()) {
for (final RequestedOrdering requestedOrdering : requestedOrderings) {
final var providedOrdering = providedOrderingEntry.getKey();
final var matchingKeyExpressionsBuilder = ImmutableSet.<KeyExpression>builder();
for (Map.Entry<KeyExpression, Comparisons.Comparison> expressionComparisonEntry : providedOrdering.getEqualityBoundKeyMap().entries()) {
final Comparisons.Comparison comparison = expressionComparisonEntry.getValue();
if (comparison.getType() == Comparisons.Type.EQUALS && comparison instanceof Comparisons.ParameterComparison) {
final Comparisons.ParameterComparison parameterComparison = (Comparisons.ParameterComparison) comparison;
if (parameterComparison.isCorrelation() && explodeAliases.containsAll(parameterComparison.getCorrelatedTo())) {
matchingKeyExpressionsBuilder.add(expressionComparisonEntry.getKey());
}
}
}
// Compute a comparison key that satisfies the requested ordering
final Optional<Ordering> combinedOrderingOptional = orderingForInUnion(providedOrdering, requestedOrdering, matchingKeyExpressionsBuilder.build());
if (combinedOrderingOptional.isEmpty()) {
continue;
}
final Ordering combinedOrdering = combinedOrderingOptional.get();
final List<KeyPart> orderingKeyParts = combinedOrdering.getOrderingKeyParts();
final List<KeyExpression> orderingKeys = orderingKeyParts.stream().map(KeyPart::getNormalizedKeyExpression).collect(ImmutableList.toImmutableList());
//
// At this point we know we can implement the distinct union over the partitions of compatibly ordered plans
//
final KeyExpression comparisonKey = orderingKeys.size() == 1 ? Iterables.getOnlyElement(orderingKeys) : Key.Expressions.concat(orderingKeys);
final GroupExpressionRef<RecordQueryPlan> newInnerPlanReference = GroupExpressionRef.from(providedOrderingEntry.getValue());
final Quantifier.Physical newInnerQuantifier = Quantifier.physical(newInnerPlanReference);
call.yield(call.ref(new RecordQueryInUnionPlan(newInnerQuantifier, inSources, comparisonKey, RecordQueryUnionPlanBase.isReversed(ImmutableList.of(newInnerQuantifier)), attemptFailedInJoinAsUnionMaxSize)));
}
}
}
use of com.apple.foundationdb.record.query.predicates.Value 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.predicates.Value in project fdb-record-layer by FoundationDB.
the class PushFilterThroughFetchRule method pushLeafPredicate.
@Nullable
private QueryPredicate pushLeafPredicate(@Nonnull RecordQueryFetchFromPartialRecordPlan fetchPlan, @Nonnull CorrelationIdentifier newInnerAlias, @Nonnull final QueryPredicate leafPredicate) {
if (leafPredicate instanceof QueryComponentPredicate) {
// We cannot push these predicates. They always contain nesteds.
return null;
}
if (!(leafPredicate instanceof PredicateWithValue)) {
// appears to be pushable as is.
return leafPredicate;
}
final PredicateWithValue predicateWithValue = (PredicateWithValue) leafPredicate;
final Value value = predicateWithValue.getValue();
final Optional<Value> pushedValueOptional = fetchPlan.pushValue(value, newInnerAlias);
// We must return null to prevent pushing of this conjunct.
return pushedValueOptional.map(predicateWithValue::withValue).orElse(null);
}
Aggregations