Search in sources :

Example 1 with PlanHashable

use of com.apple.foundationdb.record.PlanHashable in project fdb-record-layer by FoundationDB.

the class CascadesCostModel method compare.

@Override
public int compare(@Nonnull RelationalExpression a, @Nonnull RelationalExpression b) {
    if (a instanceof RecordQueryPlan && !(b instanceof RecordQueryPlan)) {
        return -1;
    }
    if (!(a instanceof RecordQueryPlan) && b instanceof RecordQueryPlan) {
        return 1;
    }
    int unsatisfiedFilterCompare = Integer.compare(PredicateCountProperty.evaluate(a), PredicateCountProperty.evaluate(b));
    if (unsatisfiedFilterCompare != 0) {
        return unsatisfiedFilterCompare;
    }
    final Map<Class<? extends RelationalExpression>, Integer> planCountMapA = ExpressionCountProperty.evaluate(interestingPlanClasses, a);
    final Map<Class<? extends RelationalExpression>, Integer> planCountMapB = ExpressionCountProperty.evaluate(interestingPlanClasses, b);
    final int numDataAccessA = planCountMapA.getOrDefault(RecordQueryScanPlan.class, 0) + planCountMapA.getOrDefault(RecordQueryPlanWithIndex.class, 0) + planCountMapA.getOrDefault(RecordQueryCoveringIndexPlan.class, 0);
    final int numDataAccessB = planCountMapB.getOrDefault(RecordQueryScanPlan.class, 0) + planCountMapB.getOrDefault(RecordQueryPlanWithIndex.class, 0) + planCountMapB.getOrDefault(RecordQueryCoveringIndexPlan.class, 0);
    int countDataAccessesCompare = Integer.compare(numDataAccessA, numDataAccessB);
    if (countDataAccessesCompare != 0) {
        return countDataAccessesCompare;
    }
    // special case
    // if one plan is a inUnion plan
    final OptionalInt inPlanVsOtherOptional = flipFlop(() -> compareInOperator(a, b), () -> compareInOperator(b, a));
    if (inPlanVsOtherOptional.isPresent() && inPlanVsOtherOptional.getAsInt() != 0) {
        return inPlanVsOtherOptional.getAsInt();
    }
    final int typeFilterCountA = TypeFilterCountProperty.evaluate(a);
    final int typeFilterCountB = TypeFilterCountProperty.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
    // unsatisfied filters (i.e. both plans use the same number of filters as search arguments), we break the tie
    // by using a planning flag
    final OptionalInt primaryScanVsIndexScanCompareOptional = flipFlop(() -> comparePrimaryScanToIndexScan(planCountMapA, planCountMapB, typeFilterCountA), () -> comparePrimaryScanToIndexScan(planCountMapB, planCountMapA, typeFilterCountB));
    if (primaryScanVsIndexScanCompareOptional.isPresent() && primaryScanVsIndexScanCompareOptional.getAsInt() != 0) {
        return primaryScanVsIndexScanCompareOptional.getAsInt();
    }
    int typeFilterCountCompare = Integer.compare(typeFilterCountA, typeFilterCountB);
    if (typeFilterCountCompare != 0) {
        return typeFilterCountCompare;
    }
    int typeFilterPositionCompare = Integer.compare(RelationalExpressionDepthProperty.TYPE_FILTER_DEPTH.evaluate(b), // prefer the one with a deeper type filter
    RelationalExpressionDepthProperty.TYPE_FILTER_DEPTH.evaluate(a));
    if (typeFilterPositionCompare != 0) {
        return typeFilterPositionCompare;
    }
    if (planCountMapA.getOrDefault(RecordQueryPlanWithIndex.class, 0) + planCountMapA.getOrDefault(RecordQueryCoveringIndexPlan.class, 0) > 0 && planCountMapB.getOrDefault(RecordQueryPlanWithIndex.class, 0) + planCountMapB.getOrDefault(RecordQueryCoveringIndexPlan.class, 0) > 0) {
        // both plans are index scans
        // how many fetches are there, regular index scans fetch when they scan
        int numFetchesA = planCountMapA.getOrDefault(RecordQueryPlanWithIndex.class, 0) + planCountMapA.getOrDefault(RecordQueryFetchFromPartialRecordPlan.class, 0);
        int numFetchesB = planCountMapB.getOrDefault(RecordQueryPlanWithIndex.class, 0) + planCountMapB.getOrDefault(RecordQueryFetchFromPartialRecordPlan.class, 0);
        final int numFetchesCompare = Integer.compare(numFetchesA, numFetchesB);
        if (numFetchesCompare != 0) {
            return numFetchesCompare;
        }
        final int fetchDepthB = RelationalExpressionDepthProperty.FETCH_DEPTH.evaluate(b);
        final int fetchDepthA = RelationalExpressionDepthProperty.FETCH_DEPTH.evaluate(a);
        int fetchPositionCompare = Integer.compare(fetchDepthA, fetchDepthB);
        if (fetchPositionCompare != 0) {
            return fetchPositionCompare;
        }
        // All things being equal for index vs covering index -- there are plans competing of the following shape
        // FETCH(COVERING(INDEX_SCAN())) vs INDEX_SCAN() that count identically up to here. Let the plan win that
        // has fewer actual FETCH() operators.
        int numFetchOperatorsCompare = Integer.compare(planCountMapA.getOrDefault(RecordQueryFetchFromPartialRecordPlan.class, 0), planCountMapB.getOrDefault(RecordQueryFetchFromPartialRecordPlan.class, 0));
        if (numFetchOperatorsCompare != 0) {
            return numFetchOperatorsCompare;
        }
    }
    int distinctFilterPositionCompare = Integer.compare(RelationalExpressionDepthProperty.DISTINCT_FILTER_DEPTH.evaluate(b), RelationalExpressionDepthProperty.DISTINCT_FILTER_DEPTH.evaluate(a));
    if (distinctFilterPositionCompare != 0) {
        return distinctFilterPositionCompare;
    }
    int ufpA = UnmatchedFieldsCountProperty.evaluate(planContext, a);
    int ufpB = UnmatchedFieldsCountProperty.evaluate(planContext, b);
    if (ufpA != ufpB) {
        return Integer.compare(ufpA, ufpB);
    }
    // 
    // If a plan has more in-join sources it is preferable.
    // 
    final int numSourcesInJoinA = planCountMapA.getOrDefault(RecordQueryInJoinPlan.class, 0);
    final int numSourcesInJoinB = planCountMapB.getOrDefault(RecordQueryInJoinPlan.class, 0);
    int countSourcesInJoinCompare = Integer.compare(numSourcesInJoinB, numSourcesInJoinA);
    if (countSourcesInJoinCompare != 0) {
        // bigger one wins
        return countSourcesInJoinCompare;
    }
    // 
    if ((a instanceof PlanHashable) && (b instanceof PlanHashable)) {
        int hA = ((PlanHashable) a).planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS);
        int hB = ((PlanHashable) b).planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS);
        return Integer.compare(hA, hB);
    }
    return 0;
}
Also used : RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) PlanHashable(com.apple.foundationdb.record.PlanHashable) RecordQueryFetchFromPartialRecordPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryFetchFromPartialRecordPlan) RecordQueryCoveringIndexPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryCoveringIndexPlan) RecordQueryPlanWithIndex(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlanWithIndex) OptionalInt(java.util.OptionalInt)

Aggregations

PlanHashable (com.apple.foundationdb.record.PlanHashable)1 RecordQueryCoveringIndexPlan (com.apple.foundationdb.record.query.plan.plans.RecordQueryCoveringIndexPlan)1 RecordQueryFetchFromPartialRecordPlan (com.apple.foundationdb.record.query.plan.plans.RecordQueryFetchFromPartialRecordPlan)1 RecordQueryPlan (com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan)1 RecordQueryPlanWithIndex (com.apple.foundationdb.record.query.plan.plans.RecordQueryPlanWithIndex)1 OptionalInt (java.util.OptionalInt)1