Browse Source

SQL: Fix issue with date range queries and timezone (#56115)

Previously, the timezone parameter was not passed to the RangeQuery
and as a results queries that use the ES date math notation (now,
now-1d, now/d, now/h, now+2h, etc.) were using the UTC timezone and
not the one passed through the "timezone"/"time_zone" JDBC/REST params.
As a consequence, the date math defined dates were always considered in
UTC and possibly led to incorrect results for queries like:
```
SELECT * FROM t WHERE date BETWEEN now-1d/d AND now/d
```

Fixes: #56049
Marios Trivyzas 5 years ago
parent
commit
300f010c0b
35 changed files with 667 additions and 432 deletions
  1. 3 2
      x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/parser/EqlParser.java
  2. 14 6
      x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/parser/ExpressionBuilder.java
  3. 3 4
      x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/parser/LogicalPlanBuilder.java
  4. 10 0
      x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/parser/ParserParams.java
  5. 1 1
      x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/TransportEqlSearchAction.java
  6. 7 6
      x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/parser/ExpressionTests.java
  7. 3 2
      x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/parser/LogicalPlanTests.java
  8. 16 8
      x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/Range.java
  9. 10 1
      x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/BinaryComparison.java
  10. 11 5
      x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/Equals.java
  11. 8 6
      x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/GreaterThan.java
  12. 8 6
      x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/GreaterThanOrEqual.java
  13. 8 6
      x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/LessThan.java
  14. 8 6
      x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/LessThanOrEqual.java
  15. 8 6
      x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/NotEquals.java
  16. 7 5
      x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/NullEquals.java
  17. 17 15
      x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/optimizer/OptimizerRules.java
  18. 12 6
      x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/planner/ExpressionTranslators.java
  19. 17 6
      x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/querydsl/query/RangeQuery.java
  20. 42 0
      x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/TestUtils.java
  21. 34 27
      x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/BinaryComparisonProcessorTests.java
  22. 185 175
      x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/optimizer/OptimizerRulesTests.java
  23. 4 2
      x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/AstBuilder.java
  24. 3 2
      x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/CommandBuilder.java
  25. 13 10
      x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ExpressionBuilder.java
  26. 3 2
      x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/LogicalPlanBuilder.java
  27. 17 7
      x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlParser.java
  28. 1 1
      x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/session/SqlSession.java
  29. 9 7
      x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/predicate/conditional/CaseTests.java
  30. 4 2
      x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/predicate/conditional/IifTests.java
  31. 36 31
      x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/optimizer/OptimizerTests.java
  32. 2 1
      x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysColumnsTests.java
  33. 1 1
      x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTablesTests.java
  34. 135 64
      x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorTests.java
  35. 7 3
      x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/querydsl/container/QueryContainerTests.java

+ 3 - 2
x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/parser/EqlParser.java

@@ -30,6 +30,7 @@ import java.util.function.BiFunction;
 import java.util.function.Function;
 
 import static java.lang.String.format;
+import static org.elasticsearch.xpack.ql.type.DateUtils.UTC;
 
 public class EqlParser {
 
@@ -41,7 +42,7 @@ public class EqlParser {
      * Parses an EQL statement into execution plan
      */
     public LogicalPlan createStatement(String eql) {
-        return createStatement(eql, new ParserParams());
+        return createStatement(eql, new ParserParams(UTC));
     }
 
     public LogicalPlan createStatement(String eql, ParserParams params) {
@@ -52,7 +53,7 @@ public class EqlParser {
     }
 
     public Expression createExpression(String expression) {
-        return createExpression(expression, new ParserParams());
+        return createExpression(expression, new ParserParams(UTC));
     }
 
     public Expression createExpression(String expression, ParserParams params) {

+ 14 - 6
x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/parser/ExpressionBuilder.java

@@ -45,6 +45,7 @@ import org.elasticsearch.xpack.ql.type.DataType;
 import org.elasticsearch.xpack.ql.type.DataTypes;
 import org.elasticsearch.xpack.ql.util.StringUtils;
 
+import java.time.ZoneId;
 import java.util.List;
 
 import static java.util.Collections.emptyList;
@@ -52,6 +53,12 @@ import static java.util.Collections.emptyList;
 
 public class ExpressionBuilder extends IdentifierBuilder {
 
+    protected final ParserParams params;
+
+    public ExpressionBuilder(ParserParams params) {
+        this.params = params;
+    }
+
     protected Expression expression(ParseTree ctx) {
         return typedParsing(ctx, Expression.class);
     }
@@ -115,20 +122,21 @@ public class ExpressionBuilder extends IdentifierBuilder {
         TerminalNode op = (TerminalNode) ctx.comparisonOperator().getChild(0);
 
         Source source = source(ctx);
+        ZoneId zoneId = params.zoneId();
 
         switch (op.getSymbol().getType()) {
             case EqlBaseParser.EQ:
-                return new Equals(source, left, right);
+                return new Equals(source, left, right, zoneId);
             case EqlBaseParser.NEQ:
-                return new NotEquals(source, left, right);
+                return new NotEquals(source, left, right, zoneId);
             case EqlBaseParser.LT:
-                return new LessThan(source, left, right);
+                return new LessThan(source, left, right, zoneId);
             case EqlBaseParser.LTE:
-                return new LessThanOrEqual(source, left, right);
+                return new LessThanOrEqual(source, left, right, zoneId);
             case EqlBaseParser.GT:
-                return new GreaterThan(source, left, right);
+                return new GreaterThan(source, left, right, zoneId);
             case EqlBaseParser.GTE:
-                return new GreaterThanOrEqual(source, left, right);
+                return new GreaterThanOrEqual(source, left, right, zoneId);
             default:
                 throw new ParsingException(source, "Unknown operator {}", source.text());
         }

+ 3 - 4
x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/parser/LogicalPlanBuilder.java

@@ -38,11 +38,10 @@ import static java.util.Collections.singletonList;
 
 public abstract class LogicalPlanBuilder extends ExpressionBuilder {
 
-    private final ParserParams params;
     private final UnresolvedRelation RELATION = new UnresolvedRelation(Source.EMPTY, null, "", false, "");
 
     public LogicalPlanBuilder(ParserParams params) {
-        this.params = params;
+        super(params);
     }
 
     @Override
@@ -56,7 +55,7 @@ public abstract class LogicalPlanBuilder extends ExpressionBuilder {
             Literal eventValue = new Literal(eventSource, eventName, DataTypes.KEYWORD);
 
             UnresolvedAttribute eventField = new UnresolvedAttribute(eventSource, params.fieldEventCategory());
-            Expression eventMatch = new Equals(eventSource, eventField, eventValue);
+            Expression eventMatch = new Equals(eventSource, eventField, eventValue, params.zoneId());
 
             condition = new And(source, eventMatch, condition);
         }
@@ -214,4 +213,4 @@ public abstract class LogicalPlanBuilder extends ExpressionBuilder {
                     text(numberCtx));
         }
     }
-}
+}

+ 10 - 0
x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/parser/ParserParams.java

@@ -6,6 +6,7 @@
 
 package org.elasticsearch.xpack.eql.parser;
 
+import java.time.ZoneId;
 import java.util.List;
 
 import static java.util.Collections.emptyList;
@@ -15,10 +16,15 @@ import static org.elasticsearch.xpack.eql.action.RequestDefaults.FIELD_IMPLICIT_
 
 public class ParserParams {
 
+    private final ZoneId zoneId;
     private String fieldEventCategory = FIELD_EVENT_CATEGORY;
     private String fieldTimestamp = FIELD_TIMESTAMP;
     private String implicitJoinKey = FIELD_IMPLICIT_JOIN_KEY;
     private List<Object> queryParams = emptyList();
+
+    public ParserParams(ZoneId zoneId) {
+        this.zoneId = zoneId;
+    }
     
     public String fieldEventCategory() {
         return fieldEventCategory;
@@ -55,4 +61,8 @@ public class ParserParams {
         this.queryParams = params;
         return this;
     }
+
+    public ZoneId zoneId() {
+        return zoneId;
+    }
 }

+ 1 - 1
x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/TransportEqlSearchAction.java

@@ -64,7 +64,7 @@ public class TransportEqlSearchAction extends HandledTransportAction<EqlSearchRe
         boolean includeFrozen = request.indicesOptions().ignoreThrottled() == false;
         String clientId = null;
 
-        ParserParams params = new ParserParams()
+        ParserParams params = new ParserParams(zoneId)
             .fieldEventCategory(request.eventCategoryField())
             .fieldTimestamp(request.timestampField())
             .implicitJoinKey(request.implicitJoinKeyField());

+ 7 - 6
x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/parser/ExpressionTests.java

@@ -30,6 +30,7 @@ import java.util.Arrays;
 import java.util.List;
 
 import static org.elasticsearch.xpack.eql.parser.AbstractBuilder.unquoteString;
+import static org.elasticsearch.xpack.ql.TestUtils.UTC;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.instanceOf;
 import static org.hamcrest.Matchers.is;
@@ -144,12 +145,12 @@ public class ExpressionTests extends ESTestCase {
         Expression field = expr(fieldText);
         Expression value = expr(valueText);
 
-        assertEquals(new Equals(null, field, value), expr(fieldText + "==" + valueText));
-        assertEquals(new NotEquals(null, field, value), expr(fieldText + "!=" + valueText));
-        assertEquals(new LessThanOrEqual(null, field, value), expr(fieldText + "<=" + valueText));
-        assertEquals(new GreaterThanOrEqual(null, field, value), expr(fieldText + ">=" + valueText));
-        assertEquals(new GreaterThan(null, field, value), expr(fieldText + ">" + valueText));
-        assertEquals(new LessThan(null, field, value), expr(fieldText + "<" + valueText));
+        assertEquals(new Equals(null, field, value, UTC), expr(fieldText + "==" + valueText));
+        assertEquals(new NotEquals(null, field, value, UTC), expr(fieldText + "!=" + valueText));
+        assertEquals(new LessThanOrEqual(null, field, value, UTC), expr(fieldText + "<=" + valueText));
+        assertEquals(new GreaterThanOrEqual(null, field, value, UTC), expr(fieldText + ">=" + valueText));
+        assertEquals(new GreaterThan(null, field, value, UTC), expr(fieldText + ">" + valueText));
+        assertEquals(new LessThan(null, field, value, UTC), expr(fieldText + "<" + valueText));
     }
 
     public void testBoolean() {

+ 3 - 2
x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/parser/LogicalPlanTests.java

@@ -26,6 +26,7 @@ import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 import static java.util.Collections.singletonList;
+import static org.elasticsearch.xpack.ql.type.DateUtils.UTC;
 
 public class LogicalPlanTests extends ESTestCase {
 
@@ -56,7 +57,7 @@ public class LogicalPlanTests extends ESTestCase {
     }
 
     public void testParameterizedEventQuery() {
-        ParserParams params = new ParserParams().fieldEventCategory("myCustomEvent");
+        ParserParams params = new ParserParams(UTC).fieldEventCategory("myCustomEvent");
         LogicalPlan fullQuery = parser.createStatement("process where process_name == 'net.exe'", params);
         Expression fullExpression = expr("myCustomEvent == 'process' and process_name == 'net.exe'");
 
@@ -126,4 +127,4 @@ public class LogicalPlanTests extends ESTestCase {
         assertEquals(new TimeValue(2, TimeUnit.SECONDS), maxSpan);
 
     }
-}
+}

+ 16 - 8
x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/Range.java

@@ -22,6 +22,7 @@ import org.elasticsearch.xpack.ql.tree.Source;
 import org.elasticsearch.xpack.ql.type.DataType;
 import org.elasticsearch.xpack.ql.type.DataTypes;
 
+import java.time.ZoneId;
 import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
@@ -35,20 +36,22 @@ public class Range extends ScalarFunction {
 
     private final Expression value, lower, upper;
     private final boolean includeLower, includeUpper;
+    private final ZoneId zoneId;
 
-    public Range(Source source, Expression value, Expression lower, boolean includeLower, Expression upper, boolean includeUpper) {
-        super(source, asList(value, lower, upper));
+    public Range(Source src, Expression value, Expression lower, boolean inclLower, Expression upper, boolean inclUpper, ZoneId zoneId) {
+        super(src, asList(value, lower, upper));
 
         this.value = value;
         this.lower = lower;
         this.upper = upper;
-        this.includeLower = includeLower;
-        this.includeUpper = includeUpper;
+        this.includeLower = inclLower;
+        this.includeUpper = inclUpper;
+        this.zoneId = zoneId;
     }
 
     @Override
     protected NodeInfo<Range> info() {
-        return NodeInfo.create(this, Range::new, value, lower, includeLower, upper, includeUpper);
+        return NodeInfo.create(this, Range::new, value, lower, includeLower, upper, includeUpper, zoneId);
     }
 
     @Override
@@ -56,7 +59,7 @@ public class Range extends ScalarFunction {
         if (newChildren.size() != 3) {
             throw new IllegalArgumentException("expected [3] children but received [" + newChildren.size() + "]");
         }
-        return new Range(source(), newChildren.get(0), newChildren.get(1), includeLower, newChildren.get(2), includeUpper);
+        return new Range(source(), newChildren.get(0), newChildren.get(1), includeLower, newChildren.get(2), includeUpper, zoneId);
     }
 
     public Expression value() {
@@ -79,6 +82,10 @@ public class Range extends ScalarFunction {
         return includeUpper;
     }
 
+    public ZoneId zoneId() {
+        return zoneId;
+    }
+
     @Override
     public boolean foldable() {
         if (lower.foldable() && upper.foldable()) {
@@ -160,7 +167,7 @@ public class Range extends ScalarFunction {
 
     @Override
     public int hashCode() {
-        return Objects.hash(includeLower, includeUpper, value, lower, upper);
+        return Objects.hash(includeLower, includeUpper, value, lower, upper, zoneId);
     }
 
     @Override
@@ -178,6 +185,7 @@ public class Range extends ScalarFunction {
                 && Objects.equals(includeUpper, other.includeUpper)
                 && Objects.equals(value, other.value)
                 && Objects.equals(lower, other.lower)
-                && Objects.equals(upper, other.upper);
+                && Objects.equals(upper, other.upper)
+                && Objects.equals(zoneId, other.zoneId);
     }
 }

+ 10 - 1
x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/BinaryComparison.java

@@ -15,11 +15,20 @@ import org.elasticsearch.xpack.ql.tree.Source;
 import org.elasticsearch.xpack.ql.type.DataType;
 import org.elasticsearch.xpack.ql.type.DataTypes;
 
+import java.time.ZoneId;
+
 // marker class to indicate operations that rely on values
 public abstract class BinaryComparison extends BinaryOperator<Object, Object, Boolean, BinaryComparisonOperation> {
 
-    protected BinaryComparison(Source source, Expression left, Expression right, BinaryComparisonOperation operation) {
+    private final ZoneId zoneId;
+
+    protected BinaryComparison(Source source, Expression left, Expression right, BinaryComparisonOperation operation, ZoneId zoneId) {
         super(source, left, right, operation);
+        this.zoneId = zoneId;
+    }
+
+    public ZoneId zoneId() {
+        return zoneId;
     }
 
     @Override

+ 11 - 5
x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/Equals.java

@@ -11,29 +11,35 @@ import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.Binar
 import org.elasticsearch.xpack.ql.tree.NodeInfo;
 import org.elasticsearch.xpack.ql.tree.Source;
 
+import java.time.ZoneId;
+
 public class Equals extends BinaryComparison implements Negatable<BinaryComparison> {
 
     public Equals(Source source, Expression left, Expression right) {
-        super(source, left, right, BinaryComparisonOperation.EQ);
+        super(source, left, right, BinaryComparisonOperation.EQ, null);
+    }
+
+    public Equals(Source source, Expression left, Expression right, ZoneId zoneId) {
+        super(source, left, right, BinaryComparisonOperation.EQ, zoneId);
     }
 
     @Override
     protected NodeInfo<Equals> info() {
-        return NodeInfo.create(this, Equals::new, left(), right());
+        return NodeInfo.create(this, Equals::new, left(), right(), zoneId());
     }
 
     @Override
     protected Equals replaceChildren(Expression newLeft, Expression newRight) {
-        return new Equals(source(), newLeft, newRight);
+        return new Equals(source(), newLeft, newRight, zoneId());
     }
 
     @Override
     public Equals swapLeftAndRight() {
-        return new Equals(source(), right(), left());
+        return new Equals(source(), right(), left(), zoneId());
     }
 
     @Override
     public BinaryComparison negate() {
-        return new NotEquals(source(), left(), right());
+        return new NotEquals(source(), left(), right(), zoneId());
     }
 }

+ 8 - 6
x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/GreaterThan.java

@@ -11,29 +11,31 @@ import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.Binar
 import org.elasticsearch.xpack.ql.tree.NodeInfo;
 import org.elasticsearch.xpack.ql.tree.Source;
 
+import java.time.ZoneId;
+
 public class GreaterThan extends BinaryComparison implements Negatable<BinaryComparison> {
 
-    public GreaterThan(Source source, Expression left, Expression right) {
-        super(source, left, right, BinaryComparisonOperation.GT);
+    public GreaterThan(Source source, Expression left, Expression right, ZoneId zoneId) {
+        super(source, left, right, BinaryComparisonOperation.GT, zoneId);
     }
 
     @Override
     protected NodeInfo<GreaterThan> info() {
-        return NodeInfo.create(this, GreaterThan::new, left(), right());
+        return NodeInfo.create(this, GreaterThan::new, left(), right(), zoneId());
     }
 
     @Override
     protected GreaterThan replaceChildren(Expression newLeft, Expression newRight) {
-        return new GreaterThan(source(), newLeft, newRight);
+        return new GreaterThan(source(), newLeft, newRight, zoneId());
     }
 
     @Override
     public LessThan swapLeftAndRight() {
-        return new LessThan(source(), right(), left());
+        return new LessThan(source(), right(), left(), zoneId());
     }
 
     @Override
     public LessThanOrEqual negate() {
-        return new LessThanOrEqual(source(), left(), right());
+        return new LessThanOrEqual(source(), left(), right(), zoneId());
     }
 }

+ 8 - 6
x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/GreaterThanOrEqual.java

@@ -11,29 +11,31 @@ import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.Binar
 import org.elasticsearch.xpack.ql.tree.NodeInfo;
 import org.elasticsearch.xpack.ql.tree.Source;
 
+import java.time.ZoneId;
+
 public class GreaterThanOrEqual extends BinaryComparison implements Negatable<BinaryComparison> {
 
-    public GreaterThanOrEqual(Source source, Expression left, Expression right) {
-        super(source, left, right, BinaryComparisonOperation.GTE);
+    public GreaterThanOrEqual(Source source, Expression left, Expression right, ZoneId zoneId) {
+        super(source, left, right, BinaryComparisonOperation.GTE, zoneId);
     }
 
     @Override
     protected NodeInfo<GreaterThanOrEqual> info() {
-        return NodeInfo.create(this, GreaterThanOrEqual::new, left(), right());
+        return NodeInfo.create(this, GreaterThanOrEqual::new, left(), right(), zoneId());
     }
 
     @Override
     protected GreaterThanOrEqual replaceChildren(Expression newLeft, Expression newRight) {
-        return new GreaterThanOrEqual(source(), newLeft, newRight);
+        return new GreaterThanOrEqual(source(), newLeft, newRight, zoneId());
     }
 
     @Override
     public LessThanOrEqual swapLeftAndRight() {
-        return new LessThanOrEqual(source(), right(), left());
+        return new LessThanOrEqual(source(), right(), left(), zoneId());
     }
 
     @Override
     public LessThan negate() {
-        return new LessThan(source(), left(), right());
+        return new LessThan(source(), left(), right(), zoneId());
     }
 }

+ 8 - 6
x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/LessThan.java

@@ -11,29 +11,31 @@ import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.Binar
 import org.elasticsearch.xpack.ql.tree.NodeInfo;
 import org.elasticsearch.xpack.ql.tree.Source;
 
+import java.time.ZoneId;
+
 public class LessThan extends BinaryComparison implements Negatable<BinaryComparison> {
 
-    public LessThan(Source source, Expression left, Expression right) {
-        super(source, left, right, BinaryComparisonOperation.LT);
+    public LessThan(Source source, Expression left, Expression right, ZoneId zoneId) {
+        super(source, left, right, BinaryComparisonOperation.LT, zoneId);
     }
 
     @Override
     protected NodeInfo<LessThan> info() {
-        return NodeInfo.create(this, LessThan::new, left(), right());
+        return NodeInfo.create(this, LessThan::new, left(), right(), zoneId());
     }
 
     @Override
     protected LessThan replaceChildren(Expression newLeft, Expression newRight) {
-        return new LessThan(source(), newLeft, newRight);
+        return new LessThan(source(), newLeft, newRight, zoneId());
     }
 
     @Override
     public GreaterThan swapLeftAndRight() {
-        return new GreaterThan(source(), right(), left());
+        return new GreaterThan(source(), right(), left(), zoneId());
     }
 
     @Override
     public GreaterThanOrEqual negate() {
-        return new GreaterThanOrEqual(source(), left(), right());
+        return new GreaterThanOrEqual(source(), left(), right(), zoneId());
     }
 }

+ 8 - 6
x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/LessThanOrEqual.java

@@ -11,29 +11,31 @@ import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.Binar
 import org.elasticsearch.xpack.ql.tree.NodeInfo;
 import org.elasticsearch.xpack.ql.tree.Source;
 
+import java.time.ZoneId;
+
 public class LessThanOrEqual extends BinaryComparison implements Negatable<BinaryComparison> {
 
-    public LessThanOrEqual(Source source, Expression left, Expression right) {
-        super(source, left, right, BinaryComparisonOperation.LTE);
+    public LessThanOrEqual(Source source, Expression left, Expression right, ZoneId zoneId) {
+        super(source, left, right, BinaryComparisonOperation.LTE, zoneId);
     }
 
     @Override
     protected NodeInfo<LessThanOrEqual> info() {
-        return NodeInfo.create(this, LessThanOrEqual::new, left(), right());
+        return NodeInfo.create(this, LessThanOrEqual::new, left(), right(), zoneId());
     }
 
     @Override
     protected LessThanOrEqual replaceChildren(Expression newLeft, Expression newRight) {
-        return new LessThanOrEqual(source(), newLeft, newRight);
+        return new LessThanOrEqual(source(), newLeft, newRight, zoneId());
     }
 
     @Override
     public GreaterThanOrEqual swapLeftAndRight() {
-        return new GreaterThanOrEqual(source(), right(), left());
+        return new GreaterThanOrEqual(source(), right(), left(), zoneId());
     }
 
     @Override
     public GreaterThan negate() {
-        return new GreaterThan(source(), left(), right());
+        return new GreaterThan(source(), left(), right(), zoneId());
     }
 }

+ 8 - 6
x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/NotEquals.java

@@ -11,29 +11,31 @@ import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.Binar
 import org.elasticsearch.xpack.ql.tree.NodeInfo;
 import org.elasticsearch.xpack.ql.tree.Source;
 
+import java.time.ZoneId;
+
 public class NotEquals extends BinaryComparison implements Negatable<BinaryComparison> {
 
-    public NotEquals(Source source, Expression left, Expression right) {
-        super(source, left, right, BinaryComparisonOperation.NEQ);
+    public NotEquals(Source source, Expression left, Expression right, ZoneId zoneId) {
+        super(source, left, right, BinaryComparisonOperation.NEQ, zoneId);
     }
 
     @Override
     protected NodeInfo<NotEquals> info() {
-        return NodeInfo.create(this, NotEquals::new, left(), right());
+        return NodeInfo.create(this, NotEquals::new, left(), right(), zoneId());
     }
 
     @Override
     protected NotEquals replaceChildren(Expression newLeft, Expression newRight) {
-        return new NotEquals(source(), newLeft, newRight);
+        return new NotEquals(source(), newLeft, newRight, zoneId());
     }
 
     @Override
     public NotEquals swapLeftAndRight() {
-        return new NotEquals(source(), right(), left());
+        return new NotEquals(source(), right(), left(), zoneId());
     }
 
     @Override
     public BinaryComparison negate() {
-        return new Equals(source(), left(), right());
+        return new Equals(source(), left(), right(), zoneId());
     }
 }

+ 7 - 5
x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/NullEquals.java

@@ -11,28 +11,30 @@ import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.Binar
 import org.elasticsearch.xpack.ql.tree.NodeInfo;
 import org.elasticsearch.xpack.ql.tree.Source;
 
+import java.time.ZoneId;
+
 /**
  * Implements the MySQL {@code <=>} operator
  */
 public class NullEquals extends BinaryComparison {
 
-    public NullEquals(Source source, Expression left, Expression right) {
-        super(source, left, right, BinaryComparisonOperation.NULLEQ);
+    public NullEquals(Source source, Expression left, Expression right, ZoneId zoneId) {
+        super(source, left, right, BinaryComparisonOperation.NULLEQ, zoneId);
     }
 
     @Override
     protected NodeInfo<NullEquals> info() {
-        return NodeInfo.create(this, NullEquals::new, left(), right());
+        return NodeInfo.create(this, NullEquals::new, left(), right(), zoneId());
     }
 
     @Override
     protected NullEquals replaceChildren(Expression newLeft, Expression newRight) {
-        return new NullEquals(source(), newLeft, newRight);
+        return new NullEquals(source(), newLeft, newRight, zoneId());
     }
 
     @Override
     public NullEquals swapLeftAndRight() {
-        return new NullEquals(source(), right(), left());
+        return new NullEquals(source(), right(), left(), zoneId());
     }
 
     @Override

+ 17 - 15
x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/optimizer/OptimizerRules.java

@@ -446,14 +446,14 @@ public final class OptimizerRules {
                         if (lowerComp != null && lowerComp == 0) {
                             if (!range.includeLower()) { // a = 2 OR 2 < a < ? -> 2 <= a < ?
                                 ranges.set(i, new Range(range.source(), range.value(), range.lower(), true,
-                                    range.upper(), range.includeUpper()));
+                                    range.upper(), range.includeUpper(), range.zoneId()));
                             } // else : a = 2 OR 2 <= a < ? -> 2 <= a < ?
                             removeEquals = true; // update range with lower equality instead or simply superfluous
                             break;
                         } else if (upperComp != null && upperComp == 0) {
                             if (!range.includeUpper()) { // a = 2 OR ? < a < 2 -> ? < a <= 2
                                 ranges.set(i, new Range(range.source(), range.value(), range.lower(), range.includeLower(),
-                                    range.upper(), true));
+                                    range.upper(), true, range.zoneId()));
                             } // else : a = 2 OR ? < a <= 2 -> ? < a <= 2
                             removeEquals = true; // update range with upper equality instead
                             break;
@@ -481,7 +481,7 @@ public final class OptimizerRules {
                                 if (comp < 0) { // a = 1 OR a > 2 -> nop
                                     continue;
                                 } else if (comp == 0 && bc instanceof GreaterThan) { // a = 2 OR a > 2 -> a >= 2
-                                    inequalities.set(i, new GreaterThanOrEqual(bc.source(), bc.left(), bc.right()));
+                                    inequalities.set(i, new GreaterThanOrEqual(bc.source(), bc.left(), bc.right(), bc.zoneId()));
                                 } // else (0 < comp || bc instanceof GreaterThanOrEqual) :
                                 // a = 3 OR a > 2 -> a > 2; a = 2 OR a => 2 -> a => 2
 
@@ -492,7 +492,7 @@ public final class OptimizerRules {
                                     continue;
                                 }
                                 if (comp == 0 && bc instanceof LessThan) { // a = 2 OR a < 2 -> a <= 2
-                                    inequalities.set(i, new LessThanOrEqual(bc.source(), bc.left(), bc.right()));
+                                    inequalities.set(i, new LessThanOrEqual(bc.source(), bc.left(), bc.right(), bc.zoneId()));
                                 } // else (comp < 0 || bc instanceof LessThanOrEqual) : a = 2 OR a < 3 -> a < 3; a = 2 OR a <= 2 -> a <= 2
                                 removeEquals = true; // update range with equality instead or simply superfluous
                                 break;
@@ -598,7 +598,7 @@ public final class OptimizerRules {
 
                             ranges.add(new Range(and.source(), main.left(),
                                 main.right(), main instanceof GreaterThanOrEqual,
-                                other.right(), other instanceof LessThanOrEqual));
+                                other.right(), other instanceof LessThanOrEqual, main.zoneId()));
 
                             changed = true;
                             step = 0;
@@ -612,7 +612,7 @@ public final class OptimizerRules {
 
                             ranges.add(new Range(and.source(), main.left(),
                                 other.right(), other instanceof GreaterThanOrEqual,
-                                main.right(), main instanceof LessThanOrEqual));
+                                main.right(), main instanceof LessThanOrEqual, main.zoneId()));
 
                             changed = true;
                             step = 0;
@@ -739,7 +739,8 @@ public final class OptimizerRules {
                                             lower ? main.lower() : other.lower(),
                                             lower ? main.includeLower() : other.includeLower(),
                                             upper ? main.upper() : other.upper(),
-                                            upper ? main.includeUpper() : other.includeUpper()));
+                                            upper ? main.includeUpper() : other.includeUpper(),
+                                            main.zoneId()));
                         }
 
                         // range was comparable
@@ -755,7 +756,8 @@ public final class OptimizerRules {
                                             lower ? main.lower() : other.lower(),
                                             lower ? main.includeLower() : other.includeLower(),
                                             upper ? main.upper() : other.upper(),
-                                            upper ? main.includeUpper() : other.includeUpper()));
+                                            upper ? main.includeUpper() : other.includeUpper(),
+                                            main.zoneId()));
                             return true;
                         }
 
@@ -790,7 +792,7 @@ public final class OptimizerRules {
                                     ranges.add(i,
                                             new Range(other.source(), other.value(),
                                                     main.right(), lowerEq ? false : main instanceof GreaterThanOrEqual,
-                                                    other.upper(), other.includeUpper()));
+                                                    other.upper(), other.includeUpper(), other.zoneId()));
                                 }
 
                                 // found a match
@@ -810,7 +812,7 @@ public final class OptimizerRules {
                                     ranges.remove(i);
                                     ranges.add(i, new Range(other.source(), other.value(),
                                             other.lower(), other.includeLower(),
-                                            main.right(), upperEq ? false : main instanceof LessThanOrEqual));
+                                            main.right(), upperEq ? false : main instanceof LessThanOrEqual, other.zoneId()));
                                 }
 
                                 // found a match
@@ -924,7 +926,7 @@ public final class OptimizerRules {
                         if (comp <= 0) {
                             if (comp == 0 && range.includeLower()) { // a != 2 AND 2 <= a < ? -> 2 < a < ?
                                 ranges.set(i, new Range(range.source(), range.value(), range.lower(), false, range.upper(),
-                                    range.includeUpper()));
+                                    range.includeUpper(), range.zoneId()));
                             }
                             // else: !.includeLower() : a != 2 AND 2 < a < 3 -> 2 < a < 3; or:
                             // else: comp < 0 : a != 2 AND 3 < a < ? ->  3 < a < ?
@@ -935,7 +937,7 @@ public final class OptimizerRules {
                             if (comp != null && comp >= 0) {
                                 if (comp == 0 && range.includeUpper()) { // a != 4 AND 2 < a <= 4 -> 2 < a < 4
                                     ranges.set(i, new Range(range.source(), range.value(), range.lower(), range.includeLower(),
-                                        range.upper(), false));
+                                        range.upper(), false, range.zoneId()));
                                 }
                                 // else: !.includeUpper() : a != 4 AND 2 < a < 4 -> 2 < a < 4
                                 // else: comp > 0 : a != 4 AND 2 < a < 3 -> 2 < a < 3
@@ -951,7 +953,7 @@ public final class OptimizerRules {
                     if (comp != null && comp >= 0) {
                         if (comp == 0 && range.includeUpper()) { // a != 3 AND ?? < a <= 3 -> ?? < a < 3
                             ranges.set(i, new Range(range.source(), range.value(), range.lower(), range.includeLower(), range.upper(),
-                                false));
+                                false, range.zoneId()));
                         }
                         // else: !.includeUpper() : a != 3 AND ?? < a < 3 -> ?? < a < 3
                         // else: comp > 0 : a != 3 and ?? < a < 2 -> ?? < a < 2
@@ -979,7 +981,7 @@ public final class OptimizerRules {
                     if (comp != null) {
                         if (comp >= 0) {
                             if (comp == 0 && bc instanceof LessThanOrEqual) { // a != 2 AND a <= 2 -> a < 2
-                                bcs.set(i, new LessThan(bc.source(), bc.left(), bc.right()));
+                                bcs.set(i, new LessThan(bc.source(), bc.left(), bc.right(), bc.zoneId()));
                             } // else : comp > 0 (a != 2 AND a </<= 1 -> a </<= 1), or == 0 && bc i.of "<" (a != 2 AND a < 2 -> a < 2)
                             return true;
                         } // else: comp < 0 : a != 2 AND a </<= 3 -> nop
@@ -989,7 +991,7 @@ public final class OptimizerRules {
                     if (comp != null) {
                         if (comp <= 0) {
                             if (comp == 0 && bc instanceof GreaterThanOrEqual) { // a != 2 AND a >= 2 -> a > 2
-                                bcs.set(i, new GreaterThan(bc.source(), bc.left(), bc.right()));
+                                bcs.set(i, new GreaterThan(bc.source(), bc.left(), bc.right(), bc.zoneId()));
                             } // else: comp < 0 (a != 2 AND a >/>= 3 -> a >/>= 3), or == 0 && bc i.of ">" (a != 2 AND a > 2 -> a > 2)
                             return true;
                         } // else: comp > 0 : a != 2 AND a >/>= 1 -> nop

+ 12 - 6
x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/planner/ExpressionTranslators.java

@@ -52,12 +52,14 @@ import org.elasticsearch.xpack.ql.util.CollectionUtils;
 import org.elasticsearch.xpack.ql.util.Holder;
 
 import java.time.OffsetTime;
+import java.time.ZoneId;
 import java.time.ZonedDateTime;
 import java.time.temporal.TemporalAccessor;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
+import static org.elasticsearch.xpack.ql.type.DataTypes.DATETIME;
 
 public final class ExpressionTranslators {
 
@@ -249,17 +251,21 @@ public final class ExpressionTranslators {
                 isDateLiteralComparison = true;
             }
 
+            ZoneId zoneId = null;
+            if (bc.left().dataType() == DATETIME) {
+                zoneId = bc.zoneId();
+            }
             if (bc instanceof GreaterThan) {
-                return new RangeQuery(source, name, value, false, null, false, format);
+                return new RangeQuery(source, name, value, false, null, false, format, zoneId);
             }
             if (bc instanceof GreaterThanOrEqual) {
-                return new RangeQuery(source, name, value, true, null, false, format);
+                return new RangeQuery(source, name, value, true, null, false, format, zoneId);
             }
             if (bc instanceof LessThan) {
-                return new RangeQuery(source, name, null, false, value, false, format);
+                return new RangeQuery(source, name, null, false, value, false, format, zoneId);
             }
             if (bc instanceof LessThanOrEqual) {
-                return new RangeQuery(source, name, null, false, value, true, format);
+                return new RangeQuery(source, name, null, false, value, true, format, zoneId);
             }
             if (bc instanceof Equals || bc instanceof NullEquals || bc instanceof NotEquals) {
                 if (bc.left() instanceof FieldAttribute) {
@@ -270,7 +276,7 @@ public final class ExpressionTranslators {
                 Query query;
                 if (isDateLiteralComparison) {
                     // dates equality uses a range query because it's the one that has a "format" parameter
-                    query = new RangeQuery(source, name, value, true, value, true, format);
+                    query = new RangeQuery(source, name, value, true, value, true, format, zoneId);
                 } else {
                     query = new TermQuery(source, name, value);
                 }
@@ -324,7 +330,7 @@ public final class ExpressionTranslators {
             }
 
             query = handler.wrapFunctionQuery(r, val, new RangeQuery(r.source(), handler.nameOf(val), lower.get(), r.includeLower(),
-                                                                     upper.get(), r.includeUpper(), format.get()));
+                                                                     upper.get(), r.includeUpper(), format.get(), r.zoneId()));
 
             return query;
         }

+ 17 - 6
x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/querydsl/query/RangeQuery.java

@@ -10,6 +10,7 @@ import org.elasticsearch.index.query.QueryBuilder;
 import org.elasticsearch.index.query.RangeQueryBuilder;
 import org.elasticsearch.xpack.ql.tree.Source;
 
+import java.time.ZoneId;
 import java.util.Objects;
 
 import static org.elasticsearch.index.query.QueryBuilders.rangeQuery;
@@ -20,13 +21,14 @@ public class RangeQuery extends LeafQuery {
     private final Object lower, upper;
     private final boolean includeLower, includeUpper;
     private final String format;
+    private final ZoneId zoneId;
 
-    public RangeQuery(Source source, String field, Object lower, boolean includeLower, Object upper, boolean includeUpper) {
-        this(source, field, lower, includeLower, upper, includeUpper, null);
+    public RangeQuery(Source source, String field, Object lower, boolean includeLower, Object upper, boolean includeUpper, ZoneId zoneId) {
+        this(source, field, lower, includeLower, upper, includeUpper, null, zoneId);
     }
 
     public RangeQuery(Source source, String field, Object lower, boolean includeLower, Object upper,
-            boolean includeUpper, String format) {
+            boolean includeUpper, String format, ZoneId zoneId) {
         super(source);
         this.field = field;
         this.lower = lower;
@@ -34,6 +36,7 @@ public class RangeQuery extends LeafQuery {
         this.includeLower = includeLower;
         this.includeUpper = includeUpper;
         this.format = format;
+        this.zoneId = zoneId;
     }
 
     public String field() {
@@ -60,19 +63,26 @@ public class RangeQuery extends LeafQuery {
         return format;
     }
 
+    public ZoneId zoneId() {
+        return zoneId;
+    }
+
     @Override
     public QueryBuilder asBuilder() {
         RangeQueryBuilder queryBuilder = rangeQuery(field).from(lower, includeLower).to(upper, includeUpper);
         if (Strings.hasText(format)) {
             queryBuilder.format(format);
         }
+        if (zoneId != null) {
+            queryBuilder.timeZone(zoneId.getId());
+        }
 
         return queryBuilder;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(field, lower, upper, includeLower, includeUpper, format);
+        return Objects.hash(field, lower, upper, includeLower, includeUpper, format, zoneId);
     }
 
     @Override
@@ -91,13 +101,14 @@ public class RangeQuery extends LeafQuery {
                 Objects.equals(includeUpper, other.includeUpper) &&
                 Objects.equals(lower, other.lower) &&
                 Objects.equals(upper, other.upper) &&
-                Objects.equals(format, other.format);
+                Objects.equals(format, other.format) &&
+                Objects.equals(zoneId, other.zoneId);
     }
 
     @Override
     protected String innerToString() {
         return field + ":"
             + (includeLower ? "[" : "(") + lower + ", "
-            + upper + (includeUpper ? "]" : ")");
+            + upper + (includeUpper ? "]" : ")") + "@" + zoneId.getId();
     }
 }

+ 42 - 0
x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/TestUtils.java

@@ -6,7 +6,16 @@
 
 package org.elasticsearch.xpack.ql;
 
+import org.elasticsearch.xpack.ql.expression.Expression;
 import org.elasticsearch.xpack.ql.expression.Literal;
+import org.elasticsearch.xpack.ql.expression.predicate.Range;
+import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.Equals;
+import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.GreaterThan;
+import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.GreaterThanOrEqual;
+import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.LessThan;
+import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.LessThanOrEqual;
+import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.NotEquals;
+import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.NullEquals;
 import org.elasticsearch.xpack.ql.session.Configuration;
 import org.elasticsearch.xpack.ql.tree.Source;
 import org.elasticsearch.xpack.ql.type.DataTypes;
@@ -15,6 +24,7 @@ import java.time.ZoneId;
 
 import static org.elasticsearch.test.ESTestCase.randomAlphaOfLength;
 import static org.elasticsearch.test.ESTestCase.randomZone;
+import static org.elasticsearch.xpack.ql.tree.Source.EMPTY;
 
 public final class TestUtils {
 
@@ -49,4 +59,36 @@ public final class TestUtils {
         }
         return new Literal(source, value, DataTypes.fromJava(value));
     }
+
+    public static Equals equalsOf(Expression left, Expression right) {
+        return new Equals(EMPTY, left, right, randomZone());
+    }
+
+    public static NotEquals notEqualsOf(Expression left, Expression right) {
+        return new NotEquals(EMPTY, left, right, randomZone());
+    }
+
+    public static NullEquals nullEqualsOf(Expression left, Expression right) {
+        return new NullEquals(EMPTY, left, right, randomZone());
+    }
+
+    public static LessThan lessThanOf(Expression left, Expression right) {
+        return new LessThan(EMPTY, left, right, randomZone());
+    }
+
+    public static LessThanOrEqual lessThanOrEqualOf(Expression left, Expression right) {
+        return new LessThanOrEqual(EMPTY, left, right, randomZone());
+    }
+
+    public static GreaterThan greaterThanOf(Expression left, Expression right) {
+        return new GreaterThan(EMPTY, left, right, randomZone());
+    }
+
+    public static GreaterThanOrEqual greaterThanOrEqualOf(Expression left, Expression right) {
+        return new GreaterThanOrEqual(EMPTY, left, right, randomZone());
+    }
+
+    public static Range rangeOf(Expression value, Expression lower, boolean includeLower, Expression upper, boolean includeUpper) {
+        return new Range(EMPTY, value, lower, includeLower, upper, includeUpper, randomZone());
+    }
 }

+ 34 - 27
x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/BinaryComparisonProcessorTests.java

@@ -13,6 +13,13 @@ import org.elasticsearch.xpack.ql.expression.Literal;
 import org.elasticsearch.xpack.ql.expression.gen.processor.ConstantProcessor;
 import org.elasticsearch.xpack.ql.expression.processor.Processors;
 
+import static org.elasticsearch.xpack.ql.TestUtils.equalsOf;
+import static org.elasticsearch.xpack.ql.TestUtils.greaterThanOf;
+import static org.elasticsearch.xpack.ql.TestUtils.greaterThanOrEqualOf;
+import static org.elasticsearch.xpack.ql.TestUtils.lessThanOf;
+import static org.elasticsearch.xpack.ql.TestUtils.lessThanOrEqualOf;
+import static org.elasticsearch.xpack.ql.TestUtils.notEqualsOf;
+import static org.elasticsearch.xpack.ql.TestUtils.nullEqualsOf;
 import static org.elasticsearch.xpack.ql.expression.Literal.NULL;
 import static org.elasticsearch.xpack.ql.tree.Source.EMPTY;
 
@@ -40,54 +47,54 @@ public class BinaryComparisonProcessorTests extends AbstractWireSerializingTestC
     }
 
     public void testEq() {
-        assertEquals(true, new Equals(EMPTY, l(4), l(4)).makePipe().asProcessor().process(null));
-        assertEquals(false, new Equals(EMPTY, l(3), l(4)).makePipe().asProcessor().process(null));
+        assertEquals(true, equalsOf(l(4), l(4)).makePipe().asProcessor().process(null));
+        assertEquals(false, equalsOf(l(3), l(4)).makePipe().asProcessor().process(null));
     }
 
     public void testNullEq() {
-        assertEquals(true, new NullEquals(EMPTY, l(4), l(4)).makePipe().asProcessor().process(null));
-        assertEquals(false, new NullEquals(EMPTY, l(3), l(4)).makePipe().asProcessor().process(null));
-        assertEquals(true, new NullEquals(EMPTY, NULL, NULL).makePipe().asProcessor().process(null));
-        assertEquals(false, new NullEquals(EMPTY, l(4), NULL).makePipe().asProcessor().process(null));
-        assertEquals(false, new NullEquals(EMPTY, NULL, l(4)).makePipe().asProcessor().process(null));
+        assertEquals(true, nullEqualsOf(l(4), l(4)).makePipe().asProcessor().process(null));
+        assertEquals(false, nullEqualsOf(l(3), l(4)).makePipe().asProcessor().process(null));
+        assertEquals(true, nullEqualsOf(NULL, NULL).makePipe().asProcessor().process(null));
+        assertEquals(false, nullEqualsOf(l(4), NULL).makePipe().asProcessor().process(null));
+        assertEquals(false, nullEqualsOf(NULL, l(4)).makePipe().asProcessor().process(null));
     }
 
     public void testNEq() {
-        assertEquals(false, new NotEquals(EMPTY, l(4), l(4)).makePipe().asProcessor().process(null));
-        assertEquals(true, new NotEquals(EMPTY, l(3), l(4)).makePipe().asProcessor().process(null));
+        assertEquals(false, notEqualsOf(l(4), l(4)).makePipe().asProcessor().process(null));
+        assertEquals(true, notEqualsOf(l(3), l(4)).makePipe().asProcessor().process(null));
     }
 
     public void testGt() {
-        assertEquals(true, new GreaterThan(EMPTY, l(4), l(3)).makePipe().asProcessor().process(null));
-        assertEquals(false, new GreaterThan(EMPTY, l(3), l(4)).makePipe().asProcessor().process(null));
-        assertEquals(false, new GreaterThan(EMPTY, l(3), l(3)).makePipe().asProcessor().process(null));
+        assertEquals(true, greaterThanOf(l(4), l(3)).makePipe().asProcessor().process(null));
+        assertEquals(false, greaterThanOf(l(3), l(4)).makePipe().asProcessor().process(null));
+        assertEquals(false, greaterThanOf(l(3), l(3)).makePipe().asProcessor().process(null));
     }
 
     public void testGte() {
-        assertEquals(true, new GreaterThanOrEqual(EMPTY, l(4), l(3)).makePipe().asProcessor().process(null));
-        assertEquals(false, new GreaterThanOrEqual(EMPTY, l(3), l(4)).makePipe().asProcessor().process(null));
-        assertEquals(true, new GreaterThanOrEqual(EMPTY, l(3), l(3)).makePipe().asProcessor().process(null));
+        assertEquals(true, greaterThanOrEqualOf(l(4), l(3)).makePipe().asProcessor().process(null));
+        assertEquals(false, greaterThanOrEqualOf(l(3), l(4)).makePipe().asProcessor().process(null));
+        assertEquals(true, greaterThanOrEqualOf(l(3), l(3)).makePipe().asProcessor().process(null));
     }
 
     public void testLt() {
-        assertEquals(false, new LessThan(EMPTY, l(4), l(3)).makePipe().asProcessor().process(null));
-        assertEquals(true, new LessThan(EMPTY, l(3), l(4)).makePipe().asProcessor().process(null));
-        assertEquals(false, new LessThan(EMPTY, l(3), l(3)).makePipe().asProcessor().process(null));
+        assertEquals(false, lessThanOf(l(4), l(3)).makePipe().asProcessor().process(null));
+        assertEquals(true, lessThanOf(l(3), l(4)).makePipe().asProcessor().process(null));
+        assertEquals(false, lessThanOf(l(3), l(3)).makePipe().asProcessor().process(null));
     }
 
     public void testLte() {
-        assertEquals(false, new LessThanOrEqual(EMPTY, l(4), l(3)).makePipe().asProcessor().process(null));
-        assertEquals(true, new LessThanOrEqual(EMPTY, l(3), l(4)).makePipe().asProcessor().process(null));
-        assertEquals(true, new LessThanOrEqual(EMPTY, l(3), l(3)).makePipe().asProcessor().process(null));
+        assertEquals(false, lessThanOrEqualOf(l(4), l(3)).makePipe().asProcessor().process(null));
+        assertEquals(true, lessThanOrEqualOf(l(3), l(4)).makePipe().asProcessor().process(null));
+        assertEquals(true, lessThanOrEqualOf(l(3), l(3)).makePipe().asProcessor().process(null));
     }
     
     public void testHandleNull() {
-        assertNull(new Equals(EMPTY, NULL, l(3)).makePipe().asProcessor().process(null));
-        assertNull(new NotEquals(EMPTY, NULL, l(3)).makePipe().asProcessor().process(null));
-        assertNull(new GreaterThan(EMPTY, NULL, l(3)).makePipe().asProcessor().process(null));
-        assertNull(new GreaterThanOrEqual(EMPTY, NULL, l(3)).makePipe().asProcessor().process(null));
-        assertNull(new LessThan(EMPTY, NULL, l(3)).makePipe().asProcessor().process(null));
-        assertNull(new LessThanOrEqual(EMPTY, NULL, l(3)).makePipe().asProcessor().process(null));
+        assertNull(equalsOf(NULL, l(3)).makePipe().asProcessor().process(null));
+        assertNull(notEqualsOf(NULL, l(3)).makePipe().asProcessor().process(null));
+        assertNull(greaterThanOf(NULL, l(3)).makePipe().asProcessor().process(null));
+        assertNull(greaterThanOrEqualOf(NULL, l(3)).makePipe().asProcessor().process(null));
+        assertNull(lessThanOf(NULL, l(3)).makePipe().asProcessor().process(null));
+        assertNull(lessThanOrEqualOf(NULL, l(3)).makePipe().asProcessor().process(null));
     }
     
     private static Literal l(Object value) {

+ 185 - 175
x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/optimizer/OptimizerRulesTests.java

@@ -44,12 +44,21 @@ import org.elasticsearch.xpack.ql.tree.Source;
 import org.elasticsearch.xpack.ql.type.DataType;
 import org.elasticsearch.xpack.ql.type.EsField;
 
+import java.time.ZoneId;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
 import static java.util.Collections.emptyMap;
+import static org.elasticsearch.xpack.ql.TestUtils.equalsOf;
+import static org.elasticsearch.xpack.ql.TestUtils.greaterThanOf;
+import static org.elasticsearch.xpack.ql.TestUtils.greaterThanOrEqualOf;
+import static org.elasticsearch.xpack.ql.TestUtils.lessThanOf;
+import static org.elasticsearch.xpack.ql.TestUtils.lessThanOrEqualOf;
+import static org.elasticsearch.xpack.ql.TestUtils.notEqualsOf;
+import static org.elasticsearch.xpack.ql.TestUtils.nullEqualsOf;
 import static org.elasticsearch.xpack.ql.TestUtils.of;
+import static org.elasticsearch.xpack.ql.TestUtils.rangeOf;
 import static org.elasticsearch.xpack.ql.expression.Literal.FALSE;
 import static org.elasticsearch.xpack.ql.expression.Literal.NULL;
 import static org.elasticsearch.xpack.ql.expression.Literal.TRUE;
@@ -144,21 +153,21 @@ public class OptimizerRulesTests extends ESTestCase {
     }
 
     public void testConstantFoldingBinaryComparison() {
-        assertEquals(FALSE, new ConstantFolding().rule(new GreaterThan(EMPTY, TWO, THREE)).canonical());
-        assertEquals(FALSE, new ConstantFolding().rule(new GreaterThanOrEqual(EMPTY, TWO, THREE)).canonical());
-        assertEquals(FALSE, new ConstantFolding().rule(new Equals(EMPTY, TWO, THREE)).canonical());
-        assertEquals(FALSE, new ConstantFolding().rule(new NullEquals(EMPTY, TWO, THREE)).canonical());
-        assertEquals(FALSE, new ConstantFolding().rule(new NullEquals(EMPTY, TWO, NULL)).canonical());
-        assertEquals(TRUE, new ConstantFolding().rule(new NotEquals(EMPTY, TWO, THREE)).canonical());
-        assertEquals(TRUE, new ConstantFolding().rule(new LessThanOrEqual(EMPTY, TWO, THREE)).canonical());
-        assertEquals(TRUE, new ConstantFolding().rule(new LessThan(EMPTY, TWO, THREE)).canonical());
+        assertEquals(FALSE, new ConstantFolding().rule(greaterThanOf(TWO, THREE)).canonical());
+        assertEquals(FALSE, new ConstantFolding().rule(greaterThanOrEqualOf(TWO, THREE)).canonical());
+        assertEquals(FALSE, new ConstantFolding().rule(equalsOf(TWO, THREE)).canonical());
+        assertEquals(FALSE, new ConstantFolding().rule(nullEqualsOf(TWO, THREE)).canonical());
+        assertEquals(FALSE, new ConstantFolding().rule(nullEqualsOf(TWO, NULL)).canonical());
+        assertEquals(TRUE, new ConstantFolding().rule(notEqualsOf(TWO, THREE)).canonical());
+        assertEquals(TRUE, new ConstantFolding().rule(lessThanOrEqualOf(TWO, THREE)).canonical());
+        assertEquals(TRUE, new ConstantFolding().rule(lessThanOf(TWO, THREE)).canonical());
     }
 
     public void testConstantFoldingBinaryLogic() {
         assertEquals(FALSE,
-                new ConstantFolding().rule(new And(EMPTY, new GreaterThan(EMPTY, TWO, THREE), TRUE)).canonical());
+                new ConstantFolding().rule(new And(EMPTY, greaterThanOf(TWO, THREE), TRUE)).canonical());
         assertEquals(TRUE,
-                new ConstantFolding().rule(new Or(EMPTY, new GreaterThanOrEqual(EMPTY, TWO, THREE), TRUE)).canonical());
+                new ConstantFolding().rule(new Or(EMPTY, greaterThanOrEqualOf(TWO, THREE), TRUE)).canonical());
     }
 
     public void testConstantFoldingBinaryLogic_WithNullHandling() {
@@ -176,8 +185,8 @@ public class OptimizerRulesTests extends ESTestCase {
     }
 
     public void testConstantFoldingRange() {
-        assertEquals(true, new ConstantFolding().rule(new Range(EMPTY, FIVE, FIVE, true, L(10), false)).fold());
-        assertEquals(false, new ConstantFolding().rule(new Range(EMPTY, FIVE, FIVE, false, L(10), false)).fold());
+        assertEquals(true, new ConstantFolding().rule(rangeOf(FIVE, FIVE, true, L(10), false)).fold());
+        assertEquals(false, new ConstantFolding().rule(rangeOf(FIVE, FIVE, false, L(10), false)).fold());
     }
 
     public void testConstantNot() {
@@ -211,14 +220,14 @@ public class OptimizerRulesTests extends ESTestCase {
 
     public void testLiteralsOnTheRight() {
         Alias a = new Alias(EMPTY, "a", L(10));
-        Expression result = new BooleanLiteralsOnTheRight().rule(new Equals(EMPTY, FIVE, a));
+        Expression result = new BooleanLiteralsOnTheRight().rule(equalsOf(FIVE, a));
         assertTrue(result instanceof Equals);
         Equals eq = (Equals) result;
         assertEquals(a, eq.left());
         assertEquals(FIVE, eq.right());
 
         a = new Alias(EMPTY, "a", L(10));
-        result = new BooleanLiteralsOnTheRight().rule(new NullEquals(EMPTY, FIVE, a));
+        result = new BooleanLiteralsOnTheRight().rule(nullEqualsOf(FIVE, a));
         assertTrue(result instanceof NullEquals);
         NullEquals nullEquals= (NullEquals) result;
         assertEquals(a, nullEquals.left());
@@ -271,7 +280,7 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testFoldExcludingRangeToFalse() {
         FieldAttribute fa = getFieldAttribute();
 
-        Range r = new Range(EMPTY, fa, SIX, false, FIVE, true);
+        Range r = rangeOf(fa, SIX, false, FIVE, true);
         assertTrue(r.foldable());
         assertEquals(Boolean.FALSE, r.fold());
     }
@@ -280,7 +289,7 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testFoldExcludingRangeWithDifferentTypesToFalse() {
         FieldAttribute fa = getFieldAttribute();
 
-        Range r = new Range(EMPTY, fa, SIX, false, L(5.5d), true);
+        Range r = rangeOf(fa, SIX, false, L(5.5d), true);
         assertTrue(r.foldable());
         assertEquals(Boolean.FALSE, r.fold());
     }
@@ -289,8 +298,8 @@ public class OptimizerRulesTests extends ESTestCase {
 
     public void testCombineBinaryComparisonsNotComparable() {
         FieldAttribute fa = getFieldAttribute();
-        LessThanOrEqual lte = new LessThanOrEqual(EMPTY, fa, SIX);
-        LessThan lt = new LessThan(EMPTY, fa, FALSE);
+        LessThanOrEqual lte = lessThanOrEqualOf(fa, SIX);
+        LessThan lt = lessThanOf(fa, FALSE);
 
         CombineBinaryComparisons rule = new CombineBinaryComparisons();
         And and = new And(EMPTY, lte, lt);
@@ -301,8 +310,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // a <= 6 AND a < 5  -> a < 5
     public void testCombineBinaryComparisonsUpper() {
         FieldAttribute fa = getFieldAttribute();
-        LessThanOrEqual lte = new LessThanOrEqual(EMPTY, fa, SIX);
-        LessThan lt = new LessThan(EMPTY, fa, FIVE);
+        LessThanOrEqual lte = lessThanOrEqualOf(fa, SIX);
+        LessThan lt = lessThanOf(fa, FIVE);
 
         CombineBinaryComparisons rule = new CombineBinaryComparisons();
 
@@ -315,8 +324,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // 6 <= a AND 5 < a  -> 6 <= a
     public void testCombineBinaryComparisonsLower() {
         FieldAttribute fa = getFieldAttribute();
-        GreaterThanOrEqual gte = new GreaterThanOrEqual(EMPTY, fa, SIX);
-        GreaterThan gt = new GreaterThan(EMPTY, fa, FIVE);
+        GreaterThanOrEqual gte = greaterThanOrEqualOf(fa, SIX);
+        GreaterThan gt = greaterThanOf(fa, FIVE);
 
         CombineBinaryComparisons rule = new CombineBinaryComparisons();
 
@@ -329,8 +338,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // 5 <= a AND 5 < a  -> 5 < a
     public void testCombineBinaryComparisonsInclude() {
         FieldAttribute fa = getFieldAttribute();
-        GreaterThanOrEqual gte = new GreaterThanOrEqual(EMPTY, fa, FIVE);
-        GreaterThan gt = new GreaterThan(EMPTY, fa, FIVE);
+        GreaterThanOrEqual gte = greaterThanOrEqualOf(fa, FIVE);
+        GreaterThan gt = greaterThanOf(fa, FIVE);
 
         CombineBinaryComparisons rule = new CombineBinaryComparisons();
 
@@ -344,8 +353,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsAndRangeLower() {
         FieldAttribute fa = getFieldAttribute();
 
-        GreaterThan gt = new GreaterThan(EMPTY, fa, TWO);
-        Range range = new Range(EMPTY, fa, TWO, true, THREE, false);
+        GreaterThan gt = greaterThanOf(fa, TWO);
+        Range range = rangeOf(fa, TWO, true, THREE, false);
 
         CombineBinaryComparisons rule = new CombineBinaryComparisons();
         Expression exp = rule.rule(new And(EMPTY, gt, range));
@@ -361,8 +370,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsAndRangeUpper() {
         FieldAttribute fa = getFieldAttribute();
 
-        LessThan lt = new LessThan(EMPTY, fa, FOUR);
-        Range range = new Range(EMPTY, fa, ONE, false, THREE, false);
+        LessThan lt = lessThanOf(fa, FOUR);
+        Range range = rangeOf(fa, ONE, false, THREE, false);
 
         CombineBinaryComparisons rule = new CombineBinaryComparisons();
         Expression exp = rule.rule(new And(EMPTY, range, lt));
@@ -378,8 +387,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsAndRangeUpperEqual() {
         FieldAttribute fa = getFieldAttribute();
 
-        LessThanOrEqual lte = new LessThanOrEqual(EMPTY, fa, TWO);
-        Range range = new Range(EMPTY, fa, ONE, false, THREE, false);
+        LessThanOrEqual lte = lessThanOrEqualOf(fa, TWO);
+        Range range = rangeOf(fa, ONE, false, THREE, false);
 
         CombineBinaryComparisons rule = new CombineBinaryComparisons();
         Expression exp = rule.rule(new And(EMPTY, lte, range));
@@ -394,10 +403,10 @@ public class OptimizerRulesTests extends ESTestCase {
     // 3 <= a AND 4 < a AND a <= 7 AND a < 6 -> 4 < a < 6
     public void testCombineMultipleBinaryComparisons() {
         FieldAttribute fa = getFieldAttribute();
-        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, SIX);
+        GreaterThanOrEqual gte = greaterThanOrEqualOf(fa, THREE);
+        GreaterThan gt = greaterThanOf(fa, FOUR);
+        LessThanOrEqual lte = lessThanOrEqualOf(fa, L(7));
+        LessThan lt = lessThanOf(fa, SIX);
 
         CombineBinaryComparisons rule = new CombineBinaryComparisons();
 
@@ -413,10 +422,10 @@ public class OptimizerRulesTests extends ESTestCase {
     // 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 = getFieldAttribute();
-        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, FIVE));
+        GreaterThanOrEqual gte = greaterThanOrEqualOf(fa, THREE);
+        GreaterThan gt = greaterThanOf(fa, FOUR);
+        LessThanOrEqual lte = lessThanOrEqualOf(fa, L(7));
+        Expression ne = new Not(EMPTY, equalsOf(fa, FIVE));
 
         CombineBinaryComparisons rule = new CombineBinaryComparisons();
 
@@ -435,8 +444,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // 1 <= a AND a < 5  -> 1 <= a < 5
     public void testCombineComparisonsIntoRange() {
         FieldAttribute fa = getFieldAttribute();
-        GreaterThanOrEqual gte = new GreaterThanOrEqual(EMPTY, fa, ONE);
-        LessThan lt = new LessThan(EMPTY, fa, FIVE);
+        GreaterThanOrEqual gte = greaterThanOrEqualOf(fa, ONE);
+        LessThan lt = lessThanOf(fa, FIVE);
 
         CombineBinaryComparisons rule = new CombineBinaryComparisons();
         Expression exp = rule.rule(new And(EMPTY, gte, lt));
@@ -455,19 +464,20 @@ public class OptimizerRulesTests extends ESTestCase {
         FieldAttribute fb = getFieldAttribute("b");
         FieldAttribute fc = getFieldAttribute("c");
 
-        GreaterThan agt1 = new GreaterThan(EMPTY, fa, ONE);
-        LessThan alt3 = new LessThan(EMPTY, fa, THREE);
-        GreaterThan bgt2 = new GreaterThan(EMPTY, fb, TWO);
-        LessThan blt4 = new LessThan(EMPTY, fb, FOUR);
-        LessThan clt4 = new LessThan(EMPTY, fc, FOUR);
+        ZoneId zoneId = randomZone();
+        GreaterThan agt1 = new GreaterThan(EMPTY, fa, ONE, zoneId);
+        LessThan alt3 = new LessThan(EMPTY, fa, THREE, zoneId);
+        GreaterThan bgt2 = new GreaterThan(EMPTY, fb, TWO, zoneId);
+        LessThan blt4 = new LessThan(EMPTY, fb, FOUR, zoneId);
+        LessThan clt4 = new LessThan(EMPTY, fc, FOUR, zoneId);
 
         Expression inputAnd = Predicates.combineAnd(Arrays.asList(agt1, alt3, bgt2, blt4, clt4));
 
         CombineBinaryComparisons rule = new CombineBinaryComparisons();
         Expression outputAnd = rule.rule(inputAnd);
 
-        Range agt1lt3 = new Range(EMPTY, fa, ONE, false, THREE, false);
-        Range bgt2lt4 = new Range(EMPTY, fb, TWO, false, FOUR, false);
+        Range agt1lt3 = new Range(EMPTY, fa, ONE, false, THREE, false, zoneId);
+        Range bgt2lt4 = new Range(EMPTY, fb, TWO, false, FOUR, false, zoneId);
 
         // The actual outcome is (c < 4) AND (1 < a < 3) AND (2 < b < 4), due to the way the Expression types are combined in the Optimizer
         Expression expectedAnd = Predicates.combineAnd(Arrays.asList(clt4, agt1lt3, bgt2lt4));
@@ -479,8 +489,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsConjunctionOfIncludedRange() {
         FieldAttribute fa = getFieldAttribute();
 
-        Range r1 = new Range(EMPTY, fa, TWO, false, THREE, false);
-        Range r2 = new Range(EMPTY, fa, ONE, false, FOUR, false);
+        Range r1 = rangeOf(fa, TWO, false, THREE, false);
+        Range r2 = rangeOf(fa, ONE, false, FOUR, false);
 
         And and = new And(EMPTY, r1, r2);
 
@@ -493,8 +503,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsConjunctionOfNonOverlappingBoundaries() {
         FieldAttribute fa = getFieldAttribute();
 
-        Range r1 = new Range(EMPTY, fa, TWO, false, THREE, false);
-        Range r2 = new Range(EMPTY, fa, ONE, false, TWO, false);
+        Range r1 = rangeOf(fa, TWO, false, THREE, false);
+        Range r2 = rangeOf(fa, ONE, false, TWO, false);
 
         And and = new And(EMPTY, r1, r2);
 
@@ -513,8 +523,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsConjunctionOfUpperEqualsOverlappingBoundaries() {
         FieldAttribute fa = getFieldAttribute();
 
-        Range r1 = new Range(EMPTY, fa, TWO, false, THREE, false);
-        Range r2 = new Range(EMPTY, fa, TWO, false, THREE, true);
+        Range r1 = rangeOf(fa, TWO, false, THREE, false);
+        Range r2 = rangeOf(fa, TWO, false, THREE, true);
 
         And and = new And(EMPTY, r1, r2);
 
@@ -527,8 +537,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsConjunctionOverlappingUpperBoundary() {
         FieldAttribute fa = getFieldAttribute();
 
-        Range r2 = new Range(EMPTY, fa, TWO, false, THREE, false);
-        Range r1 = new Range(EMPTY, fa, ONE, false, THREE, false);
+        Range r2 = rangeOf(fa, TWO, false, THREE, false);
+        Range r1 = rangeOf(fa, ONE, false, THREE, false);
 
         And and = new And(EMPTY, r1, r2);
 
@@ -541,8 +551,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsConjunctionWithDifferentUpperLimitInclusion() {
         FieldAttribute fa = getFieldAttribute();
 
-        Range r1 = new Range(EMPTY, fa, ONE, false, THREE, false);
-        Range r2 = new Range(EMPTY, fa, TWO, false, THREE, true);
+        Range r1 = rangeOf(fa, ONE, false, THREE, false);
+        Range r2 = rangeOf(fa, TWO, false, THREE, true);
 
         And and = new And(EMPTY, r1, r2);
 
@@ -560,8 +570,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testRangesOverlappingConjunctionNoLowerBoundary() {
         FieldAttribute fa = getFieldAttribute();
 
-        Range r1 = new Range(EMPTY, fa, L(0), false, ONE, true);
-        Range r2 = new Range(EMPTY, fa, L(0), true, TWO, false);
+        Range r1 = rangeOf(fa, L(0), false, ONE, true);
+        Range r2 = rangeOf(fa, L(0), true, TWO, false);
 
         And and = new And(EMPTY, r1, r2);
 
@@ -574,8 +584,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsConjunction_Neq2AndRangeGt3Lt5() {
         FieldAttribute fa = getFieldAttribute();
 
-        NotEquals neq = new NotEquals(EMPTY, fa, TWO);
-        Range range = new Range(EMPTY, fa, THREE, false, FIVE, false);
+        NotEquals neq = notEqualsOf(fa, TWO);
+        Range range = rangeOf(fa, THREE, false, FIVE, false);
         And and = new And(EMPTY, range, neq);
 
         CombineBinaryComparisons rule = new CombineBinaryComparisons();
@@ -592,8 +602,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsConjunction_Neq2AndRangeGt0Lt1() {
         FieldAttribute fa = getFieldAttribute();
 
-        NotEquals neq = new NotEquals(EMPTY, fa, TWO);
-        Range range = new Range(EMPTY, fa, L(0), false, ONE, false);
+        NotEquals neq = notEqualsOf(fa, TWO);
+        Range range = rangeOf(fa, L(0), false, ONE, false);
         And and = new And(EMPTY, neq, range);
 
         CombineBinaryComparisons rule = new CombineBinaryComparisons();
@@ -610,8 +620,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsConjunction_Neq2AndRangeGte2Lt3() {
         FieldAttribute fa = getFieldAttribute();
 
-        NotEquals neq = new NotEquals(EMPTY, fa, TWO);
-        Range range = new Range(EMPTY, fa, TWO, true, THREE, false);
+        NotEquals neq = notEqualsOf(fa, TWO);
+        Range range = rangeOf(fa, TWO, true, THREE, false);
         And and = new And(EMPTY, neq, range);
 
         CombineBinaryComparisons rule = new CombineBinaryComparisons();
@@ -628,8 +638,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsConjunction_Neq3AndRangeGt2Lte3() {
         FieldAttribute fa = getFieldAttribute();
 
-        NotEquals neq = new NotEquals(EMPTY, fa, THREE);
-        Range range = new Range(EMPTY, fa, TWO, false, THREE, true);
+        NotEquals neq = notEqualsOf(fa, THREE);
+        Range range = rangeOf(fa, TWO, false, THREE, true);
         And and = new And(EMPTY, neq, range);
 
         CombineBinaryComparisons rule = new CombineBinaryComparisons();
@@ -646,8 +656,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsConjunction_Neq2AndRangeGt1Lt3() {
         FieldAttribute fa = getFieldAttribute();
 
-        NotEquals neq = new NotEquals(EMPTY, fa, TWO);
-        Range range = new Range(EMPTY, fa, ONE, false, THREE, false);
+        NotEquals neq = notEqualsOf(fa, TWO);
+        Range range = rangeOf(fa, ONE, false, THREE, false);
         And and = new And(EMPTY, neq, range);
 
         CombineBinaryComparisons rule = new CombineBinaryComparisons();
@@ -659,8 +669,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsConjunction_Neq2AndGt3() {
         FieldAttribute fa = getFieldAttribute();
 
-        NotEquals neq = new NotEquals(EMPTY, fa, TWO);
-        GreaterThan gt = new GreaterThan(EMPTY, fa, THREE);
+        NotEquals neq = notEqualsOf(fa, TWO);
+        GreaterThan gt = greaterThanOf(fa, THREE);
         And and = new And(EMPTY, neq, gt);
 
         CombineBinaryComparisons rule = new CombineBinaryComparisons();
@@ -672,8 +682,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsConjunction_Neq2AndGte2() {
         FieldAttribute fa = getFieldAttribute();
 
-        NotEquals neq = new NotEquals(EMPTY, fa, TWO);
-        GreaterThanOrEqual gte = new GreaterThanOrEqual(EMPTY, fa, TWO);
+        NotEquals neq = notEqualsOf(fa, TWO);
+        GreaterThanOrEqual gte = greaterThanOrEqualOf(fa, TWO);
         And and = new And(EMPTY, neq, gte);
 
         CombineBinaryComparisons rule = new CombineBinaryComparisons();
@@ -687,8 +697,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsConjunction_Neq2AndGte1() {
         FieldAttribute fa = getFieldAttribute();
 
-        NotEquals neq = new NotEquals(EMPTY, fa, TWO);
-        GreaterThanOrEqual gte = new GreaterThanOrEqual(EMPTY, fa, ONE);
+        NotEquals neq = notEqualsOf(fa, TWO);
+        GreaterThanOrEqual gte = greaterThanOrEqualOf(fa, ONE);
         And and = new And(EMPTY, neq, gte);
 
         CombineBinaryComparisons rule = new CombineBinaryComparisons();
@@ -700,8 +710,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsConjunction_Neq2AndLte3() {
         FieldAttribute fa = getFieldAttribute();
 
-        NotEquals neq = new NotEquals(EMPTY, fa, TWO);
-        LessThanOrEqual lte = new LessThanOrEqual(EMPTY, fa, THREE);
+        NotEquals neq = notEqualsOf(fa, TWO);
+        LessThanOrEqual lte = lessThanOrEqualOf(fa, THREE);
         And and = new And(EMPTY, neq, lte);
 
         CombineBinaryComparisons rule = new CombineBinaryComparisons();
@@ -713,8 +723,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsConjunction_Neq2AndLte2() {
         FieldAttribute fa = getFieldAttribute();
 
-        NotEquals neq = new NotEquals(EMPTY, fa, TWO);
-        LessThanOrEqual lte = new LessThanOrEqual(EMPTY, fa, TWO);
+        NotEquals neq = notEqualsOf(fa, TWO);
+        LessThanOrEqual lte = lessThanOrEqualOf(fa, TWO);
         And and = new And(EMPTY, neq, lte);
 
         CombineBinaryComparisons rule = new CombineBinaryComparisons();
@@ -728,8 +738,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsConjunction_Neq2AndLte1() {
         FieldAttribute fa = getFieldAttribute();
 
-        NotEquals neq = new NotEquals(EMPTY, fa, TWO);
-        LessThanOrEqual lte = new LessThanOrEqual(EMPTY, fa, ONE);
+        NotEquals neq = notEqualsOf(fa, TWO);
+        LessThanOrEqual lte = lessThanOrEqualOf(fa, ONE);
         And and = new And(EMPTY, neq, lte);
 
         CombineBinaryComparisons rule = new CombineBinaryComparisons();
@@ -742,8 +752,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsDisjunctionNotComparable() {
         FieldAttribute fa = getFieldAttribute();
 
-        GreaterThan gt1 = new GreaterThan(EMPTY, fa, ONE);
-        GreaterThan gt2 = new GreaterThan(EMPTY, fa, FALSE);
+        GreaterThan gt1 = greaterThanOf(fa, ONE);
+        GreaterThan gt2 = greaterThanOf(fa, FALSE);
 
         Or or = new Or(EMPTY, gt1, gt2);
 
@@ -757,9 +767,9 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsDisjunctionLowerBound() {
         FieldAttribute fa = getFieldAttribute();
 
-        GreaterThan gt1 = new GreaterThan(EMPTY, fa, ONE);
-        GreaterThan gt2 = new GreaterThan(EMPTY, fa, TWO);
-        GreaterThan gt3 = new GreaterThan(EMPTY, fa, THREE);
+        GreaterThan gt1 = greaterThanOf(fa, ONE);
+        GreaterThan gt2 = greaterThanOf(fa, TWO);
+        GreaterThan gt3 = greaterThanOf(fa, THREE);
 
         Or or = new Or(EMPTY, gt1, new Or(EMPTY, gt2, gt3));
 
@@ -775,9 +785,9 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsDisjunctionIncludeLowerBounds() {
         FieldAttribute fa = getFieldAttribute();
 
-        GreaterThan gt1 = new GreaterThan(EMPTY, fa, ONE);
-        GreaterThan gt2 = new GreaterThan(EMPTY, fa, TWO);
-        GreaterThanOrEqual gte3 = new GreaterThanOrEqual(EMPTY, fa, THREE);
+        GreaterThan gt1 = greaterThanOf(fa, ONE);
+        GreaterThan gt2 = greaterThanOf(fa, TWO);
+        GreaterThanOrEqual gte3 = greaterThanOrEqualOf(fa, THREE);
 
         Or or = new Or(EMPTY, new Or(EMPTY, gt1, gt2), gte3);
 
@@ -793,9 +803,9 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsDisjunctionUpperBound() {
         FieldAttribute fa = getFieldAttribute();
 
-        LessThan lt1 = new LessThan(EMPTY, fa, ONE);
-        LessThan lt2 = new LessThan(EMPTY, fa, TWO);
-        LessThan lt3 = new LessThan(EMPTY, fa, THREE);
+        LessThan lt1 = lessThanOf(fa, ONE);
+        LessThan lt2 = lessThanOf(fa, TWO);
+        LessThan lt3 = lessThanOf(fa, THREE);
 
         Or or = new Or(EMPTY, new Or(EMPTY, lt1, lt2), lt3);
 
@@ -811,9 +821,9 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsDisjunctionIncludeUpperBounds() {
         FieldAttribute fa = getFieldAttribute();
 
-        LessThan lt1 = new LessThan(EMPTY, fa, ONE);
-        LessThan lt2 = new LessThan(EMPTY, fa, TWO);
-        LessThanOrEqual lte2 = new LessThanOrEqual(EMPTY, fa, TWO);
+        LessThan lt1 = lessThanOf(fa, ONE);
+        LessThan lt2 = lessThanOf(fa, TWO);
+        LessThanOrEqual lte2 = lessThanOrEqualOf(fa, TWO);
 
         Or or = new Or(EMPTY, lt2, new Or(EMPTY, lte2, lt1));
 
@@ -829,11 +839,11 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsDisjunctionOfLowerAndUpperBounds() {
         FieldAttribute fa = getFieldAttribute();
 
-        LessThan lt1 = new LessThan(EMPTY, fa, ONE);
-        LessThan lt2 = new LessThan(EMPTY, fa, TWO);
+        LessThan lt1 = lessThanOf(fa, ONE);
+        LessThan lt2 = lessThanOf(fa, TWO);
 
-        GreaterThan gt3 = new GreaterThan(EMPTY, fa, THREE);
-        GreaterThan gt4 = new GreaterThan(EMPTY, fa, FOUR);
+        GreaterThan gt3 = greaterThanOf(fa, THREE);
+        GreaterThan gt4 = greaterThanOf(fa, FOUR);
 
         Or or = new Or(EMPTY, new Or(EMPTY, lt2, gt3), new Or(EMPTY, lt1, gt4));
 
@@ -855,8 +865,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsDisjunctionOfIncludedRangeNotComparable() {
         FieldAttribute fa = getFieldAttribute();
 
-        Range r1 = new Range(EMPTY, fa, TWO, false, THREE, false);
-        Range r2 = new Range(EMPTY, fa, ONE, false, FALSE, false);
+        Range r1 = rangeOf(fa, TWO, false, THREE, false);
+        Range r2 = rangeOf(fa, ONE, false, FALSE, false);
 
         Or or = new Or(EMPTY, r1, r2);
 
@@ -870,8 +880,8 @@ public class OptimizerRulesTests extends ESTestCase {
         FieldAttribute fa = getFieldAttribute();
 
 
-        Range r1 = new Range(EMPTY, fa, TWO, false, THREE, false);
-        Range r2 = new Range(EMPTY, fa, ONE, false, FOUR, false);
+        Range r1 = rangeOf(fa, TWO, false, THREE, false);
+        Range r2 = rangeOf(fa, ONE, false, FOUR, false);
 
         Or or = new Or(EMPTY, r1, r2);
 
@@ -890,8 +900,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsDisjunctionOfNonOverlappingBoundaries() {
         FieldAttribute fa = getFieldAttribute();
 
-        Range r1 = new Range(EMPTY, fa, TWO, false, THREE, false);
-        Range r2 = new Range(EMPTY, fa, ONE, false, TWO, false);
+        Range r1 = rangeOf(fa, TWO, false, THREE, false);
+        Range r2 = rangeOf(fa, ONE, false, TWO, false);
 
         Or or = new Or(EMPTY, r1, r2);
 
@@ -904,8 +914,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsDisjunctionOfUpperEqualsOverlappingBoundaries() {
         FieldAttribute fa = getFieldAttribute();
 
-        Range r1 = new Range(EMPTY, fa, TWO, false, THREE, false);
-        Range r2 = new Range(EMPTY, fa, TWO, false, THREE, true);
+        Range r1 = rangeOf(fa, TWO, false, THREE, false);
+        Range r2 = rangeOf(fa, TWO, false, THREE, true);
 
         Or or = new Or(EMPTY, r1, r2);
 
@@ -918,8 +928,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsOverlappingUpperBoundary() {
         FieldAttribute fa = getFieldAttribute();
 
-        Range r2 = new Range(EMPTY, fa, TWO, false, THREE, false);
-        Range r1 = new Range(EMPTY, fa, ONE, false, THREE, false);
+        Range r2 = rangeOf(fa, TWO, false, THREE, false);
+        Range r1 = rangeOf(fa, ONE, false, THREE, false);
 
         Or or = new Or(EMPTY, r1, r2);
 
@@ -932,8 +942,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testCombineBinaryComparisonsWithDifferentUpperLimitInclusion() {
         FieldAttribute fa = getFieldAttribute();
 
-        Range r1 = new Range(EMPTY, fa, ONE, false, THREE, false);
-        Range r2 = new Range(EMPTY, fa, TWO, false, THREE, true);
+        Range r1 = rangeOf(fa, ONE, false, THREE, false);
+        Range r2 = rangeOf(fa, TWO, false, THREE, true);
 
         Or or = new Or(EMPTY, r1, r2);
 
@@ -948,9 +958,9 @@ public class OptimizerRulesTests extends ESTestCase {
         FieldAttribute fb = getFieldAttribute("b");
         FieldAttribute fc = getFieldAttribute("c");
 
-        Expression a1 = new Equals(EMPTY, fa, ONE);
-        Expression a2 = new Equals(EMPTY, fa, TWO);
-        And common = new And(EMPTY, new Equals(EMPTY, fb, THREE), new Equals(EMPTY, fc, FOUR));
+        Expression a1 = equalsOf(fa, ONE);
+        Expression a2 = equalsOf(fa, TWO);
+        And common = new And(EMPTY, equalsOf(fb, THREE), equalsOf(fc, FOUR));
         And left = new And(EMPTY, a1, common);
         And right = new And(EMPTY, a2, common);
         Or or = new Or(EMPTY, left, right);
@@ -963,8 +973,8 @@ public class OptimizerRulesTests extends ESTestCase {
     public void testRangesOverlappingNoLowerBoundary() {
         FieldAttribute fa = getFieldAttribute();
 
-        Range r2 = new Range(EMPTY, fa, L(0), false, TWO, false);
-        Range r1 = new Range(EMPTY, fa, L(0), false, ONE, true);
+        Range r2 = rangeOf(fa, L(0), false, TWO, false);
+        Range r1 = rangeOf(fa, L(0), false, ONE, true);
 
         Or or = new Or(EMPTY, r1, r2);
 
@@ -979,8 +989,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // 1 <= a < 10 AND a == 1 -> a == 1
     public void testEliminateRangeByEqualsInInterval() {
         FieldAttribute fa = getFieldAttribute();
-        Equals eq1 = new Equals(EMPTY, fa, ONE);
-        Range r = new Range(EMPTY, fa, ONE, true, L(10), false);
+        Equals eq1 = equalsOf(fa, ONE);
+        Range r = rangeOf(fa, ONE, true, L(10), false);
 
         PropagateEquals rule = new PropagateEquals();
         Expression exp = rule.rule(new And(EMPTY, eq1, r));
@@ -990,8 +1000,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // 1 <= a < 10 AND a <=> 1 -> a <=> 1
     public void testEliminateRangeByNullEqualsInInterval() {
         FieldAttribute fa = getFieldAttribute();
-        NullEquals eq1 = new NullEquals(EMPTY, fa, ONE);
-        Range r = new Range(EMPTY, fa, ONE, true, L(10), false);
+        NullEquals eq1 = nullEqualsOf(fa, ONE);
+        Range r = rangeOf(fa, ONE, true, L(10), false);
 
         PropagateEquals rule = new PropagateEquals();
         Expression exp = rule.rule(new And(EMPTY, eq1, r));
@@ -1006,8 +1016,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // a == 1 AND a == 2 -> FALSE
     public void testDualEqualsConjunction() {
         FieldAttribute fa = getFieldAttribute();
-        Equals eq1 = new Equals(EMPTY, fa, ONE);
-        Equals eq2 = new Equals(EMPTY, fa, TWO);
+        Equals eq1 = equalsOf(fa, ONE);
+        Equals eq2 = equalsOf(fa, TWO);
 
         PropagateEquals rule = new PropagateEquals();
         Expression exp = rule.rule(new And(EMPTY, eq1, eq2));
@@ -1017,8 +1027,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // a <=> 1 AND a <=> 2 -> FALSE
     public void testDualNullEqualsConjunction() {
         FieldAttribute fa = getFieldAttribute();
-        NullEquals eq1 = new NullEquals(EMPTY, fa, ONE);
-        NullEquals eq2 = new NullEquals(EMPTY, fa, TWO);
+        NullEquals eq1 = nullEqualsOf(fa, ONE);
+        NullEquals eq2 = nullEqualsOf(fa, TWO);
 
         PropagateEquals rule = new PropagateEquals();
         Expression exp = rule.rule(new And(EMPTY, eq1, eq2));
@@ -1028,8 +1038,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // 1 < a < 10 AND a == 10 -> FALSE
     public void testEliminateRangeByEqualsOutsideInterval() {
         FieldAttribute fa = getFieldAttribute();
-        Equals eq1 = new Equals(EMPTY, fa, L(10));
-        Range r = new Range(EMPTY, fa, ONE, false, L(10), false);
+        Equals eq1 = equalsOf(fa, L(10));
+        Range r = rangeOf(fa, ONE, false, L(10), false);
 
         PropagateEquals rule = new PropagateEquals();
         Expression exp = rule.rule(new And(EMPTY, eq1, r));
@@ -1039,8 +1049,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // 1 < a < 10 AND a <=> 10 -> FALSE
     public void testEliminateRangeByNullEqualsOutsideInterval() {
         FieldAttribute fa = getFieldAttribute();
-        NullEquals eq1 = new NullEquals(EMPTY, fa, L(10));
-        Range r = new Range(EMPTY, fa, ONE, false, L(10), false);
+        NullEquals eq1 = nullEqualsOf(fa, L(10));
+        Range r = rangeOf(fa, ONE, false, L(10), false);
 
         PropagateEquals rule = new PropagateEquals();
         Expression exp = rule.rule(new And(EMPTY, eq1, r));
@@ -1050,8 +1060,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // a != 3 AND a = 3 -> FALSE
     public void testPropagateEquals_VarNeq3AndVarEq3() {
         FieldAttribute fa = getFieldAttribute();
-        NotEquals neq = new NotEquals(EMPTY, fa, THREE);
-        Equals eq = new Equals(EMPTY, fa, THREE);
+        NotEquals neq = notEqualsOf(fa, THREE);
+        Equals eq = equalsOf(fa, THREE);
 
         PropagateEquals rule = new PropagateEquals();
         Expression exp = rule.rule(new And(EMPTY, neq, eq));
@@ -1061,8 +1071,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // a != 4 AND a = 3 -> a = 3
     public void testPropagateEquals_VarNeq4AndVarEq3() {
         FieldAttribute fa = getFieldAttribute();
-        NotEquals neq = new NotEquals(EMPTY, fa, FOUR);
-        Equals eq = new Equals(EMPTY, fa, THREE);
+        NotEquals neq = notEqualsOf(fa, FOUR);
+        Equals eq = equalsOf(fa, THREE);
 
         PropagateEquals rule = new PropagateEquals();
         Expression exp = rule.rule(new And(EMPTY, neq, eq));
@@ -1073,8 +1083,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // a = 2 AND a < 2 -> FALSE
     public void testPropagateEquals_VarEq2AndVarLt2() {
         FieldAttribute fa = getFieldAttribute();
-        Equals eq = new Equals(EMPTY, fa, TWO);
-        LessThan lt = new LessThan(EMPTY, fa, TWO);
+        Equals eq = equalsOf(fa, TWO);
+        LessThan lt = lessThanOf(fa, TWO);
 
         PropagateEquals rule = new PropagateEquals();
         Expression exp = rule.rule(new And(EMPTY, eq, lt));
@@ -1084,8 +1094,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // a = 2 AND a <= 2 -> a = 2
     public void testPropagateEquals_VarEq2AndVarLte2() {
         FieldAttribute fa = getFieldAttribute();
-        Equals eq = new Equals(EMPTY, fa, TWO);
-        LessThanOrEqual lt = new LessThanOrEqual(EMPTY, fa, TWO);
+        Equals eq = equalsOf(fa, TWO);
+        LessThanOrEqual lt = lessThanOrEqualOf(fa, TWO);
 
         PropagateEquals rule = new PropagateEquals();
         Expression exp = rule.rule(new And(EMPTY, eq, lt));
@@ -1095,8 +1105,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // a = 2 AND a <= 1 -> FALSE
     public void testPropagateEquals_VarEq2AndVarLte1() {
         FieldAttribute fa = getFieldAttribute();
-        Equals eq = new Equals(EMPTY, fa, TWO);
-        LessThanOrEqual lt = new LessThanOrEqual(EMPTY, fa, ONE);
+        Equals eq = equalsOf(fa, TWO);
+        LessThanOrEqual lt = lessThanOrEqualOf(fa, ONE);
 
         PropagateEquals rule = new PropagateEquals();
         Expression exp = rule.rule(new And(EMPTY, eq, lt));
@@ -1106,8 +1116,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // a = 2 AND a > 2 -> FALSE
     public void testPropagateEquals_VarEq2AndVarGt2() {
         FieldAttribute fa = getFieldAttribute();
-        Equals eq = new Equals(EMPTY, fa, TWO);
-        GreaterThan gt = new GreaterThan(EMPTY, fa, TWO);
+        Equals eq = equalsOf(fa, TWO);
+        GreaterThan gt = greaterThanOf(fa, TWO);
 
         PropagateEquals rule = new PropagateEquals();
         Expression exp = rule.rule(new And(EMPTY, eq, gt));
@@ -1117,8 +1127,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // a = 2 AND a >= 2 -> a = 2
     public void testPropagateEquals_VarEq2AndVarGte2() {
         FieldAttribute fa = getFieldAttribute();
-        Equals eq = new Equals(EMPTY, fa, TWO);
-        GreaterThanOrEqual gte = new GreaterThanOrEqual(EMPTY, fa, TWO);
+        Equals eq = equalsOf(fa, TWO);
+        GreaterThanOrEqual gte = greaterThanOrEqualOf(fa, TWO);
 
         PropagateEquals rule = new PropagateEquals();
         Expression exp = rule.rule(new And(EMPTY, eq, gte));
@@ -1128,8 +1138,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // a = 2 AND a > 3 -> FALSE
     public void testPropagateEquals_VarEq2AndVarLt3() {
         FieldAttribute fa = getFieldAttribute();
-        Equals eq = new Equals(EMPTY, fa, TWO);
-        GreaterThan gt = new GreaterThan(EMPTY, fa, THREE);
+        Equals eq = equalsOf(fa, TWO);
+        GreaterThan gt = greaterThanOf(fa, THREE);
 
         PropagateEquals rule = new PropagateEquals();
         Expression exp = rule.rule(new And(EMPTY, eq, gt));
@@ -1139,10 +1149,10 @@ public class OptimizerRulesTests extends ESTestCase {
     // a = 2 AND a < 3 AND a > 1 AND a != 4 -> a = 2
     public void testPropagateEquals_VarEq2AndVarLt3AndVarGt1AndVarNeq4() {
         FieldAttribute fa = getFieldAttribute();
-        Equals eq = new Equals(EMPTY, fa, TWO);
-        LessThan lt = new LessThan(EMPTY, fa, THREE);
-        GreaterThan gt = new GreaterThan(EMPTY, fa, ONE);
-        NotEquals neq = new NotEquals(EMPTY, fa, FOUR);
+        Equals eq = equalsOf(fa, TWO);
+        LessThan lt = lessThanOf(fa, THREE);
+        GreaterThan gt = greaterThanOf(fa, ONE);
+        NotEquals neq = notEqualsOf(fa, FOUR);
 
         PropagateEquals rule = new PropagateEquals();
         Expression and = Predicates.combineAnd(Arrays.asList(eq, lt, gt, neq));
@@ -1153,10 +1163,10 @@ public class OptimizerRulesTests extends ESTestCase {
     // a = 2 AND 1 < a < 3 AND a > 0 AND a != 4 -> a = 2
     public void testPropagateEquals_VarEq2AndVarRangeGt1Lt3AndVarGt0AndVarNeq4() {
         FieldAttribute fa = getFieldAttribute();
-        Equals eq = new Equals(EMPTY, fa, TWO);
-        Range range = new Range(EMPTY, fa, ONE, false, THREE, false);
-        GreaterThan gt = new GreaterThan(EMPTY, fa, L(0));
-        NotEquals neq = new NotEquals(EMPTY, fa, FOUR);
+        Equals eq = equalsOf(fa, TWO);
+        Range range = rangeOf(fa, ONE, false, THREE, false);
+        GreaterThan gt = greaterThanOf(fa, L(0));
+        NotEquals neq = notEqualsOf(fa, FOUR);
 
         PropagateEquals rule = new PropagateEquals();
         Expression and = Predicates.combineAnd(Arrays.asList(eq, range, gt, neq));
@@ -1167,8 +1177,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // a = 2 OR a > 1 -> a > 1
     public void testPropagateEquals_VarEq2OrVarGt1() {
         FieldAttribute fa = getFieldAttribute();
-        Equals eq = new Equals(EMPTY, fa, TWO);
-        GreaterThan gt = new GreaterThan(EMPTY, fa, ONE);
+        Equals eq = equalsOf(fa, TWO);
+        GreaterThan gt = greaterThanOf(fa, ONE);
 
         PropagateEquals rule = new PropagateEquals();
         Expression exp = rule.rule(new Or(EMPTY, eq, gt));
@@ -1178,8 +1188,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // a = 2 OR a > 2 -> a >= 2
     public void testPropagateEquals_VarEq2OrVarGte2() {
         FieldAttribute fa = getFieldAttribute();
-        Equals eq = new Equals(EMPTY, fa, TWO);
-        GreaterThan gt = new GreaterThan(EMPTY, fa, TWO);
+        Equals eq = equalsOf(fa, TWO);
+        GreaterThan gt = greaterThanOf(fa, TWO);
 
         PropagateEquals rule = new PropagateEquals();
         Expression exp = rule.rule(new Or(EMPTY, eq, gt));
@@ -1191,8 +1201,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // a = 2 OR a < 3 -> a < 3
     public void testPropagateEquals_VarEq2OrVarLt3() {
         FieldAttribute fa = getFieldAttribute();
-        Equals eq = new Equals(EMPTY, fa, TWO);
-        LessThan lt = new LessThan(EMPTY, fa, THREE);
+        Equals eq = equalsOf(fa, TWO);
+        LessThan lt = lessThanOf(fa, THREE);
 
         PropagateEquals rule = new PropagateEquals();
         Expression exp = rule.rule(new Or(EMPTY, eq, lt));
@@ -1202,8 +1212,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // a = 3 OR a < 3 -> a <= 3
     public void testPropagateEquals_VarEq3OrVarLt3() {
         FieldAttribute fa = getFieldAttribute();
-        Equals eq = new Equals(EMPTY, fa, THREE);
-        LessThan lt = new LessThan(EMPTY, fa, THREE);
+        Equals eq = equalsOf(fa, THREE);
+        LessThan lt = lessThanOf(fa, THREE);
 
         PropagateEquals rule = new PropagateEquals();
         Expression exp = rule.rule(new Or(EMPTY, eq, lt));
@@ -1215,8 +1225,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // a = 2 OR 1 < a < 3 -> 1 < a < 3
     public void testPropagateEquals_VarEq2OrVarRangeGt1Lt3() {
         FieldAttribute fa = getFieldAttribute();
-        Equals eq = new Equals(EMPTY, fa, TWO);
-        Range range = new Range(EMPTY, fa, ONE, false, THREE, false);
+        Equals eq = equalsOf(fa, TWO);
+        Range range = rangeOf(fa, ONE, false, THREE, false);
 
         PropagateEquals rule = new PropagateEquals();
         Expression exp = rule.rule(new Or(EMPTY, eq, range));
@@ -1226,8 +1236,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // a = 2 OR 2 < a < 3 -> 2 <= a < 3
     public void testPropagateEquals_VarEq2OrVarRangeGt2Lt3() {
         FieldAttribute fa = getFieldAttribute();
-        Equals eq = new Equals(EMPTY, fa, TWO);
-        Range range = new Range(EMPTY, fa, TWO, false, THREE, false);
+        Equals eq = equalsOf(fa, TWO);
+        Range range = rangeOf(fa, TWO, false, THREE, false);
 
         PropagateEquals rule = new PropagateEquals();
         Expression exp = rule.rule(new Or(EMPTY, eq, range));
@@ -1242,8 +1252,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // a = 3 OR 2 < a < 3 -> 2 < a <= 3
     public void testPropagateEquals_VarEq3OrVarRangeGt2Lt3() {
         FieldAttribute fa = getFieldAttribute();
-        Equals eq = new Equals(EMPTY, fa, THREE);
-        Range range = new Range(EMPTY, fa, TWO, false, THREE, false);
+        Equals eq = equalsOf(fa, THREE);
+        Range range = rangeOf(fa, TWO, false, THREE, false);
 
         PropagateEquals rule = new PropagateEquals();
         Expression exp = rule.rule(new Or(EMPTY, eq, range));
@@ -1258,8 +1268,8 @@ public class OptimizerRulesTests extends ESTestCase {
     // a = 2 OR a != 2 -> TRUE
     public void testPropagateEquals_VarEq2OrVarNeq2() {
         FieldAttribute fa = getFieldAttribute();
-        Equals eq = new Equals(EMPTY, fa, TWO);
-        NotEquals neq = new NotEquals(EMPTY, fa, TWO);
+        Equals eq = equalsOf(fa, TWO);
+        NotEquals neq = notEqualsOf(fa, TWO);
 
         PropagateEquals rule = new PropagateEquals();
         Expression exp = rule.rule(new Or(EMPTY, eq, neq));
@@ -1269,23 +1279,23 @@ public class OptimizerRulesTests extends ESTestCase {
     // a = 2 OR a != 5 -> a != 5
     public void testPropagateEquals_VarEq2OrVarNeq5() {
         FieldAttribute fa = getFieldAttribute();
-        Equals eq = new Equals(EMPTY, fa, TWO);
-        NotEquals neq = new NotEquals(EMPTY, fa, FIVE);
+        Equals eq = equalsOf(fa, TWO);
+        NotEquals neq = notEqualsOf(fa, FIVE);
 
         PropagateEquals rule = new PropagateEquals();
         Expression exp = rule.rule(new Or(EMPTY, eq, neq));
         assertEquals(NotEquals.class, exp.getClass());
         NotEquals ne = (NotEquals) exp;
-        assertEquals(ne.right(), FIVE);
+        assertEquals(FIVE, ne.right());
     }
 
     // a = 2 OR 3 < a < 4 OR a > 2 OR a!= 2 -> TRUE
     public void testPropagateEquals_VarEq2OrVarRangeGt3Lt4OrVarGt2OrVarNe2() {
         FieldAttribute fa = getFieldAttribute();
-        Equals eq = new Equals(EMPTY, fa, TWO);
-        Range range = new Range(EMPTY, fa, THREE, false, FOUR, false);
-        GreaterThan gt = new GreaterThan(EMPTY, fa, TWO);
-        NotEquals neq = new NotEquals(EMPTY, fa, TWO);
+        Equals eq = equalsOf(fa, TWO);
+        Range range = rangeOf(fa, THREE, false, FOUR, false);
+        GreaterThan gt = greaterThanOf(fa, TWO);
+        NotEquals neq = notEqualsOf(fa, TWO);
 
         PropagateEquals rule = new PropagateEquals();
         Expression exp = rule.rule(Predicates.combineOr(Arrays.asList(eq, range, neq, gt)));

+ 4 - 2
x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/AstBuilder.java

@@ -10,15 +10,17 @@ import org.elasticsearch.xpack.ql.plan.logical.LogicalPlan;
 import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SingleStatementContext;
 import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
 
+import java.time.ZoneId;
 import java.util.Map;
 
 class AstBuilder extends CommandBuilder {
     /**
      * Create AST Builder
      * @param params a map between '?' tokens that represent parameters and the actual parameter values
+     * @param zoneId user specified timezone in the session
      */
-    AstBuilder(Map<Token, SqlTypedParamValue> params) {
-        super(params);
+    AstBuilder(Map<Token, SqlTypedParamValue> params, ZoneId zoneId) {
+        super(params, zoneId);
     }
 
     @Override

+ 3 - 2
x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/CommandBuilder.java

@@ -36,6 +36,7 @@ import org.elasticsearch.xpack.sql.plan.logical.command.sys.SysTables;
 import org.elasticsearch.xpack.sql.plan.logical.command.sys.SysTypes;
 import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
 
+import java.time.ZoneId;
 import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.List;
@@ -44,8 +45,8 @@ import java.util.Map;
 
 abstract class CommandBuilder extends LogicalPlanBuilder {
 
-    protected CommandBuilder(Map<Token, SqlTypedParamValue> params) {
-        super(params);
+    protected CommandBuilder(Map<Token, SqlTypedParamValue> params, ZoneId zoneId) {
+        super(params, zoneId);
     }
 
     @Override

+ 13 - 10
x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ExpressionBuilder.java

@@ -119,6 +119,7 @@ import org.elasticsearch.xpack.sql.type.SqlDataTypes;
 
 import java.time.Duration;
 import java.time.Period;
+import java.time.ZoneId;
 import java.time.format.DateTimeParseException;
 import java.time.temporal.TemporalAmount;
 import java.util.ArrayList;
@@ -139,9 +140,11 @@ import static org.elasticsearch.xpack.sql.util.DateUtils.dateTimeOfEscapedLitera
 abstract class ExpressionBuilder extends IdentifierBuilder {
 
     private final Map<Token, SqlTypedParamValue> params;
+    private final ZoneId zoneId;
 
-    ExpressionBuilder(Map<Token, SqlTypedParamValue> params) {
+    ExpressionBuilder(Map<Token, SqlTypedParamValue> params, ZoneId zoneId) {
         this.params = params;
+        this.zoneId = zoneId;
     }
 
     protected Expression expression(ParseTree ctx) {
@@ -191,19 +194,19 @@ abstract class ExpressionBuilder extends IdentifierBuilder {
 
         switch (op.getSymbol().getType()) {
             case SqlBaseParser.EQ:
-                return new Equals(source, left, right);
+                return new Equals(source, left, right, zoneId);
             case SqlBaseParser.NULLEQ:
-                return new NullEquals(source, left, right);
+                return new NullEquals(source, left, right, zoneId);
             case SqlBaseParser.NEQ:
-                return new NotEquals(source, left, right);
+                return new NotEquals(source, left, right, zoneId);
             case SqlBaseParser.LT:
-                return new LessThan(source, left, right);
+                return new LessThan(source, left, right, zoneId);
             case SqlBaseParser.LTE:
-                return new LessThanOrEqual(source, left, right);
+                return new LessThanOrEqual(source, left, right, zoneId);
             case SqlBaseParser.GT:
-                return new GreaterThan(source, left, right);
+                return new GreaterThan(source, left, right, zoneId);
             case SqlBaseParser.GTE:
-                return new GreaterThanOrEqual(source, left, right);
+                return new GreaterThanOrEqual(source, left, right, zoneId);
             default:
                 throw new ParsingException(source, "Unknown operator {}", source.text());
         }
@@ -224,7 +227,7 @@ abstract class ExpressionBuilder extends IdentifierBuilder {
         Expression e = null;
         switch (pCtx.kind.getType()) {
             case SqlBaseParser.BETWEEN:
-                e = new Range(source, exp, expression(pCtx.lower), true, expression(pCtx.upper), true);
+                e = new Range(source, exp, expression(pCtx.lower), true, expression(pCtx.upper), true, zoneId);
                 break;
             case SqlBaseParser.IN:
                 if (pCtx.query() != null) {
@@ -473,7 +476,7 @@ abstract class ExpressionBuilder extends IdentifierBuilder {
         for (SqlBaseParser.WhenClauseContext when : ctx.whenClause()) {
             if (ctx.operand != null) {
                 expressions.add(new IfConditional(source(when),
-                    new Equals(source(when), expression(ctx.operand), expression(when.condition)), expression(when.result)));
+                    new Equals(source(when), expression(ctx.operand), expression(when.condition), zoneId), expression(when.result)));
             } else {
                 expressions.add(new IfConditional(source(when), expression(when.condition), expression(when.result)));
             }

+ 3 - 2
x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/LogicalPlanBuilder.java

@@ -54,6 +54,7 @@ import org.elasticsearch.xpack.sql.plan.logical.With;
 import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
 import org.elasticsearch.xpack.sql.session.SingletonExecutable;
 
+import java.time.ZoneId;
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -63,8 +64,8 @@ import static java.util.Collections.emptyList;
 
 abstract class LogicalPlanBuilder extends ExpressionBuilder {
 
-    protected LogicalPlanBuilder(Map<Token, SqlTypedParamValue> params) {
-        super(params);
+    protected LogicalPlanBuilder(Map<Token, SqlTypedParamValue> params, ZoneId zoneId) {
+        super(params, zoneId);
     }
 
     @Override

+ 17 - 7
x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlParser.java

@@ -28,6 +28,7 @@ import org.elasticsearch.xpack.ql.expression.Expression;
 import org.elasticsearch.xpack.ql.plan.logical.LogicalPlan;
 import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
 
+import java.time.ZoneId;
 import java.util.Arrays;
 import java.util.BitSet;
 import java.util.Collections;
@@ -39,6 +40,7 @@ import java.util.function.BiFunction;
 import java.util.function.Function;
 
 import static java.lang.String.format;
+import static org.elasticsearch.xpack.sql.util.DateUtils.UTC;
 
 public class SqlParser {
 
@@ -50,7 +52,14 @@ public class SqlParser {
      * Used only in tests
      */
     public LogicalPlan createStatement(String sql) {
-        return createStatement(sql, Collections.emptyList());
+        return createStatement(sql, Collections.emptyList(), UTC);
+    }
+
+    /**
+     * Used only in tests
+     */
+    public LogicalPlan createStatement(String sql, ZoneId zoneId) {
+        return createStatement(sql, Collections.emptyList(), zoneId);
     }
 
     /**
@@ -59,11 +68,11 @@ public class SqlParser {
      * @param params - a list of parameters for the statement if the statement is parametrized
      * @return logical plan
      */
-    public LogicalPlan createStatement(String sql, List<SqlTypedParamValue> params) {
+    public LogicalPlan createStatement(String sql, List<SqlTypedParamValue> params, ZoneId zoneId) {
         if (log.isDebugEnabled()) {
             log.debug("Parsing as statement: {}", sql);
         }
-        return invokeParser(sql, params, SqlBaseParser::singleStatement, AstBuilder::plan);
+        return invokeParser(sql, params, zoneId, SqlBaseParser::singleStatement, AstBuilder::plan);
     }
 
     /**
@@ -81,12 +90,13 @@ public class SqlParser {
             log.debug("Parsing as expression: {}", expression);
         }
 
-        return invokeParser(expression, params, SqlBaseParser::singleExpression, AstBuilder::expression);
+        return invokeParser(expression, params, UTC, SqlBaseParser::singleExpression, AstBuilder::expression);
     }
 
     private <T> T invokeParser(String sql,
-                               List<SqlTypedParamValue> params, Function<SqlBaseParser,
-                               ParserRuleContext> parseFunction,
+                               List<SqlTypedParamValue> params,
+                               ZoneId zoneId,
+                               Function<SqlBaseParser, ParserRuleContext> parseFunction,
                                BiFunction<AstBuilder, ParserRuleContext, T> visitor) {
         try {
             SqlBaseLexer lexer = new SqlBaseLexer(new CaseInsensitiveStream(sql));
@@ -126,7 +136,7 @@ public class SqlParser {
                 log.info("Parse tree {} " + tree.toStringTree());
             }
 
-            return visitor.apply(new AstBuilder(paramTokens), tree);
+            return visitor.apply(new AstBuilder(paramTokens, zoneId), tree);
         } catch (StackOverflowError e) {
             throw new ParsingException("SQL statement is too large, " +
                 "causing stack overflow when generating the parsing tree: [{}]", sql);

+ 1 - 1
x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/session/SqlSession.java

@@ -96,7 +96,7 @@ public class SqlSession implements Session {
     }
 
     private LogicalPlan doParse(String sql, List<SqlTypedParamValue> params) {
-        return new SqlParser().createStatement(sql, params);
+        return new SqlParser().createStatement(sql, params, configuration.zoneId());
     }
 
     public void analyzedPlan(LogicalPlan parsed, boolean verify, ActionListener<LogicalPlan> listener) {

+ 9 - 7
x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/predicate/conditional/CaseTests.java

@@ -21,6 +21,7 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
+import static org.elasticsearch.xpack.ql.TestUtils.equalsOf;
 import static org.elasticsearch.xpack.ql.expression.function.scalar.FunctionTestUtils.randomIntLiteral;
 import static org.elasticsearch.xpack.ql.expression.function.scalar.FunctionTestUtils.randomStringLiteral;
 import static org.elasticsearch.xpack.ql.tree.Source.EMPTY;
@@ -40,7 +41,8 @@ public class CaseTests extends AbstractNodeTestCase<Case, Expression> {
         List<Expression> expressions = new ArrayList<>(noConditionals + 1);
         for (int i = 0; i < noConditionals; i++) {
             expressions.add(new IfConditional(
-                randomSource(), new Equals(randomSource(), randomStringLiteral(), randomStringLiteral()), randomIntLiteral()));
+                randomSource(), new Equals(randomSource(), randomStringLiteral(), randomStringLiteral(), randomZone()),
+                randomIntLiteral()));
 
         }
         // default else
@@ -86,14 +88,14 @@ public class CaseTests extends AbstractNodeTestCase<Case, Expression> {
         // ELSE 'default'
         // END
         Case c = new Case(EMPTY, Arrays.asList(
-                new IfConditional(EMPTY, new Equals(EMPTY, literal(1), literal(1)), Literal.NULL), literal("default")));
+                new IfConditional(EMPTY, equalsOf(literal(1), literal(1)), Literal.NULL), literal("default")));
         assertEquals(KEYWORD, c.dataType());
 
         // CASE WHEN 1 = 1 THEN 'foo'
         // ELSE NULL
         // END
         c = new Case(EMPTY, Arrays.asList(
-                new IfConditional(EMPTY, new Equals(EMPTY, literal(1), literal(1)), literal("foo")),
+                new IfConditional(EMPTY, equalsOf(literal(1), literal(1)), literal("foo")),
             Literal.NULL));
         assertEquals(KEYWORD, c.dataType());
 
@@ -101,7 +103,7 @@ public class CaseTests extends AbstractNodeTestCase<Case, Expression> {
         // ELSE NULL
         // END
         c = new Case(EMPTY, Arrays.asList(
-                new IfConditional(EMPTY, new Equals(EMPTY, literal(1), literal(1)), Literal.NULL),
+                new IfConditional(EMPTY, equalsOf(literal(1), literal(1)), Literal.NULL),
             Literal.NULL));
         assertEquals(NULL, c.dataType());
 
@@ -110,8 +112,8 @@ public class CaseTests extends AbstractNodeTestCase<Case, Expression> {
         // ELSE NULL
         // END
         c = new Case(EMPTY, Arrays.asList(
-                new IfConditional(EMPTY, new Equals(EMPTY, literal(1), literal(1)), Literal.NULL),
-                new IfConditional(EMPTY, new Equals(EMPTY, literal(2), literal(2)), literal("foo")),
+                new IfConditional(EMPTY, equalsOf(literal(1), literal(1)), Literal.NULL),
+                new IfConditional(EMPTY, equalsOf(literal(2), literal(2)), literal("foo")),
             Literal.NULL));
         assertEquals(KEYWORD, c.dataType());
     }
@@ -133,7 +135,7 @@ public class CaseTests extends AbstractNodeTestCase<Case, Expression> {
             for (int i = 0; i < c.conditions().size(); i++) {
                 if (i == rndIdx) {
                     expressions.add(new IfConditional(randomValueOtherThan(c.conditions().get(i).source(), SourceTests::randomSource),
-                        new Equals(randomSource(), randomStringLiteral(), randomStringLiteral()),
+                        new Equals(randomSource(), randomStringLiteral(), randomStringLiteral(), randomZone()),
                         randomValueOtherThan(c.conditions().get(i).condition(), FunctionTestUtils::randomStringLiteral)));
                 } else {
                     expressions.add(c.conditions().get(i));

+ 4 - 2
x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/predicate/conditional/IifTests.java

@@ -5,6 +5,7 @@
  */
 package org.elasticsearch.xpack.sql.expression.predicate.conditional;
 
+import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.xpack.ql.expression.Expression;
 import org.elasticsearch.xpack.ql.expression.function.scalar.FunctionTestUtils;
 import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.Equals;
@@ -33,7 +34,7 @@ import static org.elasticsearch.xpack.ql.tree.SourceTests.randomSource;
 public class IifTests extends AbstractNodeTestCase<Iif, Expression> {
 
     public static Iif randomIif() {
-        return new Iif(randomSource(), new Equals(randomSource(), randomStringLiteral(), randomStringLiteral()),
+        return new Iif(randomSource(), new Equals(randomSource(), randomStringLiteral(), randomStringLiteral(), randomZone()),
             randomIntLiteral(), randomIntLiteral());
     }
 
@@ -86,7 +87,8 @@ public class IifTests extends AbstractNodeTestCase<Iif, Expression> {
         Equals eq = (Equals) iif.conditions().get(0).condition();
         expressions.add(new Equals(randomSource(),
             randomValueOtherThan(eq.left(), FunctionTestUtils::randomStringLiteral),
-            randomValueOtherThan(eq.right(), FunctionTestUtils::randomStringLiteral)));
+            randomValueOtherThan(eq.right(), FunctionTestUtils::randomStringLiteral),
+            randomValueOtherThan(eq.zoneId(), ESTestCase::randomZone)));
         expressions.add(randomValueOtherThan(iif.conditions().get(0).result(), FunctionTestUtils::randomIntLiteral));
         expressions.add(randomValueOtherThan(iif.elseResult(), FunctionTestUtils::randomIntLiteral));
         return expressions;

+ 36 - 31
x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/optimizer/OptimizerTests.java

@@ -35,8 +35,6 @@ import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.Equal
 import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.GreaterThan;
 import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.GreaterThanOrEqual;
 import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.LessThan;
-import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.LessThanOrEqual;
-import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.NotEquals;
 import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.NullEquals;
 import org.elasticsearch.xpack.ql.expression.predicate.regex.RLike;
 import org.elasticsearch.xpack.ql.expression.predicate.regex.RLikePattern;
@@ -129,6 +127,13 @@ import static java.util.Arrays.asList;
 import static java.util.Collections.emptyList;
 import static java.util.Collections.emptyMap;
 import static java.util.Collections.singletonList;
+import static org.elasticsearch.xpack.ql.TestUtils.equalsOf;
+import static org.elasticsearch.xpack.ql.TestUtils.greaterThanOf;
+import static org.elasticsearch.xpack.ql.TestUtils.greaterThanOrEqualOf;
+import static org.elasticsearch.xpack.ql.TestUtils.lessThanOf;
+import static org.elasticsearch.xpack.ql.TestUtils.lessThanOrEqualOf;
+import static org.elasticsearch.xpack.ql.TestUtils.notEqualsOf;
+import static org.elasticsearch.xpack.ql.TestUtils.nullEqualsOf;
 import static org.elasticsearch.xpack.ql.expression.Literal.FALSE;
 import static org.elasticsearch.xpack.ql.expression.Literal.NULL;
 import static org.elasticsearch.xpack.ql.expression.Literal.TRUE;
@@ -212,7 +217,7 @@ public class OptimizerTests extends ESTestCase {
         // b
         Alias b = new Alias(EMPTY, "b", L(10));
         // WHERE a < 10
-        LogicalPlan p = new Filter(EMPTY, FROM(), new LessThan(EMPTY, a, L(10)));
+        LogicalPlan p = new Filter(EMPTY, FROM(), lessThanOf(a, L(10)));
         // SELECT
         p = new Project(EMPTY, p, Arrays.asList(a, b));
         // ORDER BY
@@ -368,7 +373,7 @@ public class OptimizerTests extends ESTestCase {
         // arithmetic
         assertNullLiteral(rule.rule(new Add(EMPTY, getFieldAttribute(), NULL)));
         // comparison
-        assertNullLiteral(rule.rule(new GreaterThan(EMPTY, getFieldAttribute(), NULL)));
+        assertNullLiteral(rule.rule(greaterThanOf(getFieldAttribute(), NULL)));
         // regex
         assertNullLiteral(rule.rule(new RLike(EMPTY, NULL, new RLikePattern("123"))));
     }
@@ -581,10 +586,10 @@ public class OptimizerTests extends ESTestCase {
         // END
 
         Case c = new Case(EMPTY, Arrays.asList(
-                new IfConditional(EMPTY, new Equals(EMPTY, getFieldAttribute(), ONE), literal("foo1")),
-                new IfConditional(EMPTY, new Equals(EMPTY, ONE, TWO), literal("bar1")),
-                new IfConditional(EMPTY, new Equals(EMPTY, TWO, ONE), literal("bar2")),
-                new IfConditional(EMPTY, new GreaterThan(EMPTY, getFieldAttribute(), ONE), literal("foo2")), literal("default")));
+                new IfConditional(EMPTY, equalsOf(getFieldAttribute(), ONE), literal("foo1")),
+                new IfConditional(EMPTY, equalsOf(ONE, TWO), literal("bar1")),
+                new IfConditional(EMPTY, equalsOf(TWO, ONE), literal("bar2")),
+                new IfConditional(EMPTY, greaterThanOf(getFieldAttribute(), ONE), literal("foo2")), literal("default")));
         assertFalse(c.foldable());
         Expression e = new SimplifyCase().rule(c);
         assertEquals(Case.class, e.getClass());
@@ -607,8 +612,8 @@ public class OptimizerTests extends ESTestCase {
         // 'foo2'
 
         Case c = new Case(EMPTY, Arrays.asList(
-                new IfConditional(EMPTY, new Equals(EMPTY, ONE, TWO), literal("foo1")),
-                new IfConditional(EMPTY, new Equals(EMPTY, ONE, ONE), literal("foo2")), literal("default")));
+                new IfConditional(EMPTY, equalsOf(ONE, TWO), literal("foo1")),
+                new IfConditional(EMPTY, equalsOf(ONE, ONE), literal("foo2")), literal("default")));
         assertFalse(c.foldable());
 
         SimplifyCase rule = new SimplifyCase();
@@ -632,7 +637,7 @@ public class OptimizerTests extends ESTestCase {
         // myField (non-foldable)
 
         Case c = new Case(EMPTY, Arrays.asList(
-                new IfConditional(EMPTY, new Equals(EMPTY, ONE, TWO), literal("foo1")),
+                new IfConditional(EMPTY, equalsOf(ONE, TWO), literal("foo1")),
                 getFieldAttribute("myField")));
         assertFalse(c.foldable());
 
@@ -647,7 +652,7 @@ public class OptimizerTests extends ESTestCase {
 
     public void testSimplifyIif_ConditionTrue_FoldableResult() {
         SimplifyCase rule = new SimplifyCase();
-        Iif iif = new Iif(EMPTY, new Equals(EMPTY, ONE, ONE), literal("foo"), literal("bar"));
+        Iif iif = new Iif(EMPTY, equalsOf(ONE, ONE), literal("foo"), literal("bar"));
         assertTrue(iif.foldable());
 
         Expression e = rule.rule(iif);
@@ -661,7 +666,7 @@ public class OptimizerTests extends ESTestCase {
 
     public void testSimplifyIif_ConditionTrue_NonFoldableResult() {
         SimplifyCase rule = new SimplifyCase();
-        Iif iif = new Iif(EMPTY, new Equals(EMPTY, ONE, ONE), getFieldAttribute("myField"), literal("bar"));
+        Iif iif = new Iif(EMPTY, equalsOf(ONE, ONE), getFieldAttribute("myField"), literal("bar"));
         assertFalse(iif.foldable());
 
         Expression e = rule.rule(iif);
@@ -676,7 +681,7 @@ public class OptimizerTests extends ESTestCase {
 
     public void testSimplifyIif_ConditionFalse_FoldableResult() {
         SimplifyCase rule = new SimplifyCase();
-        Iif iif = new Iif(EMPTY, new Equals(EMPTY, ONE, TWO), literal("foo"), literal("bar"));
+        Iif iif = new Iif(EMPTY, equalsOf(ONE, TWO), literal("foo"), literal("bar"));
         assertTrue(iif.foldable());
 
         Expression e = rule.rule(iif);
@@ -690,7 +695,7 @@ public class OptimizerTests extends ESTestCase {
 
     public void testSimplifyIif_ConditionFalse_NonFoldableResult() {
         SimplifyCase rule = new SimplifyCase();
-        Iif iif = new Iif(EMPTY, new Equals(EMPTY, ONE, TWO), literal("foo"), getFieldAttribute("myField"));
+        Iif iif = new Iif(EMPTY, equalsOf(ONE, TWO), literal("foo"), getFieldAttribute("myField"));
         assertFalse(iif.foldable());
 
         Expression e = rule.rule(iif);
@@ -711,15 +716,15 @@ public class OptimizerTests extends ESTestCase {
     }
 
     public void testBinaryComparisonSimplification() {
-        assertEquals(TRUE, new BinaryComparisonSimplification().rule(new Equals(EMPTY, FIVE, FIVE)));
-        assertEquals(TRUE, new BinaryComparisonSimplification().rule(new NullEquals(EMPTY, FIVE, FIVE)));
-        assertEquals(TRUE, new BinaryComparisonSimplification().rule(new NullEquals(EMPTY, NULL, NULL)));
-        assertEquals(FALSE, new BinaryComparisonSimplification().rule(new NotEquals(EMPTY, FIVE, FIVE)));
-        assertEquals(TRUE, new BinaryComparisonSimplification().rule(new GreaterThanOrEqual(EMPTY, FIVE, FIVE)));
-        assertEquals(TRUE, new BinaryComparisonSimplification().rule(new LessThanOrEqual(EMPTY, FIVE, FIVE)));
-
-        assertEquals(FALSE, new BinaryComparisonSimplification().rule(new GreaterThan(EMPTY, FIVE, FIVE)));
-        assertEquals(FALSE, new BinaryComparisonSimplification().rule(new LessThan(EMPTY, FIVE, FIVE)));
+        assertEquals(TRUE, new BinaryComparisonSimplification().rule(equalsOf(FIVE, FIVE)));
+        assertEquals(TRUE, new BinaryComparisonSimplification().rule(nullEqualsOf(FIVE, FIVE)));
+        assertEquals(TRUE, new BinaryComparisonSimplification().rule(nullEqualsOf(NULL, NULL)));
+        assertEquals(FALSE, new BinaryComparisonSimplification().rule(notEqualsOf(FIVE, FIVE)));
+        assertEquals(TRUE, new BinaryComparisonSimplification().rule(greaterThanOrEqualOf(FIVE, FIVE)));
+        assertEquals(TRUE, new BinaryComparisonSimplification().rule(lessThanOrEqualOf(FIVE, FIVE)));
+
+        assertEquals(FALSE, new BinaryComparisonSimplification().rule(greaterThanOf(FIVE, FIVE)));
+        assertEquals(FALSE, new BinaryComparisonSimplification().rule(lessThanOf(FIVE, FIVE)));
     }
 
     public void testNullEqualsWithNullLiteralBecomesIsNull() {
@@ -728,12 +733,12 @@ public class OptimizerTests extends ESTestCase {
         FieldAttribute fa = getFieldAttribute();
         Source source = new Source(1, 10, "IS_NULL(a)");
 
-        Expression e = bcSimpl.rule(swapLiteralsToRight.rule(new NullEquals(source, fa, NULL)));
+        Expression e = bcSimpl.rule(swapLiteralsToRight.rule(new NullEquals(source, fa, NULL, randomZone())));
         assertEquals(IsNull.class, e.getClass());
         IsNull isNull = (IsNull) e;
         assertEquals(source, isNull.source());
 
-        e = bcSimpl.rule(swapLiteralsToRight.rule(new NullEquals(source, NULL, fa)));
+        e = bcSimpl.rule(swapLiteralsToRight.rule(new NullEquals(source, NULL, fa, randomZone())));
         assertEquals(IsNull.class, e.getClass());
         isNull = (IsNull) e;
         assertEquals(source, isNull.source());
@@ -764,10 +769,10 @@ public class OptimizerTests extends ESTestCase {
     public void testCombineUnbalancedComparisonsMixedWithEqualsIntoRange() {
         FieldAttribute fa = getFieldAttribute();
         IsNotNull isn = new IsNotNull(EMPTY, fa);
-        GreaterThanOrEqual gte = new GreaterThanOrEqual(EMPTY, fa, ONE);
+        GreaterThanOrEqual gte = greaterThanOrEqualOf(fa, ONE);
 
-        Equals eq = new Equals(EMPTY, fa, L(10));
-        LessThan lt = new LessThan(EMPTY, fa, FIVE);
+        Equals eq = equalsOf(fa, L(10));
+        LessThan lt = lessThanOf(fa, FIVE);
 
         And and = new And(EMPTY, new And(EMPTY, isn, gte), new And(EMPTY, lt, eq));
 
@@ -997,8 +1002,8 @@ public class OptimizerTests extends ESTestCase {
         Alias bAlias = new Alias(EMPTY, "bAlias", b);
         
         Project p = new Project(EMPTY, FROM(), Arrays.asList(aAlias, bAlias));
-        Filter f = new Filter(EMPTY, p,
-                new And(EMPTY, new GreaterThan(EMPTY, aAlias.toAttribute(), L(1)), new GreaterThan(EMPTY, bAlias.toAttribute(), L(2))));
+        Filter f = new Filter(EMPTY, p, new And(EMPTY, greaterThanOf(aAlias.toAttribute(), L(1)),
+            greaterThanOf(bAlias.toAttribute(), L(2))));
         
         ReplaceReferenceAttributeWithSource rule = new ReplaceReferenceAttributeWithSource();
         Expression condition = f.condition();

+ 2 - 1
x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysColumnsTests.java

@@ -35,6 +35,7 @@ import java.util.function.Consumer;
 import static java.util.Collections.emptyList;
 import static java.util.Collections.singletonList;
 import static org.elasticsearch.action.ActionListener.wrap;
+import static org.elasticsearch.xpack.ql.TestUtils.UTC;
 import static org.elasticsearch.xpack.sql.types.SqlTypesTests.loadMapping;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyBoolean;
@@ -521,7 +522,7 @@ public class SysColumnsTests extends ESTestCase {
         EsIndex test = new EsIndex("test", mapping);
         Analyzer analyzer = new Analyzer(SqlTestUtils.TEST_CFG, new FunctionRegistry(), IndexResolution.valid(test),
                 new Verifier(new Metrics()));
-        Command cmd = (Command) analyzer.analyze(parser.createStatement(sql, params), true);
+        Command cmd = (Command) analyzer.analyze(parser.createStatement(sql, params, UTC), true);
 
         IndexResolver resolver = mock(IndexResolver.class);
         when(resolver.clusterName()).thenReturn(CLUSTER_NAME);

+ 1 - 1
x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTablesTests.java

@@ -335,7 +335,7 @@ public class SysTablesTests extends ESTestCase {
         EsIndex test = new EsIndex("test", mapping);
         Analyzer analyzer = new Analyzer(SqlTestUtils.TEST_CFG, new FunctionRegistry(), IndexResolution.valid(test),
                                          new Verifier(new Metrics()));
-        Command cmd = (Command) analyzer.analyze(parser.createStatement(sql, params), true);
+        Command cmd = (Command) analyzer.analyze(parser.createStatement(sql, params, cfg.zoneId()), true);
 
         IndexResolver resolver = mock(IndexResolver.class);
         when(resolver.clusterName()).thenReturn(CLUSTER_NAME);

+ 135 - 64
x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorTests.java

@@ -68,6 +68,7 @@ import org.elasticsearch.xpack.sql.types.SqlTypesTests;
 import org.elasticsearch.xpack.sql.util.DateUtils;
 import org.junit.BeforeClass;
 
+import java.time.ZoneId;
 import java.time.ZonedDateTime;
 import java.util.Collection;
 import java.util.List;
@@ -87,6 +88,7 @@ import static org.elasticsearch.xpack.sql.expression.function.scalar.math.MathPr
 import static org.elasticsearch.xpack.sql.planner.QueryTranslator.DATE_FORMAT;
 import static org.elasticsearch.xpack.sql.planner.QueryTranslator.TIME_FORMAT;
 import static org.elasticsearch.xpack.sql.type.SqlDataTypes.DATE;
+import static org.elasticsearch.xpack.sql.util.DateUtils.UTC;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.Matchers.endsWith;
 import static org.hamcrest.Matchers.instanceOf;
@@ -114,20 +116,32 @@ public class QueryTranslatorTests extends ESTestCase {
     }
 
     private LogicalPlan plan(String sql) {
-        return analyzer.analyze(parser.createStatement(sql), true);
+        return plan(sql, UTC);
+    }
+
+    private LogicalPlan plan(String sql, ZoneId zoneId) {
+        return analyzer.analyze(parser.createStatement(sql, zoneId), true);
     }
 
     private PhysicalPlan optimizeAndPlan(String sql) {
         return  planner.plan(optimizer.optimize(plan(sql)), true);
     }
 
+    private QueryTranslation translate(Expression condition) {
+        return QueryTranslator.toQuery(condition, false);
+    }
+
+    private QueryTranslation translateWithAggs(Expression condition) {
+        return QueryTranslator.toQuery(condition, true);
+    }
+
     public void testTermEqualityAnalyzer() {
         LogicalPlan p = plan("SELECT some.string FROM test WHERE some.string = 'value'");
         assertTrue(p instanceof Project);
         p = ((Project) p).child();
         assertTrue(p instanceof Filter);
         Expression condition = ((Filter) p).condition();
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         Query query = translation.query;
         assertTrue(query instanceof TermQuery);
         TermQuery tq = (TermQuery) query;
@@ -141,26 +155,26 @@ public class QueryTranslatorTests extends ESTestCase {
         var pc = ((Aggregate) p).child();
         assertTrue(pc instanceof Filter);
         Expression condition = ((Filter) pc).condition();
-        assertEquals(((GreaterThan) condition).functionName(), "GREATERTHAN");
+        assertEquals("GREATERTHAN", ((GreaterThan) condition).functionName());
         List<Expression> groupings = ((Aggregate) p).groupings();
         assertTrue(groupings.get(0).resolved());
         var agg = ((Aggregate) p).aggregates();
-        assertEquals((agg.get(0)).name(), "c");
-        assertEquals(((Count) ((Alias) agg.get(0)).child()).functionName(), "COUNT");
+        assertEquals("c", (agg.get(0)).name());
+        assertEquals("COUNT", ((Count) ((Alias) agg.get(0)).child()).functionName());
     }
     public void testLiteralWithGroupBy(){
         LogicalPlan p = plan("SELECT 1 as t, 2 FROM test GROUP BY int");
         assertTrue(p instanceof Aggregate);
         List<Expression> groupings = ((Aggregate) p).groupings();
-        assertTrue(groupings.size() == 1);
+        assertEquals(1, groupings.size());
         assertTrue(groupings.get(0).resolved());
         assertTrue(groupings.get(0) instanceof FieldAttribute);
         var aggs = ((Aggregate) p).aggregates();
-        assertTrue(aggs.size() == 2);
-        assertEquals((aggs.get(0)).name(), "t");
+        assertEquals(2, aggs.size());
+        assertEquals("t", (aggs.get(0)).name());
         assertTrue(((Alias) aggs.get(0)).child() instanceof Literal);
-        assertEquals(((Alias) aggs.get(0)).child().toString(), "1");
-        assertEquals(((Alias) aggs.get(1)).child().toString(), "2");
+        assertEquals("1", ((Alias) aggs.get(0)).child().toString());
+        assertEquals("2", ((Alias) aggs.get(1)).child().toString());
     }
 
     public void testTermEqualityNotAnalyzed() {
@@ -169,7 +183,7 @@ public class QueryTranslatorTests extends ESTestCase {
         p = ((Project) p).child();
         assertTrue(p instanceof Filter);
         Expression condition = ((Filter) p).condition();
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         Query query = translation.query;
         assertTrue(query instanceof TermQuery);
         TermQuery tq = (TermQuery) query;
@@ -183,7 +197,7 @@ public class QueryTranslatorTests extends ESTestCase {
         p = ((Project) p).child();
         assertTrue(p instanceof Filter);
         Expression condition = ((Filter) p).condition();
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         Query query = translation.query;
         assertTrue(query instanceof TermQuery);
         TermQuery tq = (TermQuery) query;
@@ -192,12 +206,13 @@ public class QueryTranslatorTests extends ESTestCase {
     }
 
     public void testTermEqualityForDateWithLiteralDate() {
-        LogicalPlan p = plan("SELECT some.string FROM test WHERE date = CAST('2019-08-08T12:34:56' AS DATETIME)");
+        ZoneId zoneId = randomZone();
+        LogicalPlan p = plan("SELECT some.string FROM test WHERE date = CAST('2019-08-08T12:34:56' AS DATETIME)", zoneId);
         assertTrue(p instanceof Project);
         p = ((Project) p).child();
         assertTrue(p instanceof Filter);
         Expression condition = ((Filter) p).condition();
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         Query query = translation.query;
         assertTrue(query instanceof RangeQuery);
         RangeQuery rq = (RangeQuery) query;
@@ -207,15 +222,17 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(rq.includeLower());
         assertTrue(rq.includeUpper());
         assertEquals(DATE_FORMAT, rq.format());
+        assertEquals(zoneId, rq.zoneId());
     }
 
     public void testTermEqualityForDateWithLiteralTime() {
-        LogicalPlan p = plan("SELECT some.string FROM test WHERE date = CAST('12:34:56' AS TIME)");
+        ZoneId zoneId = randomZone();
+        LogicalPlan p = plan("SELECT some.string FROM test WHERE date = CAST('12:34:56' AS TIME)", zoneId);
         assertTrue(p instanceof Project);
         p = ((Project) p).child();
         assertTrue(p instanceof Filter);
         Expression condition = ((Filter) p).condition();
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         Query query = translation.query;
         assertTrue(query instanceof RangeQuery);
         RangeQuery rq = (RangeQuery) query;
@@ -225,6 +242,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(rq.includeLower());
         assertTrue(rq.includeUpper());
         assertEquals(TIME_FORMAT, rq.format());
+        assertEquals(zoneId, rq.zoneId());
     }
 
     public void testComparisonAgainstColumns() {
@@ -233,50 +251,56 @@ public class QueryTranslatorTests extends ESTestCase {
         p = ((Project) p).child();
         assertTrue(p instanceof Filter);
         Expression condition = ((Filter) p).condition();
-        QlIllegalArgumentException ex = expectThrows(QlIllegalArgumentException.class, () -> QueryTranslator.toQuery(condition, false));
+        QlIllegalArgumentException ex = expectThrows(QlIllegalArgumentException.class, () -> translate(condition));
         assertEquals("Line 1:43: Comparisons against variables are not (currently) supported; offender [int] in [>]", ex.getMessage());
     }
 
     public void testDateRange() {
-        LogicalPlan p = plan("SELECT some.string FROM test WHERE date > 1969-05-13");
+        ZoneId zoneId = randomZone();
+        LogicalPlan p = plan("SELECT some.string FROM test WHERE date > 1969-05-13", zoneId);
         assertTrue(p instanceof Project);
         p = ((Project) p).child();
         assertTrue(p instanceof Filter);
         Expression condition = ((Filter) p).condition();
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         Query query = translation.query;
         assertTrue(query instanceof RangeQuery);
         RangeQuery rq = (RangeQuery) query;
         assertEquals("date", rq.field());
         assertEquals(1951, rq.lower());
+        assertEquals(zoneId, rq.zoneId());
     }
 
     public void testDateRangeLiteral() {
-        LogicalPlan p = plan("SELECT some.string FROM test WHERE date > '1969-05-13'");
+        ZoneId zoneId = randomZone();
+        LogicalPlan p = plan("SELECT some.string FROM test WHERE date > '1969-05-13'", zoneId);
         assertTrue(p instanceof Project);
         p = ((Project) p).child();
         assertTrue(p instanceof Filter);
         Expression condition = ((Filter) p).condition();
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         Query query = translation.query;
         assertTrue(query instanceof RangeQuery);
         RangeQuery rq = (RangeQuery) query;
         assertEquals("date", rq.field());
         assertEquals("1969-05-13", rq.lower());
+        assertEquals(zoneId, rq.zoneId());
     }
 
     public void testDateRangeCast() {
-        LogicalPlan p = plan("SELECT some.string FROM test WHERE date > CAST('1969-05-13T12:34:56Z' AS DATETIME)");
+        ZoneId zoneId = randomZone();
+        LogicalPlan p = plan("SELECT some.string FROM test WHERE date > CAST('1969-05-13T12:34:56Z' AS DATETIME)", zoneId);
         assertTrue(p instanceof Project);
         p = ((Project) p).child();
         assertTrue(p instanceof Filter);
         Expression condition = ((Filter) p).condition();
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         Query query = translation.query;
         assertTrue(query instanceof RangeQuery);
         RangeQuery rq = (RangeQuery) query;
         assertEquals("date", rq.field());
         assertEquals("1969-05-13T12:34:56.000Z", rq.lower());
+        assertEquals(zoneId, rq.zoneId());
     }
 
     public void testDateRangeWithCurrentTimestamp() {
@@ -315,13 +339,14 @@ public class QueryTranslatorTests extends ESTestCase {
     }
 
     private void testDateRangeWithCurrentFunctions(String function, String pattern, ZonedDateTime now) {
-        String operator = randomFrom(new String[] {">", ">=", "<", "<=", "=", "!="});
-        LogicalPlan p = plan("SELECT some.string FROM test WHERE date" + operator + function);
+        ZoneId zoneId = randomZone();
+        String operator = randomFrom(">", ">=", "<", "<=", "=", "!=");
+        LogicalPlan p = plan("SELECT some.string FROM test WHERE date" + operator + function, zoneId);
         assertTrue(p instanceof Project);
         p = ((Project) p).child();
         assertTrue(p instanceof Filter);
         Expression condition = ((Filter) p).condition();
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         Query query = translation.query;
         RangeQuery rq;
 
@@ -348,12 +373,13 @@ public class QueryTranslatorTests extends ESTestCase {
         assertEquals(operator.equals("=") || operator.equals("!=") || operator.equals("<="), rq.includeUpper());
         assertEquals(operator.equals("=") || operator.equals("!=") || operator.equals(">="), rq.includeLower());
         assertEquals(pattern, rq.format());
+        assertEquals(zoneId, rq.zoneId());
     }
 
     private void testDateRangeWithCurrentFunctions_AndRangeOptimization(String function, String pattern, ZonedDateTime lowerValue,
             ZonedDateTime upperValue) {
-        String lowerOperator = randomFrom(new String[] {"<", "<="});
-        String upperOperator = randomFrom(new String[] {">", ">="});
+        String lowerOperator = randomFrom("<", "<=");
+        String upperOperator = randomFrom(">", ">=");
         // use both date-only interval (1 DAY) and time-only interval (1 second) to cover CURRENT_TIMESTAMP and TODAY scenarios
         String interval = "(INTERVAL 1 DAY + INTERVAL 1 SECOND)";
 
@@ -380,6 +406,50 @@ public class QueryTranslatorTests extends ESTestCase {
         assertEquals(lowerOperator.equals("<="), rq.includeUpper());
         assertEquals(upperOperator.equals(">="), rq.includeLower());
         assertEquals(pattern, rq.format());
+        assertEquals(UTC, rq.zoneId());
+    }
+
+    public void testDateRangeWithESDateMath() {
+        ZoneId zoneId = randomZone();
+        String operator = randomFrom(">", ">=", "<", "<=", "=", "!=");
+        String dateMath = randomFrom("now", "now/d", "now/h", "now-2h", "now+2h", "now-5d", "now+5d");
+        LogicalPlan p = plan("SELECT some.string FROM test WHERE date" + operator + "'" + dateMath + "'", zoneId);
+        assertTrue(p instanceof Project);
+        p = ((Project) p).child();
+        assertTrue(p instanceof Filter);
+        Expression condition = ((Filter) p).condition();
+        QueryTranslation translation = translate(condition);
+        Query query = translation.query;
+
+        if ("=".equals(operator) || "!=".equals(operator)) {
+            TermQuery tq;
+            if ("=".equals(operator)) {
+                assertTrue(query instanceof TermQuery);
+                tq = (TermQuery) query;
+            } else {
+                assertTrue(query instanceof NotQuery);
+                NotQuery nq = (NotQuery) query;
+                assertTrue(nq.child() instanceof TermQuery);
+                tq = (TermQuery) nq.child();
+            }
+            assertEquals("date", tq.term());
+        } else {
+            assertTrue(query instanceof RangeQuery);
+            RangeQuery rq = (RangeQuery) query;
+            assertEquals("date", rq.field());
+
+            if (operator.contains("<")) {
+                assertEquals(dateMath, rq.upper());
+            }
+            if (operator.contains(">")) {
+                assertEquals(dateMath, rq.lower());
+            }
+
+            assertEquals("<=".equals(operator), rq.includeUpper());
+            assertEquals(">=".equals(operator), rq.includeLower());
+            assertNull(rq.format());
+            assertEquals(zoneId, rq.zoneId());
+        }
     }
 
     public void testTranslateDateAdd_WhereClause_Painless() {
@@ -388,7 +458,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(p.children().get(0) instanceof Filter);
         Expression condition = ((Filter) p.children().get(0)).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         assertNull(translation.aggFilter);
         assertTrue(translation.query instanceof ScriptQuery);
         ScriptQuery sc = (ScriptQuery) translation.query;
@@ -405,7 +475,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(p.children().get(0) instanceof Filter);
         Expression condition = ((Filter) p.children().get(0)).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         assertNull(translation.aggFilter);
         assertTrue(translation.query instanceof ScriptQuery);
         ScriptQuery sc = (ScriptQuery) translation.query;
@@ -422,7 +492,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(p.children().get(0) instanceof Filter);
         Expression condition = ((Filter) p.children().get(0)).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         assertNull(translation.aggFilter);
         assertTrue(translation.query instanceof ScriptQuery);
         ScriptQuery sc = (ScriptQuery) translation.query;
@@ -438,7 +508,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(p.children().get(0) instanceof Filter);
         Expression condition = ((Filter) p.children().get(0)).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         assertNull(translation.aggFilter);
         assertTrue(translation.query instanceof ScriptQuery);
         ScriptQuery sc = (ScriptQuery) translation.query;
@@ -454,7 +524,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(p.children().get(0) instanceof Filter);
         Expression condition = ((Filter) p.children().get(0)).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         assertNull(translation.aggFilter);
         assertTrue(translation.query instanceof ScriptQuery);
         ScriptQuery sc = (ScriptQuery) translation.query;
@@ -470,7 +540,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(p.children().get(0) instanceof Filter);
         Expression condition = ((Filter) p.children().get(0)).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         assertNull(translation.aggFilter);
         assertTrue(translation.query instanceof ScriptQuery);
         ScriptQuery sc = (ScriptQuery) translation.query;
@@ -487,7 +557,7 @@ public class QueryTranslatorTests extends ESTestCase {
         p = ((Project) p).child();
         assertTrue(p instanceof Filter);
         Expression condition = ((Filter) p).condition();
-        QueryTranslation qt = QueryTranslator.toQuery(condition, false);
+        QueryTranslation qt = translate(condition);
         assertEquals(WildcardQuery.class, qt.query.getClass());
         WildcardQuery qsq = ((WildcardQuery) qt.query);
         assertEquals("some.string.typical", qsq.field());
@@ -499,7 +569,7 @@ public class QueryTranslatorTests extends ESTestCase {
         p = ((Project) p).child();
         assertTrue(p instanceof Filter);
         Expression condition = ((Filter) p).condition();
-        QueryTranslation qt = QueryTranslator.toQuery(condition, false);
+        QueryTranslation qt = translate(condition);
         assertEquals(RegexQuery.class, qt.query.getClass());
         RegexQuery qsq = ((RegexQuery) qt.query);
         assertEquals("some.string.typical", qsq.field());
@@ -511,7 +581,7 @@ public class QueryTranslatorTests extends ESTestCase {
         p = ((Project) p).child();
         assertTrue(p instanceof Filter);
         Expression condition = ((Filter) p).condition();
-        QlIllegalArgumentException ex = expectThrows(QlIllegalArgumentException.class, () -> QueryTranslator.toQuery(condition, false));
+        QlIllegalArgumentException ex = expectThrows(QlIllegalArgumentException.class, () -> translate(condition));
         assertEquals("Scalar function [LTRIM(keyword)] not allowed (yet) as argument for LTRIM(keyword) like '%a%'", ex.getMessage());
     }
 
@@ -521,7 +591,7 @@ public class QueryTranslatorTests extends ESTestCase {
         p = ((Project) p).child();
         assertTrue(p instanceof Filter);
         Expression condition = ((Filter) p).condition();
-        QlIllegalArgumentException ex = expectThrows(QlIllegalArgumentException.class, () -> QueryTranslator.toQuery(condition, false));
+        QlIllegalArgumentException ex = expectThrows(QlIllegalArgumentException.class, () -> translate(condition));
         assertEquals("Scalar function [LTRIM(keyword)] not allowed (yet) as argument for LTRIM(keyword) RLIKE '.*a.*'", ex.getMessage());
     }
 
@@ -532,7 +602,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(p instanceof Filter);
 
         Expression condition = ((Filter) p).condition();
-        QueryTranslation qt = QueryTranslator.toQuery(condition, false);
+        QueryTranslation qt = translate(condition);
         assertEquals(BoolQuery.class, qt.query.getClass());
         BoolQuery bq = ((BoolQuery) qt.query);
         assertTrue(bq.isAnd());
@@ -565,7 +635,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(p instanceof Filter);
 
         Expression condition = ((Filter) p).condition();
-        QueryTranslation qt = QueryTranslator.toQuery(condition, false);
+        QueryTranslation qt = translate(condition);
         assertEquals(BoolQuery.class, qt.query.getClass());
         BoolQuery bq = ((BoolQuery) qt.query);
         assertTrue(bq.isAnd());
@@ -606,7 +676,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(p.children().get(0) instanceof Filter);
         Expression condition = ((Filter) p.children().get(0)).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         assertTrue(translation.query instanceof ScriptQuery);
         ScriptQuery sc = (ScriptQuery) translation.query;
         assertEquals("InternalQlScriptUtils.nullSafeFilter(InternalQlScriptUtils.not(" +
@@ -622,7 +692,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(p.children().get(0) instanceof Filter);
         Expression condition = ((Filter) p.children().get(0)).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         assertTrue(translation.query instanceof NotQuery);
         NotQuery tq = (NotQuery) translation.query;
         assertTrue(tq.child() instanceof ExistsQuery);
@@ -637,7 +707,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(p.children().get(0) instanceof Filter);
         Expression condition = ((Filter) p.children().get(0)).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         assertTrue(translation.query instanceof ScriptQuery);
         ScriptQuery sc = (ScriptQuery) translation.query;
         assertEquals("InternalQlScriptUtils.nullSafeFilter(InternalQlScriptUtils.isNull(" +
@@ -652,7 +722,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(p.children().get(0) instanceof Filter);
         Expression condition = ((Filter) p.children().get(0)).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         assertTrue(translation.query instanceof ExistsQuery);
         ExistsQuery eq = (ExistsQuery) translation.query;
         assertEquals("{\"exists\":{\"field\":\"keyword\",\"boost\":1.0}}",
@@ -665,7 +735,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(p.children().get(0) instanceof Filter);
         Expression condition = ((Filter) p.children().get(0)).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         assertTrue(translation.query instanceof ScriptQuery);
         ScriptQuery sc = (ScriptQuery) translation.query;
         assertEquals("InternalQlScriptUtils.nullSafeFilter(InternalQlScriptUtils.isNotNull(" +
@@ -679,7 +749,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(p instanceof Filter);
         Expression condition = ((Filter) p).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, true);
+        QueryTranslation translation = translateWithAggs(condition);
         assertNull(translation.query);
         AggFilter aggFilter = translation.aggFilter;
         assertEquals(
@@ -693,7 +763,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(p instanceof Filter);
         Expression condition = ((Filter) p).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, true);
+        QueryTranslation translation = translateWithAggs(condition);
         assertNull(translation.query);
         AggFilter aggFilter = translation.aggFilter;
         assertEquals(
@@ -702,6 +772,8 @@ public class QueryTranslatorTests extends ESTestCase {
         assertThat(aggFilter.scriptTemplate().params().toString(), startsWith("[{a=max(int)"));
     }
 
+
+
     public void testTranslateCoalesceExpression_WhereGroupByAndHaving_Painless() {
         PhysicalPlan p = optimizeAndPlan("SELECT COALESCE(null, int) AS c, COALESCE(max(date), NULL) as m FROM test " +
                                          "WHERE c > 10 GROUP BY c HAVING m > '2020-01-01'::date");
@@ -735,7 +807,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(p.children().get(0) instanceof Filter);
         Expression condition = ((Filter) p.children().get(0)).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         Query query = translation.query;
         assertTrue(query instanceof TermsQuery);
         TermsQuery tq = (TermsQuery) query;
@@ -749,7 +821,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(p.children().get(0) instanceof Filter);
         Expression condition = ((Filter) p.children().get(0)).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         Query query = translation.query;
         assertTrue(query instanceof TermsQuery);
         TermsQuery tq = (TermsQuery) query;
@@ -763,7 +835,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(p.children().get(0) instanceof Filter);
         Expression condition = ((Filter) p.children().get(0)).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         Query query = translation.query;
         assertTrue(query instanceof TermsQuery);
         TermsQuery tq = (TermsQuery) query;
@@ -777,7 +849,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(p.children().get(0) instanceof Filter);
         Expression condition = ((Filter) p.children().get(0)).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         assertNull(translation.aggFilter);
         assertTrue(translation.query instanceof ScriptQuery);
         ScriptQuery sc = (ScriptQuery) translation.query;
@@ -792,7 +864,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(p instanceof Filter);
         Expression condition = ((Filter) p).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, true);
+        QueryTranslation translation = translateWithAggs(condition);
         assertNull(translation.query);
         AggFilter aggFilter = translation.aggFilter;
         assertEquals("InternalQlScriptUtils.nullSafeFilter(InternalQlScriptUtils.in(params.a0, params.v0))",
@@ -806,7 +878,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(p instanceof Filter);
         Expression condition = ((Filter) p).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, true);
+        QueryTranslation translation = translateWithAggs(condition);
         assertNull(translation.query);
         AggFilter aggFilter = translation.aggFilter;
         assertEquals("InternalQlScriptUtils.nullSafeFilter(InternalQlScriptUtils.in(params.a0, params.v0))",
@@ -821,7 +893,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(p instanceof Filter);
         Expression condition = ((Filter) p).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, true);
+        QueryTranslation translation = translateWithAggs(condition);
         assertNull(translation.query);
         AggFilter aggFilter = translation.aggFilter;
         assertEquals("InternalQlScriptUtils.nullSafeFilter(InternalQlScriptUtils.in(params.a0, params.v0))",
@@ -839,7 +911,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(p instanceof Filter);
         Expression condition = ((Filter) p).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, true);
+        QueryTranslation translation = translateWithAggs(condition);
         assertNull(translation.query);
         AggFilter aggFilter = translation.aggFilter;
         assertEquals("InternalQlScriptUtils.nullSafeFilter(InternalQlScriptUtils.gt(InternalSqlScriptUtils." +
@@ -901,7 +973,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertTrue(p instanceof Filter);
         Expression condition = ((Filter) p).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, true);
+        QueryTranslation translation = translateWithAggs(condition);
         assertNull(translation.query);
         AggFilter aggFilter = translation.aggFilter;
         assertEquals("InternalQlScriptUtils.nullSafeFilter(InternalQlScriptUtils.gt(InternalSqlScriptUtils.abs" +
@@ -917,7 +989,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertThat(p.children().get(0), instanceOf(Filter.class));
         Expression condition = ((Filter) p.children().get(0)).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, true);
+        QueryTranslation translation = translateWithAggs(condition);
         assertNull(translation.query);
         AggFilter aggFilter = translation.aggFilter;
         assertEquals("InternalQlScriptUtils.nullSafeFilter(InternalQlScriptUtils.eq(" +
@@ -934,7 +1006,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertThat(p.children().get(0), instanceOf(Filter.class));
         Expression condition = ((Filter) p.children().get(0)).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, true);
+        QueryTranslation translation = translateWithAggs(condition);
         assertNull(translation.query);
         AggFilter aggFilter = translation.aggFilter;
         assertEquals("InternalQlScriptUtils.nullSafeFilter(" +
@@ -952,7 +1024,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertThat(p.children().get(0), instanceOf(Filter.class));
         Expression condition = ((Filter) p.children().get(0)).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         assertNull(translation.aggFilter);
         assertTrue(translation.query instanceof ScriptQuery);
         ScriptQuery sc = (ScriptQuery) translation.query;
@@ -971,7 +1043,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertThat(p.children().get(0), instanceOf(Filter.class));
         Expression condition = ((Filter) p.children().get(0)).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         assertNull(translation.aggFilter);
         assertTrue(translation.query instanceof GeoDistanceQuery);
         GeoDistanceQuery gq = (GeoDistanceQuery) translation.query;
@@ -988,7 +1060,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertThat(p.children().get(0), instanceOf(Filter.class));
         Expression condition = ((Filter) p.children().get(0)).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         assertNull(translation.aggFilter);
         assertThat(translation.query, instanceOf(ScriptQuery.class));
         ScriptQuery sc = (ScriptQuery) translation.query;
@@ -1004,7 +1076,7 @@ public class QueryTranslatorTests extends ESTestCase {
         assertThat(p.children().get(0), instanceOf(Filter.class));
         Expression condition = ((Filter) p.children().get(0)).condition();
         assertFalse(condition.foldable());
-        QueryTranslation translation = QueryTranslator.toQuery(condition, false);
+        QueryTranslation translation = translate(condition);
         assertNull(translation.aggFilter);
         assertThat(translation.query, instanceOf(ScriptQuery.class));
         ScriptQuery sc = (ScriptQuery) translation.query;
@@ -1805,7 +1877,6 @@ public class QueryTranslatorTests extends ESTestCase {
         EsQueryExec eqe = (EsQueryExec) p;
         assertTrue("Should be tracking hits", eqe.queryContainer().shouldTrackHits());
         assertEquals(1, eqe.output().size());
-        String query = eqe.queryContainer().toString().replaceAll("\\s+", "");
         assertThat(eqe.queryContainer().toString().replaceAll("\\s+", ""), containsString("\"size\":0"));
     }
 
@@ -1879,7 +1950,7 @@ public class QueryTranslatorTests extends ESTestCase {
 
                 Expression condition = ((Filter) p).condition();
                 assertFalse(condition.foldable());
-                QueryTranslation translation = QueryTranslator.toQuery(condition, true);
+                QueryTranslation translation = translateWithAggs(condition);
                 assertNull(translation.query);
                 AggFilter aggFilter = translation.aggFilter;
                 assertEquals(

+ 7 - 3
x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/querydsl/container/QueryContainerTests.java

@@ -20,6 +20,7 @@ import org.elasticsearch.xpack.ql.tree.Source;
 import org.elasticsearch.xpack.ql.tree.SourceTests;
 import org.elasticsearch.xpack.ql.type.EsField;
 
+import java.time.ZoneId;
 import java.util.AbstractMap.SimpleImmutableEntry;
 import java.util.Arrays;
 import java.util.BitSet;
@@ -44,15 +45,17 @@ public class QueryContainerTests extends ESTestCase {
     }
 
     public void testRewriteToContainsNestedFieldWhenContainsNestedField() {
+        ZoneId zoneId = randomZone();
         Query original = new BoolQuery(source, true,
             new NestedQuery(source, path, singletonMap(name, new SimpleImmutableEntry<>(hasDocValues, format)),
                     new MatchAll(source)),
-            new RangeQuery(source, randomAlphaOfLength(5), 0, randomBoolean(), 100, randomBoolean()));
+            new RangeQuery(source, randomAlphaOfLength(5), 0, randomBoolean(), 100, randomBoolean(), zoneId));
         assertSame(original, QueryContainer.rewriteToContainNestedField(original, source, path, name, format, randomBoolean()));
     }
 
     public void testRewriteToContainsNestedFieldWhenCanAddNestedField() {
-        Query buddy = new RangeQuery(source, randomAlphaOfLength(5), 0, randomBoolean(), 100, randomBoolean());
+        ZoneId zoneId = randomZone();
+        Query buddy = new RangeQuery(source, randomAlphaOfLength(5), 0, randomBoolean(), 100, randomBoolean(), zoneId);
         Query original = new BoolQuery(source, true,
             new NestedQuery(source, path, emptyMap(), new MatchAll(source)),
             buddy);
@@ -64,7 +67,8 @@ public class QueryContainerTests extends ESTestCase {
     }
 
     public void testRewriteToContainsNestedFieldWhenDoesNotContainNestedFieldAndCantAdd() {
-        Query original = new RangeQuery(source, randomAlphaOfLength(5), 0, randomBoolean(), 100, randomBoolean());
+        ZoneId zoneId = randomZone();
+        Query original = new RangeQuery(source, randomAlphaOfLength(5), 0, randomBoolean(), 100, randomBoolean(), zoneId);
         Query expected = new BoolQuery(source, true,
             original,
             new NestedQuery(source, path, singletonMap(name, new SimpleImmutableEntry<>(hasDocValues, format)),