-
Notifications
You must be signed in to change notification settings - Fork 25.6k
SQL: Reduce number of ranges generated for comparisons #30267
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
d507741
84872b4
f0d7d4c
a3aca69
25aed21
a2fb0d2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,8 +9,11 @@ | |
| import org.elasticsearch.xpack.sql.plan.logical.LogicalPlan; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
| import java.util.function.BiFunction; | ||
|
|
||
| import static java.util.Collections.emptyList; | ||
| import static java.util.Collections.singletonList; | ||
|
|
||
| public abstract class Predicates { | ||
|
|
||
|
|
@@ -22,7 +25,7 @@ public static List<Expression> splitAnd(Expression exp) { | |
| list.addAll(splitAnd(and.right())); | ||
| return list; | ||
| } | ||
| return Collections.singletonList(exp); | ||
| return singletonList(exp); | ||
| } | ||
|
|
||
| public static List<Expression> splitOr(Expression exp) { | ||
|
|
@@ -33,15 +36,38 @@ public static List<Expression> splitOr(Expression exp) { | |
| list.addAll(splitOr(or.right())); | ||
| return list; | ||
| } | ||
| return Collections.singletonList(exp); | ||
| return singletonList(exp); | ||
| } | ||
|
|
||
| public static Expression combineOr(List<Expression> exps) { | ||
| return exps.stream().reduce((l, r) -> new Or(l.location(), l, r)).orElse(null); | ||
| return combine(exps, (l, r) -> new Or(l.location(), l, r)); | ||
| } | ||
|
|
||
| public static Expression combineAnd(List<Expression> exps) { | ||
| return exps.stream().reduce((l, r) -> new And(l.location(), l, r)).orElse(null); | ||
| return combine(exps, (l, r) -> new And(l.location(), l, r)); | ||
| } | ||
|
|
||
| /** | ||
| * Build a binary 'pyramid' - while a bit longer this method creates a balanced tree as oppose to a plain | ||
|
||
| * recursive approach that creates an unbalanced one (either to the left or right). | ||
| */ | ||
| private static Expression combine(List<Expression> exps, BiFunction<Expression, Expression, Expression> combiner) { | ||
| if (exps.isEmpty()) { | ||
| return null; | ||
| } | ||
|
|
||
| List<Expression> result = new ArrayList<>(exps); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if this should be
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I talked with @costin earlier about this - he wants to keep the order the same and my proposal doesn't. What about this? Your version works but
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you'd like we can follow this up in a different PR. I've added a comment to the loop explaining that it updates the list (and thus why it uses the temporary variable). P.S. The list is created just above as a copy for this specific reason.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm fine with leaving it, yeah. I did want a prettier one but if this is what we can do, it'll do. |
||
|
|
||
| while (result.size() > 1) { | ||
| // combine (in place) expressions in pairs | ||
| for (int i = 0; i < result.size() - 1; i++) { | ||
| Expression l = result.remove(i); | ||
| Expression r = result.remove(i); | ||
| result.add(i, combiner.apply(l, r)); | ||
| } | ||
| } | ||
|
|
||
| return result.get(0); | ||
| } | ||
|
|
||
| public static List<Expression> inCommon(List<Expression> l, List<Expression> r) { | ||
|
|
@@ -53,7 +79,7 @@ public static List<Expression> inCommon(List<Expression> l, List<Expression> r) | |
| } | ||
| } | ||
| } | ||
| return common.isEmpty() ? Collections.emptyList() : common; | ||
| return common.isEmpty() ? emptyList() : common; | ||
| } | ||
|
|
||
| public static List<Expression> subtract(List<Expression> from, List<Expression> r) { | ||
|
|
@@ -65,7 +91,7 @@ public static List<Expression> subtract(List<Expression> from, List<Expression> | |
| } | ||
| } | ||
| } | ||
| return diff.isEmpty() ? Collections.emptyList() : diff; | ||
| return diff.isEmpty() ? emptyList() : diff; | ||
| } | ||
|
|
||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,7 +9,6 @@ | |
| import org.elasticsearch.xpack.sql.tree.Location; | ||
| import org.elasticsearch.xpack.sql.tree.NodeInfo; | ||
| import org.elasticsearch.xpack.sql.type.DataType; | ||
| import org.elasticsearch.xpack.sql.type.DataTypes; | ||
|
|
||
| import java.util.Arrays; | ||
| import java.util.List; | ||
|
|
@@ -66,11 +65,19 @@ public boolean includeUpper() { | |
|
|
||
| @Override | ||
| public boolean foldable() { | ||
| return value.foldable() && lower.foldable() && upper.foldable(); | ||
| if (lower.foldable() && upper.foldable()) { | ||
| return (excludingBoundaries() || value.foldable()); | ||
|
||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| @Override | ||
| public Object fold() { | ||
| if (excludingBoundaries()) { | ||
| return Boolean.FALSE; | ||
| } | ||
|
|
||
| Object val = value.fold(); | ||
| Integer lowerCompare = BinaryComparison.compare(lower.fold(), val); | ||
| Integer upperCompare = BinaryComparison.compare(val, upper().fold()); | ||
|
|
@@ -79,6 +86,13 @@ public Object fold() { | |
| return lowerComparsion && upperComparsion; | ||
| } | ||
|
|
||
| private boolean excludingBoundaries() { | ||
|
||
| // if the boundaries exclude each other, the value doesn't need to be evaluated | ||
| Integer compare = BinaryComparison.compare(lower.fold(), upper.fold()); | ||
| // upper < lower OR upper == lower and the range doesn't contain any equals | ||
| return compare != null && (compare > 0 || (compare == 0 && (!includeLower || !includeUpper))); | ||
| } | ||
|
|
||
| @Override | ||
| public boolean nullable() { | ||
| return value.nullable() && lower.nullable() && upper.nullable(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are the side effects on not catching this and letting it bubble out? Or even wrapping it? I think it'd be nice to have a big comment here about why returning
nullis ok.Also, can you move the
tryinto theifstatement so it looks smaller? That'd just make me a bit more comfortable. If returningnullis right here then I think you should return it from thecatchrather than fall out. That feels a little safer even if it isn't.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CCE is thrown when the types are not compatible; I've expanded the comment and returns the null directly from catch.