SQL: Make Literal a NamedExpression (#33583)

* SQL: Make Literal a NamedExpression

Literal now is a NamedExpression reducing the need for Aliases for
folded expressions leading to simpler optimization rules.

Fix #33523
This commit is contained in:
Costin Leau 2018-09-11 18:47:49 +03:00 committed by GitHub
parent cbc6fa0ecb
commit 91bca174f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 214 additions and 222 deletions

View File

@ -7,30 +7,28 @@ package org.elasticsearch.xpack.sql.expression;
import org.elasticsearch.xpack.sql.tree.Location;
import java.util.List;
import java.util.Objects;
import static java.util.Collections.emptyList;
import java.util.List;
/**
* {@link Expression}s that can be converted into Elasticsearch
* sorts, aggregations, or queries. They can also be extracted
* from the result of a search.
* {@link Expression}s that can be materialized and represent the result columns sent to the client.
* Typically are converted into constants, functions or Elasticsearch order-bys,
* aggregations, or queries. They can also be extracted from the result of a search.
*
* In the statement {@code SELECT ABS(foo), A, B+C FROM ...} the three named
* expressions (ABS(foo), A, B+C) get converted to attributes and the user can
* expressions {@code ABS(foo), A, B+C} get converted to attributes and the user can
* only see Attributes.
*
* In the statement {@code SELECT foo FROM TABLE WHERE foo > 10 + 1} 10+1 is an
* expression. It's not named - meaning there's no alias for it (defined by the
* user) and as such there's no attribute - no column to be returned to the user.
* It's an expression used for filtering so it doesn't appear in the result set
* (derived table). "foo" on the other hand is an expression, a named expression
* (it has a name) and also an attribute - it's a column in the result set.
* In the statement {@code SELECT foo FROM TABLE WHERE foo > 10 + 1} both {@code foo} and
* {@code 10 + 1} are named expressions, the first due to the SELECT, the second due to being a function.
* However since {@code 10 + 1} is used for filtering it doesn't appear appear in the result set
* (derived table) and as such it is never translated to an attribute.
* "foo" on the other hand is since it's a column in the result set.
*
* Another example {@code SELECT foo FROM ... WHERE bar > 10 +1} "foo" gets
* converted into an Attribute, bar does not. That's because bar is used for
* Another example {@code SELECT foo FROM ... WHERE bar > 10 +1} {@code foo} gets
* converted into an Attribute, bar does not. That's because {@code bar} is used for
* filtering alone but it's not part of the projection meaning the user doesn't
* need it in the derived table.
*/

View File

@ -82,13 +82,7 @@ public abstract class Expressions {
}
public static String name(Expression e) {
if (e instanceof NamedExpression) {
return ((NamedExpression) e).name();
} else if (e instanceof Literal) {
return e.toString();
} else {
return e.nodeName();
}
return e instanceof NamedExpression ? ((NamedExpression) e).name() : e.nodeName();
}
public static List<String> names(Collection<? extends Expression> e) {
@ -105,7 +99,7 @@ public abstract class Expressions {
return ((NamedExpression) e).toAttribute();
}
if (e != null && e.foldable()) {
return new LiteralAttribute(Literal.of(e));
return Literal.of(e).toAttribute();
}
return null;
}

View File

@ -12,9 +12,16 @@ import org.elasticsearch.xpack.sql.type.DataType;
import org.elasticsearch.xpack.sql.type.DataTypeConversion;
import org.elasticsearch.xpack.sql.type.DataTypes;
import java.util.List;
import java.util.Objects;
public class Literal extends LeafExpression {
import static java.util.Collections.emptyList;
/**
* SQL Literal or constant.
*/
public class Literal extends NamedExpression {
public static final Literal TRUE = Literal.of(Location.EMPTY, Boolean.TRUE);
public static final Literal FALSE = Literal.of(Location.EMPTY, Boolean.FALSE);
@ -22,7 +29,11 @@ public class Literal extends LeafExpression {
private final DataType dataType;
public Literal(Location location, Object value, DataType dataType) {
super(location);
this(location, null, value, dataType);
}
public Literal(Location location, String name, Object value, DataType dataType) {
super(location, name == null ? String.valueOf(value) : name, emptyList(), null);
this.dataType = dataType;
this.value = DataTypeConversion.convert(value, dataType);
}
@ -61,10 +72,24 @@ public class Literal extends LeafExpression {
return value;
}
@Override
public Attribute toAttribute() {
return new LiteralAttribute(location(), name(), null, false, id(), false, dataType, this);
}
@Override
public Expression replaceChildren(List<Expression> newChildren) {
throw new UnsupportedOperationException("this type of node doesn't have any children to replace");
}
@Override
public AttributeSet references() {
return AttributeSet.EMPTY;
}
@Override
public int hashCode() {
return Objects.hash(value, dataType);
return Objects.hash(name(), value, dataType);
}
@Override
@ -72,21 +97,25 @@ public class Literal extends LeafExpression {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Literal other = (Literal) obj;
return Objects.equals(value, other.value)
return Objects.equals(name(), other.name())
&& Objects.equals(value, other.value)
&& Objects.equals(dataType, other.dataType);
}
@Override
public String toString() {
return Objects.toString(value);
String s = String.valueOf(value);
return name().equals(s) ? s : name() + "=" + value;
}
/**
* Utility method for creating 'in-line' Literals (out of values instead of expressions).
*/
public static Literal of(Location loc, Object value) {
if (value instanceof Literal) {
return (Literal) value;
@ -94,15 +123,32 @@ public class Literal extends LeafExpression {
return new Literal(loc, value, DataTypes.fromJava(value));
}
/**
* Utility method for creating a literal out of a foldable expression.
* Throws an exception if the expression is not foldable.
*/
public static Literal of(Expression foldable) {
if (foldable instanceof Literal) {
return (Literal) foldable;
}
return of((String) null, foldable);
}
public static Literal of(String name, Expression foldable) {
if (!foldable.foldable()) {
throw new SqlIllegalArgumentException("Foldable expression required for Literal creation; received unfoldable " + foldable);
}
return new Literal(foldable.location(), foldable.fold(), foldable.dataType());
if (foldable instanceof Literal) {
Literal l = (Literal) foldable;
if (name == null || l.name().equals(name)) {
return l;
}
}
Object fold = foldable.fold();
if (name == null) {
name = foldable instanceof NamedExpression ? ((NamedExpression) foldable).name() : String.valueOf(fold);
}
return new Literal(foldable.location(), name, fold, foldable.dataType());
}
}
}

View File

@ -15,20 +15,12 @@ public class LiteralAttribute extends TypedAttribute {
private final Literal literal;
public LiteralAttribute(Literal literal) {
this(literal.location(), String.valueOf(literal.fold()), null, false, null, false, literal.dataType(), literal);
}
public LiteralAttribute(Location location, String name, String qualifier, boolean nullable, ExpressionId id, boolean synthetic,
DataType dataType, Literal literal) {
super(location, name, dataType, qualifier, nullable, id, synthetic);
this.literal = literal;
}
public Literal literal() {
return literal;
}
@Override
protected NodeInfo<LiteralAttribute> info() {
return NodeInfo.create(this, LiteralAttribute::new,
@ -49,4 +41,4 @@ public class LiteralAttribute extends TypedAttribute {
protected String label() {
return "c";
}
}
}

View File

@ -10,7 +10,6 @@ import org.elasticsearch.xpack.sql.expression.Attribute;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.Expressions;
import org.elasticsearch.xpack.sql.expression.FieldAttribute;
import org.elasticsearch.xpack.sql.expression.LiteralAttribute;
import org.elasticsearch.xpack.sql.expression.function.Function;
import org.elasticsearch.xpack.sql.expression.function.aggregate.AggregateFunctionAttribute;
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.ProcessorDefinition;
@ -69,11 +68,9 @@ public abstract class ScalarFunction extends Function {
if (attr instanceof AggregateFunctionAttribute) {
return asScriptFrom((AggregateFunctionAttribute) attr);
}
if (attr instanceof LiteralAttribute) {
return asScriptFrom((LiteralAttribute) attr);
if (attr instanceof FieldAttribute) {
return asScriptFrom((FieldAttribute) attr);
}
// fall-back to
return asScriptFrom((FieldAttribute) attr);
}
throw new SqlIllegalArgumentException("Cannot evaluate script for expression {}", exp);
}
@ -102,12 +99,6 @@ public abstract class ScalarFunction extends Function {
aggregate.dataType());
}
protected ScriptTemplate asScriptFrom(LiteralAttribute literal) {
return new ScriptTemplate(formatScript("{}"),
paramsBuilder().variable(literal.literal()).build(),
literal.dataType());
}
protected String formatScript(String scriptTemplate) {
return formatTemplate(scriptTemplate);
}

View File

@ -21,7 +21,7 @@ public class E extends MathFunction {
private static final ScriptTemplate TEMPLATE = new ScriptTemplate("Math.E", Params.EMPTY, DataType.DOUBLE);
public E(Location location) {
super(location, new Literal(location, Math.E, DataType.DOUBLE));
super(location, new Literal(location, "E", Math.E, DataType.DOUBLE));
}
@Override

View File

@ -21,7 +21,7 @@ public class Pi extends MathFunction {
private static final ScriptTemplate TEMPLATE = new ScriptTemplate("Math.PI", Params.EMPTY, DataType.DOUBLE);
public Pi(Location location) {
super(location, new Literal(location, Math.PI, DataType.DOUBLE));
super(location, new Literal(location, "PI", Math.PI, DataType.DOUBLE));
}
@Override

View File

@ -1118,36 +1118,12 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
@Override
protected Expression rule(Expression e) {
// handle aliases to avoid double aliasing of functions
// alias points to function which gets folded and wrapped in an alias that is
// aliases
if (e instanceof Alias) {
Alias a = (Alias) e;
Expression fold = fold(a.child());
if (fold != a.child()) {
return new Alias(a.location(), a.name(), null, fold, a.id());
}
return a;
return a.child().foldable() ? Literal.of(a.name(), a.child()) : a;
}
Expression fold = fold(e);
if (fold != e) {
// preserve the name through an alias
if (e instanceof NamedExpression) {
NamedExpression ne = (NamedExpression) e;
return new Alias(e.location(), ne.name(), null, fold, ne.id());
}
return fold;
}
return e;
}
private Expression fold(Expression e) {
// literals are always foldable, so avoid creating a duplicate
if (e.foldable() && !(e instanceof Literal)) {
return new Literal(e.location(), e.fold(), e.dataType());
}
return e;
return e.foldable() ? Literal.of(e) : e;
}
}
@ -1836,14 +1812,11 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
private List<Object> extractConstants(List<? extends NamedExpression> named) {
List<Object> values = new ArrayList<>();
for (NamedExpression n : named) {
if (n instanceof Alias) {
Alias a = (Alias) n;
if (a.child().foldable()) {
values.add(a.child().fold());
}
else {
return values;
}
if (n.foldable()) {
values.add(n.fold());
} else {
// not everything is foldable, bail-out early
return values;
}
}
return values;

View File

@ -61,7 +61,7 @@ public class LiteralTests extends AbstractNodeTestCase<Literal, Expression> {
@Override
protected Literal copy(Literal instance) {
return new Literal(instance.location(), instance.value(), instance.dataType());
return new Literal(instance.location(), instance.name(), instance.value(), instance.dataType());
}
@Override

View File

@ -85,6 +85,14 @@ public class OptimizerTests extends ESTestCase {
private static final Expression DUMMY_EXPRESSION = new DummyBooleanExpression(EMPTY, 0);
private static final Literal ONE = L(1);
private static final Literal TWO = L(2);
private static final Literal THREE = L(3);
private static final Literal FOUR = L(4);
private static final Literal FIVE = L(5);
private static final Literal SIX = L(6);
public static class DummyBooleanExpression extends Expression {
private final int id;
@ -161,7 +169,7 @@ public class OptimizerTests extends ESTestCase {
public void testCombineProjections() {
// a
Alias a = new Alias(EMPTY, "a", L(5));
Alias a = new Alias(EMPTY, "a", FIVE);
// b
Alias b = new Alias(EMPTY, "b", L(10));
// x -> a
@ -187,7 +195,7 @@ public class OptimizerTests extends ESTestCase {
// SELECT 5 a, 10 b FROM foo WHERE a < 10 ORDER BY b
// a
Alias a = new Alias(EMPTY, "a", L(5));
Alias a = new Alias(EMPTY, "a", FIVE);
// b
Alias b = new Alias(EMPTY, "b", L(10));
// WHERE a < 10
@ -226,49 +234,44 @@ public class OptimizerTests extends ESTestCase {
//
public void testConstantFolding() {
Expression exp = new Add(EMPTY, L(2), L(3));
Expression exp = new Add(EMPTY, TWO, THREE);
assertTrue(exp.foldable());
assertTrue(exp instanceof NamedExpression);
String n = Expressions.name(exp);
Expression result = new ConstantFolding().rule(exp);
assertTrue(result instanceof Alias);
assertTrue(result instanceof Literal);
assertEquals(n, Expressions.name(result));
Expression c = ((Alias) result).child();
assertTrue(c instanceof Literal);
assertEquals(5, ((Literal) c).value());
assertEquals(5, ((Literal) result).value());
// check now with an alias
result = new ConstantFolding().rule(new Alias(EMPTY, "a", exp));
assertTrue(result instanceof Alias);
assertEquals("a", Expressions.name(result));
c = ((Alias) result).child();
assertTrue(c instanceof Literal);
assertEquals(5, ((Literal) c).value());
assertEquals(5, ((Literal) result).value());
}
public void testConstantFoldingBinaryComparison() {
assertEquals(Literal.FALSE, new ConstantFolding().rule(new GreaterThan(EMPTY, L(2), L(3))));
assertEquals(Literal.FALSE, new ConstantFolding().rule(new GreaterThanOrEqual(EMPTY, L(2), L(3))));
assertEquals(Literal.FALSE, new ConstantFolding().rule(new Equals(EMPTY, L(2), L(3))));
assertEquals(Literal.TRUE, new ConstantFolding().rule(new LessThanOrEqual(EMPTY, L(2), L(3))));
assertEquals(Literal.TRUE, new ConstantFolding().rule(new LessThan(EMPTY, L(2), L(3))));
assertEquals(Literal.FALSE, new ConstantFolding().rule(new GreaterThan(EMPTY, TWO, THREE)));
assertEquals(Literal.FALSE, new ConstantFolding().rule(new GreaterThanOrEqual(EMPTY, TWO, THREE)));
assertEquals(Literal.FALSE, new ConstantFolding().rule(new Equals(EMPTY, TWO, THREE)));
assertEquals(Literal.TRUE, new ConstantFolding().rule(new LessThanOrEqual(EMPTY, TWO, THREE)));
assertEquals(Literal.TRUE, new ConstantFolding().rule(new LessThan(EMPTY, TWO, THREE)));
}
public void testConstantFoldingBinaryLogic() {
assertEquals(Literal.FALSE, new ConstantFolding().rule(new And(EMPTY, new GreaterThan(EMPTY, L(2), L(3)), Literal.TRUE)));
assertEquals(Literal.TRUE, new ConstantFolding().rule(new Or(EMPTY, new GreaterThanOrEqual(EMPTY, L(2), L(3)), Literal.TRUE)));
assertEquals(Literal.FALSE, new ConstantFolding().rule(new And(EMPTY, new GreaterThan(EMPTY, TWO, THREE), Literal.TRUE)));
assertEquals(Literal.TRUE, new ConstantFolding().rule(new Or(EMPTY, new GreaterThanOrEqual(EMPTY, TWO, THREE), Literal.TRUE)));
}
public void testConstantFoldingRange() {
assertEquals(Literal.TRUE, new ConstantFolding().rule(new Range(EMPTY, L(5), L(5), true, L(10), false)));
assertEquals(Literal.FALSE, new ConstantFolding().rule(new Range(EMPTY, L(5), L(5), false, L(10), false)));
assertEquals(Literal.TRUE, new ConstantFolding().rule(new Range(EMPTY, FIVE, FIVE, true, L(10), false)));
assertEquals(Literal.FALSE, new ConstantFolding().rule(new Range(EMPTY, FIVE, FIVE, false, L(10), false)));
}
public void testConstantIsNotNull() {
assertEquals(Literal.FALSE, new ConstantFolding().rule(new IsNotNull(EMPTY, L(null))));
assertEquals(Literal.TRUE, new ConstantFolding().rule(new IsNotNull(EMPTY, L(5))));
assertEquals(Literal.TRUE, new ConstantFolding().rule(new IsNotNull(EMPTY, FIVE)));
}
public void testConstantNot() {
@ -296,30 +299,24 @@ public class OptimizerTests extends ESTestCase {
}
public void testArithmeticFolding() {
assertEquals(10, foldFunction(new Add(EMPTY, L(7), L(3))));
assertEquals(4, foldFunction(new Sub(EMPTY, L(7), L(3))));
assertEquals(21, foldFunction(new Mul(EMPTY, L(7), L(3))));
assertEquals(2, foldFunction(new Div(EMPTY, L(7), L(3))));
assertEquals(1, foldFunction(new Mod(EMPTY, L(7), L(3))));
assertEquals(10, foldFunction(new Add(EMPTY, L(7), THREE)));
assertEquals(4, foldFunction(new Sub(EMPTY, L(7), THREE)));
assertEquals(21, foldFunction(new Mul(EMPTY, L(7), THREE)));
assertEquals(2, foldFunction(new Div(EMPTY, L(7), THREE)));
assertEquals(1, foldFunction(new Mod(EMPTY, L(7), THREE)));
}
public void testMathFolding() {
assertEquals(7, foldFunction(new Abs(EMPTY, L(7))));
assertEquals(0d, (double) foldFunction(new ACos(EMPTY, L(1))), 0.01d);
assertEquals(1.57076d, (double) foldFunction(new ASin(EMPTY, L(1))), 0.01d);
assertEquals(0.78539d, (double) foldFunction(new ATan(EMPTY, L(1))), 0.01d);
assertEquals(0d, (double) foldFunction(new ACos(EMPTY, ONE)), 0.01d);
assertEquals(1.57076d, (double) foldFunction(new ASin(EMPTY, ONE)), 0.01d);
assertEquals(0.78539d, (double) foldFunction(new ATan(EMPTY, ONE)), 0.01d);
assertEquals(7, foldFunction(new Floor(EMPTY, L(7))));
assertEquals(Math.E, foldFunction(new E(EMPTY)));
}
private static Object foldFunction(Function f) {
return unwrapAlias(new ConstantFolding().rule(f));
}
private static Object unwrapAlias(Expression e) {
Alias a = (Alias) e;
Literal l = (Literal) a.child();
return l.value();
return ((Literal) new ConstantFolding().rule(f)).value();
}
//
@ -327,21 +324,21 @@ public class OptimizerTests extends ESTestCase {
//
public void testBinaryComparisonSimplification() {
assertEquals(Literal.TRUE, new BinaryComparisonSimplification().rule(new Equals(EMPTY, L(5), L(5))));
assertEquals(Literal.TRUE, new BinaryComparisonSimplification().rule(new GreaterThanOrEqual(EMPTY, L(5), L(5))));
assertEquals(Literal.TRUE, new BinaryComparisonSimplification().rule(new LessThanOrEqual(EMPTY, L(5), L(5))));
assertEquals(Literal.TRUE, new BinaryComparisonSimplification().rule(new Equals(EMPTY, FIVE, FIVE)));
assertEquals(Literal.TRUE, new BinaryComparisonSimplification().rule(new GreaterThanOrEqual(EMPTY, FIVE, FIVE)));
assertEquals(Literal.TRUE, new BinaryComparisonSimplification().rule(new LessThanOrEqual(EMPTY, FIVE, FIVE)));
assertEquals(Literal.FALSE, new BinaryComparisonSimplification().rule(new GreaterThan(EMPTY, L(5), L(5))));
assertEquals(Literal.FALSE, new BinaryComparisonSimplification().rule(new LessThan(EMPTY, L(5), L(5))));
assertEquals(Literal.FALSE, new BinaryComparisonSimplification().rule(new GreaterThan(EMPTY, FIVE, FIVE)));
assertEquals(Literal.FALSE, new BinaryComparisonSimplification().rule(new LessThan(EMPTY, FIVE, FIVE)));
}
public void testLiteralsOnTheRight() {
Alias a = new Alias(EMPTY, "a", L(10));
Expression result = new BooleanLiteralsOnTheRight().rule(new Equals(EMPTY, L(5), a));
Expression result = new BooleanLiteralsOnTheRight().rule(new Equals(EMPTY, FIVE, a));
assertTrue(result instanceof Equals);
Equals eq = (Equals) result;
assertEquals(a, eq.left());
assertEquals(L(5), eq.right());
assertEquals(FIVE, eq.right());
}
public void testBoolSimplifyOr() {
@ -390,7 +387,7 @@ public class OptimizerTests extends ESTestCase {
public void testFoldExcludingRangeToFalse() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
Range r = new Range(EMPTY, fa, L(6), false, L(5), true);
Range r = new Range(EMPTY, fa, SIX, false, FIVE, true);
assertTrue(r.foldable());
assertEquals(Boolean.FALSE, r.fold());
}
@ -399,7 +396,7 @@ public class OptimizerTests extends ESTestCase {
public void testFoldExcludingRangeWithDifferentTypesToFalse() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
Range r = new Range(EMPTY, fa, L(6), false, L(5.5d), true);
Range r = new Range(EMPTY, fa, SIX, false, L(5.5d), true);
assertTrue(r.foldable());
assertEquals(Boolean.FALSE, r.fold());
}
@ -408,7 +405,7 @@ public class OptimizerTests extends ESTestCase {
public void testCombineBinaryComparisonsNotComparable() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
LessThanOrEqual lte = new LessThanOrEqual(EMPTY, fa, L(6));
LessThanOrEqual lte = new LessThanOrEqual(EMPTY, fa, SIX);
LessThan lt = new LessThan(EMPTY, fa, Literal.FALSE);
CombineBinaryComparisons rule = new CombineBinaryComparisons();
@ -420,71 +417,71 @@ public class OptimizerTests extends ESTestCase {
// a <= 6 AND a < 5 -> a < 5
public void testCombineBinaryComparisonsUpper() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
LessThanOrEqual lte = new LessThanOrEqual(EMPTY, fa, L(6));
LessThan lt = new LessThan(EMPTY, fa, L(5));
LessThanOrEqual lte = new LessThanOrEqual(EMPTY, fa, SIX);
LessThan lt = new LessThan(EMPTY, fa, FIVE);
CombineBinaryComparisons rule = new CombineBinaryComparisons();
Expression exp = rule.rule(new And(EMPTY, lte, lt));
assertEquals(LessThan.class, exp.getClass());
LessThan r = (LessThan) exp;
assertEquals(L(5), r.right());
assertEquals(FIVE, r.right());
}
// 6 <= a AND 5 < a -> 6 <= a
public void testCombineBinaryComparisonsLower() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
GreaterThanOrEqual gte = new GreaterThanOrEqual(EMPTY, fa, L(6));
GreaterThan gt = new GreaterThan(EMPTY, fa, L(5));
GreaterThanOrEqual gte = new GreaterThanOrEqual(EMPTY, fa, SIX);
GreaterThan gt = new GreaterThan(EMPTY, fa, FIVE);
CombineBinaryComparisons rule = new CombineBinaryComparisons();
Expression exp = rule.rule(new And(EMPTY, gte, gt));
assertEquals(GreaterThanOrEqual.class, exp.getClass());
GreaterThanOrEqual r = (GreaterThanOrEqual) exp;
assertEquals(L(6), r.right());
assertEquals(SIX, r.right());
}
// 5 <= a AND 5 < a -> 5 < a
public void testCombineBinaryComparisonsInclude() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
GreaterThanOrEqual gte = new GreaterThanOrEqual(EMPTY, fa, L(5));
GreaterThan gt = new GreaterThan(EMPTY, fa, L(5));
GreaterThanOrEqual gte = new GreaterThanOrEqual(EMPTY, fa, FIVE);
GreaterThan gt = new GreaterThan(EMPTY, fa, FIVE);
CombineBinaryComparisons rule = new CombineBinaryComparisons();
Expression exp = rule.rule(new And(EMPTY, gte, gt));
assertEquals(GreaterThan.class, exp.getClass());
GreaterThan r = (GreaterThan) exp;
assertEquals(L(5), r.right());
assertEquals(FIVE, r.right());
}
// 3 <= a AND 4 < a AND a <= 7 AND a < 6 -> 4 < a < 6
public void testCombineMultipleBinaryComparisons() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
GreaterThanOrEqual gte = new GreaterThanOrEqual(EMPTY, fa, L(3));
GreaterThan gt = new GreaterThan(EMPTY, fa, L(4));
GreaterThanOrEqual gte = new GreaterThanOrEqual(EMPTY, fa, THREE);
GreaterThan gt = new GreaterThan(EMPTY, fa, FOUR);
LessThanOrEqual lte = new LessThanOrEqual(EMPTY, fa, L(7));
LessThan lt = new LessThan(EMPTY, fa, L(6));
LessThan lt = new LessThan(EMPTY, fa, SIX);
CombineBinaryComparisons rule = new CombineBinaryComparisons();
Expression exp = rule.rule(new And(EMPTY, gte, new And(EMPTY, gt, new And(EMPTY, lt, lte))));
assertEquals(Range.class, exp.getClass());
Range r = (Range) exp;
assertEquals(L(4), r.lower());
assertEquals(FOUR, r.lower());
assertFalse(r.includeLower());
assertEquals(L(6), r.upper());
assertEquals(SIX, r.upper());
assertFalse(r.includeUpper());
}
// 3 <= a AND TRUE AND 4 < a AND a != 5 AND a <= 7 -> 4 < a <= 7 AND a != 5 AND TRUE
public void testCombineMixedMultipleBinaryComparisons() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
GreaterThanOrEqual gte = new GreaterThanOrEqual(EMPTY, fa, L(3));
GreaterThan gt = new GreaterThan(EMPTY, fa, L(4));
GreaterThanOrEqual gte = new GreaterThanOrEqual(EMPTY, fa, THREE);
GreaterThan gt = new GreaterThan(EMPTY, fa, FOUR);
LessThanOrEqual lte = new LessThanOrEqual(EMPTY, fa, L(7));
Expression ne = new Not(EMPTY, new Equals(EMPTY, fa, L(5)));
Expression ne = new Not(EMPTY, new Equals(EMPTY, fa, FIVE));
CombineBinaryComparisons rule = new CombineBinaryComparisons();
@ -494,7 +491,7 @@ public class OptimizerTests extends ESTestCase {
And and = ((And) exp);
assertEquals(Range.class, and.right().getClass());
Range r = (Range) and.right();
assertEquals(L(4), r.lower());
assertEquals(FOUR, r.lower());
assertFalse(r.includeLower());
assertEquals(L(7), r.upper());
assertTrue(r.includeUpper());
@ -503,17 +500,17 @@ public class OptimizerTests extends ESTestCase {
// 1 <= a AND a < 5 -> 1 <= a < 5
public void testCombineComparisonsIntoRange() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
GreaterThanOrEqual gte = new GreaterThanOrEqual(EMPTY, fa, L(1));
LessThan lt = new LessThan(EMPTY, fa, L(5));
GreaterThanOrEqual gte = new GreaterThanOrEqual(EMPTY, fa, ONE);
LessThan lt = new LessThan(EMPTY, fa, FIVE);
CombineBinaryComparisons rule = new CombineBinaryComparisons();
Expression exp = rule.rule(new And(EMPTY, gte, lt));
assertEquals(Range.class, rule.rule(exp).getClass());
Range r = (Range) exp;
assertEquals(L(1), r.lower());
assertEquals(ONE, r.lower());
assertTrue(r.includeLower());
assertEquals(L(5), r.upper());
assertEquals(FIVE, r.upper());
assertFalse(r.includeUpper());
}
@ -521,10 +518,10 @@ public class OptimizerTests extends ESTestCase {
public void testCombineUnbalancedComparisonsMixedWithEqualsIntoRange() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
IsNotNull isn = new IsNotNull(EMPTY, fa);
GreaterThanOrEqual gte = new GreaterThanOrEqual(EMPTY, fa, L(1));
GreaterThanOrEqual gte = new GreaterThanOrEqual(EMPTY, fa, ONE);
Equals eq = new Equals(EMPTY, fa, L(10));
LessThan lt = new LessThan(EMPTY, fa, L(5));
LessThan lt = new LessThan(EMPTY, fa, FIVE);
And and = new And(EMPTY, new And(EMPTY, isn, gte), new And(EMPTY, lt, eq));
@ -535,9 +532,9 @@ public class OptimizerTests extends ESTestCase {
assertEquals(Range.class, a.right().getClass());
Range r = (Range) a.right();
assertEquals(L(1), r.lower());
assertEquals(ONE, r.lower());
assertTrue(r.includeLower());
assertEquals(L(5), r.upper());
assertEquals(FIVE, r.upper());
assertFalse(r.includeUpper());
}
@ -545,8 +542,8 @@ public class OptimizerTests extends ESTestCase {
public void testCombineBinaryComparisonsConjunctionOfIncludedRange() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
Range r1 = new Range(EMPTY, fa, L(2), false, L(3), false);
Range r2 = new Range(EMPTY, fa, L(1), false, L(4), false);
Range r1 = new Range(EMPTY, fa, TWO, false, THREE, false);
Range r2 = new Range(EMPTY, fa, ONE, false, FOUR, false);
And and = new And(EMPTY, r1, r2);
@ -559,8 +556,8 @@ public class OptimizerTests extends ESTestCase {
public void testCombineBinaryComparisonsConjunctionOfNonOverlappingBoundaries() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
Range r1 = new Range(EMPTY, fa, L(2), false, L(3), false);
Range r2 = new Range(EMPTY, fa, L(1), false, L(2), false);
Range r1 = new Range(EMPTY, fa, TWO, false, THREE, false);
Range r2 = new Range(EMPTY, fa, ONE, false, TWO, false);
And and = new And(EMPTY, r1, r2);
@ -568,9 +565,9 @@ public class OptimizerTests extends ESTestCase {
Expression exp = rule.rule(and);
assertEquals(Range.class, exp.getClass());
Range r = (Range) exp;
assertEquals(L(2), r.lower());
assertEquals(TWO, r.lower());
assertFalse(r.includeLower());
assertEquals(L(2), r.upper());
assertEquals(TWO, r.upper());
assertFalse(r.includeUpper());
assertEquals(Boolean.FALSE, r.fold());
}
@ -579,8 +576,8 @@ public class OptimizerTests extends ESTestCase {
public void testCombineBinaryComparisonsConjunctionOfUpperEqualsOverlappingBoundaries() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
Range r1 = new Range(EMPTY, fa, L(2), false, L(3), false);
Range r2 = new Range(EMPTY, fa, L(2), false, L(3), true);
Range r1 = new Range(EMPTY, fa, TWO, false, THREE, false);
Range r2 = new Range(EMPTY, fa, TWO, false, THREE, true);
And and = new And(EMPTY, r1, r2);
@ -593,8 +590,8 @@ public class OptimizerTests extends ESTestCase {
public void testCombineBinaryComparisonsConjunctionOverlappingUpperBoundary() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
Range r2 = new Range(EMPTY, fa, L(2), false, L(3), false);
Range r1 = new Range(EMPTY, fa, L(1), false, L(3), false);
Range r2 = new Range(EMPTY, fa, TWO, false, THREE, false);
Range r1 = new Range(EMPTY, fa, ONE, false, THREE, false);
And and = new And(EMPTY, r1, r2);
@ -607,8 +604,8 @@ public class OptimizerTests extends ESTestCase {
public void testCombineBinaryComparisonsConjunctionWithDifferentUpperLimitInclusion() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
Range r1 = new Range(EMPTY, fa, L(1), false, L(3), false);
Range r2 = new Range(EMPTY, fa, L(2), false, L(3), true);
Range r1 = new Range(EMPTY, fa, ONE, false, THREE, false);
Range r2 = new Range(EMPTY, fa, TWO, false, THREE, true);
And and = new And(EMPTY, r1, r2);
@ -616,9 +613,9 @@ public class OptimizerTests extends ESTestCase {
Expression exp = rule.rule(and);
assertEquals(Range.class, exp.getClass());
Range r = (Range) exp;
assertEquals(L(2), r.lower());
assertEquals(TWO, r.lower());
assertFalse(r.includeLower());
assertEquals(L(3), r.upper());
assertEquals(THREE, r.upper());
assertFalse(r.includeUpper());
}
@ -626,8 +623,8 @@ public class OptimizerTests extends ESTestCase {
public void testRangesOverlappingConjunctionNoLowerBoundary() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
Range r1 = new Range(EMPTY, fa, L(0), false, L(1), true);
Range r2 = new Range(EMPTY, fa, L(0), true, L(2), false);
Range r1 = new Range(EMPTY, fa, L(0), false, ONE, true);
Range r2 = new Range(EMPTY, fa, L(0), true, TWO, false);
And and = new And(EMPTY, r1, r2);
@ -641,7 +638,7 @@ public class OptimizerTests extends ESTestCase {
public void testCombineBinaryComparisonsDisjunctionNotComparable() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
GreaterThan gt1 = new GreaterThan(EMPTY, fa, L(1));
GreaterThan gt1 = new GreaterThan(EMPTY, fa, ONE);
GreaterThan gt2 = new GreaterThan(EMPTY, fa, Literal.FALSE);
Or or = new Or(EMPTY, gt1, gt2);
@ -656,9 +653,9 @@ public class OptimizerTests extends ESTestCase {
public void testCombineBinaryComparisonsDisjunctionLowerBound() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
GreaterThan gt1 = new GreaterThan(EMPTY, fa, L(1));
GreaterThan gt2 = new GreaterThan(EMPTY, fa, L(2));
GreaterThan gt3 = new GreaterThan(EMPTY, fa, L(3));
GreaterThan gt1 = new GreaterThan(EMPTY, fa, ONE);
GreaterThan gt2 = new GreaterThan(EMPTY, fa, TWO);
GreaterThan gt3 = new GreaterThan(EMPTY, fa, THREE);
Or or = new Or(EMPTY, gt1, new Or(EMPTY, gt2, gt3));
@ -667,16 +664,16 @@ public class OptimizerTests extends ESTestCase {
assertEquals(GreaterThan.class, exp.getClass());
GreaterThan gt = (GreaterThan) exp;
assertEquals(L(1), gt.right());
assertEquals(ONE, gt.right());
}
// 2 < a OR 1 < a OR 3 <= a -> 1 < a
public void testCombineBinaryComparisonsDisjunctionIncludeLowerBounds() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
GreaterThan gt1 = new GreaterThan(EMPTY, fa, L(1));
GreaterThan gt2 = new GreaterThan(EMPTY, fa, L(2));
GreaterThanOrEqual gte3 = new GreaterThanOrEqual(EMPTY, fa, L(3));
GreaterThan gt1 = new GreaterThan(EMPTY, fa, ONE);
GreaterThan gt2 = new GreaterThan(EMPTY, fa, TWO);
GreaterThanOrEqual gte3 = new GreaterThanOrEqual(EMPTY, fa, THREE);
Or or = new Or(EMPTY, new Or(EMPTY, gt1, gt2), gte3);
@ -685,16 +682,16 @@ public class OptimizerTests extends ESTestCase {
assertEquals(GreaterThan.class, exp.getClass());
GreaterThan gt = (GreaterThan) exp;
assertEquals(L(1), gt.right());
assertEquals(ONE, gt.right());
}
// a < 1 OR a < 2 OR a < 3 -> a < 3
public void testCombineBinaryComparisonsDisjunctionUpperBound() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
LessThan lt1 = new LessThan(EMPTY, fa, L(1));
LessThan lt2 = new LessThan(EMPTY, fa, L(2));
LessThan lt3 = new LessThan(EMPTY, fa, L(3));
LessThan lt1 = new LessThan(EMPTY, fa, ONE);
LessThan lt2 = new LessThan(EMPTY, fa, TWO);
LessThan lt3 = new LessThan(EMPTY, fa, THREE);
Or or = new Or(EMPTY, new Or(EMPTY, lt1, lt2), lt3);
@ -703,16 +700,16 @@ public class OptimizerTests extends ESTestCase {
assertEquals(LessThan.class, exp.getClass());
LessThan lt = (LessThan) exp;
assertEquals(L(3), lt.right());
assertEquals(THREE, lt.right());
}
// a < 2 OR a <= 2 OR a < 1 -> a <= 2
public void testCombineBinaryComparisonsDisjunctionIncludeUpperBounds() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
LessThan lt1 = new LessThan(EMPTY, fa, L(1));
LessThan lt2 = new LessThan(EMPTY, fa, L(2));
LessThanOrEqual lte2 = new LessThanOrEqual(EMPTY, fa, L(2));
LessThan lt1 = new LessThan(EMPTY, fa, ONE);
LessThan lt2 = new LessThan(EMPTY, fa, TWO);
LessThanOrEqual lte2 = new LessThanOrEqual(EMPTY, fa, TWO);
Or or = new Or(EMPTY, lt2, new Or(EMPTY, lte2, lt1));
@ -721,18 +718,18 @@ public class OptimizerTests extends ESTestCase {
assertEquals(LessThanOrEqual.class, exp.getClass());
LessThanOrEqual lte = (LessThanOrEqual) exp;
assertEquals(L(2), lte.right());
assertEquals(TWO, lte.right());
}
// a < 2 OR 3 < a OR a < 1 OR 4 < a -> a < 2 OR 3 < a
public void testCombineBinaryComparisonsDisjunctionOfLowerAndUpperBounds() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
LessThan lt1 = new LessThan(EMPTY, fa, L(1));
LessThan lt2 = new LessThan(EMPTY, fa, L(2));
LessThan lt1 = new LessThan(EMPTY, fa, ONE);
LessThan lt2 = new LessThan(EMPTY, fa, TWO);
GreaterThan gt3 = new GreaterThan(EMPTY, fa, L(3));
GreaterThan gt4 = new GreaterThan(EMPTY, fa, L(4));
GreaterThan gt3 = new GreaterThan(EMPTY, fa, THREE);
GreaterThan gt4 = new GreaterThan(EMPTY, fa, FOUR);
Or or = new Or(EMPTY, new Or(EMPTY, lt2, gt3), new Or(EMPTY, lt1, gt4));
@ -744,18 +741,18 @@ public class OptimizerTests extends ESTestCase {
assertEquals(LessThan.class, ro.left().getClass());
LessThan lt = (LessThan) ro.left();
assertEquals(L(2), lt.right());
assertEquals(TWO, lt.right());
assertEquals(GreaterThan.class, ro.right().getClass());
GreaterThan gt = (GreaterThan) ro.right();
assertEquals(L(3), gt.right());
assertEquals(THREE, gt.right());
}
// (2 < a < 3) OR (1 < a < 4) -> (1 < a < 4)
public void testCombineBinaryComparisonsDisjunctionOfIncludedRangeNotComparable() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
Range r1 = new Range(EMPTY, fa, L(2), false, L(3), false);
Range r2 = new Range(EMPTY, fa, L(1), false, Literal.FALSE, false);
Range r1 = new Range(EMPTY, fa, TWO, false, THREE, false);
Range r2 = new Range(EMPTY, fa, ONE, false, Literal.FALSE, false);
Or or = new Or(EMPTY, r1, r2);
@ -769,8 +766,9 @@ public class OptimizerTests extends ESTestCase {
public void testCombineBinaryComparisonsDisjunctionOfIncludedRange() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
Range r1 = new Range(EMPTY, fa, L(2), false, L(3), false);
Range r2 = new Range(EMPTY, fa, L(1), false, L(4), false);
Range r1 = new Range(EMPTY, fa, TWO, false, THREE, false);
Range r2 = new Range(EMPTY, fa, ONE, false, FOUR, false);
Or or = new Or(EMPTY, r1, r2);
@ -779,9 +777,9 @@ public class OptimizerTests extends ESTestCase {
assertEquals(Range.class, exp.getClass());
Range r = (Range) exp;
assertEquals(L(1), r.lower());
assertEquals(ONE, r.lower());
assertFalse(r.includeLower());
assertEquals(L(4), r.upper());
assertEquals(FOUR, r.upper());
assertFalse(r.includeUpper());
}
@ -789,8 +787,8 @@ public class OptimizerTests extends ESTestCase {
public void testCombineBinaryComparisonsDisjunctionOfNonOverlappingBoundaries() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
Range r1 = new Range(EMPTY, fa, L(2), false, L(3), false);
Range r2 = new Range(EMPTY, fa, L(1), false, L(2), false);
Range r1 = new Range(EMPTY, fa, TWO, false, THREE, false);
Range r2 = new Range(EMPTY, fa, ONE, false, TWO, false);
Or or = new Or(EMPTY, r1, r2);
@ -803,8 +801,8 @@ public class OptimizerTests extends ESTestCase {
public void testCombineBinaryComparisonsDisjunctionOfUpperEqualsOverlappingBoundaries() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
Range r1 = new Range(EMPTY, fa, L(2), false, L(3), false);
Range r2 = new Range(EMPTY, fa, L(2), false, L(3), true);
Range r1 = new Range(EMPTY, fa, TWO, false, THREE, false);
Range r2 = new Range(EMPTY, fa, TWO, false, THREE, true);
Or or = new Or(EMPTY, r1, r2);
@ -817,8 +815,8 @@ public class OptimizerTests extends ESTestCase {
public void testCombineBinaryComparisonsOverlappingUpperBoundary() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
Range r2 = new Range(EMPTY, fa, L(2), false, L(3), false);
Range r1 = new Range(EMPTY, fa, L(1), false, L(3), false);
Range r2 = new Range(EMPTY, fa, TWO, false, THREE, false);
Range r1 = new Range(EMPTY, fa, ONE, false, THREE, false);
Or or = new Or(EMPTY, r1, r2);
@ -831,8 +829,8 @@ public class OptimizerTests extends ESTestCase {
public void testCombineBinaryComparisonsWithDifferentUpperLimitInclusion() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
Range r1 = new Range(EMPTY, fa, L(1), false, L(3), false);
Range r2 = new Range(EMPTY, fa, L(2), false, L(3), true);
Range r1 = new Range(EMPTY, fa, ONE, false, THREE, false);
Range r2 = new Range(EMPTY, fa, TWO, false, THREE, true);
Or or = new Or(EMPTY, r1, r2);
@ -845,8 +843,8 @@ public class OptimizerTests extends ESTestCase {
public void testRangesOverlappingNoLowerBoundary() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
Range r2 = new Range(EMPTY, fa, L(0), false, L(2), false);
Range r1 = new Range(EMPTY, fa, L(0), false, L(1), true);
Range r2 = new Range(EMPTY, fa, L(0), false, TWO, false);
Range r1 = new Range(EMPTY, fa, L(0), false, ONE, true);
Or or = new Or(EMPTY, r1, r2);
@ -860,8 +858,8 @@ public class OptimizerTests extends ESTestCase {
// a == 1 AND a == 2 -> FALSE
public void testDualEqualsConjunction() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
Equals eq1 = new Equals(EMPTY, fa, L(1));
Equals eq2 = new Equals(EMPTY, fa, L(2));
Equals eq1 = new Equals(EMPTY, fa, ONE);
Equals eq2 = new Equals(EMPTY, fa, TWO);
PropagateEquals rule = new PropagateEquals();
Expression exp = rule.rule(new And(EMPTY, eq1, eq2));
@ -871,8 +869,8 @@ public class OptimizerTests extends ESTestCase {
// 1 <= a < 10 AND a == 1 -> a == 1
public void testEliminateRangeByEqualsInInterval() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
Equals eq1 = new Equals(EMPTY, fa, L(1));
Range r = new Range(EMPTY, fa, L(1), true, L(10), false);
Equals eq1 = new Equals(EMPTY, fa, ONE);
Range r = new Range(EMPTY, fa, ONE, true, L(10), false);
PropagateEquals rule = new PropagateEquals();
Expression exp = rule.rule(new And(EMPTY, eq1, r));
@ -883,7 +881,7 @@ public class OptimizerTests extends ESTestCase {
public void testEliminateRangeByEqualsOutsideInterval() {
FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", DataType.INTEGER, emptyMap(), true));
Equals eq1 = new Equals(EMPTY, fa, L(10));
Range r = new Range(EMPTY, fa, L(1), false, L(10), false);
Range r = new Range(EMPTY, fa, ONE, false, L(10), false);
PropagateEquals rule = new PropagateEquals();
Expression exp = rule.rule(new And(EMPTY, eq1, r));