toPlanPartitions() {
+ return propertiesMap.toPlanPartitions();
}
@Nullable
- public U acceptPropertyVisitor(@Nonnull ExpressionProperty property) {
- if (property.shouldVisit(this)) {
+ public U acceptPropertyVisitor(@Nonnull SimpleExpressionVisitor simpleExpressionVisitor) {
+ if (simpleExpressionVisitor.shouldVisit(this)) {
final List memberResults = new ArrayList<>(members.size());
for (RelationalExpression member : members) {
- final U result = property.shouldVisit(member) ? property.visit(member) : null;
+ final U result = simpleExpressionVisitor.shouldVisit(member) ? simpleExpressionVisitor.visit(member) : null;
if (result == null) {
return null;
}
memberResults.add(result);
}
- return property.evaluateAtRef(this, memberResults);
+ return simpleExpressionVisitor.evaluateAtRef(this, memberResults);
}
return null;
}
@@ -522,7 +525,7 @@ public boolean addPartialMatchForCandidate(final MatchCandidate candidate, final
*/
@Nonnull
public String show(final boolean renderSingleGroups) {
- return PlannerGraphProperty.show(renderSingleGroups, this);
+ return PlannerGraphVisitor.show(renderSingleGroups, this);
}
public static boolean isMemoizedExpression(@Nonnull final RelationalExpression expression,
diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/References.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/References.java
index acf6d3553c..1979d2ee86 100644
--- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/References.java
+++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/References.java
@@ -25,7 +25,6 @@
import com.apple.foundationdb.record.query.plan.cascades.debug.Debugger;
import com.apple.foundationdb.record.query.plan.cascades.expressions.RelationalExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.RelationalExpressionWithChildren;
-import com.apple.foundationdb.record.query.plan.cascades.properties.ReferencesAndDependenciesProperty;
import com.apple.foundationdb.record.query.plan.cascades.values.translation.TranslationMap;
import com.google.common.base.Verify;
import com.google.common.collect.ImmutableList;
@@ -36,6 +35,8 @@
import java.util.Objects;
import java.util.Set;
+import static com.apple.foundationdb.record.query.plan.cascades.properties.ReferencesAndDependenciesProperty.referencesAndDependencies;
+
/**
* Utility methods for {@link Reference}s.
*/
@@ -51,7 +52,7 @@ public static List extends Reference> translateCorrelations(@Nonnull final Lis
return ImmutableList.of();
}
- final var partialOrder = ReferencesAndDependenciesProperty.evaluate(refs);
+ final var partialOrder = referencesAndDependencies().evaluate(refs);
final var references =
TopologicalSort.anyTopologicalOrderPermutation(partialOrder)
diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/SimpleExpressionVisitor.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/SimpleExpressionVisitor.java
new file mode 100644
index 0000000000..ddb44ce76f
--- /dev/null
+++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/SimpleExpressionVisitor.java
@@ -0,0 +1,176 @@
+/*
+ * SimpleExpressionVisitor.java
+ *
+ * This source file is part of the FoundationDB open source project
+ *
+ * Copyright 2015-2025 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;
+
+import com.apple.foundationdb.annotation.API;
+import com.apple.foundationdb.annotation.SpotBugsSuppressWarnings;
+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.matching.structure.BindingMatcher;
+import com.google.common.collect.Lists;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * An interface for certain Cascades-style properties, which are measurable features of an expression other than the
+ * structure of the expression tree. In particular, a {@code SimpleExpressionVisitor} is a property that depends on (much of)
+ * the contents of the subtree rooted at the expression on which it is evaluated, rather than just a finite depth set
+ * of paths as a {@link BindingMatcher} would. For example,
+ * the sort order and set of record types produced by a
+ * {@link RelationalExpression} could be a
+ * {@code SimpleExpressionVisitor}.
+ *
+ *
+ * To avoid littering {@link RelationalExpression} classes with methods for various properties, properties are implemented
+ * using a variant of the hierarchical visitor pattern over a DAG of {@link RelationalExpression}s, {@link Quantifier}s,
+ * and {@link Reference}s.
+ * A property can be evaluated against an expression tree by having the visitor traverse a DAG of heterogeneous objects
+ * where expressions are said to own quantifiers which range over expression references which then contain expressions
+ * again. Shared subgraphs are visited multiple times. If desired, the caller must ensure that a subgraph is not
+ * visited more than once.
+ *
+ *
+ *
+ * Note that the methods {@link #evaluateAtExpression}, {@link #evaluateAtQuantifier}, and {@link #evaluateAtRef} are handed the
+ * results of the visitor evaluated at their owned quantifiers, references, and members respectively. Since most properties
+ * are easy to describe as a recursion with depth one, this makes properties easier to read and write.
+ *
+ *
+ *
+ * Note that the default implementations provided in this interface walk the entire DAG, i.e. all {@link #shouldVisit}
+ * methods return {@code true}. That implies that properties have been computed for sub-structures of the graph when
+ * {@code evaluateAtXXX()} is called and therefore are not {@code null}. That means that all such arguments can be
+ * assumed to be non-nullable even though parameters are annotated as {@link Nullable}. On the other hand, if the
+ * implementor also overrides {@code shouldVisit} to return {@code false}, arguments to {@code evaluateAtXXX} may become
+ * nullable or contain {@code null}s if they are collections and should be dealt with gracefully.
+ *
+ *
+ * @param the result type of the property
+ */
+@API(API.Status.EXPERIMENTAL)
+public interface SimpleExpressionVisitor extends RelationalExpressionVisitorWithDefaults {
+ /**
+ * Return whether the property should visit the subgraph rooted at the given expression.
+ * Called on nodes in the expression graph in visit pre-order of the depth-first traversal of the graph.
+ * That is, as each node is visited for the first time, {@code shouldVisit()} is called on that node.
+ * If {@code shouldVisit()} returns {@code false}, then {@link #evaluateAtExpression(RelationalExpression, List)} will
+ * not be called on the given expression.
+ * @param expression the planner expression to visit
+ * @return {@code true} if the children of {@code expression} should be visited and {@code false} if they should not be visited
+ */
+ @SuppressWarnings("unused")
+ default boolean shouldVisit(@Nonnull RelationalExpression expression) {
+ return true;
+ }
+
+ /**
+ * Return whether the property should visit the given reference and, transitively, the members of the reference.
+ * Called on expression references in the graph in visit pre-order of the depth-first traversal of the graph.
+ * That is, as a reference is visited, {@code shouldVisit()} is called on that reference.
+ * If {@code shouldVisit()} returns {@code false}, then {@link #evaluateAtRef(Reference, List)} will
+ * not be called on the given reference.
+ * @param ref the expression reference to visit
+ * @return {@code true} if the members of {@code ref} should be visited and {@code false} if they should not be visited
+ */
+ @SuppressWarnings("unused")
+ default boolean shouldVisit(@Nonnull Reference ref) {
+ return true;
+ }
+
+ /**
+ * Return whether the property should visit the given quantifier and the references that the quantifier
+ * ranges over.
+ * Called on quantifiers in the graph in visit pre-order of the depth-first traversal of the graph.
+ * That is, as a quantifier is visited, {@code shouldVisit()} is called on that quantifier.
+ * If {@code shouldVisit()} returns {@code false}, then {@link #evaluateAtQuantifier(Quantifier, Object)} will
+ * not be called on the given quantifier.
+ * @param quantifier the quantifier to visit
+ * @return {@code true} if the expression reference {@code quantifier} ranges over should be visited and
+ * {@code false} if it should not be visited
+ */
+ @SuppressWarnings("unused")
+ default boolean shouldVisit(@Nonnull Quantifier quantifier) {
+ return true;
+ }
+
+ /**
+ * Evaluate the property at the given expression, using the results of evaluating the property at its children.
+ * Called on nodes in the graph in visit post-order of the depth-first traversal of the graph.
+ * That is, as each expression is visited (after all of its children have been visited, if applicable),
+ * {@code evaluateAtExpression()} is called on that expression.
+ * @param expression the cursor to visit
+ * @param childResults the results of the property evaluated at the children of {@code expression}
+ * @return the value of property at the given expression
+ */
+ @Nonnull
+ T evaluateAtExpression(@Nonnull RelationalExpression expression, @Nonnull List childResults);
+
+ /**
+ * Evaluate the property at the given reference, using the results of evaluating the property at its members.
+ * Called on nodes in the graph in visit post-order of the depth-first traversal of the graph.
+ * That is, as each reference is visited (after all of its members have been visited, if applicable),
+ * {@code evaluateAtRef()} is called on that reference.
+ * @param ref the expression reference to visit
+ * @param memberResults the results of the property evaluated at the members of {@code ref}
+ * @return the value of property at the given reference
+ */
+ @Nonnull
+ T evaluateAtRef(@Nonnull Reference ref, @Nonnull List memberResults);
+
+ /**
+ * Evaluate the property at the given quantifier, using the result of evaluating the property at the
+ * reference the quantifier ranges over.
+ * Called on quantifiers in the graph in visit post-order of the depth-first traversal of the graph.
+ * That is, as each quantifier is visited (after the expression reference it ranges over has been visited, if applicable),
+ * {@code evaluateAtQuantifier()} is called on that quantifier.
+ * @param quantifier the quantifier to visit
+ * @param rangesOverResult the result of the property evaluated at the {@link Reference} {@code quantifier}
+ * ranges over
+ * @return the value of property at the given quantifier
+ */
+ @Nonnull
+ @SuppressWarnings("unused")
+ @SpotBugsSuppressWarnings("NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE")
+ default T evaluateAtQuantifier(@Nonnull final Quantifier quantifier, @Nullable T rangesOverResult) {
+ // since we visit the expression reference under the quantifier, we can insist that rangesOverResult is never null
+ return Objects.requireNonNull(rangesOverResult);
+ }
+
+ @Nonnull
+ @Override
+ default T visitDefault(@Nonnull final RelationalExpression relationalExpression) {
+ final var quantifierResults = visitQuantifiers(relationalExpression);
+ return evaluateAtExpression(relationalExpression, quantifierResults);
+ }
+
+ @Nonnull
+ default List visitQuantifiers(@Nonnull final RelationalExpression relationalExpression) {
+ final List extends Quantifier> quantifiers = relationalExpression.getQuantifiers();
+ final var quantifierResults = Lists.newArrayListWithCapacity(quantifiers.size());
+ for (final Quantifier quantifier : quantifiers) {
+ quantifierResults.add(quantifier.acceptPropertyVisitor(this));
+ }
+ return quantifierResults;
+ }
+}
diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/explain/AbstractPlannerGraph.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/explain/AbstractPlannerGraph.java
index e442e3ad1e..9f62dbbdbc 100644
--- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/explain/AbstractPlannerGraph.java
+++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/explain/AbstractPlannerGraph.java
@@ -42,7 +42,7 @@
import java.util.function.Supplier;
/**
- * The planner graph class. Objects of this class are computed by {@link PlannerGraphProperty},
+ * The planner graph class. Objects of this class are produced by {@link PlannerGraphVisitor},
* i.e., they get computed by walking a {@link RelationalExpression} DAG.
*
* Once computed, the property is immutable.
diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/explain/ExplainPlannerGraphRewritable.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/explain/ExplainPlannerGraphRewritable.java
index c7e2975366..4685c01520 100644
--- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/explain/ExplainPlannerGraphRewritable.java
+++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/explain/ExplainPlannerGraphRewritable.java
@@ -28,9 +28,9 @@
/**
* Interface to allow {@link RelationalExpression}s to rewrite their own
* explain graph representation.
- *
+ *
* This particular class allows {@link RelationalExpression}s to specify
- * how a {@link PlannerGraph} is modified when we compute the {@link PlannerGraphProperty} when explaining a plan
+ * how a {@link PlannerGraph} is modified when we use the {@link PlannerGraphVisitor} when explaining a plan
* to an end-user.
*/
public interface ExplainPlannerGraphRewritable {
diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/explain/InternalPlannerGraphRewritable.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/explain/InternalPlannerGraphRewritable.java
index 3a24e3b440..3ebb334c4a 100644
--- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/explain/InternalPlannerGraphRewritable.java
+++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/explain/InternalPlannerGraphRewritable.java
@@ -28,9 +28,9 @@
/**
* Interface to allow {@link RelationalExpression}s to rewrite their own
* internal graph representation. Please see {@link PlannerGraphRewritable} for a more comprehensive explanation.
- *
+ *
* This particular class allows {@link RelationalExpression}s to specify
- * how a {@link PlannerGraph} is modified when we compute the {@link PlannerGraphProperty} for developers working
+ * how a {@link PlannerGraph} is modified when we use the {@link PlannerGraphVisitor} for developers working
* on the query planner.
* @see PlannerGraphRewritable for a more comprehensive explanation of graph rewriting.
*/
diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/explain/PlannerGraphRewritable.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/explain/PlannerGraphRewritable.java
index f216382318..2cefbcbca0 100644
--- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/explain/PlannerGraphRewritable.java
+++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/explain/PlannerGraphRewritable.java
@@ -31,7 +31,7 @@
*
* This interface allows a {@link RelationalExpression} to create/modify
* low level planner graph structures while a planner graph is created through an expression walk in
- * {@link PlannerGraphProperty}. Such a rewrite can be useful if the standard representation is confusing, misleading,
+ * {@link PlannerGraphVisitor}. Such a rewrite can be useful if the standard representation is confusing, misleading,
* or just too complicated. For instance, {@link com.apple.foundationdb.record.query.plan.plans.RecordQueryPlanWithIndex}
* additionally creates a separate node underneath the actual plan operator to show the index as a record producer.
* The nature of the rewrite is highly operator-specific, therefore, the rewrite is not part of the visitor or some
diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/explain/PlannerGraphProperty.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/explain/PlannerGraphVisitor.java
similarity index 96%
rename from fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/explain/PlannerGraphProperty.java
rename to fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/explain/PlannerGraphVisitor.java
index 7f522cba77..f0095a840d 100644
--- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/explain/PlannerGraphProperty.java
+++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/explain/PlannerGraphVisitor.java
@@ -1,9 +1,9 @@
/*
- * PlannerGraphProperty.java
+ * PlannerGraphVisitor.java
*
* This source file is part of the FoundationDB open source project
*
- * Copyright 2015-2020 Apple Inc. and the FoundationDB project authors
+ * Copyright 2015-2025 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.
@@ -20,11 +20,11 @@
package com.apple.foundationdb.record.query.plan.cascades.explain;
-import com.apple.foundationdb.record.query.plan.cascades.ExpressionProperty;
-import com.apple.foundationdb.record.query.plan.cascades.Reference;
-import com.apple.foundationdb.record.query.plan.cascades.Traversal;
import com.apple.foundationdb.record.query.plan.cascades.MatchCandidate;
import com.apple.foundationdb.record.query.plan.cascades.PartialMatch;
+import com.apple.foundationdb.record.query.plan.cascades.Reference;
+import com.apple.foundationdb.record.query.plan.cascades.SimpleExpressionVisitor;
+import com.apple.foundationdb.record.query.plan.cascades.Traversal;
import com.apple.foundationdb.record.query.plan.cascades.debug.BrowserHelper;
import com.apple.foundationdb.record.query.plan.cascades.debug.Debugger;
import com.apple.foundationdb.record.query.plan.cascades.explain.GraphExporter.Cluster;
@@ -33,7 +33,6 @@
import com.apple.foundationdb.record.query.plan.cascades.explain.PlannerGraph.Node;
import com.apple.foundationdb.record.query.plan.cascades.explain.PlannerGraph.PartialMatchEdge;
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.plans.RecordQueryPlan;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
@@ -64,7 +63,7 @@
* Class to hold a graph for explain, optimization, and rewrite purposes.
*/
@SuppressWarnings({"UnstableApiUsage"})
-public class PlannerGraphProperty implements ExpressionProperty, RelationalExpressionVisitorWithDefaults {
+public class PlannerGraphVisitor implements SimpleExpressionVisitor {
public static final int EMPTY_FLAGS = 0x0000;
@@ -140,7 +139,7 @@ public static String show(final boolean renderSingleGroups, @Nonnull final Refer
@Nonnull
public static String show(final int flags, @Nonnull final RelationalExpression relationalExpression) {
final PlannerGraph plannerGraph =
- Objects.requireNonNull(relationalExpression.acceptPropertyVisitor(new PlannerGraphProperty(flags)));
+ Objects.requireNonNull(relationalExpression.acceptPropertyVisitor(new PlannerGraphVisitor(flags)));
final String dotString = exportToDot(plannerGraph);
return show(dotString);
}
@@ -155,7 +154,7 @@ public static String show(final int flags, @Nonnull final RelationalExpression r
@Nonnull
public static String show(final int flags, @Nonnull final Reference rootReference) {
final PlannerGraph plannerGraph =
- Objects.requireNonNull(rootReference.acceptPropertyVisitor(new PlannerGraphProperty(flags)));
+ Objects.requireNonNull(rootReference.acceptPropertyVisitor(new PlannerGraphVisitor(flags)));
final String dotString = exportToDot(plannerGraph);
return show(dotString);
}
@@ -262,7 +261,7 @@ public static String exportToDot(@Nonnull final AbstractPlannerGraph
(network, nodes) -> {
final ImmutableList.Builder> clusterBuilder = ImmutableList.builder();
clusterBuilder.addAll(clustersForGroups(plannerGraph.getNetwork(), queryPlannerNodes));
- clusterBuilder.addAll(clusteringFunction.apply(PlannerGraphProperty::clustersForGroups));
+ clusterBuilder.addAll(clusteringFunction.apply(PlannerGraphVisitor::clustersForGroups));
return clusterBuilder.build();
});
// export as string
@@ -403,16 +402,16 @@ private static GraphExporter createGmlExporter(@Nonnull final Map the type of the evaluated property
* @return the result of evaluating the property on the subtree rooted at this expression
*/
@Nullable
- default U acceptPropertyVisitor(@Nonnull ExpressionProperty visitor) {
- if (visitor.shouldVisit(this)) {
- return visitor.visit(this);
+ default U acceptPropertyVisitor(@Nonnull SimpleExpressionVisitor simpleExpressionVisitor) {
+ if (simpleExpressionVisitor.shouldVisit(this)) {
+ return simpleExpressionVisitor.visit(this);
}
return null;
}
@@ -852,6 +853,6 @@ default U acceptPropertyVisitor(@Nonnull ExpressionProperty visitor) {
*/
@Nonnull
default String show(final boolean renderSingleGroups) {
- return PlannerGraphProperty.show(renderSingleGroups, this);
+ return PlannerGraphVisitor.show(renderSingleGroups, this);
}
}
diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/matching/structure/PlanPartitionMatchers.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/matching/structure/PlanPartitionMatchers.java
new file mode 100644
index 0000000000..0e333c4562
--- /dev/null
+++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/matching/structure/PlanPartitionMatchers.java
@@ -0,0 +1,93 @@
+/*
+ * PlanPartitionMatchers.java
+ *
+ * This source file is part of the FoundationDB open source project
+ *
+ * Copyright 2015-2025 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.matching.structure;
+
+import com.apple.foundationdb.record.query.plan.cascades.ExpressionProperty;
+import com.apple.foundationdb.record.query.plan.cascades.PlanPartition;
+import com.apple.foundationdb.record.query.plan.cascades.PlanPartitions;
+import com.apple.foundationdb.record.query.plan.cascades.Reference;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+import javax.annotation.Nonnull;
+import java.util.Collection;
+import java.util.Set;
+import java.util.function.Predicate;
+
+import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.TypedMatcher.typed;
+
+public class PlanPartitionMatchers {
+ private PlanPartitionMatchers() {
+ // do not instantiate
+ }
+
+ @Nonnull
+ @SuppressWarnings("unchecked")
+ public static BindingMatcher planPartitions(@Nonnull final BindingMatcher extends Iterable extends PlanPartition>> downstream) {
+ return TypedMatcherWithExtractAndDownstream.typedWithDownstream((Class)(Class>)Reference.class,
+ Extractor.of(Reference::toPlanPartitions, name -> "planPartitions(" + name + ")"),
+ downstream);
+ }
+
+ @Nonnull
+ @SuppressWarnings("unchecked")
+ public static BindingMatcher> filterPartition(@Nonnull final Predicate predicate,
+ @Nonnull final BindingMatcher extends Iterable extends PlanPartition>> downstream) {
+ return TypedMatcherWithExtractAndDownstream.typedWithDownstream((Class>)(Class>)Collection.class,
+ Extractor.of(planPartitions ->
+ planPartitions.stream()
+ .filter(predicate)
+ .collect(ImmutableList.toImmutableList()),
+ name -> "filtered planPartitions(" + name + ")"),
+ downstream);
+ }
+
+ @Nonnull
+ public static BindingMatcher> rollUpPartitions(@Nonnull final BindingMatcher extends Iterable extends PlanPartition>> downstream) {
+ return rollUpPartitionsTo(downstream, ImmutableSet.of());
+ }
+
+ @Nonnull
+ public static BindingMatcher> rollUpPartitionsTo(@Nonnull final BindingMatcher extends Iterable extends PlanPartition>> downstream,
+ @Nonnull final ExpressionProperty> interestingProperty) {
+ return rollUpPartitionsTo(downstream, ImmutableSet.of(interestingProperty));
+ }
+
+ @Nonnull
+ @SuppressWarnings("unchecked")
+ public static BindingMatcher> rollUpPartitionsTo(@Nonnull final BindingMatcher extends Iterable extends PlanPartition>> downstream,
+ @Nonnull final Set> interestingProperties) {
+ return TypedMatcherWithExtractAndDownstream.typedWithDownstream((Class>)(Class>)Collection.class,
+ Extractor.of(partitions -> PlanPartitions.rollUpTo(partitions, interestingProperties),
+ name -> "rolled up planPartitions(" + name + ")"),
+ downstream);
+ }
+
+ @Nonnull
+ public static BindingMatcher anyPlanPartition() {
+ return typed(PlanPartition.class);
+ }
+
+ @Nonnull
+ public static BindingMatcher planPartitionWhere(@Nonnull Predicate predicate) {
+ return TypedMatcherWithPredicate.typedMatcherWithPredicate(PlanPartition.class, predicate);
+ }
+}
diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/matching/structure/ReferenceMatchers.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/matching/structure/ReferenceMatchers.java
index 5bad5aee6f..0ac85f1036 100644
--- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/matching/structure/ReferenceMatchers.java
+++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/matching/structure/ReferenceMatchers.java
@@ -22,17 +22,10 @@
import com.apple.foundationdb.annotation.API;
import com.apple.foundationdb.record.query.plan.cascades.Reference;
-import com.apple.foundationdb.record.query.plan.cascades.PlanPartition;
-import com.apple.foundationdb.record.query.plan.cascades.PlanProperty;
import com.apple.foundationdb.record.query.plan.cascades.expressions.RelationalExpression;
import com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
import javax.annotation.Nonnull;
-import java.util.Collection;
-import java.util.Set;
-import java.util.function.Predicate;
import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.MultiMatcher.all;
import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.TypedMatcher.typed;
@@ -73,46 +66,4 @@ public static BindingMatch
Extractor.of(Reference::getMembers, name -> "members(" + name + ")"),
downstream);
}
-
- @Nonnull
- @SuppressWarnings("unchecked")
- public static BindingMatcher planPartitions(@Nonnull final BindingMatcher extends Iterable extends PlanPartition>> downstream) {
- return TypedMatcherWithExtractAndDownstream.typedWithDownstream((Class)(Class>)Reference.class,
- Extractor.of(Reference::getPlanPartitions, name -> "planPartitions(" + name + ")"),
- downstream);
- }
-
- @Nonnull
- @SuppressWarnings("unchecked")
- public static BindingMatcher> where(@Nonnull final Predicate predicate,
- @Nonnull final BindingMatcher extends Iterable extends PlanPartition>> downstream) {
- return TypedMatcherWithExtractAndDownstream.typedWithDownstream((Class>)(Class>)Collection.class,
- Extractor.of(planPartitions -> planPartitions.stream().filter(predicate).collect(ImmutableList.toImmutableList()), name -> "filtered planPartitions(" + name + ")"),
- downstream);
- }
-
- public static BindingMatcher> rollUp(@Nonnull final BindingMatcher extends Iterable extends PlanPartition>> downstream) {
- return rollUpTo(downstream, ImmutableSet.of());
- }
-
- public static BindingMatcher> rollUpTo(@Nonnull final BindingMatcher extends Iterable extends PlanPartition>> downstream, @Nonnull final PlanProperty> interestingAttribute) {
- return rollUpTo(downstream, ImmutableSet.of(interestingAttribute));
- }
-
- @SuppressWarnings("unchecked")
- public static BindingMatcher> rollUpTo(@Nonnull final BindingMatcher extends Iterable extends PlanPartition>> downstream, @Nonnull final Set> interestingAttributes) {
- return TypedMatcherWithExtractAndDownstream.typedWithDownstream((Class>)(Class>)Collection.class,
- Extractor.of(planPartitions -> PlanPartition.rollUpTo(planPartitions, interestingAttributes), name -> "rolled up planPartitions(" + name + ")"),
- downstream);
- }
-
- @Nonnull
- public static BindingMatcher anyPlanPartition() {
- return typed(PlanPartition.class);
- }
-
- @Nonnull
- public static BindingMatcher planPartitionWhere(@Nonnull Predicate predicate) {
- return TypedMatcherWithPredicate.typedMatcherWithPredicate(PlanPartition.class, predicate);
- }
}
diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/CompatibleTypeEvolutionPredicate.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/CompatibleTypeEvolutionPredicate.java
index 8896be18d1..49e9470f2d 100644
--- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/CompatibleTypeEvolutionPredicate.java
+++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/predicates/CompatibleTypeEvolutionPredicate.java
@@ -37,18 +37,17 @@
import com.apple.foundationdb.record.provider.foundationdb.FDBRecordStoreBase;
import com.apple.foundationdb.record.query.plan.cascades.AliasMap;
import com.apple.foundationdb.record.query.plan.cascades.BooleanWithConstraint;
-import com.apple.foundationdb.record.query.plan.cascades.values.FirstOrDefaultStreamingValue;
-import com.apple.foundationdb.record.query.plan.explain.ExplainTokens;
-import com.apple.foundationdb.record.query.plan.explain.ExplainTokensWithPrecedence;
import com.apple.foundationdb.record.query.plan.cascades.ValueEquivalence;
-import com.apple.foundationdb.record.query.plan.cascades.properties.DerivationsProperty;
import com.apple.foundationdb.record.query.plan.cascades.typing.Type;
import com.apple.foundationdb.record.query.plan.cascades.values.FieldValue;
+import com.apple.foundationdb.record.query.plan.cascades.values.FirstOrDefaultStreamingValue;
import com.apple.foundationdb.record.query.plan.cascades.values.FirstOrDefaultValue;
import com.apple.foundationdb.record.query.plan.cascades.values.LeafValue;
import com.apple.foundationdb.record.query.plan.cascades.values.QueriedValue;
import com.apple.foundationdb.record.query.plan.cascades.values.RecordConstructorValue;
import com.apple.foundationdb.record.query.plan.cascades.values.Value;
+import com.apple.foundationdb.record.query.plan.explain.ExplainTokens;
+import com.apple.foundationdb.record.query.plan.explain.ExplainTokensWithPrecedence;
import com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan;
import com.apple.foundationdb.record.util.TrieNode;
import com.google.auto.service.AutoService;
@@ -67,6 +66,8 @@
import java.util.function.Function;
import java.util.function.Supplier;
+import static com.apple.foundationdb.record.query.plan.cascades.properties.DerivationsProperty.derivations;
+
/**
* A predicate to be used as part of a {@link com.apple.foundationdb.record.query.plan.QueryPlanConstraint}
* which can determine if a given plan can be executed under the types currently defined in the schema.
@@ -363,7 +364,7 @@ public static boolean isAccessCompatibleWithCurrentType(@Nonnull final FieldAcce
@Nonnull
public static CompatibleTypeEvolutionPredicate fromPlan(@Nonnull final RecordQueryPlan plannedPlan) {
- final var derivations = DerivationsProperty.evaluateDerivations(plannedPlan);
+ final var derivations = derivations().evaluate(plannedPlan);
final var simplifiedLocalValues = derivations.simplifyLocalValues();
final var fieldAccesses = CompatibleTypeEvolutionPredicate.computeFieldAccesses(simplifiedLocalValues);
return new CompatibleTypeEvolutionPredicate(fieldAccesses);
diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/properties/CardinalitiesProperty.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/properties/CardinalitiesProperty.java
index 8c835f70d3..aac1cc39ca 100644
--- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/properties/CardinalitiesProperty.java
+++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/properties/CardinalitiesProperty.java
@@ -26,8 +26,8 @@
import com.apple.foundationdb.record.query.plan.bitmap.ComposedBitmapIndexQueryPlan;
import com.apple.foundationdb.record.query.plan.cascades.AliasMap;
import com.apple.foundationdb.record.query.plan.cascades.ExpressionProperty;
-import com.apple.foundationdb.record.query.plan.cascades.Reference;
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.ValueIndexScanMatchCandidate;
import com.apple.foundationdb.record.query.plan.cascades.WithPrimaryKeyMatchCandidate;
import com.apple.foundationdb.record.query.plan.cascades.expressions.DeleteExpression;
@@ -35,9 +35,6 @@
import com.apple.foundationdb.record.query.plan.cascades.expressions.FullUnorderedScanExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.GroupByExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.InsertExpression;
-import com.apple.foundationdb.record.query.plan.cascades.expressions.RecursiveUnionExpression;
-import com.apple.foundationdb.record.query.plan.cascades.expressions.TableFunctionExpression;
-import com.apple.foundationdb.record.query.plan.cascades.expressions.TempTableInsertExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.LogicalDistinctExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.LogicalFilterExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.LogicalIntersectionExpression;
@@ -48,8 +45,12 @@
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.RecursiveUnionExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.RelationalExpression;
+import com.apple.foundationdb.record.query.plan.cascades.expressions.RelationalExpressionVisitor;
import com.apple.foundationdb.record.query.plan.cascades.expressions.SelectExpression;
+import com.apple.foundationdb.record.query.plan.cascades.expressions.TableFunctionExpression;
+import com.apple.foundationdb.record.query.plan.cascades.expressions.TempTableInsertExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.TempTableScanExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.UpdateExpression;
import com.apple.foundationdb.record.query.plan.cascades.values.LiteralValue;
@@ -75,20 +76,18 @@
import com.apple.foundationdb.record.query.plan.plans.RecordQueryInValuesJoinPlan;
import com.apple.foundationdb.record.query.plan.plans.RecordQueryIndexPlan;
import com.apple.foundationdb.record.query.plan.plans.RecordQueryInsertPlan;
-import com.apple.foundationdb.record.query.plan.plans.RecordQueryRecursiveUnionPlan;
-import com.apple.foundationdb.record.query.plan.plans.RecordQueryTableFunctionPlan;
-import com.apple.foundationdb.record.query.plan.plans.TempTableScanPlan;
-import com.apple.foundationdb.record.query.plan.plans.TempTableInsertPlan;
import com.apple.foundationdb.record.query.plan.plans.RecordQueryIntersectionOnKeyExpressionPlan;
import com.apple.foundationdb.record.query.plan.plans.RecordQueryIntersectionOnValuesPlan;
import com.apple.foundationdb.record.query.plan.plans.RecordQueryLoadByKeysPlan;
import com.apple.foundationdb.record.query.plan.plans.RecordQueryMapPlan;
import com.apple.foundationdb.record.query.plan.plans.RecordQueryPredicatesFilterPlan;
import com.apple.foundationdb.record.query.plan.plans.RecordQueryRangePlan;
+import com.apple.foundationdb.record.query.plan.plans.RecordQueryRecursiveUnionPlan;
import com.apple.foundationdb.record.query.plan.plans.RecordQueryScanPlan;
import com.apple.foundationdb.record.query.plan.plans.RecordQueryScoreForRankPlan;
import com.apple.foundationdb.record.query.plan.plans.RecordQuerySelectorPlan;
import com.apple.foundationdb.record.query.plan.plans.RecordQueryStreamingAggregationPlan;
+import com.apple.foundationdb.record.query.plan.plans.RecordQueryTableFunctionPlan;
import com.apple.foundationdb.record.query.plan.plans.RecordQueryTextIndexPlan;
import com.apple.foundationdb.record.query.plan.plans.RecordQueryTypeFilterPlan;
import com.apple.foundationdb.record.query.plan.plans.RecordQueryUnionOnKeyExpressionPlan;
@@ -97,6 +96,8 @@
import com.apple.foundationdb.record.query.plan.plans.RecordQueryUnorderedPrimaryKeyDistinctPlan;
import com.apple.foundationdb.record.query.plan.plans.RecordQueryUnorderedUnionPlan;
import com.apple.foundationdb.record.query.plan.plans.RecordQueryUpdatePlan;
+import com.apple.foundationdb.record.query.plan.plans.TempTableInsertPlan;
+import com.apple.foundationdb.record.query.plan.plans.TempTableScanPlan;
import com.apple.foundationdb.record.query.plan.sorting.RecordQueryDamPlan;
import com.apple.foundationdb.record.query.plan.sorting.RecordQuerySortPlan;
import com.google.common.base.Preconditions;
@@ -106,9 +107,7 @@
import com.google.common.collect.Lists;
import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
import java.util.List;
-import java.util.Objects;
import java.util.OptionalLong;
/**
@@ -120,732 +119,737 @@
* constrained from the data flow graph given to the property.
*/
public class CardinalitiesProperty implements ExpressionProperty {
+ private static final CardinalitiesProperty CARDINALITIES = new CardinalitiesProperty();
- private static final CardinalitiesProperty INSTANCE = new CardinalitiesProperty();
-
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryUpdatePlan(@Nonnull final RecordQueryUpdatePlan updatePlan) {
- return fromChild(updatePlan);
+ private CardinalitiesProperty() {
+ // prevent outside instantiation
}
@Nonnull
@Override
- public Cardinalities visitRecordQueryPredicatesFilterPlan(@Nonnull final RecordQueryPredicatesFilterPlan predicatesFilterPlan) {
- final var cardinalitiesFromChild = fromChild(predicatesFilterPlan);
- return new Cardinalities(Cardinality.ofCardinality(0L), cardinalitiesFromChild.getMaxCardinality());
+ public RelationalExpressionVisitor createVisitor() {
+ return new CardinalitiesVisitor();
}
- @Nonnull
@Override
- public Cardinalities visitRecordQueryLoadByKeysPlan(@Nonnull final RecordQueryLoadByKeysPlan element) {
- final var keysSource = element.getKeysSource();
- if (keysSource.maxCardinality() == QueryPlan.UNKNOWN_MAX_CARDINALITY) {
- return Cardinalities.unknownCardinalities;
- }
-
- return new Cardinalities(Cardinality.ofCardinality(0L), Cardinality.ofCardinality(keysSource.maxCardinality()));
+ public String toString() {
+ return getClass().getSimpleName();
}
@Nonnull
- @Override
- public Cardinalities visitRecordQueryInValuesJoinPlan(@Nonnull final RecordQueryInValuesJoinPlan inValuesJoinPlan) {
- final var childCardinalities = fromChild(inValuesJoinPlan);
- final var valuesSize = inValuesJoinPlan.getInListValues().size();
- return new Cardinalities(
- Cardinality.ofCardinality(valuesSize).times(childCardinalities.getMinCardinality()),
- Cardinality.ofCardinality(valuesSize).times(childCardinalities.getMaxCardinality()));
+ public Cardinalities evaluate(@Nonnull Reference ref) {
+ return evaluate(ref.get());
}
@Nonnull
- @Override
- public Cardinalities visitRecordQueryAggregateIndexPlan(@Nonnull final RecordQueryAggregateIndexPlan aggregateIndexPlan) {
- final var groupingValueMaybe = aggregateIndexPlan.getGroupingValueMaybe();
- if (groupingValueMaybe.isEmpty()) {
- return new Cardinalities(Cardinality.ofCardinality(1L), Cardinality.ofCardinality(1L));
- }
- final var groupingValue = groupingValueMaybe.get();
- final var indexScanPlan = aggregateIndexPlan.getIndexPlan();
- final var matchCandidateOptional = indexScanPlan.getMatchCandidateMaybe();
- if (matchCandidateOptional.isEmpty()) {
- return Cardinalities.unknownMaxCardinality();
- }
- final var ordering = matchCandidateOptional.get()
- .computeOrderingFromScanComparisons(
- indexScanPlan.getScanComparisons(),
- indexScanPlan.isReverse(),
- false);
- if (ordering.getEqualityBoundValues().contains(groupingValue)) {
- return new Cardinalities(Cardinality.ofCardinality(0L), Cardinality.ofCardinality(1L));
- } else {
- return Cardinalities.unknownMaxCardinality();
- }
+ public Cardinalities evaluate(@Nonnull final RelationalExpression expression) {
+ return createVisitor().visit(expression);
}
@Nonnull
- @Override
- public Cardinalities visitRecordQueryCoveringIndexPlan(@Nonnull final RecordQueryCoveringIndexPlan coveringIndexPlan) {
- if (!(coveringIndexPlan.getIndexPlan() instanceof RecordQueryIndexPlan)) {
- return Cardinalities.unknownMaxCardinality();
- }
- return visitRecordQueryIndexPlan((RecordQueryIndexPlan)coveringIndexPlan.getIndexPlan());
+ public static CardinalitiesProperty cardinalities() {
+ return CARDINALITIES;
}
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryDeletePlan(@Nonnull final RecordQueryDeletePlan deletePlan) {
- return fromChild(deletePlan);
- }
+ /**
+ * Visitor implementation.
+ */
+ public static class CardinalitiesVisitor implements RelationalExpressionVisitor {
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryUpdatePlan(@Nonnull final RecordQueryUpdatePlan updatePlan) {
+ return fromChild(updatePlan);
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryMapPlan(@Nonnull final RecordQueryMapPlan mapPlan) {
- return fromChild(mapPlan);
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryPredicatesFilterPlan(@Nonnull final RecordQueryPredicatesFilterPlan predicatesFilterPlan) {
+ final var cardinalitiesFromChild = fromChild(predicatesFilterPlan);
+ return new Cardinalities(Cardinality.ofCardinality(0L), cardinalitiesFromChild.getMaxCardinality());
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryComparatorPlan(@Nonnull final RecordQueryComparatorPlan comparatorPlan) {
- return weakenCardinalities(fromChildren(comparatorPlan));
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryLoadByKeysPlan(@Nonnull final RecordQueryLoadByKeysPlan element) {
+ final var keysSource = element.getKeysSource();
+ if (keysSource.maxCardinality() == QueryPlan.UNKNOWN_MAX_CARDINALITY) {
+ return Cardinalities.unknownCardinalities;
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryUnorderedDistinctPlan(@Nonnull final RecordQueryUnorderedDistinctPlan unorderedDistinctPlan) {
- return fromChild(unorderedDistinctPlan);
- }
+ return new Cardinalities(Cardinality.ofCardinality(0L), Cardinality.ofCardinality(keysSource.maxCardinality()));
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryIntersectionOnKeyExpressionPlan(@Nonnull final RecordQueryIntersectionOnKeyExpressionPlan intersectionOnKeyExpressionPlan) {
- return intersectCardinalities(fromChildren(intersectionOnKeyExpressionPlan));
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryInValuesJoinPlan(@Nonnull final RecordQueryInValuesJoinPlan inValuesJoinPlan) {
+ final var childCardinalities = fromChild(inValuesJoinPlan);
+ final var valuesSize = inValuesJoinPlan.getInListValues().size();
+ return new Cardinalities(
+ Cardinality.ofCardinality(valuesSize).times(childCardinalities.getMinCardinality()),
+ Cardinality.ofCardinality(valuesSize).times(childCardinalities.getMaxCardinality()));
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQuerySelectorPlan(@Nonnull final RecordQuerySelectorPlan selectorPlan) {
- return weakenCardinalities(fromChildren(selectorPlan));
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryAggregateIndexPlan(@Nonnull final RecordQueryAggregateIndexPlan aggregateIndexPlan) {
+ final var groupingValueMaybe = aggregateIndexPlan.getGroupingValueMaybe();
+ if (groupingValueMaybe.isEmpty()) {
+ return new Cardinalities(Cardinality.ofCardinality(1L), Cardinality.ofCardinality(1L));
+ }
+ final var groupingValue = groupingValueMaybe.get();
+ final var indexScanPlan = aggregateIndexPlan.getIndexPlan();
+ final var matchCandidateOptional = indexScanPlan.getMatchCandidateMaybe();
+ if (matchCandidateOptional.isEmpty()) {
+ return Cardinalities.unknownMaxCardinality();
+ }
+ final var ordering = matchCandidateOptional.get()
+ .computeOrderingFromScanComparisons(
+ indexScanPlan.getScanComparisons(),
+ indexScanPlan.isReverse(),
+ false);
+ if (ordering.getEqualityBoundValues().contains(groupingValue)) {
+ return new Cardinalities(Cardinality.ofCardinality(0L), Cardinality.ofCardinality(1L));
+ } else {
+ return Cardinalities.unknownMaxCardinality();
+ }
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryRangePlan(@Nonnull final RecordQueryRangePlan rangePlan) {
- final var limitValue = rangePlan.getExclusiveLimitValue();
- if (limitValue instanceof LiteralValue) {
- final var limit = (int)Verify.verifyNotNull(limitValue.evalWithoutStore(EvaluationContext.EMPTY));
- return new Cardinalities(Cardinality.ofCardinality(limit), Cardinality.ofCardinality(limit));
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryCoveringIndexPlan(@Nonnull final RecordQueryCoveringIndexPlan coveringIndexPlan) {
+ if (!(coveringIndexPlan.getIndexPlan() instanceof RecordQueryIndexPlan)) {
+ return Cardinalities.unknownMaxCardinality();
+ }
+ return visitRecordQueryIndexPlan((RecordQueryIndexPlan)coveringIndexPlan.getIndexPlan());
}
- return Cardinalities.unknownMaxCardinality();
- }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryExplodePlan(@Nonnull final RecordQueryExplodePlan element) {
- return Cardinalities.unknownMaxCardinality();
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryDeletePlan(@Nonnull final RecordQueryDeletePlan deletePlan) {
+ return fromChild(deletePlan);
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryInsertPlan(@Nonnull final RecordQueryInsertPlan insertPlan) {
- return fromChild(insertPlan);
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryMapPlan(@Nonnull final RecordQueryMapPlan mapPlan) {
+ return fromChild(mapPlan);
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryTableFunctionPlan(@Nonnull final RecordQueryTableFunctionPlan element) {
- return Cardinalities.unknownMaxCardinality();
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryComparatorPlan(@Nonnull final RecordQueryComparatorPlan comparatorPlan) {
+ return weakenCardinalities(fromChildren(comparatorPlan));
+ }
- @Nonnull
- @Override
- public Cardinalities visitTempTableInsertPlan(@Nonnull final TempTableInsertPlan tempTableInsertPlan) {
- return fromChild(tempTableInsertPlan);
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryUnorderedDistinctPlan(@Nonnull final RecordQueryUnorderedDistinctPlan unorderedDistinctPlan) {
+ return fromChild(unorderedDistinctPlan);
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryIntersectionOnValuesPlan(@Nonnull final RecordQueryIntersectionOnValuesPlan intersectionOnValuesPlan) {
- return weakenCardinalities(fromChildren(intersectionOnValuesPlan));
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryIntersectionOnKeyExpressionPlan(@Nonnull final RecordQueryIntersectionOnKeyExpressionPlan intersectionOnKeyExpressionPlan) {
+ return intersectCardinalities(fromChildren(intersectionOnKeyExpressionPlan));
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryScoreForRankPlan(@Nonnull final RecordQueryScoreForRankPlan scoreForRankPlan) {
- return fromChild(scoreForRankPlan);
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQuerySelectorPlan(@Nonnull final RecordQuerySelectorPlan selectorPlan) {
+ return weakenCardinalities(fromChildren(selectorPlan));
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryIndexPlan(@Nonnull final RecordQueryIndexPlan indexPlan) {
- final var matchCandidateOptional = indexPlan.getMatchCandidateMaybe();
- if (matchCandidateOptional.isEmpty()) {
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryRangePlan(@Nonnull final RecordQueryRangePlan rangePlan) {
+ final var limitValue = rangePlan.getExclusiveLimitValue();
+ if (limitValue instanceof LiteralValue) {
+ final var limit = (int)Verify.verifyNotNull(limitValue.evalWithoutStore(EvaluationContext.EMPTY));
+ return new Cardinalities(Cardinality.ofCardinality(limit), Cardinality.ofCardinality(limit));
+ }
return Cardinalities.unknownMaxCardinality();
}
- final var matchCandidate = matchCandidateOptional.get();
-
- final var ordering =
- matchCandidate.computeOrderingFromScanComparisons(indexPlan.getScanComparisons(),
- indexPlan.isReverse(),
- false);
- final var equalityBoundValues = ordering.getEqualityBoundValues();
- // try to see if the primary key is bound by equalities
- if (matchCandidate instanceof WithPrimaryKeyMatchCandidate) {
- final var primaryKeyValuesOptional = ((WithPrimaryKeyMatchCandidate)matchCandidate).getPrimaryKeyValuesMaybe();
- if (primaryKeyValuesOptional.isPresent()) {
- final var primaryKeyValues = primaryKeyValuesOptional.get();
- if (equalityBoundValues.containsAll(primaryKeyValues)) {
- return new Cardinalities(Cardinality.ofCardinality(0L), Cardinality.ofCardinality(1L));
- }
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryExplodePlan(@Nonnull final RecordQueryExplodePlan element) {
+ return Cardinalities.unknownMaxCardinality();
}
- if (matchCandidate.isUnique() && matchCandidate instanceof ValueIndexScanMatchCandidate) {
- // unique index
- final var valueIndexScanMatchCandidate = (ValueIndexScanMatchCandidate)matchCandidate;
- final var translationMap = AliasMap.ofAliases(valueIndexScanMatchCandidate.getBaseAlias(), Quantifier.current());
- final var keyValues =
- valueIndexScanMatchCandidate.getIndexKeyValues()
- .stream()
- .limit(matchCandidate.getColumnSize())
- .map(keyValue -> keyValue.rebase(translationMap))
- .collect(ImmutableList.toImmutableList());
- if (equalityBoundValues.containsAll(keyValues)) {
- return new Cardinalities(Cardinality.ofCardinality(0L), Cardinality.ofCardinality(1L));
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryInsertPlan(@Nonnull final RecordQueryInsertPlan insertPlan) {
+ return fromChild(insertPlan);
}
- return Cardinalities.unknownMaxCardinality();
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryTableFunctionPlan(@Nonnull final RecordQueryTableFunctionPlan element) {
+ return Cardinalities.unknownMaxCardinality();
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryFirstOrDefaultPlan(@Nonnull final RecordQueryFirstOrDefaultPlan element) {
- return new Cardinalities(Cardinality.ofCardinality(1L), Cardinality.ofCardinality(1L));
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitTempTableInsertPlan(@Nonnull final TempTableInsertPlan tempTableInsertPlan) {
+ return fromChild(tempTableInsertPlan);
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryDefaultOnEmptyPlan(@Nonnull final RecordQueryDefaultOnEmptyPlan defaultOnEmptyPlan) {
- final var cardinalitiesFromChild = fromChild(defaultOnEmptyPlan);
- return new Cardinalities(Cardinality.ofCardinality(1), cardinalitiesFromChild.maxCardinality);
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryIntersectionOnValuesPlan(@Nonnull final RecordQueryIntersectionOnValuesPlan intersectionOnValuesPlan) {
+ return weakenCardinalities(fromChildren(intersectionOnValuesPlan));
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryUnionOnKeyExpressionPlan(@Nonnull final RecordQueryUnionOnKeyExpressionPlan unionOnKeyExpressionPlan) {
- return unionCardinalities(fromChildren(unionOnKeyExpressionPlan));
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryScoreForRankPlan(@Nonnull final RecordQueryScoreForRankPlan scoreForRankPlan) {
+ return fromChild(scoreForRankPlan);
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryFilterPlan(@Nonnull final RecordQueryFilterPlan filterPlan) {
- final var cardinalitiesFromChild = fromChild(filterPlan);
- return new Cardinalities(Cardinality.ofCardinality(0L), cardinalitiesFromChild.getMaxCardinality());
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryIndexPlan(@Nonnull final RecordQueryIndexPlan indexPlan) {
+ final var matchCandidateOptional = indexPlan.getMatchCandidateMaybe();
+ if (matchCandidateOptional.isEmpty()) {
+ return Cardinalities.unknownMaxCardinality();
+ }
+ final var matchCandidate = matchCandidateOptional.get();
+
+ final var ordering =
+ matchCandidate.computeOrderingFromScanComparisons(indexPlan.getScanComparisons(),
+ indexPlan.isReverse(),
+ false);
+ final var equalityBoundValues = ordering.getEqualityBoundValues();
+
+ // try to see if the primary key is bound by equalities
+ if (matchCandidate instanceof WithPrimaryKeyMatchCandidate) {
+ final var primaryKeyValuesOptional = ((WithPrimaryKeyMatchCandidate)matchCandidate).getPrimaryKeyValuesMaybe();
+ if (primaryKeyValuesOptional.isPresent()) {
+ final var primaryKeyValues = primaryKeyValuesOptional.get();
+ if (equalityBoundValues.containsAll(primaryKeyValues)) {
+ return new Cardinalities(Cardinality.ofCardinality(0L), Cardinality.ofCardinality(1L));
+ }
+ }
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryUnorderedPrimaryKeyDistinctPlan(@Nonnull final RecordQueryUnorderedPrimaryKeyDistinctPlan unorderedPrimaryKeyDistinctPlan) {
- final var cardinalitiesFromChild = fromChild(unorderedPrimaryKeyDistinctPlan);
- if (!cardinalitiesFromChild.getMinCardinality().isUnknown()) {
- if (cardinalitiesFromChild.getMinCardinality().getCardinality() >= 1L) {
- return new Cardinalities(Cardinality.ofCardinality(1L), cardinalitiesFromChild.getMaxCardinality());
+ if (matchCandidate.isUnique() && matchCandidate instanceof ValueIndexScanMatchCandidate) {
+ // unique index
+ final var valueIndexScanMatchCandidate = (ValueIndexScanMatchCandidate)matchCandidate;
+ final var translationMap = AliasMap.ofAliases(valueIndexScanMatchCandidate.getBaseAlias(), Quantifier.current());
+ final var keyValues =
+ valueIndexScanMatchCandidate.getIndexKeyValues()
+ .stream()
+ .limit(matchCandidate.getColumnSize())
+ .map(keyValue -> keyValue.rebase(translationMap))
+ .collect(ImmutableList.toImmutableList());
+ if (equalityBoundValues.containsAll(keyValues)) {
+ return new Cardinalities(Cardinality.ofCardinality(0L), Cardinality.ofCardinality(1L));
+ }
}
- // the case where min-cardinality == 0L is to fall through
+
+ return Cardinalities.unknownMaxCardinality();
}
- return cardinalitiesFromChild;
- }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryTextIndexPlan(@Nonnull final RecordQueryTextIndexPlan element) {
- return Cardinalities.unknownMaxCardinality();
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryFirstOrDefaultPlan(@Nonnull final RecordQueryFirstOrDefaultPlan element) {
+ return new Cardinalities(Cardinality.ofCardinality(1L), Cardinality.ofCardinality(1L));
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryFetchFromPartialRecordPlan(@Nonnull final RecordQueryFetchFromPartialRecordPlan fetchFromPartialRecordPlan) {
- return fromChild(fetchFromPartialRecordPlan);
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryDefaultOnEmptyPlan(@Nonnull final RecordQueryDefaultOnEmptyPlan defaultOnEmptyPlan) {
+ final var cardinalitiesFromChild = fromChild(defaultOnEmptyPlan);
+ return new Cardinalities(Cardinality.ofCardinality(1), cardinalitiesFromChild.maxCardinality);
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryTypeFilterPlan(@Nonnull final RecordQueryTypeFilterPlan typeFilterPlan) {
- return fromChild(typeFilterPlan);
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryUnionOnKeyExpressionPlan(@Nonnull final RecordQueryUnionOnKeyExpressionPlan unionOnKeyExpressionPlan) {
+ return unionCardinalities(fromChildren(unionOnKeyExpressionPlan));
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryInUnionOnKeyExpressionPlan(@Nonnull final RecordQueryInUnionOnKeyExpressionPlan inUnionOnKeyExpressionPlan) {
- return visitRecordQueryInUnionPlan(inUnionOnKeyExpressionPlan);
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryFilterPlan(@Nonnull final RecordQueryFilterPlan filterPlan) {
+ final var cardinalitiesFromChild = fromChild(filterPlan);
+ return new Cardinalities(Cardinality.ofCardinality(0L), cardinalitiesFromChild.getMaxCardinality());
+ }
- @Nonnull
- public Cardinalities visitRecordQueryInUnionPlan(@Nonnull final RecordQueryInUnionPlan inUnionPlan) {
- final var inSources = inUnionPlan.getInSources();
-
- final var inSourcesCardinalitiesOptional =
- inSources.stream()
- .map(inSource -> {
- if (inSource instanceof InParameterSource || inSource instanceof InComparandSource) {
- return Cardinalities.unknownMaxCardinality();
- } else {
- Verify.verify(inSource instanceof InValuesSource);
- final var inValuesSource = (InValuesSource)inSource;
- final var size = inValuesSource.getValues().size();
- return new Cardinalities(Cardinality.ofCardinality(size), Cardinality.ofCardinality(size));
- }
- })
- .reduce(Cardinalities::times);
-
- Verify.verify(inSourcesCardinalitiesOptional.isPresent());
- final var inSourcesCardinalities = inSourcesCardinalitiesOptional.get();
- final var childCardinalities = fromChild(inUnionPlan);
-
- return inSourcesCardinalities.times(childCardinalities);
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryUnorderedPrimaryKeyDistinctPlan(@Nonnull final RecordQueryUnorderedPrimaryKeyDistinctPlan unorderedPrimaryKeyDistinctPlan) {
+ final var cardinalitiesFromChild = fromChild(unorderedPrimaryKeyDistinctPlan);
+ if (!cardinalitiesFromChild.getMinCardinality().isUnknown()) {
+ if (cardinalitiesFromChild.getMinCardinality().getCardinality() >= 1L) {
+ return new Cardinalities(Cardinality.ofCardinality(1L), cardinalitiesFromChild.getMaxCardinality());
+ }
+ // the case where min-cardinality == 0L is to fall through
+ }
+ return cardinalitiesFromChild;
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryInParameterJoinPlan(@Nonnull final RecordQueryInParameterJoinPlan element) {
- return Cardinalities.unknownMaxCardinality();
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryTextIndexPlan(@Nonnull final RecordQueryTextIndexPlan element) {
+ return Cardinalities.unknownMaxCardinality();
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryInComparandJoinPlan(@Nonnull final RecordQueryInComparandJoinPlan element) {
- return Cardinalities.unknownMaxCardinality();
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryFetchFromPartialRecordPlan(@Nonnull final RecordQueryFetchFromPartialRecordPlan fetchFromPartialRecordPlan) {
+ return fromChild(fetchFromPartialRecordPlan);
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryRecursiveUnionPlan(@Nonnull final RecordQueryRecursiveUnionPlan element) {
- final var initialStateCardinality = fromChild(element.getChildren().get(0));
- // this can be improved by imposing an assertion on the cardinality of the recursive leg; if the recursive leg
- // has a known minimum cardinality of x | x > 0 then this query is infinitely recursive, so we should probably
- // throw.
- return new Cardinalities(initialStateCardinality.minCardinality, Cardinality.unknownCardinality);
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryTypeFilterPlan(@Nonnull final RecordQueryTypeFilterPlan typeFilterPlan) {
+ return fromChild(typeFilterPlan);
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryFlatMapPlan(@Nonnull final RecordQueryFlatMapPlan flatMapPlan) {
- final var fromChildren = fromChildren(flatMapPlan);
- final var outerCardinalities = fromChildren.get(0);
- final var innerCardinalities = fromChildren.get(1);
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryInUnionOnKeyExpressionPlan(@Nonnull final RecordQueryInUnionOnKeyExpressionPlan inUnionOnKeyExpressionPlan) {
+ return visitRecordQueryInUnionPlan(inUnionOnKeyExpressionPlan);
+ }
- return outerCardinalities.times(innerCardinalities);
- }
+ @Nonnull
+ public Cardinalities visitRecordQueryInUnionPlan(@Nonnull final RecordQueryInUnionPlan inUnionPlan) {
+ final var inSources = inUnionPlan.getInSources();
+
+ final var inSourcesCardinalitiesOptional =
+ inSources.stream()
+ .map(inSource -> {
+ if (inSource instanceof InParameterSource || inSource instanceof InComparandSource) {
+ return Cardinalities.unknownMaxCardinality();
+ } else {
+ Verify.verify(inSource instanceof InValuesSource);
+ final var inValuesSource = (InValuesSource)inSource;
+ final var size = inValuesSource.getValues().size();
+ return new Cardinalities(Cardinality.ofCardinality(size), Cardinality.ofCardinality(size));
+ }
+ })
+ .reduce(Cardinalities::times);
+
+ Verify.verify(inSourcesCardinalitiesOptional.isPresent());
+ final var inSourcesCardinalities = inSourcesCardinalitiesOptional.get();
+ final var childCardinalities = fromChild(inUnionPlan);
+
+ return inSourcesCardinalities.times(childCardinalities);
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryStreamingAggregationPlan(@Nonnull final RecordQueryStreamingAggregationPlan element) {
- // if we do not have any grouping value, we will apply the aggregation(s) over the entire child result set
- // and return a single row comprising the aggregation(s) result
- if (element.getGroupingValue() == null) {
- return new Cardinalities(Cardinality.ofCardinality(1L), Cardinality.ofCardinality(1L));
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryInParameterJoinPlan(@Nonnull final RecordQueryInParameterJoinPlan element) {
+ return Cardinalities.unknownMaxCardinality();
}
- // if the grouping value is constant, the cardinality ranges between 0 and 1.
- if (element.getGroupingValue().isConstant()) {
- return new Cardinalities(Cardinality.ofCardinality(0L), Cardinality.ofCardinality(1L));
+
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryInComparandJoinPlan(@Nonnull final RecordQueryInComparandJoinPlan element) {
+ return Cardinalities.unknownMaxCardinality();
}
- return Cardinalities.unknownMaxCardinality();
- }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryUnionOnValuesPlan(@Nonnull final RecordQueryUnionOnValuesPlan unionOnValuesPlan) {
- return unionCardinalities(fromChildren(unionOnValuesPlan));
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryRecursiveUnionPlan(@Nonnull final RecordQueryRecursiveUnionPlan element) {
+ final var initialStateCardinality = fromChild(element.getChildren().get(0));
+ // this can be improved by imposing an assertion on the cardinality of the recursive leg; if the recursive leg
+ // has a known minimum cardinality of x | x > 0 then this query is infinitely recursive, so we should probably
+ // throw.
+ return new Cardinalities(initialStateCardinality.minCardinality, Cardinality.unknownCardinality);
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryUnorderedUnionPlan(@Nonnull final RecordQueryUnorderedUnionPlan unorderedUnionPlan) {
- return unionCardinalities(fromChildren(unorderedUnionPlan));
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryFlatMapPlan(@Nonnull final RecordQueryFlatMapPlan flatMapPlan) {
+ final var fromChildren = fromChildren(flatMapPlan);
+ final var outerCardinalities = fromChildren.get(0);
+ final var innerCardinalities = fromChildren.get(1);
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryScanPlan(@Nonnull final RecordQueryScanPlan scanPlan) {
- final var matchCandidateOptional = scanPlan.getMatchCandidateMaybe();
- if (matchCandidateOptional.isEmpty()) {
- return Cardinalities.unknownMaxCardinality();
+ return outerCardinalities.times(innerCardinalities);
}
- final var matchCandidate = matchCandidateOptional.get();
- final var primaryKeyValuesOptional = matchCandidate.getPrimaryKeyValuesMaybe();
- if (primaryKeyValuesOptional.isEmpty()) {
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryStreamingAggregationPlan(@Nonnull final RecordQueryStreamingAggregationPlan element) {
+ // if we do not have any grouping value, we will apply the aggregation(s) over the entire child result set
+ // and return a single row comprising the aggregation(s) result
+ if (element.getGroupingValue() == null) {
+ return new Cardinalities(Cardinality.ofCardinality(1L), Cardinality.ofCardinality(1L));
+ }
+ // if the grouping value is constant, the cardinality ranges between 0 and 1.
+ if (element.getGroupingValue().isConstant()) {
+ return new Cardinalities(Cardinality.ofCardinality(0L), Cardinality.ofCardinality(1L));
+ }
return Cardinalities.unknownMaxCardinality();
}
- final var primaryKeyValues = primaryKeyValuesOptional.get();
- final var ordering =
- matchCandidate.computeOrderingFromScanComparisons(scanPlan.getScanComparisons(),
- scanPlan.isReverse(),
- false);
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryUnionOnValuesPlan(@Nonnull final RecordQueryUnionOnValuesPlan unionOnValuesPlan) {
+ return unionCardinalities(fromChildren(unionOnValuesPlan));
+ }
- final var equalityBoundValues = ordering.getEqualityBoundValues();
- if (equalityBoundValues.containsAll(primaryKeyValues)) {
- return new Cardinalities(Cardinality.ofCardinality(0L), Cardinality.ofCardinality(1L));
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryUnorderedUnionPlan(@Nonnull final RecordQueryUnorderedUnionPlan unorderedUnionPlan) {
+ return unionCardinalities(fromChildren(unorderedUnionPlan));
}
- return Cardinalities.unknownMaxCardinality();
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryScanPlan(@Nonnull final RecordQueryScanPlan scanPlan) {
+ final var matchCandidateOptional = scanPlan.getMatchCandidateMaybe();
+ if (matchCandidateOptional.isEmpty()) {
+ return Cardinalities.unknownMaxCardinality();
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryInUnionOnValuesPlan(@Nonnull final RecordQueryInUnionOnValuesPlan inUnionOnValuesPlan) {
- return visitRecordQueryInUnionPlan(inUnionOnValuesPlan);
- }
+ final var matchCandidate = matchCandidateOptional.get();
+ final var primaryKeyValuesOptional = matchCandidate.getPrimaryKeyValuesMaybe();
+ if (primaryKeyValuesOptional.isEmpty()) {
+ return Cardinalities.unknownMaxCardinality();
+ }
+ final var primaryKeyValues = primaryKeyValuesOptional.get();
- @Nonnull
- @Override
- public Cardinalities visitComposedBitmapIndexQueryPlan(@Nonnull final ComposedBitmapIndexQueryPlan element) {
- return Cardinalities.unknownMaxCardinality();
- }
+ final var ordering =
+ matchCandidate.computeOrderingFromScanComparisons(scanPlan.getScanComparisons(),
+ scanPlan.isReverse(),
+ false);
- @Nonnull
- @Override
- public Cardinalities visitRecordQueryDamPlan(@Nonnull final RecordQueryDamPlan damPlan) {
- return fromChild(damPlan);
- }
+ final var equalityBoundValues = ordering.getEqualityBoundValues();
+ if (equalityBoundValues.containsAll(primaryKeyValues)) {
+ return new Cardinalities(Cardinality.ofCardinality(0L), Cardinality.ofCardinality(1L));
+ }
- @Nonnull
- @Override
- public Cardinalities visitMatchableSortExpression(@Nonnull final MatchableSortExpression matchableSortExpression) {
- return fromChild(matchableSortExpression);
- }
+ return Cardinalities.unknownMaxCardinality();
+ }
- @Nonnull
- @Override
- public Cardinalities visitInsertExpression(@Nonnull final InsertExpression insertExpression) {
- return fromChild(insertExpression);
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryInUnionOnValuesPlan(@Nonnull final RecordQueryInUnionOnValuesPlan inUnionOnValuesPlan) {
+ return visitRecordQueryInUnionPlan(inUnionOnValuesPlan);
+ }
- @Nonnull
- @Override
- public Cardinalities visitTempTableInsertExpression(@Nonnull final TempTableInsertExpression tempTableInsertExpression) {
- return fromChild(tempTableInsertExpression);
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitComposedBitmapIndexQueryPlan(@Nonnull final ComposedBitmapIndexQueryPlan element) {
+ return Cardinalities.unknownMaxCardinality();
+ }
- @Nonnull
- @Override
- public Cardinalities visitPrimaryScanExpression(@Nonnull final PrimaryScanExpression element) {
- // TODO do better
- return Cardinalities.unknownMaxCardinality();
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQueryDamPlan(@Nonnull final RecordQueryDamPlan damPlan) {
+ return fromChild(damPlan);
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecursiveUnionExpression(@Nonnull final RecursiveUnionExpression element) {
- final var initialStateCardinality = fromQuantifier(element.getQuantifiers().get(0));
- return new Cardinalities(initialStateCardinality.minCardinality, Cardinality.unknownCardinality);
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitMatchableSortExpression(@Nonnull final MatchableSortExpression matchableSortExpression) {
+ return fromChild(matchableSortExpression);
+ }
- @Nonnull
- @Override
- public Cardinalities visitLogicalSortExpression(@Nonnull final LogicalSortExpression logicalSortExpression) {
- return fromChild(logicalSortExpression);
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitInsertExpression(@Nonnull final InsertExpression insertExpression) {
+ return fromChild(insertExpression);
+ }
- @Nonnull
- @Override
- public Cardinalities visitLogicalTypeFilterExpression(@Nonnull final LogicalTypeFilterExpression logicalTypeFilterExpression) {
- return fromChild(logicalTypeFilterExpression);
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitTempTableInsertExpression(@Nonnull final TempTableInsertExpression tempTableInsertExpression) {
+ return fromChild(tempTableInsertExpression);
+ }
- @Nonnull
- @Override
- public Cardinalities visitLogicalUnionExpression(@Nonnull final LogicalUnionExpression logicalUnionExpression) {
- return unionCardinalities(fromChildren(logicalUnionExpression));
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitPrimaryScanExpression(@Nonnull final PrimaryScanExpression element) {
+ // TODO do better
+ return Cardinalities.unknownMaxCardinality();
+ }
- @Nonnull
- @Override
- public Cardinalities visitLogicalIntersectionExpression(@Nonnull final LogicalIntersectionExpression logicalIntersectionExpression) {
- return intersectCardinalities(fromChildren(logicalIntersectionExpression));
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecursiveUnionExpression(@Nonnull final RecursiveUnionExpression element) {
+ final var initialStateCardinality = fromQuantifier(element.getQuantifiers().get(0));
+ return new Cardinalities(initialStateCardinality.minCardinality, Cardinality.unknownCardinality);
+ }
- @Nonnull
- @Override
- public Cardinalities visitTableFunctionExpression(@Nonnull final TableFunctionExpression element) {
- return Cardinalities.unknownMaxCardinality();
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitLogicalSortExpression(@Nonnull final LogicalSortExpression logicalSortExpression) {
+ return fromChild(logicalSortExpression);
+ }
- @Nonnull
- @Override
- public Cardinalities visitLogicalUniqueExpression(@Nonnull final LogicalUniqueExpression logicalUniqueExpression) {
- return fromChild(logicalUniqueExpression);
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitLogicalTypeFilterExpression(@Nonnull final LogicalTypeFilterExpression logicalTypeFilterExpression) {
+ return fromChild(logicalTypeFilterExpression);
+ }
- @Nonnull
- @Override
- public Cardinalities visitLogicalProjectionExpression(@Nonnull final LogicalProjectionExpression logicalProjectionExpression) {
- return fromChild(logicalProjectionExpression);
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitLogicalUnionExpression(@Nonnull final LogicalUnionExpression logicalUnionExpression) {
+ return unionCardinalities(fromChildren(logicalUnionExpression));
+ }
- @Nonnull
- @Override
- public Cardinalities visitSelectExpression(@Nonnull final SelectExpression selectExpression) {
- return fromChildren(selectExpression)
- .stream()
- .reduce(Cardinalities::times)
- .orElseThrow(() -> new RecordCoreException("must have at least one quantifier"));
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitLogicalIntersectionExpression(@Nonnull final LogicalIntersectionExpression logicalIntersectionExpression) {
+ return intersectCardinalities(fromChildren(logicalIntersectionExpression));
+ }
- @Nonnull
- @Override
- public Cardinalities visitExplodeExpression(@Nonnull final ExplodeExpression element) {
- return Cardinalities.unknownMaxCardinality();
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitTableFunctionExpression(@Nonnull final TableFunctionExpression element) {
+ return Cardinalities.unknownMaxCardinality();
+ }
- @Nonnull
- @Override
- public Cardinalities visitFullUnorderedScanExpression(@Nonnull final FullUnorderedScanExpression element) {
- return Cardinalities.unknownMaxCardinality();
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitLogicalUniqueExpression(@Nonnull final LogicalUniqueExpression logicalUniqueExpression) {
+ return fromChild(logicalUniqueExpression);
+ }
- @Nonnull
- @Override
- public Cardinalities visitTempTableScanExpression(@Nonnull final TempTableScanExpression element) {
- return Cardinalities.unknownMaxCardinality();
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitLogicalProjectionExpression(@Nonnull final LogicalProjectionExpression logicalProjectionExpression) {
+ return fromChild(logicalProjectionExpression);
+ }
- @Nonnull
- @Override
- public Cardinalities visitGroupByExpression(@Nonnull final GroupByExpression element) {
- // if we do not have any grouping value, we will apply the aggregation(s) over the entire child result set
- // and return a single row comprising the aggregation(s) result
- if (element.getGroupingValue() == null) {
- return new Cardinalities(Cardinality.ofCardinality(1L), Cardinality.ofCardinality(1L));
+ @Nonnull
+ @Override
+ public Cardinalities visitSelectExpression(@Nonnull final SelectExpression selectExpression) {
+ return fromChildren(selectExpression)
+ .stream()
+ .reduce(Cardinalities::times)
+ .orElseThrow(() -> new RecordCoreException("must have at least one quantifier"));
}
- // if the grouping value is constant, the cardinality ranges between 0 and 1.
- if (element.getGroupingValue().isConstant()) {
- return new Cardinalities(Cardinality.ofCardinality(0L), Cardinality.ofCardinality(1L));
+
+ @Nonnull
+ @Override
+ public Cardinalities visitExplodeExpression(@Nonnull final ExplodeExpression element) {
+ return Cardinalities.unknownMaxCardinality();
}
- return Cardinalities.unknownMaxCardinality();
- }
- @Nonnull
- @Override
- public Cardinalities visitUpdateExpression(@Nonnull final UpdateExpression updateExpression) {
- return fromChild(updateExpression);
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitFullUnorderedScanExpression(@Nonnull final FullUnorderedScanExpression element) {
+ return Cardinalities.unknownMaxCardinality();
+ }
- @Nonnull
- @Override
- public Cardinalities visitLogicalDistinctExpression(@Nonnull final LogicalDistinctExpression logicalDistinctExpression) {
- return fromChild(logicalDistinctExpression);
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitTempTableScanExpression(@Nonnull final TempTableScanExpression element) {
+ return Cardinalities.unknownMaxCardinality();
+ }
- @Nonnull
- @Override
- public Cardinalities visitLogicalFilterExpression(@Nonnull final LogicalFilterExpression logicalFilterExpression) {
- return fromChild(logicalFilterExpression);
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitGroupByExpression(@Nonnull final GroupByExpression element) {
+ // if we do not have any grouping value, we will apply the aggregation(s) over the entire child result set
+ // and return a single row comprising the aggregation(s) result
+ if (element.getGroupingValue() == null) {
+ return new Cardinalities(Cardinality.ofCardinality(1L), Cardinality.ofCardinality(1L));
+ }
+ // if the grouping value is constant, the cardinality ranges between 0 and 1.
+ if (element.getGroupingValue().isConstant()) {
+ return new Cardinalities(Cardinality.ofCardinality(0L), Cardinality.ofCardinality(1L));
+ }
+ return Cardinalities.unknownMaxCardinality();
+ }
- @Nonnull
- @Override
- public Cardinalities visitDeleteExpression(@Nonnull final DeleteExpression deleteExpression) {
- return fromChild(deleteExpression);
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitUpdateExpression(@Nonnull final UpdateExpression updateExpression) {
+ return fromChild(updateExpression);
+ }
- @Nonnull
- @Override
- public Cardinalities visitRecordQuerySortPlan(@Nonnull final RecordQuerySortPlan querySortPlan) {
- return fromChild(querySortPlan);
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitLogicalDistinctExpression(@Nonnull final LogicalDistinctExpression logicalDistinctExpression) {
+ return fromChild(logicalDistinctExpression);
+ }
- @Nonnull
- @Override
- public Cardinalities visitTempTableScanPlan(@Nonnull final TempTableScanPlan element) {
- return Cardinalities.unknownMaxCardinality();
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitLogicalFilterExpression(@Nonnull final LogicalFilterExpression logicalFilterExpression) {
+ return fromChild(logicalFilterExpression);
+ }
- @Nonnull
- @Override
- public Cardinalities evaluateAtExpression(@Nonnull RelationalExpression expression, @Nonnull List childResults) {
- throw new RecordCoreException("unsupported method call. property should instead override visit for expression");
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitDeleteExpression(@Nonnull final DeleteExpression deleteExpression) {
+ return fromChild(deleteExpression);
+ }
- @Nonnull
- @Override
- public Cardinalities evaluateAtRef(@Nonnull Reference ref, @Nonnull List memberResults) {
- return intersectCardinalities(memberResults);
- }
+ @Nonnull
+ @Override
+ public Cardinalities visitRecordQuerySortPlan(@Nonnull final RecordQuerySortPlan querySortPlan) {
+ return fromChild(querySortPlan);
+ }
- @Nonnull
- private Cardinalities intersectCardinalities(@Nonnull Iterable cardinalitiesIterable) {
- //
- // Merge all cardinalities in the iterable.
- // If the cardinality in one member is unknown but not in the other, we'll take the more constraining one
- //
- var minCardinality = Cardinality.unknownCardinality();
- var maxCardinality = Cardinality.unknownCardinality();
- for (final var cardinalities : cardinalitiesIterable) {
- if (minCardinality.isUnknown()) {
- minCardinality = cardinalities.getMinCardinality();
- } else {
- if (!cardinalities.getMinCardinality().isUnknown()) {
- minCardinality = Cardinality.ofCardinality(0);
+ @Nonnull
+ @Override
+ public Cardinalities visitTempTableScanPlan(@Nonnull final TempTableScanPlan element) {
+ return Cardinalities.unknownMaxCardinality();
+ }
+
+ @Nonnull
+ private Cardinalities intersectCardinalities(@Nonnull Iterable cardinalitiesIterable) {
+ //
+ // Merge all cardinalities in the iterable.
+ // If the cardinality in one member is unknown but not in the other, we'll take the more constraining one
+ //
+ var minCardinality = Cardinality.unknownCardinality();
+ var maxCardinality = Cardinality.unknownCardinality();
+ for (final var cardinalities : cardinalitiesIterable) {
+ if (minCardinality.isUnknown()) {
+ minCardinality = cardinalities.getMinCardinality();
} else {
- minCardinality = Cardinality.unknownCardinality();
+ if (!cardinalities.getMinCardinality().isUnknown()) {
+ minCardinality = Cardinality.ofCardinality(0);
+ } else {
+ minCardinality = Cardinality.unknownCardinality();
+ }
}
- }
- if (maxCardinality.isUnknown()) {
- maxCardinality = cardinalities.getMaxCardinality();
- } else {
- if (!cardinalities.getMaxCardinality().isUnknown()) {
- maxCardinality = Cardinality.ofCardinality(Math.min(maxCardinality.getCardinality(), cardinalities.getMaxCardinality().getCardinality()));
+ if (maxCardinality.isUnknown()) {
+ maxCardinality = cardinalities.getMaxCardinality();
} else {
- maxCardinality = Cardinality.unknownCardinality();
+ if (!cardinalities.getMaxCardinality().isUnknown()) {
+ maxCardinality = Cardinality.ofCardinality(Math.min(maxCardinality.getCardinality(), cardinalities.getMaxCardinality().getCardinality()));
+ } else {
+ maxCardinality = Cardinality.unknownCardinality();
+ }
}
}
- }
-
- return new Cardinalities(minCardinality, maxCardinality);
- }
- @Nonnull
- private Cardinalities unionCardinalities(@Nonnull Iterable cardinalitiesIterable) {
- //
- // Merge all cardinalities in the iterable.
- //
- final var iterator = cardinalitiesIterable.iterator();
-
- if (!iterator.hasNext()) {
- return Cardinalities.unknownMaxCardinality();
+ return new Cardinalities(minCardinality, maxCardinality);
}
- var cardinalities = iterator.next();
+ @Nonnull
+ private Cardinalities unionCardinalities(@Nonnull Iterable cardinalitiesIterable) {
+ //
+ // Merge all cardinalities in the iterable.
+ //
+ final var iterator = cardinalitiesIterable.iterator();
+
+ if (!iterator.hasNext()) {
+ return Cardinalities.unknownMaxCardinality();
+ }
- var minCardinality = cardinalities.getMinCardinality();
- var maxCardinality = cardinalities.getMaxCardinality();
- while (iterator.hasNext()) {
- cardinalities = iterator.next();
+ var cardinalities = iterator.next();
- if (!minCardinality.isUnknown()) {
- final var currentMinCardinality = cardinalities.getMinCardinality();
- if (currentMinCardinality.isUnknown()) {
- minCardinality = Cardinality.unknownCardinality();
- } else {
- minCardinality = Cardinality.ofCardinality(minCardinality.getCardinality() + currentMinCardinality.getCardinality());
+ var minCardinality = cardinalities.getMinCardinality();
+ var maxCardinality = cardinalities.getMaxCardinality();
+ while (iterator.hasNext()) {
+ cardinalities = iterator.next();
+
+ if (!minCardinality.isUnknown()) {
+ final var currentMinCardinality = cardinalities.getMinCardinality();
+ if (currentMinCardinality.isUnknown()) {
+ minCardinality = Cardinality.unknownCardinality();
+ } else {
+ minCardinality = Cardinality.ofCardinality(minCardinality.getCardinality() + currentMinCardinality.getCardinality());
+ }
}
- }
- if (!maxCardinality.isUnknown()) {
- final var currentMaxCardinality = cardinalities.getMaxCardinality();
- if (currentMaxCardinality.isUnknown()) {
- maxCardinality = Cardinality.unknownCardinality();
- } else {
- maxCardinality = Cardinality.ofCardinality(maxCardinality.getCardinality() + currentMaxCardinality.getCardinality());
+ if (!maxCardinality.isUnknown()) {
+ final var currentMaxCardinality = cardinalities.getMaxCardinality();
+ if (currentMaxCardinality.isUnknown()) {
+ maxCardinality = Cardinality.unknownCardinality();
+ } else {
+ maxCardinality = Cardinality.ofCardinality(maxCardinality.getCardinality() + currentMaxCardinality.getCardinality());
+ }
}
}
- }
- return new Cardinalities(minCardinality, maxCardinality);
- }
-
- @Nonnull
- private Cardinalities weakenCardinalities(@Nonnull Iterable cardinalitiesIterable) {
- //
- // Merge all cardinalities in the iterable.
- // If the cardinality in one member is unknown but not in the other, we'll take the less constraining one
- //
- final var iterator = cardinalitiesIterable.iterator();
-
- if (!iterator.hasNext()) {
- return Cardinalities.unknownMaxCardinality();
+ return new Cardinalities(minCardinality, maxCardinality);
}
- var cardinalities = iterator.next();
+ @Nonnull
+ private Cardinalities weakenCardinalities(@Nonnull Iterable