Search before asking
Paimon version
master, 475be566f (2.1-SNAPSHOT).
Compute Engine
Spark, through a procedure's where argument.
Minimal reproduce step
Pass a procedure filter that compares a column to NULL on both sides of a range:
CALL sys.compact(table => 'db.t', where => 'dt >= 1 AND dt <= null');
The call fails with a NullPointerException out of Between.optimize. PredicateBuilder.and asks it to merge the >= and <= on the same field, and it compares the two bounds directly:
Object lowerBound = greaterOrEqual.literals().get(0);
Object upperBound = lessOrEqual.literals().get(0);
if (compareLiteral(type, lowerBound, upperBound) >= 0) {
compareLiteral then runs ((Comparable<Object>) v1).compareTo(v2), which throws when v2 is null; with the null on the other side it falls through to RuntimeException("Unsupported type") instead.
The procedure path is what makes this reachable: ExpressionHelper.resolveFilter runs ConstantFolding only, not NullPropagation or ReplaceNullWithFalseInPredicate, so dt <= null survives resolution, and SparkV2FilterConverter passes the null literal through. A plain SELECT ... WHERE dt <= null does not reach it, because Spark's optimizer folds the comparison to false first.
What doesn't meet your expectations?
A null literal is a legal input elsewhere in the predicate framework, so it should not crash the optimizer. LeafBinaryFunction.test already treats a null literal as no match:
public boolean test(DataType type, Object field, List<Object> literals) {
Object literal = literals.get(0);
return field != null && literal != null && test(type, field, literal);
}
Merging two range predicates into a BETWEEN is an optimization; when one bound is null there is nothing to order, and the pair can simply stay unmerged and be evaluated by the rules above.
Anything else?
compareLiteral is also called from NotIn, NotBetween and LessThan. Those all null-check the literal before calling, or are only reached with non-null bounds, so Between.optimize is the one caller that got there with a null.
Are you willing to submit a PR?
Search before asking
Paimon version
master,
475be566f(2.1-SNAPSHOT).Compute Engine
Spark, through a procedure's
whereargument.Minimal reproduce step
Pass a procedure filter that compares a column to NULL on both sides of a range:
The call fails with a
NullPointerExceptionout ofBetween.optimize.PredicateBuilder.andasks it to merge the>=and<=on the same field, and it compares the two bounds directly:compareLiteralthen runs((Comparable<Object>) v1).compareTo(v2), which throws whenv2is null; with the null on the other side it falls through toRuntimeException("Unsupported type")instead.The procedure path is what makes this reachable:
ExpressionHelper.resolveFilterrunsConstantFoldingonly, notNullPropagationorReplaceNullWithFalseInPredicate, sodt <= nullsurvives resolution, andSparkV2FilterConverterpasses the null literal through. A plainSELECT ... WHERE dt <= nulldoes not reach it, because Spark's optimizer folds the comparison to false first.What doesn't meet your expectations?
A null literal is a legal input elsewhere in the predicate framework, so it should not crash the optimizer.
LeafBinaryFunction.testalready treats a null literal as no match:Merging two range predicates into a
BETWEENis an optimization; when one bound is null there is nothing to order, and the pair can simply stay unmerged and be evaluated by the rules above.Anything else?
compareLiteralis also called fromNotIn,NotBetweenandLessThan. Those all null-check the literal before calling, or are only reached with non-null bounds, soBetween.optimizeis the one caller that got there with a null.Are you willing to submit a PR?