use of com.apple.foundationdb.record.query.predicates.QuantifiedColumnValue 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.QuantifiedColumnValue 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.QuantifiedColumnValue in project fdb-record-layer by FoundationDB.
the class ImplementInJoinRule method onMatch.
@SuppressWarnings("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 selectExpression = bindings.get(root);
if (!selectExpression.getPredicates().isEmpty()) {
return;
}
final var explodeQuantifiers = bindings.get(explodeQuantifiersMatcher);
if (explodeQuantifiers.isEmpty()) {
return;
}
final var explodeAliasToQuantifierMap = Quantifiers.aliasToQuantifierMap(explodeQuantifiers);
final var explodeAliases = explodeAliasToQuantifierMap.keySet();
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 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())));
for (final Map.Entry<Ordering, ImmutableList<RecordQueryPlan>> providedOrderingEntry : groupedByOrdering.entrySet()) {
final var providedOrdering = providedOrderingEntry.getKey();
for (final RequestedOrdering requestedOrdering : requestedOrderings) {
final ImmutableList<InSource> sources = getInSourcesForRequestedOrdering(explodeAliasToQuantifierMap, explodeAliases, quantifierToExplodeBiMap, providedOrdering, requestedOrdering);
if (sources.isEmpty()) {
continue;
}
final var reverseSources = Lists.reverse(sources);
GroupExpressionRef<RecordQueryPlan> newInnerPlanReference = GroupExpressionRef.from(providedOrderingEntry.getValue());
for (final InSource inSource : reverseSources) {
final var inJoinPlan = inSource.toInJoinPlan(Quantifier.physical(newInnerPlanReference));
newInnerPlanReference = GroupExpressionRef.of(inJoinPlan);
}
call.yield(newInnerPlanReference);
}
}
}
use of com.apple.foundationdb.record.query.predicates.QuantifiedColumnValue in project fdb-record-layer by FoundationDB.
the class ValueIndexExpansionVisitor method expand.
@Nonnull
@Override
public MatchCandidate expand(@Nonnull final Quantifier.ForEach baseQuantifier, @Nullable final KeyExpression primaryKey, final boolean isReverse) {
Debugger.updateIndex(ValueComparisonRangePredicate.Placeholder.class, old -> 0);
final ImmutableList.Builder<GraphExpansion> allExpansionsBuilder = ImmutableList.builder();
// add the value for the flow of records
final QuantifiedColumnValue recordValue = QuantifiedColumnValue.of(baseQuantifier.getAlias(), 0);
allExpansionsBuilder.add(GraphExpansion.ofResultValueAndQuantifier(recordValue, baseQuantifier));
KeyExpression rootExpression = index.getRootExpression();
final int keyValueSplitPoint;
if (rootExpression instanceof KeyWithValueExpression) {
final KeyWithValueExpression keyWithValueExpression = (KeyWithValueExpression) rootExpression;
keyValueSplitPoint = keyWithValueExpression.getSplitPoint();
rootExpression = keyWithValueExpression.getInnerKey();
} else {
keyValueSplitPoint = -1;
}
final List<Value> keyValues = Lists.newArrayList();
final List<Value> valueValues = Lists.newArrayList();
final VisitorState initialState = VisitorState.of(keyValues, valueValues, baseQuantifier.getAlias(), ImmutableList.of(), keyValueSplitPoint, 0);
final GraphExpansion keyValueExpansion = pop(rootExpression.expand(push(initialState)));
allExpansionsBuilder.add(keyValueExpansion);
final int keySize = keyValues.size();
if (primaryKey != null) {
// unfortunately we must copy as the returned list is not guaranteed to be mutable which is needed for the
// trimPrimaryKey() function as it is causing a side-effect
final List<KeyExpression> trimmedPrimaryKeys = Lists.newArrayList(primaryKey.normalizeKeyForPositions());
index.trimPrimaryKey(trimmedPrimaryKeys);
for (int i = 0; i < trimmedPrimaryKeys.size(); i++) {
final KeyExpression primaryKeyPart = trimmedPrimaryKeys.get(i);
final VisitorState initialStateForKeyPart = VisitorState.of(keyValues, Lists.newArrayList(), baseQuantifier.getAlias(), ImmutableList.of(), -1, keySize + i);
final GraphExpansion primaryKeyPartExpansion = pop(primaryKeyPart.expand(push(initialStateForKeyPart)));
allExpansionsBuilder.add(primaryKeyPartExpansion);
}
}
final GraphExpansion completeExpansion = GraphExpansion.ofOthers(allExpansionsBuilder.build());
final List<CorrelationIdentifier> parameters = completeExpansion.getPlaceholderAliases();
final MatchableSortExpression matchableSortExpression = new MatchableSortExpression(parameters, isReverse, completeExpansion.buildSelect());
return new ValueIndexScanMatchCandidate(index, recordTypes, ExpressionRefTraversal.withRoot(GroupExpressionRef.of(matchableSortExpression)), parameters, recordValue, keyValues, valueValues, fullKey(index, primaryKey));
}
use of com.apple.foundationdb.record.query.predicates.QuantifiedColumnValue in project fdb-record-layer by FoundationDB.
the class ValueIndexScanMatchCandidate method pushValueThroughFetch.
@Nonnull
@Override
public Optional<Value> pushValueThroughFetch(@Nonnull Value value, @Nonnull QuantifiedColumnValue indexRecordColumnValue) {
final Set<Value> quantifiedColumnValues = ImmutableSet.copyOf(value.filter(v -> v instanceof QuantifiedColumnValue));
// if this is a value that is referring to more than one value from its quantifier or two multiple quantifiers
if (quantifiedColumnValues.size() != 1) {
return Optional.empty();
}
final QuantifiedColumnValue quantifiedColumnValue = (QuantifiedColumnValue) Iterables.getOnlyElement(quantifiedColumnValues);
// replace the quantified column value inside the given value with the quantified value in the match candidate
final Optional<Value> translatedValueOptional = value.translate(ImmutableMap.of(quantifiedColumnValue, QuantifiedColumnValue.of(recordValue.getAlias(), 0)));
if (!translatedValueOptional.isPresent()) {
return Optional.empty();
}
final Value translatedValue = translatedValueOptional.get();
final AliasMap equivalenceMap = AliasMap.identitiesFor(ImmutableSet.of(recordValue.getAlias()));
for (final Value matchResultValue : Iterables.concat(ImmutableList.of(recordValue), indexKeyValues, indexValueValues)) {
final Set<CorrelationIdentifier> resultValueCorrelatedTo = matchResultValue.getCorrelatedTo();
if (resultValueCorrelatedTo.size() != 1) {
continue;
}
if (translatedValue.semanticEquals(matchResultValue, equivalenceMap)) {
return matchResultValue.translate(ImmutableMap.of(QuantifiedColumnValue.of(recordValue.getAlias(), 0), indexRecordColumnValue));
}
}
return Optional.empty();
}
Aggregations