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()); + } + } }