diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Order.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Order.java index 094ae227a2c..1713cbe97ca 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Order.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Order.java @@ -5,10 +5,10 @@ */ package org.elasticsearch.xpack.sql.expression; -import java.util.Objects; - import org.elasticsearch.xpack.sql.tree.Location; +import java.util.Objects; + public class Order extends UnaryExpression { public enum OrderDirection { @@ -26,6 +26,11 @@ public class Order extends UnaryExpression { return direction; } + @Override + public boolean foldable() { + return false; + } + @Override public int hashCode() { return Objects.hash(child(), direction); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/UnaryExpression.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/UnaryExpression.java index 40c264e9bb9..a178347c4f1 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/UnaryExpression.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/UnaryExpression.java @@ -25,6 +25,11 @@ public abstract class UnaryExpression extends Expression { return child; } + @Override + public boolean foldable() { + return child.foldable(); + } + @Override public boolean nullable() { return child.nullable(); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/And.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/And.java index 39a076ce491..32f48806140 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/And.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/And.java @@ -10,12 +10,19 @@ import org.elasticsearch.xpack.sql.expression.BinaryOperator.Negateable; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import java.util.Objects; + public class And extends BinaryLogic implements Negateable { public And(Location location, Expression left, Expression right) { super(location, left, right); } + @Override + public Object fold() { + return Objects.equals(left().fold(), Boolean.TRUE) && Objects.equals(right().fold(), Boolean.TRUE); + } + @Override public Or negate() { return new Or(location(), new Not(location(), left()), new Not(location(), right())); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/BinaryComparison.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/BinaryComparison.java index 25aa003c7d2..a4158926fc8 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/BinaryComparison.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/BinaryComparison.java @@ -32,4 +32,12 @@ public abstract class BinaryComparison extends BinaryOperator { public DataType dataType() { return DataTypes.BOOLEAN; } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + static Integer compare(Object left, Object right) { + if (left instanceof Comparable && right instanceof Comparable) { + return Integer.valueOf(((Comparable) left).compareTo(right)); + } + return null; + } } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Equals.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Equals.java index 51529fd6917..c69301b84a8 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Equals.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Equals.java @@ -8,12 +8,19 @@ package org.elasticsearch.xpack.sql.expression.predicate; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import java.util.Objects; + public class Equals extends BinaryComparison { public Equals(Location location, Expression left, Expression right) { super(location, left, right); } + @Override + public Object fold() { + return Objects.equals(left().fold(), right().fold()); + } + @Override public Equals swapLeftAndRight() { return new Equals(location(), right(), left()); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/GreaterThan.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/GreaterThan.java index ffc1c89e6d4..ad577f63e2e 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/GreaterThan.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/GreaterThan.java @@ -15,6 +15,12 @@ public class GreaterThan extends BinaryComparison implements Negateable { super(location, left, right); } + @Override + public Object fold() { + Integer compare = compare(left().fold(), right().fold()); + return compare != null && compare.intValue() > 0; + } + @Override public LessThan swapLeftAndRight() { return new LessThan(location(), right(), left()); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/GreaterThanOrEqual.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/GreaterThanOrEqual.java index 475afe66310..0c0110c1f7d 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/GreaterThanOrEqual.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/GreaterThanOrEqual.java @@ -15,6 +15,12 @@ public class GreaterThanOrEqual extends BinaryComparison implements Negateable { super(location, left, right); } + @Override + public Object fold() { + Integer compare = compare(left().fold(), right().fold()); + return compare != null && compare.intValue() >= 0; + } + @Override public LessThanOrEqual swapLeftAndRight() { return new LessThanOrEqual(location(), right(), left()); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/IsNotNull.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/IsNotNull.java index 15918c17a1b..8425f249759 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/IsNotNull.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/IsNotNull.java @@ -17,6 +17,11 @@ public class IsNotNull extends UnaryExpression { super(location, child); } + @Override + public Object fold() { + return child().fold() != null && !DataTypes.NULL.same(child().dataType()); + } + @Override public boolean nullable() { return false; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/LessThan.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/LessThan.java index 05286ff9016..a98c503ce41 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/LessThan.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/LessThan.java @@ -15,6 +15,12 @@ public class LessThan extends BinaryComparison implements Negateable { super(location, left, right); } + @Override + public Object fold() { + Integer compare = compare(left().fold(), right().fold()); + return compare != null && compare.intValue() < 0; + } + @Override public GreaterThan swapLeftAndRight() { return new GreaterThan(location(), right(), left()); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/LessThanOrEqual.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/LessThanOrEqual.java index dc61e0362e9..11b0ce0475c 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/LessThanOrEqual.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/LessThanOrEqual.java @@ -15,6 +15,12 @@ public class LessThanOrEqual extends BinaryComparison implements Negateable { super(location, left, right); } + @Override + public Object fold() { + Integer compare = compare(left().fold(), right().fold()); + return compare != null && compare.intValue() <= 0; + } + @Override public GreaterThanOrEqual swapLeftAndRight() { return new GreaterThanOrEqual(location(), right(), left()); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Not.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Not.java index 2b8470e86d3..72f511a4523 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Not.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Not.java @@ -7,17 +7,34 @@ package org.elasticsearch.xpack.sql.expression.predicate; import org.elasticsearch.xpack.sql.expression.BinaryOperator.Negateable; import org.elasticsearch.xpack.sql.expression.Expression; +import org.elasticsearch.xpack.sql.expression.Expressions; import org.elasticsearch.xpack.sql.expression.UnaryExpression; import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; +import java.util.Objects; + public class Not extends UnaryExpression { public Not(Location location, Expression child) { super(location, child); } + @Override + protected TypeResolution resolveType() { + if (DataTypes.BOOLEAN.same(child().dataType())) { + return TypeResolution.TYPE_RESOLVED; + } + return new TypeResolution("Cannot negate expression ([" + Expressions.name(child()) + "] of type [" + + child().dataType().esName() + "])"); + } + + @Override + public Object fold() { + return Objects.equals(child().fold(), Boolean.TRUE) ? Boolean.FALSE : Boolean.TRUE; + } + @Override protected Expression canonicalize() { Expression canonicalChild = child().canonical(); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Or.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Or.java index 1d45d186e62..24354ea53cf 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Or.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Or.java @@ -10,12 +10,19 @@ import org.elasticsearch.xpack.sql.expression.BinaryOperator.Negateable; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import java.util.Objects; + public class Or extends BinaryLogic implements Negateable { public Or(Location location, Expression left, Expression right) { super(location, left, right); } + @Override + public Object fold() { + return Objects.equals(left().fold(), Boolean.TRUE) || Objects.equals(right().fold(), Boolean.TRUE); + } + @Override public Or swapLeftAndRight() { return new Or(location(), right(), left()); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Range.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Range.java index 080d547de13..08b2be78a57 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Range.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Range.java @@ -13,7 +13,7 @@ import org.elasticsearch.xpack.sql.type.DataTypes; import java.util.Arrays; import java.util.Objects; -// BETWEEN or range - is a mix of gt(e) AND lt(e) +// BETWEEN or range - is a mix of gt(e) AND lt(e) public class Range extends Expression { private final Expression value, lower, upper; @@ -54,6 +54,16 @@ public class Range extends Expression { return value.foldable() && lower.foldable() && upper.foldable(); } + @Override + public Object fold() { + Object val = value.fold(); + Integer lowerCompare = BinaryComparison.compare(lower.fold(), val); + Integer upperCompare = BinaryComparison.compare(val, upper().fold()); + boolean lowerComparsion = lowerCompare == null ? false : (includeLower ? lowerCompare <= 0 : lowerCompare < 0); + boolean upperComparsion = upperCompare == null ? false : (includeUpper ? upperCompare <= 0 : upperCompare < 0); + return lowerComparsion && upperComparsion; + } + @Override public boolean nullable() { return value.nullable() && lower.nullable() && upper.nullable(); @@ -80,7 +90,7 @@ public class Range extends Expression { } Range other = (Range) obj; - return Objects.equals(includeLower, other.includeLower) + return Objects.equals(includeLower, other.includeLower) && Objects.equals(includeUpper, other.includeUpper) && Objects.equals(value, other.value) && Objects.equals(lower, other.lower) diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/regex/Like.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/regex/Like.java index 34a77a2d0af..ebc7572edf3 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/regex/Like.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/regex/Like.java @@ -11,6 +11,8 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; +import java.util.regex.Pattern; + public class Like extends BinaryExpression { public Like(Location location, Expression left, LikePattern right) { @@ -18,8 +20,9 @@ public class Like extends BinaryExpression { } @Override - public LikePattern right() { - return (LikePattern) super.right(); + public Object fold() { + Pattern p = Pattern.compile(right().fold().toString()); + return p.matcher(left().fold().toString()).matches(); } @Override diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/regex/LikePattern.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/regex/LikePattern.java index b4e98c2a433..3056b03e144 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/regex/LikePattern.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/regex/LikePattern.java @@ -69,6 +69,17 @@ public class LikePattern extends LeafExpression { return indexNameWildcard; } + @Override + public boolean foldable() { + return true; + } + + @Override + public Object fold() { + // being in Java, the Java regex is returned + return asJavaRegex(); + } + @Override public boolean nullable() { return false; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/regex/RLike.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/regex/RLike.java index 8762f30b505..ce0571c89e9 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/regex/RLike.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/regex/RLike.java @@ -12,6 +12,8 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; +import java.util.regex.Pattern; + public class RLike extends BinaryExpression { public RLike(Location location, Expression left, Literal right) { @@ -19,8 +21,9 @@ public class RLike extends BinaryExpression { } @Override - public Literal right() { - return (Literal) super.right(); + public Object fold() { + Pattern p = Pattern.compile(right().fold().toString()); + return p.matcher(left().fold().toString()).matches(); } @Override diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/planner/QueryTranslator.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/planner/QueryTranslator.java index fb9d7bee09e..5a59b775640 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/planner/QueryTranslator.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/planner/QueryTranslator.java @@ -441,7 +441,7 @@ abstract class QueryTranslator { } if (e instanceof Like) { - LikePattern p = ((Like) e).right(); + LikePattern p = (LikePattern) ((Like) e).right(); if (inexact) { q = new QueryStringQuery(e.location(), p.asLuceneWildcard(), target); } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/type/NullType.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/type/NullType.java index 3c59d711ec3..f96f8411b1e 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/type/NullType.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/type/NullType.java @@ -10,7 +10,7 @@ import java.sql.JDBCType; public class NullType extends AbstractDataType { NullType() { - super(JDBCType.NULL, true); + super(JDBCType.NULL, false); } @Override diff --git a/sql/server/src/test/java/org/elasticsearch/xpack/sql/optimizer/OptimizerTests.java b/sql/server/src/test/java/org/elasticsearch/xpack/sql/optimizer/OptimizerTests.java index dddf071b04a..18af4cdd5d5 100644 --- a/sql/server/src/test/java/org/elasticsearch/xpack/sql/optimizer/OptimizerTests.java +++ b/sql/server/src/test/java/org/elasticsearch/xpack/sql/optimizer/OptimizerTests.java @@ -20,9 +20,15 @@ import org.elasticsearch.xpack.sql.expression.predicate.And; import org.elasticsearch.xpack.sql.expression.predicate.Equals; import org.elasticsearch.xpack.sql.expression.predicate.GreaterThan; import org.elasticsearch.xpack.sql.expression.predicate.GreaterThanOrEqual; +import org.elasticsearch.xpack.sql.expression.predicate.IsNotNull; import org.elasticsearch.xpack.sql.expression.predicate.LessThan; import org.elasticsearch.xpack.sql.expression.predicate.LessThanOrEqual; +import org.elasticsearch.xpack.sql.expression.predicate.Not; import org.elasticsearch.xpack.sql.expression.predicate.Or; +import org.elasticsearch.xpack.sql.expression.predicate.Range; +import org.elasticsearch.xpack.sql.expression.regex.Like; +import org.elasticsearch.xpack.sql.expression.regex.LikePattern; +import org.elasticsearch.xpack.sql.expression.regex.RLike; import org.elasticsearch.xpack.sql.optimizer.Optimizer.BinaryComparisonSimplification; import org.elasticsearch.xpack.sql.optimizer.Optimizer.BooleanLiteralsOnTheRight; import org.elasticsearch.xpack.sql.optimizer.Optimizer.BooleanSimplification; @@ -205,6 +211,40 @@ public class OptimizerTests extends ESTestCase { assertEquals(5, ((Literal) c).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)))); + } + + 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))); + } + + 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))); + } + + 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)))); + } + + public void testConstantNot() { + assertEquals(Literal.FALSE, new ConstantFolding().rule(new Not(EMPTY, Literal.TRUE))); + assertEquals(Literal.TRUE, new ConstantFolding().rule(new Not(EMPTY, Literal.FALSE))); + } + + public void testConstantFoldingLikes() { + assertEquals(Literal.TRUE, + new ConstantFolding().rule(new Like(EMPTY, Literal.of(EMPTY, "test_emp"), new LikePattern(EMPTY, "test%", (char) 0)))); + assertEquals(Literal.TRUE, + new ConstantFolding().rule(new RLike(EMPTY, Literal.of(EMPTY, "test_emp"), Literal.of(EMPTY, "test.emp")))); + } public void testBinaryComparisonSimplification() { assertEquals(Literal.TRUE, new BinaryComparisonSimplification().rule(new Equals(EMPTY, L(5), L(5)))); @@ -224,7 +264,6 @@ public class OptimizerTests extends ESTestCase { assertEquals(L(5), eq.right()); } - public void testBoolSimplifyOr() { BooleanSimplification simplification = new BooleanSimplification(); diff --git a/sql/server/src/test/java/org/elasticsearch/xpack/sql/parser/LikeEscapingParsingTests.java b/sql/server/src/test/java/org/elasticsearch/xpack/sql/parser/LikeEscapingParsingTests.java index 3d17fdd4931..9e173dd5429 100644 --- a/sql/server/src/test/java/org/elasticsearch/xpack/sql/parser/LikeEscapingParsingTests.java +++ b/sql/server/src/test/java/org/elasticsearch/xpack/sql/parser/LikeEscapingParsingTests.java @@ -31,7 +31,7 @@ public class LikeEscapingParsingTests extends ESTestCase { Expression exp = parser.createExpression(String.format(Locale.ROOT, "exp LIKE %s", pattern)); assertThat(exp, instanceOf(Like.class)); Like l = (Like) exp; - return l.right(); + return (LikePattern) l.right(); } public void testNoEscaping() {