-
Notifications
You must be signed in to change notification settings - Fork 24.9k
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
SQL: Optimisation fixes for conjunction merges #50703
Changes from 1 commit
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 |
---|---|---|
|
@@ -92,6 +92,7 @@ | |
import java.util.Map.Entry; | ||
import java.util.Set; | ||
import java.util.function.Consumer; | ||
import java.util.Comparator; | ||
|
||
import static java.util.Collections.singletonList; | ||
import static org.elasticsearch.xpack.sql.expression.Expressions.equalsAsAttribute; | ||
|
@@ -397,7 +398,7 @@ protected LogicalPlan rule(OrderBy ob) { | |
return ob; | ||
} | ||
} | ||
|
||
static class PruneOrderByForImplicitGrouping extends OptimizerRule<OrderBy> { | ||
|
||
@Override | ||
|
@@ -1090,7 +1091,21 @@ private Expression combine(And and) { | |
|
||
boolean changed = false; | ||
|
||
for (Expression ex : Predicates.splitAnd(and)) { | ||
List<Expression> andExps = Predicates.splitAnd(and); | ||
// Ranges need to show up before BinaryComparisons in list, to allow the latter be optimized away into a Range, if possible | ||
andExps.sort(new Comparator<Expression>() { | ||
@Override | ||
public int compare(Expression o1, Expression o2) { | ||
if (o1 instanceof Range && o2 instanceof Range) { | ||
return 0; // keep ranges' order | ||
} else if (o1 instanceof Range || o2 instanceof Range) { | ||
return o2 instanceof Range ? 1 : -1; | ||
} else { | ||
return 0; // keep non-ranges' order | ||
} | ||
} | ||
}); | ||
for (Expression ex : andExps) { | ||
if (ex instanceof Range) { | ||
Range r = (Range) ex; | ||
if (findExistingRange(r, ranges, true)) { | ||
|
@@ -1215,9 +1230,9 @@ private static boolean findExistingRange(Range main, List<Range> ranges, boolean | |
lowerEq = comp == 0 && main.includeLower() == other.includeLower(); | ||
// AND | ||
if (conjunctive) { | ||
// (2 < a < 3) AND (1 < a < 3) -> (1 < a < 3) | ||
// (2 < a < 3) AND (1 < a < 3) -> (2 < a < 3) | ||
lower = comp > 0 || | ||
// (2 < a < 3) AND (2 < a <= 3) -> (2 < a < 3) | ||
// (2 < a < 3) AND (2 <= a < 3) -> (2 < a < 3) | ||
(comp == 0 && !main.includeLower() && other.includeLower()); | ||
} | ||
// OR | ||
|
@@ -1316,7 +1331,7 @@ private boolean findConjunctiveComparisonInRange(BinaryComparison main, List<Ran | |
ranges.remove(i); | ||
ranges.add(i, | ||
new Range(other.source(), other.value(), | ||
main.right(), lowerEq ? true : other.includeLower(), | ||
main.right(), lowerEq ? false : main instanceof GreaterThanOrEqual, | ||
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. That's a big miss - were there no tests for this? 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. It seemed much like a c&p fallout. |
||
other.upper(), other.includeUpper())); | ||
} | ||
|
||
|
@@ -1325,19 +1340,19 @@ private boolean findConjunctiveComparisonInRange(BinaryComparison main, List<Ran | |
} | ||
} | ||
} else if (main instanceof LessThan || main instanceof LessThanOrEqual) { | ||
if (other.lower().foldable()) { | ||
Integer comp = BinaryComparison.compare(value, other.lower().fold()); | ||
if (other.upper().foldable()) { | ||
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. Likewise - a whole optimization being skipped... |
||
Integer comp = BinaryComparison.compare(value, other.upper().fold()); | ||
if (comp != null) { | ||
// a < 2 AND (1 < a <= 2) -> 1 < a < 2 | ||
boolean upperEq = comp == 0 && other.includeUpper() && main instanceof LessThan; | ||
// a < 2 AND (1 < a < 3) -> 1 < a < 2 | ||
boolean upper = comp > 0 || upperEq; | ||
boolean upper = comp < 0 || upperEq; | ||
|
||
if (upper) { | ||
ranges.remove(i); | ||
ranges.add(i, new Range(other.source(), other.value(), | ||
other.lower(), other.includeLower(), | ||
main.right(), upperEq ? true : other.includeUpper())); | ||
main.right(), upperEq ? false : main instanceof LessThanOrEqual)); | ||
} | ||
|
||
// found a match | ||
|
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.
Maybe extract this custom Comparator into its own variable inside
CombineBinaryComparisons
and re-use that, without creating it each time thecombine
method is called?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.
Good point, thanks.
I've however replaced with a (non-capturing) lambda, which should then be optimised by the jvm.