Skip to content

Commit

Permalink
Add some Cascades support.
Browse files Browse the repository at this point in the history
  • Loading branch information
MMcM committed Aug 1, 2024
1 parent 6fe912b commit 1b370ed
Show file tree
Hide file tree
Showing 9 changed files with 516 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import com.apple.foundationdb.record.query.plan.cascades.rules.ImplementIntersectionRule;
import com.apple.foundationdb.record.query.plan.cascades.rules.ImplementNestedLoopJoinRule;
import com.apple.foundationdb.record.query.plan.cascades.rules.ImplementPhysicalScanRule;
import com.apple.foundationdb.record.query.plan.cascades.rules.ImplementRecursiveRule;
import com.apple.foundationdb.record.query.plan.cascades.rules.ImplementSimpleSelectRule;
import com.apple.foundationdb.record.query.plan.cascades.rules.ImplementStreamingAggregationRule;
import com.apple.foundationdb.record.query.plan.cascades.rules.ImplementTypeFilterRule;
Expand Down Expand Up @@ -66,6 +67,7 @@
import com.apple.foundationdb.record.query.plan.cascades.rules.PushRequestedOrderingThroughGroupByRule;
import com.apple.foundationdb.record.query.plan.cascades.rules.PushRequestedOrderingThroughInLikeSelectRule;
import com.apple.foundationdb.record.query.plan.cascades.rules.PushRequestedOrderingThroughInsertRule;
import com.apple.foundationdb.record.query.plan.cascades.rules.PushRequestedOrderingThroughRecursiveRule;
import com.apple.foundationdb.record.query.plan.cascades.rules.PushRequestedOrderingThroughSelectExistentialRule;
import com.apple.foundationdb.record.query.plan.cascades.rules.PushRequestedOrderingThroughSelectRule;
import com.apple.foundationdb.record.query.plan.cascades.rules.PushRequestedOrderingThroughSortRule;
Expand Down Expand Up @@ -130,7 +132,8 @@ public class PlannerRuleSet {
new PushRequestedOrderingThroughDeleteRule(),
new PushRequestedOrderingThroughInsertRule(),
new PushRequestedOrderingThroughUpdateRule(),
new PushRequestedOrderingThroughUniqueRule()
new PushRequestedOrderingThroughUniqueRule(),
new PushRequestedOrderingThroughRecursiveRule()
);

