use of com.apple.foundationdb.record.query.plan.temp.Quantifier in project fdb-record-layer by FoundationDB.
the class RecordQueryPlan method structuralEquals.
/**
* Determine if two plans are structurally equal. This differs from the semantic equality defined in
* {@link RelationalExpression}. For instance this method would return false
* for two given plans {@code UNION(p1, p2)} and {@code UNION(p2, p1)} of two different sub-plans {@code p1} and
* {@code p2}. In contrast to that these plans are considered semantically equal.
* @param other object to compare this object with
* @param equivalenceMap alias map to indicate aliases that should be considered as equal when {@code other} is
* compared to {@code this}. For instance {@code q1.x = 1} is only structurally equal with {@code q2.x = 1}
* if there is a mapping {@code q1 -> q2} in the alias map passed in
* @return {@code true} if {@code this} is structurally equal to {@code other}, {@code false} otherwise
*/
@API(API.Status.EXPERIMENTAL)
default boolean structuralEquals(@Nullable final Object other, @Nonnull final AliasMap equivalenceMap) {
if (this == other) {
return true;
}
if (other == null || getClass() != other.getClass()) {
return false;
}
final RelationalExpression otherExpression = (RelationalExpression) other;
// We know this and otherExpression are of the same class. canCorrelate() needs to match as well.
Verify.verify(canCorrelate() == otherExpression.canCorrelate());
final List<Quantifier.Physical> quantifiers = Quantifiers.narrow(Quantifier.Physical.class, getQuantifiers());
final List<Quantifier.Physical> otherQuantifiers = Quantifiers.narrow(Quantifier.Physical.class, otherExpression.getQuantifiers());
if (quantifiers.size() != otherQuantifiers.size()) {
return false;
}
final Iterable<AliasMap> boundCorrelatedReferencesIterable = enumerateUnboundCorrelatedTo(equivalenceMap, otherExpression);
for (final AliasMap boundCorrelatedReferencesMap : boundCorrelatedReferencesIterable) {
final AliasMap.Builder boundCorrelatedToBuilder = boundCorrelatedReferencesMap.derived();
AliasMap boundCorrelatedToMap = AliasMap.emptyMap();
int i;
for (i = 0; i < quantifiers.size(); i++) {
boundCorrelatedToMap = boundCorrelatedToBuilder.build();
final Quantifier.Physical quantifier = quantifiers.get(i);
final Quantifier.Physical otherQuantifier = otherQuantifiers.get(i);
if (quantifier.structuralHashCode() != otherQuantifier.structuralHashCode()) {
break;
}
if (!quantifier.structuralEquals(otherQuantifier)) {
break;
}
if (canCorrelate()) {
boundCorrelatedToBuilder.put(quantifier.getAlias(), otherQuantifier.getAlias());
}
}
if (i == quantifiers.size() && (equalsWithoutChildren(otherExpression, boundCorrelatedToMap))) {
return true;
}
}
return false;
}
use of com.apple.foundationdb.record.query.plan.temp.Quantifier 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.plan.temp.Quantifier in project fdb-record-layer by FoundationDB.
the class InComparisonToExplodeRule method onMatch.
@Override
public void onMatch(@Nonnull PlannerRuleCall call) {
final PlannerBindings bindings = call.getBindings();
final SelectExpression selectExpression = bindings.get(root);
// we don't need iteration stability
final List<? extends ValuePredicate> inPredicatesList = bindings.getAll(inPredicateMatcher);
if (inPredicatesList.isEmpty()) {
return;
}
final Set<QueryPredicate> inPredicates = Sets.newIdentityHashSet();
inPredicates.addAll(inPredicatesList);
final ImmutableList.Builder<Quantifier> transformedQuantifiers = ImmutableList.builder();
final ImmutableList.Builder<QueryPredicate> transformedPredicates = ImmutableList.builder();
for (final QueryPredicate predicate : selectExpression.getPredicates()) {
if (inPredicates.contains(predicate)) {
final ValuePredicate valuePredicate = (ValuePredicate) predicate;
final Comparisons.Comparison comparison = valuePredicate.getComparison();
Verify.verify(comparison.getType() == Comparisons.Type.IN);
final ExplodeExpression explodeExpression;
if (comparison instanceof Comparisons.ListComparison) {
explodeExpression = new ExplodeExpression(new LiteralValue<>(comparison.getComparand()));
} else if (comparison instanceof Comparisons.ParameterComparison) {
explodeExpression = new ExplodeExpression(QuantifiedColumnValue.of(CorrelationIdentifier.of(((Comparisons.ParameterComparison) comparison).getParameter()), 0));
} else {
throw new RecordCoreException("unknown in comparison " + comparison.getClass().getSimpleName());
}
final Quantifier.ForEach newQuantifier = Quantifier.forEach(GroupExpressionRef.of(explodeExpression));
transformedPredicates.add(new ValuePredicate(((ValuePredicate) predicate).getValue(), new Comparisons.ParameterComparison(Comparisons.Type.EQUALS, Bindings.Internal.CORRELATION.bindingName(newQuantifier.getAlias().toString()), Bindings.Internal.CORRELATION)));
transformedQuantifiers.add(newQuantifier);
} else {
transformedPredicates.add(predicate);
}
}
transformedQuantifiers.addAll(bindings.getAll(innerQuantifierMatcher));
call.yield(call.ref(new SelectExpression(selectExpression.getResultValues(), transformedQuantifiers.build(), transformedPredicates.build())));
}
use of com.apple.foundationdb.record.query.plan.temp.Quantifier in project fdb-record-layer by FoundationDB.
the class RelationalExpressionWithChildren method computeMappedQuantifiers.
@Nonnull
default Set<Quantifier> computeMappedQuantifiers(@Nonnull final PartialMatch partialMatch) {
final var matchInfo = partialMatch.getMatchInfo();
final var mappedForEachQuantifiers = new LinkedIdentitySet<Quantifier>();
for (final Quantifier quantifier : getQuantifiers()) {
if (matchInfo.getChildPartialMatch(quantifier.getAlias()).isPresent()) {
mappedForEachQuantifiers.add(quantifier);
}
}
return mappedForEachQuantifiers;
}
use of com.apple.foundationdb.record.query.plan.temp.Quantifier in project fdb-record-layer by FoundationDB.
the class SelectExpression method compensate.
@Override
@SuppressWarnings({ "java:S135", "java:S1066" })
public Compensation compensate(@Nonnull final PartialMatch partialMatch, @Nonnull final Map<CorrelationIdentifier, ComparisonRange> boundParameterPrefixMap) {
final Map<QueryPredicate, QueryPredicate> toBeReappliedPredicatesMap = Maps.newIdentityHashMap();
final MatchInfo matchInfo = partialMatch.getMatchInfo();
final PredicateMap predicateMap = matchInfo.getPredicateMap();
//
// The partial match we are called with here has child matches that have compensations on their own.
// Given a pair of these matches that we reach along two for each quantifiers (forming a join) we have to
// apply both compensations. The compensation class has a union method to combine two compensations in an
// optimal way. We need to fold over all those compensations to form one child compensation. The tree that
// is formed by partial matches therefore collapses into a chain of compensations.
//
final List<? extends Quantifier> quantifiers = getQuantifiers();
final Compensation childCompensation = quantifiers.stream().filter(quantifier -> quantifier instanceof Quantifier.ForEach).flatMap(quantifier -> matchInfo.getChildPartialMatch(quantifier).map(childPartialMatch -> childPartialMatch.compensate(boundParameterPrefixMap)).map(Stream::of).orElse(Stream.empty())).reduce(Compensation.noCompensation(), Compensation::union);
//
// The fact that we matched the partial match handed in must mean that the child compensation is not impossible.
//
Verify.verify(!childCompensation.isImpossible());
//
for (final QueryPredicate predicate : getPredicates()) {
final Optional<PredicateMapping> predicateMappingOptional = predicateMap.getMappingOptional(predicate);
Verify.verify(predicateMappingOptional.isPresent());
final PredicateMapping predicateMapping = predicateMappingOptional.get();
final Optional<QueryPredicate> reappliedPredicateOptional = predicateMapping.reapplyPredicateFunction().reapplyPredicateMaybe(matchInfo, boundParameterPrefixMap);
reappliedPredicateOptional.ifPresent(reappliedPredicate -> toBeReappliedPredicatesMap.put(predicate, reappliedPredicate));
}
return Compensation.ofChildCompensationAndPredicateMap(childCompensation, toBeReappliedPredicatesMap, computeMappedQuantifiers(partialMatch), computeUnmatchedForEachQuantifiers(partialMatch));
}
Aggregations