From 74ae8e3373c27e8def9eb9dc60660cabcaafa062 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Wed, 17 Jan 2018 12:49:47 -0500 Subject: [PATCH] SQL: Replace Node's reflection with explicit code (elastic/x-pack-elasticsearch#3490) This isn't pretty but it removes our need to compile with parameter names in the debug symbols and the use of reflection during tree transforms. `instanceof` is still used. It does so by forcing all subclasses of `Node` to implement two methods like this: ``` @Override protected NodeInfo info() { return info(this, PercentileRank::new, field(), value()); } @Override protected Expression replaceChildren(List newChildren) { if (newChildren.size() != 2) { throw new IllegalArgumentException("Expected [2] children but got [" + newChildren.size() + "]"); } return new PercentileRank(location(), newChildren.get(0), newChildren.get(1)); } ``` Every. Single. One. This is tedious and painful and you have to do each one perfectly, but it *is* checked by the compiler so it is less scary then the reflection based approach it is replacing. Marginally. It is still pretty terrifying because it requires so many tiny changes. While the compiler *does* check that you've made all the right methods it doesn't check that you've implemented them correctly. Technically relates elastic/x-pack-elasticsearch#2871 but doesn't really close the "OO all the things" spirit of elastic/x-pack-elasticsearch#2871. A change like this deserves a million tests. Instead, I've created a hacky reflection based test that attempts to verify that all subclasses of `Node` implement these method correctly for some test verifiable definition of "correct". Original commit: elastic/x-pack-elasticsearch@a69ab634f4f1a00534bf87dc8d245590143f6166 --- sql/server/build.gradle | 4 - .../xpack/sql/execution/search/Scroller.java | 4 +- .../xpack/sql/expression/Alias.java | 18 +- .../xpack/sql/expression/Attribute.java | 7 + .../sql/expression/BinaryExpression.java | 16 +- .../xpack/sql/expression/BinaryOperator.java | 4 +- .../xpack/sql/expression/Exists.java | 6 + .../xpack/sql/expression/Expression.java | 3 +- .../xpack/sql/expression/FieldAttribute.java | 7 + .../xpack/sql/expression/LeafExpression.java | 7 + .../xpack/sql/expression/Literal.java | 24 +- .../sql/expression/LiteralAttribute.java | 9 +- .../xpack/sql/expression/NamedExpression.java | 8 +- .../xpack/sql/expression/Order.java | 11 + .../xpack/sql/expression/ScalarSubquery.java | 6 + .../sql/expression/SubQueryExpression.java | 8 +- .../xpack/sql/expression/UnaryExpression.java | 13 +- .../xpack/sql/expression/UnresolvedAlias.java | 34 +- .../sql/expression/UnresolvedAttribute.java | 6 + .../xpack/sql/expression/UnresolvedStar.java | 31 +- .../xpack/sql/expression/function/Score.java | 28 + .../expression/function/ScoreAttribute.java | 8 +- .../function/UnresolvedFunction.java | 31 + .../function/aggregate/AggregateFunction.java | 16 + .../aggregate/AggregateFunctionAttribute.java | 9 +- .../expression/function/aggregate/Avg.java | 16 + .../function/aggregate/Correlation.java | 15 + .../expression/function/aggregate/Count.java | 16 + .../function/aggregate/Covariance.java | 15 + .../function/aggregate/ExtendedStats.java | 15 + .../function/aggregate/InnerAggregate.java | 23 +- .../function/aggregate/Kurtosis.java | 15 + .../function/aggregate/MatrixCount.java | 15 + .../function/aggregate/MatrixMean.java | 12 + .../function/aggregate/MatrixStats.java | 15 + .../function/aggregate/MatrixVariance.java | 15 + .../expression/function/aggregate/Max.java | 12 + .../expression/function/aggregate/Mean.java | 15 + .../expression/function/aggregate/Min.java | 15 + .../function/aggregate/NumericAggregate.java | 2 +- .../function/aggregate/Percentile.java | 18 +- .../function/aggregate/PercentileRank.java | 18 +- .../function/aggregate/PercentileRanks.java | 17 +- .../function/aggregate/Percentiles.java | 17 +- .../function/aggregate/Skewness.java | 15 + .../expression/function/aggregate/Stats.java | 15 + .../function/aggregate/StddevPop.java | 15 + .../expression/function/aggregate/Sum.java | 15 + .../function/aggregate/SumOfSquares.java | 15 + .../expression/function/aggregate/VarPop.java | 15 + .../function/scalar/BinaryScalarFunction.java | 13 +- .../sql/expression/function/scalar/Cast.java | 24 +- .../scalar/ScalarFunctionAttribute.java | 12 +- .../function/scalar/UnaryScalarFunction.java | 13 +- .../function/scalar/arithmetic/Add.java | 12 +- .../scalar/arithmetic/ArithmeticFunction.java | 19 +- .../BinaryArithmeticProcessorDefinition.java | 15 +- .../function/scalar/arithmetic/Div.java | 12 + .../function/scalar/arithmetic/Mod.java | 12 + .../function/scalar/arithmetic/Mul.java | 12 + .../function/scalar/arithmetic/Neg.java | 14 +- .../function/scalar/arithmetic/Sub.java | 12 + .../scalar/datetime/DateTimeFunction.java | 25 +- .../function/scalar/datetime/DayOfMonth.java | 11 + .../function/scalar/datetime/DayOfWeek.java | 11 + .../function/scalar/datetime/DayOfYear.java | 12 + .../function/scalar/datetime/HourOfDay.java | 11 + .../function/scalar/datetime/MinuteOfDay.java | 11 + .../scalar/datetime/MinuteOfHour.java | 11 + .../function/scalar/datetime/MonthOfYear.java | 11 + .../scalar/datetime/SecondOfMinute.java | 11 + .../scalar/datetime/WeekOfWeekYear.java | 11 + .../function/scalar/datetime/Year.java | 11 + .../expression/function/scalar/math/ACos.java | 11 + .../expression/function/scalar/math/ASin.java | 11 + .../expression/function/scalar/math/ATan.java | 12 +- .../expression/function/scalar/math/Abs.java | 11 + .../expression/function/scalar/math/Cbrt.java | 11 + .../expression/function/scalar/math/Ceil.java | 11 + .../expression/function/scalar/math/Cos.java | 11 + .../expression/function/scalar/math/Cosh.java | 11 + .../function/scalar/math/Degrees.java | 11 + .../expression/function/scalar/math/E.java | 12 + .../expression/function/scalar/math/Exp.java | 11 + .../function/scalar/math/Expm1.java | 11 + .../function/scalar/math/Floor.java | 11 + .../expression/function/scalar/math/Log.java | 11 + .../function/scalar/math/Log10.java | 11 + .../function/scalar/math/MathFunction.java | 20 +- .../expression/function/scalar/math/Pi.java | 12 + .../function/scalar/math/Radians.java | 11 + .../function/scalar/math/Round.java | 11 + .../expression/function/scalar/math/Sin.java | 11 + .../expression/function/scalar/math/Sinh.java | 11 + .../expression/function/scalar/math/Sqrt.java | 11 + .../expression/function/scalar/math/Tan.java | 11 + .../processor/definition/AggNameInput.java | 11 +- .../processor/definition/AggPathInput.java | 17 +- .../processor/definition/AggValueInput.java | 12 +- .../processor/definition/AttributeInput.java | 13 +- .../definition/BinaryProcessorDefinition.java | 15 +- .../definition/CommonNonExecutableInput.java | 5 +- .../processor/definition/ConstantInput.java | 11 +- .../definition/HitExtractorInput.java | 11 +- .../processor/definition/LeafInput.java | 13 +- .../definition/NonExecutableInput.java | 6 +- .../definition/ProcessorDefinition.java | 5 +- .../definition/ProcessorDefinitions.java | 8 +- .../processor/definition/ReferenceInput.java | 11 +- .../definition/ScoreProcessorDefinition.java | 19 +- .../definition/UnaryProcessorDefinition.java | 24 +- .../function/scalar/script/Params.java | 18 +- .../scalar/script/ScriptTemplate.java | 2 +- .../xpack/sql/expression/predicate/And.java | 13 +- .../sql/expression/predicate/Equals.java | 11 + .../sql/expression/predicate/GreaterThan.java | 10 + .../predicate/GreaterThanOrEqual.java | 10 + .../xpack/sql/expression/predicate/In.java | 22 +- .../sql/expression/predicate/IsNotNull.java | 10 + .../sql/expression/predicate/LessThan.java | 10 + .../expression/predicate/LessThanOrEqual.java | 11 + .../xpack/sql/expression/predicate/Not.java | 10 + .../xpack/sql/expression/predicate/Or.java | 12 + .../xpack/sql/expression/predicate/Range.java | 39 +- .../predicate/fulltext/FullTextPredicate.java | 14 +- .../fulltext/MatchQueryPredicate.java | 16 + .../fulltext/MultiMatchQueryPredicate.java | 16 +- .../fulltext/StringQueryPredicate.java | 13 + .../xpack/sql/expression/regex/Like.java | 11 + .../sql/expression/regex/LikePattern.java | 17 +- .../xpack/sql/expression/regex/RLike.java | 10 + .../xpack/sql/optimizer/Optimizer.java | 2 +- .../xpack/sql/parser/LogicalPlanBuilder.java | 2 +- .../xpack/sql/plan/logical/Aggregate.java | 15 +- .../xpack/sql/plan/logical/Distinct.java | 11 + .../xpack/sql/plan/logical/EsRelation.java | 8 +- .../xpack/sql/plan/logical/Filter.java | 13 +- .../xpack/sql/plan/logical/Join.java | 20 +- .../xpack/sql/plan/logical/LeafPlan.java | 6 + .../xpack/sql/plan/logical/Limit.java | 13 +- .../xpack/sql/plan/logical/LocalRelation.java | 11 + .../xpack/sql/plan/logical/OrderBy.java | 13 +- .../xpack/sql/plan/logical/Project.java | 13 +- .../xpack/sql/plan/logical/SubQueryAlias.java | 17 +- .../xpack/sql/plan/logical/UnaryPlan.java | 9 + .../sql/plan/logical/UnresolvedRelation.java | 6 + .../xpack/sql/plan/logical/With.java | 11 + .../sql/plan/logical/command/Command.java | 7 + .../xpack/sql/plan/logical/command/Debug.java | 8 +- .../sql/plan/logical/command/Explain.java | 6 + .../sql/plan/logical/command/ShowColumns.java | 9 +- .../plan/logical/command/ShowFunctions.java | 8 +- .../sql/plan/logical/command/ShowSchemas.java | 6 + .../sql/plan/logical/command/ShowTables.java | 6 + .../sql/plan/physical/AggregateExec.java | 18 +- .../xpack/sql/plan/physical/BinaryExec.java | 18 +- .../xpack/sql/plan/physical/CommandExec.java | 11 +- .../xpack/sql/plan/physical/EsQueryExec.java | 6 + .../xpack/sql/plan/physical/FilterExec.java | 24 +- .../xpack/sql/plan/physical/LeafExec.java | 6 + .../xpack/sql/plan/physical/LimitExec.java | 24 +- .../xpack/sql/plan/physical/LocalExec.java | 6 + .../xpack/sql/plan/physical/OrderExec.java | 19 +- .../xpack/sql/plan/physical/ProjectExec.java | 22 +- .../xpack/sql/plan/physical/UnaryExec.java | 14 +- .../sql/plan/physical/UnplannedExec.java | 13 +- .../xpack/sql/planner/Mapper.java | 22 +- .../xpack/sql/planner/QueryFolder.java | 5 +- .../xpack/sql/querydsl/agg/AggFilter.java | 6 +- .../xpack/sql/querydsl/agg/Aggs.java | 2 +- .../querydsl/container/QueryContainer.java | 2 +- .../elasticsearch/xpack/sql/tree/Node.java | 177 ++++-- .../xpack/sql/tree/NodeInfo.java | 403 ++++++++++++ .../xpack/sql/tree/NodeUtils.java | 321 ---------- .../xpack/sql/type/DataTypeConversion.java | 8 +- .../xpack/sql/util/Graphviz.java | 64 +- .../xpack/sql/expression/LiteralTests.java | 162 +++++ .../function/FunctionRegistryTests.java | 15 +- .../definition/AttributeInputTests.java | 4 +- .../BinaryProcessorDefinitionTests.java | 33 +- .../UnaryProcessorDefinitionTests.java | 3 +- .../xpack/sql/optimizer/OptimizerTests.java | 29 +- .../xpack/sql/tree/AbstractNodeTestCase.java | 42 ++ .../xpack/sql/tree/NodeSubclassTests.java | 590 ++++++++++++++++++ .../xpack/sql/tree/NodeTests.java | 124 ++++ .../sql/type/DataTypeConversionTests.java | 36 ++ 186 files changed, 3526 insertions(+), 644 deletions(-) create mode 100644 sql/server/src/main/java/org/elasticsearch/xpack/sql/tree/NodeInfo.java create mode 100644 sql/server/src/test/java/org/elasticsearch/xpack/sql/expression/LiteralTests.java create mode 100644 sql/server/src/test/java/org/elasticsearch/xpack/sql/tree/AbstractNodeTestCase.java create mode 100644 sql/server/src/test/java/org/elasticsearch/xpack/sql/tree/NodeSubclassTests.java create mode 100644 sql/server/src/test/java/org/elasticsearch/xpack/sql/tree/NodeTests.java diff --git a/sql/server/build.gradle b/sql/server/build.gradle index c6b5e6703c3..fb5d8c8a161 100644 --- a/sql/server/build.gradle +++ b/sql/server/build.gradle @@ -11,10 +11,6 @@ dependencyLicenses { ignoreSha 'rest-proto' } -// TODO probably not a good thing to rely on. See https://github.com/elastic/x-pack-elasticsearch/issues/2871 -compileJava.options.compilerArgs << "-parameters" -compileTestJava.options.compilerArgs << "-parameters" - /********************************************** * SQL Parser regeneration * **********************************************/ diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/execution/search/Scroller.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/execution/search/Scroller.java index 4199aa39bf3..981dbd564a7 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/execution/search/Scroller.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/execution/search/Scroller.java @@ -135,7 +135,7 @@ public class Scroller { else { action = () -> aggValues.column(aggPosition); } - return new AggValueInput(a.expression(), action, a.innerKey()); + return new AggValueInput(a.location(), a.expression(), action, a.innerKey()); }, AggPathInput.class).asProcessor(); // the input is provided through the value input above supplier = () -> processor.process(null); @@ -288,7 +288,7 @@ public class Scroller { throw new SqlIllegalArgumentException("Multi-level nested fields [%s] not supported yet", hitNames); } - return new HitExtractorInput(l.expression(), he); + return new HitExtractorInput(l.location(), l.expression(), he); }, ReferenceInput.class); String hitName = null; if (hitNames.size() == 1) { diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Alias.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Alias.java index e2bd7726e09..f439ce03c6a 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Alias.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Alias.java @@ -6,10 +6,13 @@ package org.elasticsearch.xpack.sql.expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import static java.util.Collections.singletonList; +import java.util.List; + public class Alias extends NamedExpression { private final Expression child; @@ -39,6 +42,19 @@ public class Alias extends NamedExpression { this.qualifier = qualifier; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Alias::new, name(), qualifier, child, id(), synthetic()); + } + + @Override + public Expression replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return new Alias(location(), name(), qualifier, newChildren.get(0), id(), synthetic()); + } + public Expression child() { return child; } @@ -85,4 +101,4 @@ public class Alias extends NamedExpression { public String toString() { return child + " AS " + name() + "#" + id(); } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Attribute.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Attribute.java index c3994f50dad..1401cf54584 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Attribute.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Attribute.java @@ -12,6 +12,8 @@ import java.util.Objects; import static java.util.Collections.emptyList; +import java.util.List; + /** * {@link Expression}s that can be converted into Elasticsearch * sorts, aggregations, or queries. They can also be extracted @@ -40,6 +42,11 @@ public abstract class Attribute extends NamedExpression { this.nullable = nullable; } + @Override + public final Expression replaceChildren(List newChildren) { + throw new UnsupportedOperationException("this type of node doesn't have any children to replace"); + } + public String qualifier() { return qualifier; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/BinaryExpression.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/BinaryExpression.java index 0b654dd26d4..fd6b8632f8e 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/BinaryExpression.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/BinaryExpression.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression; import org.elasticsearch.xpack.sql.tree.Location; import java.util.Arrays; +import java.util.List; import java.util.Objects; public abstract class BinaryExpression extends Expression { @@ -20,6 +21,15 @@ public abstract class BinaryExpression extends Expression { this.right = right; } + @Override + public final BinaryExpression replaceChildren(List newChildren) { + if (newChildren.size() != 2) { + throw new IllegalArgumentException("expected [2] children but received [" + newChildren.size() + "]"); + } + return replaceChildren(newChildren.get(0), newChildren.get(1)); + } + protected abstract BinaryExpression replaceChildren(Expression newLeft, Expression newRight); + public Expression left() { return left; } @@ -42,13 +52,13 @@ public abstract class BinaryExpression extends Expression { public int hashCode() { return Objects.hash(left, right); } - + @Override public boolean equals(Object obj) { if (!super.equals(obj)) { return false; } - + BinaryExpression other = (BinaryExpression) obj; return Objects.equals(left, other.left) && Objects.equals(right, other.right); @@ -69,4 +79,4 @@ public abstract class BinaryExpression extends Expression { public abstract String symbol(); public abstract BinaryExpression swapLeftAndRight(); -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/BinaryOperator.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/BinaryOperator.java index 8390a86c4ad..bfa4358d240 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/BinaryOperator.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/BinaryOperator.java @@ -28,7 +28,7 @@ public abstract class BinaryOperator extends BinaryExpression { } DataType l = left().dataType(); DataType r = right().dataType(); - + TypeResolution resolution = resolveInputType(l); if (resolution == TypeResolution.TYPE_RESOLVED) { @@ -36,4 +36,4 @@ public abstract class BinaryOperator extends BinaryExpression { } return resolution; } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Exists.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Exists.java index a158661e560..1a111af96e3 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Exists.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Exists.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.sql.expression; import org.elasticsearch.xpack.sql.plan.logical.LogicalPlan; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; @@ -20,6 +21,11 @@ public class Exists extends SubQueryExpression { super(location, query, id); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Exists::new, query(), id()); + } + @Override protected SubQueryExpression clone(LogicalPlan newQuery) { return new Exists(location(), newQuery); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Expression.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Expression.java index 6b12bddcad1..f0fe4e928da 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Expression.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Expression.java @@ -10,7 +10,6 @@ import org.elasticsearch.xpack.sql.capabilities.Resolvable; import org.elasticsearch.xpack.sql.capabilities.Resolvables; import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.Node; -import org.elasticsearch.xpack.sql.tree.NodeUtils; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.util.StringUtils; @@ -122,6 +121,6 @@ public abstract class Expression extends Node implements Resolvable @Override public String toString() { - return nodeName() + "[" + NodeUtils.propertiesToString(this, false) + "]"; + return nodeName() + "[" + propertiesToString(false) + "]"; } } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/FieldAttribute.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/FieldAttribute.java index e05706e08fd..6f5e23ff708 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/FieldAttribute.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/FieldAttribute.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression; import org.elasticsearch.common.Strings; import org.elasticsearch.xpack.sql.analysis.index.MappingException; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.KeywordType; import org.elasticsearch.xpack.sql.type.NestedType; @@ -57,6 +58,12 @@ public class FieldAttribute extends TypedAttribute { this.nestedParent = nestedPar; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, FieldAttribute::new, + parent, name(), dataType(), qualifier(), nullable(), id(), synthetic()); + } + public FieldAttribute parent() { return parent; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/LeafExpression.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/LeafExpression.java index 3f8978725de..49325ce1c81 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/LeafExpression.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/LeafExpression.java @@ -7,6 +7,8 @@ package org.elasticsearch.xpack.sql.expression; import org.elasticsearch.xpack.sql.tree.Location; +import java.util.List; + import static java.util.Collections.emptyList; public abstract class LeafExpression extends Expression { @@ -15,6 +17,11 @@ public abstract class LeafExpression extends Expression { super(location, emptyList()); } + @Override + public final Expression replaceChildren(List newChildren) { + throw new UnsupportedOperationException("this type of node doesn't have any children to replace"); + } + public AttributeSet references() { return AttributeSet.EMPTY; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Literal.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Literal.java index aa36caa8997..9a4ffce9295 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Literal.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Literal.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.sql.expression; import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypeConversion; import org.elasticsearch.xpack.sql.type.DataTypes; @@ -14,19 +15,23 @@ import org.elasticsearch.xpack.sql.type.DataTypes; import java.util.Objects; public class Literal extends LeafExpression { + public static final Literal TRUE = Literal.of(Location.EMPTY, Boolean.TRUE); + public static final Literal FALSE = Literal.of(Location.EMPTY, Boolean.FALSE); private final Object value; private final DataType dataType; - public static final Literal TRUE = Literal.of(Location.EMPTY, Boolean.TRUE); - public static final Literal FALSE = Literal.of(Location.EMPTY, Boolean.FALSE); - public Literal(Location location, Object value, DataType dataType) { super(location); this.dataType = dataType; this.value = DataTypeConversion.convert(value, dataType); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Literal::new, value, dataType); + } + public Object value() { return value; } @@ -46,11 +51,6 @@ public class Literal extends LeafExpression { return dataType; } - @Override - public int hashCode() { - return Objects.hash(value, dataType); - } - @Override public boolean resolved() { return true; @@ -61,6 +61,12 @@ public class Literal extends LeafExpression { return value; } + + @Override + public int hashCode() { + return Objects.hash(value, dataType); + } + @Override public boolean equals(Object obj) { if (this == obj) { @@ -99,4 +105,4 @@ public class Literal extends LeafExpression { return new Literal(foldable.location(), foldable.fold(), foldable.dataType()); } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/LiteralAttribute.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/LiteralAttribute.java index f8b9f82eb5b..d045daa8627 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/LiteralAttribute.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/LiteralAttribute.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.ConstantInput; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.ProcessorDefinition; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; public class LiteralAttribute extends TypedAttribute { @@ -28,6 +29,12 @@ public class LiteralAttribute extends TypedAttribute { return literal; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, LiteralAttribute::new, + name(), qualifier(), nullable(), id(), synthetic(), dataType(), literal); + } + @Override protected LiteralAttribute clone(Location location, String name, DataType dataType, String qualifier, boolean nullable, ExpressionId id, boolean synthetic) { @@ -35,7 +42,7 @@ public class LiteralAttribute extends TypedAttribute { } public ProcessorDefinition asProcessorDefinition() { - return new ConstantInput(literal, literal.value()); + return new ConstantInput(location(), literal, literal.value()); } @Override diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/NamedExpression.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/NamedExpression.java index 274234b90a1..cf06ddcc09c 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/NamedExpression.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/NamedExpression.java @@ -58,7 +58,13 @@ public abstract class NamedExpression extends Expression { NamedExpression other = (NamedExpression) obj; return Objects.equals(synthetic, other.synthetic) && Objects.equals(id, other.id) - && Objects.equals(name(), other.name()) + /* + * It is important that the line below be `name` + * and not `name()` because subclasses might override + * `name()` in ways that are not compatible with + * equality. Specifically the `Unresolved` subclasses. + */ + && Objects.equals(name, other.name) && Objects.equals(children(), other.children()); } } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Order.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Order.java index 1713cbe97ca..70e537527c8 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Order.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/Order.java @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.sql.expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import java.util.Objects; @@ -22,6 +23,16 @@ public class Order extends UnaryExpression { this.direction = direction; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Order::new, child(), direction); + } + + @Override + protected UnaryExpression replaceChild(Expression newChild) { + return new Order(location(), newChild, direction); + } + public OrderDirection direction() { return direction; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/ScalarSubquery.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/ScalarSubquery.java index c28068396f4..76906a714d0 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/ScalarSubquery.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/ScalarSubquery.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.sql.expression; import org.elasticsearch.xpack.sql.plan.logical.LogicalPlan; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; public class ScalarSubquery extends SubQueryExpression { @@ -19,6 +20,11 @@ public class ScalarSubquery extends SubQueryExpression { super(location, query, id); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, ScalarSubquery::new, query(), id()); + } + @Override protected ScalarSubquery clone(LogicalPlan newQuery) { return new ScalarSubquery(location(), newQuery); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/SubQueryExpression.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/SubQueryExpression.java index 89e387f6a40..33f2f3d0b07 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/SubQueryExpression.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/SubQueryExpression.java @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.sql.expression; import java.util.Collections; +import java.util.List; import java.util.Objects; import org.elasticsearch.xpack.sql.plan.logical.LogicalPlan; @@ -26,6 +27,11 @@ public abstract class SubQueryExpression extends Expression { this.id = id == null ? new ExpressionId() : id; } + @Override + public final Expression replaceChildren(List newChildren) { + throw new UnsupportedOperationException("this type of node doesn't have any children to replace"); + } + public LogicalPlan query() { return query; } @@ -60,7 +66,7 @@ public abstract class SubQueryExpression extends Expression { return false; } - Exists other = (Exists) obj; + SubQueryExpression other = (SubQueryExpression) obj; return Objects.equals(query(), other.query()); } } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/UnaryExpression.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/UnaryExpression.java index a178347c4f1..710ee760328 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/UnaryExpression.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/UnaryExpression.java @@ -12,6 +12,8 @@ import java.util.Objects; import static java.util.Collections.singletonList; +import java.util.List; + public abstract class UnaryExpression extends Expression { private final Expression child; @@ -21,6 +23,15 @@ public abstract class UnaryExpression extends Expression { this.child = child; } + @Override + public final UnaryExpression replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return replaceChild(newChildren.get(0)); + } + protected abstract UnaryExpression replaceChild(Expression newChild); + public Expression child() { return child; } @@ -63,4 +74,4 @@ public abstract class UnaryExpression extends Expression { UnaryExpression other = (UnaryExpression) obj; return Objects.equals(child, other.child); } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/UnresolvedAlias.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/UnresolvedAlias.java index 34d8f464a05..eaa6aeb5afa 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/UnresolvedAlias.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/UnresolvedAlias.java @@ -6,20 +6,36 @@ package org.elasticsearch.xpack.sql.expression; import org.elasticsearch.xpack.sql.capabilities.UnresolvedException; - +import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import java.util.Objects; import static java.util.Collections.singletonList; +import java.util.List; + public class UnresolvedAlias extends UnresolvedNamedExpression { private final Expression child; - public UnresolvedAlias(Expression child) { - super(child.location(), singletonList(child)); + public UnresolvedAlias(Location location, Expression child) { + super(location, singletonList(child)); this.child = child; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, UnresolvedAlias::new, child); + } + + @Override + public Expression replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return new UnresolvedAlias(location(), newChildren.get(0)); + } + public Expression child() { return child; } @@ -36,12 +52,20 @@ public class UnresolvedAlias extends UnresolvedNamedExpression { @Override public int hashCode() { - return Objects.hash(super.hashCode(), child); + return Objects.hash(child); } @Override public boolean equals(Object obj) { - return super.equals(obj) && Objects.equals(child, ((UnresolvedAlias) obj).child); + /* + * Intentionally not calling the superclass + * equals because it uses id which we always + * mutate when we make a clone. + */ + if (obj == null || obj.getClass() != getClass()) { + return false; + } + return Objects.equals(child, ((UnresolvedAlias) obj).child); } @Override diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/UnresolvedAttribute.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/UnresolvedAttribute.java index 9b3e87be026..bf7f4c7a237 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/UnresolvedAttribute.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/UnresolvedAttribute.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression; import org.elasticsearch.xpack.sql.capabilities.Unresolvable; import org.elasticsearch.xpack.sql.capabilities.UnresolvedException; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.util.CollectionUtils; @@ -44,6 +45,11 @@ public class UnresolvedAttribute extends Attribute implements Unresolvable { this.resolutionMetadata = resolutionMetadata; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, UnresolvedAttribute::new, + name(), qualifier(), id(), unresolvedMsg, resolutionMetadata); + } public Object resolutionMetadata() { return resolutionMetadata; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/UnresolvedStar.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/UnresolvedStar.java index a4da706ae7e..948a5465efa 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/UnresolvedStar.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/UnresolvedStar.java @@ -7,11 +7,13 @@ package org.elasticsearch.xpack.sql.expression; import org.elasticsearch.xpack.sql.capabilities.UnresolvedException; import org.elasticsearch.xpack.sql.tree.Location; - +import org.elasticsearch.xpack.sql.tree.NodeInfo; import java.util.Objects; import static java.util.Collections.emptyList; +import java.util.List; + public class UnresolvedStar extends UnresolvedNamedExpression { // typically used for nested fields or inner/dotted fields @@ -22,6 +24,16 @@ public class UnresolvedStar extends UnresolvedNamedExpression { this.qualifier = qualifier; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, UnresolvedStar::new, qualifier); + } + + @Override + public Expression replaceChildren(List newChildren) { + throw new UnsupportedOperationException("this type of node doesn't have any children to replace"); + } + @Override public boolean nullable() { throw new UnresolvedException("nullable", this); @@ -33,17 +45,24 @@ public class UnresolvedStar extends UnresolvedNamedExpression { @Override public int hashCode() { - return Objects.hash(super.hashCode(), qualifier); + return Objects.hash(qualifier); } @Override public boolean equals(Object obj) { - if (super.equals(obj)) { - UnresolvedStar other = (UnresolvedStar) obj; - return Objects.equals(qualifier, other.qualifier); + /* + * Intentionally not calling the superclass + * equals because it uses id which we always + * mutate when we make a clone. So we need + * to ignore it in equals for the transform + * tests to pass. + */ + if (obj == null || obj.getClass() != getClass()) { + return false; } - return false; + UnresolvedStar other = (UnresolvedStar) obj; + return Objects.equals(qualifier, other.qualifier); } private String message() { diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/Score.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/Score.java index c1ab18c4d92..16e86e5ed4e 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/Score.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/Score.java @@ -6,13 +6,17 @@ package org.elasticsearch.xpack.sql.expression.function; import org.elasticsearch.xpack.sql.expression.Attribute; +import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.Function; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; import static java.util.Collections.emptyList; +import java.util.List; + /** * Function referring to the {@code _score} in a search. Only available * in the search context, and only at the "root" so it can't be combined @@ -23,6 +27,16 @@ public class Score extends Function { super(location, emptyList()); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this); + } + + @Override + public Expression replaceChildren(List newChildren) { + throw new UnsupportedOperationException("this type of node doesn't have any children to replace"); + } + @Override public DataType dataType() { return DataTypes.FLOAT; @@ -32,4 +46,18 @@ public class Score extends Function { public Attribute toAttribute() { return new ScoreAttribute(location()); } + + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != getClass()) { + return false; + } + Score other = (Score) obj; + return location().equals(other.location()); + } + + @Override + public int hashCode() { + return location().hashCode(); + } } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/ScoreAttribute.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/ScoreAttribute.java index 73a9b3b18f6..fd2ea288f47 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/ScoreAttribute.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/ScoreAttribute.java @@ -9,6 +9,7 @@ import org.elasticsearch.xpack.sql.expression.Attribute; import org.elasticsearch.xpack.sql.expression.ExpressionId; import org.elasticsearch.xpack.sql.expression.function.FunctionAttribute; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; @@ -19,7 +20,7 @@ public class ScoreAttribute extends FunctionAttribute { /** * Constructor for normal use. */ - ScoreAttribute(Location location) { + public ScoreAttribute(Location location) { this(location, "SCORE()", DataTypes.FLOAT, null, false, null, false); } @@ -31,6 +32,11 @@ public class ScoreAttribute extends FunctionAttribute { super(location, name, dataType, qualifier, nullable, id, synthetic, "SCORE"); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this); + } + @Override protected Attribute clone(Location location, String name, DataType dataType, String qualifier, boolean nullable, ExpressionId id, boolean synthetic) { diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/UnresolvedFunction.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/UnresolvedFunction.java index aed571f7534..91dbc5f1e41 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/UnresolvedFunction.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/UnresolvedFunction.java @@ -10,10 +10,12 @@ import org.elasticsearch.xpack.sql.capabilities.UnresolvedException; import org.elasticsearch.xpack.sql.expression.Attribute; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.util.CollectionUtils; import java.util.List; +import java.util.Objects; public class UnresolvedFunction extends Function implements Unresolvable { @@ -44,6 +46,17 @@ public class UnresolvedFunction extends Function implements Unresolvable { this.unresolvedMsg = unresolvedMessage == null ? errorMessage(name, null) : unresolvedMessage; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, UnresolvedFunction::new, + name, distinct, children(), analyzed, unresolvedMsg); + } + + @Override + public Expression replaceChildren(List newChildren) { + return new UnresolvedFunction(location(), name, distinct, newChildren, analyzed, unresolvedMsg); + } + @Override public boolean resolved() { return false; @@ -92,6 +105,24 @@ public class UnresolvedFunction extends Function implements Unresolvable { return UNRESOLVED_PREFIX + functionName() + functionArgs(); } + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != getClass()) { + return false; + } + UnresolvedFunction other = (UnresolvedFunction) obj; + return name.equals(other.name) + && distinct == other.distinct + && children().equals(other.children()) + && analyzed == other.analyzed + && unresolvedMsg.equals(other.unresolvedMsg); + } + + @Override + public int hashCode() { + return Objects.hash(name, distinct, children(), analyzed, unresolvedMsg); + } + public static String errorMessage(String name, List potentialMatches) { String msg = "Unknown function [" + name + "]"; if (!CollectionUtils.isEmpty(potentialMatches)) { diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/AggregateFunction.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/AggregateFunction.java index bf82e5f11b0..aeadc724707 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/AggregateFunction.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/AggregateFunction.java @@ -11,6 +11,7 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.util.CollectionUtils; import java.util.List; +import java.util.Objects; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; @@ -48,4 +49,19 @@ public abstract class AggregateFunction extends Function { } return lazyAttribute; } + + @Override + public boolean equals(Object obj) { + if (false == super.equals(obj)) { + return false; + } + AggregateFunction other = (AggregateFunction) obj; + return Objects.equals(other.field(), field()) + && Objects.equals(other.parameters(), parameters()); + } + + @Override + public int hashCode() { + return Objects.hash(field(), parameters()); + } } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/AggregateFunctionAttribute.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/AggregateFunctionAttribute.java index e9a6aa5f35c..55400a888e3 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/AggregateFunctionAttribute.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/AggregateFunctionAttribute.java @@ -10,6 +10,7 @@ import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.ExpressionId; import org.elasticsearch.xpack.sql.expression.function.FunctionAttribute; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import java.util.Objects; @@ -23,12 +24,18 @@ public class AggregateFunctionAttribute extends FunctionAttribute { this(location, name, dataType, null, false, id, false, functionId, propertyPath); } - AggregateFunctionAttribute(Location location, String name, DataType dataType, String qualifier, + public AggregateFunctionAttribute(Location location, String name, DataType dataType, String qualifier, boolean nullable, ExpressionId id, boolean synthetic, String functionId, String propertyPath) { super(location, name, dataType, qualifier, nullable, id, synthetic, functionId); this.propertyPath = propertyPath; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, AggregateFunctionAttribute::new, + name(), dataType(), qualifier(), nullable(), id(), synthetic(), functionId(), propertyPath); + } + public String propertyPath() { return propertyPath; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Avg.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Avg.java index 8c63dcdebf2..7298f8eb767 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Avg.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Avg.java @@ -5,8 +5,11 @@ */ package org.elasticsearch.xpack.sql.expression.function.aggregate; +import java.util.List; + import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; public class Avg extends NumericAggregate implements EnclosedAgg { @@ -15,6 +18,19 @@ public class Avg extends NumericAggregate implements EnclosedAgg { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Avg::new, field()); + } + + @Override + public Avg replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return new Avg(location(), newChildren.get(0)); + } + @Override public String innerName() { return "avg"; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Correlation.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Correlation.java index a49d6fa6978..ef8eaa9a25a 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Correlation.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Correlation.java @@ -5,8 +5,10 @@ */ package org.elasticsearch.xpack.sql.expression.function.aggregate; +import java.util.List; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class Correlation extends NumericAggregate implements MatrixStatsEnclosed { @@ -14,6 +16,19 @@ public class Correlation extends NumericAggregate implements MatrixStatsEnclosed super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Correlation::new, field()); + } + + @Override + public Correlation replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return new Correlation(location(), newChildren.get(0)); + } + @Override public String innerName() { return "correlation"; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Count.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Count.java index 0b76c54a103..c8026180c5c 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Count.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Count.java @@ -5,9 +5,12 @@ */ package org.elasticsearch.xpack.sql.expression.function.aggregate; +import java.util.List; + import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.NamedExpression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; @@ -20,6 +23,19 @@ public class Count extends AggregateFunction { this.distinct = distinct; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Count::new, field(), distinct); + } + + @Override + public Count replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return new Count(location(), newChildren.get(0), distinct); + } + public boolean distinct() { return distinct; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Covariance.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Covariance.java index bd8d5f83ccb..4f8c279d430 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Covariance.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Covariance.java @@ -5,8 +5,10 @@ */ package org.elasticsearch.xpack.sql.expression.function.aggregate; +import java.util.List; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class Covariance extends NumericAggregate implements MatrixStatsEnclosed { @@ -14,6 +16,19 @@ public class Covariance extends NumericAggregate implements MatrixStatsEnclosed super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Covariance::new, field()); + } + + @Override + public Covariance replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return new Covariance(location(), newChildren.get(0)); + } + @Override public String innerName() { return "covariance"; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/ExtendedStats.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/ExtendedStats.java index aded773608a..3f0555cd577 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/ExtendedStats.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/ExtendedStats.java @@ -5,12 +5,27 @@ */ package org.elasticsearch.xpack.sql.expression.function.aggregate; +import java.util.List; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class ExtendedStats extends CompoundNumericAggregate { public ExtendedStats(Location location, Expression field) { super(location, field); } + + @Override + protected NodeInfo info() { + return NodeInfo.create(this, ExtendedStats::new, field()); + } + + @Override + public ExtendedStats replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return new ExtendedStats(location(), newChildren.get(0)); + } } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/InnerAggregate.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/InnerAggregate.java index 041b10f7754..7ab8a703cab 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/InnerAggregate.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/InnerAggregate.java @@ -5,9 +5,12 @@ */ package org.elasticsearch.xpack.sql.expression.function.aggregate; +import java.util.List; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.Function; import org.elasticsearch.xpack.sql.querydsl.agg.AggPath; +import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; public class InnerAggregate extends AggregateFunction { @@ -19,17 +22,31 @@ public class InnerAggregate extends AggregateFunction { private final Expression innerKey; public InnerAggregate(AggregateFunction inner, CompoundNumericAggregate outer) { - this(inner, outer, null); + this(inner.location(), inner, outer, null); } - public InnerAggregate(AggregateFunction inner, CompoundNumericAggregate outer, Expression innerKey) { - super(inner.location(), outer.field(), outer.arguments()); + public InnerAggregate(Location location, AggregateFunction inner, CompoundNumericAggregate outer, Expression innerKey) { + super(location, outer.field(), outer.arguments()); this.inner = inner; this.outer = outer; this.innerId = ((EnclosedAgg) inner).innerName(); this.innerKey = innerKey; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, InnerAggregate::new, inner, outer, innerKey); + } + + @Override + public Expression replaceChildren(List newChildren) { + /* I can't figure out how rewriting this one's children ever worked because its + * are all twisted up in `outer`. Refusing to rewrite it doesn't break anything + * that I can see right now so lets just go with it and hope for the best. + * Maybe someone will make this make sense one day! */ + throw new UnsupportedOperationException("can't be rewritten"); + } + public AggregateFunction inner() { return inner; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Kurtosis.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Kurtosis.java index ef7c280324a..5d46fa68f2b 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Kurtosis.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Kurtosis.java @@ -5,8 +5,10 @@ */ package org.elasticsearch.xpack.sql.expression.function.aggregate; +import java.util.List; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class Kurtosis extends NumericAggregate implements MatrixStatsEnclosed { @@ -14,6 +16,19 @@ public class Kurtosis extends NumericAggregate implements MatrixStatsEnclosed { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Kurtosis::new, field()); + } + + @Override + public Kurtosis replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return new Kurtosis(location(), newChildren.get(0)); + } + @Override public String innerName() { return "kurtosis"; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/MatrixCount.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/MatrixCount.java index e84cabf8d04..7627802f0be 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/MatrixCount.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/MatrixCount.java @@ -5,8 +5,10 @@ */ package org.elasticsearch.xpack.sql.expression.function.aggregate; +import java.util.List; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class MatrixCount extends NumericAggregate implements MatrixStatsEnclosed { @@ -14,6 +16,19 @@ public class MatrixCount extends NumericAggregate implements MatrixStatsEnclosed super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, MatrixCount::new, field()); + } + + @Override + public MatrixCount replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return new MatrixCount(location(), newChildren.get(0)); + } + @Override public String innerName() { return "matrix_count"; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/MatrixMean.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/MatrixMean.java index 7fe9be31a14..f0b92997932 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/MatrixMean.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/MatrixMean.java @@ -5,8 +5,10 @@ */ package org.elasticsearch.xpack.sql.expression.function.aggregate; +import java.util.List; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class MatrixMean extends NumericAggregate implements MatrixStatsEnclosed { @@ -14,6 +16,16 @@ public class MatrixMean extends NumericAggregate implements MatrixStatsEnclosed super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, MatrixMean::new, field()); + } + + @Override + public MatrixMean replaceChildren(List newChildren) { + return new MatrixMean(location(), newChildren.get(0)); + } + @Override public String innerName() { return "matrix_mean"; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/MatrixStats.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/MatrixStats.java index 9f5c5750c99..fa7697b55c3 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/MatrixStats.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/MatrixStats.java @@ -5,12 +5,27 @@ */ package org.elasticsearch.xpack.sql.expression.function.aggregate; +import java.util.List; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class MatrixStats extends CompoundNumericAggregate { public MatrixStats(Location location, Expression field) { super(location, field); } + + @Override + protected NodeInfo info() { + return NodeInfo.create(this, MatrixStats::new, field()); + } + + @Override + public MatrixStats replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return new MatrixStats(location(), newChildren.get(0)); + } } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/MatrixVariance.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/MatrixVariance.java index 549317da58f..ae444f80dda 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/MatrixVariance.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/MatrixVariance.java @@ -5,8 +5,10 @@ */ package org.elasticsearch.xpack.sql.expression.function.aggregate; +import java.util.List; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class MatrixVariance extends NumericAggregate implements MatrixStatsEnclosed { @@ -14,6 +16,19 @@ public class MatrixVariance extends NumericAggregate implements MatrixStatsEnclo super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, MatrixVariance::new, field()); + } + + @Override + public MatrixVariance replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return new MatrixVariance(location(), newChildren.get(0)); + } + @Override public String innerName() { return "matrix_variance"; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Max.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Max.java index 011087e35f7..0578216979d 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Max.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Max.java @@ -5,8 +5,10 @@ */ package org.elasticsearch.xpack.sql.expression.function.aggregate; +import java.util.List; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; public class Max extends NumericAggregate implements EnclosedAgg { @@ -15,6 +17,16 @@ public class Max extends NumericAggregate implements EnclosedAgg { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Max::new, field()); + } + + @Override + public Max replaceChildren(List newChildren) { + return new Max(location(), newChildren.get(0)); + } + @Override public DataType dataType() { return field().dataType(); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Mean.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Mean.java index 2c1755fe546..4181c85e90c 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Mean.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Mean.java @@ -5,8 +5,10 @@ */ package org.elasticsearch.xpack.sql.expression.function.aggregate; +import java.util.List; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; @@ -16,6 +18,19 @@ public class Mean extends NumericAggregate implements MatrixStatsEnclosed { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Mean::new, field()); + } + + @Override + public Mean replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return new Mean(location(), newChildren.get(0)); + } + @Override public DataType dataType() { return DataTypes.DOUBLE; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Min.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Min.java index 2e098da9a07..ec6e60f3b1f 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Min.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Min.java @@ -5,8 +5,10 @@ */ package org.elasticsearch.xpack.sql.expression.function.aggregate; +import java.util.List; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; public class Min extends NumericAggregate implements EnclosedAgg { @@ -15,6 +17,19 @@ public class Min extends NumericAggregate implements EnclosedAgg { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Min::new, field()); + } + + @Override + public Min replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return new Min(location(), newChildren.get(0)); + } + @Override public DataType dataType() { return field().dataType(); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/NumericAggregate.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/NumericAggregate.java index 09306c35a07..5e5c081a75f 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/NumericAggregate.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/NumericAggregate.java @@ -13,7 +13,7 @@ import org.elasticsearch.xpack.sql.type.DataTypes; import java.util.List; -class NumericAggregate extends AggregateFunction { +abstract class NumericAggregate extends AggregateFunction { NumericAggregate(Location location, Expression field, List parameters) { super(location, field, parameters); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Percentile.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Percentile.java index 07083bf2d3e..1ef911e60ab 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Percentile.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Percentile.java @@ -9,9 +9,12 @@ import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.Expressions; import org.elasticsearch.xpack.sql.expression.Foldables; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; +import java.util.List; + import static java.util.Collections.singletonList; public class Percentile extends NumericAggregate implements EnclosedAgg { @@ -23,6 +26,19 @@ public class Percentile extends NumericAggregate implements EnclosedAgg { this.percent = percent; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Percentile::new, field(), percent); + } + + @Override + public Percentile replaceChildren(List newChildren) { + if (newChildren.size() != 2) { + throw new IllegalArgumentException("expected [2] children but received [" + newChildren.size() + "]"); + } + return new Percentile(location(), newChildren.get(0), newChildren.get(1)); + } + @Override protected TypeResolution resolveType() { TypeResolution resolution = super.resolveType(); @@ -47,4 +63,4 @@ public class Percentile extends NumericAggregate implements EnclosedAgg { public String innerName() { return "[" + Double.toString(Foldables.doubleValueOf(percent)) + "]"; } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/PercentileRank.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/PercentileRank.java index ccdd8333a02..319afa5f880 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/PercentileRank.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/PercentileRank.java @@ -9,9 +9,12 @@ import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.Expressions; import org.elasticsearch.xpack.sql.expression.Foldables; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; +import java.util.List; + import static java.util.Collections.singletonList; public class PercentileRank extends AggregateFunction implements EnclosedAgg { @@ -23,6 +26,19 @@ public class PercentileRank extends AggregateFunction implements EnclosedAgg { this.value = value; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, PercentileRank::new, field(), value); + } + + @Override + public Expression replaceChildren(List newChildren) { + if (newChildren.size() != 2) { + throw new IllegalArgumentException("expected [2] children but received [" + newChildren.size() + "]"); + } + return new PercentileRank(location(), newChildren.get(0), newChildren.get(1)); + } + @Override protected TypeResolution resolveType() { TypeResolution resolution = super.resolveType(); @@ -47,4 +63,4 @@ public class PercentileRank extends AggregateFunction implements EnclosedAgg { public String innerName() { return "[" + Double.toString(Foldables.doubleValueOf(value)) + "]"; } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/PercentileRanks.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/PercentileRanks.java index 651a517be17..38c79ebd621 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/PercentileRanks.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/PercentileRanks.java @@ -7,7 +7,7 @@ package org.elasticsearch.xpack.sql.expression.function.aggregate; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; - +import org.elasticsearch.xpack.sql.tree.NodeInfo; import java.util.List; public class PercentileRanks extends CompoundNumericAggregate { @@ -19,7 +19,20 @@ public class PercentileRanks extends CompoundNumericAggregate { this.values = values; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, PercentileRanks::new, field(), values); + } + + @Override + public PercentileRanks replaceChildren(List newChildren) { + if (newChildren.size() < 2) { + throw new IllegalArgumentException("expected at least [2] children but received [" + newChildren.size() + "]"); + } + return new PercentileRanks(location(), newChildren.get(0), newChildren.subList(1, newChildren.size())); + } + public List values() { return values; } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Percentiles.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Percentiles.java index 876fe7fa1d6..932a887806f 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Percentiles.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Percentiles.java @@ -7,7 +7,7 @@ package org.elasticsearch.xpack.sql.expression.function.aggregate; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; - +import org.elasticsearch.xpack.sql.tree.NodeInfo; import java.util.List; public class Percentiles extends CompoundNumericAggregate { @@ -19,7 +19,20 @@ public class Percentiles extends CompoundNumericAggregate { this.percents = percents; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Percentiles::new, field(), percents); + } + + @Override + public Percentiles replaceChildren(List newChildren) { + if (newChildren.size() < 2) { + throw new IllegalArgumentException("expected more than one child but received [" + newChildren.size() + "]"); + } + return new Percentiles(location(), newChildren.get(0), newChildren.subList(1, newChildren.size())); + } + public List percents() { return percents; } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Skewness.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Skewness.java index 605812a82dc..d8514e40ede 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Skewness.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Skewness.java @@ -5,8 +5,10 @@ */ package org.elasticsearch.xpack.sql.expression.function.aggregate; +import java.util.List; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class Skewness extends NumericAggregate implements MatrixStatsEnclosed { @@ -14,6 +16,19 @@ public class Skewness extends NumericAggregate implements MatrixStatsEnclosed { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Skewness::new, field()); + } + + @Override + public Skewness replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return new Skewness(location(), newChildren.get(0)); + } + @Override public String innerName() { return "skewness"; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Stats.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Stats.java index 87f1f70fc5d..3bfddd9374e 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Stats.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Stats.java @@ -5,8 +5,10 @@ */ package org.elasticsearch.xpack.sql.expression.function.aggregate; +import java.util.List; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class Stats extends CompoundNumericAggregate { @@ -14,6 +16,19 @@ public class Stats extends CompoundNumericAggregate { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Stats::new, field()); + } + + @Override + public Stats replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return new Stats(location(), newChildren.get(0)); + } + public static boolean isTypeCompatible(Expression e) { return e instanceof Min || e instanceof Max || e instanceof Avg || e instanceof Sum; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/StddevPop.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/StddevPop.java index a9eee0b6dae..acdfaecf556 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/StddevPop.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/StddevPop.java @@ -5,8 +5,10 @@ */ package org.elasticsearch.xpack.sql.expression.function.aggregate; +import java.util.List; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class StddevPop extends NumericAggregate implements ExtendedStatsEnclosed { @@ -14,6 +16,19 @@ public class StddevPop extends NumericAggregate implements ExtendedStatsEnclosed super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, StddevPop::new, field()); + } + + @Override + public StddevPop replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return new StddevPop(location(), newChildren.get(0)); + } + @Override public String innerName() { return "std_deviation"; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Sum.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Sum.java index 3b6becd0236..9fed861c947 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Sum.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Sum.java @@ -5,8 +5,10 @@ */ package org.elasticsearch.xpack.sql.expression.function.aggregate; +import java.util.List; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; public class Sum extends NumericAggregate implements EnclosedAgg { @@ -15,6 +17,19 @@ public class Sum extends NumericAggregate implements EnclosedAgg { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Sum::new, field()); + } + + @Override + public Sum replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return new Sum(location(), newChildren.get(0)); + } + @Override public DataType dataType() { return field().dataType(); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/SumOfSquares.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/SumOfSquares.java index ecf0bacc840..a52c279e83a 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/SumOfSquares.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/SumOfSquares.java @@ -5,8 +5,10 @@ */ package org.elasticsearch.xpack.sql.expression.function.aggregate; +import java.util.List; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class SumOfSquares extends NumericAggregate implements ExtendedStatsEnclosed { @@ -14,6 +16,19 @@ public class SumOfSquares extends NumericAggregate implements ExtendedStatsEnclo super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, SumOfSquares::new, field()); + } + + @Override + public SumOfSquares replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return new SumOfSquares(location(), newChildren.get(0)); + } + @Override public String innerName() { return "sum_of_squares"; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/VarPop.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/VarPop.java index 546e2df4bf1..0acfeba279d 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/VarPop.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/VarPop.java @@ -5,8 +5,10 @@ */ package org.elasticsearch.xpack.sql.expression.function.aggregate; +import java.util.List; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class VarPop extends NumericAggregate implements ExtendedStatsEnclosed { @@ -14,6 +16,19 @@ public class VarPop extends NumericAggregate implements ExtendedStatsEnclosed { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, VarPop::new, field()); + } + + @Override + public VarPop replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return new VarPop(location(), newChildren.get(0)); + } + @Override public String innerName() { return "variance"; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/BinaryScalarFunction.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/BinaryScalarFunction.java index 9395bd336a6..4e2882d46c1 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/BinaryScalarFunction.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/BinaryScalarFunction.java @@ -10,6 +10,7 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.script.ScriptTempl import org.elasticsearch.xpack.sql.tree.Location; import java.util.Arrays; +import java.util.List; public abstract class BinaryScalarFunction extends ScalarFunction { @@ -21,6 +22,16 @@ public abstract class BinaryScalarFunction extends ScalarFunction { this.right = right; } + @Override + public final BinaryScalarFunction replaceChildren(List newChildren) { + if (newChildren.size() != 2) { + throw new IllegalArgumentException("expected [2] children but received [" + newChildren.size() + "]"); + } + return replaceChildren(newChildren.get(0), newChildren.get(1)); + } + + protected abstract BinaryScalarFunction replaceChildren(Expression newLeft, Expression newRight); + public Expression left() { return left; } @@ -43,4 +54,4 @@ public abstract class BinaryScalarFunction extends ScalarFunction { } protected abstract ScriptTemplate asScriptFrom(ScriptTemplate leftScript, ScriptTemplate rightScript); -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/Cast.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/Cast.java index 5c8fe31b626..4d68ad57cf9 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/Cast.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/Cast.java @@ -13,6 +13,7 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definiti import org.elasticsearch.xpack.sql.expression.function.scalar.script.Params; import org.elasticsearch.xpack.sql.expression.function.scalar.script.ScriptTemplate; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypeConversion; import org.elasticsearch.xpack.sql.type.DataTypes; @@ -20,7 +21,6 @@ import org.elasticsearch.xpack.sql.type.DataTypes; import java.util.Objects; public class Cast extends UnaryScalarFunction { - private final DataType dataType; public Cast(Location location, Expression field, DataType dataType) { @@ -28,6 +28,16 @@ public class Cast extends UnaryScalarFunction { this.dataType = dataType; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Cast::new, field(), dataType); + } + + @Override + protected UnaryScalarFunction replaceChild(Expression newChild) { + return new Cast(location(), newChild, dataType); + } + public DataType from() { return field().dataType(); } @@ -75,7 +85,7 @@ public class Cast extends UnaryScalarFunction { @Override protected ProcessorDefinition makeProcessorDefinition() { - return new UnaryProcessorDefinition(this, ProcessorDefinitions.toProcessorDefinition(field()), + return new UnaryProcessorDefinition(location(), this, ProcessorDefinitions.toProcessorDefinition(field()), new CastProcessor(DataTypeConversion.conversionFor(from(), to()))); } @@ -86,7 +96,15 @@ public class Cast extends UnaryScalarFunction { @Override public boolean equals(Object obj) { - return super.equals(obj) && Objects.equals(dataType, ((Cast) obj).dataType()); + if (this == obj) { + return true; + } + if (obj == null || obj.getClass() != getClass()) { + return false; + } + Cast other = (Cast) obj; + return Objects.equals(dataType, other.dataType()) + && Objects.equals(field(), other.field()); } @Override diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/ScalarFunctionAttribute.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/ScalarFunctionAttribute.java index ceb39edeb82..ebcddd209cb 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/ScalarFunctionAttribute.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/ScalarFunctionAttribute.java @@ -8,10 +8,12 @@ package org.elasticsearch.xpack.sql.expression.function.scalar; import org.elasticsearch.xpack.sql.expression.Attribute; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.ExpressionId; +import org.elasticsearch.xpack.sql.expression.NamedExpression; import org.elasticsearch.xpack.sql.expression.function.FunctionAttribute; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.ProcessorDefinition; import org.elasticsearch.xpack.sql.expression.function.scalar.script.ScriptTemplate; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import java.util.Objects; @@ -27,15 +29,23 @@ public class ScalarFunctionAttribute extends FunctionAttribute { this(location, name, dataType, null, true, id, false, functionId, script, orderBy, processorDef); } - ScalarFunctionAttribute(Location location, String name, DataType dataType, String qualifier, + public ScalarFunctionAttribute(Location location, String name, DataType dataType, String qualifier, boolean nullable, ExpressionId id, boolean synthetic, String functionId, ScriptTemplate script, Expression orderBy, ProcessorDefinition processorDef) { super(location, name, dataType, qualifier, nullable, id, synthetic, functionId); + this.script = script; this.orderBy = orderBy; this.processorDef = processorDef; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, ScalarFunctionAttribute::new, + name(), dataType(), qualifier(), nullable(), id(), synthetic(), + functionId(), script, orderBy, processorDef); + } + public ScriptTemplate script() { return script; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/UnaryScalarFunction.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/UnaryScalarFunction.java index b974b50e217..e4a0953c115 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/UnaryScalarFunction.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/UnaryScalarFunction.java @@ -11,6 +11,8 @@ import org.elasticsearch.xpack.sql.tree.Location; import static java.util.Collections.singletonList; +import java.util.List; + public abstract class UnaryScalarFunction extends ScalarFunction { private final Expression field; @@ -25,6 +27,15 @@ public abstract class UnaryScalarFunction extends ScalarFunction { this.field = field; } + @Override + public final UnaryScalarFunction replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return replaceChild(newChildren.get(0)); + } + protected abstract UnaryScalarFunction replaceChild(Expression newChild); + public Expression field() { return field; } @@ -38,4 +49,4 @@ public abstract class UnaryScalarFunction extends ScalarFunction { public ScriptTemplate asScript() { return asScript(field); } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/Add.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/Add.java index 16d91276905..8cb1cc63455 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/Add.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/Add.java @@ -8,16 +8,26 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.arithmetic; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.arithmetic.BinaryArithmeticProcessor.BinaryArithmeticOperation; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; /** * Addition function ({@code a + b}). */ public class Add extends ArithmeticFunction { - public Add(Location location, Expression left, Expression right) { super(location, left, right, BinaryArithmeticOperation.ADD); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Add::new, left(), right()); + } + + @Override + protected Add replaceChildren(Expression left, Expression right) { + return new Add(location(), left, right); + } + @Override public Number fold() { return Arithmetics.add((Number) left().fold(), (Number) right().fold()); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/ArithmeticFunction.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/ArithmeticFunction.java index b7cf36dd756..a42f564d019 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/ArithmeticFunction.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/ArithmeticFunction.java @@ -16,6 +16,7 @@ import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypeConversion; import java.util.Locale; +import java.util.Objects; import static java.lang.String.format; import static org.elasticsearch.xpack.sql.expression.function.scalar.script.ParamsBuilder.paramsBuilder; @@ -74,7 +75,7 @@ public abstract class ArithmeticFunction extends BinaryScalarFunction { @Override protected final BinaryArithmeticProcessorDefinition makeProcessorDefinition() { - return new BinaryArithmeticProcessorDefinition(this, + return new BinaryArithmeticProcessorDefinition(location(), this, ProcessorDefinitions.toProcessorDefinition(left()), ProcessorDefinitions.toProcessorDefinition(right()), operation); } @@ -110,4 +111,20 @@ public abstract class ArithmeticFunction extends BinaryScalarFunction { protected boolean useParanthesis() { return !(left() instanceof Literal) || !(right() instanceof Literal); } + + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != getClass()) { + return false; + } + ArithmeticFunction other = (ArithmeticFunction) obj; + return Objects.equals(other.left(), left()) + && Objects.equals(other.right(), right()) + && Objects.equals(other.operation, operation); + } + + @Override + public int hashCode() { + return Objects.hash(left(), right(), operation); + } } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/BinaryArithmeticProcessorDefinition.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/BinaryArithmeticProcessorDefinition.java index 70afe2e8f58..b94a726290e 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/BinaryArithmeticProcessorDefinition.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/BinaryArithmeticProcessorDefinition.java @@ -9,26 +9,33 @@ import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.arithmetic.BinaryArithmeticProcessor.BinaryArithmeticOperation; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.BinaryProcessorDefinition; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.ProcessorDefinition; - +import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import java.util.Objects; public class BinaryArithmeticProcessorDefinition extends BinaryProcessorDefinition { private final BinaryArithmeticOperation operation; - public BinaryArithmeticProcessorDefinition(Expression expression, ProcessorDefinition left, + public BinaryArithmeticProcessorDefinition(Location location, Expression expression, ProcessorDefinition left, ProcessorDefinition right, BinaryArithmeticOperation operation) { - super(expression, left, right); + super(location, expression, left, right); this.operation = operation; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, BinaryArithmeticProcessorDefinition::new, + expression(), left(), right(), operation); + } + public BinaryArithmeticOperation operation() { return operation; } @Override protected BinaryProcessorDefinition replaceChildren(ProcessorDefinition left, ProcessorDefinition right) { - return new BinaryArithmeticProcessorDefinition(expression(), left, right, operation); + return new BinaryArithmeticProcessorDefinition(location(), expression(), left, right, operation); } @Override diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/Div.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/Div.java index 59d079b4e9b..6d6410aaedf 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/Div.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/Div.java @@ -6,8 +6,10 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.arithmetic; import org.elasticsearch.xpack.sql.expression.Expression; +import org.elasticsearch.xpack.sql.expression.function.scalar.BinaryScalarFunction; import org.elasticsearch.xpack.sql.expression.function.scalar.arithmetic.BinaryArithmeticProcessor.BinaryArithmeticOperation; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypeConversion; @@ -20,6 +22,16 @@ public class Div extends ArithmeticFunction { super(location, left, right, BinaryArithmeticOperation.DIV); } + @Override + protected NodeInfo
info() { + return NodeInfo.create(this, Div::new, left(), right()); + } + + @Override + protected BinaryScalarFunction replaceChildren(Expression newLeft, Expression newRight) { + return new Div(location(), newLeft, newRight); + } + @Override public Object fold() { return Arithmetics.div((Number) left().fold(), (Number) right().fold()); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/Mod.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/Mod.java index 06530e42726..b7cea1ced67 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/Mod.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/Mod.java @@ -6,8 +6,10 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.arithmetic; import org.elasticsearch.xpack.sql.expression.Expression; +import org.elasticsearch.xpack.sql.expression.function.scalar.BinaryScalarFunction; import org.elasticsearch.xpack.sql.expression.function.scalar.arithmetic.BinaryArithmeticProcessor.BinaryArithmeticOperation; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; /** * Modulo @@ -19,6 +21,16 @@ public class Mod extends ArithmeticFunction { super(location, left, right, BinaryArithmeticOperation.MOD); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Mod::new, left(), right()); + } + + @Override + protected BinaryScalarFunction replaceChildren(Expression newLeft, Expression newRight) { + return new Mod(location(), newLeft, newRight); + } + @Override public Object fold() { return Arithmetics.mod((Number) left().fold(), (Number) right().fold()); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/Mul.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/Mul.java index d255466edcd..20394cb55a5 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/Mul.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/Mul.java @@ -6,8 +6,10 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.arithmetic; import org.elasticsearch.xpack.sql.expression.Expression; +import org.elasticsearch.xpack.sql.expression.function.scalar.BinaryScalarFunction; import org.elasticsearch.xpack.sql.expression.function.scalar.arithmetic.BinaryArithmeticProcessor.BinaryArithmeticOperation; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; /** * Multiplication function ({@code a * b}). @@ -18,6 +20,16 @@ public class Mul extends ArithmeticFunction { super(location, left, right, BinaryArithmeticOperation.MUL); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Mul::new, left(), right()); + } + + @Override + protected BinaryScalarFunction replaceChildren(Expression newLeft, Expression newRight) { + return new Mul(location(), newLeft, newRight); + } + @Override public Object fold() { return Arithmetics.mul((Number) left().fold(), (Number) right().fold()); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/Neg.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/Neg.java index 4f47d0d41a2..6b71ca6d9f1 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/Neg.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/Neg.java @@ -13,17 +13,27 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definiti import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.ProcessorDefinitions; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.UnaryProcessorDefinition; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; /** * Negation function (@{code -x}). */ public class Neg extends UnaryScalarFunction { - public Neg(Location location, Expression field) { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Neg::new, field()); + } + + @Override + protected UnaryScalarFunction replaceChild(Expression newChild) { + return new Neg(location(), newChild); + } + @Override protected TypeResolution resolveType() { return Expressions.typeMustBeNumeric(field()); @@ -47,7 +57,7 @@ public class Neg extends UnaryScalarFunction { @Override protected ProcessorDefinition makeProcessorDefinition() { - return new UnaryProcessorDefinition(this, ProcessorDefinitions.toProcessorDefinition(field()), + return new UnaryProcessorDefinition(location(), this, ProcessorDefinitions.toProcessorDefinition(field()), new UnaryArithmeticProcessor(UnaryArithmeticOperation.NEGATE)); } } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/Sub.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/Sub.java index 18867f2f9d7..892d886034e 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/Sub.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/arithmetic/Sub.java @@ -6,8 +6,10 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.arithmetic; import org.elasticsearch.xpack.sql.expression.Expression; +import org.elasticsearch.xpack.sql.expression.function.scalar.BinaryScalarFunction; import org.elasticsearch.xpack.sql.expression.function.scalar.arithmetic.BinaryArithmeticProcessor.BinaryArithmeticOperation; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; /** * Subtraction function ({@code a - b}). @@ -18,6 +20,16 @@ public class Sub extends ArithmeticFunction { super(location, left, right, BinaryArithmeticOperation.SUB); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Sub::new, left(), right()); + } + + @Override + protected BinaryScalarFunction replaceChildren(Expression newLeft, Expression newRight) { + return new Sub(location(), newLeft, newRight); + } + @Override public Object fold() { return Arithmetics.sub((Number) left().fold(), (Number) right().fold()); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeFunction.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeFunction.java index 302defd6017..360b06c9566 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeFunction.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeFunction.java @@ -17,11 +17,13 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definiti import org.elasticsearch.xpack.sql.expression.function.scalar.script.ParamsBuilder; import org.elasticsearch.xpack.sql.expression.function.scalar.script.ScriptTemplate; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; import org.joda.time.DateTimeZone; import java.time.temporal.ChronoField; +import java.util.Objects; import static org.elasticsearch.xpack.sql.expression.function.scalar.script.ParamsBuilder.paramsBuilder; import static org.elasticsearch.xpack.sql.expression.function.scalar.script.ScriptTemplate.formatTemplate; @@ -42,6 +44,12 @@ public abstract class DateTimeFunction extends UnaryScalarFunction { this.name = sb.toString(); } + @Override + protected final NodeInfo info() { + return NodeInfo.create(this, ctorForInfo(), field(), timeZone()); + } + protected abstract NodeInfo.NodeCtor2 ctorForInfo(); + public DateTimeZone timeZone() { return timeZone; } @@ -112,7 +120,7 @@ public abstract class DateTimeFunction extends UnaryScalarFunction { @Override protected final ProcessorDefinition makeProcessorDefinition() { - return new UnaryProcessorDefinition(this, ProcessorDefinitions.toProcessorDefinition(field()), + return new UnaryProcessorDefinition(location(), this, ProcessorDefinitions.toProcessorDefinition(field()), new DateTimeProcessor(extractor(), timeZone)); } @@ -131,4 +139,19 @@ public abstract class DateTimeFunction extends UnaryScalarFunction { public String name() { return name; } + + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != getClass()) { + return false; + } + DateTimeFunction other = (DateTimeFunction) obj; + return Objects.equals(other.field(), field()) + && Objects.equals(other.timeZone, timeZone); + } + + @Override + public int hashCode() { + return Objects.hash(field(), timeZone); + } } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfMonth.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfMonth.java index db74265fff5..03fb7907464 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfMonth.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfMonth.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.datetime; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeProcessor.DateTimeExtractor; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; import org.joda.time.DateTimeZone; import java.time.temporal.ChronoField; @@ -17,6 +18,16 @@ public class DayOfMonth extends DateTimeFunction { super(location, field, timeZone); } + @Override + protected NodeCtor2 ctorForInfo() { + return DayOfMonth::new; + } + + @Override + protected DayOfMonth replaceChild(Expression newChild) { + return new DayOfMonth(location(), newChild, timeZone()); + } + @Override public String dateTimeFormat() { return "d"; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfWeek.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfWeek.java index 4753b94f8f8..ad0a0e20b77 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfWeek.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfWeek.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.datetime; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeProcessor.DateTimeExtractor; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; import org.joda.time.DateTimeZone; import java.time.temporal.ChronoField; @@ -17,6 +18,16 @@ public class DayOfWeek extends DateTimeFunction { super(location, field, timeZone); } + @Override + protected NodeCtor2 ctorForInfo() { + return DayOfWeek::new; + } + + @Override + protected DayOfWeek replaceChild(Expression newChild) { + return new DayOfWeek(location(), newChild, timeZone()); + } + @Override public String dateTimeFormat() { return "e"; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfYear.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfYear.java index 3891af67a27..95816a80391 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfYear.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DayOfYear.java @@ -6,8 +6,10 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.datetime; import org.elasticsearch.xpack.sql.expression.Expression; +import org.elasticsearch.xpack.sql.expression.function.scalar.UnaryScalarFunction; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeProcessor.DateTimeExtractor; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; import org.joda.time.DateTimeZone; import java.time.temporal.ChronoField; @@ -17,6 +19,16 @@ public class DayOfYear extends DateTimeFunction { super(location, field, timeZone); } + @Override + protected NodeCtor2 ctorForInfo() { + return DayOfYear::new; + } + + @Override + protected UnaryScalarFunction replaceChild(Expression newChild) { + return new DayOfYear(location(), newChild, timeZone()); + } + @Override public String dateTimeFormat() { return "D"; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/HourOfDay.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/HourOfDay.java index 5dd4f7d0a9d..740c75bf3b3 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/HourOfDay.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/HourOfDay.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.datetime; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeProcessor.DateTimeExtractor; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; import org.joda.time.DateTimeZone; import java.time.temporal.ChronoField; @@ -17,6 +18,16 @@ public class HourOfDay extends DateTimeFunction { super(location, field, timeZone); } + @Override + protected NodeCtor2 ctorForInfo() { + return HourOfDay::new; + } + + @Override + protected HourOfDay replaceChild(Expression newChild) { + return new HourOfDay(location(), newChild, timeZone()); + } + @Override public String dateTimeFormat() { return "hour"; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/MinuteOfDay.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/MinuteOfDay.java index 2f0698e3326..85a0a250b12 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/MinuteOfDay.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/MinuteOfDay.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.datetime; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeProcessor.DateTimeExtractor; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; import org.joda.time.DateTimeZone; import java.time.temporal.ChronoField; @@ -18,6 +19,16 @@ public class MinuteOfDay extends DateTimeFunction { super(location, field, timeZone); } + @Override + protected NodeCtor2 ctorForInfo() { + return MinuteOfDay::new; + } + + @Override + protected MinuteOfDay replaceChild(Expression newChild) { + return new MinuteOfDay(location(), newChild, timeZone()); + } + @Override public String dateTimeFormat() { throw new UnsupportedOperationException("is there a format for it?"); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/MinuteOfHour.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/MinuteOfHour.java index 2c420af6456..31c4aee0cfc 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/MinuteOfHour.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/MinuteOfHour.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.datetime; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeProcessor.DateTimeExtractor; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; import org.joda.time.DateTimeZone; import java.time.temporal.ChronoField; @@ -17,6 +18,16 @@ public class MinuteOfHour extends DateTimeFunction { super(location, field, timeZone); } + @Override + protected NodeCtor2 ctorForInfo() { + return MinuteOfHour::new; + } + + @Override + protected MinuteOfHour replaceChild(Expression newChild) { + return new MinuteOfHour(location(), newChild, timeZone()); + } + @Override public String dateTimeFormat() { return "m"; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/MonthOfYear.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/MonthOfYear.java index f553bf74555..fa76530caf3 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/MonthOfYear.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/MonthOfYear.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.datetime; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeProcessor.DateTimeExtractor; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; import org.joda.time.DateTimeZone; import java.time.temporal.ChronoField; @@ -17,6 +18,16 @@ public class MonthOfYear extends DateTimeFunction { super(location, field, timeZone); } + @Override + protected NodeCtor2 ctorForInfo() { + return MonthOfYear::new; + } + + @Override + protected MonthOfYear replaceChild(Expression newChild) { + return new MonthOfYear(location(), newChild, timeZone()); + } + @Override public String dateTimeFormat() { return "M"; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/SecondOfMinute.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/SecondOfMinute.java index 83e3d5ced42..d9d3827a12d 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/SecondOfMinute.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/SecondOfMinute.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.datetime; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeProcessor.DateTimeExtractor; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; import org.joda.time.DateTimeZone; import java.time.temporal.ChronoField; @@ -17,6 +18,16 @@ public class SecondOfMinute extends DateTimeFunction { super(location, field, timeZone); } + @Override + protected NodeCtor2 ctorForInfo() { + return SecondOfMinute::new; + } + + @Override + protected SecondOfMinute replaceChild(Expression newChild) { + return new SecondOfMinute(location(), newChild, timeZone()); + } + @Override public String dateTimeFormat() { return "s"; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/WeekOfWeekYear.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/WeekOfWeekYear.java index e17a2461528..7becd0d6929 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/WeekOfWeekYear.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/WeekOfWeekYear.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.datetime; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeProcessor.DateTimeExtractor; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; import org.joda.time.DateTimeZone; import java.time.temporal.ChronoField; @@ -17,6 +18,16 @@ public class WeekOfWeekYear extends DateTimeFunction { super(location, field, timeZone); } + @Override + protected NodeCtor2 ctorForInfo() { + return WeekOfWeekYear::new; + } + + @Override + protected WeekOfWeekYear replaceChild(Expression newChild) { + return new WeekOfWeekYear(location(), newChild, timeZone()); + } + @Override public String dateTimeFormat() { return "w"; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/Year.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/Year.java index a062ead4876..b68f293892b 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/Year.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/Year.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.datetime; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeProcessor.DateTimeExtractor; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; import org.joda.time.DateTimeZone; import java.time.temporal.ChronoField; @@ -17,6 +18,16 @@ public class Year extends DateTimeHistogramFunction { super(location, field, timeZone); } + @Override + protected NodeCtor2 ctorForInfo() { + return Year::new; + } + + @Override + protected Year replaceChild(Expression newChild) { + return new Year(location(), newChild, timeZone()); + } + @Override public String dateTimeFormat() { return "year"; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/ACos.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/ACos.java index a819483b0dd..108556c91c5 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/ACos.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/ACos.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; /** @@ -19,6 +20,16 @@ public class ACos extends MathFunction { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, ACos::new, field()); + } + + @Override + protected ACos replaceChild(Expression newChild) { + return new ACos(location(), newChild); + } + @Override protected MathOperation operation() { return MathOperation.ACOS; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/ASin.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/ASin.java index beb84b30e3b..83fb5db97d9 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/ASin.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/ASin.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; /** * Arc sine @@ -18,6 +19,16 @@ public class ASin extends MathFunction { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, ASin::new, field()); + } + + @Override + protected ASin replaceChild(Expression newChild) { + return new ASin(location(), newChild); + } + @Override protected MathOperation operation() { return MathOperation.ASIN; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/ATan.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/ATan.java index 70f25cec500..7b046da97d3 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/ATan.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/ATan.java @@ -8,7 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import org.elasticsearch.xpack.sql.tree.Location; - +import org.elasticsearch.xpack.sql.tree.NodeInfo; /** * Arc tangent @@ -19,6 +19,16 @@ public class ATan extends MathFunction { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, ATan::new, field()); + } + + @Override + protected ATan replaceChild(Expression newChild) { + return new ATan(location(), newChild); + } + @Override protected MathOperation operation() { return MathOperation.ATAN; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Abs.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Abs.java index 8838fcfe9ba..def66bfe4a8 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Abs.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Abs.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; /** @@ -19,6 +20,16 @@ public class Abs extends MathFunction { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Abs::new, field()); + } + + @Override + protected Abs replaceChild(Expression newChild) { + return new Abs(location(), newChild); + } + @Override protected MathOperation operation() { return MathOperation.ABS; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Cbrt.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Cbrt.java index a72534d6969..323e343d97c 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Cbrt.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Cbrt.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; /** * Cube root @@ -18,6 +19,16 @@ public class Cbrt extends MathFunction { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Cbrt::new, field()); + } + + @Override + protected Cbrt replaceChild(Expression newChild) { + return new Cbrt(location(), newChild); + } + @Override protected MathOperation operation() { return MathOperation.CBRT; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Ceil.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Ceil.java index e49f29feea9..adbcd08ce42 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Ceil.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Ceil.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypeConversion; @@ -20,6 +21,16 @@ public class Ceil extends MathFunction { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Ceil::new, field()); + } + + @Override + protected Ceil replaceChild(Expression newChild) { + return new Ceil(location(), newChild); + } + @Override protected MathOperation operation() { return MathOperation.CEIL; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Cos.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Cos.java index ffe2c32aedf..5458caf5521 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Cos.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Cos.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; /** * Cosine @@ -18,6 +19,16 @@ public class Cos extends MathFunction { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Cos::new, field()); + } + + @Override + protected Cos replaceChild(Expression newChild) { + return new Cos(location(), newChild); + } + @Override protected MathOperation operation() { return MathOperation.COS; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Cosh.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Cosh.java index 63bf05ca5ba..77df8221218 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Cosh.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Cosh.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; /** * Hyperbolic cosine @@ -18,6 +19,16 @@ public class Cosh extends MathFunction { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Cosh::new, field()); + } + + @Override + protected Cosh replaceChild(Expression newChild) { + return new Cosh(location(), newChild); + } + @Override protected MathOperation operation() { return MathOperation.COSH; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Degrees.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Degrees.java index bc66acec357..138fda5bcae 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Degrees.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Degrees.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; /** * Convert from radians @@ -18,6 +19,16 @@ public class Degrees extends MathFunction { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Degrees::new, field()); + } + + @Override + protected Degrees replaceChild(Expression newChild) { + return new Degrees(location(), newChild); + } + @Override protected String mathFunction() { return "toDegrees"; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/E.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/E.java index 356dc57b058..4206ebb7b99 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/E.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/E.java @@ -6,11 +6,13 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math; +import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.Literal; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import org.elasticsearch.xpack.sql.expression.function.scalar.script.Params; import org.elasticsearch.xpack.sql.expression.function.scalar.script.ScriptTemplate; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataTypes; import org.elasticsearch.xpack.sql.util.StringUtils; @@ -22,6 +24,16 @@ public class E extends MathFunction { super(location, new Literal(location, Math.E, DataTypes.DOUBLE)); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this); + } + + @Override + protected E replaceChild(Expression field) { + throw new UnsupportedOperationException("this node doesn't have any children"); + } + @Override public Object fold() { return Math.E; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Exp.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Exp.java index 6ab01a0b6bb..b8ec51ca27d 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Exp.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Exp.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; /** * ex @@ -18,6 +19,16 @@ public class Exp extends MathFunction { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Exp::new, field()); + } + + @Override + protected Exp replaceChild(Expression newChild) { + return new Exp(location(), newChild); + } + @Override protected MathOperation operation() { return MathOperation.EXP; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Expm1.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Expm1.java index d12e51426b0..3a844f8c39b 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Expm1.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Expm1.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; /** * ex + 1 @@ -18,6 +19,16 @@ public class Expm1 extends MathFunction { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Expm1::new, field()); + } + + @Override + protected Expm1 replaceChild(Expression newChild) { + return new Expm1(location(), newChild); + } + @Override protected MathOperation operation() { return MathOperation.EXPM1; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Floor.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Floor.java index cc64600ec10..66a5d8ef85e 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Floor.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Floor.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypeConversion; @@ -20,6 +21,16 @@ public class Floor extends MathFunction { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Floor::new, field()); + } + + @Override + protected Floor replaceChild(Expression newChild) { + return new Floor(location(), newChild); + } + @Override protected MathOperation operation() { return MathOperation.FLOOR; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Log.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Log.java index cee23ea14cb..0202f61a7ab 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Log.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Log.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; /** * Natural logarithm @@ -18,6 +19,16 @@ public class Log extends MathFunction { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Log::new, field()); + } + + @Override + protected Log replaceChild(Expression newChild) { + return new Log(location(), newChild); + } + @Override protected MathOperation operation() { return MathOperation.LOG; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Log10.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Log10.java index eca75f48a53..0005488c619 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Log10.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Log10.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; /** * Logarithm @@ -18,6 +19,16 @@ public class Log10 extends MathFunction { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Log10::new, field()); + } + + @Override + protected Log10 replaceChild(Expression newChild) { + return new Log10(location(), newChild); + } + @Override protected MathOperation operation() { return MathOperation.LOG10; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/MathFunction.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/MathFunction.java index 3c9b3ec4c2a..182a812f34a 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/MathFunction.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/MathFunction.java @@ -16,6 +16,7 @@ import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; import java.util.Locale; +import java.util.Objects; import static java.lang.String.format; @@ -50,8 +51,23 @@ public abstract class MathFunction extends UnaryScalarFunction { @Override protected final ProcessorDefinition makeProcessorDefinition() { - return new UnaryProcessorDefinition(this, ProcessorDefinitions.toProcessorDefinition(field()), new MathProcessor(operation())); + return new UnaryProcessorDefinition(location(), this, + ProcessorDefinitions.toProcessorDefinition(field()), new MathProcessor(operation())); } protected abstract MathOperation operation(); -} \ No newline at end of file + + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != getClass()) { + return false; + } + MathFunction other = (MathFunction) obj; + return Objects.equals(other.field(), field()); + } + + @Override + public int hashCode() { + return Objects.hash(field()); + } +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Pi.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Pi.java index 424c305c0f2..9e4d1dee887 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Pi.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Pi.java @@ -6,11 +6,13 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math; +import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.Literal; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import org.elasticsearch.xpack.sql.expression.function.scalar.script.Params; import org.elasticsearch.xpack.sql.expression.function.scalar.script.ScriptTemplate; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataTypes; import org.elasticsearch.xpack.sql.util.StringUtils; @@ -22,6 +24,16 @@ public class Pi extends MathFunction { super(location, new Literal(location, Math.PI, DataTypes.DOUBLE)); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this); + } + + @Override + protected Pi replaceChild(Expression field) { + throw new UnsupportedOperationException("this node doesn't have any children"); + } + @Override public Object fold() { return Math.PI; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Radians.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Radians.java index 6a10b8f3c71..290600d1d99 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Radians.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Radians.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; /** * Convert from degrees @@ -18,6 +19,16 @@ public class Radians extends MathFunction { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Radians::new, field()); + } + + @Override + protected Radians replaceChild(Expression newChild) { + return new Radians(location(), newChild); + } + @Override protected String mathFunction() { return "toRadians"; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Round.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Round.java index bc4ff742de8..52d7bc5aeca 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Round.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Round.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypeConversion; @@ -23,6 +24,16 @@ public class Round extends MathFunction { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Round::new, field()); + } + + @Override + protected Round replaceChild(Expression newChild) { + return new Round(location(), newChild); + } + @Override protected MathOperation operation() { return MathOperation.ROUND; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Sin.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Sin.java index a2e25041f25..e61ba739e52 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Sin.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Sin.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; /** * Sine @@ -18,6 +19,16 @@ public class Sin extends MathFunction { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Sin::new, field()); + } + + @Override + protected Sin replaceChild(Expression newChild) { + return new Sin(location(), newChild); + } + @Override protected MathOperation operation() { return MathOperation.SIN; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Sinh.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Sinh.java index 544f3e420f9..52a358176d0 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Sinh.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Sinh.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; /** * Hyperbolic sine @@ -18,6 +19,16 @@ public class Sinh extends MathFunction { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Sinh::new, field()); + } + + @Override + protected Sinh replaceChild(Expression newChild) { + return new Sinh(location(), newChild); + } + @Override protected MathOperation operation() { return MathOperation.SINH; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Sqrt.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Sqrt.java index 4f4253263fd..f9daf25d218 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Sqrt.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Sqrt.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; /** * Square root @@ -18,6 +19,16 @@ public class Sqrt extends MathFunction { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Sqrt::new, field()); + } + + @Override + protected Sqrt replaceChild(Expression newChild) { + return new Sqrt(location(), newChild); + } + @Override protected MathOperation operation() { return MathOperation.SQRT; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Tan.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Tan.java index af15f6e3a6a..4728a7947e7 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Tan.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/Tan.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; /** * Tangent @@ -18,6 +19,16 @@ public class Tan extends MathFunction { super(location, field); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Tan::new, field()); + } + + @Override + protected Tan replaceChild(Expression newChild) { + return new Tan(location(), newChild); + } + @Override protected MathOperation operation() { return MathOperation.TAN; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/AggNameInput.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/AggNameInput.java index a803e11b150..43da886de49 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/AggNameInput.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/AggNameInput.java @@ -6,10 +6,17 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition; import org.elasticsearch.xpack.sql.expression.Expression; +import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class AggNameInput extends CommonNonExecutableInput { - public AggNameInput(Expression expression, String context) { - super(expression, context); + public AggNameInput(Location location, Expression expression, String context) { + super(location, expression, context); + } + + @Override + protected NodeInfo info() { + return NodeInfo.create(this, AggNameInput::new, expression(), context()); } @Override diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/AggPathInput.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/AggPathInput.java index 2c8cbfe301e..0e1358b4aae 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/AggPathInput.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/AggPathInput.java @@ -5,10 +5,10 @@ */ package org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition; -import org.elasticsearch.xpack.sql.execution.search.SqlSourceBuilder; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.Processor; - +import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import java.util.Objects; public class AggPathInput extends CommonNonExecutableInput { @@ -18,19 +18,24 @@ public class AggPathInput extends CommonNonExecutableInput { private final Processor action; public AggPathInput(Expression expression, String context) { - this(expression, context, null, null); + this(Location.EMPTY, expression, context, null, null); } public AggPathInput(Expression expression, String context, String innerKey) { - this(expression, context, innerKey, null); + this(Location.EMPTY, expression, context, innerKey, null); } - public AggPathInput(Expression expression, String context, String innerKey, Processor action) { - super(expression, context); + public AggPathInput(Location location, Expression expression, String context, String innerKey, Processor action) { + super(location, expression, context); this.innerKey = innerKey; this.action = action; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, AggPathInput::new, expression(), context(), innerKey, action); + } + public String innerKey() { return innerKey; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/AggValueInput.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/AggValueInput.java index 914c4294305..3add0a3977b 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/AggValueInput.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/AggValueInput.java @@ -10,7 +10,8 @@ import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.MatrixFieldProcessor; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.Processor; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.SuppliedProcessor; - +import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import java.util.Objects; import java.util.function.Supplier; @@ -19,12 +20,17 @@ public class AggValueInput extends LeafInput> { private final String innerKey; private final Processor matrixProcessor; - public AggValueInput(Expression expression, Supplier context, String innerKey) { - super(expression, context); + public AggValueInput(Location location, Expression expression, Supplier context, String innerKey) { + super(location, expression, context); this.innerKey = innerKey; this.matrixProcessor = innerKey != null ? new MatrixFieldProcessor(innerKey) : null; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, AggValueInput::new, expression(), context(), innerKey); + } + public String innerKey() { return innerKey; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/AttributeInput.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/AttributeInput.java index 55a87d9ed66..54ed7712366 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/AttributeInput.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/AttributeInput.java @@ -8,14 +8,21 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.processor.definit import org.elasticsearch.xpack.sql.execution.search.SqlSourceBuilder; import org.elasticsearch.xpack.sql.expression.Attribute; import org.elasticsearch.xpack.sql.expression.Expression; +import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; /** * An input that must first be rewritten against the rest of the query * before it can be further processed. */ public class AttributeInput extends NonExecutableInput { - public AttributeInput(Expression expression, Attribute context) { - super(expression, context); + public AttributeInput(Location location, Expression expression, Attribute context) { + super(location, expression, context); + } + + @Override + protected NodeInfo info() { + return NodeInfo.create(this, AttributeInput::new, expression(), context()); } @Override @@ -25,7 +32,7 @@ public class AttributeInput extends NonExecutableInput { @Override public ProcessorDefinition resolveAttributes(AttributeResolver resolver) { - return new ReferenceInput(expression(), resolver.resolve(context())); + return new ReferenceInput(location(), expression(), resolver.resolve(context())); } @Override diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/BinaryProcessorDefinition.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/BinaryProcessorDefinition.java index b38f107b98d..c8f066d6777 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/BinaryProcessorDefinition.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/BinaryProcessorDefinition.java @@ -7,19 +7,28 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.processor.definit import org.elasticsearch.xpack.sql.execution.search.SqlSourceBuilder; import org.elasticsearch.xpack.sql.expression.Expression; - +import org.elasticsearch.xpack.sql.tree.Location; import java.util.Arrays; +import java.util.List; public abstract class BinaryProcessorDefinition extends ProcessorDefinition { private final ProcessorDefinition left, right; - public BinaryProcessorDefinition(Expression expression, ProcessorDefinition left, ProcessorDefinition right) { - super(expression, Arrays.asList(left, right)); + public BinaryProcessorDefinition(Location location, Expression expression, ProcessorDefinition left, ProcessorDefinition right) { + super(location, expression, Arrays.asList(left, right)); this.left = left; this.right = right; } + @Override + public final ProcessorDefinition replaceChildren(List newChildren) { + if (newChildren.size() != 2) { + throw new IllegalArgumentException("expected [2] children but received [" + newChildren.size() + "]"); + } + return replaceChildren(newChildren.get(0), newChildren.get(1)); + } + public ProcessorDefinition left() { return left; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/CommonNonExecutableInput.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/CommonNonExecutableInput.java index 8459214070a..3088f50472b 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/CommonNonExecutableInput.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/CommonNonExecutableInput.java @@ -9,14 +9,15 @@ import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; import org.elasticsearch.xpack.sql.execution.search.SqlSourceBuilder; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.Processor; +import org.elasticsearch.xpack.sql.tree.Location; /** * Implementation common to most subclasses of * {@link NonExecutableInput} but not shared by all. */ abstract class CommonNonExecutableInput extends NonExecutableInput { - CommonNonExecutableInput(Expression expression, T context) { - super(expression, context); + CommonNonExecutableInput(Location location, Expression expression, T context) { + super(location, expression, context); } @Override diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/ConstantInput.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/ConstantInput.java index 14e2219e9db..10444fce13b 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/ConstantInput.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/ConstantInput.java @@ -9,11 +9,18 @@ import org.elasticsearch.xpack.sql.execution.search.SqlSourceBuilder; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.ConstantProcessor; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.Processor; +import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class ConstantInput extends LeafInput { - public ConstantInput(Expression expression, Object context) { - super(expression, context); + public ConstantInput(Location location, Expression expression, Object context) { + super(location, expression, context); + } + + @Override + protected NodeInfo info() { + return NodeInfo.create(this, ConstantInput::new, expression(), context()); } @Override diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/HitExtractorInput.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/HitExtractorInput.java index 357ee69cb81..79290475173 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/HitExtractorInput.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/HitExtractorInput.java @@ -10,11 +10,18 @@ import org.elasticsearch.xpack.sql.execution.search.extractor.HitExtractor; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.HitExtractorProcessor; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.Processor; +import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class HitExtractorInput extends LeafInput { - public HitExtractorInput(Expression expression, HitExtractor context) { - super(expression, context); + public HitExtractorInput(Location location, Expression expression, HitExtractor context) { + super(location, expression, context); + } + + @Override + protected NodeInfo info() { + return NodeInfo.create(this, HitExtractorInput::new, expression(), context()); } @Override diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/LeafInput.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/LeafInput.java index 365c979e817..5f9bef3b12a 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/LeafInput.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/LeafInput.java @@ -6,20 +6,27 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition; import org.elasticsearch.xpack.sql.expression.Expression; - +import org.elasticsearch.xpack.sql.tree.Location; import java.util.Objects; import static java.util.Collections.emptyList; +import java.util.List; + public abstract class LeafInput extends ProcessorDefinition { private T context; - public LeafInput(Expression expression, T context) { - super(expression, emptyList()); + public LeafInput(Location location, Expression expression, T context) { + super(location, expression, emptyList()); this.context = context; } + @Override + public final ProcessorDefinition replaceChildren(List newChildren) { + throw new UnsupportedOperationException("this type of node doesn't have any children to replace"); + } + public T context() { return context; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/NonExecutableInput.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/NonExecutableInput.java index 49c1b39b250..2161f09d46a 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/NonExecutableInput.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/NonExecutableInput.java @@ -8,11 +8,11 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.processor.definit import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.Processor; +import org.elasticsearch.xpack.sql.tree.Location; public abstract class NonExecutableInput extends LeafInput { - - NonExecutableInput(Expression expression, T context) { - super(expression, context); + NonExecutableInput(Location location, Expression expression, T context) { + super(location, expression, context); } @Override diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/ProcessorDefinition.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/ProcessorDefinition.java index 5da2fa81b48..605aae63f4d 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/ProcessorDefinition.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/ProcessorDefinition.java @@ -9,6 +9,7 @@ import org.elasticsearch.xpack.sql.execution.search.FieldExtraction; import org.elasticsearch.xpack.sql.expression.Attribute; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.Processor; +import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.Node; import java.util.List; @@ -17,8 +18,8 @@ public abstract class ProcessorDefinition extends Node impl private final Expression expression; - public ProcessorDefinition(Expression expression, List children) { - super(children); + public ProcessorDefinition(Location location, Expression expression, List children) { + super(location, children); this.expression = expression; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/ProcessorDefinitions.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/ProcessorDefinitions.java index c633ae3a61d..cf794226ac3 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/ProcessorDefinitions.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/ProcessorDefinitions.java @@ -15,18 +15,18 @@ public abstract class ProcessorDefinitions { public static ProcessorDefinition toProcessorDefinition(Expression ex) { if (ex.foldable()) { - return new ConstantInput(ex, ex.fold()); + return new ConstantInput(ex.location(), ex, ex.fold()); } if (ex instanceof ScalarFunction) { return ((ScalarFunction) ex).asProcessorDefinition(); } if (ex instanceof AggregateFunction) { // unresolved AggNameInput (should always get replaced by the folder) - return new AggNameInput(ex, ((AggregateFunction) ex).name()); + return new AggNameInput(ex.location(), ex, ((AggregateFunction) ex).name()); } if (ex instanceof NamedExpression) { - return new AttributeInput(ex, ((NamedExpression) ex).toAttribute()); + return new AttributeInput(ex.location(), ex, ((NamedExpression) ex).toAttribute()); } throw new SqlIllegalArgumentException("Cannot extract processor from %s", ex); } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/ReferenceInput.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/ReferenceInput.java index 4efc31f9310..1b82173212f 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/ReferenceInput.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/ReferenceInput.java @@ -8,10 +8,17 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.processor.definit import org.elasticsearch.xpack.sql.execution.search.FieldExtraction; import org.elasticsearch.xpack.sql.execution.search.SqlSourceBuilder; import org.elasticsearch.xpack.sql.expression.Expression; +import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class ReferenceInput extends NonExecutableInput { - public ReferenceInput(Expression expression, FieldExtraction context) { - super(expression, context); + public ReferenceInput(Location location, Expression expression, FieldExtraction context) { + super(location, expression, context); + } + + @Override + protected NodeInfo info() { + return NodeInfo.create(this, ReferenceInput::new, expression(), context()); } @Override diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/ScoreProcessorDefinition.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/ScoreProcessorDefinition.java index 24f3012ba96..e12fa3e90de 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/ScoreProcessorDefinition.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/ScoreProcessorDefinition.java @@ -9,14 +9,27 @@ import org.elasticsearch.xpack.sql.execution.search.SqlSourceBuilder; import org.elasticsearch.xpack.sql.execution.search.extractor.ScoreExtractor; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.Processor; +import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.HitExtractorProcessor; import static java.util.Collections.emptyList; -public class ScoreProcessorDefinition extends ProcessorDefinition { +import java.util.List; - public ScoreProcessorDefinition(Expression expression) { - super(expression, emptyList()); +public class ScoreProcessorDefinition extends ProcessorDefinition { + public ScoreProcessorDefinition(Location location, Expression expression) { + super(location, expression, emptyList()); + } + + @Override + protected NodeInfo info() { + return NodeInfo.create(this, ScoreProcessorDefinition::new, expression()); + } + + @Override + public final ProcessorDefinition replaceChildren(List newChildren) { + throw new UnsupportedOperationException("this type of node doesn't have any children to replace"); } @Override diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/UnaryProcessorDefinition.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/UnaryProcessorDefinition.java index d1be90c3855..b49d3145d84 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/UnaryProcessorDefinition.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/UnaryProcessorDefinition.java @@ -9,22 +9,38 @@ import org.elasticsearch.xpack.sql.execution.search.SqlSourceBuilder; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.ChainingProcessor; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.Processor; - +import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import java.util.Objects; import static java.util.Collections.singletonList; +import java.util.List; + public final class UnaryProcessorDefinition extends ProcessorDefinition { private final ProcessorDefinition child; private final Processor action; - public UnaryProcessorDefinition(Expression expression, ProcessorDefinition child, Processor action) { - super(expression, singletonList(child)); + public UnaryProcessorDefinition(Location location, Expression expression, ProcessorDefinition child, Processor action) { + super(location, expression, singletonList(child)); this.child = child; this.action = action; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, UnaryProcessorDefinition::new, expression(), child, action); + } + + @Override + public ProcessorDefinition replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return new UnaryProcessorDefinition(location(), expression(), newChildren.get(0), action); + } + public ProcessorDefinition child() { return child; } @@ -54,7 +70,7 @@ public final class UnaryProcessorDefinition extends ProcessorDefinition { if (newChild == child) { return this; } - return new UnaryProcessorDefinition(expression(), newChild, action); + return new UnaryProcessorDefinition(location(), expression(), newChild, action); } @Override diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/script/Params.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/script/Params.java index 1b35aa0bea8..b252bcbad4b 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/script/Params.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/script/Params.java @@ -16,24 +16,24 @@ import static java.util.Collections.emptyList; /** * Parameters for a script - * + * * This class mainly exists to handle the different aggregation cases. * While aggs can appear in scripts like regular parameters, they are not passed * as parameters but rather as bucket_path. * However in some cases (like count), it's not the agg path that is relevant but rather - * its property (_count). + * its property (_count). * As the agg name still needs to be remembered to properly associate the script with. - * + * * Hence why this class supports aggRef (which always returns the agg names) and aggPaths * (which returns the agg property if it exists or the agg name/reference). - * + * * Also the parameter names support late binding/evaluation since the agg reference (like function id) * can be changed during the optimization phase (for example min agg -> stats.min). */ public class Params { public static final Params EMPTY = new Params(emptyList()); - + private final List> params; Params(List> params) { @@ -49,7 +49,7 @@ public class Params { List names = new ArrayList<>(params.size()); int aggs = 0, vars = 0; - + for (Param p : params) { names.add(p.prefix() + (p instanceof Agg ? aggs++ : vars++)); } @@ -63,13 +63,13 @@ public class Params { Map map = new LinkedHashMap<>(params.size()); int count = 0; - + for (Param p : params) { if (p instanceof Var) { map.put(p.prefix() + count++, p.value()); } } - + return map; } @@ -131,4 +131,4 @@ public class Params { public String toString() { return params.toString(); } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/script/ScriptTemplate.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/script/ScriptTemplate.java index 7da07333844..ef8b4abfabc 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/script/ScriptTemplate.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/script/ScriptTemplate.java @@ -95,4 +95,4 @@ public class ScriptTemplate { public static String formatTemplate(String template) { return template.replace("{}", "params.%s"); } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/And.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/And.java index 32f48806140..0cabf065f8f 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/And.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/And.java @@ -6,19 +6,30 @@ package org.elasticsearch.xpack.sql.expression.predicate; import org.elasticsearch.xpack.sql.expression.BinaryLogic; +import org.elasticsearch.xpack.sql.expression.BinaryOperator; import org.elasticsearch.xpack.sql.expression.BinaryOperator.Negateable; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import java.util.Objects; public class And extends BinaryLogic implements Negateable { - + public And(Location location, Expression left, Expression right) { super(location, left, right); } @Override + protected NodeInfo info() { + return NodeInfo.create(this, And::new, left(), right()); + } + + @Override + protected BinaryOperator replaceChildren(Expression newLeft, Expression newRight) { + return new And(location(), newLeft, newRight); + } + public Object fold() { return Objects.equals(left().fold(), Boolean.TRUE) && Objects.equals(right().fold(), Boolean.TRUE); } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Equals.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Equals.java index c69301b84a8..4d4d7082eea 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Equals.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Equals.java @@ -5,8 +5,10 @@ */ package org.elasticsearch.xpack.sql.expression.predicate; +import org.elasticsearch.xpack.sql.expression.BinaryOperator; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import java.util.Objects; @@ -17,6 +19,15 @@ public class Equals extends BinaryComparison { } @Override + protected NodeInfo info() { + return NodeInfo.create(this, Equals::new, left(), right()); + } + + @Override + protected Equals replaceChildren(Expression newLeft, Expression newRight) { + return new Equals(location(), newLeft, newRight); + } + public Object fold() { return Objects.equals(left().fold(), right().fold()); } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/GreaterThan.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/GreaterThan.java index ad577f63e2e..5fecc7c4f63 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/GreaterThan.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/GreaterThan.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.predicate; import org.elasticsearch.xpack.sql.expression.BinaryOperator.Negateable; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class GreaterThan extends BinaryComparison implements Negateable { @@ -16,6 +17,15 @@ public class GreaterThan extends BinaryComparison implements Negateable { } @Override + protected NodeInfo info() { + return NodeInfo.create(this, GreaterThan::new, left(), right()); + } + + @Override + protected GreaterThan replaceChildren(Expression newLeft, Expression newRight) { + return new GreaterThan(location(), newLeft, newRight); + } + public Object fold() { Integer compare = compare(left().fold(), right().fold()); return compare != null && compare.intValue() > 0; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/GreaterThanOrEqual.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/GreaterThanOrEqual.java index 0c0110c1f7d..837cfa1df93 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/GreaterThanOrEqual.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/GreaterThanOrEqual.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.predicate; import org.elasticsearch.xpack.sql.expression.BinaryOperator.Negateable; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class GreaterThanOrEqual extends BinaryComparison implements Negateable { @@ -16,6 +17,15 @@ public class GreaterThanOrEqual extends BinaryComparison implements Negateable { } @Override + protected NodeInfo info() { + return NodeInfo.create(this, GreaterThanOrEqual::new, left(), right()); + } + + @Override + protected GreaterThanOrEqual replaceChildren(Expression newLeft, Expression newRight) { + return new GreaterThanOrEqual(location(), newLeft, newRight); + } + public Object fold() { Integer compare = compare(left().fold(), right().fold()); return compare != null && compare.intValue() >= 0; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/In.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/In.java index 5c961583aa5..ed93b43ab44 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/In.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/In.java @@ -10,6 +10,7 @@ import java.util.Objects; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; import org.elasticsearch.xpack.sql.util.CollectionUtils; @@ -29,6 +30,19 @@ public class In extends Expression { this.foldable = children().stream().allMatch(Expression::foldable); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, In::new, value(), list()); + } + + @Override + public Expression replaceChildren(List newChildren) { + if (newChildren.size() < 2) { + throw new IllegalArgumentException("expected more than one child but received [" + newChildren.size() + "]"); + } + return new In(location(), newChildren.get(newChildren.size() - 1), newChildren.subList(0, newChildren.size() - 1)); + } + public Expression value() { return value; } @@ -62,13 +76,13 @@ public class In extends Expression { if (this == obj) { return true; } - - if (!this.equals(obj) || getClass() != obj.getClass()) { + + if (!super.equals(obj) || getClass() != obj.getClass()) { return false; } - + In other = (In) obj; return Objects.equals(value, other.value) && Objects.equals(list, other.list); } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/IsNotNull.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/IsNotNull.java index 8425f249759..b9f866bf1e1 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/IsNotNull.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/IsNotNull.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.predicate; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.UnaryExpression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; @@ -18,6 +19,15 @@ public class IsNotNull extends UnaryExpression { } @Override + protected NodeInfo info() { + return NodeInfo.create(this, IsNotNull::new, child()); + } + + @Override + protected IsNotNull replaceChild(Expression newChild) { + return new IsNotNull(location(), newChild); + } + public Object fold() { return child().fold() != null && !DataTypes.NULL.same(child().dataType()); } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/LessThan.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/LessThan.java index a98c503ce41..151614b45dd 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/LessThan.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/LessThan.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.predicate; import org.elasticsearch.xpack.sql.expression.BinaryOperator.Negateable; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class LessThan extends BinaryComparison implements Negateable { @@ -16,6 +17,15 @@ public class LessThan extends BinaryComparison implements Negateable { } @Override + protected NodeInfo info() { + return NodeInfo.create(this, LessThan::new, left(), right()); + } + + @Override + protected LessThan replaceChildren(Expression newLeft, Expression newRight) { + return new LessThan(location(), newLeft, newRight); + } + public Object fold() { Integer compare = compare(left().fold(), right().fold()); return compare != null && compare.intValue() < 0; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/LessThanOrEqual.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/LessThanOrEqual.java index 11b0ce0475c..3f5a1252691 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/LessThanOrEqual.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/LessThanOrEqual.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.predicate; import org.elasticsearch.xpack.sql.expression.BinaryOperator.Negateable; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class LessThanOrEqual extends BinaryComparison implements Negateable { @@ -15,6 +16,16 @@ public class LessThanOrEqual extends BinaryComparison implements Negateable { super(location, left, right); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, LessThanOrEqual::new, left(), right()); + } + + @Override + protected LessThanOrEqual replaceChildren(Expression newLeft, Expression newRight) { + return new LessThanOrEqual(location(), newLeft, newRight); + } + @Override public Object fold() { Integer compare = compare(left().fold(), right().fold()); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Not.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Not.java index 72f511a4523..efc53c3e3eb 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Not.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Not.java @@ -10,6 +10,7 @@ import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.Expressions; import org.elasticsearch.xpack.sql.expression.UnaryExpression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; @@ -22,6 +23,15 @@ public class Not extends UnaryExpression { } @Override + protected NodeInfo info() { + return NodeInfo.create(this, Not::new, child()); + } + + @Override + protected Not replaceChild(Expression newChild) { + return new Not(location(), newChild); + } + protected TypeResolution resolveType() { if (DataTypes.BOOLEAN.same(child().dataType())) { return TypeResolution.TYPE_RESOLVED; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Or.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Or.java index 24354ea53cf..49bd40b2846 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Or.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Or.java @@ -6,9 +6,11 @@ package org.elasticsearch.xpack.sql.expression.predicate; import org.elasticsearch.xpack.sql.expression.BinaryLogic; +import org.elasticsearch.xpack.sql.expression.BinaryOperator; import org.elasticsearch.xpack.sql.expression.BinaryOperator.Negateable; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import java.util.Objects; @@ -18,6 +20,16 @@ public class Or extends BinaryLogic implements Negateable { super(location, left, right); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Or::new, left(), right()); + } + + @Override + protected BinaryOperator replaceChildren(Expression newLeft, Expression newRight) { + return new Or(location(), newLeft, newRight); + } + @Override public Object fold() { return Objects.equals(left().fold(), Boolean.TRUE) || Objects.equals(right().fold(), Boolean.TRUE); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Range.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Range.java index 08b2be78a57..c1f9754edd0 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Range.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Range.java @@ -7,10 +7,12 @@ package org.elasticsearch.xpack.sql.expression.predicate; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; import java.util.Arrays; +import java.util.List; import java.util.Objects; // BETWEEN or range - is a mix of gt(e) AND lt(e) @@ -18,33 +20,46 @@ public class Range extends Expression { private final Expression value, lower, upper; private final boolean includeLower, includeUpper; - + public Range(Location location, Expression value, Expression lower, boolean includeLower, Expression upper, boolean includeUpper) { super(location, Arrays.asList(value, lower, upper)); - + this.value = value; this.lower = lower; this.upper = upper; this.includeLower = includeLower; this.includeUpper = includeUpper; } - + + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Range::new, value, lower, includeLower, upper, includeUpper); + } + + @Override + public Expression replaceChildren(List newChildren) { + if (newChildren.size() != 3) { + throw new IllegalArgumentException("expected [3] children but received [" + newChildren.size() + "]"); + } + return new Range(location(), newChildren.get(0), newChildren.get(1), includeLower, newChildren.get(2), includeUpper); + } + public Expression value() { return value; } - + public Expression lower() { return lower; } - + public Expression upper() { return upper; } - + public boolean includeLower() { return includeLower; } - + public boolean includeUpper() { return includeUpper; } @@ -78,17 +93,17 @@ public class Range extends Expression { public int hashCode() { return Objects.hash(includeLower, includeUpper, value, lower, upper); } - + @Override public boolean equals(Object obj) { if (this == obj) { return true; } - + if (obj == null || getClass() != obj.getClass()) { return false; } - + Range other = (Range) obj; return Objects.equals(includeLower, other.includeLower) && Objects.equals(includeUpper, other.includeUpper) @@ -96,7 +111,7 @@ public class Range extends Expression { && Objects.equals(lower, other.lower) && Objects.equals(upper, other.upper); } - + @Override public String toString() { StringBuilder sb = new StringBuilder(); @@ -107,4 +122,4 @@ public class Range extends Expression { sb.append(upper); return sb.toString(); } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/fulltext/FullTextPredicate.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/fulltext/FullTextPredicate.java index 05cfbce4c2f..5e1b5ccb272 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/fulltext/FullTextPredicate.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/fulltext/FullTextPredicate.java @@ -14,17 +14,17 @@ import java.util.List; import java.util.Map; import java.util.Objects; -public class FullTextPredicate extends Expression { +public abstract class FullTextPredicate extends Expression { public enum Operator { AND, OR; - + public org.elasticsearch.index.query.Operator toEs() { return org.elasticsearch.index.query.Operator.fromString(name()); } } - + private final String query; private final String options; private final Map optionMap; @@ -65,7 +65,7 @@ public class FullTextPredicate extends Expression { public DataType dataType() { return DataTypes.BOOLEAN; } - + @Override public int hashCode() { return Objects.hash(query, options); @@ -76,13 +76,13 @@ public class FullTextPredicate extends Expression { if (this == obj) { return true; } - + if (obj == null || getClass() != obj.getClass()) { return false; } - + FullTextPredicate other = (FullTextPredicate) obj; return Objects.equals(query, other.query) && Objects.equals(options, other.options); } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/fulltext/MatchQueryPredicate.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/fulltext/MatchQueryPredicate.java index 8fe8e99e2e4..1235cb1a205 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/fulltext/MatchQueryPredicate.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/fulltext/MatchQueryPredicate.java @@ -9,9 +9,12 @@ import java.util.Objects; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import static java.util.Collections.singletonList; +import java.util.List; + public class MatchQueryPredicate extends FullTextPredicate { private final Expression field; @@ -21,6 +24,19 @@ public class MatchQueryPredicate extends FullTextPredicate { this.field = field; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, MatchQueryPredicate::new, field, query(), options()); + } + + @Override + public MatchQueryPredicate replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return new MatchQueryPredicate(location(), newChildren.get(0), query(), options()); + } + public Expression field() { return field; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/fulltext/MultiMatchQueryPredicate.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/fulltext/MultiMatchQueryPredicate.java index 814ba1efdd3..eb3df01d6ef 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/fulltext/MultiMatchQueryPredicate.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/fulltext/MultiMatchQueryPredicate.java @@ -8,10 +8,14 @@ package org.elasticsearch.xpack.sql.expression.predicate.fulltext; import java.util.Map; import java.util.Objects; +import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import static java.util.Collections.emptyList; +import java.util.List; + public class MultiMatchQueryPredicate extends FullTextPredicate { private final String fieldString; @@ -24,10 +28,20 @@ public class MultiMatchQueryPredicate extends FullTextPredicate { this.fields = FullTextUtils.parseFields(fieldString, location); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, MultiMatchQueryPredicate::new, fieldString, query(), options()); + } + + @Override + public Expression replaceChildren(List newChildren) { + throw new UnsupportedOperationException("this type of node doesn't have any children to replace"); + } + public String fieldString() { return fieldString; } - + public Map fields() { return fields; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/fulltext/StringQueryPredicate.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/fulltext/StringQueryPredicate.java index bfe0035b125..32750444774 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/fulltext/StringQueryPredicate.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/fulltext/StringQueryPredicate.java @@ -6,8 +6,11 @@ package org.elasticsearch.xpack.sql.expression.predicate.fulltext; import java.util.Map; +import java.util.List; +import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import static java.util.Collections.emptyList; @@ -22,6 +25,16 @@ public class StringQueryPredicate extends FullTextPredicate { this.fields = FullTextUtils.parseFields(optionMap(), location); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, StringQueryPredicate::new, query(), options()); + } + + @Override + public Expression replaceChildren(List newChildren) { + throw new UnsupportedOperationException("this type of node doesn't have any children to replace"); + } + public Map fields() { return fields; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/regex/Like.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/regex/Like.java index 370f495a0b5..8b148c6def8 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/regex/Like.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/regex/Like.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.regex; import org.elasticsearch.xpack.sql.expression.BinaryExpression; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; @@ -20,12 +21,22 @@ public class Like extends BinaryExpression { } @Override + protected NodeInfo info() { + return NodeInfo.create(this, Like::new, left(), right()); + } + + @Override + protected BinaryExpression replaceChildren(Expression newLeft, Expression newRight) { + return new Like(location(), newLeft, (LikePattern) newRight); + } + public LikePattern right() { return (LikePattern) super.right(); } @Override public boolean foldable() { + // right() is not directly foldable in any context but Like can fold it. return left().foldable(); } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/regex/LikePattern.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/regex/LikePattern.java index b4e98c2a433..b3b21ecebbb 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/regex/LikePattern.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/regex/LikePattern.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.sql.expression.regex; import org.elasticsearch.xpack.sql.expression.LeafExpression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; import org.elasticsearch.xpack.sql.util.StringUtils; @@ -18,7 +19,7 @@ import java.util.Objects; * Similar to basic regex, supporting '_' instead of '?' and '%' instead of '*'. *

* Allows escaping based on a regular char. - * + * * To prevent conflicts with ES, the string and char must be validated to not contain '*'. */ public class LikePattern extends LeafExpression { @@ -40,6 +41,11 @@ public class LikePattern extends LeafExpression { this.indexNameWildcard = StringUtils.likeToIndexWildcard(pattern, escape); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, LikePattern::new, pattern, escape); + } + public String pattern() { return pattern; } @@ -96,11 +102,6 @@ public class LikePattern extends LeafExpression { LikePattern other = (LikePattern) obj; return Objects.equals(pattern, other.pattern) - && Objects.equals(escape, other.escape); + && escape == other.escape; } - - @Override - public String toString() { - return pattern; - } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/regex/RLike.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/regex/RLike.java index 75c1400cb2e..fb387f252fb 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/regex/RLike.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/expression/regex/RLike.java @@ -9,6 +9,7 @@ import org.elasticsearch.xpack.sql.expression.BinaryExpression; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.Literal; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; @@ -21,6 +22,15 @@ public class RLike extends BinaryExpression { } @Override + protected NodeInfo info() { + return NodeInfo.create(this, RLike::new, left(), right()); + } + + @Override + protected BinaryExpression replaceChildren(Expression newLeft, Expression newRight) { + return new RLike(location(), newLeft, (Literal) newRight); + } + public Literal right() { return (Literal) super.right(); } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/optimizer/Optimizer.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/optimizer/Optimizer.java index f3ff2cc027b..b56baecb6fe 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/optimizer/Optimizer.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/optimizer/Optimizer.java @@ -291,7 +291,7 @@ public class Optimizer extends RuleExecutor { seen.put(argument, matrixStats); } - InnerAggregate ia = new InnerAggregate(f, matrixStats, f.field()); + InnerAggregate ia = new InnerAggregate(f.location(), f, matrixStats, f.field()); promotedIds.putIfAbsent(f.functionId(), ia.toAttribute()); return ia; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/parser/LogicalPlanBuilder.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/parser/LogicalPlanBuilder.java index 6fa213d2db2..fe97b2cd06d 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/parser/LogicalPlanBuilder.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/parser/LogicalPlanBuilder.java @@ -113,7 +113,7 @@ abstract class LogicalPlanBuilder extends ExpressionBuilder { // SELECT a, b, c ... if (!ctx.selectItem().isEmpty()) { selectTarget = expressions(ctx.selectItem()).stream() - .map(e -> (e instanceof NamedExpression) ? (NamedExpression) e : new UnresolvedAlias(e)) + .map(e -> (e instanceof NamedExpression) ? (NamedExpression) e : new UnresolvedAlias(e.location(), e)) .collect(toList()); } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Aggregate.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Aggregate.java index a04923d6ccb..b588bff3865 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Aggregate.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Aggregate.java @@ -11,6 +11,7 @@ import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.Expressions; import org.elasticsearch.xpack.sql.expression.NamedExpression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import java.util.List; import java.util.Objects; @@ -26,6 +27,16 @@ public class Aggregate extends UnaryPlan { this.aggregates = aggregates; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Aggregate::new, child(), groupings, aggregates); + } + + @Override + protected Aggregate replaceChild(LogicalPlan newChild) { + return new Aggregate(location(), newChild, groupings, aggregates); + } + public List groupings() { return groupings; } @@ -54,11 +65,11 @@ public class Aggregate extends UnaryPlan { if (this == obj) { return true; } - + if (obj == null || getClass() != obj.getClass()) { return false; } - + Aggregate other = (Aggregate) obj; return Objects.equals(groupings, other.groupings) && Objects.equals(aggregates, other.aggregates) diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Distinct.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Distinct.java index be79e3a4ffc..63759f94412 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Distinct.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Distinct.java @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.sql.plan.logical; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class Distinct extends UnaryPlan { @@ -13,6 +14,16 @@ public class Distinct extends UnaryPlan { super(location, child); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Distinct::new, child()); + } + + @Override + protected Distinct replaceChild(LogicalPlan newChild) { + return new Distinct(location(), newChild); + } + @Override public boolean expressionsResolved() { return true; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/EsRelation.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/EsRelation.java index b8e09d131e8..1ace2df6241 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/EsRelation.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/EsRelation.java @@ -9,6 +9,7 @@ import org.elasticsearch.xpack.sql.analysis.index.EsIndex; import org.elasticsearch.xpack.sql.expression.Attribute; import org.elasticsearch.xpack.sql.expression.FieldAttribute; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.CompoundDataType; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.util.StringUtils; @@ -30,6 +31,11 @@ public class EsRelation extends LeafPlan { attrs = flatten(location, index.mapping()); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, EsRelation::new, index); + } + private static List flatten(Location location, Map mapping) { return flatten(location, mapping, null); } @@ -90,4 +96,4 @@ public class EsRelation extends LeafPlan { public String nodeString() { return nodeName() + "[" + index + "]" + StringUtils.limitedToString(attrs); } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Filter.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Filter.java index 368eb12d804..ce36333d791 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Filter.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Filter.java @@ -9,6 +9,7 @@ import java.util.Objects; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class Filter extends UnaryPlan { @@ -19,6 +20,16 @@ public class Filter extends UnaryPlan { this.condition = condition; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Filter::new, child(), condition); + } + + @Override + protected Filter replaceChild(LogicalPlan newChild) { + return new Filter(location(), newChild, condition); + } + public Expression condition() { return condition; } @@ -43,7 +54,7 @@ public class Filter extends UnaryPlan { } Filter other = (Filter) obj; - + return Objects.equals(condition, other.condition) && Objects.equals(child(), other.child()); } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Join.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Join.java index 799f8d85580..c9c4512ebfb 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Join.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Join.java @@ -11,6 +11,7 @@ import java.util.Objects; import org.elasticsearch.xpack.sql.expression.Attribute; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataTypes; import static java.util.stream.Collectors.toList; @@ -29,13 +30,26 @@ public class Join extends BinaryPlan { FULL, // OUTER IMPLICIT, } - + public Join(Location location, LogicalPlan left, LogicalPlan right, JoinType type, Expression condition) { super(location, left, right); this.type = type; this.condition = condition; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Join::new, left(), right(), type, condition); + } + + @Override + public LogicalPlan replaceChildren(List newChildren) { + if (newChildren.size() != 2) { + throw new IllegalArgumentException("expected [2] children but received [" + newChildren.size() + "]"); + } + return new Join(location(), newChildren.get(0), newChildren.get(1), type, condition); + } + public JoinType type() { return type; } @@ -86,7 +100,7 @@ public class Join extends BinaryPlan { return childrenResolved() && duplicatesResolved() && expressionsResolved() && - (condition == null || DataTypes.BOOLEAN.equals(condition.dataType())); + (condition == null || DataTypes.BOOLEAN.equals(condition.dataType())); } @Override @@ -104,7 +118,7 @@ public class Join extends BinaryPlan { } Join other = (Join) obj; - + return Objects.equals(type, other.type) && Objects.equals(condition, other.condition) && Objects.equals(left(), other.left()) diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/LeafPlan.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/LeafPlan.java index 1128f15a38e..a68eeb53e1d 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/LeafPlan.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/LeafPlan.java @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.sql.plan.logical; import java.util.Collections; +import java.util.List; import org.elasticsearch.xpack.sql.tree.Location; @@ -14,4 +15,9 @@ abstract class LeafPlan extends LogicalPlan { protected LeafPlan(Location location) { super(location, Collections.emptyList()); } + + @Override + public final LogicalPlan replaceChildren(List newChildren) { + throw new UnsupportedOperationException("this type of node doesn't have any children to replace"); + } } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Limit.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Limit.java index a0cb4fc2675..ef194c4eae3 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Limit.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Limit.java @@ -9,6 +9,7 @@ import java.util.Objects; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class Limit extends UnaryPlan { @@ -19,6 +20,16 @@ public class Limit extends UnaryPlan { this.limit = limit; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Limit::new, limit, child()); + } + + @Override + protected Limit replaceChild(LogicalPlan newChild) { + return new Limit(location(), limit, newChild); + } + public Expression limit() { return limit; } @@ -43,7 +54,7 @@ public class Limit extends UnaryPlan { } Limit other = (Limit) obj; - + return Objects.equals(limit, other.limit) && Objects.equals(child(), other.child()); } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/LocalRelation.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/LocalRelation.java index d02726ed582..cf6b4933787 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/LocalRelation.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/LocalRelation.java @@ -11,6 +11,7 @@ import org.elasticsearch.xpack.sql.session.Executable; import org.elasticsearch.xpack.sql.session.SchemaRowSet; import org.elasticsearch.xpack.sql.session.SqlSession; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import java.util.List; import java.util.Objects; @@ -26,6 +27,16 @@ public class LocalRelation extends LogicalPlan implements Executable { this.executable = executable; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, LocalRelation::new, executable); + } + + @Override + public LogicalPlan replaceChildren(List newChildren) { + throw new UnsupportedOperationException("this type of node doesn't have any children to replace"); + } + public Executable executable() { return executable; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/OrderBy.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/OrderBy.java index 95a5bdecbdc..8800dcaae65 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/OrderBy.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/OrderBy.java @@ -11,6 +11,7 @@ import java.util.Objects; import org.elasticsearch.xpack.sql.capabilities.Resolvables; import org.elasticsearch.xpack.sql.expression.Order; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class OrderBy extends UnaryPlan { @@ -21,6 +22,16 @@ public class OrderBy extends UnaryPlan { this.order = order; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, OrderBy::new, child(), order); + } + + @Override + protected OrderBy replaceChild(LogicalPlan newChild) { + return new OrderBy(location(), newChild, order); + } + public List order() { return order; } @@ -49,4 +60,4 @@ public class OrderBy extends UnaryPlan { return Objects.equals(order, other.order) && Objects.equals(child(), other.child()); } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Project.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Project.java index e48278e21a8..7445d72f43f 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Project.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Project.java @@ -14,6 +14,7 @@ import org.elasticsearch.xpack.sql.expression.Expressions; import org.elasticsearch.xpack.sql.expression.NamedExpression; import org.elasticsearch.xpack.sql.expression.function.Functions; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class Project extends UnaryPlan { @@ -24,6 +25,16 @@ public class Project extends UnaryPlan { this.projections = projections; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Project::new, child(), projections); + } + + @Override + protected Project replaceChild(LogicalPlan newChild) { + return new Project(location(), newChild, projections); + } + public List projections() { return projections; } @@ -58,7 +69,7 @@ public class Project extends UnaryPlan { } Project other = (Project) obj; - + return Objects.equals(projections, other.projections) && Objects.equals(child(), other.child()); } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/SubQueryAlias.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/SubQueryAlias.java index 97f36c34576..b068d09febf 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/SubQueryAlias.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/SubQueryAlias.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.sql.plan.logical; import org.elasticsearch.xpack.sql.expression.Attribute; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import java.util.List; import java.util.Objects; @@ -22,13 +23,23 @@ public class SubQueryAlias extends UnaryPlan { this.alias = alias; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, SubQueryAlias::new, child(), alias); + } + + @Override + protected SubQueryAlias replaceChild(LogicalPlan newChild) { + return new SubQueryAlias(location(), newChild, alias); + } + public String alias() { return alias; } @Override public List output() { - return (alias == null ? child().output() : + return (alias == null ? child().output() : child().output().stream() .map(e -> e.withQualifier(alias)) .collect(toList()) @@ -42,7 +53,7 @@ public class SubQueryAlias extends UnaryPlan { @Override public int hashCode() { - return Objects.hash(alias, child()); + return Objects.hash(alias, super.hashCode()); } @Override @@ -50,7 +61,7 @@ public class SubQueryAlias extends UnaryPlan { if (!super.equals(obj)) { return false; } - + SubQueryAlias other = (SubQueryAlias) obj; return Objects.equals(alias, other.alias); } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/UnaryPlan.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/UnaryPlan.java index 3b4399e2e9f..e65b9befc2c 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/UnaryPlan.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/UnaryPlan.java @@ -21,6 +21,15 @@ public abstract class UnaryPlan extends LogicalPlan { this.child = child; } + @Override + public final UnaryPlan replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return replaceChild(newChildren.get(0)); + } + protected abstract UnaryPlan replaceChild(LogicalPlan newChild); + public LogicalPlan child() { return child; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/UnresolvedRelation.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/UnresolvedRelation.java index f748bd41354..472503af4b9 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/UnresolvedRelation.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/UnresolvedRelation.java @@ -9,6 +9,7 @@ import org.elasticsearch.xpack.sql.capabilities.Unresolvable; import org.elasticsearch.xpack.sql.expression.Attribute; import org.elasticsearch.xpack.sql.plan.TableIdentifier; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import java.util.Collections; import java.util.List; @@ -31,6 +32,11 @@ public class UnresolvedRelation extends LeafPlan implements Unresolvable { this.unresolvedMsg = unresolvedMessage == null ? "Unknown index [" + table.index() + "]" : unresolvedMessage; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, UnresolvedRelation::new, table, alias, unresolvedMsg); + } + public TableIdentifier table() { return table; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/With.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/With.java index 10ccc5cc0e0..7c76ba4e9ba 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/With.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/With.java @@ -9,6 +9,7 @@ import java.util.Map; import java.util.Objects; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class With extends UnaryPlan { private final Map subQueries; @@ -18,6 +19,16 @@ public class With extends UnaryPlan { this.subQueries = subQueries; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, With::new, child(), subQueries); + } + + @Override + protected With replaceChild(LogicalPlan newChild) { + return new With(location(), newChild, subQueries); + } + public Map subQueries() { return subQueries; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/Command.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/Command.java index 49693be8e17..3cccc76018e 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/Command.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/Command.java @@ -11,12 +11,19 @@ import org.elasticsearch.xpack.sql.tree.Location; import static java.util.Collections.emptyList; +import java.util.List; + public abstract class Command extends LogicalPlan implements Executable { public Command(Location location) { super(location, emptyList()); } + @Override + public final LogicalPlan replaceChildren(List newChildren) { + throw new UnsupportedOperationException("this type of node doesn't have any children to replace"); + } + @Override public boolean expressionsResolved() { return true; diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/Debug.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/Debug.java index a5fca9beabe..eef2baba387 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/Debug.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/Debug.java @@ -17,6 +17,7 @@ import org.elasticsearch.xpack.sql.session.SchemaRowSet; import org.elasticsearch.xpack.sql.session.SqlSession; import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.Node; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.tree.NodeUtils; import org.elasticsearch.xpack.sql.type.DataTypes; import org.elasticsearch.xpack.sql.util.Graphviz; @@ -51,6 +52,11 @@ public class Debug extends Command { this.type = type == null ? Type.OPTIMIZED : type; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Debug::new, plan, type, format); + } + public LogicalPlan plan() { return plan; } @@ -148,4 +154,4 @@ public class Debug extends Command { Debug o = (Debug) obj; return Objects.equals(format, o.format) && Objects.equals(type, o.type) && Objects.equals(plan, o.plan); } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/Explain.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/Explain.java index 3a46126bc9f..87b5a5a8cac 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/Explain.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/Explain.java @@ -18,6 +18,7 @@ import org.elasticsearch.xpack.sql.session.Rows; import org.elasticsearch.xpack.sql.session.SchemaRowSet; import org.elasticsearch.xpack.sql.session.SqlSession; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataTypes; import org.elasticsearch.xpack.sql.util.Graphviz; @@ -58,6 +59,11 @@ public class Explain extends Command { this.type = type == null ? Type.ANALYZED : type; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Explain::new, plan, type, format, verify); + } + public LogicalPlan plan() { return plan; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowColumns.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowColumns.java index 38bad99c6d9..7d04c6018cf 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowColumns.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowColumns.java @@ -8,10 +8,12 @@ package org.elasticsearch.xpack.sql.plan.logical.command; import org.elasticsearch.action.ActionListener; import org.elasticsearch.xpack.sql.expression.Attribute; import org.elasticsearch.xpack.sql.expression.FieldAttribute; +import org.elasticsearch.xpack.sql.plan.logical.LogicalPlan; import org.elasticsearch.xpack.sql.session.Rows; import org.elasticsearch.xpack.sql.session.SchemaRowSet; import org.elasticsearch.xpack.sql.session.SqlSession; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.CompoundDataType; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; @@ -38,6 +40,11 @@ public class ShowColumns extends Command { return index; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, ShowColumns::new, index); + } + @Override public List output() { return asList(new FieldAttribute(location(), "column", DataTypes.KEYWORD), @@ -91,4 +98,4 @@ public class ShowColumns extends Command { ShowColumns other = (ShowColumns) obj; return Objects.equals(index, other.index); } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowFunctions.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowFunctions.java index 4fd4dbc863f..9f06989e1a8 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowFunctions.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowFunctions.java @@ -15,6 +15,7 @@ import org.elasticsearch.xpack.sql.session.Rows; import org.elasticsearch.xpack.sql.session.SchemaRowSet; import org.elasticsearch.xpack.sql.session.SqlSession; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataTypes; import java.util.Collection; @@ -33,6 +34,11 @@ public class ShowFunctions extends Command { this.pattern = pattern; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, ShowFunctions::new, pattern); + } + public LikePattern pattern() { return pattern; } @@ -71,4 +77,4 @@ public class ShowFunctions extends Command { ShowFunctions other = (ShowFunctions) obj; return Objects.equals(pattern, other.pattern); } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowSchemas.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowSchemas.java index 9fd77979dc2..ce0a5f6fd73 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowSchemas.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowSchemas.java @@ -12,6 +12,7 @@ import org.elasticsearch.xpack.sql.session.Rows; import org.elasticsearch.xpack.sql.session.SchemaRowSet; import org.elasticsearch.xpack.sql.session.SqlSession; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataTypes; import java.util.List; @@ -24,6 +25,11 @@ public class ShowSchemas extends Command { super(location); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this); + } + @Override public List output() { return singletonList(new FieldAttribute(location(), "schema", DataTypes.KEYWORD)); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowTables.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowTables.java index 640bc9911a5..3e43b6bdf1c 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowTables.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowTables.java @@ -13,6 +13,7 @@ import org.elasticsearch.xpack.sql.session.Rows; import org.elasticsearch.xpack.sql.session.SchemaRowSet; import org.elasticsearch.xpack.sql.session.SqlSession; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataTypes; import java.util.List; @@ -30,6 +31,11 @@ public class ShowTables extends Command { this.pattern = pattern; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, ShowTables::new, pattern); + } + public LikePattern pattern() { return pattern; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/AggregateExec.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/AggregateExec.java index 66b97e4b675..6814633c7e3 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/AggregateExec.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/AggregateExec.java @@ -9,7 +9,8 @@ import org.elasticsearch.xpack.sql.expression.Attribute; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.Expressions; import org.elasticsearch.xpack.sql.expression.NamedExpression; - +import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import java.util.List; import java.util.Objects; @@ -18,12 +19,23 @@ public class AggregateExec extends UnaryExec implements Unexecutable { private final List groupings; private final List aggregates; - public AggregateExec(PhysicalPlan child, List groupings, List aggregates) { - super(child); + public AggregateExec(Location location, PhysicalPlan child, + List groupings, List aggregates) { + super(location, child); this.groupings = groupings; this.aggregates = aggregates; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, AggregateExec::new, child(), groupings, aggregates); + } + + @Override + protected AggregateExec replaceChild(PhysicalPlan newChild) { + return new AggregateExec(location(), newChild, groupings, aggregates); + } + public List groupings() { return groupings; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/BinaryExec.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/BinaryExec.java index 813f2476fb7..e3b78001ed6 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/BinaryExec.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/BinaryExec.java @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.sql.plan.physical; import java.util.Arrays; +import java.util.List; import java.util.Objects; import org.elasticsearch.xpack.sql.tree.Location; @@ -20,6 +21,15 @@ abstract class BinaryExec extends PhysicalPlan { this.right = right; } + @Override + public final BinaryExec replaceChildren(List newChildren) { + if (newChildren.size() != 2) { + throw new IllegalArgumentException("expected [2] children but received [" + newChildren.size() + "]"); + } + return replaceChildren(newChildren.get(0), newChildren.get(1)); + } + protected abstract BinaryExec replaceChildren(PhysicalPlan newLeft, PhysicalPlan newRight); + public PhysicalPlan left() { return left; } @@ -27,7 +37,7 @@ abstract class BinaryExec extends PhysicalPlan { public PhysicalPlan right() { return right; } - + @Override public int hashCode() { return Objects.hash(left, right); @@ -38,13 +48,13 @@ abstract class BinaryExec extends PhysicalPlan { if (this == obj) { return true; } - + if (obj == null || getClass() != obj.getClass()) { return false; } - + BinaryExec other = (BinaryExec) obj; - return Objects.equals(left, other.left) + return Objects.equals(left, other.left) && Objects.equals(right, other.right); } } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/CommandExec.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/CommandExec.java index 11bab8d1017..3d392eee881 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/CommandExec.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/CommandExec.java @@ -10,6 +10,8 @@ import org.elasticsearch.xpack.sql.expression.Attribute; import org.elasticsearch.xpack.sql.plan.logical.command.Command; import org.elasticsearch.xpack.sql.session.SchemaRowSet; import org.elasticsearch.xpack.sql.session.SqlSession; +import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import java.util.List; import java.util.Objects; @@ -18,11 +20,16 @@ public class CommandExec extends LeafExec { private final Command command; - public CommandExec(Command command) { - super(command.location()); + public CommandExec(Location location, Command command) { + super(location); this.command = command; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, CommandExec::new, command); + } + public Command command() { return command; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/EsQueryExec.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/EsQueryExec.java index 0c7d6c0f161..6e5f95710db 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/EsQueryExec.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/EsQueryExec.java @@ -13,6 +13,7 @@ import org.elasticsearch.xpack.sql.session.Rows; import org.elasticsearch.xpack.sql.session.SchemaRowSet; import org.elasticsearch.xpack.sql.session.SqlSession; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import java.util.List; import java.util.Objects; @@ -31,6 +32,11 @@ public class EsQueryExec extends LeafExec { this.queryContainer = queryContainer; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, EsQueryExec::new, index, output, queryContainer); + } + public EsQueryExec with(QueryContainer queryContainer) { return new EsQueryExec(location(), index, output, queryContainer); } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/FilterExec.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/FilterExec.java index 53c49e6307c..6c2f8523b3f 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/FilterExec.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/FilterExec.java @@ -10,6 +10,8 @@ import java.util.Objects; import org.elasticsearch.xpack.sql.expression.Attribute; import org.elasticsearch.xpack.sql.expression.Expression; +import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class FilterExec extends UnaryExec implements Unexecutable { @@ -18,16 +20,26 @@ public class FilterExec extends UnaryExec implements Unexecutable { // gets setup automatically and then copied over during cloning private final boolean isHaving; - public FilterExec(PhysicalPlan child, Expression condition) { - this(child, condition, child instanceof AggregateExec); + public FilterExec(Location location, PhysicalPlan child, Expression condition) { + this(location, child, condition, child instanceof AggregateExec); } - public FilterExec(PhysicalPlan child, Expression condition, boolean isHaving) { - super(child); + public FilterExec(Location location, PhysicalPlan child, Expression condition, boolean isHaving) { + super(location, child); this.condition = condition; this.isHaving = isHaving; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, FilterExec::new, child(), condition, isHaving); + } + + @Override + protected FilterExec replaceChild(PhysicalPlan newChild) { + return new FilterExec(location(), newChild, condition, isHaving); + } + public Expression condition() { return condition; } @@ -51,11 +63,11 @@ public class FilterExec extends UnaryExec implements Unexecutable { if (this == obj) { return true; } - + if (obj == null || getClass() != obj.getClass()) { return false; } - + FilterExec other = (FilterExec) obj; return Objects.equals(condition, other.condition) && Objects.equals(child(), other.child()); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/LeafExec.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/LeafExec.java index 99fd22a6f45..eec10b307c0 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/LeafExec.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/LeafExec.java @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.sql.plan.physical; import java.util.Collections; +import java.util.List; import org.elasticsearch.xpack.sql.tree.Location; @@ -13,4 +14,9 @@ abstract class LeafExec extends PhysicalPlan { LeafExec(Location location) { super(location, Collections.emptyList()); } + + @Override + public final LeafExec replaceChildren(List newChildren) { + throw new UnsupportedOperationException("this type of node doesn't have any children to replace"); + } } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/LimitExec.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/LimitExec.java index e768fcb62d6..1d4d5a24221 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/LimitExec.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/LimitExec.java @@ -8,16 +8,28 @@ package org.elasticsearch.xpack.sql.plan.physical; import java.util.Objects; import org.elasticsearch.xpack.sql.expression.Expression; +import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class LimitExec extends UnaryExec implements Unexecutable { private final Expression limit; - public LimitExec(PhysicalPlan child, Expression limit) { - super(child); + public LimitExec(Location location, PhysicalPlan child, Expression limit) { + super(location, child); this.limit = limit; } - + + @Override + protected NodeInfo info() { + return NodeInfo.create(this, LimitExec::new, child(), limit); + } + + @Override + protected LimitExec replaceChild(PhysicalPlan newChild) { + return new LimitExec(location(), newChild, limit); + } + public Expression limit() { return limit; } @@ -26,17 +38,17 @@ public class LimitExec extends UnaryExec implements Unexecutable { public int hashCode() { return Objects.hash(limit, child()); } - + @Override public boolean equals(Object obj) { if (this == obj) { return true; } - + if (obj == null || getClass() != obj.getClass()) { return false; } - + LimitExec other = (LimitExec) obj; return Objects.equals(limit, other.limit) && Objects.equals(child(), other.child()); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/LocalExec.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/LocalExec.java index d27bc97ed5d..287d006b5e6 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/LocalExec.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/LocalExec.java @@ -12,6 +12,7 @@ import org.elasticsearch.xpack.sql.session.Executable; import org.elasticsearch.xpack.sql.session.SchemaRowSet; import org.elasticsearch.xpack.sql.session.SqlSession; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import java.util.List; import java.util.Objects; @@ -25,6 +26,11 @@ public class LocalExec extends LeafExec { this.executable = executable; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, LocalExec::new, executable); + } + public Executable executable() { return executable; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/OrderExec.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/OrderExec.java index 21182f03b5f..8b2f224260a 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/OrderExec.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/OrderExec.java @@ -8,17 +8,30 @@ package org.elasticsearch.xpack.sql.plan.physical; import java.util.List; import java.util.Objects; +import org.elasticsearch.xpack.sql.expression.Attribute; import org.elasticsearch.xpack.sql.expression.Order; +import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class OrderExec extends UnaryExec implements Unexecutable { private final List order; - public OrderExec(PhysicalPlan child, List order) { - super(child); + public OrderExec(Location location, PhysicalPlan child, List order) { + super(location, child); this.order = order; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, OrderExec::new, child(), order); + } + + @Override + protected OrderExec replaceChild(PhysicalPlan newChild) { + return new OrderExec(location(), newChild, order); + } + public List order() { return order; } @@ -38,7 +51,7 @@ public class OrderExec extends UnaryExec implements Unexecutable { } OrderExec other = (OrderExec) obj; - + return Objects.equals(order, other.order) && Objects.equals(child(), other.child()); } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/ProjectExec.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/ProjectExec.java index 10977fb8f60..411e6c6a20c 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/ProjectExec.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/ProjectExec.java @@ -11,20 +11,32 @@ import java.util.Objects; import org.elasticsearch.xpack.sql.expression.Attribute; import org.elasticsearch.xpack.sql.expression.Expressions; import org.elasticsearch.xpack.sql.expression.NamedExpression; +import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class ProjectExec extends UnaryExec implements Unexecutable { private final List projections; - public ProjectExec(PhysicalPlan child, List projections) { - super(child); + public ProjectExec(Location location, PhysicalPlan child, List projections) { + super(location, child); this.projections = projections; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, ProjectExec::new, child(), projections); + } + + @Override + protected ProjectExec replaceChild(PhysicalPlan newChild) { + return new ProjectExec(location(), newChild, projections); + } + public List projections() { return projections; } - + @Override public List output() { return Expressions.asAttributes(projections); @@ -45,8 +57,8 @@ public class ProjectExec extends UnaryExec implements Unexecutable { } ProjectExec other = (ProjectExec) obj; - + return Objects.equals(projections, other.projections) && Objects.equals(child(), other.child()); } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/UnaryExec.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/UnaryExec.java index 404cef106e7..942a60b2cd8 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/UnaryExec.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/UnaryExec.java @@ -10,16 +10,26 @@ import java.util.List; import java.util.Objects; import org.elasticsearch.xpack.sql.expression.Attribute; +import org.elasticsearch.xpack.sql.tree.Location; abstract class UnaryExec extends PhysicalPlan { private final PhysicalPlan child; - UnaryExec(PhysicalPlan child) { - super(child.location(), Collections.singletonList(child)); + UnaryExec(Location location, PhysicalPlan child) { + super(location, Collections.singletonList(child)); this.child = child; } + @Override + public final PhysicalPlan replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return replaceChild(newChildren.get(0)); + } + protected abstract UnaryExec replaceChild(PhysicalPlan newChild); + public PhysicalPlan child() { return child; } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/UnplannedExec.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/UnplannedExec.java index e78e63b1a1d..a2a2055da6c 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/UnplannedExec.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plan/physical/UnplannedExec.java @@ -10,16 +10,23 @@ import java.util.Objects; import org.elasticsearch.xpack.sql.expression.Attribute; import org.elasticsearch.xpack.sql.plan.logical.LogicalPlan; +import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; public class UnplannedExec extends LeafExec implements Unexecutable { private final LogicalPlan plan; - public UnplannedExec(LogicalPlan plan) { - super(plan.location()); + public UnplannedExec(Location location, LogicalPlan plan) { + super(location); this.plan = plan; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, UnplannedExec::new, plan); + } + public LogicalPlan plan() { return plan; } @@ -52,4 +59,4 @@ public class UnplannedExec extends LeafExec implements Unexecutable { public String nodeString() { return nodeName() + "[" + plan.nodeString() + "]"; } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/planner/Mapper.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/planner/Mapper.java index caa0734ce37..6a0b96f444a 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/planner/Mapper.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/planner/Mapper.java @@ -36,7 +36,7 @@ import java.util.Arrays; import java.util.List; class Mapper extends RuleExecutor { - + public PhysicalPlan map(LogicalPlan plan) { return execute(planLater(plan)); } @@ -52,7 +52,7 @@ class Mapper extends RuleExecutor { } private static PhysicalPlan planLater(LogicalPlan plan) { - return new UnplannedExec(plan); + return new UnplannedExec(plan.location(), plan); } private static class SimpleExecMapper extends MapExecRule { @@ -60,7 +60,7 @@ class Mapper extends RuleExecutor { @Override protected PhysicalPlan map(LogicalPlan p) { if (p instanceof Command) { - return new CommandExec((Command) p); + return new CommandExec(p.location(), (Command) p); } if (p instanceof LocalRelation) { @@ -69,34 +69,34 @@ class Mapper extends RuleExecutor { if (p instanceof Project) { Project pj = (Project) p; - return new ProjectExec(map(pj.child()), pj.projections()); + return new ProjectExec(p.location(), map(pj.child()), pj.projections()); } if (p instanceof Filter) { Filter fl = (Filter) p; - return new FilterExec(map(fl.child()), fl.condition()); + return new FilterExec(p.location(), map(fl.child()), fl.condition()); } if (p instanceof OrderBy) { OrderBy o = (OrderBy) p; - return new OrderExec(map(o.child()), o.order()); + return new OrderExec(p.location(), map(o.child()), o.order()); } - + if (p instanceof Aggregate) { Aggregate a = (Aggregate) p; // analysis and optimizations have converted the grouping into actual attributes - return new AggregateExec(map(a.child()), a.groupings(), a.aggregates()); + return new AggregateExec(p.location(), map(a.child()), a.groupings(), a.aggregates()); } if (p instanceof EsRelation) { EsRelation c = (EsRelation) p; List output = c.output(); - return new EsQueryExec(c.location(), c.index().name(), output, new QueryContainer()); + return new EsQueryExec(p.location(), c.index().name(), output, new QueryContainer()); } if (p instanceof Limit) { Limit l = (Limit) p; - return new LimitExec(map(l.child()), l.limit()); + return new LimitExec(p.location(), map(l.child()), l.limit()); } // TODO: Translate With in a subplan if (p instanceof With) { @@ -146,4 +146,4 @@ class Mapper extends RuleExecutor { protected abstract PhysicalPlan map(SubPlan plan); } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/planner/QueryFolder.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/planner/QueryFolder.java index fc84608c3b4..4ed09897700 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/planner/QueryFolder.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/planner/QueryFolder.java @@ -196,8 +196,9 @@ class QueryFolder extends RuleExecutor { } if (groupAgg == null) { + // Weird ctor call to make sure we don't interpret the message as a pattern throw new FoldingException(fexec, "Cannot find group for agg " + refId - + " referrenced by agg filter " + filter.name() + "(" + filter + ")"); + + " referrenced by agg filter " + filter.name() + "(" + filter + ")", (Exception) null); } String path = groupAgg.asParentPath(); @@ -322,7 +323,7 @@ class QueryFolder extends RuleExecutor { if (exp instanceof DateTimeHistogramFunction) { action = ((UnaryProcessorDefinition) p).action(); } - return new AggPathInput(exp, matchingGroup.propertyPath(), null, action); + return new AggPathInput(exp.location(), exp, matchingGroup.propertyPath(), null, action); } } // or found an aggregate expression (which has to work on an attribute used for grouping) diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/AggFilter.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/AggFilter.java index 1b8f6c99d61..60f621b38a3 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/AggFilter.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/AggFilter.java @@ -56,11 +56,11 @@ public class AggFilter extends PipelineAgg { if (this == obj) { return true; } - + if (obj == null || getClass() != obj.getClass()) { return false; } - + AggFilter other = (AggFilter) obj; return Objects.equals(name(), other.name()) && Objects.equals(scriptTemplate(), other.scriptTemplate()); @@ -70,4 +70,4 @@ public class AggFilter extends PipelineAgg { public String toString() { return scriptTemplate.toString(); } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/Aggs.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/Aggs.java index ccb97f3d692..200dc7494c0 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/Aggs.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/Aggs.java @@ -226,4 +226,4 @@ public class Aggs { && Objects.equals(rootPipelineAggs, other.rootPipelineAggs) && Objects.equals(groups, other.groups); } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/querydsl/container/QueryContainer.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/querydsl/container/QueryContainer.java index 7c6394e123a..02481a96f82 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/querydsl/container/QueryContainer.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/querydsl/container/QueryContainer.java @@ -281,7 +281,7 @@ public class QueryContainer { return new Tuple<>(this, new ComputedRef(((LiteralAttribute) attr).asProcessorDefinition())); } if (attr instanceof ScoreAttribute) { - return new Tuple<>(this, new ComputedRef(new ScoreProcessorDefinition(attr))); + return new Tuple<>(this, new ComputedRef(new ScoreProcessorDefinition(attr.location(), attr))); } throw new SqlIllegalArgumentException("Unknown output attribute %s", attr); diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/tree/Node.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/tree/Node.java index f1d72ab839b..c0d885c6dcc 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/tree/Node.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/tree/Node.java @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.sql.tree; import java.util.ArrayList; +import java.util.BitSet; import java.util.List; import java.util.Objects; import java.util.function.Consumer; @@ -18,24 +19,22 @@ import static java.util.Collections.emptyList; * Immutable tree structure. * The traversal is done depth-first, pre-order (first the node then its children), that is seeks up and then goes down. * Alternative method for post-order (children first, then node) is also offered, that is seeks down and then goes up. - * + * * Allows transformation which returns the same tree (if no change has been performed) or a new tree otherwise. - * + * * While it tries as much as possible to use functional Java, due to lack of parallelism, * the use of streams and iterators is not really useful and brings too much baggage which - * might be used incorrectly. - * + * might be used incorrectly. + * * @param node type */ public abstract class Node> { + private static final int TO_STRING_MAX_PROP = 10; + private static final int TO_STRING_MAX_WIDTH = 110; private final Location location; private final List children; - public Node(List children) { - this(Location.EMPTY, children); - } - public Node(Location location, List children) { this.location = (location != null ? location : Location.EMPTY); this.children = children; @@ -58,8 +57,8 @@ public abstract class Node> { @SuppressWarnings("unchecked") public void forEachDown(Consumer action, final Class typeToken) { forEachDown(t -> { - if (typeToken.isInstance(t)) { - action.accept((E) t); + if (typeToken.isInstance(t)) { + action.accept((E) t); } }); } @@ -93,7 +92,7 @@ public abstract class Node> { @SuppressWarnings("unchecked") protected void forEachProperty(Consumer rule, Class typeToken) { - for (Object prop : NodeUtils.properties(this)) { + for (Object prop : info().properties()) { // skip children (only properties are interesting) if (prop != children && !children.contains(prop) && typeToken.isInstance(prop)) { rule.accept((E) prop); @@ -147,7 +146,7 @@ public abstract class Node> { } } - // TODO: maybe add a flatMap (need to double check the Stream bit) + // TODO: maybe add a flatMap (need to double check the Stream bit) // // Transform methods @@ -189,7 +188,7 @@ public abstract class Node> { boolean childrenChanged = false; // stream() could be used but the code is just as complicated without any advantages - // further more, it would include bring in all the associated stream/collector object creation even though in + // further more, it would include bring in all the associated stream/collector object creation even though in // most cases the immediate tree would be quite small (0,1,2 elements) List transformedChildren = new ArrayList<>(children().size()); @@ -208,9 +207,10 @@ public abstract class Node> { return (childrenChanged ? replaceChildren(transformedChildren) : (T) this); } - public T replaceChildren(List newChildren) { - return NodeUtils.copyTree(this, newChildren); - } + /** + * Replace the children of this node. + */ + public abstract T replaceChildren(List newChildren); // // transform the node properties and use the tree only for navigation @@ -228,26 +228,22 @@ public abstract class Node> { return transformUp(t -> t.transformNodeProps(rule, typeToken)); } - @SuppressWarnings("unchecked") - protected T transformNodeProps(Function rule, Class typeToken) { - Object[] props = NodeUtils.properties(this); - boolean changed = false; - - for (int i = 0; i < props.length; i++) { - Object prop = props[i]; - // skip children (only properties are interesting) - if (prop != children && !children.contains(prop) && typeToken.isInstance(prop)) { - Object transformed = rule.apply((E) prop); - if (!prop.equals(transformed)) { - changed = true; - props[i] = transformed; - } - } - } - - return changed ? NodeUtils.cloneNode(this, props) : (T) this; + /** + * Transform this node's properties. + *

+ * This always returns something of the same type as the current + * node but since {@link Node} doesn't have a {@code SelfT} parameter + * we return the closest thing we do have: {@code T}, which is the + * root of the hierarchy for the this node. + */ + protected final T transformNodeProps(Function rule, Class typeToken) { + return info().transform(rule, typeToken); } + /** + * Return the information about this node. + */ + protected abstract NodeInfo info(); @Override public int hashCode() { @@ -273,11 +269,118 @@ public abstract class Node> { } public String nodeString() { - return NodeUtils.nodeString(this); + StringBuilder sb = new StringBuilder(); + sb.append(nodeName()); + sb.append("["); + sb.append(propertiesToString(true)); + sb.append("]"); + return sb.toString(); } @Override public String toString() { - return NodeUtils.toString(this); + return treeString(new StringBuilder(), 0, new BitSet()).toString(); } -} \ No newline at end of file + + /** + * Render this {@link Node} as a tree like + *

+     * {@code
+     * Project[[i{f}#0]]
+     * \_Filter[i{f}#1]
+     *   \_SubQueryAlias[test]
+     *     \_EsRelation[test][i{f}#2]
+     * }
+     * 
+ */ + final StringBuilder treeString(StringBuilder sb, int depth, BitSet hasParentPerDepth) { + if (depth > 0) { + // draw children + for (int column = 0; column < depth; column++) { + if (hasParentPerDepth.get(column)) { + sb.append("|"); + // if not the last elder, adding padding (since each column has two chars ("|_" or "\_") + if (column < depth - 1) { + sb.append(" "); + } + } + else { + // if the child has no parent (elder on the previous level), it means its the last sibling + sb.append((column == depth - 1) ? "\\" : " "); + } + } + + sb.append("_"); + } + + sb.append(nodeString()); + + List children = children(); + if (!children.isEmpty()) { + sb.append("\n"); + } + for (int i = 0; i < children.size(); i++) { + T t = children.get(i); + hasParentPerDepth.set(depth, i < children.size() - 1); + t.treeString(sb, depth + 1, hasParentPerDepth); + if (i < children.size() - 1) { + sb.append("\n"); + } + } + return sb; + } + + /** + * Render the properties of this {@link Node} one by + * one like {@code foo bar baz}. These go inside the + * {@code [} and {@code ]} of the output of {@link #treeString}. + */ + public String propertiesToString(boolean skipIfChild) { + NodeInfo> info = info(); + StringBuilder sb = new StringBuilder(); + + List children = children(); + // eliminate children (they are rendered as part of the tree) + int remainingProperties = TO_STRING_MAX_PROP; + int maxWidth = 0; + boolean needsComma = false; + + List props = info.properties(); + for (Object prop : props) { + // consider a property if it is not ignored AND + // it's not a child (optional) + if (!(skipIfChild && (children.contains(prop) || children.equals(prop)))) { + if (remainingProperties-- < 0) { + sb.append("...").append(props.size() - TO_STRING_MAX_PROP).append("fields not shown"); + break; + } + + if (needsComma) { + sb.append(","); + } + String stringValue = Objects.toString(prop); + if (maxWidth + stringValue.length() > TO_STRING_MAX_WIDTH) { + int cutoff = Math.max(0, TO_STRING_MAX_WIDTH - maxWidth); + sb.append(stringValue.substring(0, cutoff)); + sb.append("\n"); + stringValue = stringValue.substring(cutoff); + maxWidth = 0; + } + maxWidth += stringValue.length(); + sb.append(stringValue); + + needsComma = true; + } + } + + return sb.toString(); + } + + /** + * The values of all the properties that are important + * to this {@link Node}. + */ + public List properties() { + return info().properties(); + } +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/tree/NodeInfo.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/tree/NodeInfo.java new file mode 100644 index 00000000000..23f29137cff --- /dev/null +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/tree/NodeInfo.java @@ -0,0 +1,403 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.sql.tree; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.function.BiFunction; +import java.util.function.Function; + +import static java.util.Collections.emptyList; +import static java.util.Collections.unmodifiableList; + +/** + * Information about a {@link Node}. + *

+ * All the uses of this are fairly non-OO and we're looking + * for ways to use this less and less. + *

+ * The implementations of this class are super copy-and-paste-ish + * but they are better then the sneaky reflection tricks we had + * earlier. Still terrifying. + * + * @param actual subclass of node that produced this {@linkplain NodeInfo} + */ +public abstract class NodeInfo> { + protected final T node; + + private NodeInfo(T node) { + this.node = node; + } + + /** + * Values for all properties on the instance that created + * this {@linkplain NodeInfo}. + */ + public final List properties() { + return unmodifiableList(innerProperties()); + } + protected abstract List innerProperties(); + + /** + * Transform the properties on {@code node}, returning a new instance + * of {@code N} if any properties change. + */ + final T transform(Function rule, Class typeToken) { + List children = node.children(); + + Function realRule = p -> { + if (p != children && false == children.contains(p) + && (p == null || typeToken.isInstance(p))) { + return rule.apply(typeToken.cast(p)); + } + return p; + }; + return innerTransform(realRule); + } + protected abstract T innerTransform(Function rule); + + /** + * Builds a {@link NodeInfo} for Nodes without any properties. + */ + public static > NodeInfo create(T n) { + return new NodeInfo(n) { + @Override + protected List innerProperties() { + return emptyList(); + } + + protected T innerTransform(Function rule) { + return node; + } + }; + } + + public static , P1> NodeInfo create( + T n, BiFunction ctor, + P1 p1) { + return new NodeInfo(n) { + @Override + protected List innerProperties() { + return Arrays.asList(p1); + } + + protected T innerTransform(Function rule) { + boolean same = true; + + @SuppressWarnings("unchecked") + P1 newP1 = (P1) rule.apply(p1); + same &= Objects.equals(p1, newP1); + + return same ? node : ctor.apply(node.location(), newP1); + } + }; + } + + public static , P1, P2> NodeInfo create( + T n, NodeCtor2 ctor, + P1 p1, P2 p2) { + return new NodeInfo(n) { + @Override + protected List innerProperties() { + return Arrays.asList(p1, p2); + } + + protected T innerTransform(Function rule) { + boolean same = true; + + @SuppressWarnings("unchecked") + P1 newP1 = (P1) rule.apply(p1); + same &= Objects.equals(p1, newP1); + @SuppressWarnings("unchecked") + P2 newP2 = (P2) rule.apply(p2); + same &= Objects.equals(p2, newP2); + + return same ? node : ctor.apply(node.location(), newP1, newP2); + } + }; + } + public interface NodeCtor2 { + T apply(Location l, P1 p1, P2 p2); + } + + public static , P1, P2, P3> NodeInfo create( + T n, NodeCtor3 ctor, + P1 p1, P2 p2, P3 p3) { + return new NodeInfo(n) { + @Override + protected List innerProperties() { + return Arrays.asList(p1, p2, p3); + } + + protected T innerTransform(Function rule) { + boolean same = true; + + @SuppressWarnings("unchecked") + P1 newP1 = (P1) rule.apply(p1); + same &= Objects.equals(p1, newP1); + @SuppressWarnings("unchecked") + P2 newP2 = (P2) rule.apply(p2); + same &= Objects.equals(p2, newP2); + @SuppressWarnings("unchecked") + P3 newP3 = (P3) rule.apply(p3); + same &= Objects.equals(p3, newP3); + + return same ? node : ctor.apply(node.location(), newP1, newP2, newP3); + } + }; + } + public interface NodeCtor3 { + T apply(Location l, P1 p1, P2 p2, P3 p3); + } + + public static , P1, P2, P3, P4> NodeInfo create( + T n, NodeCtor4 ctor, + P1 p1, P2 p2, P3 p3, P4 p4) { + return new NodeInfo(n) { + @Override + protected List innerProperties() { + return Arrays.asList(p1, p2, p3, p4); + } + + protected T innerTransform(Function rule) { + boolean same = true; + + @SuppressWarnings("unchecked") + P1 newP1 = (P1) rule.apply(p1); + same &= Objects.equals(p1, newP1); + @SuppressWarnings("unchecked") + P2 newP2 = (P2) rule.apply(p2); + same &= Objects.equals(p2, newP2); + @SuppressWarnings("unchecked") + P3 newP3 = (P3) rule.apply(p3); + same &= Objects.equals(p3, newP3); + @SuppressWarnings("unchecked") + P4 newP4 = (P4) rule.apply(p4); + same &= Objects.equals(p4, newP4); + + return same ? node : ctor.apply(node.location(), newP1, newP2, newP3, newP4); + } + }; + } + public interface NodeCtor4 { + T apply(Location l, P1 p1, P2 p2, P3 p3, P4 p4); + } + + public static , P1, P2, P3, P4, P5> NodeInfo create( + T n, NodeCtor5 ctor, + P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) { + return new NodeInfo(n) { + @Override + protected List innerProperties() { + return Arrays.asList(p1, p2, p3, p4, p5); + } + + protected T innerTransform(Function rule) { + boolean same = true; + + @SuppressWarnings("unchecked") + P1 newP1 = (P1) rule.apply(p1); + same &= Objects.equals(p1, newP1); + @SuppressWarnings("unchecked") + P2 newP2 = (P2) rule.apply(p2); + same &= Objects.equals(p2, newP2); + @SuppressWarnings("unchecked") + P3 newP3 = (P3) rule.apply(p3); + same &= Objects.equals(p3, newP3); + @SuppressWarnings("unchecked") + P4 newP4 = (P4) rule.apply(p4); + same &= Objects.equals(p4, newP4); + @SuppressWarnings("unchecked") + P5 newP5 = (P5) rule.apply(p5); + same &= Objects.equals(p5, newP5); + + return same ? node : ctor.apply(node.location(), newP1, newP2, newP3, newP4, newP5); + } + }; + } + public interface NodeCtor5 { + T apply(Location l, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5); + } + + public static , P1, P2, P3, P4, P5, P6> NodeInfo create( + T n, NodeCtor6 ctor, + P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) { + return new NodeInfo(n) { + @Override + protected List innerProperties() { + return Arrays.asList(p1, p2, p3, p4, p5, p6); + } + + protected T innerTransform(Function rule) { + boolean same = true; + + @SuppressWarnings("unchecked") + P1 newP1 = (P1) rule.apply(p1); + same &= Objects.equals(p1, newP1); + @SuppressWarnings("unchecked") + P2 newP2 = (P2) rule.apply(p2); + same &= Objects.equals(p2, newP2); + @SuppressWarnings("unchecked") + P3 newP3 = (P3) rule.apply(p3); + same &= Objects.equals(p3, newP3); + @SuppressWarnings("unchecked") + P4 newP4 = (P4) rule.apply(p4); + same &= Objects.equals(p4, newP4); + @SuppressWarnings("unchecked") + P5 newP5 = (P5) rule.apply(p5); + same &= Objects.equals(p5, newP5); + @SuppressWarnings("unchecked") + P6 newP6 = (P6) rule.apply(p6); + same &= Objects.equals(p6, newP6); + + return same ? node : ctor.apply(node.location(), newP1, newP2, newP3, newP4, newP5, newP6); + } + }; + } + public interface NodeCtor6 { + T apply(Location l, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6); + } + + public static , P1, P2, P3, P4, P5, P6, P7> NodeInfo create( + T n, NodeCtor7 ctor, + P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7) { + return new NodeInfo(n) { + @Override + protected List innerProperties() { + return Arrays.asList(p1, p2, p3, p4, p5, p6, p7); + } + + protected T innerTransform(Function rule) { + boolean same = true; + + @SuppressWarnings("unchecked") + P1 newP1 = (P1) rule.apply(p1); + same &= Objects.equals(p1, newP1); + @SuppressWarnings("unchecked") + P2 newP2 = (P2) rule.apply(p2); + same &= Objects.equals(p2, newP2); + @SuppressWarnings("unchecked") + P3 newP3 = (P3) rule.apply(p3); + same &= Objects.equals(p3, newP3); + @SuppressWarnings("unchecked") + P4 newP4 = (P4) rule.apply(p4); + same &= Objects.equals(p4, newP4); + @SuppressWarnings("unchecked") + P5 newP5 = (P5) rule.apply(p5); + same &= Objects.equals(p5, newP5); + @SuppressWarnings("unchecked") + P6 newP6 = (P6) rule.apply(p6); + same &= Objects.equals(p6, newP6); + @SuppressWarnings("unchecked") + P7 newP7 = (P7) rule.apply(p7); + same &= Objects.equals(p7, newP7); + + return same ? node : ctor.apply(node.location(), newP1, newP2, newP3, newP4, newP5, newP6, newP7); + } + }; + } + public interface NodeCtor7 { + T apply(Location l, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7); + } + + public static , P1, P2, P3, P4, P5, P6, P7, P8> NodeInfo create( + T n, NodeCtor8 ctor, + P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8) { + return new NodeInfo(n) { + @Override + protected List innerProperties() { + return Arrays.asList(p1, p2, p3, p4, p5, p6, p7, p8); + } + + protected T innerTransform(Function rule) { + boolean same = true; + + @SuppressWarnings("unchecked") + P1 newP1 = (P1) rule.apply(p1); + same &= Objects.equals(p1, newP1); + @SuppressWarnings("unchecked") + P2 newP2 = (P2) rule.apply(p2); + same &= Objects.equals(p2, newP2); + @SuppressWarnings("unchecked") + P3 newP3 = (P3) rule.apply(p3); + same &= Objects.equals(p3, newP3); + @SuppressWarnings("unchecked") + P4 newP4 = (P4) rule.apply(p4); + same &= Objects.equals(p4, newP4); + @SuppressWarnings("unchecked") + P5 newP5 = (P5) rule.apply(p5); + same &= Objects.equals(p5, newP5); + @SuppressWarnings("unchecked") + P6 newP6 = (P6) rule.apply(p6); + same &= Objects.equals(p6, newP6); + @SuppressWarnings("unchecked") + P7 newP7 = (P7) rule.apply(p7); + same &= Objects.equals(p7, newP7); + @SuppressWarnings("unchecked") + P8 newP8 = (P8) rule.apply(p8); + same &= Objects.equals(p8, newP8); + + return same ? node : ctor.apply(node.location(), newP1, newP2, newP3, newP4, newP5, newP6, newP7, newP8); + } + }; + } + public interface NodeCtor8 { + T apply(Location l, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8); + } + + public static , P1, P2, P3, P4, P5, P6, P7, P8, P9, P10> NodeInfo create( + T n, NodeCtor10 ctor, + P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9, P10 p10) { + return new NodeInfo(n) { + @Override + protected List innerProperties() { + return Arrays.asList(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); + } + + protected T innerTransform(Function rule) { + boolean same = true; + + @SuppressWarnings("unchecked") + P1 newP1 = (P1) rule.apply(p1); + same &= Objects.equals(p1, newP1); + @SuppressWarnings("unchecked") + P2 newP2 = (P2) rule.apply(p2); + same &= Objects.equals(p2, newP2); + @SuppressWarnings("unchecked") + P3 newP3 = (P3) rule.apply(p3); + same &= Objects.equals(p3, newP3); + @SuppressWarnings("unchecked") + P4 newP4 = (P4) rule.apply(p4); + same &= Objects.equals(p4, newP4); + @SuppressWarnings("unchecked") + P5 newP5 = (P5) rule.apply(p5); + same &= Objects.equals(p5, newP5); + @SuppressWarnings("unchecked") + P6 newP6 = (P6) rule.apply(p6); + same &= Objects.equals(p6, newP6); + @SuppressWarnings("unchecked") + P7 newP7 = (P7) rule.apply(p7); + same &= Objects.equals(p7, newP7); + @SuppressWarnings("unchecked") + P8 newP8 = (P8) rule.apply(p8); + same &= Objects.equals(p8, newP8); + @SuppressWarnings("unchecked") + P9 newP9 = (P9) rule.apply(p9); + same &= Objects.equals(p9, newP9); + @SuppressWarnings("unchecked") + P10 newP10 = (P10) rule.apply(p10); + same &= Objects.equals(p10, newP10); + + return same ? node : ctor.apply(node.location(), newP1, newP2, newP3, newP4, newP5, newP6, newP7, newP8, + newP9, newP10); + } + }; + } + public interface NodeCtor10 { + T apply(Location l, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9, P10 p10); + } +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/tree/NodeUtils.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/tree/NodeUtils.java index 2c93a1cb463..f2cb74f6abc 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/tree/NodeUtils.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/tree/NodeUtils.java @@ -5,328 +5,7 @@ */ package org.elasticsearch.xpack.sql.tree; -import org.elasticsearch.common.util.CollectionUtils; -import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; -import org.elasticsearch.xpack.sql.util.Check; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Parameter; -import java.util.BitSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Set; - -import static java.lang.String.format; - public abstract class NodeUtils { - - public static class NodeInfo { - public final Constructor ctr; - public final Map params; - private final int childrenIndex; - - NodeInfo(Constructor ctr, Map params, int childrenIndex) { - this.ctr = ctr; - this.params = params; - this.childrenIndex = childrenIndex; - } - }; - - private static final String TO_STRING_IGNORE_PROP = "location"; - private static final int TO_STRING_MAX_PROP = 10; - private static final int TO_STRING_MAX_WIDTH = 110; - - private static final Map, NodeInfo> CACHE = new LinkedHashMap<>(); - - /** - * - * make a modified copy of a tree node by replacing its children. - * to do so it instantiates the class with the new values assuming it will 'replay' the creation. - * as any child might be also a field the method uses the following convention: - * - * 1. the children are created through constructor alone - * 2. any children referenced through fields are also present in the children list - * 3. the list of children is created through the constructor - * 4. all the constructor arguments are available on the given instance through public methods using the same name. - * - * As an example: - * - *

-     * class Add extends Node<T> {
-     *   private Literal left;
-     *   private Literal right;
-     *
-     *   public Add(Literal left, Literal right) {
-     *     this.left = left;
-     *     this.right = right;
-     *   }
-     *
-     *   public Literal left() { return left; }
-     *   public Literal right() { return right; }
-     * }
-     * 
- */ - static > T copyTree(Node tree, List newChildren) { - Check.notNull(tree, "Non-null tree expected"); - - // basic sanity check - List currentChildren = tree.children(); - Check.isTrue(currentChildren.size() == newChildren.size(), "Cannot make copy; expected %s children but received %s", - currentChildren.size(), newChildren.size()); - - NodeInfo info = info(tree.getClass()); - Object[] props = properties(tree, info); - - // for each parameter, look in the list of children to find it - // if it's missing, it's added as is, otherwise it gets picked up from the new ones - for (int i = 0; i < props.length; i++) { - Object property = props[i]; - - // in the rare case (UnresolvedFunction) the children are specified, copy them directly in the constructor - if (i == info.childrenIndex) { - props[i] = newChildren; - } - // check children only if needed - else if (property instanceof Node) { - // as the same instances are inside the children, an identity check is done instead of the usual equals - for (int childIndex = 0; childIndex < currentChildren.size(); childIndex++) { - T child = currentChildren.get(childIndex); - // replace old property with the new one - if (property == child) { - props[i] = newChildren.get(childIndex); - // skip the rest of the children, if there are duplicates, will find them on their turn - break; - } - } - } - } - - return cloneNode(tree, props); - } - - - @SuppressWarnings("unchecked") - static > T cloneNode(Node tree, Object[] props) { - NodeInfo treeNodeInfo = info(tree.getClass()); - - // finally invoke the constructor and return the new copy - try { - return (T) treeNodeInfo.ctr.newInstance(props); - } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { - throw new SqlIllegalArgumentException("Cannot call constructor %s to copy tree node", - treeNodeInfo.ctr.toGenericString(), ex); - } - } - - @SuppressWarnings("rawtypes") - public static NodeInfo info(Class clazz) { - NodeInfo treeNodeInfo = CACHE.get(clazz); - - // perform discovery (and cache it) - if (treeNodeInfo == null) { - Constructor[] constructors = clazz.getConstructors(); - Check.isTrue(!CollectionUtils.isEmpty(constructors), "No public constructors found for class %s", clazz); - - // find the longest constructor - Constructor ctr = null; - int maxParameterCount = -1; - for (Constructor constructor : constructors) { - if (ctr == null || maxParameterCount < constructor.getParameterCount()) { - ctr = constructor; - maxParameterCount = constructor.getParameterCount(); - } - } - - int childrenIndex = -1; - - Map params = new LinkedHashMap<>(ctr.getParameterCount()); - - // find each argument in the ctr and find its relevant method/getter - Parameter[] parameters = ctr.getParameters(); - for (int paramIndex = 0; paramIndex < parameters.length; paramIndex++) { - Parameter param = parameters[paramIndex]; - Check.isTrue(param.isNamePresent(), - "Can't find constructor parameter names for [%s]. Is class debug information available?", - clazz.toGenericString()); - String paramName = param.getName(); - - if (paramName.equals("children")) { - childrenIndex = paramIndex; - } - // find getter for it - Method getter = null; - try { - getter = clazz.getMethod(paramName); - } catch (NoSuchMethodException nsme) { - throw new SqlIllegalArgumentException( - "class [%s] expected to have method [%s] for retrieving constructor arguments; none found", - clazz.getName(), paramName); - } - - // validate return type - Class expected = param.getType(); - Class found = getter.getReturnType(); - // found == Object if we're dealing with generics - Check.isTrue(found == Object.class || expected.isAssignableFrom(found), - "Constructor param [%s] in class [%s] has type [%s] but found getter [%s]", paramName, clazz, - expected, getter.toGenericString()); - - params.put(paramName, getter); - } - - treeNodeInfo = new NodeInfo(ctr, params, childrenIndex); - CACHE.put(clazz, treeNodeInfo); - } - - return treeNodeInfo; - } - - public static Map propertiesMap(Node tree) { - NodeInfo info = info(tree.getClass()); - Object[] results = properties(tree, info); - - Map props = new LinkedHashMap<>(results.length); - - int index = 0; - for (String name : info.params.keySet()) { - props.put(name, results[index++]); - } - return props; - } - - static Object[] properties(Node tree) { - return properties(tree, info(tree.getClass())); - } - - // minor optimization to avoid double map lookup inside this class - private static Object[] properties(Node tree, NodeInfo info) { - Object[] props = new Object[info.params.size()]; - int copyIndex = 0; - - for (Entry param : info.params.entrySet()) { - Method getter = param.getValue(); - Object result; - try { - result = getter.invoke(tree); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { - throw new SqlIllegalArgumentException("Cannot invoke method [%s]", getter.toGenericString(), ex); - } - - props[copyIndex++] = result; - } - - return props; - } - - public static String propertiesToString(Node tree, boolean skipIfChild) { - StringBuilder sb = new StringBuilder(); - - NodeInfo info = info(tree.getClass()); - Set keySet = info.params.keySet(); - Object[] properties = properties(tree, info); - - List children = tree.children(); - // eliminate children (they are rendered as part of the tree) - int maxProperties = TO_STRING_MAX_PROP; - int maxWidth = 0; - Iterator nameIterator = keySet.iterator(); - boolean needsComma = false; - - for (Object property : properties) { - Object object = property; - String propertyName = nameIterator.next(); - // consider a property if it is not ignored AND - // it's not a child (optional) - if (!TO_STRING_IGNORE_PROP.equals(propertyName) && !(skipIfChild && children.contains(object))) { - if (maxProperties-- < 0) { - sb.append(format(Locale.ROOT, "...%s fields not shown", properties.length - TO_STRING_MAX_PROP)); - break; - } - - if (needsComma) { - sb.append(","); - } - String stringValue = Objects.toString(object); - if (maxWidth + stringValue.length() > TO_STRING_MAX_WIDTH) { - int cutoff = Math.max(0, TO_STRING_MAX_WIDTH - maxWidth); - sb.append(stringValue.substring(0, cutoff)); - sb.append("\n"); - stringValue = stringValue.substring(cutoff); - maxWidth = 0; - } - maxWidth += stringValue.length(); - sb.append(stringValue); - - needsComma = true; - } - } - - return sb.toString(); - } - - static String nodeString(Node treeNode) { - StringBuilder sb = new StringBuilder(); - sb.append(treeNode.nodeName()); - sb.append("["); - sb.append(propertiesToString(treeNode, true)); - sb.append("]"); - return sb.toString(); - } - - static String toString(Node treeNode) { - return treeString(treeNode, new StringBuilder(), 0, new BitSet()).toString(); - } - - static > StringBuilder treeString(Node treeNode, StringBuilder sb, int depth, BitSet hasParentPerDepth) { - if (depth > 0) { - // draw children - for (int column = 0; column < depth; column++) { - if (hasParentPerDepth.get(column)) { - sb.append("|"); - // if not the last elder, adding padding (since each column has two chars ("|_" or "\_") - if (column < depth - 1) { - sb.append(" "); - } - } - else { - // if the child has no parent (elder on the previous level), it means its the last sibling - sb.append((column == depth - 1) ? "\\" : " "); - } - } - - sb.append("_"); - } - - if (treeNode == null) { - sb.append("null"); - return sb; - } - - // TreeNode by name (to allow nodes to override their expression) - sb.append(treeNode.nodeString()); - - List children = treeNode.children(); - if (!children.isEmpty()) { - sb.append("\n"); - } - for (int i = 0; i < children.size(); i++) { - T t = children.get(i); - hasParentPerDepth.set(depth, i < children.size() - 1); - treeString(t, sb, depth + 1, hasParentPerDepth); - if (i < children.size() - 1) { - sb.append("\n"); - } - } - return sb; - } - public static , B extends Node> String diffString(A left, B right) { return diffString(left.toString(), right.toString()); } diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java index 213dc420ebd..37223cc2d66 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java @@ -264,21 +264,21 @@ public abstract class DataTypeConversion { public static byte safeToByte(long x) { if (x > Byte.MAX_VALUE || x < Byte.MIN_VALUE) { - throw new SqlIllegalArgumentException("Numeric %d out of byte range", Long.toString(x)); + throw new SqlIllegalArgumentException("[" + x + "] out of [Byte] range"); } return (byte) x; } public static short safeToShort(long x) { if (x > Short.MAX_VALUE || x < Short.MIN_VALUE) { - throw new SqlIllegalArgumentException("Numeric %d out of short range", Long.toString(x)); + throw new SqlIllegalArgumentException("[" + x + "] out of [Short] range"); } return (short) x; } public static int safeToInt(long x) { if (x > Integer.MAX_VALUE || x < Integer.MIN_VALUE) { - throw new SqlIllegalArgumentException("numeric %d out of int range", Long.toString(x)); + throw new SqlIllegalArgumentException("[" + x + "] out of [Int] range"); } return (int) x; } @@ -389,4 +389,4 @@ public abstract class DataTypeConversion { return dataType.isInteger() ? dataType : DataTypes.LONG; } -} \ No newline at end of file +} diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/util/Graphviz.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/util/Graphviz.java index feb953bd5d9..97e84efa3b0 100644 --- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/util/Graphviz.java +++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/util/Graphviz.java @@ -7,14 +7,12 @@ package org.elasticsearch.xpack.sql.util; import java.util.ArrayList; import java.util.Collection; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicInteger; import org.elasticsearch.xpack.sql.tree.Node; -import org.elasticsearch.xpack.sql.tree.NodeUtils; // use the awesome http://mdaines.github.io/viz.js/ to visualize and play around with the various options public abstract class Graphviz { @@ -47,7 +45,7 @@ public abstract class Graphviz { + "node[shape=plaintext, color=azure1];\n " + "edge[color=black];\n " + "graph[compound=true];\n\n"); - + int clusterNodeStart = 1; int clusterId = 0; @@ -79,9 +77,9 @@ public abstract class Graphviz { sb.append(" -> "); sb.append("c" + clusterId); sb.append(" [style=invis];\n"); - + handleNode(sb, entry.getValue(), nodeCounter, CLUSTER_INDENT, drawSubTrees); - + int clusterNodeStop = nodeCounter.get(); indent(sb, INDENT); @@ -103,7 +101,7 @@ public abstract class Graphviz { } sb.append("\n"); - + // connecting the clusters arranges them in a weird position // so don't //sb.append(clusterEdges.toString()); @@ -133,22 +131,20 @@ public abstract class Graphviz { + n.nodeName() + "\n"); indent(nodeInfo, currentIndent + NODE_LABEL_INDENT); - - Map props = NodeUtils.propertiesMap(n); - Map parsed = new LinkedHashMap<>(props.size()); + + List props = n.properties(); + List parsed = new ArrayList<>(props.size()); List> subTrees = new ArrayList<>(); - for (Entry entry : props.entrySet()) { - Object v = entry.getValue(); - + for (Object v : props) { // skip null values, children and location - if (v != null && !n.children().contains(v) && !"location".equals(entry.getKey())) { + if (v != null && !n.children().contains(v)) { if (v instanceof Collection) { Collection c = (Collection) v; StringBuilder colS = new StringBuilder(); for (Object o : c) { if (drawSubTrees && isAnotherTree(o)) { - subTrees.add((Node) o); + subTrees.add((Node) o); } else { colS.append(o); @@ -156,39 +152,27 @@ public abstract class Graphviz { } } if (colS.length() > 0) { - parsed.put(entry.getKey(), colS.toString()); + parsed.add(colS.toString()); } } else { - if (drawSubTrees && isAnotherTree(entry.getValue())) { - subTrees.add((Node) entry.getValue()); + if (drawSubTrees && isAnotherTree(v)) { + subTrees.add((Node) v); } else { - parsed.put(entry.getKey(), entry.getValue().toString()); + parsed.add(v.toString()); } } } } - - // remove the field name if only one prop is specified - if (parsed.size() == 1) { - nodeInfo.append(""); - nodeInfo.append(escapeHtml(parsed.values().iterator().next())); + + for (String line : parsed) { + nodeInfo.append(""); + nodeInfo.append(escapeHtml(line)); nodeInfo.append("\n"); indent(nodeInfo, currentIndent + NODE_LABEL_INDENT); } - // add the name and include a border - else { - for (Entry entry : parsed.entrySet()) { - nodeInfo.append(""); - nodeInfo.append(entry.getKey()); - nodeInfo.append(""); - nodeInfo.append(escapeHtml(entry.getValue())); - nodeInfo.append("\n"); - indent(nodeInfo, currentIndent + NODE_LABEL_INDENT); - } - } - + nodeInfo.append("\n"); // check any subtrees @@ -249,7 +233,7 @@ public abstract class Graphviz { indent(output, currentIndent); //output.append("}\n"); } - + private static void drawNodeTree(StringBuilder sb, Node node, String prefix, int counter) { String nodeName = prefix + counter; prefix = nodeName; @@ -275,7 +259,7 @@ public abstract class Graphviz { sb.append(prefix + (i + 1) + " -> " + nodeName + ";\n"); } - // draw the child + // draw the child counter = saveId; for (Node child : node.children()) { drawNodeTree(sb, child, prefix, ++counter); @@ -316,10 +300,10 @@ public abstract class Graphviz { if (value.contains("<")) { return "<" + value + ">"; } - + return "\"" + value + "\""; } - + private static String escapeGraphviz(String value) { return value .replace("<", "\\<") @@ -332,4 +316,4 @@ public abstract class Graphviz { sb.append(" "); } } -} \ No newline at end of file +} diff --git a/sql/server/src/test/java/org/elasticsearch/xpack/sql/expression/LiteralTests.java b/sql/server/src/test/java/org/elasticsearch/xpack/sql/expression/LiteralTests.java new file mode 100644 index 00000000000..3db46e90233 --- /dev/null +++ b/sql/server/src/test/java/org/elasticsearch/xpack/sql/expression/LiteralTests.java @@ -0,0 +1,162 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.sql.expression; + +import org.elasticsearch.common.io.PathUtils; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; +import org.elasticsearch.xpack.sql.expression.Expression; +import org.elasticsearch.xpack.sql.expression.FieldAttribute; +import org.elasticsearch.xpack.sql.expression.Literal; +import org.elasticsearch.xpack.sql.expression.UnresolvedAttribute; +import org.elasticsearch.xpack.sql.expression.function.aggregate.AggregateFunction; +import org.elasticsearch.xpack.sql.expression.function.aggregate.Avg; +import org.elasticsearch.xpack.sql.expression.function.aggregate.InnerAggregate; +import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.AggValueInput; +import org.elasticsearch.xpack.sql.expression.predicate.fulltext.FullTextPredicate; +import org.elasticsearch.xpack.sql.expression.regex.LikePattern; +import org.elasticsearch.xpack.sql.tree.AbstractNodeTestCase; +import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.LocationTests; +import org.elasticsearch.xpack.sql.tree.NodeTests.ChildrenAreAProperty; +import org.elasticsearch.xpack.sql.tree.NodeTests.Dummy; +import org.elasticsearch.xpack.sql.tree.NodeTests.NoChildren; +import org.elasticsearch.xpack.sql.type.DataType; +import org.elasticsearch.xpack.sql.type.DataTypeConversion; +import org.elasticsearch.xpack.sql.type.DataTypes; +import org.mockito.exceptions.base.MockitoException; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.WildcardType; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Supplier; + +import static org.mockito.Mockito.mock; +import static java.util.Collections.emptyList; +import static java.util.stream.Collectors.toList; + +public class LiteralTests extends AbstractNodeTestCase { + static class ValueAndCompatibleTypes { + final Supplier valueSupplier; + final List validDataTypes; + + ValueAndCompatibleTypes(Supplier valueSupplier, DataType... validDataTypes) { + this.valueSupplier = valueSupplier; + this.validDataTypes = Arrays.asList(validDataTypes); + } + } + /** + * Generators for values and data types. The first valid + * data type is special it is used when picking a generator + * for a specific data type. So the first valid data type + * after a generators is its "native" type. + */ + private static final List GENERATORS = Arrays.asList( + new ValueAndCompatibleTypes(() -> randomBoolean() ? randomBoolean() : randomFrom("true", "false"), DataTypes.BOOLEAN), + new ValueAndCompatibleTypes(ESTestCase::randomByte, DataTypes.BYTE, DataTypes.SHORT, DataTypes.INTEGER, DataTypes.LONG, + DataTypes.FLOAT, DataTypes.DOUBLE, DataTypes.BOOLEAN), + new ValueAndCompatibleTypes(ESTestCase::randomShort, DataTypes.SHORT, DataTypes.INTEGER, DataTypes.LONG, + DataTypes.FLOAT, DataTypes.DOUBLE, DataTypes.BOOLEAN), + new ValueAndCompatibleTypes(ESTestCase::randomInt, DataTypes.INTEGER, DataTypes.LONG, + DataTypes.FLOAT, DataTypes.DOUBLE, DataTypes.BOOLEAN), + new ValueAndCompatibleTypes(ESTestCase::randomLong, DataTypes.LONG, DataTypes.FLOAT, DataTypes.DOUBLE, DataTypes.BOOLEAN), + new ValueAndCompatibleTypes(ESTestCase::randomFloat, DataTypes.FLOAT, DataTypes.LONG, DataTypes.DOUBLE, DataTypes.BOOLEAN), + new ValueAndCompatibleTypes(ESTestCase::randomDouble, DataTypes.DOUBLE, DataTypes.LONG, DataTypes.FLOAT, DataTypes.BOOLEAN), + new ValueAndCompatibleTypes(() -> randomAlphaOfLength(5), DataTypes.KEYWORD)); + + public static Literal randomLiteral() { + ValueAndCompatibleTypes gen = randomFrom(GENERATORS); + return new Literal(LocationTests.randomLocation(), gen.valueSupplier.get(), randomFrom(gen.validDataTypes)); + } + + @Override + protected Literal randomInstance() { + return randomLiteral(); + } + + @Override + protected Literal copy(Literal instance) { + return new Literal(instance.location(), instance.value(), instance.dataType()); + } + + @Override + protected Literal mutate(Literal instance) { + List> mutators = new ArrayList<>(); + // Changing the location doesn't count as mutation because..... it just doesn't, ok?! + // Change the value to another valid value + mutators.add(l -> new Literal(l.location(), randomValueOfTypeOtherThan(l.value(), l.dataType()), l.dataType())); + // If we can change the data type then add that as an option as well + List validDataTypes = validReplacementDataTypes(instance.value(), instance.dataType()); + if (validDataTypes.size() > 1) { + mutators.add(l -> new Literal(l.location(), l.value(), randomValueOtherThan(l.dataType(), () -> randomFrom(validDataTypes)))); + } + return randomFrom(mutators).apply(instance); + } + + @Override + public void testTransform() { + Literal literal = randomInstance(); + + // Replace value + Object newValue = randomValueOfTypeOtherThan(literal.value(), literal.dataType()); + assertEquals((Expression) new Literal(literal.location(), newValue, literal.dataType()), + literal.transformPropertiesOnly(p -> p == literal.value() ? newValue : p, Object.class)); + + // Replace data type if there are more compatible data types + List validDataTypes = validReplacementDataTypes(literal.value(), literal.dataType()); + if (validDataTypes.size() > 1) { + DataType newDataType = randomValueOtherThan(literal.dataType(), () -> randomFrom(validDataTypes)); + assertEquals((Expression) new Literal(literal.location(), literal.value(), newDataType), + literal.transformPropertiesOnly(p -> newDataType, DataType.class)); + } + } + + @Override + public void testReplaceChildren() { + Exception e = expectThrows(UnsupportedOperationException.class, () -> randomInstance().replaceChildren(emptyList())); + assertEquals("this type of node doesn't have any children to replace", e.getMessage()); + } + + private Object randomValueOfTypeOtherThan(Object original, DataType type) { + for (ValueAndCompatibleTypes gen : GENERATORS) { + if (gen.validDataTypes.get(0) == type) { + return randomValueOtherThan(original, () -> DataTypeConversion.convert(gen.valueSupplier.get(), type)); + } + } + throw new IllegalArgumentException("No native generator for [" + type + "]"); + } + + private List validReplacementDataTypes(Object value, DataType type) { + List validDataTypes = new ArrayList<>(); + List options = Arrays.asList(DataTypes.BYTE, DataTypes.SHORT, DataTypes.INTEGER, DataTypes.LONG, + DataTypes.FLOAT, DataTypes.DOUBLE, DataTypes.BOOLEAN); + for (DataType candidate : options) { + try { + DataTypeConversion.Conversion c = DataTypeConversion.conversionFor(type, candidate); + c.convert(value); + validDataTypes.add(candidate); + } catch (SqlIllegalArgumentException e) { + // invalid conversion then.... + } + } + return validDataTypes; + } +} diff --git a/sql/server/src/test/java/org/elasticsearch/xpack/sql/expression/function/FunctionRegistryTests.java b/sql/server/src/test/java/org/elasticsearch/xpack/sql/expression/function/FunctionRegistryTests.java index dbdc2d3c3bd..e4a853f2456 100644 --- a/sql/server/src/test/java/org/elasticsearch/xpack/sql/expression/function/FunctionRegistryTests.java +++ b/sql/server/src/test/java/org/elasticsearch/xpack/sql/expression/function/FunctionRegistryTests.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.function; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.LocationTests; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.joda.time.DateTimeZone; import org.elasticsearch.xpack.sql.expression.Expression; @@ -16,6 +17,7 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definiti import org.elasticsearch.xpack.sql.expression.function.scalar.script.ScriptTemplate; import org.elasticsearch.xpack.sql.parser.ParsingException; import java.util.Arrays; +import java.util.List; import static org.elasticsearch.xpack.sql.expression.function.FunctionRegistry.def; import static org.hamcrest.Matchers.endsWith; @@ -143,11 +145,20 @@ public class FunctionRegistryTests extends ESTestCase { return new UnresolvedFunction(LocationTests.randomLocation(), "dummy", distinct, Arrays.asList(children)); } - private static class Dummy extends ScalarFunction { - private Dummy(Location location) { + public static class Dummy extends ScalarFunction { + public Dummy(Location location) { super(location, emptyList()); } + @Override + protected NodeInfo info() { + return NodeInfo.create(this); + } + + @Override + public Expression replaceChildren(List newChildren) { + throw new UnsupportedOperationException("this type of node doesn't have any children to replace"); + } @Override public DataType dataType() { diff --git a/sql/server/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/AttributeInputTests.java b/sql/server/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/AttributeInputTests.java index 55c8a5625cd..7378675075d 100644 --- a/sql/server/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/AttributeInputTests.java +++ b/sql/server/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/AttributeInputTests.java @@ -18,9 +18,9 @@ public class AttributeInputTests extends ESTestCase { Expression expression = mock(Expression.class); Attribute attribute = mock(Attribute.class); - ReferenceInput expected = new ReferenceInput(expression, column); + ReferenceInput expected = new ReferenceInput(expression.location(), expression, column); - assertEquals(expected, new AttributeInput(expression, attribute).resolveAttributes(a -> { + assertEquals(expected, new AttributeInput(expression.location(), expression, attribute).resolveAttributes(a -> { assertSame(attribute, a); return column; })); diff --git a/sql/server/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/BinaryProcessorDefinitionTests.java b/sql/server/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/BinaryProcessorDefinitionTests.java index aeb9cd54fc1..59bbdad0430 100644 --- a/sql/server/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/BinaryProcessorDefinitionTests.java +++ b/sql/server/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/BinaryProcessorDefinitionTests.java @@ -10,9 +10,13 @@ import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.sql.execution.search.SqlSourceBuilder; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.ProcessorDefinition.AttributeResolver; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.Processor; +import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import static java.util.Collections.emptyList; +import java.util.List; + public class BinaryProcessorDefinitionTests extends ESTestCase { public void testSupportedByAggsOnlyQuery() { ProcessorDefinition supported = new DummyProcessorDefinition(true); @@ -95,7 +99,16 @@ public class BinaryProcessorDefinitionTests extends ESTestCase { public static final class DummyBinaryProcessorDefinition extends BinaryProcessorDefinition { public DummyBinaryProcessorDefinition(ProcessorDefinition left, ProcessorDefinition right) { - super(null, left, right); + this(Location.EMPTY, left, right); + } + + public DummyBinaryProcessorDefinition(Location location, ProcessorDefinition left, ProcessorDefinition right) { + super(location, null, left, right); + } + + @Override + protected NodeInfo info() { + return NodeInfo.create(this, DummyBinaryProcessorDefinition::new, left(), right()); } @Override @@ -105,7 +118,7 @@ public class BinaryProcessorDefinitionTests extends ESTestCase { @Override protected BinaryProcessorDefinition replaceChildren(ProcessorDefinition left, ProcessorDefinition right) { - return new DummyBinaryProcessorDefinition(left, right); + return new DummyBinaryProcessorDefinition(location(), left, right); } } @@ -113,10 +126,24 @@ public class BinaryProcessorDefinitionTests extends ESTestCase { private final boolean supportedByAggsOnlyQuery; public DummyProcessorDefinition(boolean supportedByAggsOnlyQuery) { - super(null, emptyList()); + this(Location.EMPTY, supportedByAggsOnlyQuery); + } + + public DummyProcessorDefinition(Location location, boolean supportedByAggsOnlyQuery) { + super(location, null, emptyList()); this.supportedByAggsOnlyQuery = supportedByAggsOnlyQuery; } + @Override + protected NodeInfo info() { + return NodeInfo.create(this, DummyProcessorDefinition::new, supportedByAggsOnlyQuery); + } + + @Override + public ProcessorDefinition replaceChildren(List newChildren) { + throw new UnsupportedOperationException("this type of node doesn't have any children to replace"); + } + @Override public boolean supportedByAggsOnlyQuery() { return supportedByAggsOnlyQuery; diff --git a/sql/server/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/UnaryProcessorDefinitionTests.java b/sql/server/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/UnaryProcessorDefinitionTests.java index 5bf73cb14ce..9937d7a59d6 100644 --- a/sql/server/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/UnaryProcessorDefinitionTests.java +++ b/sql/server/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/processor/definition/UnaryProcessorDefinitionTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.xpack.sql.execution.search.SqlSourceBuilder; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.BinaryProcessorDefinitionTests.DummyProcessorDefinition; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.ProcessorDefinition.AttributeResolver; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.Processor; +import org.elasticsearch.xpack.sql.tree.Location; import static org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.BinaryProcessorDefinitionTests.dummyWithDepth; import static org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.BinaryProcessorDefinitionTests.tracksScores; @@ -64,6 +65,6 @@ public class UnaryProcessorDefinitionTests extends ESTestCase { } private ProcessorDefinition newUnaryProcessor(ProcessorDefinition child) { - return new UnaryProcessorDefinition(null, child, null); + return new UnaryProcessorDefinition(Location.EMPTY, null, child, null); } } diff --git a/sql/server/src/test/java/org/elasticsearch/xpack/sql/optimizer/OptimizerTests.java b/sql/server/src/test/java/org/elasticsearch/xpack/sql/optimizer/OptimizerTests.java index 18af4cdd5d5..f8e9dbd1397 100644 --- a/sql/server/src/test/java/org/elasticsearch/xpack/sql/optimizer/OptimizerTests.java +++ b/sql/server/src/test/java/org/elasticsearch/xpack/sql/optimizer/OptimizerTests.java @@ -46,6 +46,7 @@ import org.elasticsearch.xpack.sql.plan.logical.SubQueryAlias; import org.elasticsearch.xpack.sql.plan.logical.command.ShowTables; import org.elasticsearch.xpack.sql.session.EmptyExecutable; import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; @@ -62,15 +63,25 @@ public class OptimizerTests extends ESTestCase { private static final Expression DUMMY_EXPRESSION = new DummyBooleanExpression(EMPTY, 0); - private static class DummyBooleanExpression extends Expression { - + public static class DummyBooleanExpression extends Expression { + private final int id; - - DummyBooleanExpression(Location location, int id) { + + public DummyBooleanExpression(Location location, int id) { super(location, Collections.emptyList()); this.id = id; } - + + @Override + protected NodeInfo info() { + return NodeInfo.create(this, DummyBooleanExpression::new, id); + } + + @Override + public Expression replaceChildren(List newChildren) { + throw new UnsupportedOperationException("this type of node doesn't have any children"); + } + @Override public boolean nullable() { return false; @@ -133,7 +144,7 @@ public class OptimizerTests extends ESTestCase { Alias b = new Alias(EMPTY, "b", L(10)); // x -> a Alias x = new Alias(EMPTY, "x", a); - + Project lowerP = new Project(EMPTY, FROM(), asList(a, b)); Project upperP = new Project(EMPTY, lowerP, singletonList(x)); @@ -266,7 +277,7 @@ public class OptimizerTests extends ESTestCase { public void testBoolSimplifyOr() { BooleanSimplification simplification = new BooleanSimplification(); - + assertEquals(Literal.TRUE, simplification.rule(new Or(EMPTY, Literal.TRUE, Literal.TRUE))); assertEquals(Literal.TRUE, simplification.rule(new Or(EMPTY, Literal.TRUE, DUMMY_EXPRESSION))); assertEquals(Literal.TRUE, simplification.rule(new Or(EMPTY, DUMMY_EXPRESSION, Literal.TRUE))); @@ -278,7 +289,7 @@ public class OptimizerTests extends ESTestCase { public void testBoolSimplifyAnd() { BooleanSimplification simplification = new BooleanSimplification(); - + assertEquals(Literal.TRUE, simplification.rule(new And(EMPTY, Literal.TRUE, Literal.TRUE))); assertEquals(DUMMY_EXPRESSION, simplification.rule(new And(EMPTY, Literal.TRUE, DUMMY_EXPRESSION))); assertEquals(DUMMY_EXPRESSION, simplification.rule(new And(EMPTY, DUMMY_EXPRESSION, Literal.TRUE))); @@ -301,4 +312,4 @@ public class OptimizerTests extends ESTestCase { assertEquals(expected, simplification.rule(actual)); } -} \ No newline at end of file +} diff --git a/sql/server/src/test/java/org/elasticsearch/xpack/sql/tree/AbstractNodeTestCase.java b/sql/server/src/test/java/org/elasticsearch/xpack/sql/tree/AbstractNodeTestCase.java new file mode 100644 index 00000000000..b5b0adfb0e6 --- /dev/null +++ b/sql/server/src/test/java/org/elasticsearch/xpack/sql/tree/AbstractNodeTestCase.java @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.sql.tree; + +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.EqualsHashCodeTestUtils; + +/** + * Superclass for tests of subclasses of {@link Node}. + */ +public abstract class AbstractNodeTestCase> extends ESTestCase { + /** + * Make a new random instance. + */ + protected abstract T randomInstance(); + /** + * Mutate an instance into some other similar instance that + * shouldn't be {@link #equals} to the original. + */ + protected abstract T mutate(T instance); + /** + * Copy and instance so it isn't {@code ==} but should still + * be {@link #equals}. + */ + protected abstract T copy(T instance); + + /** + * Test this subclass's implementation of {@link Node#transformNodeProps}. + */ + public abstract void testTransform(); + /** + * Test this subclass's implementation of {@link Node#replaceChildren}. + */ + public abstract void testReplaceChildren(); + + public final void testHashCodeAndEquals() { + EqualsHashCodeTestUtils.checkEqualsAndHashCode(randomInstance(), this::copy, this::mutate); + } +} diff --git a/sql/server/src/test/java/org/elasticsearch/xpack/sql/tree/NodeSubclassTests.java b/sql/server/src/test/java/org/elasticsearch/xpack/sql/tree/NodeSubclassTests.java new file mode 100644 index 00000000000..3dd1b0bb6da --- /dev/null +++ b/sql/server/src/test/java/org/elasticsearch/xpack/sql/tree/NodeSubclassTests.java @@ -0,0 +1,590 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.sql.tree; + +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.io.PathUtils; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.sql.expression.Expression; +import org.elasticsearch.xpack.sql.expression.FieldAttribute; +import org.elasticsearch.xpack.sql.expression.UnresolvedAttribute; +import org.elasticsearch.xpack.sql.expression.function.Function; +import org.elasticsearch.xpack.sql.expression.function.aggregate.AggregateFunction; +import org.elasticsearch.xpack.sql.expression.function.aggregate.Avg; +import org.elasticsearch.xpack.sql.expression.function.aggregate.InnerAggregate; +import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.AggValueInput; +import org.elasticsearch.xpack.sql.expression.predicate.fulltext.FullTextPredicate; +import org.elasticsearch.xpack.sql.expression.regex.LikePattern; +import org.elasticsearch.xpack.sql.tree.NodeTests.ChildrenAreAProperty; +import org.elasticsearch.xpack.sql.tree.NodeTests.Dummy; +import org.elasticsearch.xpack.sql.tree.NodeTests.NoChildren; +import org.elasticsearch.xpack.sql.type.DataType; +import org.elasticsearch.xpack.sql.type.DataTypes; +import org.mockito.exceptions.base.MockitoException; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.WildcardType; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Supplier; + +import static org.mockito.Mockito.mock; +import static java.util.Collections.emptyList; +import static java.util.stream.Collectors.toList; + +/** + * Looks for all subclasses of {@link Node} and verifies that they + * implement {@link Node#info()} and + * {@link Node#replaceChildren(List)} sanely. It'd be better if + * each subclass had its own test case that verified those methods + * and any other interesting things that that they do but we're a + * long way from that and this gets the job done for now. + *

+ * This test attempts to use reflection to create believeable nodes + * and manipulate them in believeable ways with as little knowledge + * of the actual subclasses as possible. This is problematic because + * it is possible, for example, for nodes to stackoverflow because + * they can contain themselves. So this class + * does have some {@link Node}-subclass-specific + * knowledge. As little as I could get away with though. + *

+ * When there are actual tests for a subclass of {@linkplain Node} + * then this class will do two things: + *

    + *
  • Skip running any tests for that subclass entirely. + *
  • Delegate to that test to build nodes of that type when a + * node of that type is called for. + *
+ */ +public class NodeSubclassTests> extends ESTestCase { + private final Class subclass; + + public NodeSubclassTests(Class subclass) { + this.subclass = subclass; + } + + public void testInfoParameters() throws Exception { + Constructor ctor = longestCtor(subclass); + Object[] nodeCtorArgs = ctorArgs(ctor); + T node = ctor.newInstance(nodeCtorArgs); + /* + * The count should be the same size as the longest constructor + * by convention. If it isn't then we're missing something. + */ + int expectedCount = ctor.getParameterCount(); + /* + * Except the first `Location` argument of the ctor is implicit + * in the parameters and not included. + */ + expectedCount -= 1; + assertEquals(expectedCount, node.info().properties().size()); + } + + /** + * Test {@link Node#transformPropertiesOnly(java.util.function.Function, Class)} + * implementation on {@link #subclass} which tests the implementation of + * {@link Node#info()}. And tests the actual {@link NodeInfo} subclass + * implementations in the process. + */ + public void testTransform() throws Exception { + Constructor ctor = longestCtor(subclass); + Object[] nodeCtorArgs = ctorArgs(ctor); + T node = ctor.newInstance(nodeCtorArgs); + + Type[] argTypes = ctor.getGenericParameterTypes(); + // start at 1 because we can't change Location. + for (int changedArgOffset = 1; changedArgOffset < ctor.getParameterCount(); changedArgOffset++) { + Object originalArgValue = nodeCtorArgs[changedArgOffset]; + + Type changedArgType = argTypes[changedArgOffset]; + Object changedArgValue = randomValueOtherThan(nodeCtorArgs[changedArgOffset], () -> makeArg(changedArgType)); + + B transformed = node.transformNodeProps(prop -> { + return Objects.equals(prop, originalArgValue) ? changedArgValue : prop; + }, Object.class); + + if (node.children().contains(originalArgValue) || node.children().equals(originalArgValue)) { + if (node.children().equals(emptyList()) && originalArgValue.equals(emptyList())) { + /* + * If the children are an empty list and the value + * we want to change is an empty list they'll be + * equal to one another so they'll come on this branch. + * This case is rare and hard to reason about so we're + * just going to assert nothing here and hope to catch + * it when we write non-reflection hack tests. + */ + continue; + } + // Transformation shouldn't apply to children. + assertSame(node, transformed); + } else { + assertTransformedOrReplacedChildren(node, transformed, ctor, nodeCtorArgs, changedArgOffset, changedArgValue); + } + } + } + + /** + * Test {@link Node#replaceChildren} implementation on {@link #subclass}. + */ + public void testReplaceChildren() throws Exception { + Constructor ctor = longestCtor(subclass); + Object[] nodeCtorArgs = ctorArgs(ctor); + T node = ctor.newInstance(nodeCtorArgs); + + Type[] argTypes = ctor.getGenericParameterTypes(); + // start at 1 because we can't change Location. + for (int changedArgOffset = 1; changedArgOffset < ctor.getParameterCount(); changedArgOffset++) { + Object originalArgValue = nodeCtorArgs[changedArgOffset]; + Type changedArgType = argTypes[changedArgOffset]; + + if (originalArgValue instanceof Collection) { + List originalList = (List) originalArgValue; + if (originalList.isEmpty()) { + /* + * We skip empty lists here because they'll spuriously + * pass the conditions below if statements even if they don't + * have anything to do with children. This might cause us to + * ignore the case where a parameter gets copied into the + * children and just happens to be empty but I don't really + * know another way. + */ + + continue; + } + if (node.children().equals(originalList)) { + // The arg we're looking at *is* the children + @SuppressWarnings("unchecked") // we pass a reasonable type so get reasonable results + List newChildren = (List) makeListOfSameSizeOtherThan(changedArgType, originalList); + B transformed = node.replaceChildren(newChildren); + assertTransformedOrReplacedChildren(node, transformed, ctor, nodeCtorArgs, changedArgOffset, newChildren); + } else if (false == originalList.isEmpty() && node.children().containsAll(originalList)) { + // The arg we're looking at is a collection contained within the children + + // First make the new children + @SuppressWarnings("unchecked") // we pass a reasonable type so get reasonable results + List newCollection = (List) makeListOfSameSizeOtherThan(changedArgType, originalList); + + // Now merge that list of thildren into the original list of children + List originalChildren = node.children(); + List newChildren = new ArrayList<>(originalChildren.size()); + int originalOffset = 0; + for (int i = 0; i < originalChildren.size(); i++) { + if (originalOffset < originalList.size() && originalChildren.get(i).equals(originalList.get(originalOffset))) { + newChildren.add(newCollection.get(originalOffset)); + originalOffset++; + } else { + newChildren.add(originalChildren.get(i)); + } + } + + // Finally! We can assert..... + B transformed = node.replaceChildren(newChildren); + assertTransformedOrReplacedChildren(node, transformed, ctor, nodeCtorArgs, changedArgOffset, newCollection); + } else { + // The arg we're looking at has nothing to do with the children + } + } else { + if (node.children().contains(originalArgValue)) { + // The arg we're looking at is one of the children + List newChildren = new ArrayList<>(node.children()); + @SuppressWarnings("unchecked") // makeArg produced reasonable values + B newChild = (B) randomValueOtherThan(nodeCtorArgs[changedArgOffset], () -> makeArg(changedArgType)); + newChildren.replaceAll(e -> Objects.equals(originalArgValue, e) ? newChild : e); + B transformed = node.replaceChildren(newChildren); + assertTransformedOrReplacedChildren(node, transformed, ctor, nodeCtorArgs, changedArgOffset, newChild); + } else { + // The arg we're looking at has nothing to do with the children + } + } + } + } + + private void assertTransformedOrReplacedChildren(T node, B transformed, Constructor ctor, + Object[] nodeCtorArgs, int changedArgOffset, Object changedArgValue) throws Exception { + if (node instanceof Function) { + /* + * Functions have a weaker definition of transform then other + * things: + * + * Transforming using the way we did above should only change + * the one property of the node that we intended to transform. + */ + assertEquals(node.location(), transformed.location()); + List op = node.properties(); + List tp = transformed.properties(); + for (int p = 0; p < op.size(); p++) { + if (p == changedArgOffset - 1) { // -1 because location isn't in the list + assertEquals(changedArgValue, tp.get(p)); + } else { + assertEquals(op.get(p), tp.get(p)); + } + } + } else { + /* + * The stronger assertion for all non-Functions: transforming + * a node changes *only* the transformed value such that you + * can rebuild a copy of the node using its constructor changing + * only one argument and it'll be *equal* to the result of the + * transformation. + */ + Type[] argTypes = ctor.getGenericParameterTypes(); + Object[] args = new Object[argTypes.length]; + for (int i = 0; i < argTypes.length; i++) { + args[i] = nodeCtorArgs[i] == nodeCtorArgs[changedArgOffset] ? changedArgValue : nodeCtorArgs[i]; + } + T reflectionTransformed = ctor.newInstance(args); + assertEquals(reflectionTransformed, transformed); + } + } + + /** + * Find the longest constructor of the given class. + * By convention, for all subclasses of {@link Node}, + * this constructor should have "all" of the state of + * the node. All other constructors should all delegate + * to this constructor. + */ + static Constructor longestCtor(Class clazz) { + Constructor longest = null; + for (Constructor ctor: clazz.getConstructors()) { + if (longest == null || longest.getParameterCount() < ctor.getParameterCount()) { + @SuppressWarnings("unchecked") // Safe because the ctor has to be a ctor for T + Constructor castCtor = (Constructor) ctor; + longest = castCtor; + } + } + if (longest == null) { + throw new IllegalArgumentException("Couldn't find any constructors for [" + clazz.getName() + "]"); + } + return longest; + } + + /** + * Scans the {@code .class} files to identify all classes and + * checks if they are subclasses of {@link Node}. + */ + @ParametersFactory + @SuppressWarnings("rawtypes") + public static List nodeSubclasses() throws IOException { + return subclassesOf(Node.class).stream() + .filter(c -> testClassFor(c) == null) + .map(c -> new Object[] {c}) + .collect(toList()); + } + + /** + * Build a list of arguments to use when calling + * {@code ctor} that make sense when {@code ctor} + * builds subclasses of {@link Node}. + */ + private static Object[] ctorArgs(Constructor> ctor) throws Exception { + Type[] argTypes = ctor.getGenericParameterTypes(); + Object[] args = new Object[argTypes.length]; + for (int i = 0; i < argTypes.length; i++) { + final int currentArgIndex = i; + args[i] = randomValueOtherThanMany(candidate -> { + for (int a = 0; a < currentArgIndex; a++) { + if (Objects.equals(args[a], candidate)) { + return true; + } + } + return false; + }, () -> { + try { + return makeArg(ctor.getDeclaringClass(), argTypes[currentArgIndex]); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } + return args; + } + + /** + * Make an argument to feed the {@link #subclass}'s ctor. + */ + private Object makeArg(Type argType) { + try { + return makeArg(subclass, argType); + } catch (Exception e) { + // Wrap to make `randomValueOtherThan` happy. + throw new RuntimeException(e); + } + } + + /** + * Make an argument to feed to the constructor for {@code toBuildClass}. + */ + private static Object makeArg(Class> toBuildClass, Type argType) throws Exception { + if (argType instanceof ParameterizedType) { + ParameterizedType pt = (ParameterizedType) argType; + if (pt.getRawType() == Map.class) { + Map map = new HashMap<>(); + int size = between(0, 10); + while (map.size() < size) { + Object key = makeArg(toBuildClass, pt.getActualTypeArguments()[0]); + Object value = makeArg(toBuildClass, pt.getActualTypeArguments()[1]); + map.put(key, value); + } + return map; + } + if (pt.getRawType() == List.class) { + return makeList(toBuildClass, pt, between(0, 10)); + } + if (pt.getRawType() == Supplier.class) { + if (toBuildClass == AggValueInput.class) { + // AggValueInput just needs a valid java type in a supplier + Object o = randomBoolean() ? null : randomAlphaOfLength(5); + // But the supplier has to implement equals for randomValueOtherThan + return new Supplier() { + @Override + public Object get() { + return o; + } + + @Override + public int hashCode() { + return Objects.hash(o); + } + + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != getClass()) { + return false; + } + Supplier other = (Supplier) obj; + return Objects.equals(o, other.get()); + } + }; + } + + } + throw new IllegalArgumentException("Unsupported parameterized type [" + pt + "]"); + } + if (argType instanceof WildcardType) { + WildcardType wt = (WildcardType) argType; + if (wt.getLowerBounds().length > 0 || wt.getUpperBounds().length > 1) { + throw new IllegalArgumentException("Unsupported wildcard type [" + wt + "]"); + } + return makeArg(toBuildClass, wt.getUpperBounds()[0]); + } + Class argClass = (Class) argType; + + /* + * Sometimes all of the required type information isn't in the ctor + * so we have to hard code it here. + */ + if (toBuildClass == InnerAggregate.class) { + // InnerAggregate's AggregateFunction must be an EnclosedAgg. Avg is. + if (argClass == AggregateFunction.class) { + return makeNode(Avg.class); + } + } else if (toBuildClass == FieldAttribute.class) { + // `parent` is nullable. + if (argClass == FieldAttribute.class && randomBoolean()) { + return null; + } + } else if (toBuildClass == ChildrenAreAProperty.class) { + /* + * While any subclass of Dummy will do here we want to prevent + * stack overflow so we use the one without children. + */ + if (argClass == Dummy.class) { + return makeNode(NoChildren.class); + } + } else if (FullTextPredicate.class.isAssignableFrom(toBuildClass)) { + /* + * FullTextPredicate analyzes its string arguments on + * construction so they have to be valid. + */ + if (argClass == String.class) { + int size = between(0, 5); + StringBuilder b = new StringBuilder(); + for (int i = 0; i < size; i++) { + if (i != 0) { + b.append(';'); + } + b.append(randomAlphaOfLength(5)).append('=').append(randomAlphaOfLength(5)); + } + return b.toString(); + } + } else if (toBuildClass == LikePattern.class) { + /* + * The pattern and escape character have to be valid together + * so we pick an escape character that isn't used + */ + if (argClass == char.class) { + return randomFrom('\\', '|', '/', '`'); + } + } + + if (Expression.class == argClass) { + /* + * Rather than use any old subclass of expression lets + * use a simple one. Without this we're very prone to + * stackoverflow errors while building the tree. + */ + return new UnresolvedAttribute(LocationTests.randomLocation(), randomAlphaOfLength(5)); + } + if (Node.class.isAssignableFrom(argClass)) { + /* + * Rather than attempting to mock subclasses of node + * and emulate them we just try and instantiate an + * appropriate subclass + */ + @SuppressWarnings("unchecked") // safe because this is the lowest possible bounds for Node + Class> asNodeSubclass = (Class>) argType; + return makeNode(asNodeSubclass); + } + + if (argClass.isEnum()) { + // Can't mock enums but luckilly we can just pick one + return randomFrom(argClass.getEnumConstants()); + } + if (argClass == boolean.class) { + // Can't mock primitives.... + return randomBoolean(); + } + if (argClass == int.class) { + return randomInt(); + } + if (argClass == String.class) { + // Nor strings + return randomAlphaOfLength(5); + } + if (argClass == Location.class) { + // Location is final and can't be mocked but we have a handy method to generate ones. + return LocationTests.randomLocation(); + } + try { + return mock(argClass); + } catch (MockitoException e) { + throw new RuntimeException("failed to mock [" + argClass.getName() + "] for [" + toBuildClass.getName() + "]", e); + } + } + + private static List makeList(Class> toBuildClass, ParameterizedType listType, int size) throws Exception { + List list = new ArrayList<>(); + for (int i = 0; i < size; i++) { + list.add(makeArg(toBuildClass, listType.getActualTypeArguments()[0])); + } + return list; + } + + private List makeListOfSameSizeOtherThan(Type listType, List original) throws Exception { + if (original.isEmpty()) { + throw new IllegalArgumentException("Can't make a different empty list"); + } + return randomValueOtherThan(original, () -> { + try { + return makeList(subclass, (ParameterizedType) listType, original.size()); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + + } + + private static > T makeNode(Class nodeClass) throws Exception { + if (Modifier.isAbstract(nodeClass.getModifiers())) { + nodeClass = randomFrom(subclassesOf(nodeClass)); + } + Class testSubclassFor = testClassFor(nodeClass); + if (testSubclassFor != null) { + // Delegate to the test class for a node if there is one + Method m = testSubclassFor.getMethod("random" + Strings.capitalize(nodeClass.getSimpleName())); + return nodeClass.cast(m.invoke(null)); + } + Constructor ctor = longestCtor(nodeClass); + Object[] nodeCtorArgs = ctorArgs(ctor); + return ctor.newInstance(nodeCtorArgs); + } + + /** + * Cache of subclasses. We use a cache because it significantly speeds up + * the test. + */ + private static final Map, List> subclassCache = new HashMap<>(); + /** + * Find all subclasses of a particular class. + */ + private static List> subclassesOf(Class clazz) throws IOException { + @SuppressWarnings("unchecked") // The map is built this way + List> lookup = (List>) subclassCache.get(clazz); + if (lookup != null) { + return lookup; + } + List> results = new ArrayList<>(); + String[] paths = System.getProperty("java.class.path").split(":"); + for (String path: paths) { + Path root = PathUtils.get(path); + int rootLength = root.toString().length() + 1; + Files.walkFileTree(root, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + if (Files.isRegularFile(file) && file.getFileName().toString().endsWith(".class")) { + String className = file.toString(); + // Chop off the root and file extension + className = className.substring(rootLength, className.length() - ".class".length()); + // Go from "path" style to class style + className = className.replace(PathUtils.getDefaultFileSystem().getSeparator(), "."); + + Class c; + try { + c = Class.forName(className); + } catch (ClassNotFoundException e) { + throw new IOException("Couldn't find " + file, e); + } + + if (false == Modifier.isAbstract(c.getModifiers()) + && false == c.isAnonymousClass() + && clazz.isAssignableFrom(c)) { + Class s = c.asSubclass(clazz); + results.add(s); + } + } + return super.visitFile(file, attrs); + } + }); + } + subclassCache.put(clazz, results); + return results; + } + + /** + * The test class for some subclass of node or {@code null} + * if there isn't such a class or it doesn't extend + * {@link AbstractNodeTestCase}. + */ + private static Class testClassFor(Class nodeSubclass) { + String testClassName = nodeSubclass.getName() + "Tests"; + try { + Class c = Class.forName(testClassName); + if (AbstractNodeTestCase.class.isAssignableFrom(c)) { + return c; + } + return null; + } catch (ClassNotFoundException e) { + return null; + } + } +} diff --git a/sql/server/src/test/java/org/elasticsearch/xpack/sql/tree/NodeTests.java b/sql/server/src/test/java/org/elasticsearch/xpack/sql/tree/NodeTests.java new file mode 100644 index 00000000000..e9d03d31c1b --- /dev/null +++ b/sql/server/src/test/java/org/elasticsearch/xpack/sql/tree/NodeTests.java @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.sql.tree; + +import org.elasticsearch.test.ESTestCase; + +import static org.elasticsearch.xpack.sql.tree.LocationTests.randomLocation; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; + +public class NodeTests extends ESTestCase { + public void testToString() { + assertEquals("NoChildren[thing]", new NoChildren(randomLocation(), "thing").toString()); + { + ChildrenAreAProperty empty = new ChildrenAreAProperty(randomLocation(), emptyList(), "thing"); + assertEquals("ChildrenAreAProperty[thing]", empty.toString()); + assertEquals("ChildrenAreAProperty[single]\n\\_ChildrenAreAProperty[thing]", + new ChildrenAreAProperty(randomLocation(), singletonList(empty), "single").toString()); + assertEquals("ChildrenAreAProperty[many]\n" + + "|_ChildrenAreAProperty[thing]\n" + + "\\_ChildrenAreAProperty[thing]", + new ChildrenAreAProperty(randomLocation(), Arrays.asList(empty, empty), "many").toString()); + } + { + NoChildren empty = new NoChildren(randomLocation(), "thing"); + assertEquals("AChildIsAProperty[single]\n" + + "\\_NoChildren[thing]", + new AChildIsAProperty(randomLocation(), empty, "single").toString()); + } + } + + public abstract static class Dummy extends Node { + private final String thing; + public Dummy(Location location, List children, String thing) { + super(location, children); + this.thing = thing; + } + + public String thing() { + return thing; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || obj.getClass() != getClass()) { + return false; + } + Dummy other = (Dummy) obj; + return thing.equals(other.thing) + && children().equals(other.children()); + } + + @Override + public int hashCode() { + return Objects.hash(thing, children()); + } + } + + public static class ChildrenAreAProperty extends Dummy { + public ChildrenAreAProperty(Location location, List children, String thing) { + super(location, children, thing); + } + + @Override + protected NodeInfo info() { + return NodeInfo.create(this, ChildrenAreAProperty::new, children(), thing()); + } + + @Override + public ChildrenAreAProperty replaceChildren(List newChildren) { + return new ChildrenAreAProperty(location(), newChildren, thing()); + } + } + + public static class AChildIsAProperty extends Dummy { + public AChildIsAProperty(Location location, Dummy child, String thing) { + super(location, singletonList(child), thing); + } + + @Override + protected NodeInfo info() { + return NodeInfo.create(this, AChildIsAProperty::new, child(), thing()); + } + + @Override + public AChildIsAProperty replaceChildren(List newChildren) { + if (newChildren.size() != 1) { + throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); + } + return new AChildIsAProperty(location(), newChildren.get(0), thing()); + } + + public Dummy child() { + return children().get(0); + } + } + + public static class NoChildren extends Dummy { + public NoChildren(Location location, String thing) { + super(location, emptyList(), thing); + } + + @Override + protected NodeInfo info() { + return NodeInfo.create(this, NoChildren::new, thing()); + } + + @Override + public Dummy replaceChildren(List newChildren) { + throw new UnsupportedOperationException("no children to replace"); + } + } +} diff --git a/sql/server/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java b/sql/server/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java index e05fcceaa6b..193132c1056 100644 --- a/sql/server/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java +++ b/sql/server/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java @@ -141,4 +141,40 @@ public class DataTypeConversionTests extends ESTestCase { assertEquals("cannot cast [nO] to [Boolean]", e.getMessage()); } } + + public void testConversionToInt() { + { + Conversion conversion = DataTypeConversion.conversionFor(new DoubleType(true), new IntegerType(true)); + assertNull(conversion.convert(null)); + assertEquals(10, conversion.convert(10.0)); + assertEquals(10, conversion.convert(10.1)); + assertEquals(11, conversion.convert(10.6)); + Exception e = expectThrows(SqlIllegalArgumentException.class, () -> conversion.convert(Long.MAX_VALUE)); + assertEquals("[" + Long.MAX_VALUE + "] out of [Int] range", e.getMessage()); + } + } + + public void testConversionToShort() { + { + Conversion conversion = DataTypeConversion.conversionFor(new DoubleType(true), new ShortType(true)); + assertNull(conversion.convert(null)); + assertEquals((short) 10, conversion.convert(10.0)); + assertEquals((short) 10, conversion.convert(10.1)); + assertEquals((short) 11, conversion.convert(10.6)); + Exception e = expectThrows(SqlIllegalArgumentException.class, () -> conversion.convert(Integer.MAX_VALUE)); + assertEquals("[" + Integer.MAX_VALUE + "] out of [Short] range", e.getMessage()); + } + } + + public void testConversionToByte() { + { + Conversion conversion = DataTypeConversion.conversionFor(new DoubleType(true), new ByteType(true)); + assertNull(conversion.convert(null)); + assertEquals((byte) 10, conversion.convert(10.0)); + assertEquals((byte) 10, conversion.convert(10.1)); + assertEquals((byte) 11, conversion.convert(10.6)); + Exception e = expectThrows(SqlIllegalArgumentException.class, () -> conversion.convert(Short.MAX_VALUE)); + assertEquals("[" + Short.MAX_VALUE + "] out of [Byte] range", e.getMessage()); + } + } }