private static final List<CascadesRule<? extends RelationalExpression>> IMPLEMENTATION_RULES = ImmutableList.of(
Expand Down Expand Up @@ -167,7 +170,8 @@ public class PlannerRuleSet {
new ImplementStreamingAggregationRule(),
new ImplementDeleteRule(),
new ImplementInsertRule(),
new ImplementUpdateRule()
new ImplementUpdateRule(),
new ImplementRecursiveRule()
);

private static final List<CascadesRule<? extends RelationalExpression>> EXPLORATION_RULES =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* RecursiveExpression.java
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2015-2020 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.apple.foundationdb.record.query.plan.cascades.expressions;

import com.apple.foundationdb.annotation.API;
import com.apple.foundationdb.record.query.plan.cascades.AliasMap;
import com.apple.foundationdb.record.query.plan.cascades.CorrelationIdentifier;
import com.apple.foundationdb.record.query.plan.cascades.Quantifier;
import com.apple.foundationdb.record.query.plan.cascades.explain.InternalPlannerGraphRewritable;
import com.apple.foundationdb.record.query.plan.cascades.explain.PlannerGraph;
import com.apple.foundationdb.record.query.plan.cascades.values.Value;
import com.apple.foundationdb.record.query.plan.cascades.values.Values;
import com.apple.foundationdb.record.query.plan.cascades.values.translation.TranslationMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;

import javax.annotation.Nonnull;
import java.util.List;
import java.util.Set;

/**
* A recursive expression.
*/
@API(API.Status.EXPERIMENTAL)
public class RecursiveExpression implements RelationalExpressionWithChildren, InternalPlannerGraphRewritable {
@Nonnull
private final Value resultValue;
@Nonnull
private final Quantifier rootQuantifier;
@Nonnull
private final Quantifier childQuantifier;

public RecursiveExpression(@Nonnull Value resultValue,
@Nonnull Quantifier rootQuantifier,
@Nonnull Quantifier childQuantifier) {
this.resultValue = resultValue;
this.rootQuantifier = rootQuantifier;
this.childQuantifier = childQuantifier;
}

@Nonnull
@Override
public Value getResultValue() {
return resultValue;
}

@Nonnull
public List<? extends Value> getResultValues() {
return Values.deconstructRecord(getResultValue());
}

@Nonnull
@Override
public List<? extends Quantifier> getQuantifiers() {
return List.of(rootQuantifier, childQuantifier);
}

@Override
public int getRelationalChildCount() {
return 2;
}

@Override
public boolean canCorrelate() {
return true;
}

@Nonnull
@Override
public Set<CorrelationIdentifier> getCorrelatedToWithoutChildren() {
return resultValue.getCorrelatedTo();
}

@Nonnull
@Override
public RecursiveExpression translateCorrelations(@Nonnull final TranslationMap translationMap, @Nonnull final List<? extends Quantifier> translatedQuantifiers) {
final Value translatedResultValue = resultValue.translateCorrelations(translationMap);
return new RecursiveExpression(translatedResultValue, translatedQuantifiers.get(0), translatedQuantifiers.get(1));
}

@SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
@Override
public boolean equals(final Object other) {
return semanticEquals(other);
}

@Override
public int hashCode() {
return semanticHashCode();
}

@Override
@SuppressWarnings({"UnstableApiUsage", "PMD.CompareObjectsWithEquals"})
public boolean equalsWithoutChildren(@Nonnull RelationalExpression otherExpression,
@Nonnull final AliasMap aliasMap) {
if (this == otherExpression) {
return true;
}
if (getClass() != otherExpression.getClass()) {
return false;
}

return semanticEqualsForResults(otherExpression, aliasMap);
}

@Override
public int hashCodeWithoutChildren() {
return getResultValue().hashCode();
}

@Nonnull
@Override
public PlannerGraph rewriteInternalPlannerGraph(@Nonnull final List<? extends PlannerGraph> childGraphs) {
return PlannerGraph.fromNodeAndChildGraphs(
new PlannerGraph.LogicalOperatorNode(this,
"RECURSIVE " + resultValue,
ImmutableList.of(),
ImmutableMap.of()),
childGraphs);
}

@Override
public String toString() {
return "RECURSIVE " + resultValue;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import com.apple.foundationdb.record.query.plan.cascades.expressions.LogicalUnionExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.LogicalUniqueExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.PrimaryScanExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.RecursiveExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.RelationalExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.RelationalExpressionWithPredicates;
import com.apple.foundationdb.record.query.plan.cascades.expressions.SelectExpression;
Expand Down Expand Up @@ -277,4 +278,9 @@ public static BindingMatcher<InsertExpression> insertExpression(@Nonnull final B
public static BindingMatcher<UpdateExpression> updateExpression(@Nonnull final BindingMatcher<? extends Quantifier> downstream) {
return ofTypeOwning(UpdateExpression.class, only(downstream));
}

@Nonnull
public static BindingMatcher<RecursiveExpression> recursiveExpression(@Nonnull final CollectionMatcher<? extends Quantifier> downstream) {
return ofTypeOwning(RecursiveExpression.class, downstream);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import com.apple.foundationdb.record.query.plan.cascades.expressions.LogicalUniqueExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.MatchableSortExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.PrimaryScanExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.RecursiveExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.RelationalExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.SelectExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.UpdateExpression;
Expand Down Expand Up @@ -601,6 +602,12 @@ public Cardinalities visitRecordQueryRecursivePlan(@Nonnull final RecordQueryRec
return Cardinalities.unknownMaxCardinality();
}

@Nonnull
@Override
public Cardinalities visitRecursiveExpression(@Nonnull final RecursiveExpression element) {
return Cardinalities.unknownMaxCardinality();
}

@Nonnull
@Override
public Cardinalities evaluateAtExpression(@Nonnull RelationalExpression expression, @Nonnull List<Cardinalities> childResults) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import com.apple.foundationdb.record.query.plan.cascades.expressions.FullUnorderedScanExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.LogicalUnionExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.PrimaryScanExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.RecursiveExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.RelationalExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.RelationalExpressionVisitorWithDefaults;
import com.apple.foundationdb.record.query.plan.cascades.expressions.SelectExpression;
Expand Down Expand Up @@ -125,7 +126,8 @@ public Set<String> evaluateAtExpression(@Nonnull RelationalExpression expression
expression instanceof RecordQueryUnorderedUnionPlan ||
expression instanceof RecordQueryIntersectionPlan ||
expression instanceof LogicalUnionExpression ||
expression instanceof SelectExpression) {
expression instanceof SelectExpression ||
expression instanceof RecursiveExpression) {
final Set<String> union = new HashSet<>();
for (Set<String> childResulSet : childResults) {
union.addAll(childResulSet);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* ImplementRecursiveRule.java
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2015-2019 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.apple.foundationdb.record.query.plan.cascades.rules;

import com.apple.foundationdb.annotation.API;
import com.apple.foundationdb.record.logging.KeyValueLogMessage;
import com.apple.foundationdb.record.query.plan.cascades.CascadesRule;
import com.apple.foundationdb.record.query.plan.cascades.CascadesRuleCall;
import com.apple.foundationdb.record.query.plan.cascades.PlanPartition;
import com.apple.foundationdb.record.query.plan.cascades.Quantifier;
import com.apple.foundationdb.record.query.plan.cascades.Reference;
import com.apple.foundationdb.record.query.plan.cascades.debug.Debugger;
import com.apple.foundationdb.record.query.plan.cascades.expressions.RecursiveExpression;
import com.apple.foundationdb.record.query.plan.cascades.matching.structure.BindingMatcher;
import com.apple.foundationdb.record.query.plan.plans.RecordQueryRecursivePlan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;

import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.ListMatcher.exactly;
import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.MultiMatcher.all;
import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.QuantifierMatchers.anyQuantifierOverRef;
import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.ReferenceMatchers.anyPlanPartition;
import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.ReferenceMatchers.planPartitions;
import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.ReferenceMatchers.rollUp;
import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.RelationalExpressionMatchers.canBeImplemented;
import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.RelationalExpressionMatchers.recursiveExpression;

/**
* A rule that implements an existential nested loop join of its (already implemented) children.
*/
@API(API.Status.EXPERIMENTAL)
public class ImplementRecursiveRule extends CascadesRule<RecursiveExpression> {
@Nonnull
private static final Logger logger = LoggerFactory.getLogger(ImplementRecursiveRule.class);

@Nonnull
private static final BindingMatcher<PlanPartition> rootPlanPartitionsMatcher = anyPlanPartition();

@Nonnull
private static final BindingMatcher<Reference> rootReferenceMatcher = planPartitions(rollUp(all(rootPlanPartitionsMatcher)));
@Nonnull
private static final BindingMatcher<Quantifier> rootQuantifierMatcher = anyQuantifierOverRef(rootReferenceMatcher);
@Nonnull
private static final BindingMatcher<PlanPartition> childPlanPartitionsMatcher = anyPlanPartition();

@Nonnull
private static final BindingMatcher<Reference> childReferenceMatcher = planPartitions(rollUp(all(childPlanPartitionsMatcher)));
@Nonnull
private static final BindingMatcher<Quantifier> childQuantifierMatcher = anyQuantifierOverRef(childReferenceMatcher);
@Nonnull
private static final BindingMatcher<RecursiveExpression> root =
recursiveExpression(exactly(rootQuantifierMatcher, childQuantifierMatcher)).where(canBeImplemented());

public ImplementRecursiveRule() {
super(root);
}

@Override
public void onMatch(@Nonnull final CascadesRuleCall call) {
final var bindings = call.getBindings();
final var recursiveExpression = bindings.get(root);
Debugger.withDebugger(debugger -> logger.debug(KeyValueLogMessage.of("matched RecursiveExpression", "legs", recursiveExpression.getQuantifiers().size())));

final var rootQuantifier = bindings.get(rootQuantifierMatcher);
final var childQuantifier = bindings.get(childQuantifierMatcher);

final var rootReference = bindings.get(rootReferenceMatcher);
final var childReference = bindings.get(childReferenceMatcher);

final var rootPartition = bindings.get(rootPlanPartitionsMatcher);
final var childPartition = bindings.get(childPlanPartitionsMatcher);

final var rootAlias = rootQuantifier.getAlias();
final var childAlias = childQuantifier.getAlias();

var rootRef = call.memoizeMemberPlans(rootReference, rootPartition.getPlans());
final var newRootQuantifier = Quantifier.physicalBuilder().withAlias(rootAlias).build(rootRef);

var childRef = call.memoizeMemberPlans(childReference, childPartition.getPlans());
final var newChildQuantifier = Quantifier.physicalBuilder().withAlias(childAlias).build(childRef);

call.yieldExpression(new RecordQueryRecursivePlan(newRootQuantifier, newChildQuantifier, recursiveExpression.getResultValue(), true));
}
}
Loading

0 comments on commit 1b370ed

Please sign in to comment.