use of com.apple.foundationdb.record.query.plan.temp.Ordering in project fdb-record-layer by FoundationDB.
the class OrderingProperty method deriveForIntersectionFromOrderings.
public static Optional<Ordering> deriveForIntersectionFromOrderings(@Nonnull final List<Optional<Ordering>> orderingOptionals, @Nonnull final RequestedOrdering requestedOrdering, @Nonnull final BinaryOperator<SetMultimap<KeyExpression, Comparisons.Comparison>> combineFn) {
final Optional<SetMultimap<KeyExpression, Comparisons.Comparison>> commonEqualityBoundKeysMapOptional = Ordering.combineEqualityBoundKeys(orderingOptionals, combineFn);
if (commonEqualityBoundKeysMapOptional.isEmpty()) {
return Optional.empty();
}
final var commonEqualityBoundKeysMap = commonEqualityBoundKeysMapOptional.get();
final Optional<List<KeyPart>> commonOrderingKeysOptional = Ordering.commonOrderingKeys(orderingOptionals, requestedOrdering);
if (commonOrderingKeysOptional.isEmpty()) {
return Optional.empty();
}
final var commonOrderingKeys = commonOrderingKeysOptional.get().stream().filter(keyPart -> !commonEqualityBoundKeysMap.containsKey(keyPart.getNormalizedKeyExpression())).collect(ImmutableList.toImmutableList());
final boolean allAreDistinct = orderingOptionals.stream().anyMatch(orderingOptional -> orderingOptional.map(Ordering::isDistinct).orElse(false));
return Optional.of(new Ordering(commonEqualityBoundKeysMap, commonOrderingKeys, allAreDistinct));
}
use of com.apple.foundationdb.record.query.plan.temp.Ordering 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.plan.temp.Ordering in project fdb-record-layer by FoundationDB.
the class RemoveSortRule method onMatch.
@Override
public void onMatch(@Nonnull PlannerRuleCall call) {
final LogicalSortExpression sortExpression = call.get(root);
final RecordQueryPlan innerPlan = call.get(innerPlanMatcher);
final KeyExpression sortKeyExpression = sortExpression.getSort();
if (sortKeyExpression == null) {
call.yield(call.ref(innerPlan));
return;
}
final Optional<Ordering> orderingOptional = OrderingProperty.evaluate(innerPlan, call.getContext());
if (orderingOptional.isEmpty()) {
return;
}
final Ordering ordering = orderingOptional.get();
final Set<KeyExpression> equalityBoundKeys = ordering.getEqualityBoundKeys();
int equalityBoundUnsorted = equalityBoundKeys.size();
final List<KeyPart> orderingKeys = ordering.getOrderingKeyParts();
final Iterator<KeyPart> orderingKeysIterator = orderingKeys.iterator();
final List<KeyExpression> normalizedSortExpressions = sortKeyExpression.normalizeKeyForPositions();
for (final KeyExpression normalizedSortExpression : normalizedSortExpressions) {
if (equalityBoundKeys.contains(normalizedSortExpression)) {
equalityBoundUnsorted--;
continue;
}
if (!orderingKeysIterator.hasNext()) {
return;
}
final KeyPart currentOrderingKeyPart = orderingKeysIterator.next();
if (!normalizedSortExpression.equals(currentOrderingKeyPart.getNormalizedKeyExpression())) {
return;
}
}
final boolean strictOrdered = // If we have exhausted the ordering info's keys, too, then its constituents are strictly ordered.
!orderingKeysIterator.hasNext() || // Also a unique index if have gone through declared fields.
strictlyOrderedIfUnique(innerPlan, call.getContext()::getIndexByName, normalizedSortExpressions.size() + equalityBoundUnsorted);
call.yield(call.ref(strictOrdered ? innerPlan.strictlySorted() : innerPlan));
}
use of com.apple.foundationdb.record.query.plan.temp.Ordering in project fdb-record-layer by FoundationDB.
the class ImplementDistinctUnionRule method onMatch.
@Override
@SuppressWarnings("java:S135")
public void onMatch(@Nonnull PlannerRuleCall call) {
final PlanContext context = call.getContext();
final Optional<Set<RequestedOrdering>> requiredOrderingsOptional = call.getInterestingProperty(OrderingAttribute.ORDERING);
if (requiredOrderingsOptional.isEmpty()) {
return;
}
final Set<RequestedOrdering> requestedOrderings = requiredOrderingsOptional.get();
final KeyExpression commonPrimaryKey = context.getCommonPrimaryKey();
if (commonPrimaryKey == null) {
return;
}
final List<KeyExpression> commonPrimaryKeyParts = commonPrimaryKey.normalizeKeyForPositions();
final PlannerBindings bindings = call.getBindings();
final Quantifier.ForEach unionForEachQuantifier = bindings.get(unionForEachQuantifierMatcher);
final List<? extends Collection<? extends RecordQueryPlan>> plansByQuantifier = bindings.getAll(unionLegPlansMatcher);
// group each leg's plans by their provided ordering
final ImmutableList<Set<Map.Entry<Ordering, ImmutableList<RecordQueryPlan>>>> plansByQuantifierOrdering = plansByQuantifier.stream().map(plansForQuantifier -> {
final Map<Ordering, ImmutableList<RecordQueryPlan>> groupedBySortedness = plansForQuantifier.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())));
return groupedBySortedness.entrySet();
}).collect(ImmutableList.toImmutableList());
for (final List<Map.Entry<Ordering, ImmutableList<RecordQueryPlan>>> entries : CrossProduct.crossProduct(plansByQuantifierOrdering)) {
final ImmutableList<Optional<Ordering>> orderingOptionals = entries.stream().map(entry -> Optional.of(entry.getKey())).collect(ImmutableList.toImmutableList());
for (final RequestedOrdering requestedOrdering : requestedOrderings) {
final Optional<Ordering> combinedOrderingOptional = OrderingProperty.deriveForUnionFromOrderings(orderingOptionals, requestedOrdering, Ordering::intersectEqualityBoundKeys);
pushInterestingOrders(call, unionForEachQuantifier, orderingOptionals, requestedOrdering);
if (combinedOrderingOptional.isEmpty()) {
//
continue;
}
final Ordering ordering = combinedOrderingOptional.get();
final Set<KeyExpression> equalityBoundKeys = ordering.getEqualityBoundKeys();
final List<KeyPart> orderingKeyParts = ordering.getOrderingKeyParts();
final List<KeyExpression> orderingKeys = orderingKeyParts.stream().map(KeyPart::getNormalizedKeyExpression).collect(ImmutableList.toImmutableList());
// make sure the common primary key parts are either bound through equality or they are part of the ordering
if (!isPrimaryKeyCompatibleWithOrdering(commonPrimaryKeyParts, orderingKeys, equalityBoundKeys)) {
continue;
}
//
// 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);
//
// create new references
//
final ImmutableList<Quantifier.Physical> newQuantifiers = entries.stream().map(Map.Entry::getValue).map(GroupExpressionRef::from).map(Quantifier::physical).collect(ImmutableList.toImmutableList());
call.yield(call.ref(RecordQueryUnionPlan.fromQuantifiers(newQuantifiers, comparisonKey, true)));
}
}
}
use of com.apple.foundationdb.record.query.plan.temp.Ordering 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