diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/combinatorics/TopologicalSort.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/combinatorics/TopologicalSort.java index a61714e859..02c1ce76e3 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/combinatorics/TopologicalSort.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/combinatorics/TopologicalSort.java @@ -730,6 +730,10 @@ public static Optional> anyTopologicalOrderPermutation(@Nonnull fina * @return a permutation of the set that is topologically correctly ordered with respect to {@code dependsOnFn} */ public static Optional> anyTopologicalOrderPermutation(@Nonnull final PartiallyOrderedSet partiallyOrderedSet) { + if (partiallyOrderedSet.getDependencyMap().isEmpty()) { + // if there are no dependencies, just return a list copy of the set + return Optional.of(ImmutableList.copyOf(partiallyOrderedSet.getSet())); + } return anyTopologicalOrderPermutation(partiallyOrderedSet.getSet(), () -> new KahnIterable<>(PartiallyOrderedSet.of(partiallyOrderedSet.getSet(), partiallyOrderedSet.getDependencyMap().inverse()))); } diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/RecordQueryPlanComplexityException.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/RecordQueryPlanComplexityException.java index bf6acfb247..dd93bb6d89 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/RecordQueryPlanComplexityException.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/RecordQueryPlanComplexityException.java @@ -23,8 +23,8 @@ import com.apple.foundationdb.annotation.API; import com.apple.foundationdb.record.RecordCoreException; import com.apple.foundationdb.record.logging.LogMessageKeys; +import com.apple.foundationdb.record.query.plan.cascades.explain.ExplainPlanVisitor; import com.apple.foundationdb.record.query.plan.explain.ExplainLevel; -import com.apple.foundationdb.record.query.plan.explain.ExplainPlanVisitor; import com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan; import javax.annotation.Nonnull; diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/RecordQueryPlanner.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/RecordQueryPlanner.java index f2420bdf28..f1bcd08a7f 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/RecordQueryPlanner.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/RecordQueryPlanner.java @@ -74,9 +74,7 @@ import com.apple.foundationdb.record.query.expressions.RecordTypeKeyComparison; import com.apple.foundationdb.record.query.plan.cascades.ComparisonRange; import com.apple.foundationdb.record.query.plan.cascades.ComparisonRanges; -import com.apple.foundationdb.record.query.plan.cascades.explain.PlannerGraphProperty; -import com.apple.foundationdb.record.query.plan.cascades.properties.ComparisonsProperty; -import com.apple.foundationdb.record.query.plan.cascades.properties.FieldWithComparisonCountProperty; +import com.apple.foundationdb.record.query.plan.cascades.explain.PlannerGraphVisitor; import com.apple.foundationdb.record.query.plan.cascades.typing.Type; import com.apple.foundationdb.record.query.plan.planning.BooleanNormalizer; import com.apple.foundationdb.record.query.plan.planning.FilterSatisfiedMask; @@ -131,6 +129,9 @@ import java.util.function.Supplier; import java.util.stream.Collectors; +import static com.apple.foundationdb.record.query.plan.cascades.properties.ComparisonsProperty.comparisons; +import static com.apple.foundationdb.record.query.plan.cascades.properties.FieldWithComparisonCountProperty.fieldWithComparisonCount; + /** * The query planner. * @@ -333,7 +334,7 @@ public RecordQueryPlan plan(@Nonnull RecordQuery query, @Nonnull ParameterRelati if (logger.isTraceEnabled()) { logger.trace(KeyValueLogMessage.of("explain of plan", - "explain", PlannerGraphProperty.explain(plan))); + "explain", PlannerGraphVisitor.explain(plan))); } return plan; @@ -543,7 +544,8 @@ private ScoredPlan planFilter(@Nonnull PlanContext planContext, @Nonnull QueryCo final ScoredPlan withInJoin = planFilterWithInJoin(planContext, inExtractor, needOrdering); if (withInAsOrUnion != null) { if (withInJoin == null || withInAsOrUnion.score > withInJoin.score || - FieldWithComparisonCountProperty.evaluate(withInAsOrUnion.getPlan()) < FieldWithComparisonCountProperty.evaluate(withInJoin.getPlan())) { + fieldWithComparisonCount().evaluate(withInAsOrUnion.getPlan()) < + fieldWithComparisonCount().evaluate(withInJoin.getPlan())) { return withInAsOrUnion; } } @@ -943,7 +945,7 @@ private ScoredPlan computePlanProperties(@Nonnull PlanContext planContext, @Nonn } protected Set computeSargedComparisons(@Nonnull final RecordQueryPlan plan) { - return ComparisonsProperty.evaluate(plan); + return comparisons().evaluate(plan); } @Nullable diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/RecordQueryPlannerConfiguration.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/RecordQueryPlannerConfiguration.java index 9d1ed9d53c..c5775ae64a 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/RecordQueryPlannerConfiguration.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/RecordQueryPlannerConfiguration.java @@ -25,7 +25,7 @@ import com.apple.foundationdb.record.RecordPlannerConfigurationProto; import com.apple.foundationdb.record.query.RecordQuery; import com.apple.foundationdb.record.query.plan.cascades.CascadesRule; -import com.apple.foundationdb.record.query.plan.cascades.PlannerRuleSet; +import com.apple.foundationdb.record.query.plan.cascades.PlanningRuleSet; import com.apple.foundationdb.record.query.plan.cascades.rules.PredicateToLogicalUnionRule; import com.apple.foundationdb.record.query.plan.plans.QueryPlan; import com.apple.foundationdb.record.query.plan.serialization.PlanSerialization; @@ -605,11 +605,11 @@ public Builder setDisabledTransformationRules(@Nonnull final Set disabledTransformationRuleNames, @Nonnull PlannerRuleSet plannerRuleSet) { + public Builder setDisabledTransformationRuleNames(@Nonnull final Set disabledTransformationRuleNames, @Nonnull PlanningRuleSet planningRuleSet) { protoBuilder.clearDisabledTransformationRules() .addAllDisabledTransformationRules(disabledTransformationRuleNames); return this; diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/CascadesCostModel.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/CascadesCostModel.java index 78c19faf43..fe9497f93c 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/CascadesCostModel.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/CascadesCostModel.java @@ -26,15 +26,10 @@ import com.apple.foundationdb.record.query.plan.QueryPlanner.IndexScanPreference; import com.apple.foundationdb.record.query.plan.RecordQueryPlannerConfiguration; import com.apple.foundationdb.record.query.plan.cascades.expressions.RelationalExpression; -import com.apple.foundationdb.record.query.plan.cascades.properties.CardinalitiesProperty; import com.apple.foundationdb.record.query.plan.cascades.properties.CardinalitiesProperty.Cardinalities; import com.apple.foundationdb.record.query.plan.cascades.properties.CardinalitiesProperty.Cardinality; -import com.apple.foundationdb.record.query.plan.cascades.properties.ComparisonsProperty; -import com.apple.foundationdb.record.query.plan.cascades.properties.FindExpressionProperty; +import com.apple.foundationdb.record.query.plan.cascades.properties.ExpressionDepthProperty; import com.apple.foundationdb.record.query.plan.cascades.properties.NormalizedResidualPredicateProperty; -import com.apple.foundationdb.record.query.plan.cascades.properties.RelationalExpressionDepthProperty; -import com.apple.foundationdb.record.query.plan.cascades.properties.TypeFilterCountProperty; -import com.apple.foundationdb.record.query.plan.cascades.properties.UnmatchedFieldsCountProperty; import com.apple.foundationdb.record.query.plan.plans.RecordQueryCoveringIndexPlan; import com.apple.foundationdb.record.query.plan.plans.RecordQueryFetchFromPartialRecordPlan; import com.apple.foundationdb.record.query.plan.plans.RecordQueryInJoinPlan; @@ -55,11 +50,18 @@ import java.util.function.Supplier; import static com.apple.foundationdb.record.Bindings.Internal.CORRELATION; +import static com.apple.foundationdb.record.query.plan.cascades.properties.CardinalitiesProperty.cardinalities; +import static com.apple.foundationdb.record.query.plan.cascades.properties.ComparisonsProperty.comparisons; +import static com.apple.foundationdb.record.query.plan.cascades.properties.ExpressionDepthProperty.fetchDepth; +import static com.apple.foundationdb.record.query.plan.cascades.properties.ExpressionDepthProperty.typeFilterDepth; +import static com.apple.foundationdb.record.query.plan.cascades.properties.TypeFilterCountProperty.typeFilterCount; +import static com.apple.foundationdb.record.query.plan.cascades.properties.UnmatchedFieldsCountProperty.unmatchedFieldsCount; /** * A comparator implementing the current heuristic cost model for the {@link CascadesPlanner}. */ @API(API.Status.EXPERIMENTAL) +@SuppressWarnings("PMD.TooManyStaticImports") public class CascadesCostModel implements Comparator { @Nonnull private static final Set> interestingPlanClasses = @@ -89,13 +91,13 @@ public int compare(@Nonnull RelationalExpression a, @Nonnull RelationalExpressio } final Map, Set> planOpsMapA = - FindExpressionProperty.evaluate(interestingPlanClasses, a); + FindExpressionVisitor.evaluate(interestingPlanClasses, a); final Map, Set> planOpsMapB = - FindExpressionProperty.evaluate(interestingPlanClasses, b); + FindExpressionVisitor.evaluate(interestingPlanClasses, b); - final Cardinalities cardinalitiesA = CardinalitiesProperty.evaluate(a); - final Cardinalities cardinalitiesB = CardinalitiesProperty.evaluate(b); + final Cardinalities cardinalitiesA = cardinalities().evaluate(a); + final Cardinalities cardinalitiesB = cardinalities().evaluate(b); // // Technically, both cardinalities at runtime must be the same. The question is if we can actually @@ -155,8 +157,8 @@ public int compare(@Nonnull RelationalExpression a, @Nonnull RelationalExpressio return inPlanVsOtherOptional.getAsInt(); } - final int typeFilterCountA = TypeFilterCountProperty.evaluate(a); - final int typeFilterCountB = TypeFilterCountProperty.evaluate(b); + final int typeFilterCountA = typeFilterCount().evaluate(a); + final int typeFilterCountB = typeFilterCount().evaluate(b); // special case // if one plan is a primary scan with a type filter and the other one is an index scan with the same number of @@ -174,8 +176,8 @@ public int compare(@Nonnull RelationalExpression a, @Nonnull RelationalExpressio return typeFilterCountCompare; } - int typeFilterPositionCompare = Integer.compare(RelationalExpressionDepthProperty.TYPE_FILTER_DEPTH.evaluate(b), - RelationalExpressionDepthProperty.TYPE_FILTER_DEPTH.evaluate(a)); // prefer the one with a deeper type filter + // prefer the one with a deeper type filter + int typeFilterPositionCompare = Integer.compare(typeFilterDepth().evaluate(b), typeFilterDepth().evaluate(a)); if (typeFilterPositionCompare != 0) { return typeFilterPositionCompare; } @@ -193,8 +195,8 @@ public int compare(@Nonnull RelationalExpression a, @Nonnull RelationalExpressio return numFetchesCompare; } - final int fetchDepthB = RelationalExpressionDepthProperty.FETCH_DEPTH.evaluate(b); - final int fetchDepthA = RelationalExpressionDepthProperty.FETCH_DEPTH.evaluate(a); + final int fetchDepthB = fetchDepth().evaluate(b); + final int fetchDepthA = fetchDepth().evaluate(a); int fetchPositionCompare = Integer.compare(fetchDepthA, fetchDepthB); if (fetchPositionCompare != 0) { return fetchPositionCompare; @@ -211,14 +213,14 @@ public int compare(@Nonnull RelationalExpression a, @Nonnull RelationalExpressio } } - int distinctFilterPositionCompare = Integer.compare(RelationalExpressionDepthProperty.DISTINCT_FILTER_DEPTH.evaluate(b), - RelationalExpressionDepthProperty.DISTINCT_FILTER_DEPTH.evaluate(a)); + int distinctFilterPositionCompare = Integer.compare(ExpressionDepthProperty.distinctDepth().evaluate(b), + ExpressionDepthProperty.distinctDepth().evaluate(a)); if (distinctFilterPositionCompare != 0) { return distinctFilterPositionCompare; } - int ufpA = UnmatchedFieldsCountProperty.evaluate(a); - int ufpB = UnmatchedFieldsCountProperty.evaluate(b); + int ufpA = unmatchedFieldsCount().evaluate(a); + int ufpB = unmatchedFieldsCount().evaluate(b); if (ufpA != ufpB) { return Integer.compare(ufpA, ufpB); } @@ -266,9 +268,9 @@ public int compare(@Nonnull RelationalExpression a, @Nonnull RelationalExpressio @Nonnull private Cardinality maxOfMaxCardinalitiesOfAllDataAccesses(@Nonnull final Map, Set> planOpsMap) { - return FindExpressionProperty.slice(planOpsMap, RecordQueryScanPlan.class, RecordQueryPlanWithIndex.class, RecordQueryCoveringIndexPlan.class) + return FindExpressionVisitor.slice(planOpsMap, RecordQueryScanPlan.class, RecordQueryPlanWithIndex.class, RecordQueryCoveringIndexPlan.class) .stream() - .map(plan -> CardinalitiesProperty.evaluate(plan).getMaxCardinality()) + .map(plan -> cardinalities().evaluate(plan).getMaxCardinality()) .reduce(Cardinality.ofCardinality(0), (l, r) -> { if (l.isUnknown()) { @@ -314,8 +316,8 @@ private OptionalInt comparePrimaryScanToIndexScan(@Nonnull RelationalExpression isSingularIndexScanWithFetch(planOpsMapIndexScan)) { if (typeFilterCountPrimaryScan > 0 && typeFilterCountIndexScan == 0) { - final var primaryScanComparisons = ComparisonsProperty.evaluate(primaryScan); - final var indexScanComparisons = ComparisonsProperty.evaluate(indexScan); + final var primaryScanComparisons = comparisons().evaluate(primaryScan); + final var indexScanComparisons = comparisons().evaluate(indexScan); // // The primary scan side has a type filter in it, the index scan side does not. The primary side @@ -373,7 +375,7 @@ private OptionalInt compareInOperator(@Nonnull final RelationalExpression leftEx // If no scan comparison on the in union side uses a comparison to the in-values, then the in union // plan is not useful. - final Set scanComparisonsSet = ComparisonsProperty.evaluate(leftExpression); + final Set scanComparisonsSet = comparisons().evaluate(leftExpression); final ImmutableSet scanComparisonsCorrelatedTo = scanComparisonsSet @@ -429,6 +431,6 @@ private static OptionalInt flipFlop(final Supplier variantA, @SafeVarargs private static int count(@Nonnull final Map, Set> expressionsMap, @Nonnull final Class... interestingClasses) { - return FindExpressionProperty.slice(expressionsMap, interestingClasses).size(); + return FindExpressionVisitor.slice(expressionsMap, interestingClasses).size(); } } diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/CascadesPlanner.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/CascadesPlanner.java index 3afd814791..28ba0774e4 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/CascadesPlanner.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/CascadesPlanner.java @@ -30,7 +30,6 @@ import com.apple.foundationdb.record.query.IndexQueryabilityFilter; import com.apple.foundationdb.record.query.ParameterRelationshipGraph; import com.apple.foundationdb.record.query.RecordQuery; -import com.apple.foundationdb.record.query.plan.explain.ExplainPlanVisitor; import com.apple.foundationdb.record.query.plan.QueryPlanConstraint; import com.apple.foundationdb.record.query.plan.QueryPlanInfo; import com.apple.foundationdb.record.query.plan.QueryPlanInfoKeys; @@ -42,11 +41,12 @@ import com.apple.foundationdb.record.query.plan.cascades.debug.Debugger; import com.apple.foundationdb.record.query.plan.cascades.debug.Debugger.Location; import com.apple.foundationdb.record.query.plan.cascades.debug.RestartException; -import com.apple.foundationdb.record.query.plan.cascades.explain.PlannerGraphProperty; +import com.apple.foundationdb.record.query.plan.cascades.explain.PlannerGraphVisitor; import com.apple.foundationdb.record.query.plan.cascades.expressions.RelationalExpression; import com.apple.foundationdb.record.query.plan.cascades.matching.structure.BindingMatcher; import com.apple.foundationdb.record.query.plan.cascades.matching.structure.PlannerBindings; import com.apple.foundationdb.record.query.plan.cascades.matching.structure.ReferenceMatchers; +import com.apple.foundationdb.record.query.plan.cascades.explain.ExplainPlanVisitor; import com.apple.foundationdb.record.query.plan.plans.QueryPlan; import com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan; import com.google.common.base.Suppliers; @@ -83,7 +83,7 @@ * Like many optimization frameworks, Cascades is driven by sets of {@link CascadesRule}s that can be defined for * {@link RelationalExpression}s, {@link PartialMatch}es and {@link MatchPartition}s, each of which describes a * particular transformation and encapsulates the logic for determining its applicability and applying it. The planner - * searches through its {@link PlannerRuleSet} to find a matching rule and then executes that rule, creating zero or + * searches through its {@link PlanningRuleSet} to find a matching rule and then executes that rule, creating zero or * more additional {@code PlannerExpression}s and/or zero or more additional {@link PartialMatch}es. A rule is defined by: *

*