expressionProperty) {
+ throw new UnsupportedOperationException("method cannot provide plans");
+ }
+
+ @Nonnull
+ public List toPlanPartitions() {
+ throw new UnsupportedOperationException("method cannot provide plan partitions");
+ }
+}
diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/ExpressionProperty.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/ExpressionProperty.java
index 3576c6f840..0e42e0d190 100644
--- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/ExpressionProperty.java
+++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/ExpressionProperty.java
@@ -3,7 +3,7 @@
*
* This source file is part of the FoundationDB open source project
*
- * Copyright 2015-2018 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,158 +20,72 @@
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.RelationalExpressionVisitor;
-import com.apple.foundationdb.record.query.plan.cascades.matching.structure.BindingMatcher;
-import com.google.common.collect.Lists;
+import com.apple.foundationdb.record.query.plan.cascades.expressions.RelationalExpressionVisitorWithDefaults;
+import com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan;
+import com.apple.foundationdb.record.query.plan.plans.RecordQueryPlanVisitor;
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 ExpressionProperty} 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 ExpressionProperty}.
- *
- *
- * 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
+ * Base interface to capture properties for expressions.
+ * An instance of this usually class serves as a key in maps much like an enum, but provides strong typing.
+ * In particular, the following holds true for all properties:
+ *
+ * -
+ * Properties are singletons (or multitons; enumerated singletons) of implementors of this class.
+ *
+ * -
+ * Properties create a {@link RelationalExpressionVisitor} using the method {@link #createVisitor()}}. That
+ * means that properties use visitors that operate on all kinds of relational expressions (from a Java typing
+ * standpoint). In reality, as some properties are meaningless for some expressions but not others, the visitor
+ * may decide to throw an exception for a class it cannot process.
+ * There are some helpers that can transform e.g. a {@link RecordQueryPlanVisitor} into a
+ * {@link RelationalExpressionVisitor} which add functionality to throw an exception when a non-plan is visited.
+ * This does not weaken the usual contracts that these visitor classes have. It just allows for e.g.
+ * {@link RecordQueryPlanVisitor}s to be used in place of {@link RelationalExpressionVisitor}s.
+ *
+ * -
+ * Properties are not dependent on parameters but are computed by only traversing the
+ * {@link RelationalExpression} DAG. The method {@link #createVisitor()} does not use any parameters.
+ *
+ * -
+ * Properties may or may not be used in an {@link ExpressionPropertiesMap}. If the property is maintained in
+ * such a map for a given reference, the implementor of the visitor may assume that the property values for
+ * children expressions are also maintained in such a map and therefore accessible though the individual
+ * child's {@link Reference}. In this way, expensive re-computation of the property value can be avoided.
+ *
+ *
+ * @param the type representing the actual property
*/
-@API(API.Status.EXPERIMENTAL)
-public interface ExpressionProperty extends RelationalExpressionVisitor {
- /**
- * 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);
-
+public interface ExpressionProperty {
/**
- * 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
+ * Method to narrow the type from {@link Object} to the declared type of the attribute. Note that
+ * the caller must guarantee that the narrowing is well-defined and successful.
+ * @param object an object that actually is of dynamic type {@code P}
+ * @return the narrowed object of type {@code P}
*/
@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 final T rangesOverResult) {
- // since we visit the expression reference under the quantifier, we can insist that rangesOverResult is never null
- return Objects.requireNonNull(rangesOverResult);
+ @SuppressWarnings("unchecked")
+ default P narrowAttribute(@Nonnull final Object object) {
+ return (P)object;
}
@Nonnull
- @Override
- default T visitDefault(@Nonnull final RelationalExpression relationalExpression) {
- final var quantifierResults = visitQuantifiers(relationalExpression);
- return evaluateAtExpression(relationalExpression, quantifierResults);
- }
+ RelationalExpressionVisitor createVisitor();
@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;
+ static RelationalExpressionVisitor toExpressionVisitor(@Nonnull final RecordQueryPlanVisitor planVisitor) {
+ return new RelationalExpressionVisitorWithDefaults<>() {
+ @Nonnull
+ @Override
+ public T visitDefault(@Nonnull final RelationalExpression element) {
+ if (element instanceof RecordQueryPlan) {
+ return planVisitor.visit((RecordQueryPlan)element);
+ }
+ throw new UnsupportedOperationException("plan visitor does not support visitor interface for ordinary expressions");
+ }
+ };
}
}
diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/properties/FindExpressionProperty.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/FindExpressionVisitor.java
similarity index 85%
rename from fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/properties/FindExpressionProperty.java
rename to fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/FindExpressionVisitor.java
index ab37c6f08e..4e217fcd2e 100644
--- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/properties/FindExpressionProperty.java
+++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/FindExpressionVisitor.java
@@ -1,9 +1,9 @@
/*
- * FindExpressionProperty.java
+ * FindExpressionVisitor.java
*
* This source file is part of the FoundationDB open source project
*
- * Copyright 2015-2019 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.
@@ -18,13 +18,9 @@
* limitations under the License.
*/
-package com.apple.foundationdb.record.query.plan.cascades.properties;
+package com.apple.foundationdb.record.query.plan.cascades;
-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.LinkedIdentitySet;
import com.apple.foundationdb.record.query.plan.cascades.expressions.RelationalExpression;
-import com.apple.foundationdb.record.query.plan.cascades.expressions.RelationalExpressionVisitorWithDefaults;
import com.google.common.base.Verify;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -39,12 +35,12 @@
import java.util.Set;
/**
- * A property that determines finds all occurrences of expressions of interest in a planner graph.
+ * A visitor that finds all occurrences of expressions of interest in a planner graph.
*/
-public class FindExpressionProperty implements ExpressionProperty