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;
}
Aggregations