SQL: add folding to BinaryComparison (elastic/x-pack-elasticsearch#3529)

* SQL: add folding to BinaryComparison

Add missing folding to predicates and logical operators
relates elastic/x-pack-elasticsearch#3528

Original commit: elastic/x-pack-elasticsearch@4a7d3765e5
This commit is contained in:
Costin Leau 2018-01-16 23:26:30 +02:00 committed by GitHub
parent 9401569195
commit ab14aa7059
20 changed files with 163 additions and 12 deletions

View File

@ -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);

View File

@ -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();

View File

@ -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()));

View File

@ -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;
}
}

View File

@ -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());

View File

@ -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());

View File

@ -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());

View File

@ -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;

View File

@ -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());

View File

@ -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());

View File

@ -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();

View File

@ -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());

View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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);
}

View File

@ -10,7 +10,7 @@ import java.sql.JDBCType;
public class NullType extends AbstractDataType {
NullType() {
super(JDBCType.NULL, true);
super(JDBCType.NULL, false);
}
@Override

View File

@ -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();

View File

@ -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() {