Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,16 @@ public static String name(Expression e) {
return e instanceof NamedExpression ne ? ne.name() : e.sourceText();
}

public static boolean isNull(Expression e) {
return e.dataType() == DataType.NULL || (e.foldable() && e.fold() == null);
/**
* Is this {@linkplain Expression} <strong>guaranteed</strong> to have
* only the {@code null} value. {@linkplain Expression}s that
* {@link Expression#fold()} to {@code null} <strong>may</strong>
* return {@code false} here, but should <strong>eventually</strong> be folded
* into a {@link Literal} containing {@code null} which will return
* {@code true} from here.
*/
public static boolean isGuaranteedNull(Expression e) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: isGuaranteedNull makes me wonder what is "null" and more relevant when is it not guaranteed :)
I'd find something like isNullTypeOrLiteral clearer, but just a nitty nit.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. I think I'm going to keep it because everything feels not so great. Maybe valueIsGuaranteedNull or something? I'm not sure that makes it more descriptive.

return e.dataType() == DataType.NULL || (e instanceof Literal lit && lit.value() == null);
}

public static List<String> names(Collection<? extends Expression> e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,14 @@ public Expression replaceChildren(List<Expression> newChildren) {
public boolean foldable() {
// QL's In fold()s to null, if value() is null, but isn't foldable() unless all children are
// TODO: update this null check in QL too?
return Expressions.isNull(value)
return Expressions.isGuaranteedNull(value)
|| Expressions.foldable(children())
|| (Expressions.foldable(list) && list.stream().allMatch(Expressions::isNull));
|| (Expressions.foldable(list) && list.stream().allMatch(Expressions::isGuaranteedNull));
}

@Override
public Object fold() {
if (Expressions.isNull(value) || list.stream().allMatch(Expressions::isNull)) {
if (Expressions.isGuaranteedNull(value) || list.stream().allMatch(Expressions::isGuaranteedNull)) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious about this one. I feel like it's tricky to be sure this is right.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to try removing this line from ESQL entirely to see what happens.... Learning!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only one test fails if I zap this - testFoldNullListInToLocalRelation - which I don't really have the background to know. I don't think we should remove this, but I want to make sure this is still running properly in this way.

return null;
}
return super.fold();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,21 @@ public Expression rule(Expression e) {
// perform this early to prevent the rule from converting the null filter into nullifying the whole expression
// P.S. this could be done inside the Aggregate but this place better centralizes the logic
if (e instanceof AggregateFunction agg) {
if (Expressions.isNull(agg.filter())) {
if (Expressions.isGuaranteedNull(agg.filter())) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure the changes to this file are fine because, if we don't fold to null immediately, we will fold constants, which will yield literal null, which will then fold null here.

return agg.withFilter(Literal.of(agg.filter(), false));
}
}

if (result != e) {
return result;
} else if (e instanceof In in) {
if (Expressions.isNull(in.value())) {
if (Expressions.isGuaranteedNull(in.value())) {
return Literal.of(in, null);
}
} else if (e instanceof Alias == false
&& e.nullable() == Nullability.TRUE
&& e instanceof Categorize == false
&& Expressions.anyMatch(e.children(), Expressions::isNull)) {
&& Expressions.anyMatch(e.children(), Expressions::isGuaranteedNull)) {
return Literal.of(e, null);
}
return e;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ protected LogicalPlan rule(Filter filter) {
if (TRUE.equals(condition)) {
return filter.child();
}
if (FALSE.equals(condition) || Expressions.isNull(condition)) {
if (FALSE.equals(condition) || Expressions.isGuaranteedNull(condition)) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure these are fine because we'll have rewritten all null valued expressions into literal nulls by the time we make it here. But I'd love is someone could confirm that we have a test for the plan bits.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And that someone could be me....

return PruneEmptyPlans.skipPlan(filter);
}
}
Expand All @@ -42,8 +42,8 @@ protected LogicalPlan rule(Filter filter) {

private static Expression foldBinaryLogic(BinaryLogic binaryLogic) {
if (binaryLogic instanceof Or or) {
boolean nullLeft = Expressions.isNull(or.left());
boolean nullRight = Expressions.isNull(or.right());
boolean nullLeft = Expressions.isGuaranteedNull(or.left());
boolean nullRight = Expressions.isGuaranteedNull(or.right());
if (nullLeft && nullRight) {
return new Literal(binaryLogic.source(), null, DataType.NULL);
}
Expand All @@ -55,7 +55,7 @@ private static Expression foldBinaryLogic(BinaryLogic binaryLogic) {
}
}
if (binaryLogic instanceof And and) {
if (Expressions.isNull(and.left()) || Expressions.isNull(and.right())) {
if (Expressions.isGuaranteedNull(and.left()) || Expressions.isGuaranteedNull(and.right())) {
return new Literal(binaryLogic.source(), null, DataType.NULL);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public Expression rule(In in) {
List<Expression> foldables = new ArrayList<>(in.list().size());
List<Expression> nonFoldables = new ArrayList<>(in.list().size());
in.list().forEach(e -> {
if (e.foldable() && Expressions.isNull(e) == false) { // keep `null`s, needed for the 3VL
if (e.foldable() && Expressions.isGuaranteedNull(e) == false) { // keep `null`s, needed for the 3VL
foldables.add(e);
} else {
nonFoldables.add(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4820,7 +4820,7 @@ private static boolean oneLeaveIsNull(Expression e) {

e.forEachUp(node -> {
if (node.children().size() == 0) {
result.set(result.get() || Expressions.isNull(node));
result.set(result.get() || Expressions.isGuaranteedNull(node));
}
});

Expand Down