Skip to content

Commit 7425042

Browse files
authored
SQL: refactor In predicate moving it to QL project (#52870)
* Move In, InPipe and InProcessor out of SQL to the common QL project. * Move tests classes to the QL project. * Create SQL dedicated In class to handle SQL specific data types. * Update SQL classes to use the InPipe and InProcessor QL classes. * Extract common Foldables methods in QL project. * Be more explicit when folding and converting a foldable value, by removing most of the code inside Foldables class.
1 parent 007ec1f commit 7425042

File tree

21 files changed

+270
-222
lines changed

21 files changed

+270
-222
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
package org.elasticsearch.xpack.ql.expression;
7+
8+
import org.elasticsearch.xpack.ql.QlIllegalArgumentException;
9+
10+
public abstract class Foldables {
11+
12+
public static Object valueOf(Expression e) {
13+
if (e.foldable()) {
14+
return e.fold();
15+
}
16+
throw new QlIllegalArgumentException("Cannot determine value for {}", e);
17+
}
18+
}
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
package org.elasticsearch.xpack.ql.expression.predicate.operator.comparison;
7+
8+
import org.elasticsearch.xpack.ql.expression.Expression;
9+
import org.elasticsearch.xpack.ql.expression.Expressions;
10+
import org.elasticsearch.xpack.ql.expression.Foldables;
11+
import org.elasticsearch.xpack.ql.expression.Nullability;
12+
import org.elasticsearch.xpack.ql.expression.TypeResolutions;
13+
import org.elasticsearch.xpack.ql.expression.function.scalar.ScalarFunction;
14+
import org.elasticsearch.xpack.ql.expression.gen.pipeline.Pipe;
15+
import org.elasticsearch.xpack.ql.expression.gen.script.ScriptTemplate;
16+
import org.elasticsearch.xpack.ql.tree.NodeInfo;
17+
import org.elasticsearch.xpack.ql.tree.Source;
18+
import org.elasticsearch.xpack.ql.type.DataType;
19+
import org.elasticsearch.xpack.ql.type.DataTypeConverter;
20+
import org.elasticsearch.xpack.ql.type.DataTypes;
21+
import org.elasticsearch.xpack.ql.util.CollectionUtils;
22+
23+
import java.util.ArrayList;
24+
import java.util.LinkedHashSet;
25+
import java.util.List;
26+
import java.util.Objects;
27+
import java.util.stream.Collectors;
28+
29+
import static org.elasticsearch.common.logging.LoggerMessageFormat.format;
30+
import static org.elasticsearch.xpack.ql.expression.gen.script.ParamsBuilder.paramsBuilder;
31+
import static org.elasticsearch.xpack.ql.util.StringUtils.ordinal;
32+
33+
public class In extends ScalarFunction {
34+
35+
private final Expression value;
36+
private final List<Expression> list;
37+
38+
public In(Source source, Expression value, List<Expression> list) {
39+
super(source, CollectionUtils.combine(list, value));
40+
this.value = value;
41+
this.list = new ArrayList<>(new LinkedHashSet<>(list));
42+
}
43+
44+
@Override
45+
protected NodeInfo<In> info() {
46+
return NodeInfo.create(this, In::new, value(), list());
47+
}
48+
49+
@Override
50+
public Expression replaceChildren(List<Expression> newChildren) {
51+
if (newChildren.size() < 2) {
52+
throw new IllegalArgumentException("expected at least [2] children but received [" + newChildren.size() + "]");
53+
}
54+
return new In(source(), newChildren.get(newChildren.size() - 1), newChildren.subList(0, newChildren.size() - 1));
55+
}
56+
57+
public Expression value() {
58+
return value;
59+
}
60+
61+
public List<Expression> list() {
62+
return list;
63+
}
64+
65+
@Override
66+
public DataType dataType() {
67+
return DataTypes.BOOLEAN;
68+
}
69+
70+
@Override
71+
public Nullability nullable() {
72+
return Nullability.UNKNOWN;
73+
}
74+
75+
@Override
76+
public boolean foldable() {
77+
return Expressions.foldable(children()) ||
78+
(Expressions.foldable(list) && list().stream().allMatch(Expressions::isNull));
79+
}
80+
81+
@Override
82+
public Boolean fold() {
83+
// Optimization for early return and Query folding to LocalExec
84+
if (Expressions.isNull(value) || list.size() == 1 && Expressions.isNull(list.get(0))) {
85+
return null;
86+
}
87+
return InProcessor.apply(value.fold(), foldAndConvertListOfValues(list, value.dataType()));
88+
}
89+
90+
@Override
91+
public ScriptTemplate asScript() {
92+
ScriptTemplate leftScript = asScript(value);
93+
94+
// fold & remove duplicates
95+
List<Object> values = new ArrayList<>(new LinkedHashSet<>(foldAndConvertListOfValues(list, value.dataType())));
96+
97+
return new ScriptTemplate(
98+
formatTemplate(format("{sql}.","in({}, {})", leftScript.template())),
99+
paramsBuilder()
100+
.script(leftScript.params())
101+
.variable(values)
102+
.build(),
103+
dataType());
104+
}
105+
106+
protected List<Object> foldAndConvertListOfValues(List<Expression> list, DataType dataType) {
107+
List<Object> values = new ArrayList<>(list.size());
108+
for (Expression e : list) {
109+
values.add(DataTypeConverter.convert(Foldables.valueOf(e), dataType));
110+
}
111+
return values;
112+
}
113+
114+
protected boolean areCompatible(DataType left, DataType right) {
115+
return DataTypes.areCompatible(left, right);
116+
}
117+
118+
@Override
119+
protected Pipe makePipe() {
120+
return new InPipe(source(), this, children().stream().map(Expressions::pipe).collect(Collectors.toList()));
121+
}
122+
123+
@Override
124+
protected TypeResolution resolveType() {
125+
TypeResolution resolution = TypeResolutions.isExact(value, functionName(), Expressions.ParamOrdinal.DEFAULT);
126+
if (resolution.unresolved()) {
127+
return resolution;
128+
}
129+
130+
for (Expression ex : list) {
131+
if (ex.foldable() == false) {
132+
return new TypeResolution(format(null, "Comparisons against variables are not (currently) supported; offender [{}] in [{}]",
133+
Expressions.name(ex),
134+
sourceText()));
135+
}
136+
}
137+
138+
DataType dt = value.dataType();
139+
for (int i = 0; i < list.size(); i++) {
140+
Expression listValue = list.get(i);
141+
if (areCompatible(dt, listValue.dataType()) == false) {
142+
return new TypeResolution(format(null, "{} argument of [{}] must be [{}], found value [{}] type [{}]",
143+
ordinal(i + 1),
144+
sourceText(),
145+
dt.typeName(),
146+
Expressions.name(listValue),
147+
listValue.dataType().typeName()));
148+
}
149+
}
150+
151+
return super.resolveType();
152+
}
153+
154+
@Override
155+
public int hashCode() {
156+
return Objects.hash(value, list);
157+
}
158+
159+
@Override
160+
public boolean equals(Object obj) {
161+
if (this == obj) {
162+
return true;
163+
}
164+
if (obj == null || getClass() != obj.getClass()) {
165+
return false;
166+
}
167+
168+
In other = (In) obj;
169+
return Objects.equals(value, other.value)
170+
&& Objects.equals(list, other.list);
171+
}
172+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* or more contributor license agreements. Licensed under the Elastic License;
44
* you may not use this file except in compliance with the Elastic License.
55
*/
6-
package org.elasticsearch.xpack.sql.expression.predicate.operator.comparison;
6+
package org.elasticsearch.xpack.ql.expression.predicate.operator.comparison;
77

88
import org.elasticsearch.xpack.ql.expression.Expression;
99
import org.elasticsearch.xpack.ql.expression.gen.pipeline.MultiPipe;
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@
33
* or more contributor license agreements. Licensed under the Elastic License;
44
* you may not use this file except in compliance with the Elastic License.
55
*/
6-
package org.elasticsearch.xpack.sql.expression.predicate.operator.comparison;
6+
package org.elasticsearch.xpack.ql.expression.predicate.operator.comparison;
77

88
import org.elasticsearch.common.io.stream.StreamInput;
99
import org.elasticsearch.common.io.stream.StreamOutput;
1010
import org.elasticsearch.xpack.ql.expression.gen.processor.Processor;
11-
import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.Comparisons;
1211

1312
import java.io.IOException;
1413
import java.util.ArrayList;
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@
33
* or more contributor license agreements. Licensed under the Elastic License;
44
* you may not use this file except in compliance with the Elastic License.
55
*/
6-
package org.elasticsearch.xpack.sql.expression.predicate.operator.comparison;
6+
package org.elasticsearch.xpack.ql.expression.predicate.operator.comparison;
77

88
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
99
import org.elasticsearch.common.io.stream.Writeable.Reader;
1010
import org.elasticsearch.test.AbstractWireSerializingTestCase;
1111
import org.elasticsearch.xpack.ql.TestUtils;
1212
import org.elasticsearch.xpack.ql.expression.Literal;
1313
import org.elasticsearch.xpack.ql.expression.gen.processor.ConstantProcessor;
14+
import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.In;
15+
import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.InProcessor;
1416
import org.elasticsearch.xpack.ql.expression.processor.Processors;
15-
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.In;
16-
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.InProcessor;
1717

1818
import java.util.Arrays;
1919

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
* or more contributor license agreements. Licensed under the Elastic License;
44
* you may not use this file except in compliance with the Elastic License.
55
*/
6-
package org.elasticsearch.xpack.sql.expression.predicate.operator.comparison;
6+
package org.elasticsearch.xpack.ql.expression.predicate.operator.comparison;
77

88
import org.elasticsearch.test.ESTestCase;
99
import org.elasticsearch.xpack.ql.TestUtils;
1010
import org.elasticsearch.xpack.ql.expression.Literal;
11-
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.In;
11+
import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.In;
1212

1313
import java.util.Arrays;
1414

x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import org.elasticsearch.xpack.ql.expression.Expression;
1616
import org.elasticsearch.xpack.ql.expression.Expressions;
1717
import org.elasticsearch.xpack.ql.expression.FieldAttribute;
18+
import org.elasticsearch.xpack.ql.expression.Foldables;
1819
import org.elasticsearch.xpack.ql.expression.NamedExpression;
1920
import org.elasticsearch.xpack.ql.expression.Order;
2021
import org.elasticsearch.xpack.ql.expression.ReferenceAttribute;
@@ -46,7 +47,6 @@
4647
import org.elasticsearch.xpack.ql.type.UnsupportedEsField;
4748
import org.elasticsearch.xpack.ql.util.CollectionUtils;
4849
import org.elasticsearch.xpack.ql.util.Holder;
49-
import org.elasticsearch.xpack.sql.expression.Foldables;
5050
import org.elasticsearch.xpack.sql.expression.SubQueryExpression;
5151
import org.elasticsearch.xpack.sql.expression.function.scalar.Cast;
5252
import org.elasticsearch.xpack.sql.plan.logical.Join;

x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Foldables.java

Lines changed: 0 additions & 63 deletions
This file was deleted.

x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Percentile.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@
77

88
import org.elasticsearch.xpack.ql.expression.Expression;
99
import org.elasticsearch.xpack.ql.expression.Expressions.ParamOrdinal;
10+
import org.elasticsearch.xpack.ql.expression.Foldables;
1011
import org.elasticsearch.xpack.ql.expression.function.aggregate.EnclosedAgg;
1112
import org.elasticsearch.xpack.ql.tree.NodeInfo;
1213
import org.elasticsearch.xpack.ql.tree.Source;
1314
import org.elasticsearch.xpack.ql.type.DataType;
1415
import org.elasticsearch.xpack.ql.type.DataTypes;
15-
import org.elasticsearch.xpack.sql.expression.Foldables;
16+
import org.elasticsearch.xpack.sql.type.SqlDataTypeConverter;
1617

1718
import java.util.List;
1819

@@ -68,6 +69,7 @@ public DataType dataType() {
6869

6970
@Override
7071
public String innerName() {
71-
return Double.toString(Foldables.doubleValueOf(percent));
72+
Double value = (Double) SqlDataTypeConverter.convert(Foldables.valueOf(percent), DataTypes.DOUBLE);
73+
return Double.toString(value);
7274
}
7375
}

x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/PercentileRank.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@
77

88
import org.elasticsearch.xpack.ql.expression.Expression;
99
import org.elasticsearch.xpack.ql.expression.Expressions.ParamOrdinal;
10+
import org.elasticsearch.xpack.ql.expression.Foldables;
1011
import org.elasticsearch.xpack.ql.expression.function.aggregate.AggregateFunction;
1112
import org.elasticsearch.xpack.ql.expression.function.aggregate.EnclosedAgg;
1213
import org.elasticsearch.xpack.ql.tree.NodeInfo;
1314
import org.elasticsearch.xpack.ql.tree.Source;
1415
import org.elasticsearch.xpack.ql.type.DataType;
1516
import org.elasticsearch.xpack.ql.type.DataTypes;
16-
import org.elasticsearch.xpack.sql.expression.Foldables;
17+
import org.elasticsearch.xpack.sql.type.SqlDataTypeConverter;
1718

1819
import java.util.List;
1920

@@ -69,6 +70,7 @@ public DataType dataType() {
6970

7071
@Override
7172
public String innerName() {
72-
return Double.toString(Foldables.doubleValueOf(value));
73+
Double doubleValue = (Double) SqlDataTypeConverter.convert(Foldables.valueOf(value), DataTypes.DOUBLE);
74+
return Double.toString(doubleValue);
7375
}
7476
}

x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/Processors.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.elasticsearch.xpack.ql.expression.gen.processor.Processor;
1111
import org.elasticsearch.xpack.ql.expression.predicate.nulls.CheckNullProcessor;
1212
import org.elasticsearch.xpack.ql.expression.predicate.operator.arithmetic.BinaryArithmeticOperation;
13+
import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.InProcessor;
1314
import org.elasticsearch.xpack.ql.type.Converter;
1415
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateAddProcessor;
1516
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateDiffProcessor;
@@ -38,7 +39,6 @@
3839
import org.elasticsearch.xpack.sql.expression.predicate.conditional.ConditionalProcessor;
3940
import org.elasticsearch.xpack.sql.expression.predicate.conditional.NullIfProcessor;
4041
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.SqlBinaryArithmeticOperation;
41-
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.InProcessor;
4242
import org.elasticsearch.xpack.sql.type.SqlDataTypeConverter.SqlConverter;
4343

4444
import java.util.ArrayList;

0 commit comments

Comments
 (0)