mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-03-09 14:34:43 +00:00
SQL: Implement GREATEST and LEAST functions (#35879)
Add GREATEST(expr1, expr2, ... exprN) and LEAST(expr1, expr2, exprN) functions which are in the family of CONDITIONAL functions. Implementation follows PostgreSQL behaviour, so the functions return `NULL` when all of their arguments evaluate to `NULL`. Renamed `CoalescePipe` and `CoalesceProcessor` to `ConditionalPipe` and `ConditionalProcessor` respectively, to be able to reuse them for `Greatest` and `Least` evaluations. To achieve that `ConditionalOperation` has been added to differentiate between the functionalities at execution time. Closes: #35878
This commit is contained in:
parent
b95a4db6e6
commit
3f7cae3f0d
@ -190,3 +190,87 @@ include-tagged::{sql-specs}/docs.csv-spec[nullIfReturnFirst]
|
||||
----
|
||||
include-tagged::{sql-specs}/docs.csv-spec[nullIfReturnNull]
|
||||
----
|
||||
|
||||
|
||||
[[sql-functions-conditional-greatest]]
|
||||
==== `GREATEST`
|
||||
|
||||
.Synopsis
|
||||
[source, sql]
|
||||
----
|
||||
GREATEST ( expression<1>, expression<2>, ... )
|
||||
----
|
||||
|
||||
*Input*:
|
||||
|
||||
<1> 1st expression
|
||||
|
||||
<2> 2nd expression
|
||||
|
||||
...
|
||||
|
||||
**N**th expression
|
||||
|
||||
GREATEST can take an arbitrary number of arguments and
|
||||
all of them must be of the same data type.
|
||||
|
||||
*Output*: one of the expressions or `null`
|
||||
|
||||
.Description
|
||||
|
||||
Returns the argument that has the largest value which is not null.
|
||||
If all arguments are null, then it returns `null`.
|
||||
|
||||
|
||||
|
||||
["source","sql",subs="attributes,callouts,macros"]
|
||||
----
|
||||
include-tagged::{sql-specs}/docs.csv-spec[greatestReturnNonNull]
|
||||
----
|
||||
|
||||
["source","sql",subs="attributes,callouts,macros"]
|
||||
----
|
||||
include-tagged::{sql-specs}/docs.csv-spec[greatestReturnNull]
|
||||
----
|
||||
|
||||
|
||||
[[sql-functions-conditional-least]]
|
||||
==== `LEAST`
|
||||
|
||||
.Synopsis
|
||||
[source, sql]
|
||||
----
|
||||
LEAST ( expression<1>, expression<2>, ... )
|
||||
----
|
||||
|
||||
*Input*:
|
||||
|
||||
<1> 1st expression
|
||||
|
||||
<2> 2nd expression
|
||||
|
||||
...
|
||||
|
||||
**N**th expression
|
||||
|
||||
LEAST can take an arbitrary number of arguments and
|
||||
all of them must be of the same data type.
|
||||
|
||||
*Output*: one of the expressions or `null`
|
||||
|
||||
.Description
|
||||
|
||||
Returns the argument that has the smallest value which is not null.
|
||||
If all arguments are null, then it returns `null`.
|
||||
|
||||
|
||||
|
||||
["source","sql",subs="attributes,callouts,macros"]
|
||||
----
|
||||
include-tagged::{sql-specs}/docs.csv-spec[leastReturnNonNull]
|
||||
----
|
||||
|
||||
["source","sql",subs="attributes,callouts,macros"]
|
||||
----
|
||||
include-tagged::{sql-specs}/docs.csv-spec[leastReturnNull]
|
||||
----
|
||||
|
@ -13,11 +13,14 @@ import java.util.regex.Pattern;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
|
||||
public abstract class ShowTestCase extends CliIntegrationTestCase {
|
||||
|
||||
private static final String HEADER_SEPARATOR = "----------";
|
||||
|
||||
public void testShowTables() throws IOException {
|
||||
index("test1", body -> body.field("test_field", "test_value"));
|
||||
index("test2", body -> body.field("test_field", "test_value"));
|
||||
assertThat(command("SHOW TABLES"), RegexMatcher.matches("\\s*name\\s*"));
|
||||
assertThat(readLine(), containsString("----------"));
|
||||
assertThat(readLine(), containsString(HEADER_SEPARATOR));
|
||||
assertThat(readLine(), RegexMatcher.matches("\\s*test[12]\\s*"));
|
||||
assertThat(readLine(), RegexMatcher.matches("\\s*test[12]\\s*"));
|
||||
assertEquals("", readLine());
|
||||
@ -25,7 +28,7 @@ public abstract class ShowTestCase extends CliIntegrationTestCase {
|
||||
|
||||
public void testShowFunctions() throws IOException {
|
||||
assertThat(command("SHOW FUNCTIONS"), RegexMatcher.matches("\\s*name\\s*\\|\\s*type\\s*"));
|
||||
assertThat(readLine(), containsString("----------"));
|
||||
assertThat(readLine(), containsString(HEADER_SEPARATOR));
|
||||
assertThat(readLine(), RegexMatcher.matches("\\s*AVG\\s*\\|\\s*AGGREGATE\\s*"));
|
||||
assertThat(readLine(), RegexMatcher.matches("\\s*COUNT\\s*\\|\\s*AGGREGATE\\s*"));
|
||||
assertThat(readLine(), RegexMatcher.matches("\\s*MAX\\s*\\|\\s*AGGREGATE\\s*"));
|
||||
@ -50,7 +53,8 @@ public abstract class ShowTestCase extends CliIntegrationTestCase {
|
||||
|
||||
public void testShowFunctionsLikePrefix() throws IOException {
|
||||
assertThat(command("SHOW FUNCTIONS LIKE 'L%'"), RegexMatcher.matches("\\s*name\\s*\\|\\s*type\\s*"));
|
||||
assertThat(readLine(), containsString("----------"));
|
||||
assertThat(readLine(), containsString(HEADER_SEPARATOR));
|
||||
assertThat(readLine(), RegexMatcher.matches("\\s*LEAST\\s*\\|\\s*CONDITIONAL\\s*"));
|
||||
assertThat(readLine(), RegexMatcher.matches("\\s*LOG\\s*\\|\\s*SCALAR\\s*"));
|
||||
assertThat(readLine(), RegexMatcher.matches("\\s*LOG10\\s*\\|\\s*SCALAR\\s*"));
|
||||
assertThat(readLine(), RegexMatcher.matches("\\s*LCASE\\s*\\|\\s*SCALAR\\s*"));
|
||||
@ -63,7 +67,7 @@ public abstract class ShowTestCase extends CliIntegrationTestCase {
|
||||
|
||||
public void testShowFunctionsLikeInfix() throws IOException {
|
||||
assertThat(command("SHOW FUNCTIONS LIKE '%DAY%'"), RegexMatcher.matches("\\s*name\\s*\\|\\s*type\\s*"));
|
||||
assertThat(readLine(), containsString("----------"));
|
||||
assertThat(readLine(), containsString(HEADER_SEPARATOR));
|
||||
assertThat(readLine(), RegexMatcher.matches("\\s*DAY\\s*\\|\\s*SCALAR\\s*"));
|
||||
assertThat(readLine(), RegexMatcher.matches("\\s*DAYNAME\\s*\\|\\s*SCALAR\\s*"));
|
||||
assertThat(readLine(), RegexMatcher.matches("\\s*DAYOFMONTH\\s*\\|\\s*SCALAR\\s*"));
|
||||
|
@ -20,10 +20,12 @@ STDDEV_POP |AGGREGATE
|
||||
SUM_OF_SQUARES |AGGREGATE
|
||||
VAR_POP |AGGREGATE
|
||||
COALESCE |CONDITIONAL
|
||||
GREATEST |CONDITIONAL
|
||||
IFNULL |CONDITIONAL
|
||||
ISNULL |CONDITIONAL
|
||||
NVL |CONDITIONAL
|
||||
LEAST |CONDITIONAL
|
||||
NULLIF |CONDITIONAL
|
||||
NVL |CONDITIONAL
|
||||
DAY |SCALAR
|
||||
DAYNAME |SCALAR
|
||||
DAYOFMONTH |SCALAR
|
||||
|
@ -197,10 +197,12 @@ STDDEV_POP |AGGREGATE
|
||||
SUM_OF_SQUARES |AGGREGATE
|
||||
VAR_POP |AGGREGATE
|
||||
COALESCE |CONDITIONAL
|
||||
GREATEST |CONDITIONAL
|
||||
IFNULL |CONDITIONAL
|
||||
ISNULL |CONDITIONAL
|
||||
NVL |CONDITIONAL
|
||||
LEAST |CONDITIONAL
|
||||
NULLIF |CONDITIONAL
|
||||
NVL |CONDITIONAL
|
||||
DAY |SCALAR
|
||||
DAYNAME |SCALAR
|
||||
DAYOFMONTH |SCALAR
|
||||
@ -1620,6 +1622,48 @@ null
|
||||
// end::nullIfReturnNull
|
||||
;
|
||||
|
||||
greatestReturnNonNull
|
||||
// tag::greatestReturnNonNull
|
||||
SELECT GREATEST(null, 1, 2) AS "greatest";
|
||||
|
||||
greatest
|
||||
---------------
|
||||
2
|
||||
// end::greatestReturnNonNull
|
||||
;
|
||||
|
||||
|
||||
greatestReturnNull
|
||||
// tag::greatestReturnNull
|
||||
SELECT GREATEST(null, null, null, null) AS "greatest";
|
||||
|
||||
greatest
|
||||
---------------
|
||||
null
|
||||
// end::greatestReturnNull
|
||||
;
|
||||
|
||||
leastReturnNonNull
|
||||
// tag::leastReturnNonNull
|
||||
SELECT LEAST(null, 2, 1) AS "least";
|
||||
|
||||
least
|
||||
---------------
|
||||
1
|
||||
// end::leastReturnNonNull
|
||||
;
|
||||
|
||||
|
||||
leastReturnNull
|
||||
// tag::leastReturnNull
|
||||
SELECT LEAST(null, null, null, null) AS "least";
|
||||
|
||||
least
|
||||
---------------
|
||||
null
|
||||
// end::leastReturnNull
|
||||
;
|
||||
|
||||
nullEqualsCompareWithNull
|
||||
// tag::nullEqualsCompareWithNull
|
||||
SELECT 'elastic' <=> null AS "equals";
|
||||
|
@ -15,10 +15,28 @@ ifNullField
|
||||
SELECT IFNULL(null, ABS(emp_no) + 1) AS "ifnull" FROM test_emp ORDER BY emp_no LIMIT 5;
|
||||
|
||||
nullIfField
|
||||
SELECT NULLIF(emp_no - 2 + 3, ABS(emp_no) + 1) AS "nullif1", NULLIF(emp_no + 1, emp_no - 1) as "nullif2" FROM test_emp ORDER BY emp_no LIMIT 5;
|
||||
SELECT NULLIF(emp_no - 2 + 3, ABS(emp_no) + 1) AS "nullif1", NULLIF(emp_no + 1, emp_no - 1) AS "nullif2" FROM test_emp ORDER BY emp_no LIMIT 5;
|
||||
|
||||
nullIfWhere
|
||||
SELECT NULLIF(10002, ABS(emp_no) + 1) AS c, emp_no FROM test_emp WHERE NULLIF(10003, ABS(emp_no) + 1) IS NOT NULL ORDER BY emp_no NULLS FIRST LIMIT 5;
|
||||
SELECT NULLIF(10002, ABS(emp_no) + 1) AS c, emp_no FROM test_emp WHERE NULLIF(10003, ABS(emp_no) + 1) IS NOT NULL ORDER BY emp_no LIMIT 5;
|
||||
|
||||
nullIfHaving
|
||||
SELECT NULLIF(10030, ABS(MAX(emp_no)) + 1) AS nif FROM test_emp GROUP BY languages HAVING nif IS NOT NULL ORDER BY languages;
|
||||
|
||||
greatestField
|
||||
SELECT GREATEST(emp_no - 1 + 3, ABS(emp_no) + 1) AS "greatest" FROM test_emp ORDER BY emp_no LIMIT 5;
|
||||
|
||||
greatestWhere
|
||||
SELECT emp_no FROM test_emp WHERE GREATEST(10005, ABS(emp_no) + 1, null, emp_no - 1 + 3) > 10008 ORDER BY emp_no LIMIT 5;
|
||||
|
||||
greatestHaving
|
||||
SELECT GREATEST(10096, ABS(MAX(emp_no)) + 1) AS gt FROM test_emp GROUP BY languages HAVING gt >= 10098 ORDER BY languages;
|
||||
|
||||
leastField
|
||||
SELECT LEAST(emp_no - 1 + 3, ABS(emp_no) + 1) AS "least" FROM test_emp ORDER BY emp_no LIMIT 5;
|
||||
|
||||
leastWhere
|
||||
SELECT emp_no FROM test_emp WHERE LEAST(10005, ABS(emp_no) + 1, null, emp_no - 1 + 3) > 10004 ORDER BY emp_no LIMIT 5;
|
||||
|
||||
leastHaving
|
||||
SELECT LEAST(10098, ABS(MAX(emp_no)) + 1) AS lt FROM test_emp GROUP BY languages HAVING lt >= 10095 ORDER BY languages;
|
||||
|
@ -10,7 +10,10 @@ import org.elasticsearch.xpack.sql.type.DataType;
|
||||
import org.elasticsearch.xpack.sql.type.DataTypeConversion;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class Foldables {
|
||||
|
||||
@ -46,11 +49,18 @@ public abstract class Foldables {
|
||||
}
|
||||
|
||||
public static <T> List<T> valuesOf(List<Expression> list, DataType to) {
|
||||
List<T> l = new ArrayList<>(list.size());
|
||||
for (Expression e : list) {
|
||||
l.add(valueOf(e, to));
|
||||
return foldTo(list, to, new ArrayList<>(list.size()));
|
||||
}
|
||||
|
||||
public static <T> Set<T> valuesOfNoDuplicates(List<Expression> list, DataType to) {
|
||||
return foldTo(list, to, new LinkedHashSet<>(list.size()));
|
||||
}
|
||||
|
||||
private static <T, C extends Collection<T>> C foldTo(Collection<Expression> expressions, DataType to, C values) {
|
||||
for (Expression e : expressions) {
|
||||
values.add(valueOf(e, to));
|
||||
}
|
||||
return l;
|
||||
return values;
|
||||
}
|
||||
|
||||
public static List<Double> doubleValuesOf(List<Expression> list) {
|
||||
|
@ -83,7 +83,9 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.string.Space;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.string.Substring;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.string.UCase;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Coalesce;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Greatest;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.IfNull;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Least;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.NullIf;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Mod;
|
||||
import org.elasticsearch.xpack.sql.parser.ParsingException;
|
||||
@ -157,9 +159,11 @@ public class FunctionRegistry {
|
||||
def(Kurtosis.class, Kurtosis::new));
|
||||
// Scalar functions
|
||||
// conditional
|
||||
addToMap(def(Coalesce.class, Coalesce::new));
|
||||
addToMap(def(IfNull.class, IfNull::new, "ISNULL", "NVL"));
|
||||
addToMap(def(NullIf.class, NullIf::new));
|
||||
addToMap(def(Coalesce.class, Coalesce::new),
|
||||
def(IfNull.class, IfNull::new, "ISNULL", "NVL"),
|
||||
def(NullIf.class, NullIf::new),
|
||||
def(Greatest.class, Greatest::new),
|
||||
def(Least.class, Least::new));
|
||||
// Date
|
||||
addToMap(def(DayName.class, DayName::new, "DAYNAME"),
|
||||
def(DayOfMonth.class, DayOfMonth::new, "DAYOFMONTH", "DAY", "DOM"),
|
||||
|
@ -25,7 +25,7 @@ import org.elasticsearch.xpack.sql.expression.gen.processor.ChainingProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.processor.ConstantProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.processor.HitExtractorProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.CoalesceProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.ConditionalProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.NullIfProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.logical.BinaryLogicProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.logical.NotProcessor;
|
||||
@ -61,7 +61,7 @@ public final class Processors {
|
||||
entries.add(new Entry(Processor.class, NotProcessor.NAME, NotProcessor::new));
|
||||
// null
|
||||
entries.add(new Entry(Processor.class, CheckNullProcessor.NAME, CheckNullProcessor::new));
|
||||
entries.add(new Entry(Processor.class, CoalesceProcessor.NAME, CoalesceProcessor::new));
|
||||
entries.add(new Entry(Processor.class, ConditionalProcessor.NAME, ConditionalProcessor::new));
|
||||
entries.add(new Entry(Processor.class, NullIfProcessor.NAME, NullIfProcessor::new));
|
||||
|
||||
// arithmetic
|
||||
|
@ -23,7 +23,7 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.string.StringProce
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.string.SubstringFunctionProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.literal.IntervalDayTime;
|
||||
import org.elasticsearch.xpack.sql.expression.literal.IntervalYearMonth;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.CoalesceProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.ConditionalProcessor.ConditionalOperation;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.NullIfProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.logical.BinaryLogicProcessor.BinaryLogicOperation;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.logical.NotProcessor;
|
||||
@ -144,7 +144,15 @@ public final class InternalSqlScriptUtils {
|
||||
// Null
|
||||
//
|
||||
public static Object coalesce(List<Object> expressions) {
|
||||
return CoalesceProcessor.apply(expressions);
|
||||
return ConditionalOperation.COALESCE.apply(expressions);
|
||||
}
|
||||
|
||||
public static Object greatest(List<Object> expressions) {
|
||||
return ConditionalOperation.GREATEST.apply(expressions);
|
||||
}
|
||||
|
||||
public static Object least(List<Object> expressions) {
|
||||
return ConditionalOperation.LEAST.apply(expressions);
|
||||
}
|
||||
|
||||
public static Object nullif(Object left, Object right) {
|
||||
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.predicate.conditional;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.ConditionalProcessor.ConditionalOperation;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.type.DataTypeConversion;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
|
||||
|
||||
/**
|
||||
* Base class for conditional predicates with arbitrary number of arguments
|
||||
*/
|
||||
public abstract class ArbitraryConditionalFunction extends ConditionalFunction {
|
||||
|
||||
private final ConditionalOperation operation;
|
||||
|
||||
ArbitraryConditionalFunction(Location location, List<Expression> fields, ConditionalOperation operation) {
|
||||
super(location, fields);
|
||||
this.operation = operation;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TypeResolution resolveType() {
|
||||
for (Expression e : children()) {
|
||||
dataType = DataTypeConversion.commonType(dataType, e.dataType());
|
||||
}
|
||||
return TypeResolution.TYPE_RESOLVED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Pipe makePipe() {
|
||||
return new ConditionalPipe(location(), this, Expressions.pipe(children()), operation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptTemplate asScript() {
|
||||
List<ScriptTemplate> templates = new ArrayList<>();
|
||||
for (Expression ex : children()) {
|
||||
templates.add(asScript(ex));
|
||||
}
|
||||
|
||||
StringJoiner template = new StringJoiner(",", "{sql}." + operation.scriptMethodName() +"([", "])");
|
||||
ParamsBuilder params = paramsBuilder();
|
||||
|
||||
for (ScriptTemplate scriptTemplate : templates) {
|
||||
template.add(scriptTemplate.template());
|
||||
params.script(scriptTemplate.params());
|
||||
}
|
||||
|
||||
return new ScriptTemplate(template.toString(), params.build(), dataType());
|
||||
}
|
||||
}
|
@ -7,27 +7,17 @@
|
||||
package org.elasticsearch.xpack.sql.expression.predicate.conditional;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
|
||||
import static org.elasticsearch.xpack.sql.expression.predicate.conditional.ConditionalProcessor.ConditionalOperation.COALESCE;
|
||||
|
||||
public class Coalesce extends ConditionalFunction {
|
||||
|
||||
private DataType dataType = DataType.NULL;
|
||||
public class Coalesce extends ArbitraryConditionalFunction {
|
||||
|
||||
public Coalesce(Location location, List<Expression> fields) {
|
||||
super(location, fields);
|
||||
super(location, fields, COALESCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -40,19 +30,6 @@ public class Coalesce extends ConditionalFunction {
|
||||
return new Coalesce(location(), newChildren);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TypeResolution resolveType() {
|
||||
for (Expression e : children()) {
|
||||
dataType = DataTypeConversion.commonType(dataType, e.dataType());
|
||||
}
|
||||
return TypeResolution.TYPE_RESOLVED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType dataType() {
|
||||
return dataType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean foldable() {
|
||||
// if the first entry is foldable, so is coalesce
|
||||
@ -62,37 +39,9 @@ public class Coalesce extends ConditionalFunction {
|
||||
return (children.isEmpty() || (children.get(0).foldable() && children.get(0).fold() != null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fold() {
|
||||
List<Expression> children = children();
|
||||
return children.isEmpty() ? null : children.get(0).fold();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptTemplate asScript() {
|
||||
List<ScriptTemplate> templates = new ArrayList<>();
|
||||
for (Expression ex : children()) {
|
||||
templates.add(asScript(ex));
|
||||
}
|
||||
|
||||
StringJoiner template = new StringJoiner(",", "{sql}.coalesce([", "])");
|
||||
ParamsBuilder params = paramsBuilder();
|
||||
|
||||
for (ScriptTemplate scriptTemplate : templates) {
|
||||
template.add(scriptTemplate.template());
|
||||
params.script(scriptTemplate.params());
|
||||
}
|
||||
|
||||
return new ScriptTemplate(template.toString(), params.build(), dataType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Pipe makePipe() {
|
||||
return new CoalescePipe(location(), this, Expressions.pipe(children()));
|
||||
}
|
||||
}
|
||||
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* 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.predicate.conditional;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.MultiPipe;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CoalescePipe extends MultiPipe {
|
||||
|
||||
public CoalescePipe(Location location, Expression expression, List<Pipe> children) {
|
||||
super(location, expression, children);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NodeInfo<CoalescePipe> info() {
|
||||
return NodeInfo.create(this, CoalescePipe::new, expression(), children());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pipe replaceChildren(List<Pipe> newChildren) {
|
||||
return new CoalescePipe(location(), expression(), newChildren);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Processor asProcessor(List<Processor> procs) {
|
||||
return new CoalesceProcessor(procs);
|
||||
}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
/*
|
||||
* 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.predicate.conditional;
|
||||
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class CoalesceProcessor implements Processor {
|
||||
|
||||
public static final String NAME = "nco";
|
||||
|
||||
private final List<Processor> processsors;
|
||||
|
||||
public CoalesceProcessor(List<Processor> processors) {
|
||||
this.processsors = processors;
|
||||
}
|
||||
|
||||
public CoalesceProcessor(StreamInput in) throws IOException {
|
||||
processsors = in.readNamedWriteableList(Processor.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWriteableName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeNamedWriteableList(processsors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object process(Object input) {
|
||||
for (Processor proc : processsors) {
|
||||
Object result = proc.process(input);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Object apply(List<?> values) {
|
||||
if (values == null || values.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (Object object : values) {
|
||||
if (object != null) {
|
||||
return object;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(processsors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
CoalesceProcessor that = (CoalesceProcessor) o;
|
||||
return Objects.equals(processsors, that.processsors);
|
||||
}
|
||||
}
|
@ -7,8 +7,10 @@
|
||||
package org.elasticsearch.xpack.sql.expression.predicate.conditional;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.ScalarFunction;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -17,7 +19,24 @@ import java.util.List;
|
||||
*/
|
||||
public abstract class ConditionalFunction extends ScalarFunction {
|
||||
|
||||
protected ConditionalFunction(Location location, List<Expression> fields) {
|
||||
protected DataType dataType = DataType.NULL;
|
||||
|
||||
ConditionalFunction(Location location, List<Expression> fields) {
|
||||
super(location, fields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType dataType() {
|
||||
return dataType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean foldable() {
|
||||
return Expressions.foldable(children());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.predicate.conditional;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.MultiPipe;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.ConditionalProcessor.ConditionalOperation;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ConditionalPipe extends MultiPipe {
|
||||
|
||||
private final ConditionalOperation operation;
|
||||
|
||||
public ConditionalPipe(Location location, Expression expression, List<Pipe> children, ConditionalOperation operation) {
|
||||
super(location, expression, children);
|
||||
this.operation = operation;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NodeInfo<ConditionalPipe> info() {
|
||||
return NodeInfo.create(this, ConditionalPipe::new, expression(), children(), operation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pipe replaceChildren(List<Pipe> newChildren) {
|
||||
return new ConditionalPipe(location(), expression(), newChildren, operation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Processor asProcessor(List<Processor> procs) {
|
||||
return new ConditionalProcessor(procs, operation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), operation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (super.equals(obj)) {
|
||||
ConditionalPipe other = (ConditionalPipe) obj;
|
||||
return Objects.equals(operation, other.operation);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.predicate.conditional;
|
||||
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class ConditionalProcessor implements Processor {
|
||||
|
||||
public enum ConditionalOperation implements Function<Collection<Object>, Object> {
|
||||
|
||||
COALESCE(Conditionals::coalesce, Conditionals::coalesceInput),
|
||||
GREATEST(Conditionals::greatest, Conditionals::greatestInput),
|
||||
LEAST(Conditionals::least, Conditionals::leastInput);
|
||||
|
||||
|
||||
String scriptMethodName() {
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
private final Function<Collection<Object>, Object> process;
|
||||
private final BiFunction<List<Processor>, Object, Object> inputProcess;
|
||||
|
||||
ConditionalOperation(Function<Collection<Object>, Object> process,
|
||||
BiFunction<List<Processor>, Object, Object> inputProcess) {
|
||||
this.process = process;
|
||||
this.inputProcess = inputProcess;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object apply(Collection<Object> objects) {
|
||||
return process.apply(objects);
|
||||
}
|
||||
|
||||
Object applyOnInput(List<Processor> processors, Object input) {
|
||||
return inputProcess.apply(processors, input);
|
||||
}
|
||||
}
|
||||
|
||||
public static final String NAME = "nco";
|
||||
|
||||
private final List<Processor> processors;
|
||||
private final ConditionalOperation operation;
|
||||
|
||||
public ConditionalProcessor(List<Processor> processors, ConditionalOperation operation) {
|
||||
this.processors = processors;
|
||||
this.operation = operation;
|
||||
}
|
||||
|
||||
public ConditionalProcessor(StreamInput in) throws IOException {
|
||||
processors = in.readNamedWriteableList(Processor.class);
|
||||
operation = in.readEnum(ConditionalOperation.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWriteableName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeNamedWriteableList(processors);
|
||||
out.writeEnum(operation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object process(Object input) {
|
||||
return operation.applyOnInput(processors, input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ConditionalProcessor that = (ConditionalProcessor) o;
|
||||
return Objects.equals(processors, that.processors) &&
|
||||
operation == that.operation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(processors, operation);
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.predicate.conditional;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.Comparisons;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
final class Conditionals {
|
||||
|
||||
private Conditionals() {}
|
||||
|
||||
static Object coalesce(Collection<Object> values) {
|
||||
if (values == null || values.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (Object object : values) {
|
||||
if (object != null) {
|
||||
return object;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static Object coalesceInput(List<Processor> processors, Object input) {
|
||||
for (Processor proc : processors) {
|
||||
Object result = proc.process(input);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Object greatest(Collection<Object> values) {
|
||||
return extremum(values, Comparisons::gt);
|
||||
}
|
||||
|
||||
static Object greatestInput(Collection<Processor> processors, Object input) {
|
||||
List<Object> values = new ArrayList<>(processors.size());
|
||||
for (Processor processor : processors) {
|
||||
values.add(processor.process(input));
|
||||
}
|
||||
return greatest(values);
|
||||
}
|
||||
|
||||
static Object least(Collection<Object> values) {
|
||||
return extremum(values, Comparisons::lt);
|
||||
}
|
||||
|
||||
static Object leastInput(List<Processor> processors, Object input) {
|
||||
List<Object> values = new ArrayList<>(processors.size());
|
||||
for (Processor processor : processors) {
|
||||
values.add(processor.process(input));
|
||||
}
|
||||
return least(values);
|
||||
}
|
||||
|
||||
private static Object extremum(Collection<Object> values, BiFunction<Object, Object, Boolean> comparison) {
|
||||
if (values == null || values.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object result = null;
|
||||
boolean isFirst = true;
|
||||
for (Object value : values) {
|
||||
if (isFirst || (result == null) || (comparison.apply(value, result) == Boolean.TRUE)) {
|
||||
result = value;
|
||||
}
|
||||
isFirst = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.predicate.conditional;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Foldables;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.expression.predicate.conditional.ConditionalProcessor.ConditionalOperation.GREATEST;
|
||||
|
||||
public class Greatest extends ArbitraryConditionalFunction {
|
||||
|
||||
public Greatest(Location location, List<Expression> fields) {
|
||||
super(location, new ArrayList<>(new LinkedHashSet<>(fields)), GREATEST);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NodeInfo<? extends Greatest> info() {
|
||||
return NodeInfo.create(this, Greatest::new, children());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression replaceChildren(List<Expression> newChildren) {
|
||||
return new Greatest(location(), newChildren);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fold() {
|
||||
return GREATEST.apply(Foldables.valuesOfNoDuplicates(children(), dataType));
|
||||
}
|
||||
}
|
@ -19,12 +19,16 @@ import java.util.List;
|
||||
public class IfNull extends Coalesce {
|
||||
|
||||
public IfNull(Location location, Expression first, Expression second) {
|
||||
super(location, Arrays.asList(first, second));
|
||||
this(location, Arrays.asList(first, second));
|
||||
}
|
||||
|
||||
private IfNull(Location location, List<Expression> expressions) {
|
||||
super(location, expressions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression replaceChildren(List<Expression> newChildren) {
|
||||
return new IfNull(location(), newChildren.get(0), newChildren.get(1));
|
||||
return new IfNull(location(), newChildren);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.predicate.conditional;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Foldables;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.expression.predicate.conditional.ConditionalProcessor.ConditionalOperation.LEAST;
|
||||
|
||||
public class Least extends ArbitraryConditionalFunction {
|
||||
|
||||
public Least(Location location, List<Expression> fields) {
|
||||
super(location, new ArrayList<>(new LinkedHashSet<>(fields)), LEAST);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NodeInfo<? extends Least> info() {
|
||||
return NodeInfo.create(this, Least::new, children());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression replaceChildren(List<Expression> newChildren) {
|
||||
return new Least(location(), newChildren);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fold() {
|
||||
return LEAST.apply(Foldables.valuesOfNoDuplicates(children(), dataType));
|
||||
}
|
||||
}
|
@ -26,8 +26,6 @@ import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.pa
|
||||
*/
|
||||
public class NullIf extends ConditionalFunction {
|
||||
|
||||
private DataType dataType;
|
||||
|
||||
public NullIf(Location location, Expression left, Expression right) {
|
||||
super(location, Arrays.asList(left, right));
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ public final class Comparisons {
|
||||
return i == null ? null : i.intValue() != 0;
|
||||
}
|
||||
|
||||
static Boolean lt(Object l, Object r) {
|
||||
public static Boolean lt(Object l, Object r) {
|
||||
Integer i = compare(l, r);
|
||||
return i == null ? null : i.intValue() < 0;
|
||||
}
|
||||
@ -42,7 +42,7 @@ public final class Comparisons {
|
||||
return i == null ? null : i.intValue() <= 0;
|
||||
}
|
||||
|
||||
static Boolean gt(Object l, Object r) {
|
||||
public static Boolean gt(Object l, Object r) {
|
||||
Integer i = compare(l, r);
|
||||
return i == null ? null : i.intValue() > 0;
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ import org.elasticsearch.xpack.sql.expression.predicate.BinaryPredicate;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.Negatable;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.Predicates;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.Range;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.ArbitraryConditionalFunction;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Coalesce;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.NullIf;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.logical.And;
|
||||
@ -132,7 +133,7 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
|
||||
new ReplaceFoldableAttributes(),
|
||||
new FoldNull(),
|
||||
new ConstantFolding(),
|
||||
new SimplifyCoalesce(),
|
||||
new SimplifyConditional(),
|
||||
// boolean
|
||||
new BooleanSimplification(),
|
||||
new BooleanLiteralsOnTheRight(),
|
||||
@ -1202,34 +1203,35 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
|
||||
}
|
||||
}
|
||||
|
||||
static class SimplifyCoalesce extends OptimizerExpressionRule {
|
||||
static class SimplifyConditional extends OptimizerExpressionRule {
|
||||
|
||||
SimplifyCoalesce() {
|
||||
SimplifyConditional() {
|
||||
super(TransformDirection.DOWN);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Expression rule(Expression e) {
|
||||
if (e instanceof Coalesce) {
|
||||
Coalesce c = (Coalesce) e;
|
||||
if (e instanceof ArbitraryConditionalFunction) {
|
||||
ArbitraryConditionalFunction c = (ArbitraryConditionalFunction) e;
|
||||
|
||||
// find the first non-null foldable child (if any) and remove the rest
|
||||
// while at it, exclude any nulls found
|
||||
// exclude any nulls found
|
||||
List<Expression> newChildren = new ArrayList<>();
|
||||
|
||||
for (Expression child : c.children()) {
|
||||
if (Expressions.isNull(child) == false) {
|
||||
newChildren.add(child);
|
||||
if (child.foldable()) {
|
||||
|
||||
// For Coalesce find the first non-null foldable child (if any) and break early
|
||||
if (e instanceof Coalesce && child.foldable()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newChildren.size() < c.children().size()) {
|
||||
return new Coalesce(c.location(), newChildren);
|
||||
return c.replaceChildren(newChildren);
|
||||
}
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ import org.elasticsearch.xpack.sql.expression.gen.pipeline.AggPathInput;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.UnaryPipe;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.In;
|
||||
import org.elasticsearch.xpack.sql.plan.physical.AggregateExec;
|
||||
import org.elasticsearch.xpack.sql.plan.physical.EsQueryExec;
|
||||
import org.elasticsearch.xpack.sql.plan.physical.FilterExec;
|
||||
@ -140,9 +139,6 @@ class QueryFolder extends RuleExecutor<PhysicalPlan> {
|
||||
if (pj instanceof ScalarFunction) {
|
||||
ScalarFunction f = (ScalarFunction) pj;
|
||||
processors.put(f.toAttribute(), Expressions.pipe(f));
|
||||
} else if (pj instanceof In) {
|
||||
In in = (In) pj;
|
||||
processors.put(in.toAttribute(), Expressions.pipe(in));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +47,8 @@ class org.elasticsearch.xpack.sql.expression.function.scalar.whitelist.InternalS
|
||||
# Null
|
||||
#
|
||||
Object coalesce(java.util.List)
|
||||
Object greatest(java.util.List)
|
||||
Object least(java.util.List)
|
||||
Object nullif(Object, Object)
|
||||
|
||||
#
|
||||
|
@ -37,7 +37,9 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.string.Repeat;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.Range;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Coalesce;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Greatest;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.IfNull;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Least;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.NullIf;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.logical.And;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.logical.Not;
|
||||
@ -71,7 +73,7 @@ import org.elasticsearch.xpack.sql.optimizer.Optimizer.PropagateEquals;
|
||||
import org.elasticsearch.xpack.sql.optimizer.Optimizer.PruneDuplicateFunctions;
|
||||
import org.elasticsearch.xpack.sql.optimizer.Optimizer.PruneSubqueryAliases;
|
||||
import org.elasticsearch.xpack.sql.optimizer.Optimizer.ReplaceFoldableAttributes;
|
||||
import org.elasticsearch.xpack.sql.optimizer.Optimizer.SimplifyCoalesce;
|
||||
import org.elasticsearch.xpack.sql.optimizer.Optimizer.SimplifyConditional;
|
||||
import org.elasticsearch.xpack.sql.plan.logical.Filter;
|
||||
import org.elasticsearch.xpack.sql.plan.logical.LocalRelation;
|
||||
import org.elasticsearch.xpack.sql.plan.logical.LogicalPlan;
|
||||
@ -420,19 +422,19 @@ public class OptimizerTests extends ESTestCase {
|
||||
}
|
||||
|
||||
public void testSimplifyCoalesceNulls() {
|
||||
Expression e = new SimplifyCoalesce().rule(new Coalesce(EMPTY, asList(Literal.NULL, Literal.NULL)));
|
||||
Expression e = new SimplifyConditional().rule(new Coalesce(EMPTY, asList(Literal.NULL, Literal.NULL)));
|
||||
assertEquals(Coalesce.class, e.getClass());
|
||||
assertEquals(0, e.children().size());
|
||||
}
|
||||
|
||||
public void testSimplifyCoalesceRandomNulls() {
|
||||
Expression e = new SimplifyCoalesce().rule(new Coalesce(EMPTY, randomListOfNulls()));
|
||||
Expression e = new SimplifyConditional().rule(new Coalesce(EMPTY, randomListOfNulls()));
|
||||
assertEquals(Coalesce.class, e.getClass());
|
||||
assertEquals(0, e.children().size());
|
||||
}
|
||||
|
||||
public void testSimplifyCoalesceRandomNullsWithValue() {
|
||||
Expression e = new SimplifyCoalesce().rule(new Coalesce(EMPTY,
|
||||
Expression e = new SimplifyConditional().rule(new Coalesce(EMPTY,
|
||||
CollectionUtils.combine(
|
||||
CollectionUtils.combine(randomListOfNulls(), Literal.TRUE, Literal.FALSE, Literal.TRUE),
|
||||
randomListOfNulls())));
|
||||
@ -445,7 +447,7 @@ public class OptimizerTests extends ESTestCase {
|
||||
}
|
||||
|
||||
public void testSimplifyCoalesceFirstLiteral() {
|
||||
Expression e = new SimplifyCoalesce()
|
||||
Expression e = new SimplifyConditional()
|
||||
.rule(new Coalesce(EMPTY,
|
||||
Arrays.asList(Literal.NULL, Literal.TRUE, Literal.FALSE, new Abs(EMPTY, getFieldAttribute()))));
|
||||
assertEquals(Coalesce.class, e.getClass());
|
||||
@ -454,17 +456,19 @@ public class OptimizerTests extends ESTestCase {
|
||||
}
|
||||
|
||||
public void testSimplifyIfNullNulls() {
|
||||
Expression e = new SimplifyCoalesce().rule(new IfNull(EMPTY, Literal.NULL, Literal.NULL));
|
||||
assertEquals(Coalesce.class, e.getClass());
|
||||
Expression e = new SimplifyConditional().rule(new IfNull(EMPTY, Literal.NULL, Literal.NULL));
|
||||
assertEquals(IfNull.class, e.getClass());
|
||||
assertEquals(0, e.children().size());
|
||||
}
|
||||
|
||||
public void testSimplifyIfNullWithNullAndValue() {
|
||||
Expression e = new SimplifyCoalesce().rule(new IfNull(EMPTY, Literal.NULL, ONE));
|
||||
Expression e = new SimplifyConditional().rule(new IfNull(EMPTY, Literal.NULL, ONE));
|
||||
assertEquals(IfNull.class, e.getClass());
|
||||
assertEquals(1, e.children().size());
|
||||
assertEquals(ONE, e.children().get(0));
|
||||
|
||||
e = new SimplifyCoalesce().rule(new IfNull(EMPTY, ONE, Literal.NULL));
|
||||
e = new SimplifyConditional().rule(new IfNull(EMPTY, ONE, Literal.NULL));
|
||||
assertEquals(IfNull.class, e.getClass());
|
||||
assertEquals(1, e.children().size());
|
||||
assertEquals(ONE, e.children().get(0));
|
||||
}
|
||||
@ -475,6 +479,48 @@ public class OptimizerTests extends ESTestCase {
|
||||
assertEquals(orig, f);
|
||||
}
|
||||
|
||||
public void testSimplifyGreatestNulls() {
|
||||
Expression e = new SimplifyConditional().rule(new Greatest(EMPTY, asList(Literal.NULL, Literal.NULL)));
|
||||
assertEquals(Greatest.class, e.getClass());
|
||||
assertEquals(0, e.children().size());
|
||||
}
|
||||
|
||||
public void testSimplifyGreatestRandomNulls() {
|
||||
Expression e = new SimplifyConditional().rule(new Greatest(EMPTY, randomListOfNulls()));
|
||||
assertEquals(Greatest.class, e.getClass());
|
||||
assertEquals(0, e.children().size());
|
||||
}
|
||||
|
||||
public void testSimplifyGreatestRandomNullsWithValue() {
|
||||
Expression e = new SimplifyConditional().rule(new Greatest(EMPTY,
|
||||
CollectionUtils.combine(CollectionUtils.combine(randomListOfNulls(), ONE, TWO, ONE), randomListOfNulls())));
|
||||
assertEquals(Greatest.class, e.getClass());
|
||||
assertEquals(2, e.children().size());
|
||||
assertEquals(ONE, e.children().get(0));
|
||||
assertEquals(TWO, e.children().get(1));
|
||||
}
|
||||
|
||||
public void testSimplifyLeastNulls() {
|
||||
Expression e = new SimplifyConditional().rule(new Least(EMPTY, asList(Literal.NULL, Literal.NULL)));
|
||||
assertEquals(Least.class, e.getClass());
|
||||
assertEquals(0, e.children().size());
|
||||
}
|
||||
|
||||
public void testSimplifyLeastRandomNulls() {
|
||||
Expression e = new SimplifyConditional().rule(new Least(EMPTY, randomListOfNulls()));
|
||||
assertEquals(Least.class, e.getClass());
|
||||
assertEquals(0, e.children().size());
|
||||
}
|
||||
|
||||
public void testSimplifyLeastRandomNullsWithValue() {
|
||||
Expression e = new SimplifyConditional().rule(new Least(EMPTY,
|
||||
CollectionUtils.combine(CollectionUtils.combine(randomListOfNulls(), ONE, TWO, ONE), randomListOfNulls())));
|
||||
assertEquals(Least.class, e.getClass());
|
||||
assertEquals(2, e.children().size());
|
||||
assertEquals(ONE, e.children().get(0));
|
||||
assertEquals(TWO, e.children().get(1));
|
||||
}
|
||||
|
||||
//
|
||||
// Logical simplifications
|
||||
//
|
||||
|
Loading…
x
Reference in New Issue
Block a user