Skip to content

QueryPredicate simplification rule for PredicateWithValueAndRanges #3407

Closed
@hatyo

Description

@hatyo

There is an interesting behavior in the construction of PredicateWithValueAndRanges that prevents simplification from working correctly.

If we have the following two predicates in a query:

P1 = PredicateWithValueAndRanges('False, [Null, Null])
P2 = PredicateWithValueAndRanges('False, ['False, 'False])

That is, the customer created a conjunction of the above predicates and passed it to the SelectExpression. If these predicates are kept as-is, then the simplification engine will be to recognize that they are constant predicates and simplifies them correctly:

P1 ^ P2
≡ PredicateWithValueAndRanges('False, [Null, Null]) ^ PredicateWithValueAndRanges('False, ['False, 'False])
≡ ConstantPredicate.FALSE ^ PredicateWithValueAndRanges('False, ['False, 'False]) -- ConstantFoldingPredicateWithRangesRule
≡ ConstantPredicate.FALSE ^ ConstantPredicate.FALSE -- ConstantFoldingPredicateWithRangesRule
≡ ConstantPredicate.FALSE -- AnnulmentAndRule

However, in the SelectExpression construction logic, there is handling of conjunctions that attempt to simplify predicates in a way that makes it easier to match against (filtered) indexes:

private static List<QueryPredicate> simplifyConjunction(@Nonnull final Value value,
@Nonnull final Collection<PredicateWithValue> predicates) {
final ImmutableList.Builder<QueryPredicate> result = ImmutableList.builder();
final var rangeBuilder = RangeConstraints.newBuilder();
for (final var predicate : predicates) {
if (predicate instanceof ValuePredicate) {
final var predicateRange = ((ValuePredicate)predicate).getComparison();
if (!rangeBuilder.addComparisonMaybe(predicateRange)) {
result.add(value.withComparison(predicateRange)); // give up.
}
} else if (predicate instanceof PredicateWithValueAndRanges && ((PredicateWithValueAndRanges)predicate).isSargable()) {
final var predicateRange = Iterables.getOnlyElement(((PredicateWithValueAndRanges)predicate).getRanges());
rangeBuilder.add(predicateRange);
} else {
result.add(predicate);
}
}
// If the compile-time range is defined, create a sargable from it.
final var rangeMaybe = rangeBuilder.build();
rangeMaybe.ifPresent(range -> result.add(PredicateWithValueAndRanges.sargable(value, range)));
return result.build();
}

That logic creates a new PredicateWithValueAndRanges that looks like this:

P' = PredicateWithValueAndRanges('False, [Null, Null] AND ['False, 'False])

In other words, it creates a predicate that attempts to match 'False against a range of two singletons [Null, Null], and [False, False]. Albeit being a clear contradiction, the simplification engine will fail to pattern match it, since it has two ranges not one. And therefore, will not be simplified.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestperformancePerformance issuesplannerRelated to the query planner

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions